mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Adding episodes triggers
This commit is contained in:
parent
d2f774e32d
commit
37c752229e
10
Kyoo.Common/Models/Attributes/ComputedAttribute.cs
Normal file
10
Kyoo.Common/Models/Attributes/ComputedAttribute.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Models.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// An attribute to inform that the property is computed automatically and can't be assigned manually.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class ComputedAttribute : NotMergeableAttribute { }
|
||||
}
|
@ -17,12 +17,17 @@ namespace Kyoo.Models
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug
|
||||
[Computed] public string Slug
|
||||
{
|
||||
get => GetSlug(ShowSlug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||
get
|
||||
{
|
||||
if (ShowSlug == null && Show == null)
|
||||
return GetSlug(ShowID.ToString(), SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||
return GetSlug(ShowSlug ?? Show.Slug, SeasonNumber, EpisodeNumber, AbsoluteNumber);
|
||||
}
|
||||
[UsedImplicitly] private set
|
||||
{
|
||||
Match match = Regex.Match(value, @"(?<show>.*)-s(?<season>\d*)e(?<episode>\d*)");
|
||||
Match match = Regex.Match(value, @"(?<show>.+)-s(?<season>\d+)e(?<episode>\d+)");
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
|
@ -16,12 +16,17 @@ namespace Kyoo.Models
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug
|
||||
[Computed] public string Slug
|
||||
{
|
||||
get => $"{ShowSlug}-s{SeasonNumber}";
|
||||
get
|
||||
{
|
||||
if (ShowSlug == null && Show == null)
|
||||
return $"{ShowID}-s{SeasonNumber}";
|
||||
return $"{ShowSlug ?? Show?.Slug}-s{SeasonNumber}";
|
||||
}
|
||||
[UsedImplicitly] private set
|
||||
{
|
||||
Match match = Regex.Match(value, @"(?<show>.*)-s(?<season>\d*)");
|
||||
Match match = Regex.Match(value ?? "", @"(?<show>.+)-s(?<season>\d+)");
|
||||
|
||||
if (!match.Success)
|
||||
throw new ArgumentException("Invalid season slug. Format: {showSlug}-s{seasonNumber}");
|
||||
|
@ -29,7 +29,7 @@ namespace Kyoo.Models
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Slug
|
||||
[Computed] public string Slug
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -330,6 +330,16 @@ namespace Kyoo
|
||||
modelBuilder.Entity<Track>()
|
||||
.Property(x => x.Slug)
|
||||
.ValueGeneratedOnAddOrUpdate();
|
||||
|
||||
modelBuilder.Entity<Episode>()
|
||||
.Property(x => x.EpisodeNumber)
|
||||
.HasDefaultValue(-1);
|
||||
modelBuilder.Entity<Episode>()
|
||||
.Property(x => x.SeasonNumber)
|
||||
.HasDefaultValue(-1);
|
||||
modelBuilder.Entity<Episode>()
|
||||
.Property(x => x.AbsoluteNumber)
|
||||
.HasDefaultValue(-1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -14,6 +14,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -257,6 +257,8 @@ namespace Kyoo.Controllers
|
||||
/// <exception cref="ArgumentException">You can throw this if the resource is illegal and should not be saved.</exception>
|
||||
protected virtual Task Validate(T resource)
|
||||
{
|
||||
if (typeof(T).GetProperty(nameof(resource.Slug))!.GetCustomAttribute<ComputedAttribute>() != null)
|
||||
return Task.CompletedTask;
|
||||
if (string.IsNullOrEmpty(resource.Slug))
|
||||
throw new ArgumentException("Resource can't have null as a slug.");
|
||||
if (int.TryParse(resource.Slug, out int _))
|
||||
|
@ -18,10 +18,26 @@ namespace Kyoo.SqLite.Migrations
|
||||
UPDATE Seasons SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber
|
||||
WHERE ID == new.ID;
|
||||
END");
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
CREATE TRIGGER EpisodeSlugInsert AFTER INSERT ON Episodes FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Episodes SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber || 'e' || EpisodeNumber
|
||||
WHERE ID == new.ID;
|
||||
END");
|
||||
migrationBuilder.Sql(@"
|
||||
CREATE TRIGGER EpisodeSlugUpdate AFTER UPDATE OF EpisodeNumber, SeasonNumber, ShowID ON Episodes FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Episodes SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber || 'e' || EpisodeNumber
|
||||
WHERE ID == new.ID;
|
||||
END");
|
||||
|
||||
|
||||
migrationBuilder.Sql(@"
|
||||
CREATE TRIGGER ShowSlugUpdate AFTER UPDATE OF Slug ON Shows FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Seasons SET Slug = new.Slug || '-s' || SeasonNumber WHERE ShowID = new.ID;
|
||||
UPDATE Episodes SET Slug = new.Slug || '-s' || SeasonNumber || 'e' || EpisodeNumber WHERE ShowID = new.ID;
|
||||
END;");
|
||||
}
|
||||
|
||||
@ -29,6 +45,8 @@ namespace Kyoo.SqLite.Migrations
|
||||
{
|
||||
migrationBuilder.Sql("DROP TRIGGER SeasonSlugInsert;");
|
||||
migrationBuilder.Sql("DROP TRIGGER SeasonSlugUpdate;");
|
||||
migrationBuilder.Sql("DROP TRIGGER EpisodeSlugInsert;");
|
||||
migrationBuilder.Sql("DROP TRIGGER EpisodeSlugUpdate;");
|
||||
migrationBuilder.Sql("DROP TRIGGER ShowSlugUpdate;");
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Xunit;
|
||||
|
||||
@ -25,8 +27,70 @@ namespace Kyoo.Tests.Library
|
||||
|
||||
public abstract class AEpisodeTests : RepositoryTests<Episode>
|
||||
{
|
||||
private readonly IEpisodeRepository _repository;
|
||||
|
||||
protected AEpisodeTests(RepositoryActivator repositories)
|
||||
: base(repositories)
|
||||
{ }
|
||||
{
|
||||
_repository = repositories.LibraryManager.EpisodeRepository;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SlugEditTest()
|
||||
{
|
||||
Episode episode = await _repository.Get(1);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
|
||||
Show show = new()
|
||||
{
|
||||
ID = episode.ShowID,
|
||||
Slug = "new-slug"
|
||||
};
|
||||
await Repositories.LibraryManager.ShowRepository.Edit(show, false);
|
||||
episode = await _repository.Get(1);
|
||||
Assert.Equal("new-slug-s1e1", episode.Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SeasonNumberEditTest()
|
||||
{
|
||||
Episode episode = await _repository.Get(1);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
|
||||
await _repository.Edit(new Episode
|
||||
{
|
||||
ID = 1,
|
||||
SeasonNumber = 2
|
||||
}, false);
|
||||
episode = await _repository.Get(1);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EpisodeNumberEditTest()
|
||||
{
|
||||
Episode episode = await _repository.Get(1);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
|
||||
await _repository.Edit(new Episode
|
||||
{
|
||||
ID = 1,
|
||||
EpisodeNumber = 2
|
||||
}, false);
|
||||
episode = await _repository.Get(1);
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EpisodeCreationSlugTest()
|
||||
{
|
||||
Episode season = await _repository.Create(new Episode
|
||||
{
|
||||
ShowID = TestSample.Get<Show>().ID,
|
||||
SeasonNumber = 2,
|
||||
EpisodeNumber = 4
|
||||
});
|
||||
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e4", season.Slug);
|
||||
}
|
||||
|
||||
|
||||
// TODO absolute numbering tests
|
||||
}
|
||||
}
|
@ -145,7 +145,7 @@ namespace Kyoo.Controllers
|
||||
/// <returns>The <see cref="resource"/> parameter is returned.</returns>
|
||||
private async Task<Episode> ValidateTracks(Episode resource)
|
||||
{
|
||||
resource.Tracks = await resource.Tracks.MapAsync((x, i) =>
|
||||
resource.Tracks = await TaskUtils.DefaultIfNull(resource.Tracks?.MapAsync((x, i) =>
|
||||
{
|
||||
x.Episode = resource;
|
||||
x.TrackIndex = resource.Tracks.Take(i).Count(y => x.Language == y.Language
|
||||
@ -153,7 +153,7 @@ namespace Kyoo.Controllers
|
||||
&& x.Codec == y.Codec
|
||||
&& x.Type == y.Type);
|
||||
return _tracks.Create(x);
|
||||
}).ToListAsync();
|
||||
}).ToListAsync());
|
||||
return resource;
|
||||
}
|
||||
|
||||
@ -161,13 +161,12 @@ namespace Kyoo.Controllers
|
||||
protected override async Task Validate(Episode resource)
|
||||
{
|
||||
await base.Validate(resource);
|
||||
resource.ExternalIDs = await resource.ExternalIDs.SelectAsync(async x =>
|
||||
await resource.ExternalIDs.ForEachAsync(async x =>
|
||||
{
|
||||
x.Second = await _providers.CreateIfNotExists(x.Second);
|
||||
x.SecondID = x.Second.ID;
|
||||
_database.Entry(x.Second).State = EntityState.Detached;
|
||||
return x;
|
||||
}).ToListAsync();
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
Loading…
x
Reference in New Issue
Block a user