Mixcloud playlists working

This commit is contained in:
Fergal Moran
2018-05-12 21:26:13 +01:00
parent 12705b6a5a
commit ad70d00cf9
16 changed files with 2445 additions and 130 deletions

View File

@@ -118,7 +118,8 @@ namespace PodNoms.Api.Controllers {
return result;
}
}
} else if (status == AudioType.Playlist && YouTubeParser.ValidateUrl(item.SourceUrl)) {
} else if ((status == AudioType.Playlist && YouTubeParser.ValidateUrl(item.SourceUrl))
|| MixcloudParser.ValidateUrl(item.SourceUrl)) {
entry.ProcessingStatus = ProcessingStatus.Deferred;
var result = _mapper.Map<PodcastEntry, PodcastEntryViewModel>(entry);
return Accepted(result);

View File

@@ -0,0 +1,512 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PodNoms.Api.Persistence;
namespace PodNoms.Api.Migrations
{
[DbContext(typeof(PodNomsDbContext))]
[Migration("20180512180833_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.0-rc1-32029")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("PodNoms.Api.Models.ChatMessage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("FromUserId");
b.Property<string>("Message");
b.Property<DateTime?>("MessageSeen");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("ToUserId");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("FromUserId");
b.HasIndex("ToUserId");
b.ToTable("ChatMessages");
});
modelBuilder.Entity("PodNoms.Api.Models.ParsedPlaylistItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<bool>("IsProcessed");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int>("PlaylistId");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.Property<string>("VideoId");
b.Property<string>("VideoType");
b.HasKey("Id");
b.HasIndex("PlaylistId");
b.ToTable("ParsedPlaylistItems");
});
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int>("PodcastId");
b.Property<string>("SourceUrl");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("PodcastId");
b.ToTable("Playlists");
});
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AppUserId");
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Description");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("Slug");
b.Property<string>("TemporaryImageUrl");
b.Property<string>("Title");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("AppUserId");
b.ToTable("Podcasts");
});
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("AudioFileSize");
b.Property<float>("AudioLength");
b.Property<string>("AudioUrl");
b.Property<string>("Author");
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Description");
b.Property<string>("ImageUrl");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int?>("PlaylistId");
b.Property<int>("PodcastId");
b.Property<bool>("Processed");
b.Property<string>("ProcessingPayload");
b.Property<int>("ProcessingStatus");
b.Property<string>("SourceUrl");
b.Property<string>("Title");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("PlaylistId");
b.HasIndex("PodcastId");
b.ToTable("PodcastEntries");
});
modelBuilder.Entity("PodNoms.Api.Models.ServerConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Key");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("ServerConfig","admin");
});
modelBuilder.Entity("PodNoms.Api.Services.Auth.ApplicationUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<long?>("FacebookId");
b.Property<string>("FirstName");
b.Property<string>("LastName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("PictureUrl");
b.Property<string>("SecurityStamp");
b.Property<string>("Slug");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.ChatMessage", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "FromUser")
.WithMany()
.HasForeignKey("FromUserId");
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "ToUser")
.WithMany()
.HasForeignKey("ToUserId");
});
modelBuilder.Entity("PodNoms.Api.Models.ParsedPlaylistItem", b =>
{
b.HasOne("PodNoms.Api.Models.Playlist", "Playlist")
.WithMany("ParsedPlaylistItems")
.HasForeignKey("PlaylistId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
{
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
.WithMany()
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "AppUser")
.WithMany()
.HasForeignKey("AppUserId");
});
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
{
b.HasOne("PodNoms.Api.Models.Playlist")
.WithMany("PodcastEntries")
.HasForeignKey("PlaylistId");
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
.WithMany("PodcastEntries")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,451 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace PodNoms.Api.Migrations
{
public partial class Initial : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "admin");
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
PasswordHash = table.Column<string>(nullable: true),
SecurityStamp = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
TwoFactorEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
LockoutEnabled = table.Column<bool>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false),
FirstName = table.Column<string>(nullable: true),
LastName = table.Column<string>(nullable: true),
FacebookId = table.Column<long>(nullable: true),
PictureUrl = table.Column<string>(nullable: true),
Slug = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ServerConfig",
schema: "admin",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Uid = table.Column<string>(nullable: true),
NewId = table.Column<Guid>(nullable: false, defaultValueSql: "newsequentialid()"),
CreateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
UpdateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
Key = table.Column<string>(nullable: true),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ServerConfig", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
RoleId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
UserId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(nullable: false),
ProviderKey = table.Column<string>(nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
RoleId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
LoginProvider = table.Column<string>(nullable: false),
Name = table.Column<string>(nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ChatMessages",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Uid = table.Column<string>(nullable: true),
NewId = table.Column<Guid>(nullable: false, defaultValueSql: "newsequentialid()"),
CreateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
UpdateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
FromUserId = table.Column<string>(nullable: true),
ToUserId = table.Column<string>(nullable: true),
Message = table.Column<string>(nullable: true),
MessageSeen = table.Column<DateTime>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ChatMessages", x => x.Id);
table.ForeignKey(
name: "FK_ChatMessages_AspNetUsers_FromUserId",
column: x => x.FromUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_ChatMessages_AspNetUsers_ToUserId",
column: x => x.ToUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Podcasts",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Uid = table.Column<string>(nullable: true),
NewId = table.Column<Guid>(nullable: false, defaultValueSql: "newsequentialid()"),
CreateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
UpdateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
AppUserId = table.Column<string>(nullable: true),
Title = table.Column<string>(nullable: true),
Description = table.Column<string>(nullable: true),
Slug = table.Column<string>(nullable: true),
TemporaryImageUrl = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Podcasts", x => x.Id);
table.ForeignKey(
name: "FK_Podcasts_AspNetUsers_AppUserId",
column: x => x.AppUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Playlists",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Uid = table.Column<string>(nullable: true),
NewId = table.Column<Guid>(nullable: false, defaultValueSql: "newsequentialid()"),
CreateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
UpdateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
SourceUrl = table.Column<string>(nullable: true),
PodcastId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Playlists", x => x.Id);
table.ForeignKey(
name: "FK_Playlists_Podcasts_PodcastId",
column: x => x.PodcastId,
principalTable: "Podcasts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ParsedPlaylistItems",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Uid = table.Column<string>(nullable: true),
NewId = table.Column<Guid>(nullable: false, defaultValueSql: "newsequentialid()"),
CreateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
UpdateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
VideoId = table.Column<string>(nullable: true),
VideoType = table.Column<string>(nullable: true),
IsProcessed = table.Column<bool>(nullable: false),
PlaylistId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ParsedPlaylistItems", x => x.Id);
table.ForeignKey(
name: "FK_ParsedPlaylistItems_Playlists_PlaylistId",
column: x => x.PlaylistId,
principalTable: "Playlists",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PodcastEntries",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Uid = table.Column<string>(nullable: true),
NewId = table.Column<Guid>(nullable: false, defaultValueSql: "newsequentialid()"),
CreateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
UpdateDate = table.Column<DateTime>(nullable: false, defaultValueSql: "getdate()"),
Author = table.Column<string>(nullable: true),
Title = table.Column<string>(nullable: true),
Description = table.Column<string>(nullable: true),
SourceUrl = table.Column<string>(nullable: true),
AudioUrl = table.Column<string>(nullable: true),
AudioLength = table.Column<float>(nullable: false),
AudioFileSize = table.Column<long>(nullable: false),
ImageUrl = table.Column<string>(nullable: true),
ProcessingPayload = table.Column<string>(nullable: true),
ProcessingStatus = table.Column<int>(nullable: false),
Processed = table.Column<bool>(nullable: false),
PodcastId = table.Column<int>(nullable: false),
PlaylistId = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PodcastEntries", x => x.Id);
table.ForeignKey(
name: "FK_PodcastEntries_Playlists_PlaylistId",
column: x => x.PlaylistId,
principalTable: "Playlists",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_PodcastEntries_Podcasts_PodcastId",
column: x => x.PodcastId,
principalTable: "Podcasts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true,
filter: "[NormalizedName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true,
filter: "[NormalizedUserName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_ChatMessages_FromUserId",
table: "ChatMessages",
column: "FromUserId");
migrationBuilder.CreateIndex(
name: "IX_ChatMessages_ToUserId",
table: "ChatMessages",
column: "ToUserId");
migrationBuilder.CreateIndex(
name: "IX_ParsedPlaylistItems_PlaylistId",
table: "ParsedPlaylistItems",
column: "PlaylistId");
migrationBuilder.CreateIndex(
name: "IX_Playlists_PodcastId",
table: "Playlists",
column: "PodcastId");
migrationBuilder.CreateIndex(
name: "IX_PodcastEntries_PlaylistId",
table: "PodcastEntries",
column: "PlaylistId");
migrationBuilder.CreateIndex(
name: "IX_PodcastEntries_PodcastId",
table: "PodcastEntries",
column: "PodcastId");
migrationBuilder.CreateIndex(
name: "IX_Podcasts_AppUserId",
table: "Podcasts",
column: "AppUserId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "ChatMessages");
migrationBuilder.DropTable(
name: "ParsedPlaylistItems");
migrationBuilder.DropTable(
name: "PodcastEntries");
migrationBuilder.DropTable(
name: "ServerConfig",
schema: "admin");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "Playlists");
migrationBuilder.DropTable(
name: "Podcasts");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}

View File

@@ -0,0 +1,516 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PodNoms.Api.Persistence;
namespace PodNoms.Api.Migrations
{
[DbContext(typeof(PodNomsDbContext))]
[Migration("20180512181432_PK_OnParsedPlaylistItem")]
partial class PK_OnParsedPlaylistItem
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.0-rc1-32029")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("PodNoms.Api.Models.ChatMessage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("FromUserId");
b.Property<string>("Message");
b.Property<DateTime?>("MessageSeen");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("ToUserId");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("FromUserId");
b.HasIndex("ToUserId");
b.ToTable("ChatMessages");
});
modelBuilder.Entity("PodNoms.Api.Models.ParsedPlaylistItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<bool>("IsProcessed");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int>("PlaylistId");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.Property<string>("VideoId");
b.Property<string>("VideoType");
b.HasKey("Id");
b.HasIndex("PlaylistId");
b.HasIndex("VideoId", "PlaylistId")
.IsUnique()
.HasFilter("[VideoId] IS NOT NULL");
b.ToTable("ParsedPlaylistItems");
});
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int>("PodcastId");
b.Property<string>("SourceUrl");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("PodcastId");
b.ToTable("Playlists");
});
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AppUserId");
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Description");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("Slug");
b.Property<string>("TemporaryImageUrl");
b.Property<string>("Title");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("AppUserId");
b.ToTable("Podcasts");
});
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("AudioFileSize");
b.Property<float>("AudioLength");
b.Property<string>("AudioUrl");
b.Property<string>("Author");
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Description");
b.Property<string>("ImageUrl");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int?>("PlaylistId");
b.Property<int>("PodcastId");
b.Property<bool>("Processed");
b.Property<string>("ProcessingPayload");
b.Property<int>("ProcessingStatus");
b.Property<string>("SourceUrl");
b.Property<string>("Title");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("PlaylistId");
b.HasIndex("PodcastId");
b.ToTable("PodcastEntries");
});
modelBuilder.Entity("PodNoms.Api.Models.ServerConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Key");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("ServerConfig","admin");
});
modelBuilder.Entity("PodNoms.Api.Services.Auth.ApplicationUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<long?>("FacebookId");
b.Property<string>("FirstName");
b.Property<string>("LastName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("PictureUrl");
b.Property<string>("SecurityStamp");
b.Property<string>("Slug");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.ChatMessage", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "FromUser")
.WithMany()
.HasForeignKey("FromUserId");
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "ToUser")
.WithMany()
.HasForeignKey("ToUserId");
});
modelBuilder.Entity("PodNoms.Api.Models.ParsedPlaylistItem", b =>
{
b.HasOne("PodNoms.Api.Models.Playlist", "Playlist")
.WithMany("ParsedPlaylistItems")
.HasForeignKey("PlaylistId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
{
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
.WithMany()
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "AppUser")
.WithMany()
.HasForeignKey("AppUserId");
});
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
{
b.HasOne("PodNoms.Api.Models.Playlist")
.WithMany("PodcastEntries")
.HasForeignKey("PlaylistId");
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
.WithMany("PodcastEntries")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,38 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace PodNoms.Api.Migrations
{
public partial class PK_OnParsedPlaylistItem : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "VideoId",
table: "ParsedPlaylistItems",
nullable: true,
oldClrType: typeof(string),
oldNullable: true);
migrationBuilder.CreateIndex(
name: "IX_ParsedPlaylistItems_VideoId_PlaylistId",
table: "ParsedPlaylistItems",
columns: new[] { "VideoId", "PlaylistId" },
unique: true,
filter: "[VideoId] IS NOT NULL");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_ParsedPlaylistItems_VideoId_PlaylistId",
table: "ParsedPlaylistItems");
migrationBuilder.AlterColumn<string>(
name: "VideoId",
table: "ParsedPlaylistItems",
nullable: true,
oldClrType: typeof(string),
oldNullable: true);
}
}
}

