Added API for managing job of updating videos

This commit is contained in:
hak8or 2017-09-03 03:00:16 -04:00
parent a7c5878a04
commit 28de8f1279
7 changed files with 133 additions and 25 deletions

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using YTManager;
using YTManager.Models;
using Hangfire.Storage;
namespace YTManager.Controllers {
[Produces("application/json")]
[Route("api/Admin")]
public class AdminController : Controller {
// POST api/Admin/refreshytvids
// Force a general youtube channel update.
[HttpPost("job_update")]
public void Post_forcejobupdate() {
Hangfire.RecurringJob.Trigger(Startup.periodicupdatejobID);
}
// POST api/Admin/job_periodicupdate/YTChannelID
// Force a YT Channel update.
[HttpPost("job_update/{id}")]
public void Post_forcejobupdateID([FromRoute] string id) {
Hangfire.BackgroundJob.Enqueue(() => YTManager.Tasks.FetchVideos.run(id));
}
// POST api/Admin/job_periodicupdate
// Ensures that the background YT Channel update API is running.
[HttpPost("job_periodicupdate")]
public void Post_periodicupdatejob() {
Hangfire.RecurringJob.AddOrUpdate(
Startup.periodicupdatejobID,
() => YTManager.Tasks.FetchVideos.run(""),
Hangfire.Cron.Hourly);
}
// GET api/Admin/job_periodicupdate
// Check if the periodic update job is enqued.
[HttpGet("job_periodicupdate")]
public IActionResult Get_periodicupdatejob() {
bool exists = Hangfire.JobStorage.Current.GetConnection().GetRecurringJobs().Count(j => j.Id == Startup.periodicupdatejobID) > 0;
return Ok(exists ? "true" : "false");
}
}
}

View File

