Fix tracks slugs

This commit is contained in:
Zoe Roux 2023-07-25 01:09:10 +09:00
parent 0f96f02df5
commit 066229eb0e
15 changed files with 121 additions and 46 deletions

View File

@ -38,7 +38,7 @@ jobs:
dotnet test --no-build '-p:CollectCoverage=true;CoverletOutputFormat=opencover' --logger "trx;LogFileName=TestOutputResults.xml"
env:
POSTGRES_HOST: postgres
POSTGRES_USERNAME: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
- name: Sanitize coverage output

1
back/.gitignore vendored
View File

@ -80,7 +80,6 @@ StyleCopReport.xml
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli

4
back/ef.rsp Normal file
View File

@ -0,0 +1,4 @@
--project
src/Kyoo.Postgresql/Kyoo.Postgresql.csproj
--msbuildprojectextensionspath
out/obj/Kyoo.Postgresql

View File

@ -80,7 +80,7 @@ namespace Kyoo.Abstractions.Models
/// <summary>
/// The slug of the Show that contain this episode. If this is not set, this episode is ill-formed.
/// </summary>
[SerializeIgnore] public string ShowSlug { get; set; }
[SerializeIgnore] public string ShowSlug { private get; set; }
/// <summary>
/// The ID of the Show containing this episode.

View File

@ -68,7 +68,7 @@ namespace Kyoo.Abstractions.Models
{
string type = Type.ToString().ToLowerInvariant();
string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty;
string episode = _episodeSlug ?? Episode?.Slug ?? EpisodeID.ToString(CultureInfo.InvariantCulture);
string episode = EpisodeSlug ?? Episode?.Slug ?? EpisodeID.ToString(CultureInfo.InvariantCulture);
return $"{episode}.{Language ?? "und"}{index}{(IsForced ? ".forced" : string.Empty)}.{type}";
}
@ -88,7 +88,7 @@ namespace Kyoo.Abstractions.Models
);
}
_episodeSlug = match.Groups["ep"].Value;
EpisodeSlug = match.Groups["ep"].Value;
Language = match.Groups["lang"].Value;
if (Language == "und")
Language = null;
@ -154,7 +154,7 @@ namespace Kyoo.Abstractions.Models
{
_episode = value;
if (_episode != null)
_episodeSlug = _episode.Slug;
EpisodeSlug = _episode.Slug;
}
}
@ -190,7 +190,7 @@ namespace Kyoo.Abstractions.Models
/// <summary>
/// The slug of the episode that contain this track. If this is not set, this track is ill-formed.
/// </summary>
[SerializeIgnore] private string _episodeSlug;
[SerializeIgnore] public string EpisodeSlug { private get; set; }
/// <summary>
/// The episode that uses this track.

View File

@ -47,7 +47,7 @@ namespace Kyoo.Core.Controllers
/// <summary>
/// A track repository to handle creation and deletion of tracks related to the current episode.
/// </summary>
private readonly ITrackRepository _tracks;
private readonly Lazy<ITrackRepository> _tracks;
/// <inheritdoc />
// Use absolute numbers by default and fallback to season/episodes if it does not exists.
@ -67,7 +67,7 @@ namespace Kyoo.Core.Controllers
public EpisodeRepository(DatabaseContext database,
IShowRepository shows,
IProviderRepository providers,
ITrackRepository tracks)
Lazy<ITrackRepository> tracks)
: base(database)
{
_database = database;
@ -75,15 +75,15 @@ namespace Kyoo.Core.Controllers
_tracks = tracks;
// Edit episode slugs when the show's slug changes.
shows.OnEdited += async (show) =>
shows.OnEdited += (show) =>
{
foreach (Episode ep in _database.Episodes.Where(x => x.ShowID == show.ID))
List<Episode> episodes = _database.Episodes.AsTracking().Where(x => x.ShowID == show.ID).ToList();
foreach (Episode ep in episodes)
{
Console.WriteLine("BFR ID: {0}; Slug: {1}; ShowSlug: {2}", ep.ID, ep.Slug, ep.ShowSlug);
ep.ShowSlug = show.Slug;
Console.WriteLine("AFT ID: {0}; Slug: {1}; ShowSlug: {2}", ep.ID, ep.Slug, ep.ShowSlug);
_database.SaveChanges();
OnResourceEdited(ep);
}
await _database.SaveChangesAsync();
};
}
@ -171,7 +171,7 @@ namespace Kyoo.Core.Controllers
if (changed.Tracks != null || resetOld)
{
await _tracks.DeleteAll(x => x.EpisodeID == resource.ID);
await _tracks.Value.DeleteAll(x => x.EpisodeID == resource.ID);
resource.Tracks = changed.Tracks;
await _ValidateTracks(resource);
}
@ -196,7 +196,7 @@ namespace Kyoo.Core.Controllers
resource.Tracks = await resource.Tracks.SelectAsync(x =>
{
x.Episode = resource;
return _tracks.Create(x);
return _tracks.Value.Create(x);
}).ToListAsync();
_database.Tracks.AttachRange(resource.Tracks);
return resource;
@ -235,7 +235,7 @@ namespace Kyoo.Core.Controllers
throw new ArgumentNullException(nameof(obj));
_database.Entry(obj).State = EntityState.Deleted;
await obj.Tracks.ForEachAsync(x => _tracks.Delete(x));
await obj.Tracks.ForEachAsync(x => _tracks.Value.Delete(x));
obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Deleted);
await _database.SaveChangesAsync();
await base.Delete(obj);

