diff --git a/Kyoo.Common/Controllers/IPlugin.cs b/Kyoo.Common/Controllers/IPlugin.cs index c535992d..3201df83 100644 --- a/Kyoo.Common/Controllers/IPlugin.cs +++ b/Kyoo.Common/Controllers/IPlugin.cs @@ -72,6 +72,13 @@ namespace Kyoo.Controllers /// /// The Asp.Net application builder. On most case it is not needed but you can use it to add asp net functionalities. void ConfigureAspNet(IApplicationBuilder app) {} + + /// + /// An optional function to execute and initialize your plugin. + /// It can be used to initialize a database connection, fill initial data or anything. + /// + /// A service provider to request services + void Initialize(IServiceProvider provider) {} } /// diff --git a/Kyoo.Common/Controllers/ITask.cs b/Kyoo.Common/Controllers/ITask.cs index 6ac064e9..75277dd2 100644 --- a/Kyoo.Common/Controllers/ITask.cs +++ b/Kyoo.Common/Controllers/ITask.cs @@ -151,7 +151,7 @@ namespace Kyoo.Controllers public string HelpMessage { get; } /// - /// Should this task be automatically runned at app startup? + /// Should this task be automatically run at app startup? /// public bool RunOnStartup { get; } @@ -165,7 +165,7 @@ namespace Kyoo.Controllers /// Start this task. /// /// The list of parameters. - /// A token to request the task's cancelation. + /// A token to request the task's cancellation. /// If this task is not cancelled quickly, it might be killed by the runner. /// /// Your task can have any service as a public field and use the , diff --git a/Kyoo.Common/Controllers/ITaskManager.cs b/Kyoo.Common/Controllers/ITaskManager.cs index 5a0710c6..392355d3 100644 --- a/Kyoo.Common/Controllers/ITaskManager.cs +++ b/Kyoo.Common/Controllers/ITaskManager.cs @@ -30,10 +30,5 @@ namespace Kyoo.Controllers /// /// A list of every tasks that this instance know. ICollection GetAllTasks(); - - /// - /// Reload tasks and run startup tasks. - /// - void ReloadTasks(); } } \ No newline at end of file diff --git a/Kyoo.Common/Models/Attributes/InjectedAttribute.cs b/Kyoo.Common/Models/Attributes/InjectedAttribute.cs index af76eb88..1e9a8ece 100644 --- a/Kyoo.Common/Models/Attributes/InjectedAttribute.cs +++ b/Kyoo.Common/Models/Attributes/InjectedAttribute.cs @@ -1,4 +1,5 @@ using System; +using JetBrains.Annotations; using Kyoo.Controllers; namespace Kyoo.Models.Attributes @@ -10,5 +11,6 @@ namespace Kyoo.Models.Attributes /// It should only be used on and will be injected before calling /// [AttributeUsage(AttributeTargets.Property)] + [MeansImplicitUse(ImplicitUseKindFlags.Assign)] public class InjectedAttribute : Attribute { } } \ No newline at end of file diff --git a/Kyoo.Postgresql/Kyoo.Postgresql.csproj b/Kyoo.Postgresql/Kyoo.Postgresql.csproj index 10887b80..56479e95 100644 --- a/Kyoo.Postgresql/Kyoo.Postgresql.csproj +++ b/Kyoo.Postgresql/Kyoo.Postgresql.csproj @@ -16,17 +16,17 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - all - false - all - false diff --git a/Kyoo.Postgresql/Migrations/20210505182627_Initial.Designer.cs b/Kyoo.Postgresql/Migrations/20210505182627_Initial.Designer.cs new file mode 100644 index 00000000..20ac842f --- /dev/null +++ b/Kyoo.Postgresql/Migrations/20210505182627_Initial.Designer.cs @@ -0,0 +1,786 @@ +// +using System; +using Kyoo.Models; +using Kyoo.Postgresql; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Kyoo.Postgresql.Migrations +{ + [DbContext(typeof(PostgresContext))] + [Migration("20210505182627_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasPostgresEnum(null, "item_type", new[] { "show", "movie", "collection" }) + .HasPostgresEnum(null, "status", new[] { "finished", "airing", "planned", "unknown" }) + .HasPostgresEnum(null, "stream_type", new[] { "unknown", "video", "audio", "subtitle", "attachment" }) + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.5") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + modelBuilder.Entity("Kyoo.Models.Collection", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Overview") + .HasColumnType("text"); + + b.Property("Poster") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Collections"); + }); + + modelBuilder.Entity("Kyoo.Models.Episode", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AbsoluteNumber") + .HasColumnType("integer"); + + b.Property("EpisodeNumber") + .HasColumnType("integer"); + + b.Property("Overview") + .HasColumnType("text"); + + b.Property("Path") + .HasColumnType("text"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Runtime") + .HasColumnType("integer"); + + b.Property("SeasonID") + .HasColumnType("integer"); + + b.Property("SeasonNumber") + .HasColumnType("integer"); + + b.Property("ShowID") + .HasColumnType("integer"); + + b.Property("Thumb") + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("SeasonID"); + + b.HasIndex("ShowID", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") + .IsUnique(); + + b.ToTable("Episodes"); + }); + + modelBuilder.Entity("Kyoo.Models.Genre", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Genres"); + }); + + modelBuilder.Entity("Kyoo.Models.Library", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Paths") + .HasColumnType("text[]"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Libraries"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.MetadataID", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("DataID") + .HasColumnType("text"); + + b.Property("EpisodeID") + .HasColumnType("integer"); + + b.Property("Link") + .HasColumnType("text"); + + b.Property("PeopleID") + .HasColumnType("integer"); + + b.Property("ProviderID") + .HasColumnType("integer"); + + b.Property("SeasonID") + .HasColumnType("integer"); + + b.Property("ShowID") + .HasColumnType("integer"); + + b.HasKey("ID"); + + b.HasIndex("EpisodeID"); + + b.HasIndex("PeopleID"); + + b.HasIndex("ProviderID"); + + b.HasIndex("SeasonID"); + + b.HasIndex("ShowID"); + + b.ToTable("MetadataIds"); + }); + + modelBuilder.Entity("Kyoo.Models.People", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Poster") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("People"); + }); + + modelBuilder.Entity("Kyoo.Models.PeopleRole", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("PeopleID") + .HasColumnType("integer"); + + b.Property("Role") + .HasColumnType("text"); + + b.Property("ShowID") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("PeopleID"); + + b.HasIndex("ShowID"); + + b.ToTable("PeopleRoles"); + }); + + modelBuilder.Entity("Kyoo.Models.Provider", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Logo") + .HasColumnType("text"); + + b.Property("LogoExtension") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Providers"); + }); + + modelBuilder.Entity("Kyoo.Models.Season", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Overview") + .HasColumnType("text"); + + b.Property("Poster") + .HasColumnType("text"); + + b.Property("SeasonNumber") + .HasColumnType("integer"); + + b.Property("ShowID") + .HasColumnType("integer"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("ID"); + + b.HasIndex("ShowID", "SeasonNumber") + .IsUnique(); + + b.ToTable("Seasons"); + }); + + modelBuilder.Entity("Kyoo.Models.Show", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Aliases") + .HasColumnType("text[]"); + + b.Property("Backdrop") + .HasColumnType("text"); + + b.Property("EndYear") + .HasColumnType("integer"); + + b.Property("IsMovie") + .HasColumnType("boolean"); + + b.Property("Logo") + .HasColumnType("text"); + + b.Property("Overview") + .HasColumnType("text"); + + b.Property("Path") + .HasColumnType("text"); + + b.Property("Poster") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartYear") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("status"); + + b.Property("StudioID") + .HasColumnType("integer"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("TrailerUrl") + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StudioID"); + + b.ToTable("Shows"); + }); + + modelBuilder.Entity("Kyoo.Models.Studio", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Studios"); + }); + + modelBuilder.Entity("Kyoo.Models.Track", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Codec") + .HasColumnType("text"); + + b.Property("EpisodeID") + .HasColumnType("integer"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsExternal") + .HasColumnType("boolean"); + + b.Property("IsForced") + .HasColumnType("boolean"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("Path") + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("TrackIndex") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("stream_type"); + + b.HasKey("ID"); + + b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced") + .IsUnique(); + + b.ToTable("Tracks"); + }); + + modelBuilder.Entity("Kyoo.Models.Episode", b => + { + b.HasOne("Kyoo.Models.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonID"); + + b.HasOne("Kyoo.Models.Show", "Show") + .WithMany("Episodes") + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Collection", "First") + .WithMany("ShowLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Show", "Second") + .WithMany("CollectionLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Library", "First") + .WithMany("CollectionLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Collection", "Second") + .WithMany("LibraryLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Library", "First") + .WithMany("ProviderLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Provider", "Second") + .WithMany("LibraryLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Library", "First") + .WithMany("ShowLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Show", "Second") + .WithMany("LibraryLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Show", "First") + .WithMany("GenreLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Genre", "Second") + .WithMany("ShowLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.MetadataID", b => + { + b.HasOne("Kyoo.Models.Episode", "Episode") + .WithMany("ExternalIDs") + .HasForeignKey("EpisodeID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Kyoo.Models.People", "People") + .WithMany("ExternalIDs") + .HasForeignKey("PeopleID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Kyoo.Models.Provider", "Provider") + .WithMany("MetadataLinks") + .HasForeignKey("ProviderID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Season", "Season") + .WithMany("ExternalIDs") + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Kyoo.Models.Show", "Show") + .WithMany("ExternalIDs") + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Episode"); + + b.Navigation("People"); + + b.Navigation("Provider"); + + b.Navigation("Season"); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("Kyoo.Models.PeopleRole", b => + { + b.HasOne("Kyoo.Models.People", "People") + .WithMany("Roles") + .HasForeignKey("PeopleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Show", "Show") + .WithMany("People") + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("People"); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("Kyoo.Models.Season", b => + { + b.HasOne("Kyoo.Models.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("Kyoo.Models.Show", b => + { + b.HasOne("Kyoo.Models.Studio", "Studio") + .WithMany("Shows") + .HasForeignKey("StudioID"); + + b.Navigation("Studio"); + }); + + modelBuilder.Entity("Kyoo.Models.Track", b => + { + b.HasOne("Kyoo.Models.Episode", "Episode") + .WithMany("Tracks") + .HasForeignKey("EpisodeID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("Kyoo.Models.Collection", b => + { + b.Navigation("LibraryLinks"); + + b.Navigation("ShowLinks"); + }); + + modelBuilder.Entity("Kyoo.Models.Episode", b => + { + b.Navigation("ExternalIDs"); + + b.Navigation("Tracks"); + }); + + modelBuilder.Entity("Kyoo.Models.Genre", b => + { + b.Navigation("ShowLinks"); + }); + + modelBuilder.Entity("Kyoo.Models.Library", b => + { + b.Navigation("CollectionLinks"); + + b.Navigation("ProviderLinks"); + + b.Navigation("ShowLinks"); + }); + + modelBuilder.Entity("Kyoo.Models.People", b => + { + b.Navigation("ExternalIDs"); + + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Kyoo.Models.Provider", b => + { + b.Navigation("LibraryLinks"); + + b.Navigation("MetadataLinks"); + }); + + modelBuilder.Entity("Kyoo.Models.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("ExternalIDs"); + }); + + modelBuilder.Entity("Kyoo.Models.Show", b => + { + b.Navigation("CollectionLinks"); + + b.Navigation("Episodes"); + + b.Navigation("ExternalIDs"); + + b.Navigation("GenreLinks"); + + b.Navigation("LibraryLinks"); + + b.Navigation("People"); + + b.Navigation("Seasons"); + }); + + modelBuilder.Entity("Kyoo.Models.Studio", b => + { + b.Navigation("Shows"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Kyoo.Postgresql/Migrations/20210505182627_Initial.cs b/Kyoo.Postgresql/Migrations/20210505182627_Initial.cs new file mode 100644 index 00000000..eacd436d --- /dev/null +++ b/Kyoo.Postgresql/Migrations/20210505182627_Initial.cs @@ -0,0 +1,608 @@ +using System; +using Kyoo.Models; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Kyoo.Postgresql.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:item_type", "show,movie,collection") + .Annotation("Npgsql:Enum:status", "finished,airing,planned,unknown") + .Annotation("Npgsql:Enum:stream_type", "unknown,video,audio,subtitle,attachment"); + + migrationBuilder.CreateTable( + name: "Collections", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Slug = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + Poster = table.Column(type: "text", nullable: true), + Overview = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Collections", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "Genres", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Slug = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Genres", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "Libraries", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Slug = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + Paths = table.Column(type: "text[]", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Libraries", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "People", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Slug = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + Poster = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_People", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "Providers", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Slug = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true), + Logo = table.Column(type: "text", nullable: true), + LogoExtension = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Providers", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "Studios", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Slug = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Studios", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "Link", + columns: table => new + { + FirstID = table.Column(type: "integer", nullable: false), + SecondID = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Link", x => new { x.FirstID, x.SecondID }); + table.ForeignKey( + name: "FK_Link_Collections_SecondID", + column: x => x.SecondID, + principalTable: "Collections", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Link_Libraries_FirstID", + column: x => x.FirstID, + principalTable: "Libraries", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Link", + columns: table => new + { + FirstID = table.Column(type: "integer", nullable: false), + SecondID = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Link", x => new { x.FirstID, x.SecondID }); + table.ForeignKey( + name: "FK_Link_Libraries_FirstID", + column: x => x.FirstID, + principalTable: "Libraries", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Link_Providers_SecondID", + column: x => x.SecondID, + principalTable: "Providers", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Shows", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Slug = table.Column(type: "text", nullable: false), + Title = table.Column(type: "text", nullable: true), + Aliases = table.Column(type: "text[]", nullable: true), + Path = table.Column(type: "text", nullable: true), + Overview = table.Column(type: "text", nullable: true), + Status = table.Column(type: "status", nullable: true), + TrailerUrl = table.Column(type: "text", nullable: true), + StartYear = table.Column(type: "integer", nullable: true), + EndYear = table.Column(type: "integer", nullable: true), + Poster = table.Column(type: "text", nullable: true), + Logo = table.Column(type: "text", nullable: true), + Backdrop = table.Column(type: "text", nullable: true), + IsMovie = table.Column(type: "boolean", nullable: false), + StudioID = table.Column(type: "integer", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Shows", x => x.ID); + table.ForeignKey( + name: "FK_Shows_Studios_StudioID", + column: x => x.StudioID, + principalTable: "Studios", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Link", + columns: table => new + { + FirstID = table.Column(type: "integer", nullable: false), + SecondID = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Link", x => new { x.FirstID, x.SecondID }); + table.ForeignKey( + name: "FK_Link_Collections_FirstID", + column: x => x.FirstID, + principalTable: "Collections", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Link_Shows_SecondID", + column: x => x.SecondID, + principalTable: "Shows", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Link", + columns: table => new + { + FirstID = table.Column(type: "integer", nullable: false), + SecondID = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Link", x => new { x.FirstID, x.SecondID }); + table.ForeignKey( + name: "FK_Link_Libraries_FirstID", + column: x => x.FirstID, + principalTable: "Libraries", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Link_Shows_SecondID", + column: x => x.SecondID, + principalTable: "Shows", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Link", + columns: table => new + { + FirstID = table.Column(type: "integer", nullable: false), + SecondID = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Link", x => new { x.FirstID, x.SecondID }); + table.ForeignKey( + name: "FK_Link_Genres_SecondID", + column: x => x.SecondID, + principalTable: "Genres", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Link_Shows_FirstID", + column: x => x.FirstID, + principalTable: "Shows", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PeopleRoles", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + PeopleID = table.Column(type: "integer", nullable: false), + ShowID = table.Column(type: "integer", nullable: false), + Role = table.Column(type: "text", nullable: true), + Type = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PeopleRoles", x => x.ID); + table.ForeignKey( + name: "FK_PeopleRoles_People_PeopleID", + column: x => x.PeopleID, + principalTable: "People", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_PeopleRoles_Shows_ShowID", + column: x => x.ShowID, + principalTable: "Shows", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Seasons", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ShowID = table.Column(type: "integer", nullable: false), + SeasonNumber = table.Column(type: "integer", nullable: false), + Title = table.Column(type: "text", nullable: true), + Overview = table.Column(type: "text", nullable: true), + Year = table.Column(type: "integer", nullable: true), + Poster = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Seasons", x => x.ID); + table.ForeignKey( + name: "FK_Seasons_Shows_ShowID", + column: x => x.ShowID, + principalTable: "Shows", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Episodes", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ShowID = table.Column(type: "integer", nullable: false), + SeasonID = table.Column(type: "integer", nullable: true), + SeasonNumber = table.Column(type: "integer", nullable: false), + EpisodeNumber = table.Column(type: "integer", nullable: false), + AbsoluteNumber = table.Column(type: "integer", nullable: false), + Path = table.Column(type: "text", nullable: true), + Thumb = table.Column(type: "text", nullable: true), + Title = table.Column(type: "text", nullable: true), + Overview = table.Column(type: "text", nullable: true), + ReleaseDate = table.Column(type: "timestamp without time zone", nullable: true), + Runtime = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Episodes", x => x.ID); + table.ForeignKey( + name: "FK_Episodes_Seasons_SeasonID", + column: x => x.SeasonID, + principalTable: "Seasons", + principalColumn: "ID", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Episodes_Shows_ShowID", + column: x => x.ShowID, + principalTable: "Shows", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "MetadataIds", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ProviderID = table.Column(type: "integer", nullable: false), + ShowID = table.Column(type: "integer", nullable: true), + EpisodeID = table.Column(type: "integer", nullable: true), + SeasonID = table.Column(type: "integer", nullable: true), + PeopleID = table.Column(type: "integer", nullable: true), + DataID = table.Column(type: "text", nullable: true), + Link = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MetadataIds", x => x.ID); + table.ForeignKey( + name: "FK_MetadataIds_Episodes_EpisodeID", + column: x => x.EpisodeID, + principalTable: "Episodes", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MetadataIds_People_PeopleID", + column: x => x.PeopleID, + principalTable: "People", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MetadataIds_Providers_ProviderID", + column: x => x.ProviderID, + principalTable: "Providers", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MetadataIds_Seasons_SeasonID", + column: x => x.SeasonID, + principalTable: "Seasons", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MetadataIds_Shows_ShowID", + column: x => x.ShowID, + principalTable: "Shows", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Tracks", + columns: table => new + { + ID = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + EpisodeID = table.Column(type: "integer", nullable: false), + TrackIndex = table.Column(type: "integer", nullable: false), + IsDefault = table.Column(type: "boolean", nullable: false), + IsForced = table.Column(type: "boolean", nullable: false), + IsExternal = table.Column(type: "boolean", nullable: false), + Title = table.Column(type: "text", nullable: true), + Language = table.Column(type: "text", nullable: true), + Codec = table.Column(type: "text", nullable: true), + Path = table.Column(type: "text", nullable: true), + Type = table.Column(type: "stream_type", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Tracks", x => x.ID); + table.ForeignKey( + name: "FK_Tracks_Episodes_EpisodeID", + column: x => x.EpisodeID, + principalTable: "Episodes", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Collections_Slug", + table: "Collections", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Episodes_SeasonID", + table: "Episodes", + column: "SeasonID"); + + migrationBuilder.CreateIndex( + name: "IX_Episodes_ShowID_SeasonNumber_EpisodeNumber_AbsoluteNumber", + table: "Episodes", + columns: new[] { "ShowID", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Genres_Slug", + table: "Genres", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Libraries_Slug", + table: "Libraries", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Link_SecondID", + table: "Link", + column: "SecondID"); + + migrationBuilder.CreateIndex( + name: "IX_Link_SecondID", + table: "Link", + column: "SecondID"); + + migrationBuilder.CreateIndex( + name: "IX_Link_SecondID", + table: "Link", + column: "SecondID"); + + migrationBuilder.CreateIndex( + name: "IX_Link_SecondID", + table: "Link", + column: "SecondID"); + + migrationBuilder.CreateIndex( + name: "IX_Link_SecondID", + table: "Link", + column: "SecondID"); + + migrationBuilder.CreateIndex( + name: "IX_MetadataIds_EpisodeID", + table: "MetadataIds", + column: "EpisodeID"); + + migrationBuilder.CreateIndex( + name: "IX_MetadataIds_PeopleID", + table: "MetadataIds", + column: "PeopleID"); + + migrationBuilder.CreateIndex( + name: "IX_MetadataIds_ProviderID", + table: "MetadataIds", + column: "ProviderID"); + + migrationBuilder.CreateIndex( + name: "IX_MetadataIds_SeasonID", + table: "MetadataIds", + column: "SeasonID"); + + migrationBuilder.CreateIndex( + name: "IX_MetadataIds_ShowID", + table: "MetadataIds", + column: "ShowID"); + + migrationBuilder.CreateIndex( + name: "IX_People_Slug", + table: "People", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_PeopleRoles_PeopleID", + table: "PeopleRoles", + column: "PeopleID"); + + migrationBuilder.CreateIndex( + name: "IX_PeopleRoles_ShowID", + table: "PeopleRoles", + column: "ShowID"); + + migrationBuilder.CreateIndex( + name: "IX_Providers_Slug", + table: "Providers", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Seasons_ShowID_SeasonNumber", + table: "Seasons", + columns: new[] { "ShowID", "SeasonNumber" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Shows_Slug", + table: "Shows", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Shows_StudioID", + table: "Shows", + column: "StudioID"); + + migrationBuilder.CreateIndex( + name: "IX_Studios_Slug", + table: "Studios", + column: "Slug", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Tracks_EpisodeID_Type_Language_TrackIndex_IsForced", + table: "Tracks", + columns: new[] { "EpisodeID", "Type", "Language", "TrackIndex", "IsForced" }, + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Link"); + + migrationBuilder.DropTable( + name: "Link"); + + migrationBuilder.DropTable( + name: "Link"); + + migrationBuilder.DropTable( + name: "Link"); + + migrationBuilder.DropTable( + name: "Link"); + + migrationBuilder.DropTable( + name: "MetadataIds"); + + migrationBuilder.DropTable( + name: "PeopleRoles"); + + migrationBuilder.DropTable( + name: "Tracks"); + + migrationBuilder.DropTable( + name: "Collections"); + + migrationBuilder.DropTable( + name: "Libraries"); + + migrationBuilder.DropTable( + name: "Genres"); + + migrationBuilder.DropTable( + name: "Providers"); + + migrationBuilder.DropTable( + name: "People"); + + migrationBuilder.DropTable( + name: "Episodes"); + + migrationBuilder.DropTable( + name: "Seasons"); + + migrationBuilder.DropTable( + name: "Shows"); + + migrationBuilder.DropTable( + name: "Studios"); + } + } +} diff --git a/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs b/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs new file mode 100644 index 00000000..05d8772d --- /dev/null +++ b/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs @@ -0,0 +1,784 @@ +// +using System; +using Kyoo.Models; +using Kyoo.Postgresql; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Kyoo.Postgresql.Migrations +{ + [DbContext(typeof(PostgresContext))] + partial class PostgresContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasPostgresEnum(null, "item_type", new[] { "show", "movie", "collection" }) + .HasPostgresEnum(null, "status", new[] { "finished", "airing", "planned", "unknown" }) + .HasPostgresEnum(null, "stream_type", new[] { "unknown", "video", "audio", "subtitle", "attachment" }) + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.5") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + modelBuilder.Entity("Kyoo.Models.Collection", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Overview") + .HasColumnType("text"); + + b.Property("Poster") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Collections"); + }); + + modelBuilder.Entity("Kyoo.Models.Episode", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AbsoluteNumber") + .HasColumnType("integer"); + + b.Property("EpisodeNumber") + .HasColumnType("integer"); + + b.Property("Overview") + .HasColumnType("text"); + + b.Property("Path") + .HasColumnType("text"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Runtime") + .HasColumnType("integer"); + + b.Property("SeasonID") + .HasColumnType("integer"); + + b.Property("SeasonNumber") + .HasColumnType("integer"); + + b.Property("ShowID") + .HasColumnType("integer"); + + b.Property("Thumb") + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("SeasonID"); + + b.HasIndex("ShowID", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") + .IsUnique(); + + b.ToTable("Episodes"); + }); + + modelBuilder.Entity("Kyoo.Models.Genre", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Genres"); + }); + + modelBuilder.Entity("Kyoo.Models.Library", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Paths") + .HasColumnType("text[]"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Libraries"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.Property("FirstID") + .HasColumnType("integer"); + + b.Property("SecondID") + .HasColumnType("integer"); + + b.HasKey("FirstID", "SecondID"); + + b.HasIndex("SecondID"); + + b.ToTable("Link"); + }); + + modelBuilder.Entity("Kyoo.Models.MetadataID", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("DataID") + .HasColumnType("text"); + + b.Property("EpisodeID") + .HasColumnType("integer"); + + b.Property("Link") + .HasColumnType("text"); + + b.Property("PeopleID") + .HasColumnType("integer"); + + b.Property("ProviderID") + .HasColumnType("integer"); + + b.Property("SeasonID") + .HasColumnType("integer"); + + b.Property("ShowID") + .HasColumnType("integer"); + + b.HasKey("ID"); + + b.HasIndex("EpisodeID"); + + b.HasIndex("PeopleID"); + + b.HasIndex("ProviderID"); + + b.HasIndex("SeasonID"); + + b.HasIndex("ShowID"); + + b.ToTable("MetadataIds"); + }); + + modelBuilder.Entity("Kyoo.Models.People", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Poster") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("People"); + }); + + modelBuilder.Entity("Kyoo.Models.PeopleRole", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("PeopleID") + .HasColumnType("integer"); + + b.Property("Role") + .HasColumnType("text"); + + b.Property("ShowID") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("PeopleID"); + + b.HasIndex("ShowID"); + + b.ToTable("PeopleRoles"); + }); + + modelBuilder.Entity("Kyoo.Models.Provider", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Logo") + .HasColumnType("text"); + + b.Property("LogoExtension") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Providers"); + }); + + modelBuilder.Entity("Kyoo.Models.Season", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Overview") + .HasColumnType("text"); + + b.Property("Poster") + .HasColumnType("text"); + + b.Property("SeasonNumber") + .HasColumnType("integer"); + + b.Property("ShowID") + .HasColumnType("integer"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("Year") + .HasColumnType("integer"); + + b.HasKey("ID"); + + b.HasIndex("ShowID", "SeasonNumber") + .IsUnique(); + + b.ToTable("Seasons"); + }); + + modelBuilder.Entity("Kyoo.Models.Show", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Aliases") + .HasColumnType("text[]"); + + b.Property("Backdrop") + .HasColumnType("text"); + + b.Property("EndYear") + .HasColumnType("integer"); + + b.Property("IsMovie") + .HasColumnType("boolean"); + + b.Property("Logo") + .HasColumnType("text"); + + b.Property("Overview") + .HasColumnType("text"); + + b.Property("Path") + .HasColumnType("text"); + + b.Property("Poster") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartYear") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("status"); + + b.Property("StudioID") + .HasColumnType("integer"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("TrailerUrl") + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("StudioID"); + + b.ToTable("Shows"); + }); + + modelBuilder.Entity("Kyoo.Models.Studio", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Studios"); + }); + + modelBuilder.Entity("Kyoo.Models.Track", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Codec") + .HasColumnType("text"); + + b.Property("EpisodeID") + .HasColumnType("integer"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsExternal") + .HasColumnType("boolean"); + + b.Property("IsForced") + .HasColumnType("boolean"); + + b.Property("Language") + .HasColumnType("text"); + + b.Property("Path") + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("TrackIndex") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("stream_type"); + + b.HasKey("ID"); + + b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced") + .IsUnique(); + + b.ToTable("Tracks"); + }); + + modelBuilder.Entity("Kyoo.Models.Episode", b => + { + b.HasOne("Kyoo.Models.Season", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonID"); + + b.HasOne("Kyoo.Models.Show", "Show") + .WithMany("Episodes") + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Collection", "First") + .WithMany("ShowLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Show", "Second") + .WithMany("CollectionLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Library", "First") + .WithMany("CollectionLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Collection", "Second") + .WithMany("LibraryLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Library", "First") + .WithMany("ProviderLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Provider", "Second") + .WithMany("LibraryLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Library", "First") + .WithMany("ShowLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Show", "Second") + .WithMany("LibraryLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.Link", b => + { + b.HasOne("Kyoo.Models.Show", "First") + .WithMany("GenreLinks") + .HasForeignKey("FirstID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Genre", "Second") + .WithMany("ShowLinks") + .HasForeignKey("SecondID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("First"); + + b.Navigation("Second"); + }); + + modelBuilder.Entity("Kyoo.Models.MetadataID", b => + { + b.HasOne("Kyoo.Models.Episode", "Episode") + .WithMany("ExternalIDs") + .HasForeignKey("EpisodeID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Kyoo.Models.People", "People") + .WithMany("ExternalIDs") + .HasForeignKey("PeopleID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Kyoo.Models.Provider", "Provider") + .WithMany("MetadataLinks") + .HasForeignKey("ProviderID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Season", "Season") + .WithMany("ExternalIDs") + .HasForeignKey("SeasonID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Kyoo.Models.Show", "Show") + .WithMany("ExternalIDs") + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Episode"); + + b.Navigation("People"); + + b.Navigation("Provider"); + + b.Navigation("Season"); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("Kyoo.Models.PeopleRole", b => + { + b.HasOne("Kyoo.Models.People", "People") + .WithMany("Roles") + .HasForeignKey("PeopleID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Kyoo.Models.Show", "Show") + .WithMany("People") + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("People"); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("Kyoo.Models.Season", b => + { + b.HasOne("Kyoo.Models.Show", "Show") + .WithMany("Seasons") + .HasForeignKey("ShowID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Show"); + }); + + modelBuilder.Entity("Kyoo.Models.Show", b => + { + b.HasOne("Kyoo.Models.Studio", "Studio") + .WithMany("Shows") + .HasForeignKey("StudioID"); + + b.Navigation("Studio"); + }); + + modelBuilder.Entity("Kyoo.Models.Track", b => + { + b.HasOne("Kyoo.Models.Episode", "Episode") + .WithMany("Tracks") + .HasForeignKey("EpisodeID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Episode"); + }); + + modelBuilder.Entity("Kyoo.Models.Collection", b => + { + b.Navigation("LibraryLinks"); + + b.Navigation("ShowLinks"); + }); + + modelBuilder.Entity("Kyoo.Models.Episode", b => + { + b.Navigation("ExternalIDs"); + + b.Navigation("Tracks"); + }); + + modelBuilder.Entity("Kyoo.Models.Genre", b => + { + b.Navigation("ShowLinks"); + }); + + modelBuilder.Entity("Kyoo.Models.Library", b => + { + b.Navigation("CollectionLinks"); + + b.Navigation("ProviderLinks"); + + b.Navigation("ShowLinks"); + }); + + modelBuilder.Entity("Kyoo.Models.People", b => + { + b.Navigation("ExternalIDs"); + + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Kyoo.Models.Provider", b => + { + b.Navigation("LibraryLinks"); + + b.Navigation("MetadataLinks"); + }); + + modelBuilder.Entity("Kyoo.Models.Season", b => + { + b.Navigation("Episodes"); + + b.Navigation("ExternalIDs"); + }); + + modelBuilder.Entity("Kyoo.Models.Show", b => + { + b.Navigation("CollectionLinks"); + + b.Navigation("Episodes"); + + b.Navigation("ExternalIDs"); + + b.Navigation("GenreLinks"); + + b.Navigation("LibraryLinks"); + + b.Navigation("People"); + + b.Navigation("Seasons"); + }); + + modelBuilder.Entity("Kyoo.Models.Studio", b => + { + b.Navigation("Shows"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Kyoo.Postgresql/PostgresContext.cs b/Kyoo.Postgresql/PostgresContext.cs index 36ca803e..b5e4febe 100644 --- a/Kyoo.Postgresql/PostgresContext.cs +++ b/Kyoo.Postgresql/PostgresContext.cs @@ -69,7 +69,10 @@ namespace Kyoo.Postgresql { if (!_skipConfigure) { - optionsBuilder.UseNpgsql(_connection); + if (_connection != null) + optionsBuilder.UseNpgsql(_connection); + else + optionsBuilder.UseNpgsql(); if (_debugMode) optionsBuilder.EnableDetailedErrors().EnableSensitiveDataLogging(); } diff --git a/Kyoo.Postgresql/PostgresModule.cs b/Kyoo.Postgresql/PostgresModule.cs index df829fdd..7a818296 100644 --- a/Kyoo.Postgresql/PostgresModule.cs +++ b/Kyoo.Postgresql/PostgresModule.cs @@ -71,5 +71,12 @@ namespace Kyoo.Postgresql // _environment.IsDevelopment())); // services.AddScoped(x => x.GetRequiredService()); } + + /// + public void Initialize(IServiceProvider provider) + { + DatabaseContext context = provider.GetRequiredService(); + context.Database.Migrate(); + } } } \ No newline at end of file diff --git a/Kyoo/Controllers/TaskManager.cs b/Kyoo/Controllers/TaskManager.cs index 7ba56a07..5f281ba3 100644 --- a/Kyoo/Controllers/TaskManager.cs +++ b/Kyoo/Controllers/TaskManager.cs @@ -4,9 +4,11 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using Kyoo.Models.Attributes; using Kyoo.Models.Exceptions; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -34,7 +36,7 @@ namespace Kyoo.Controllers /// /// The list of tasks and their next scheduled run. /// - private List<(ITask task, DateTime scheduledDate)> _tasks; + private readonly List<(ITask task, DateTime scheduledDate)> _tasks; /// /// The queue of tasks that should be run as soon as possible. /// @@ -47,8 +49,8 @@ namespace Kyoo.Controllers /// The cancellation token used to cancel the running task when the runner should shutdown. /// private readonly CancellationTokenSource _taskToken = new(); - - + + /// /// Create a new . /// @@ -64,7 +66,7 @@ namespace Kyoo.Controllers _provider = provider; _configuration = configuration.GetSection("scheduledTasks"); _logger = logger; - _tasks = tasks.Select(x => (x, DateTime.Now + GetTaskDelay(x.Slug))).ToList(); + _tasks = tasks.Select(x => (x, GetNextTaskDate(x.Slug))).ToList(); if (_tasks.Any()) _logger.LogTrace("Task manager initiated with: {Tasks}", _tasks.Select(x => x.task.Name)); @@ -154,9 +156,11 @@ namespace Kyoo.Controllers " but it was not specified."); return x.CreateValue(value ?? x.DefaultValue); })); - - InjectServices(task); + + using IServiceScope scope = _provider.CreateScope(); + InjectServices(task, x => scope.ServiceProvider.GetRequiredService(x)); await task.Run(args, _taskToken.Token); + InjectServices(task, _ => null); _logger.LogInformation("Task finished: {Task}", task.Name); } @@ -164,17 +168,15 @@ namespace Kyoo.Controllers /// Inject services into the marked properties of the given object. /// /// The object to inject - private void InjectServices(ITask obj) + /// The function used to retrieve services. (The function is called immediately) + private static void InjectServices(ITask obj, [InstantHandle] Func retrieve) { IEnumerable properties = obj.GetType().GetProperties() .Where(x => x.GetCustomAttribute() != null) .Where(x => x.CanWrite); foreach (PropertyInfo property in properties) - { - object value = _provider.GetService(property.PropertyType); - property.SetValue(obj, value); - } + property.SetValue(obj, retrieve(property.PropertyType)); } /// @@ -197,7 +199,7 @@ namespace Kyoo.Controllers private void EnqueueStartupTasks() { IEnumerable startupTasks = _tasks.Select(x => x.task) - .Where(x => x.RunOnStartup && x.Priority != int.MaxValue) + .Where(x => x.RunOnStartup) .OrderByDescending(x => x.Priority); foreach (ITask task in startupTasks) _queuedTasks.Enqueue((task, new Dictionary())); @@ -212,20 +214,20 @@ namespace Kyoo.Controllers if (index == -1) throw new ItemNotFoundException($"No task found with the slug {taskSlug}"); _queuedTasks.Enqueue((_tasks[index].task, arguments)); - _tasks[index] = (_tasks[index].task, DateTime.Now + GetTaskDelay(taskSlug)); + _tasks[index] = (_tasks[index].task, GetNextTaskDate(taskSlug)); } /// - /// Get the delay of a task + /// Get the next date of the execution of the given task. /// /// The slug of the task - /// The delay of the task. - private TimeSpan GetTaskDelay(string taskSlug) + /// The next date. + private DateTime GetNextTaskDate(string taskSlug) { TimeSpan delay = _configuration.GetValue(taskSlug); if (delay == default) - delay = TimeSpan.MaxValue; - return delay; + return DateTime.MaxValue; + return DateTime.Now + delay; } /// @@ -239,12 +241,5 @@ namespace Kyoo.Controllers { return _tasks.Select(x => x.task).ToArray(); } - - /// - public void ReloadTasks() - { - // _tasks = _provider.Resolve>().Select(x => (x, DateTime.Now + GetTaskDelay(x.Slug))).ToList(); - EnqueueStartupTasks(); - } } } \ No newline at end of file diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index c267bd00..b60b3e09 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -3,6 +3,7 @@ using System.IO; using Kyoo.Controllers; using Kyoo.Models; using Kyoo.Postgresql; +using Kyoo.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.SpaServices.AngularCli; @@ -85,9 +86,10 @@ namespace Kyoo // }); // }); // services.AddAuthentication() + services.AddTransient(typeof(Lazy<>), typeof(LazyDi<>)); services.AddSingleton(_plugins); - services.AddTransient(typeof(Lazy<>), typeof(LazyDi<>)); + services.AddTask(); _plugins.ConfigureServices(services); } diff --git a/Kyoo/Tasks/PluginInitializer.cs b/Kyoo/Tasks/PluginInitializer.cs new file mode 100644 index 00000000..55907649 --- /dev/null +++ b/Kyoo/Tasks/PluginInitializer.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Kyoo.Controllers; +using Kyoo.Models.Attributes; + +namespace Kyoo.Tasks +{ + /// + /// A task run on Kyoo's startup to initialize plugins + /// + public class PluginInitializer : ITask + { + /// + public string Slug => "plugin-init"; + + /// + public string Name => "PluginInitializer"; + + /// + public string Description => "A task to initialize plugins."; + + /// + public string HelpMessage => null; + + /// + public bool RunOnStartup => true; + + /// + public int Priority => int.MaxValue; + + + /// + /// The plugin manager used to retrieve plugins to initialize them. + /// + [Injected] public IPluginManager PluginManager { private get; set; } + /// + /// The service provider given to each method. + /// + [Injected] public IServiceProvider Provider { private get; set; } + + /// + public Task Run(TaskParameters arguments, CancellationToken cancellationToken) + { + foreach (IPlugin plugin in PluginManager.GetAllPlugins()) + plugin.Initialize(Provider); + return Task.CompletedTask; + } + + public TaskParameters GetParameters() + { + return new(); + } + + public int? Progress() + { + return null; + } + } +} \ No newline at end of file