@ -7,16 +7,6 @@ using Newtonsoft.Json.Linq;
using Microsoft.EntityFrameworkCore;
namespace YTManager.Controllers {
[Produces("application/json")]
[Route("api/Admin")]
public class AdminController : Controller {
// POST api/Admin/refreshytvids
[HttpPost("refreshytvids")]
public void Post() {
Hangfire.BackgroundJob.Enqueue(() => YTManager.Tasks.FetchVideos.run());
}
}
[Produces("application/json")]
[Route("api/Channels")]
public class ChannelsController : Controller {
@ -71,6 +61,11 @@ namespace YTManager.Controllers {
// Seems good, so add it.
_context.Channels.Add(posted);
_context.SaveChanges();
// Get all new videos for the channel.
Hangfire.RecurringJob.Trigger(Startup.periodicupdatejobID);
// And all is well.
return Ok();
}

View File

@ -13,10 +13,8 @@ using Hangfire.PostgreSql;
using Microsoft.EntityFrameworkCore;
namespace YTManager {
public class Startup
{
public Startup(IConfiguration configuration)
{
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
@ -25,21 +23,20 @@ namespace YTManager {
// String used for connecting to the database server.
public static string dbstr { get; } = "Host=192.168.1.130;Database=postgres;Username=postgres;Password=pass";
// ID for periodic job to update all channels.
public static string periodicupdatejobID { get; } = "2013066213";
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddHangfire(x => x.UsePostgreSqlStorage(dbstr));
services.AddDbContext<MediaDB>(options => options.UseNpgsql(dbstr));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();

View File

@ -9,7 +9,7 @@ namespace YTManager.Tasks
{
public class FetchVideos
{
public static void run()
public static void run(string youtubechannelIDstr = "")
{
// YT API access key
var youtubeService = new YouTubeService(new Google.Apis.Services.BaseClientService.Initializer()
@ -22,7 +22,12 @@ namespace YTManager.Tasks
var ops = new DbContextOptionsBuilder<MediaDB>();
ops.UseNpgsql(YTManager.Startup.dbstr);
using (var dbcontext = new MediaDB(ops.Options)) {
var channels = dbcontext.Channels.ToList();
// Get all the potential relevant channels.
List<Models.Channel> channels;
if (youtubechannelIDstr == "")
channels = dbcontext.Channels.ToList();
else
channels = dbcontext.Channels.Where(c => c.YTChannelID == youtubechannelIDstr).ToList();
// Get all the most recent videos for each channel.
channels.ForEach(ch => {
@ -35,7 +40,7 @@ namespace YTManager.Tasks
// Get all videos which aren't already in the DB.
var notindb = response.Items
.Where(i => i.Id.Kind == "youtube#video")
.Where(i => !dbcontext.Videos.Any(dbvid => dbvid.YTVideoID != i.Id.VideoId))
.Where(i => !dbcontext.Videos.Any(dbvid => dbvid.YTVideoID == i.Id.VideoId))
.Select(newvid =>
new Models.Video {
Title = newvid.Snippet.Title,

View File

@ -12,6 +12,8 @@
<div class="pageheader">
<h1>Dumb YT Manager</h1>
<p>Youtube banned my account and refuses to say why, taking all my subscribed channels with it. This is a simple scrubscribed channel manager, showing recent releases from each channel and finally proper sub-catagory functionality!</p>
<div id="apistatus-0"></div>
</div>
<hr />

View File

@ -67,8 +67,6 @@ var addchanel0 = new Vue({
this.entry.thumbnailURL = "";
this.expanded = false;
tbody0.retrieve();
axios.post('/api/admin/refreshytvids');
setTimeout(() => videostb.retrieve(), 1000);
}.bind(this))
.catch(function (error) {
@ -129,6 +127,7 @@ var tbody0 = new Vue({
<th>Title</th>
<th>Description</th>
<th>ID</th>
<th>Update</th>
</tr>
</thead>
<tbody>
@ -137,6 +136,7 @@ var tbody0 = new Vue({
<td class="tinytext12px">{{entry.title}}</td>
<td class="tinytext12px">{{entry.description}}</td>
<td class="tinytext12px">{{entry.channelId}}</td>
<td><i class="fa fa-refresh" aria-hidden="true" :id="entry.ytChannelID" @click="forcerefresh"/></td>
</tr>
</tbody>
</table>
@ -159,6 +159,49 @@ var tbody0 = new Vue({
.catch(function (error) {
console.log(error);
});
},
forcerefresh: function (event) {
console.log("Refreshing for ID: " + event.target.id);
axios
.post('/api/Admin/job_update/' + event.target.id)
.catch(function (error) { console.log(error); });
}
}
});
// Bind to our table of entries.
var apistatus = new Vue({
el: '#apistatus-0',
data: {
apiaccessible: false,
updatingjobactive: false
},
template: `
<div class="grid-x">
<div class="medium-1 cell">API Status</div>
<div class="medium-1 cell">
<i class="fa fa-thumbs-o-up apistatusicon fa-2x" aria-hidden="true" v-if="apiaccessible"></i>
<i class="fa fa-thumbs-o-down apistatusicon fa-2x" aria-hidden="true" v-else></i>
</div>
<div class="medium-7 cell"></div>
<div class="medium-2 cell">Video Fetching Status</div>
<div class="medium-1 cell">
<i class="fa fa-thumbs-o-up apistatusicon fa-2x" aria-hidden="true" v-if="updatingjobactive"></i>
<i class="fa fa-thumbs-o-down apistatusicon fa-2x" aria-hidden="true" v-else></i>
</div>
</div>
`,
methods: {
update: function (event) {
axios.get('/api/Admin/job_periodicupdate')
.then(function (response) {
this.apiaccessible = (response.status == 200);
this.updatingjobactive = (response.data != "false")
}.bind(this)
)
.catch(function (error) {
console.log(error);
});
}
}
});
@ -215,6 +258,7 @@ var videostb = new Vue({
});
window.addEventListener('load', function () {
apistatus.update();
tbody0.retrieve();
videostb.retrieve();
});

View File

@ -47,3 +47,20 @@
.fade-enter, .fade-leave-to {
opacity: 0;
}
.apistatusicon {
animation-duration: 1s;
animation-name: spinny;
animation-iteration-count: infinite;
animation-direction: alternate;
}
@keyframes spinny {
from {
transform: rotate(-15deg);
}
to {
transform: rotate(15deg);
}
}