View File

@ -353,6 +353,15 @@ namespace Kyoo.Core.Controllers
OnCreated?.Invoke(obj);
}
/// <summary>
/// Callback that should be called after a resource has been edited.
/// </summary>
/// <param name="obj">The resource newly edited.</param>
protected void OnResourceEdited(T obj)
{
OnEdited?.Invoke(obj);
}
/// <inheritdoc/>
public virtual async Task<T> CreateIfNotExists(T obj)
{

View File

@ -61,11 +61,15 @@ namespace Kyoo.Core.Controllers
_providers = providers;
// Edit seasons slugs when the show's slug changes.
shows.OnEdited += async (show) =>
shows.OnEdited += (show) =>
{
foreach (Season season in _database.Seasons.Where(x => x.ShowID == show.ID))
List<Season> seasons = _database.Seasons.AsTracking().Where(x => x.ShowID == show.ID).ToList();
foreach (Season season in seasons)
{
season.ShowSlug = show.Slug;
await _database.SaveChangesAsync();
_database.SaveChanges();
OnResourceEdited(season);
}
};
}

View File

@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
@ -43,10 +44,23 @@ namespace Kyoo.Core.Controllers
/// Create a new <see cref="TrackRepository"/>.
/// </summary>
/// <param name="database">The database handle</param>
public TrackRepository(DatabaseContext database)
/// <param name="episodes">The episode repository</param>
public TrackRepository(DatabaseContext database, IEpisodeRepository episodes)
: base(database)
{
_database = database;
// Edit tracks slugs when the episodes's slug changes.
episodes.OnEdited += (ep) =>
{
List<Track> tracks = _database.Tracks.AsTracking().Where(x => x.EpisodeID == ep.ID).ToList();
foreach (Track track in tracks)
{
track.EpisodeSlug = ep.Slug;
_database.SaveChanges();
OnResourceEdited(track);
}
};
}
/// <inheritdoc />

View File

@ -305,6 +305,9 @@ namespace Kyoo.Postgresql
modelBuilder.Entity<People>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Provider>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Show>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Season>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Episode>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Track>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<Studio>().Property(x => x.Slug).IsRequired();
modelBuilder.Entity<User>().Property(x => x.Slug).IsRequired();
@ -350,16 +353,6 @@ namespace Kyoo.Postgresql
modelBuilder.Entity<User>()
.HasIndex(x => x.Slug)
.IsUnique();
modelBuilder.Entity<Season>()
.Property(x => x.Slug)
.ValueGeneratedOnAddOrUpdate();
modelBuilder.Entity<Episode>()
.Property(x => x.Slug)
.ValueGeneratedOnAddOrUpdate();
modelBuilder.Entity<Track>()
.Property(x => x.Slug)
.ValueGeneratedOnAddOrUpdate();
}
/// <summary>

View File