View File

@@ -0,0 +1,514 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PodNoms.Api.Persistence;
namespace PodNoms.Api.Migrations
{
[DbContext(typeof(PodNomsDbContext))]
partial class PodNomsDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.0-rc1-32029")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider");
b.Property<string>("ProviderKey");
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider");
b.Property<string>("Name");
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("PodNoms.Api.Models.ChatMessage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("FromUserId");
b.Property<string>("Message");
b.Property<DateTime?>("MessageSeen");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("ToUserId");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("FromUserId");
b.HasIndex("ToUserId");
b.ToTable("ChatMessages");
});
modelBuilder.Entity("PodNoms.Api.Models.ParsedPlaylistItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<bool>("IsProcessed");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int>("PlaylistId");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.Property<string>("VideoId");
b.Property<string>("VideoType");
b.HasKey("Id");
b.HasIndex("PlaylistId");
b.HasIndex("VideoId", "PlaylistId")
.IsUnique()
.HasFilter("[VideoId] IS NOT NULL");
b.ToTable("ParsedPlaylistItems");
});
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int>("PodcastId");
b.Property<string>("SourceUrl");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("PodcastId");
b.ToTable("Playlists");
});
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AppUserId");
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Description");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("Slug");
b.Property<string>("TemporaryImageUrl");
b.Property<string>("Title");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("AppUserId");
b.ToTable("Podcasts");
});
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("AudioFileSize");
b.Property<float>("AudioLength");
b.Property<string>("AudioUrl");
b.Property<string>("Author");
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Description");
b.Property<string>("ImageUrl");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<int?>("PlaylistId");
b.Property<int>("PodcastId");
b.Property<bool>("Processed");
b.Property<string>("ProcessingPayload");
b.Property<int>("ProcessingStatus");
b.Property<string>("SourceUrl");
b.Property<string>("Title");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.HasKey("Id");
b.HasIndex("PlaylistId");
b.HasIndex("PodcastId");
b.ToTable("PodcastEntries");
});
modelBuilder.Entity("PodNoms.Api.Models.ServerConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("CreateDate")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("getdate()");
b.Property<string>("Key");
b.Property<Guid>("NewId")
.ValueGeneratedOnAdd()
.HasDefaultValueSql("newsequentialid()");
b.Property<string>("Uid");
b.Property<DateTime>("UpdateDate")
.ValueGeneratedOnAddOrUpdate()
.HasDefaultValueSql("getdate()");
b.Property<string>("Value");
b.HasKey("Id");
b.ToTable("ServerConfig","admin");
});
modelBuilder.Entity("PodNoms.Api.Services.Auth.ApplicationUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<long?>("FacebookId");
b.Property<string>("FirstName");
b.Property<string>("LastName");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("PictureUrl");
b.Property<string>("SecurityStamp");
b.Property<string>("Slug");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.ChatMessage", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "FromUser")
.WithMany()
.HasForeignKey("FromUserId");
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "ToUser")
.WithMany()
.HasForeignKey("ToUserId");
});
modelBuilder.Entity("PodNoms.Api.Models.ParsedPlaylistItem", b =>
{
b.HasOne("PodNoms.Api.Models.Playlist", "Playlist")
.WithMany("ParsedPlaylistItems")
.HasForeignKey("PlaylistId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.Playlist", b =>
{
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
.WithMany()
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("PodNoms.Api.Models.Podcast", b =>
{
b.HasOne("PodNoms.Api.Services.Auth.ApplicationUser", "AppUser")
.WithMany()
.HasForeignKey("AppUserId");
});
modelBuilder.Entity("PodNoms.Api.Models.PodcastEntry", b =>
{
b.HasOne("PodNoms.Api.Models.Playlist")
.WithMany("PodcastEntries")
.HasForeignKey("PlaylistId");
b.HasOne("PodNoms.Api.Models.Podcast", "Podcast")
.WithMany("PodcastEntries")
.HasForeignKey("PodcastId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -30,6 +30,9 @@ namespace PodNoms.Api.Persistence {
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ParsedPlaylistItem>()
.HasIndex(p => new { p.VideoId, p.PlaylistId })
.IsUnique(true);
foreach (var pb in __getColumn(modelBuilder, "CreateDate")) {
pb.ValueGeneratedOnAdd()

View File

@@ -13,7 +13,11 @@ namespace PodNoms.Api.Persistence {
public class PlaylistRepository : GenericRepository<Playlist>, IPlaylistRepository {
public PlaylistRepository(PodNomsDbContext context, ILogger<PlaylistRepository> logger) : base(context, logger) {
}
public new async Task<Playlist> GetAsync(int id) {
return await GetContext().Playlists
.Include(i => i.ParsedPlaylistItems)
.SingleOrDefaultAsync(i => i.Id == id);
}
public async Task<ParsedPlaylistItem> GetParsedItem(string itemId, int playlistId) {
return await GetContext().ParsedPlaylistItems
.Include(i => i.Playlist)

View File

@@ -10,7 +10,7 @@ namespace PodNoms.Api.Services.Jobs {
public static void BootstrapJobs() {
RecurringJob.AddOrUpdate<ClearOrphanAudioJob>(x => x.Execute(), Cron.Daily(1));
RecurringJob.AddOrUpdate<UpdateYouTubeDlJob>(x => x.Execute(), Cron.Daily(1, 30));
// BackgroundJob.Schedule<ProcessPlaylistsJob>(x => x.Execute(), TimeSpan.FromSeconds(1));
BackgroundJob.Schedule<ProcessPlaylistsJob>(x => x.Execute(3), TimeSpan.FromSeconds(1));
RecurringJob.AddOrUpdate<ProcessPlaylistsJob>(x => x.Execute(), Cron.Daily(2));
}
}

View File

@@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage;
namespace PodNoms.Api.Services.Jobs {
public class MutexAttribute : JobFilterAttribute, IElectStateFilter, IApplyStateFilter {
private static readonly TimeSpan DistributedLockTimeout = TimeSpan.FromMinutes(1);
private readonly string _resource;
public MutexAttribute(string resource) {
_resource = resource;
RetryInSeconds = 15;
}
public int RetryInSeconds { get; set; }
public int MaxAttempts { get; set; }
public void OnStateElection(ElectStateContext context) {
// We are intercepting transitions to the Processed state, that is performed by
// a worker just before processing a job. During the state election phase we can
// change the target state to another one, causing a worker not to process the
// backgorund job.
if (context.CandidateState.Name != ProcessingState.StateName ||
context.BackgroundJob.Job == null) {
return;
}
// This filter requires an extended set of storage operations. It's supported
// by all the official storages, and many of the community-based ones.
var storageConnection = context.Connection as JobStorageConnection;
if (storageConnection == null) {
throw new NotSupportedException("This version of storage doesn't support extended methods. Please try to update to the latest version.");
}
string blockedBy;
try {
// Distributed lock is needed here only to prevent a race condition, when another
// worker picks up a background job with the same resource between GET and SET
// operations.
// There will be no race condition, when two or more workers pick up background job
// with the same id, because state transitions are protected with distributed lock
// themselves.
using (AcquireDistributedSetLock(context.Connection, context.BackgroundJob.Job.Args)) {
// Resource set contains a background job id that acquired a mutex for the resource.
// We are getting only one element to see what background job blocked the invocation.
var range = storageConnection.GetRangeFromSet(
GetResourceKey(context.BackgroundJob.Job.Args),
0,
0);
blockedBy = range.Count > 0 ? range[0] : null;
// We should permit an invocation only when the set is empty, or if current background
// job is already owns a resource. This may happen, when the localTransaction succeeded,
// but outer transaction was failed.
if (blockedBy == null || blockedBy == context.BackgroundJob.Id) {
// We need to commit the changes inside a distributed lock, otherwise it's
// useless. So we create a local transaction instead of using the
// context.Transaction property.
var localTransaction = context.Connection.CreateWriteTransaction();
// Add the current background job identifier to a resource set. This means
// that resource is owned by the current background job. Identifier will be
// removed only on failed state, or in one of final states (succeeded or
// deleted).
localTransaction.AddToSet(GetResourceKey(context.BackgroundJob.Job.Args), context.BackgroundJob.Id);
localTransaction.Commit();
// Invocation is permitted, and we did all the required things.
return;
}
}
} catch (DistributedLockTimeoutException) {
// We weren't able to acquire a distributed lock within a specified window. This may
// be caused by network delays, storage outages or abandoned locks in some storages.
// Since it is required to expire abandoned locks after some time, we can simply
// postpone the invocation.
context.CandidateState = new ScheduledState(TimeSpan.FromSeconds(RetryInSeconds)) {
Reason = "Couldn't acquire a distributed lock for mutex: timeout exceeded"
};
return;
}
// Background job execution is blocked. We should change the target state either to
// the Scheduled or to the Deleted one, depending on current retry attempt number.
var currentAttempt = context.GetJobParameter<int>("MutexAttempt") + 1;
context.SetJobParameter("MutexAttempt", currentAttempt);
context.CandidateState = MaxAttempts == 0 || currentAttempt <= MaxAttempts
? CreateScheduledState(blockedBy, currentAttempt)
: CreateDeletedState(blockedBy);
}
public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction) {
if (context.BackgroundJob.Job == null) return;
if (context.OldStateName == ProcessingState.StateName) {
using (AcquireDistributedSetLock(context.Connection, context.BackgroundJob.Job.Args)) {
var localTransaction = context.Connection.CreateWriteTransaction();
localTransaction.RemoveFromSet(GetResourceKey(context.BackgroundJob.Job.Args), context.BackgroundJob.Id);
localTransaction.Commit();
}
}
}
public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction) {
}
private static DeletedState CreateDeletedState(string blockedBy) {
return new DeletedState {
Reason = $"Execution was blocked by background job {blockedBy}, all attempts exhausted"
};
}
private IState CreateScheduledState(string blockedBy, int currentAttempt) {
var reason = $"Execution is blocked by background job {blockedBy}, retry attempt: {currentAttempt}";
if (MaxAttempts > 0) {
reason += $"/{MaxAttempts}";
}
return new ScheduledState(TimeSpan.FromSeconds(RetryInSeconds)) {
Reason = reason
};
}
private IDisposable AcquireDistributedSetLock(IStorageConnection connection, IEnumerable<object> args) {
return connection.AcquireDistributedLock(GetDistributedLockKey(args), DistributedLockTimeout);
}
private string GetDistributedLockKey(IEnumerable<object> args) {
return $"extension:job-mutex:lock:{GetKeyFormat(args, _resource)}";
}
private string GetResourceKey(IEnumerable<object> args) {
return $"extension:job-mutex:set:{GetKeyFormat(args, _resource)}";
}
private static string GetKeyFormat(IEnumerable<object> args, string keyFormat) {
return String.Format(keyFormat, args.ToArray());
}
}
}

View File

@@ -33,16 +33,23 @@ namespace PodNoms.Api.Services.Jobs {
this._helpersSettings = _helpersSettings.Value;
this._logger = logger;
}
[Mutex("ProcessPlaylistItemJob")]
public async Task Execute() {
var items = await _playlistRepository.GetUnprocessedItems();
foreach (var item in items) {
await ExecuteForItem(item.VideoId, item.Playlist.Id);
}
}
[Mutex("ProcessPlaylistItemJob")]
public async Task ExecuteForItem(string itemId, int playlistId) {
var item = await _playlistRepository.GetParsedItem(itemId, playlistId);
if (item != null && !string.IsNullOrEmpty(item.VideoType) && item.VideoType.Equals("youtube")) {
var url = $"https://www.youtube.com/watch?v={item.VideoId}";
if (item != null && !string.IsNullOrEmpty(item.VideoType) &&
(item.VideoType.Equals("youtube") || item.VideoType.Equals("mixcloud"))) {
var url = item.VideoType.Equals("youtube") ? $"https://www.youtube.com/watch?v={item.VideoId}"
: item.VideoType.Equals("mixcloud") ? $"https://mixcloud.com/{item.VideoId}" : string.Empty;
if (string.IsNullOrEmpty(url)) {
_logger.LogError($"Unknown video type for ParsedItem: {itemId} - {playlistId}");
} else {
var downloader = new AudioDownloader(url, _helpersSettings.Downloader);
var info = downloader.GetInfo();
if (info == AudioType.Valid) {
@@ -78,3 +85,4 @@ namespace PodNoms.Api.Services.Jobs {
}
}
}
}

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -45,23 +46,24 @@ namespace PodNoms.Api.Services.Jobs {
}
}
public async Task Execute(int playlistId) {
try {
var playlist = await _playlistRepository.GetAsync(playlistId);
var resultList = new List<ParsedItemResult>();
var downloader = new AudioDownloader(playlist.SourceUrl, _helpersSettings.Downloader);
var info = downloader.GetInfo();
var id = ((PlaylistDownloadInfo)downloader.RawProperties).Id;
if (info == AudioType.Playlist && downloader.RawProperties is PlaylistDownloadInfo) {
var id = ((PlaylistDownloadInfo)downloader.RawProperties)?.Id;
if (!string.IsNullOrEmpty(id)) {
if (YouTubeParser.ValidateUrl(playlist.SourceUrl)) {
var searchTerm = (playlist.SourceUrl.Contains("/user/")) ? "forUsername" : "id";
resultList = await _youTubeParser.GetPlaylistEntriesForId(id);
//make sure the items are sorted in ascending date order
//so they will be processed in the order they were created
} else if (MixcloudParser.ValidateUrl(playlist.SourceUrl)) {
resultList = await _mixcloudParser.GetEntries(id);
}
resultList = await _mixcloudParser.GetEntries(playlist.SourceUrl);
}
if (resultList != null) {
//order in reverse so the newest item is added first
foreach (var item in resultList?.OrderBy(r => r.UploadDate)) {
if (!playlist.ParsedPlaylistItems.Any(p => p.VideoId == item.Id)) {
playlist.ParsedPlaylistItems.Add(new ParsedPlaylistItem {
@@ -69,10 +71,14 @@ namespace PodNoms.Api.Services.Jobs {
VideoType = item.VideoType
});
await _unitOfWork.CompleteAsync();
BackgroundJob.Enqueue<ProcessPlaylistItemJob>(service => service.ExecuteForItem(item.Id, playlist.Id));
}
}
}
}
} catch (Exception ex) {
_logger.LogError(ex.Message);
}
}
}
}

View File

@@ -17,5 +17,19 @@ namespace PodNoms.Api.Utils {
}
return file;
}
public static string UrlCombine(string url1, string url2) {
if (url1.Length == 0) {
return url2;
}
if (url2.Length == 0) {
return url1;
}
url1 = url1.TrimEnd('/', '\\');
url2 = url2.TrimStart('/', '\\');
return string.Format("{0}/{1}", url1, url2);
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace PodNoms.Api.Utils.RemoteParsers {
internal static class MixcloudJsonConverter {
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings {
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters = {
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}

View File

@@ -4,31 +4,58 @@ using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using System;
using Microsoft.Extensions.Logging;
using System.Security.Policy;
namespace PodNoms.Api.Utils.RemoteParsers {
public class MixcloudParser {
const string URL_REGEX = @"^(http(s)?:\/\/)?((w){3}.)?mixcloud?(\.com)?\/.+";
static string[] VALID_PATHS = new string[] {
"stream", "uploads", "favorites", "listens", "playlists"
};
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<MixcloudParser> _logger;
public MixcloudParser(IHttpClientFactory httpClientFactory) {
public MixcloudParser(IHttpClientFactory httpClientFactory, ILogger<MixcloudParser> logger) {
this._logger = logger;
this._httpClientFactory = httpClientFactory;
}
public static bool ValidateUrl(string url) {
var regex = new Regex(URL_REGEX);
var result = regex.Match(url);
return result.Success;
try {
var uri = new Uri(url);
if (uri.Host.EndsWith("mixcloud.com")) {
var path = uri.Segments[uri.Segments.Length - 1].ToString().TrimEnd(new[] { '/' });
return (VALID_PATHS.Any(path.Equals)) || uri.Segments.Length == 1;
}
public async Task<List<ParsedItemResult>> GetEntries(string identifier) {
} catch (Exception) {
}
return false;
}
public async Task<List<ParsedItemResult>> GetEntries(string url) {
try {
var path = new Uri(url).Segments.First(s => s != "/");
var newUrl = HttpUtils.UrlCombine(path, "cloudcasts");
var client = _httpClientFactory.CreateClient("mixcloud");
var result = await client.GetAsync(identifier);
var result = await client.GetAsync(newUrl);
if (result.IsSuccessStatusCode) {
var typed = JsonConvert.DeserializeObject<MixcloudResult>(await result.Content.ReadAsStringAsync());
return typed.data[0].cloudcasts.Select(c => new ParsedItemResult {
Id = c.key,
var body = await result.Content.ReadAsStringAsync();
Console.WriteLine(body);
System.IO.File.WriteAllText("/tmp/dump.json", body);
var typed = JsonConvert.DeserializeObject<Welcome>(body, MixcloudJsonConverter.Settings);
// var typed = JsonConvert.DeserializeObject<MixcloudResult>(body);
var data = typed.Data.OrderByDescending(p => p.UpdatedTime)
.Select(c => new ParsedItemResult {
Id = c.Key,
VideoType = "mixcloud",
UploadDate = c.updated_time
}).ToList();
UploadDate = c.UpdatedTime.DateTime
}).Take(10).ToList();
return data;
}
} catch (Exception ex) {
_logger.LogError($"Error parsing url: {url}");
_logger.LogError(ex.Message);
}
return null;
}

View File

@@ -1,75 +1,131 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PodNoms.Api.Utils.RemoteParsers {
public partial class Welcome {
[JsonProperty("paging")]
public Paging Paging { get; set; }
[JsonProperty("data")]
public Datum[] Data { get; set; }
public class Paging {
public string previous { get; set; }
public string next { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
public class Pictures {
public string medium { get; set; }
public string extra_large { get; set; }
public string large { get; set; }
public string medium_mobile { get; set; }
public string small { get; set; }
public string thumbnail { get; set; }
public partial class Datum {
[JsonProperty("tags")]
public Tag[] Tags { get; set; }
[JsonProperty("play_count")]
public long PlayCount { get; set; }
[JsonProperty("user")]
public User User { get; set; }
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("created_time")]
public DateTimeOffset CreatedTime { get; set; }
[JsonProperty("audio_length")]
public long AudioLength { get; set; }
[JsonProperty("slug")]
public string Slug { get; set; }
[JsonProperty("favorite_count")]
public long FavoriteCount { get; set; }
[JsonProperty("listener_count")]
public long ListenerCount { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("pictures")]
public Pictures Pictures { get; set; }
[JsonProperty("repost_count")]
public long RepostCount { get; set; }
[JsonProperty("updated_time")]
public DateTimeOffset UpdatedTime { get; set; }
[JsonProperty("comment_count")]
public long CommentCount { get; set; }
}
public class From {
public string url { get; set; }
public string username { get; set; }
public string name { get; set; }
public string key { get; set; }
public Pictures pictures { get; set; }
public partial class Pictures {
[JsonProperty("medium")]
public string Medium { get; set; }
[JsonProperty("768wx768h", NullValueHandling = NullValueHandling.Ignore)]
public string The768Wx768H { get; set; }
[JsonProperty("320wx320h")]
public string The320Wx320H { get; set; }
[JsonProperty("extra_large")]
public string ExtraLarge { get; set; }
[JsonProperty("large")]
public string Large { get; set; }
[JsonProperty("640wx640h")]
public string The640Wx640H { get; set; }
[JsonProperty("medium_mobile")]
public string MediumMobile { get; set; }
[JsonProperty("small")]
public string Small { get; set; }
[JsonProperty("1024wx1024h", NullValueHandling = NullValueHandling.Ignore)]
public string The1024Wx1024H { get; set; }
[JsonProperty("thumbnail")]
public string Thumbnail { get; set; }
}
public class Tag {
public string url { get; set; }
public string name { get; set; }
public string key { get; set; }
public partial class Tag {
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("key")]
public string Key { get; set; }
}
public class User {
public string url { get; set; }
public string username { get; set; }
public string name { get; set; }
public string key { get; set; }
public partial class User {
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("username")]
public string Username { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("pictures")]
public Pictures Pictures { get; set; }
}
public class Cloudcast {
public IList<Tag> tags { get; set; }
public int play_count { get; set; }
public User user { get; set; }
public string key { get; set; }
public DateTime created_time { get; set; }
public int audio_length { get; set; }
public string slug { get; set; }
public int favorite_count { get; set; }
public int listener_count { get; set; }
public string name { get; set; }
public string url { get; set; }
public int repost_count { get; set; }
public DateTime updated_time { get; set; }
public int comment_count { get; set; }
}
public partial class Paging {
[JsonProperty("previous")]
public string Previous { get; set; }
public class Datum {
public From from { get; set; }
public string title { get; set; }
public string url { get; set; }
public string key { get; set; }
public DateTime created_time { get; set; }
public IList<Cloudcast> cloudcasts { get; set; }
public string type { get; set; }
[JsonProperty("next")]
public string Next { get; set; }
}
public class MixcloudResult {
public Paging paging { get; set; }
public IList<Datum> data { get; set; }
public string name { get; set; }
}
}