Added API for managing job of updating videos
This commit is contained in:
parent
a7c5878a04
commit
28de8f1279
48
YTManager/Controllers/AdminController.cs
Normal file
48
YTManager/Controllers/AdminController.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,16 +7,6 @@ using Newtonsoft.Json.Linq;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace YTManager.Controllers {
|
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")]
|
[Produces("application/json")]
|
||||||
[Route("api/Channels")]
|
[Route("api/Channels")]
|
||||||
public class ChannelsController : Controller {
|
public class ChannelsController : Controller {
|
||||||
@ -71,6 +61,11 @@ namespace YTManager.Controllers {
|
|||||||
// Seems good, so add it.
|
// Seems good, so add it.
|
||||||
_context.Channels.Add(posted);
|
_context.Channels.Add(posted);
|
||||||
_context.SaveChanges();
|
_context.SaveChanges();
|
||||||
|
|
||||||
|
// Get all new videos for the channel.
|
||||||
|
Hangfire.RecurringJob.Trigger(Startup.periodicupdatejobID);
|
||||||
|
|
||||||
|
// And all is well.
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,10 +13,8 @@ using Hangfire.PostgreSql;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace YTManager {
|
namespace YTManager {
|
||||||
public class Startup
|
public class Startup {
|
||||||
{
|
public Startup(IConfiguration configuration) {
|
||||||
public Startup(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
Configuration = configuration;
|
Configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,21 +23,20 @@ namespace YTManager {
|
|||||||
// String used for connecting to the database server.
|
// String used for connecting to the database server.
|
||||||
public static string dbstr { get; } = "Host=192.168.1.130;Database=postgres;Username=postgres;Password=pass";
|
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.
|
// 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.AddMvc();
|
||||||
services.AddHangfire(x => x.UsePostgreSqlStorage(dbstr));
|
services.AddHangfire(x => x.UsePostgreSqlStorage(dbstr));
|
||||||
services.AddDbContext<MediaDB>(options => options.UseNpgsql(dbstr));
|
services.AddDbContext<MediaDB>(options => options.UseNpgsql(dbstr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// 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())
|
if (env.IsDevelopment())
|
||||||
{
|
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
}
|
|
||||||
|
|
||||||
app.UseDefaultFiles();
|
app.UseDefaultFiles();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
@ -9,7 +9,7 @@ namespace YTManager.Tasks
|
|||||||
{
|
{
|
||||||
public class FetchVideos
|
public class FetchVideos
|
||||||
{
|
{
|
||||||
public static void run()
|
public static void run(string youtubechannelIDstr = "")
|
||||||
{
|
{
|
||||||
// YT API access key
|
// YT API access key
|
||||||
var youtubeService = new YouTubeService(new Google.Apis.Services.BaseClientService.Initializer()
|
var youtubeService = new YouTubeService(new Google.Apis.Services.BaseClientService.Initializer()
|
||||||
@ -22,7 +22,12 @@ namespace YTManager.Tasks
|
|||||||
var ops = new DbContextOptionsBuilder<MediaDB>();
|
var ops = new DbContextOptionsBuilder<MediaDB>();
|
||||||
ops.UseNpgsql(YTManager.Startup.dbstr);
|
ops.UseNpgsql(YTManager.Startup.dbstr);
|
||||||
using (var dbcontext = new MediaDB(ops.Options)) {
|
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.
|
// Get all the most recent videos for each channel.
|
||||||
channels.ForEach(ch => {
|
channels.ForEach(ch => {
|
||||||
@ -35,7 +40,7 @@ namespace YTManager.Tasks
|
|||||||
// Get all videos which aren't already in the DB.
|
// Get all videos which aren't already in the DB.
|
||||||
var notindb = response.Items
|
var notindb = response.Items
|
||||||
.Where(i => i.Id.Kind == "youtube#video")
|
.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 =>
|
.Select(newvid =>
|
||||||
new Models.Video {
|
new Models.Video {
|
||||||
Title = newvid.Snippet.Title,
|
Title = newvid.Snippet.Title,
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
<div class="pageheader">
|
<div class="pageheader">
|
||||||
<h1>Dumb YT Manager</h1>
|
<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>
|
<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>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
@ -67,8 +67,6 @@ var addchanel0 = new Vue({
|
|||||||
this.entry.thumbnailURL = "";
|
this.entry.thumbnailURL = "";
|
||||||
this.expanded = false;
|
this.expanded = false;
|
||||||
tbody0.retrieve();
|
tbody0.retrieve();
|
||||||
|
|
||||||
axios.post('/api/admin/refreshytvids');
|
|
||||||
setTimeout(() => videostb.retrieve(), 1000);
|
setTimeout(() => videostb.retrieve(), 1000);
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
@ -129,6 +127,7 @@ var tbody0 = new Vue({
|
|||||||
<th>Title</th>
|
<th>Title</th>
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
|
<th>Update</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -137,6 +136,7 @@ var tbody0 = new Vue({
|
|||||||
<td class="tinytext12px">{{entry.title}}</td>
|
<td class="tinytext12px">{{entry.title}}</td>
|
||||||
<td class="tinytext12px">{{entry.description}}</td>
|
<td class="tinytext12px">{{entry.description}}</td>
|
||||||
<td class="tinytext12px">{{entry.channelId}}</td>
|
<td class="tinytext12px">{{entry.channelId}}</td>
|
||||||
|
<td><i class="fa fa-refresh" aria-hidden="true" :id="entry.ytChannelID" @click="forcerefresh"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -159,6 +159,49 @@ var tbody0 = new Vue({
|
|||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
console.log(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 () {
|
window.addEventListener('load', function () {
|
||||||
|
apistatus.update();
|
||||||
tbody0.retrieve();
|
tbody0.retrieve();
|
||||||
videostb.retrieve();
|
videostb.retrieve();
|
||||||
});
|
});
|
||||||
|
@ -47,3 +47,20 @@
|
|||||||
.fade-enter, .fade-leave-to {
|
.fade-enter, .fade-leave-to {
|
||||||
opacity: 0;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user