Add project files.
This commit is contained in:
parent
70d055282c
commit
ae782025f6
25
YTManager.sln
Normal file
25
YTManager.sln
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.12
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YTManager", "YTManager\YTManager.csproj", "{14F56CC1-67A1-477F-817C-A0A7B55AE954}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{14F56CC1-67A1-477F-817C-A0A7B55AE954}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{14F56CC1-67A1-477F-817C-A0A7B55AE954}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{14F56CC1-67A1-477F-817C-A0A7B55AE954}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{14F56CC1-67A1-477F-817C-A0A7B55AE954}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DB293C09-A2E4-4179-ADA9-BAE2093F425A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
95
YTManager/Controllers/ValuesController.cs
Normal file
95
YTManager/Controllers/ValuesController.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
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 {
|
||||
private readonly MediaDB _context;
|
||||
|
||||
public ChannelsController(MediaDB context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET api/Channels
|
||||
[HttpGet]
|
||||
public IEnumerable<Models.Channel> Get() {
|
||||
return _context.Channels.ToList();
|
||||
}
|
||||
|
||||
// GET api/Channels/5
|
||||
[HttpGet("{id}")]
|
||||
public Models.Channel Get(int id) {
|
||||
return _context.Channels.Single(m => m.ChannelId == id);
|
||||
}
|
||||
|
||||
// GET: api/Channels/sdfs6DFS65f/Videos (using YouTube channel ID)
|
||||
[HttpGet("{id}/Videos")]
|
||||
public async Task<IActionResult> GetVideos([FromRoute] string YTchannelID) {
|
||||
if (!ModelState.IsValid)
|
||||
return BadRequest(ModelState);
|
||||
|
||||
// Verify the channel exists.
|
||||
var Chan = await _context.Channels.SingleOrDefaultAsync(c => c.YTChannelID == YTchannelID);
|
||||
if (Chan == null)
|
||||
return NotFound();
|
||||
|
||||
// Send back the found stuff.
|
||||
return Ok(_context.Entry(Chan).Collection(c => c.Videos).LoadAsync());
|
||||
}
|
||||
|
||||
// POST api/values
|
||||
[HttpPost]
|
||||
public IActionResult Post([FromBody]JObject value) {
|
||||
// Get the channel out of our json body.
|
||||
Models.Channel posted = value.ToObject<Models.Channel>();
|
||||
|
||||
// Verify items aren't empty.
|
||||
if ((posted.YTChannelID.Length == 0) || (posted.Description.Length == 0) || (posted.Title.Length == 0))
|
||||
return BadRequest();
|
||||
|
||||
// Verify the channel doesn't already exist.
|
||||
if (_context.Channels.Any(c => c.YTChannelID == posted.YTChannelID))
|
||||
return BadRequest();
|
||||
|
||||
// Seems good, so add it.
|
||||
_context.Channels.Add(posted);
|
||||
_context.SaveChanges();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// PUT api/values/5
|
||||
[HttpPut("{id}")]
|
||||
public void Put(int id, [FromBody]JObject value)
|
||||
{
|
||||
Models.Channel posted = value.ToObject<Models.Channel>();
|
||||
posted.ChannelId = id; // Ensure an id is attached
|
||||
_context.Channels.Update(posted);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
// DELETE api/values/5
|
||||
[HttpDelete("{id}")]
|
||||
public void Delete(int id)
|
||||
{
|
||||
_context.Remove(_context.Channels.Single(m => m.ChannelId == id));
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
126
YTManager/Controllers/VideosController.cs
Normal file
126
YTManager/Controllers/VideosController.cs
Normal file
@ -0,0 +1,126 @@
|
||||
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;
|
||||
|
||||
namespace YTManager.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/Videos")]
|
||||
public class VideosController : Controller
|
||||
{
|
||||
private readonly MediaDB _context;
|
||||
|
||||
public VideosController(MediaDB context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET: api/Videos
|
||||
[HttpGet]
|
||||
public IEnumerable<Video> GetVideos()
|
||||
{
|
||||
return _context.Videos;
|
||||
}
|
||||
|
||||
// GET: api/Videos/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetVideo([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var video = await _context.Videos.SingleOrDefaultAsync(m => m.VideoId == id);
|
||||
|
||||
if (video == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(video);
|
||||
}
|
||||
|
||||
// PUT: api/Videos/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutVideo([FromRoute] long id, [FromBody] Video video)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != video.VideoId)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(video).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!VideoExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/Videos
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostVideo([FromBody] Video video)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.Videos.Add(video);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetVideo", new { id = video.VideoId }, video);
|
||||
}
|
||||
|
||||
// DELETE: api/Videos/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteVideo([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var video = await _context.Videos.SingleOrDefaultAsync(m => m.VideoId == id);
|
||||
if (video == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.Videos.Remove(video);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(video);
|
||||
}
|
||||
|
||||
private bool VideoExists(long id)
|
||||
{
|
||||
return _context.Videos.Any(e => e.VideoId == id);
|
||||
}
|
||||
}
|
||||
}
|
21
YTManager/MediaDB.cs
Normal file
21
YTManager/MediaDB.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace YTManager
|
||||
{
|
||||
public class MediaDB : DbContext
|
||||
{
|
||||
public DbSet<Models.Channel> Channels { get; set; }
|
||||
public DbSet<Models.Video> Videos { get; set; }
|
||||
|
||||
public MediaDB(DbContextOptions<MediaDB> options) :base(options){ }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
}
|
||||
}
|
||||
}
|
86
YTManager/Migrations/20170901083156_initial.Designer.cs
generated
Normal file
86
YTManager/Migrations/20170901083156_initial.Designer.cs
generated
Normal file
@ -0,0 +1,86 @@
|
||||
// <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("20170901083156_initial")]
|
||||
partial class initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn)
|
||||
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
|
||||
|
||||
modelBuilder.Entity("YTManager.Models.Channel", b =>
|
||||
{
|
||||
b.Property<long>("ChannelId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("ThumbnailURL")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("YTChannelID")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("ChannelId");
|
||||
|
||||
b.ToTable("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("YTManager.Models.Video", b =>
|
||||
{
|
||||
b.Property<long>("VideoId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedtoDB");
|
||||
|
||||
b.Property<long>("ChannelId");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("ThumbnailURL")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("Uploaded");
|
||||
|
||||
b.Property<string>("YTVideoID")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("VideoId");
|
||||
|
||||
b.HasIndex("ChannelId");
|
||||
|
||||
b.ToTable("Videos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("YTManager.Models.Video", b =>
|
||||
{
|
||||
b.HasOne("YTManager.Models.Channel", "Channel")
|
||||
.WithMany("Videos")
|
||||
.HasForeignKey("ChannelId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
68
YTManager/Migrations/20170901083156_initial.cs
Normal file
68
YTManager/Migrations/20170901083156_initial.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YTManager.Migrations
|
||||
{
|
||||
public partial class initial : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Channels",
|
||||
columns: table => new
|
||||
{
|
||||
ChannelId = table.Column<long>(type: "int8", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
|
||||
Description = table.Column<string>(type: "text", nullable: false),
|
||||
ThumbnailURL = table.Column<string>(type: "text", nullable: false),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
YTChannelID = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Channels", x => x.ChannelId);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Videos",
|
||||
columns: table => new
|
||||
{
|
||||
VideoId = table.Column<long>(type: "int8", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn),
|
||||
AddedtoDB = table.Column<DateTime>(type: "timestamp", nullable: false),
|
||||
ChannelId = table.Column<long>(type: "int8", nullable: false),
|
||||
Description = table.Column<string>(type: "text", nullable: false),
|
||||
ThumbnailURL = table.Column<string>(type: "text", nullable: false),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
Uploaded = table.Column<DateTime>(type: "timestamp", nullable: false),
|
||||
YTVideoID = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Videos", x => x.VideoId);
|
||||
table.ForeignKey(
|
||||
name: "FK_Videos_Channels_ChannelId",
|
||||
column: x => x.ChannelId,
|
||||
principalTable: "Channels",
|
||||
principalColumn: "ChannelId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Videos_ChannelId",
|
||||
table: "Videos",
|
||||
column: "ChannelId");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Videos");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Channels");
|
||||
}
|
||||
}
|
||||
}
|
85
YTManager/Migrations/MediaDBModelSnapshot.cs
Normal file
85
YTManager/Migrations/MediaDBModelSnapshot.cs
Normal file
@ -0,0 +1,85 @@
|
||||
// <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.0-rtm-26452");
|
||||
|
||||
modelBuilder.Entity("YTManager.Models.Channel", b =>
|
||||
{
|
||||
b.Property<long>("ChannelId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("ThumbnailURL")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("YTChannelID")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("ChannelId");
|
||||
|
||||
b.ToTable("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("YTManager.Models.Video", b =>
|
||||
{
|
||||
b.Property<long>("VideoId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedtoDB");
|
||||
|
||||
b.Property<long>("ChannelId");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("ThumbnailURL")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("Uploaded");
|
||||
|
||||
b.Property<string>("YTVideoID")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("VideoId");
|
||||
|
||||
b.HasIndex("ChannelId");
|
||||
|
||||
b.ToTable("Videos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("YTManager.Models.Video", b =>
|
||||
{
|
||||
b.HasOne("YTManager.Models.Channel", "Channel")
|
||||
.WithMany("Videos")
|
||||
.HasForeignKey("ChannelId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
32
YTManager/Models/Channel.cs
Normal file
32
YTManager/Models/Channel.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace YTManager.Models {
|
||||
public class Channel {
|
||||
// Uniquie ID for this media type
|
||||
[Key]
|
||||
public long ChannelId { get; set; }
|
||||
|
||||
// Title of the media
|
||||
[Required]
|
||||
public string Title { get; set; }
|
||||
|
||||
// Short description of the media
|
||||
[Required]
|
||||
public string Description { get; set; }
|
||||
|
||||
// Thumbnail link
|
||||
[Required]
|
||||
public string ThumbnailURL { get; set; }
|
||||
|
||||
// Youtube Channel ID
|
||||
[Required]
|
||||
public string YTChannelID { get; set; }
|
||||
|
||||
// Videos this channel has.
|
||||
public List<Video> Videos { get; set; }
|
||||
}
|
||||
}
|
41
YTManager/Models/Video.cs
Normal file
41
YTManager/Models/Video.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace YTManager.Models {
|
||||
public class Video {
|
||||
// Uniquie ID for this media type
|
||||
[Key]
|
||||
public long VideoId { get; set; }
|
||||
|
||||
// Title of the media
|
||||
[Required]
|
||||
public string Title { get; set; }
|
||||
|
||||
// Short description of the media
|
||||
[Required]
|
||||
public string Description { get; set; }
|
||||
|
||||
// Youtube Video ID
|
||||
[Required]
|
||||
public string YTVideoID { get; set; }
|
||||
|
||||
// Thumbnail link
|
||||
[Required]
|
||||
public string ThumbnailURL { get; set; }
|
||||
|
||||
// Date video was uploaded to YT
|
||||
[Required]
|
||||
public DateTime Uploaded { get; set; }
|
||||
|
||||
// Date added to database
|
||||
[Required]
|
||||
public DateTime AddedtoDB { get; set; }
|
||||
|
||||
// Refer back to what channel this video belongs to.
|
||||
public long ChannelId { get; set; }
|
||||
public Channel Channel { get; set; }
|
||||
}
|
||||
}
|
26
YTManager/Program.cs
Normal file
26
YTManager/Program.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace YTManager
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildWebHost(args).Run();
|
||||
}
|
||||
|
||||
public static IWebHost BuildWebHost(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
}
|
||||
}
|
28
YTManager/Properties/launchSettings.json
Normal file
28
YTManager/Properties/launchSettings.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:62213/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "api/values",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"YTManager": {
|
||||
"commandName": "Project",
|
||||
"launchUrl": "api/values",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:62214/"
|
||||
}
|
||||
}
|
||||
}
|
39
YTManager/ScaffoldingReadMe.txt
Normal file
39
YTManager/ScaffoldingReadMe.txt
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
ASP.NET MVC core dependencies have been added to the project.
|
||||
(These dependencies include packages required to enable scaffolding)
|
||||
|
||||
However you may still need to do make changes to your project.
|
||||
|
||||
1. Suggested changes to Startup class:
|
||||
1.1 Add a constructor:
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
1.2 Add MVC services:
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Add framework services.
|
||||
services.AddMvc();
|
||||
}
|
||||
|
||||
1.3 Configure web app to use use Configuration and use MVC routing:
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute(
|
||||
name: "default",
|
||||
template: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
}
|
49
YTManager/Startup.cs
Normal file
49
YTManager/Startup.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Hangfire;
|
||||
using Hangfire.PostgreSql;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace YTManager {
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc();
|
||||
string constr = "Host=home.hak8or.com;Database=postgres;Username=postgres;Password=mysecretpassword";
|
||||
services.AddHangfire(x => x.UsePostgreSqlStorage(constr));
|
||||
services.AddDbContext<MediaDB>(options => options.UseNpgsql(constr));
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
app.UseMvc();
|
||||
app.UseHangfireServer();
|
||||
app.UseHangfireDashboard();
|
||||
}
|
||||
}
|
||||
}
|
60
YTManager/Tasks/FetchVideos.cs
Normal file
60
YTManager/Tasks/FetchVideos.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Apis.YouTube.v3;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace YTManager.Tasks
|
||||
{
|
||||
public class FetchVideos
|
||||
{
|
||||
public static void run()
|
||||
{
|
||||
Console.WriteLine("Startnig job ...");
|
||||
|
||||
// YT API access key
|
||||
var youtubeService = new YouTubeService(new Google.Apis.Services.BaseClientService.Initializer()
|
||||
{
|
||||
ApiKey = "AIzaSyCuIYkMc5SktlnXRXNaDf2ObX-fQvtWCnQ",
|
||||
ApplicationName = "testingapppp"
|
||||
});
|
||||
|
||||
// Get all the channels to update.
|
||||
var ops = new DbContextOptionsBuilder<MediaDB>();
|
||||
string constr = "Host=home.hak8or.com;Database=postgres;Username=postgres;Password=mysecretpassword";
|
||||
ops.UseNpgsql(constr);
|
||||
using (var dbcontext = new MediaDB(ops.Options)) {
|
||||
var channels = dbcontext.Channels.ToList();
|
||||
|
||||
// Get all the most recent videos for each channel.
|
||||
channels.ForEach(ch => {
|
||||
// Get channel videos from youtube.
|
||||
var query = youtubeService.Activities.List("snippet");
|
||||
query.ChannelId = ch.YTChannelID;
|
||||
var response = query.Execute();
|
||||
|
||||
// Get all videos which aren't already in the DB.
|
||||
var notindb = response.Items
|
||||
.Where(i => !dbcontext.Videos.Any(dbvid => dbvid.YTVideoID != i.Id))
|
||||
.Select(newvid =>
|
||||
new Models.Video {
|
||||
Title = newvid.Snippet.Title,
|
||||
Description = newvid.Snippet.Description,
|
||||
YTVideoID = newvid.Id,
|
||||
Uploaded = 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.
|
||||
notindb.ForEach(newvid => dbcontext.Videos.Add(newvid));
|
||||
|
||||
// And save since we are done.
|
||||
dbcontext.SaveChanges();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
YTManager/YTManager.csproj
Normal file
31
YTManager/YTManager.csproj
Normal file
@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.29.0.760" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.15" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.6.15" />
|
||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Migrations\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
10
YTManager/appsettings.Development.json
Normal file
10
YTManager/appsettings.Development.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
15
YTManager/appsettings.json
Normal file
15
YTManager/appsettings.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"Debug": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
},
|
||||
"Console": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
109
YTManager/wwwroot/admin.html
Normal file
109
YTManager/wwwroot/admin.html
Normal file
@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>YT Manager Admin</title>
|
||||
|
||||
<!-- Compressed CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="index.css">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
|
||||
<hr />
|
||||
<h2>Subscribed Channels</h2>
|
||||
<table id="tbody-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>YT Channel ID</th>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-cloak v-for="entry in entries">
|
||||
<td class="tinytext12px">{{entry.ytChannelID}}</td>
|
||||
<td class="tinytext12px">{{entry.title}}</td>
|
||||
<td class="tinytext12px">{{entry.description}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="addchanel-0">
|
||||
<div class="grid-x">
|
||||
<div class="medium-11 cell"></div>
|
||||
<div class="medium-1 cell">
|
||||
<button type="button" class="button input-group" v-on:click='expanded = !expanded'>
|
||||
<i class="fa fa-plus" aria-hidden="true"></i> Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<transition name="fade">
|
||||
<form v-cloak v-if="expanded">
|
||||
<h2>Add new Channel</h2>
|
||||
<div class="grid-x">
|
||||
<div class="input-group medium-6 cell">
|
||||
<span class="input-group-label">Title</span>
|
||||
<input class="input-group-field" type="text" placeholder="Title of Entry" v-model="entry.title">
|
||||
</div>
|
||||
<div class="input-group medium-1 cell"></div>
|
||||
<div class="input-group medium-5 cell">
|
||||
<span class="input-group-label">Channel</span>
|
||||
<input class="input-group-field" v-bind:class="{ error: fail }" type="text"
|
||||
@blur="newurl" placeholder="URL of Entry" v-model="entry.yTChannelID">
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span class="input-group-label">Description</span>
|
||||
<input class="input-group-field" type="text" placeholder="Description of Entry" v-model="entry.description">
|
||||
</div>
|
||||
<div class="grid-x">
|
||||
<img class="small-4 cell" :src="entry.thumbnailURL" />
|
||||
<div class="small-7"></div>
|
||||
<button type="button" style="max-height:50px" class="button input-group small-1 cell" @click="submit">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<h2>Videos in DB</h2>
|
||||
<table id="videostb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
<th>Youtube Video ID</th>
|
||||
<th>ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-cloak v-for="video in Videos">
|
||||
<td class="tinytext12px">{{video.title}}</td>
|
||||
<td class="tinytext12px">{{video.description}}</td>
|
||||
<td class="tinytext12px">{{video.ytVideoID}}</td>
|
||||
<td class="tinytext12px">{{video.videoId}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Compressed JavaScript -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js"></script>
|
||||
|
||||
<!-- For doing REST based API stuff. -->
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
|
||||
<!-- Some icons -->
|
||||
<script src="https://use.fontawesome.com/91af8ab4ba.js"></script>
|
||||
|
||||
<!-- Good ole Vue :D -->
|
||||
<script src="https://unpkg.com/vue"></script>
|
||||
|
||||
<!-- All of my custom JS. Put here so body loads before Vue based stuff loads/attempts to bind. -->
|
||||
<script src="admin.js"></script>
|
||||
</body>
|
||||
</html>
|
139
YTManager/wwwroot/admin.js
Normal file
139
YTManager/wwwroot/admin.js
Normal file
@ -0,0 +1,139 @@
|
||||
$(document).foundation();
|
||||
|
||||
// How many chars at most for the description.
|
||||
var maxcharstablefield = 200;
|
||||
|
||||
// Bind to the form for submitting a new channel
|
||||
var addchanel0 = new Vue({
|
||||
el: '#addchanel-0',
|
||||
data: {
|
||||
entry: {
|
||||
title: "",
|
||||
description: "",
|
||||
yTChannelID: "",
|
||||
thumbnailURL: "",
|
||||
},
|
||||
fail: false,
|
||||
expanded: false
|
||||
},
|
||||
methods: {
|
||||
submit: function (event) {
|
||||
// Send the channel to our API and request tables be updated.
|
||||
var d = this.entry;
|
||||
axios.post('/api/Channels', d)
|
||||
.then(function (response) {
|
||||
this.entry.title = "";
|
||||
this.entry.description = "";
|
||||
this.entry.yTChannelID = "";
|
||||
this.entry.thumbnailURL = "";
|
||||
this.expanded = false;
|
||||
tbody0.retrieve();
|
||||
|
||||
axios.post('/api/admin/refreshytvids');
|
||||
setTimeout(() => videostb.retrieve(), 1000);
|
||||
}.bind(this))
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
|
||||
// Handle when user put in a new URL (verification and thumbnail fetch)
|
||||
newurl: function (event) {
|
||||
// Remove any potential youtube URL from the field.
|
||||
var ytchurl = "https://www.youtube.com/channel/";
|
||||
if (this.entry.yTChannelID.startsWith(ytchurl)) {
|
||||
this.entry.yTChannelID = this.entry.yTChannelID.replace(ytchurl, "");
|
||||
}
|
||||
|
||||
// Check if what remains looks like a youtube channel ID.
|
||||
if (this.entry.yTChannelID.length != "UCyS4xQE6DK4_p3qXQwJQAyA".length) {
|
||||
this.fail = true;
|
||||
return;
|
||||
}
|
||||
this.fail = false;
|
||||
|
||||
// Get the Channel URL
|
||||
var basestr = 'https://www.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics';
|
||||
var apikey = '&key=AIzaSyCuIYkMc5SktlnXRXNaDf2ObX-fQvtWCnQ '
|
||||
var channel = '&id=' + addchanel0.entry.yTChannelID;
|
||||
axios.get(basestr + channel + apikey)
|
||||
.then(function (response) {
|
||||
// Only attempt to fill the UI text boxes if they are empty.
|
||||
if (this.entry.description.length == 0) {
|
||||
this.entry.description = response.data.items[0].snippet.description;
|
||||
}
|
||||
if (this.entry.title.length == 0) {
|
||||
this.entry.title = response.data.items[0].snippet.title;
|
||||
}
|
||||
|
||||
// Get client to load the thumbnail
|
||||
this.entry.thumbnailURL = response.data.items[0].snippet.thumbnails.medium.url;
|
||||
}.bind(this))
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Bind to our table of entries.
|
||||
var tbody0 = new Vue({
|
||||
el: '#tbody-0',
|
||||
data: {
|
||||
entries: [""]
|
||||
},
|
||||
methods: {
|
||||
retrieve: function (event) {
|
||||
axios.get('/api/Channels')
|
||||
.then(function (response) {
|
||||
// Wipe out all old entries.
|
||||
this.entries = [];
|
||||
|
||||
// And fill it with all the retrieved entries.
|
||||
response.data.forEach(function (x) {
|
||||
if (x.description.length > maxcharstablefield) {
|
||||
x.description = x.description.substring(0, maxcharstablefield) + " ...";
|
||||
}
|
||||
this.entries.push(x);
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Grid if images.
|
||||
var videostb = new Vue({
|
||||
el: '#videostb-0',
|
||||
data: {
|
||||
Videos: []
|
||||
},
|
||||
methods: {
|
||||
// Get new videos from the web api.
|
||||
retrieve: function (event) {
|
||||
axios.get('/api/Videos')
|
||||
.then(function (response) {
|
||||
// And fill it with all the retrieved entries.
|
||||
response.data.forEach(function (x) {
|
||||
// Trim description if needed.
|
||||
if (x.description.length > maxcharstablefield) {
|
||||
x.description = x.description.substring(0, maxcharstablefield) + " ...";
|
||||
}
|
||||
|
||||
// Add it to our array
|
||||
this.Videos.push(x);
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
tbody0.retrieve();
|
||||
videostb.retrieve();
|
||||
});
|
39
YTManager/wwwroot/index.css
Normal file
39
YTManager/wwwroot/index.css
Normal file
@ -0,0 +1,39 @@
|
||||
body {
|
||||
max-width: 1280px;
|
||||
margin: auto;
|
||||
line-height: 1.6;
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
background-color: #F8F8F8;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
#addnewchannelform {
|
||||
width: inherit;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#vidholder-0 {
|
||||
|
||||
}
|
||||
|
||||
.tinytext10px{ font-size: 10px; }
|
||||
.tinytext12px{ font-size: 12px; }
|
||||
.tinytext14px{ font-size: 14px; }
|
||||
.tinytext16px{ font-size: 16px; }
|
||||
.tinytext18px{ font-size: 18px; }
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity 0.75s;
|
||||
}
|
||||
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
47
YTManager/wwwroot/index.html
Normal file
47
YTManager/wwwroot/index.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>YT Manager</title>
|
||||
|
||||
<!-- Compressed CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="index.css">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
|
||||
<hr />
|
||||
<h2>Most Recent Videos</h2>
|
||||
<div v-cloak id="vidholder-0">
|
||||
<div class="grid-x grid-margin-x imgrow" v-for="row in Videos.Rows">
|
||||
<div class="card medium-2 large-1 cell" v-for="video in row.Columns">
|
||||
<div class="card-divider">
|
||||
<h6>{{video.title}}</h6>
|
||||
</div>
|
||||
<img :src="video.thumbnailURL">
|
||||
<div class="card-section">
|
||||
<p class="tinytext14px">{{video.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Compressed JavaScript -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js"></script>
|
||||
|
||||
<!-- For doing REST based API stuff. -->
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
|
||||
<!-- Some icons -->
|
||||
<script src="https://use.fontawesome.com/91af8ab4ba.js"></script>
|
||||
|
||||
<!-- Good ole Vue :D -->
|
||||
<script src="https://unpkg.com/vue"></script>
|
||||
|
||||
<!-- All of my custom JS. Put here so body loads before Vue based stuff loads/attempts to bind. -->
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
73
YTManager/wwwroot/index.js
Normal file
73
YTManager/wwwroot/index.js
Normal file
@ -0,0 +1,73 @@
|
||||
$(document).foundation();
|
||||
|
||||
// How many chars at most for the description.
|
||||
var maxcharsdescriptionfield = 100;
|
||||
|
||||
// Grid if images.
|
||||
var vidholder = new Vue({
|
||||
el: '#vidholder-0',
|
||||
data: {
|
||||
Videos: {
|
||||
Rows: [
|
||||
/*{
|
||||
Columns: [
|
||||
{
|
||||
"title": "0",
|
||||
"description": "",
|
||||
"yTVideoID": "",
|
||||
"thumbnailURL": "",
|
||||
"uploaded": "",
|
||||
"yTChannelID": ""
|
||||
}
|
||||
]
|
||||
}*/
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Get new videos from the web api.
|
||||
retrieve: function (event) {
|
||||
// Wipe out all old entries.
|
||||
this.Videos.Rows = [];
|
||||
|
||||
axios.get('/api/Videos')
|
||||
.then(function (response) {
|
||||
// Dimension counters and limits.
|
||||
var maxcols = 4;
|
||||
var colcounter = 0;
|
||||
var rowcounter = 0;
|
||||
|
||||
// And fill it with all the retrieved entries.
|
||||
response.data.forEach(function (x) {
|
||||
// Trim description if needed.
|
||||
if (x.description.length > maxcharsdescriptionfield) {
|
||||
x.description = x.description.substring(0, maxcharsdescriptionfield) + " ...";
|
||||
}
|
||||
|
||||
// Handle creation of a new row.
|
||||
if (colcounter == 0) {
|
||||
Vue.set(this.Videos.Rows, rowcounter, []);
|
||||
Vue.set(this.Videos.Rows[rowcounter], 'Columns', []);
|
||||
}
|
||||
|
||||
// Add it to our array
|
||||
this.Videos.Rows[rowcounter].Columns.push(x);
|
||||
|
||||
// So we get it all in groups of maxcols.
|
||||
colcounter = colcounter + 1;
|
||||
if (colcounter == maxcols) {
|
||||
colcounter = 0;
|
||||
rowcounter = rowcounter + 1;
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
vidholder.retrieve();
|
||||
});
|
Loading…
Reference in New Issue
Block a user