Huge cleanup and rework pass, postgres now usable too
This commit is contained in:
parent
d996f7a2cb
commit
a3a3c3a780
@ -13,35 +13,40 @@ namespace YTManager.Controllers {
|
|||||||
[Produces("application/json")]
|
[Produces("application/json")]
|
||||||
[Route("api/Admin")]
|
[Route("api/Admin")]
|
||||||
public class AdminController : Controller {
|
public class AdminController : Controller {
|
||||||
// POST api/Admin/refreshytvids
|
// ID for mass update job, used to tell if the job is running or not.
|
||||||
// Force a general youtube channel update.
|
public static string Mass_Updater_ID { get; } = "2013066213";
|
||||||
[HttpPost("job_update")]
|
|
||||||
public void Post_forcejobupdate() {
|
// POST api/Admin/Trigger_Mass_Update
|
||||||
Hangfire.RecurringJob.Trigger(Startup.periodicupdatejobID);
|
// Trigger a mass channel update.
|
||||||
|
[HttpPost("Trigger_Mass_Update")]
|
||||||
|
public IActionResult Trigger_Mass_Update() {
|
||||||
|
Hangfire.RecurringJob.Trigger(Mass_Updater_ID);
|
||||||
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/Admin/job_periodicupdate/YTChannelID
|
// POST api/Admin/Update/YTChannelID
|
||||||
// Force a YT Channel update.
|
// Update the videos for a specific channel.
|
||||||
[HttpPost("job_update/{id}")]
|
[HttpPost("Update/{YoutubeID}")]
|
||||||
public void Post_forcejobupdateID([FromRoute] string id) {
|
public IActionResult Update([FromRoute] string YoutubeID) {
|
||||||
Hangfire.BackgroundJob.Enqueue(() => YTManager.Tasks.FetchVideos.run(id));
|
Hangfire.BackgroundJob.Enqueue(() => Tasks.FetchVideos.ChannelUpdate(Startup.DBStr, YoutubeID));
|
||||||
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST api/Admin/job_periodicupdate
|
// POST api/Admin/Start_Updater
|
||||||
// Ensures that the background YT Channel update API is running.
|
// Ensures that the background YT Channel update API is running.
|
||||||
[HttpPost("job_periodicupdate")]
|
[HttpPost("Start_Updater")]
|
||||||
public void Post_periodicupdatejob() {
|
public IActionResult Start_Updater() {
|
||||||
Hangfire.RecurringJob.AddOrUpdate(
|
Hangfire.RecurringJob.AddOrUpdate(
|
||||||
Startup.periodicupdatejobID,
|
Mass_Updater_ID,
|
||||||
() => YTManager.Tasks.FetchVideos.run(""),
|
() => Tasks.FetchVideos.MassUpdate(Startup.DBStr), Hangfire.Cron.Hourly);
|
||||||
Hangfire.Cron.Hourly);
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET api/Admin/job_periodicupdate
|
// GET api/Admin/Updater
|
||||||
// Check if the periodic update job is enqued.
|
// Check if the periodic update job is enqued.
|
||||||
[HttpGet("job_periodicupdate")]
|
[HttpGet("Update")]
|
||||||
public IActionResult Get_periodicupdatejob() {
|
public IActionResult Get_Update_Status() {
|
||||||
bool exists = Hangfire.JobStorage.Current.GetConnection().GetRecurringJobs().Count(j => j.Id == Startup.periodicupdatejobID) > 0;
|
bool exists = Hangfire.JobStorage.Current.GetConnection().GetRecurringJobs().Any(j => j.Id == Mass_Updater_ID);
|
||||||
return Ok(exists ? "true" : "false");
|
return Ok(exists ? "true" : "false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ namespace YTManager.Controllers {
|
|||||||
// GET api/Channels/5
|
// GET api/Channels/5
|
||||||
[HttpGet("{YTchannelID}")]
|
[HttpGet("{YTchannelID}")]
|
||||||
public Task<Models.Channel> Get([FromQuery] string YTchannelID) {
|
public Task<Models.Channel> Get([FromQuery] string YTchannelID) {
|
||||||
return db.Channels.SingleOrDefaultAsync(c => c.YTChannelID == YTchannelID);
|
return db.Channels.SingleOrDefaultAsync(c => c.YoutubeID == YTchannelID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/Channels/sdfs6DFS65f/Videos (using YouTube channel ID)
|
// GET: api/Channels/sdfs6DFS65f/Videos (using YouTube channel ID)
|
||||||
@ -38,7 +38,7 @@ namespace YTManager.Controllers {
|
|||||||
return BadRequest(ModelState);
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
// Attempt to get the video from the database.
|
// Attempt to get the video from the database.
|
||||||
var channel = await db.Channels.SingleOrDefaultAsync(m => m.YTChannelID == YTchannelID);
|
var channel = await db.Channels.SingleOrDefaultAsync(m => m.YoutubeID == YTchannelID);
|
||||||
|
|
||||||
// If the channel wasn't found then send back not found.
|
// If the channel wasn't found then send back not found.
|
||||||
if (channel == null)
|
if (channel == null)
|
||||||
@ -48,6 +48,27 @@ namespace YTManager.Controllers {
|
|||||||
return Ok(db.Entry(channel).Collection(c => c.Videos).LoadAsync());
|
return Ok(db.Entry(channel).Collection(c => c.Videos).LoadAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// POST api/Channels/sdfs6DFS65f
|
||||||
|
[HttpPost("{YTchannelID}")]
|
||||||
|
public async Task<IActionResult> Post([FromRoute] string YTchannelID) {
|
||||||
|
// Check if we were able to parse.
|
||||||
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|
||||||
|
// Verify the channel doesn't already exist.
|
||||||
|
if (db.Channels.Any(c => c.YoutubeID == YTchannelID))
|
||||||
|
return BadRequest();
|
||||||
|
|
||||||
|
// Get the channel info.
|
||||||
|
var ch = await Tasks.FetchVideos.Get_YTChannel(YTchannelID);
|
||||||
|
|
||||||
|
// Seems good, so add it.
|
||||||
|
db.Channels.Add(ch);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
// And all is well.
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
// POST api/Channels
|
// POST api/Channels
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Post([FromBody] Channel channel) {
|
public async Task<IActionResult> Post([FromBody] Channel channel) {
|
||||||
@ -55,28 +76,25 @@ namespace YTManager.Controllers {
|
|||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|
||||||
// Verify the channel doesn't already exist.
|
// Verify the channel doesn't already exist.
|
||||||
if (db.Channels.Any(c => c.YTChannelID == channel.YTChannelID))
|
if (db.Channels.Any(c => c.YoutubeID == channel.YoutubeID))
|
||||||
return BadRequest();
|
return BadRequest();
|
||||||
|
|
||||||
// Seems good, so add it.
|
// Seems good, so add it.
|
||||||
db.Channels.Add(channel);
|
db.Channels.Add(channel);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
// Get all new videos for the channel.
|
|
||||||
Hangfire.RecurringJob.Trigger(Startup.periodicupdatejobID);
|
|
||||||
|
|
||||||
// And all is well.
|
// And all is well.
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
// DELETE api/Channels/5
|
// DELETE api/Channels/5
|
||||||
[HttpDelete("{YTChannelID}")]
|
[HttpDelete("{YTChannelID}")]
|
||||||
public async Task<IActionResult> Delete([FromQuery] string YTChannelID) {
|
public async Task<IActionResult> Delete([FromRoute] string YTChannelID) {
|
||||||
// Check if we were able to parse.
|
// Check if we were able to parse.
|
||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|
||||||
// Attempt to find the channel.
|
// Attempt to find the channel.
|
||||||
var channel = await db.Channels.SingleOrDefaultAsync(c => c.YTChannelID == YTChannelID);
|
var channel = await db.Channels.SingleOrDefaultAsync(c => c.YoutubeID == YTChannelID);
|
||||||
|
|
||||||
// Check if such an entry exists already.
|
// Check if such an entry exists already.
|
||||||
if (channel == null)
|
if (channel == null)
|
||||||
|
@ -31,7 +31,7 @@ namespace YTManager.Controllers {
|
|||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|
||||||
// Attempt to get the video from the database.
|
// Attempt to get the video from the database.
|
||||||
var video = await db.Videos.SingleOrDefaultAsync(m => m.key == id);
|
var video = await db.Videos.SingleOrDefaultAsync(m => m.PrimaryKey == id);
|
||||||
|
|
||||||
// If the video wasn't found then send back not foud.
|
// If the video wasn't found then send back not foud.
|
||||||
if (video == null)
|
if (video == null)
|
||||||
@ -47,7 +47,7 @@ namespace YTManager.Controllers {
|
|||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|
||||||
// Check if such a database exists already.
|
// Check if such a database exists already.
|
||||||
if (await db.Videos.AnyAsync(d => d.YTVideoID == video.YTVideoID))
|
if (await db.Videos.AnyAsync(d => d.YoutubeID == video.YoutubeID))
|
||||||
return BadRequest();
|
return BadRequest();
|
||||||
|
|
||||||
// Add our video to the database and tell db to update.
|
// Add our video to the database and tell db to update.
|
||||||
@ -65,7 +65,7 @@ namespace YTManager.Controllers {
|
|||||||
if (!ModelState.IsValid) return BadRequest(ModelState);
|
if (!ModelState.IsValid) return BadRequest(ModelState);
|
||||||
|
|
||||||
// Attempt to find the video.
|
// Attempt to find the video.
|
||||||
var video = await db.Videos.SingleOrDefaultAsync(m => m.YTVideoID == YTVideoID);
|
var video = await db.Videos.SingleOrDefaultAsync(m => m.YoutubeID == YTVideoID);
|
||||||
|
|
||||||
// Check if such a database exists already.
|
// Check if such a database exists already.
|
||||||
if (video == null)
|
if (video == null)
|
||||||
|
@ -12,10 +12,9 @@ namespace YTManager
|
|||||||
public DbSet<Models.Video> Videos { get; set; }
|
public DbSet<Models.Video> Videos { get; set; }
|
||||||
public DbSet<Models.Tag> Tags { get; set; }
|
public DbSet<Models.Tag> Tags { get; set; }
|
||||||
|
|
||||||
public MediaDB(DbContextOptions<MediaDB> options) :base(options){ }
|
public MediaDB(DbContextOptions<MediaDB> options) : base(options){ }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||||
{
|
|
||||||
base.OnConfiguring(optionsBuilder);
|
base.OnConfiguring(optionsBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
100
YTManager/Migrations/20180220032847_initiailmigration.Designer.cs
generated
Normal file
100
YTManager/Migrations/20180220032847_initiailmigration.Designer.cs
generated
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||||
|
using System;
|
||||||
|
using YTManager;
|
||||||
|
|
||||||
|
namespace YTManager.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MediaDB))]
|
||||||
|
[Migration("20180220032847_initiailmigration")]
|
||||||
|
partial class initiailmigration
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||||
|
.HasAnnotation("ProductVersion", "2.0.1-rtm-125");
|
||||||
|
|
||||||
|
modelBuilder.Entity("YTManager.Models.Channel", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("PrimaryKey")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedtoDB");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailURL")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("YoutubeID")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("PrimaryKey");
|
||||||
|
|
||||||
|
b.ToTable("Channels");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YTManager.Models.Tag", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("PrimaryKey")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("PrimaryKey");
|
||||||
|
|
||||||
|
b.ToTable("Tags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YTManager.Models.Video", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("PrimaryKey")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedToYT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedtoDB");
|
||||||
|
|
||||||
|
b.Property<long?>("ChannelPrimaryKey");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailURL")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("YoutubeID")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("PrimaryKey");
|
||||||
|
|
||||||
|
b.HasIndex("ChannelPrimaryKey");
|
||||||
|
|
||||||
|
b.ToTable("Videos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YTManager.Models.Video", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("YTManager.Models.Channel")
|
||||||
|
.WithMany("Videos")
|
||||||
|
.HasForeignKey("ChannelPrimaryKey");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
YTManager/Migrations/20180220032847_initiailmigration.cs
Normal file
85
YTManager/Migrations/20180220032847_initiailmigration.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace YTManager.Migrations
|
||||||
|
{
|
||||||
|
public partial class initiailmigration : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Channels",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
PrimaryKey = table.Column<long>(nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
|
||||||
|
AddedtoDB = table.Column<DateTime>(nullable: false),
|
||||||
|
Description = table.Column<string>(nullable: false),
|
||||||
|
ThumbnailURL = table.Column<string>(nullable: false),
|
||||||
|
Title = table.Column<string>(nullable: false),
|
||||||
|
YoutubeID = table.Column<string>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Channels", x => x.PrimaryKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Tags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
PrimaryKey = table.Column<long>(nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
|
||||||
|
Name = table.Column<string>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Tags", x => x.PrimaryKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Videos",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
PrimaryKey = table.Column<long>(nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
|
||||||
|
AddedToYT = table.Column<DateTime>(nullable: false),
|
||||||
|
AddedtoDB = table.Column<DateTime>(nullable: false),
|
||||||
|
ChannelPrimaryKey = table.Column<long>(nullable: true),
|
||||||
|
Description = table.Column<string>(nullable: false),
|
||||||
|
ThumbnailURL = table.Column<string>(nullable: false),
|
||||||
|
Title = table.Column<string>(nullable: false),
|
||||||
|
YoutubeID = table.Column<string>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Videos", x => x.PrimaryKey);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Videos_Channels_ChannelPrimaryKey",
|
||||||
|
column: x => x.ChannelPrimaryKey,
|
||||||
|
principalTable: "Channels",
|
||||||
|
principalColumn: "PrimaryKey",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Videos_ChannelPrimaryKey",
|
||||||
|
table: "Videos",
|
||||||
|
column: "ChannelPrimaryKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Tags");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Videos");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Channels");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
YTManager/Migrations/MediaDBModelSnapshot.cs
Normal file
99
YTManager/Migrations/MediaDBModelSnapshot.cs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||||
|
using System;
|
||||||
|
using YTManager;
|
||||||
|
|
||||||
|
namespace YTManager.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MediaDB))]
|
||||||
|
partial class MediaDBModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||||
|
.HasAnnotation("ProductVersion", "2.0.1-rtm-125");
|
||||||
|
|
||||||
|
modelBuilder.Entity("YTManager.Models.Channel", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("PrimaryKey")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedtoDB");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailURL")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("YoutubeID")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("PrimaryKey");
|
||||||
|
|
||||||
|
b.ToTable("Channels");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YTManager.Models.Tag", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("PrimaryKey")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("PrimaryKey");
|
||||||
|
|
||||||
|
b.ToTable("Tags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YTManager.Models.Video", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("PrimaryKey")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedToYT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedtoDB");
|
||||||
|
|
||||||
|
b.Property<long?>("ChannelPrimaryKey");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailURL")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("YoutubeID")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("PrimaryKey");
|
||||||
|
|
||||||
|
b.HasIndex("ChannelPrimaryKey");
|
||||||
|
|
||||||
|
b.ToTable("Videos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("YTManager.Models.Video", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("YTManager.Models.Channel")
|
||||||
|
.WithMany("Videos")
|
||||||
|
.HasForeignKey("ChannelPrimaryKey");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ namespace YTManager.Models {
|
|||||||
public class Channel {
|
public class Channel {
|
||||||
// Uniquie ID for this media type
|
// Uniquie ID for this media type
|
||||||
[Key]
|
[Key]
|
||||||
public long key { get; set; }
|
public long PrimaryKey { get; set; }
|
||||||
|
|
||||||
// Title of the media
|
// Title of the media
|
||||||
[Required]
|
[Required]
|
||||||
@ -24,7 +24,7 @@ namespace YTManager.Models {
|
|||||||
|
|
||||||
// Youtube Channel ID
|
// Youtube Channel ID
|
||||||
[Required]
|
[Required]
|
||||||
public string YTChannelID { get; set; }
|
public string YoutubeID { get; set; }
|
||||||
|
|
||||||
// Added to this manager.
|
// Added to this manager.
|
||||||
[Required]
|
[Required]
|
||||||
|
@ -10,16 +10,10 @@ namespace YTManager.Models
|
|||||||
{
|
{
|
||||||
// Uniquie ID for this media type
|
// Uniquie ID for this media type
|
||||||
[Key]
|
[Key]
|
||||||
public long key { get; set; }
|
public long PrimaryKey { get; set; }
|
||||||
|
|
||||||
// Tag Name.
|
// Tag Name.
|
||||||
[Required]
|
[Required]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
// Videos this tag applies to.
|
|
||||||
public List<Video> Videos { get; set; }
|
|
||||||
|
|
||||||
// Channels this tag applies to.
|
|
||||||
public List<Channel> Channels { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace YTManager.Models {
|
|||||||
public class Video {
|
public class Video {
|
||||||
// Uniquie ID for this media type
|
// Uniquie ID for this media type
|
||||||
[Key]
|
[Key]
|
||||||
public long key { get; set; }
|
public long PrimaryKey { get; set; }
|
||||||
|
|
||||||
// Title of the media
|
// Title of the media
|
||||||
[Required]
|
[Required]
|
||||||
@ -20,7 +20,7 @@ namespace YTManager.Models {
|
|||||||
|
|
||||||
// Youtube Video ID
|
// Youtube Video ID
|
||||||
[Required]
|
[Required]
|
||||||
public string YTVideoID { get; set; }
|
public string YoutubeID { get; set; }
|
||||||
|
|
||||||
// Thumbnail link
|
// Thumbnail link
|
||||||
[Required]
|
[Required]
|
||||||
@ -34,8 +34,8 @@ namespace YTManager.Models {
|
|||||||
[Required]
|
[Required]
|
||||||
public DateTime AddedtoDB { get; set; }
|
public DateTime AddedtoDB { get; set; }
|
||||||
|
|
||||||
// Channel this video comes from.
|
// Tag this video applies to.
|
||||||
[Required]
|
[Required]
|
||||||
public Channel channel;
|
public List<Tag> Tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchUrl": "api/values",
|
"launchUrl": "api/values",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
|
"POSTGRESQL_DBSTR": "Server=192.168.1.211;Port=32768;Database=postgres;User Id=postgres;",
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:62214/"
|
"applicationUrl": "http://localhost:62214/"
|
||||||
|
@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Hangfire.MemoryStorage;
|
using Hangfire.PostgreSql;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace YTManager {
|
namespace YTManager {
|
||||||
@ -20,14 +20,20 @@ namespace YTManager {
|
|||||||
|
|
||||||
public IConfiguration Configuration { get; }
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
// ID for periodic job to update all channels.
|
public static string DBStr {
|
||||||
public static string periodicupdatejobID { get; } = "2013066213";
|
get {
|
||||||
|
string s = Environment.GetEnvironmentVariable("POSTGRESQL_DBSTR");
|
||||||
|
s = s ?? "Server=localhost;Port=32768;Database=postgres;User Id=postgres;";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
Console.WriteLine("Using db string of: " + DBStr);
|
||||||
services.AddMvc();
|
services.AddMvc();
|
||||||
services.AddHangfire(x => x.UseMemoryStorage());
|
services.AddDbContext<MediaDB>(x => x.UseNpgsql(DBStr));
|
||||||
services.AddDbContext<MediaDB>(options => options.UseInMemoryDatabase(databaseName: "testdb"));
|
services.AddHangfire(x => x.UsePostgreSqlStorage(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.
|
||||||
|
@ -5,12 +5,10 @@ using System.Threading.Tasks;
|
|||||||
using Google.Apis.YouTube.v3;
|
using Google.Apis.YouTube.v3;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace YTManager.Tasks
|
namespace YTManager.Tasks {
|
||||||
{
|
public class FetchVideos {
|
||||||
public class FetchVideos
|
// Get a bunch of videos from youtube that the channel generated.
|
||||||
{
|
private static async Task<List<Models.Video>> Get_YTVideos(string channelID) {
|
||||||
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()
|
||||||
{
|
{
|
||||||
@ -18,49 +16,92 @@ namespace YTManager.Tasks
|
|||||||
ApplicationName = "testingapppp"
|
ApplicationName = "testingapppp"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Search youtube for all the relevant data of the channel.
|
||||||
|
var query = youtubeService.Search.List("snippet");
|
||||||
|
query.ChannelId = channelID;
|
||||||
|
query.Order = SearchResource.ListRequest.OrderEnum.Date;
|
||||||
|
query.MaxResults = 50;
|
||||||
|
var response = await query.ExecuteAsync();
|
||||||
|
|
||||||
|
// Convert the response into models.
|
||||||
|
return response.Items?
|
||||||
|
.Where(i => i.Id.Kind == "youtube#video")
|
||||||
|
.Select(newvid => new Models.Video
|
||||||
|
{
|
||||||
|
Title = newvid.Snippet.Title,
|
||||||
|
Description = newvid.Snippet.Description,
|
||||||
|
YoutubeID = newvid.Id.VideoId,
|
||||||
|
AddedToYT = newvid.Snippet.PublishedAt.GetValueOrDefault(),
|
||||||
|
AddedtoDB = DateTime.Now,
|
||||||
|
ThumbnailURL = newvid.Snippet.Thumbnails.Medium.Url
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<Models.Channel> Get_YTChannel(string channelID) {
|
||||||
|
// YT API access key
|
||||||
|
var youtubeService = new YouTubeService(new Google.Apis.Services.BaseClientService.Initializer()
|
||||||
|
{
|
||||||
|
ApiKey = "AIzaSyCuIYkMc5SktlnXRXNaDf2ObX-fQvtWCnQ",
|
||||||
|
ApplicationName = "testingapppp"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search youtube for all the relevant data of the channel.
|
||||||
|
var query = youtubeService.Channels.List("snippet");
|
||||||
|
query.Id = channelID;
|
||||||
|
query.MaxResults = 1;
|
||||||
|
var response = await query.ExecuteAsync();
|
||||||
|
|
||||||
|
// Parse the response into a channel.
|
||||||
|
return new Models.Channel {
|
||||||
|
Description = response.Items.First().Snippet.Description,
|
||||||
|
Title = response.Items.First().Snippet.Title,
|
||||||
|
ThumbnailURL = response.Items.First().Snippet.Thumbnails.Medium.Url,
|
||||||
|
YoutubeID = channelID,
|
||||||
|
AddedtoDB = DateTime.Now
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update videos for all our channels.
|
||||||
|
public static async Task MassUpdate(string dbstr) {
|
||||||
// Get the interface to the database.
|
// Get the interface to the database.
|
||||||
var ops = new DbContextOptionsBuilder<MediaDB>();
|
var ops = new DbContextOptionsBuilder<MediaDB>();
|
||||||
ops.UseInMemoryDatabase(databaseName: "testdb");
|
ops.UseNpgsql(dbstr);
|
||||||
|
|
||||||
// Get all the channels to update.
|
// Get all the channels from the db.
|
||||||
using (var dbcontext = new MediaDB(ops.Options)) {
|
var channels = await (new MediaDB(ops.Options)).Channels.ToListAsync();
|
||||||
// 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.
|
// For each channel, do an update.
|
||||||
channels.ForEach(ch => {
|
channels.ForEach(async ch => await ChannelUpdate(dbstr, ch.YoutubeID));
|
||||||
// Get channel videos from youtube.
|
}
|
||||||
var query = youtubeService.Search.List("snippet");
|
|
||||||
query.ChannelId = ch.YTChannelID;
|
|
||||||
query.Order = SearchResource.ListRequest.OrderEnum.Date;
|
|
||||||
query.MaxResults = 50;
|
|
||||||
var response = query.Execute();
|
|
||||||
|
|
||||||
// Get all videos which aren't already in the DB based on the ytid.
|
// Update videos for just one channel.
|
||||||
var notindb = response.Items
|
public static async Task ChannelUpdate(string dbstr, string youtubechannelIDstr) {
|
||||||
.Where(i => i.Id.Kind == "youtube#video")
|
// Get the interface to the database.
|
||||||
.Where(i => !dbcontext.Videos.Any(dbvid => dbvid.YTVideoID == i.Id.VideoId))
|
var ops = new DbContextOptionsBuilder<MediaDB>();
|
||||||
.Select(newvid =>
|
ops.UseNpgsql(dbstr);
|
||||||
new Models.Video {
|
var db = new MediaDB(ops.Options);
|
||||||
Title = newvid.Snippet.Title,
|
|
||||||
Description = newvid.Snippet.Description,
|
|
||||||
YTVideoID = newvid.Id.VideoId,
|
|
||||||
AddedToYT = newvid.Snippet.PublishedAt.GetValueOrDefault(),
|
|
||||||
AddedtoDB = DateTime.Now,
|
|
||||||
channel = ch,
|
|
||||||
ThumbnailURL = newvid.Snippet.Thumbnails.Medium.Url
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
// Add all videos not already in the database over.
|
// Get the channel from the db when including it's videos.
|
||||||
notindb.ForEach(newvid => dbcontext.Videos.Add(newvid));
|
var channel = await db.Channels
|
||||||
|
.Include(c => c.Videos)
|
||||||
|
.SingleOrDefaultAsync(ch => ch.YoutubeID == youtubechannelIDstr);
|
||||||
|
|
||||||
// And save since we are done.
|
// Update the channel if it was found.
|
||||||
dbcontext.SaveChanges();
|
if (channel != null) {
|
||||||
});
|
// Get all the new videos for the channel.
|
||||||
|
var Videos = await Get_YTVideos(channel.YoutubeID);
|
||||||
|
|
||||||
|
// Get all the videos which haven't been put into this channels videos.
|
||||||
|
var newvids = Videos.Where(nv => !channel.Videos.Any(cv => cv.YoutubeID == nv.YoutubeID));
|
||||||
|
|
||||||
|
// Add all the videos to the databse.
|
||||||
|
await db.Videos.AddRangeAsync(newvids);
|
||||||
|
|
||||||
|
// Update the videos this channel refers to.
|
||||||
|
channel.Videos.AddRange(newvids);
|
||||||
|
|
||||||
|
// And say the database should be changed.
|
||||||
|
await db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,22 @@
|
|||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Job System -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Hangfire" Version="1.6.17" />
|
||||||
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.6.17" />
|
||||||
|
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Database -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Backend Website -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.32.2.1131" />
|
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.32.2.1131" />
|
||||||
<PackageReference Include="Hangfire" Version="1.6.17"/>
|
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.6.17" />
|
|
||||||
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0"/>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" />
|
||||||
@ -17,6 +28,7 @@
|
|||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.2" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- So we can run dotnet migration ... -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
|
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
|
||||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
|
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
|
||||||
|
@ -59,7 +59,7 @@ var addchanel0 = new Vue({
|
|||||||
submit: function (event) {
|
submit: function (event) {
|
||||||
// Send the channel to our API and request tables be updated.
|
// Send the channel to our API and request tables be updated.
|
||||||
var d = this.entry;
|
var d = this.entry;
|
||||||
axios.post('/api/Channels', d)
|
axios.post('/api/Channels/' + this.entry.yTChannelID)
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
this.entry.title = "";
|
this.entry.title = "";
|
||||||
this.entry.description = "";
|
this.entry.description = "";
|
||||||
@ -132,13 +132,13 @@ var tbody0 = new Vue({
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-cloak v-for="entry in entries">
|
<tr v-cloak v-for="entry in entries">
|
||||||
<td class="tinytext12px">{{entry.ytChannelID}}</td>
|
<td class="tinytext12px">{{entry.youtubeID}}</td>
|
||||||
<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.primaryKey}}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#"><i class="fa fa-refresh" aria-hidden="true" :id="entry.ytChannelID" @click="forcerefresh"/></a>
|
<a href="#"><i class="fa fa-refresh" aria-hidden="true" :id="entry.youtubeID" @click="forcerefresh"/></a>
|
||||||
<a href="#"><i class="fa fa-trash-o" aria-hidden="true" :id="entry.ytChannelID" @click="deletechannel"/></a>
|
<a href="#"><i class="fa fa-trash-o" aria-hidden="true" :id="entry.youtubeID" @click="deletechannel"/></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -165,7 +165,7 @@ var tbody0 = new Vue({
|
|||||||
},
|
},
|
||||||
forcerefresh: function (event) {
|
forcerefresh: function (event) {
|
||||||
axios
|
axios
|
||||||
.post('/api/Admin/job_update/' + event.target.id)
|
.post('/api/Admin/Update/' + event.target.id)
|
||||||
.catch(function (error) { console.log(error); });
|
.catch(function (error) { console.log(error); });
|
||||||
},
|
},
|
||||||
deletechannel: function (event) {
|
deletechannel: function (event) {
|
||||||
@ -200,7 +200,7 @@ var apistatus = new Vue({
|
|||||||
`,
|
`,
|
||||||
methods: {
|
methods: {
|
||||||
update: function (event) {
|
update: function (event) {
|
||||||
axios.get('/api/Admin/job_periodicupdate')
|
axios.get('/api/Admin/Update')
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
this.apiaccessible = (response.status == 200);
|
this.apiaccessible = (response.status == 200);
|
||||||
this.updatingjobactive = (response.data != "false")
|
this.updatingjobactive = (response.data != "false")
|
||||||
@ -213,7 +213,7 @@ var apistatus = new Vue({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Grid if images.
|
// Grid of images.
|
||||||
var videostb = new Vue({
|
var videostb = new Vue({
|
||||||
el: '#videosindbtable-0',
|
el: '#videosindbtable-0',
|
||||||
data: {
|
data: {
|
||||||
@ -234,9 +234,9 @@ var videostb = new Vue({
|
|||||||
<tr v-cloak v-for="video in Videos">
|
<tr v-cloak v-for="video in Videos">
|
||||||
<td class="tinytext12px">{{video.title}}</td>
|
<td class="tinytext12px">{{video.title}}</td>
|
||||||
<td class="tinytext12px">{{video.description}}</td>
|
<td class="tinytext12px">{{video.description}}</td>
|
||||||
<td class="tinytext12px">{{video.ytVideoID}}</td>
|
<td class="tinytext12px">{{video.youtubeID}}</td>
|
||||||
<td class="tinytext12px">{{video.uploaded}}</td>
|
<td class="tinytext12px">{{video.uploaded}}</td>
|
||||||
<td class="tinytext12px">{{video.videoId}}</td>
|
<td class="tinytext12px">{{video.primaryKey}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -5,10 +5,12 @@
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: #003636;
|
color: #003636;
|
||||||
background-color: #F8F8F8;
|
background-color: #F8F8F8;
|
||||||
|
max-width: 1024px;
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pageheader {
|
.pageheader {
|
||||||
max-width: 1280px;
|
max-width: 960px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
<h2>Most Recent Videos</h2>
|
|
||||||
<div v-cloak id="vidholder-0"></div>
|
<div v-cloak id="vidholder-0"></div>
|
||||||
|
|
||||||
<!-- Compressed JavaScript -->
|
<!-- Compressed JavaScript -->
|
||||||
|
@ -40,7 +40,7 @@ var vidholder = new Vue({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new URL by adding the YT ID.
|
// Generate a new URL by adding the YT ID.
|
||||||
x.url = "https://www.youtube.com/watch?v=" + x.ytVideoID
|
x.url = "https://www.youtube.com/watch?v=" + x.youtubeID;
|
||||||
|
|
||||||
// Add it to our array
|
// Add it to our array
|
||||||
this.Videos.push(x);
|
this.Videos.push(x);
|
||||||
|
Loading…
Reference in New Issue
Block a user