@ -1,4 +1,4 @@
// <auto-generated />
// <auto-generated />
using System;
using System.Collections.Generic;
using Kyoo.Abstractions.Models;
@ -12,11 +12,10 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Kyoo.Postgresql.Migrations
{
[DbContext(typeof(PostgresContext))]
[Migration("20230621074246_RemoveTrigers")]
partial class RemoveTrigers
[Migration("20230724144449_RemoveTriggers")]
partial class RemoveTriggers
{
/// <inheritdoc/>
protected override void BuildTargetModel(ModelBuilder modelBuilder)
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
@ -107,7 +106,7 @@ namespace Kyoo.Postgresql.Migrations
.HasColumnName("show_id");
b.Property<string>("Slug")
.ValueGeneratedOnAddOrUpdate()
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
@ -362,7 +361,7 @@ namespace Kyoo.Postgresql.Migrations
.HasColumnName("show_id");
b.Property<string>("Slug")
.ValueGeneratedOnAddOrUpdate()
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
@ -518,7 +517,7 @@ namespace Kyoo.Postgresql.Migrations
.HasColumnName("path");
b.Property<string>("Slug")
.ValueGeneratedOnAddOrUpdate()
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");

View File

@ -23,11 +23,41 @@ namespace Kyoo.Postgresql.Migrations
/// <summary>
/// Remove triggers
/// </summary>
public partial class RemoveTrigers : Migration
public partial class RemoveTriggers : Migration
{
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "slug",
table: "tracks",
type: "text",
nullable: false,
defaultValue: string.Empty,
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "slug",
table: "seasons",
type: "text",
nullable: false,
defaultValue: string.Empty,
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "slug",
table: "episodes",
type: "text",
nullable: false,
defaultValue: string.Empty,
oldClrType: typeof(string),
oldType: "text",
oldNullable: true);
// language=PostgreSQL
migrationBuilder.Sql("DROP TRIGGER show_slug_trigger ON shows;");
// language=PostgreSQL
@ -53,6 +83,29 @@ namespace Kyoo.Postgresql.Migrations
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "slug",
table: "tracks",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "text");
migrationBuilder.AlterColumn<string>(
name: "slug",
table: "seasons",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "text");
migrationBuilder.AlterColumn<string>(
name: "slug",
table: "episodes",
type: "text",
nullable: true,
oldClrType: typeof(string),
oldType: "text");
}
}
}

View File

@ -104,7 +104,7 @@ namespace Kyoo.Postgresql.Migrations
.HasColumnName("show_id");
b.Property<string>("Slug")
.ValueGeneratedOnAddOrUpdate()
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
@ -359,7 +359,7 @@ namespace Kyoo.Postgresql.Migrations
.HasColumnName("show_id");
b.Property<string>("Slug")
.ValueGeneratedOnAddOrUpdate()
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
@ -515,7 +515,7 @@ namespace Kyoo.Postgresql.Migrations
.HasColumnName("path");
b.Property<string>("Slug")
.ValueGeneratedOnAddOrUpdate()
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");

View File

@ -48,8 +48,8 @@ namespace Kyoo.Tests.Database
SeasonRepository season = new(_NewContext(), show, provider);
LibraryItemRepository libraryItem = new(_NewContext(),
new Lazy<ILibraryRepository>(() => LibraryManager.LibraryRepository));
TrackRepository track = new(_NewContext());
EpisodeRepository episode = new(_NewContext(), show, provider, track);
EpisodeRepository episode = new(_NewContext(), show, provider, new Lazy<ITrackRepository>(() => LibraryManager.TrackRepository));
TrackRepository track = new(_NewContext(), episode);
UserRepository user = new(_NewContext());
LibraryManager = new LibraryManager(new IBaseRepository[] {

View File

@ -104,7 +104,7 @@ namespace Kyoo.Tests
{
string server = Environment.GetEnvironmentVariable("POSTGRES_HOST") ?? "127.0.0.1";
string port = Environment.GetEnvironmentVariable("POSTGRES_PORT") ?? "5432";
string username = Environment.GetEnvironmentVariable("POSTGRES_USERNAME") ?? "kyoo";
string username = Environment.GetEnvironmentVariable("POSTGRES_USER") ?? "kyoo";
string password = Environment.GetEnvironmentVariable("POSTGRES_PASSWORD") ?? "kyooPassword";
return $"Server={server};Port={port};Database={database};User ID={username};Password={password};Include Error Detail=true";
}