diff --git a/Kyoo.Postgresql/Kyoo.Postgresql.csproj b/Kyoo.Postgresql/Kyoo.Postgresql.csproj
index d4fbfb84..6c67a653 100644
--- a/Kyoo.Postgresql/Kyoo.Postgresql.csproj
+++ b/Kyoo.Postgresql/Kyoo.Postgresql.csproj
@@ -22,7 +22,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Kyoo.Postgresql/Migrations/20210620120239_Triggers.Designer.cs b/Kyoo.Postgresql/Migrations/20210620120239_Triggers.Designer.cs
new file mode 100644
index 00000000..6b87547b
--- /dev/null
+++ b/Kyoo.Postgresql/Migrations/20210620120239_Triggers.Designer.cs
@@ -0,0 +1,988 @@
+//
+using System;
+using System.Collections.Generic;
+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("20210620120239_Triggers")]
+ partial class Triggers
+ {
+ 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.7")
+ .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("SeasonID")
+ .HasColumnType("integer");
+
+ b.Property("SeasonNumber")
+ .HasColumnType("integer");
+
+ b.Property("ShowID")
+ .HasColumnType("integer");
+
+ b.Property("Slug")
+ .HasColumnType("text");
+
+ 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.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("FirstID")
+ .HasColumnType("integer");
+
+ b.Property("SecondID")
+ .HasColumnType("integer");
+
+ b.Property("DataID")
+ .HasColumnType("text");
+
+ b.Property("Link")
+ .HasColumnType("text");
+
+ b.HasKey("FirstID", "SecondID");
+
+ b.HasIndex("SecondID");
+
+ b.ToTable("MetadataID");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
+ {
+ b.Property("FirstID")
+ .HasColumnType("integer");
+
+ b.Property("SecondID")
+ .HasColumnType("integer");
+
+ b.Property("DataID")
+ .HasColumnType("text");
+
+ b.Property("Link")
+ .HasColumnType("text");
+
+ b.HasKey("FirstID", "SecondID");
+
+ b.HasIndex("SecondID");
+
+ b.ToTable("MetadataID");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
+ {
+ b.Property("FirstID")
+ .HasColumnType("integer");
+
+ b.Property("SecondID")
+ .HasColumnType("integer");
+
+ b.Property("DataID")
+ .HasColumnType("text");
+
+ b.Property("Link")
+ .HasColumnType("text");
+
+ b.HasKey("FirstID", "SecondID");
+
+ b.HasIndex("SecondID");
+
+ b.ToTable("MetadataID");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
+ {
+ b.Property("FirstID")
+ .HasColumnType("integer");
+
+ b.Property("SecondID")
+ .HasColumnType("integer");
+
+ b.Property("DataID")
+ .HasColumnType("text");
+
+ b.Property("Link")
+ .HasColumnType("text");
+
+ b.HasKey("FirstID", "SecondID");
+
+ b.HasIndex("SecondID");
+
+ b.ToTable("MetadataID");
+ });
+
+ 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("ForPeople")
+ .HasColumnType("boolean");
+
+ 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("EndDate")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property("Overview")
+ .HasColumnType("text");
+
+ b.Property("Poster")
+ .HasColumnType("text");
+
+ b.Property("SeasonNumber")
+ .HasColumnType("integer");
+
+ b.Property("ShowID")
+ .HasColumnType("integer");
+
+ b.Property("Slug")
+ .HasColumnType("text");
+
+ b.Property("StartDate")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property("Title")
+ .HasColumnType("text");
+
+ 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("EndAir")
+ .HasColumnType("timestamp without time zone");
+
+ 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("StartAir")
+ .HasColumnType("timestamp without time zone");
+
+ 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("Slug")
+ .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.User", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property("Email")
+ .HasColumnType("text");
+
+ b.Property>("ExtraData")
+ .HasColumnType("jsonb");
+
+ b.Property("Password")
+ .HasColumnType("text");
+
+ b.Property("Permissions")
+ .HasColumnType("text[]");
+
+ b.Property("Slug")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Username")
+ .HasColumnType("text");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Slug")
+ .IsUnique();
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.WatchedEpisode", b =>
+ {
+ b.Property("FirstID")
+ .HasColumnType("integer");
+
+ b.Property("SecondID")
+ .HasColumnType("integer");
+
+ b.Property("WatchedPercentage")
+ .HasColumnType("integer");
+
+ b.HasKey("FirstID", "SecondID");
+
+ b.HasIndex("SecondID");
+
+ b.ToTable("WatchedEpisodes");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.Episode", b =>
+ {
+ b.HasOne("Kyoo.Models.Season", "Season")
+ .WithMany("Episodes")
+ .HasForeignKey("SeasonID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ 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.Link", b =>
+ {
+ b.HasOne("Kyoo.Models.User", "First")
+ .WithMany("ShowLinks")
+ .HasForeignKey("FirstID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Kyoo.Models.Show", "Second")
+ .WithMany()
+ .HasForeignKey("SecondID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("First");
+
+ b.Navigation("Second");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
+ {
+ b.HasOne("Kyoo.Models.Episode", "First")
+ .WithMany("ExternalIDs")
+ .HasForeignKey("FirstID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Kyoo.Models.Provider", "Second")
+ .WithMany()
+ .HasForeignKey("SecondID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("First");
+
+ b.Navigation("Second");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
+ {
+ b.HasOne("Kyoo.Models.People", "First")
+ .WithMany("ExternalIDs")
+ .HasForeignKey("FirstID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Kyoo.Models.Provider", "Second")
+ .WithMany()
+ .HasForeignKey("SecondID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("First");
+
+ b.Navigation("Second");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
+ {
+ b.HasOne("Kyoo.Models.Season", "First")
+ .WithMany("ExternalIDs")
+ .HasForeignKey("FirstID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Kyoo.Models.Provider", "Second")
+ .WithMany()
+ .HasForeignKey("SecondID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("First");
+
+ b.Navigation("Second");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.MetadataID", b =>
+ {
+ b.HasOne("Kyoo.Models.Show", "First")
+ .WithMany("ExternalIDs")
+ .HasForeignKey("FirstID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Kyoo.Models.Provider", "Second")
+ .WithMany()
+ .HasForeignKey("SecondID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("First");
+
+ b.Navigation("Second");
+ });
+
+ 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.WatchedEpisode", b =>
+ {
+ b.HasOne("Kyoo.Models.User", "First")
+ .WithMany("CurrentlyWatching")
+ .HasForeignKey("FirstID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Kyoo.Models.Episode", "Second")
+ .WithMany()
+ .HasForeignKey("SecondID")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("First");
+
+ b.Navigation("Second");
+ });
+
+ 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");
+ });
+
+ 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");
+ });
+
+ modelBuilder.Entity("Kyoo.Models.User", b =>
+ {
+ b.Navigation("CurrentlyWatching");
+
+ b.Navigation("ShowLinks");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Kyoo.Postgresql/Migrations/20210620120239_Triggers.cs b/Kyoo.Postgresql/Migrations/20210620120239_Triggers.cs
new file mode 100644
index 00000000..bdb354a8
--- /dev/null
+++ b/Kyoo.Postgresql/Migrations/20210620120239_Triggers.cs
@@ -0,0 +1,57 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Kyoo.Postgresql.Migrations
+{
+ public partial class Triggers : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql(@"
+ CREATE FUNCTION season_slug_update()
+ RETURNS TRIGGER
+ LANGUAGE PLPGSQL
+ AS $$
+ BEGIN
+ NEW.""Slug"" := CONCAT(
+ (SELECT ""Slug"" FROM ""Shows"" WHERE ""ID"" = NEW.""ShowID""),
+ NEW.""ShowID"",
+ OLD.""SeasonNumber"",
+ NEW.""SeasonNumber"",
+ '-s',
+ NEW.""SeasonNumber""
+ );
+ NEW.""Poster"" := 'NICE';
+ RETURN NEW;
+ END
+ $$;");
+
+ migrationBuilder.Sql(@"
+ CREATE TRIGGER ""SeasonSlug"" AFTER INSERT OR UPDATE OF ""SeasonNumber"", ""ShowID"" ON ""Seasons""
+ FOR EACH ROW EXECUTE PROCEDURE season_slug_update();");
+
+
+ migrationBuilder.Sql(@"
+ CREATE FUNCTION show_slug_update()
+ RETURNS TRIGGER
+ LANGUAGE PLPGSQL
+ AS $$
+ BEGIN
+ UPDATE ""Seasons"" SET ""Slug"" = CONCAT(new.""Slug"", '-s', ""SeasonNumber"") WHERE ""ShowID"" = NEW.""ID"";
+ RETURN NEW;
+ END
+ $$;");
+
+ migrationBuilder.Sql(@"
+ CREATE TRIGGER ""ShowSlug"" AFTER UPDATE OF ""Slug"" ON ""Shows""
+ FOR EACH ROW EXECUTE PROCEDURE show_slug_update();");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql(@"DROP FUNCTION ""season_slug_update"";");
+ migrationBuilder.Sql(@"DROP TRIGGER ""SeasonSlug"";");
+ migrationBuilder.Sql(@"DROP FUNCTION ""show_slug_update"";");
+ migrationBuilder.Sql(@"DROP TRIGGER ""ShowSlug"";");
+ }
+ }
+}
diff --git a/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs b/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs
index bb529e05..820088d9 100644
--- a/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs
+++ b/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs
@@ -56,7 +56,10 @@ namespace Kyoo.Tests.SpecificTests
SeasonNumber = 2
}, false);
season = await _repository.Get(1);
+ Assert.Equal("anohana-s2_NICE", season.Slug + "_" + season.Poster);
Assert.Equal("anohana-s2", season.Slug);
}
+
+ //TODO test insert trigger
}
}
\ No newline at end of file
diff --git a/Kyoo.Tests/Library/TestContext.cs b/Kyoo.Tests/Library/TestContext.cs
index 1662e12c..37e1d7b8 100644
--- a/Kyoo.Tests/Library/TestContext.cs
+++ b/Kyoo.Tests/Library/TestContext.cs
@@ -66,6 +66,8 @@ namespace Kyoo.Tests
public PostgresFixture()
{
+ // TODO Assert.Skip when postgres is not available. (this needs xunit v3)
+
string id = Guid.NewGuid().ToString().Replace('-', '_');
Template = $"kyoo_template_{id}";