mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-08 02:34:16 -04:00
Fixing composed slug handling & page field serialization
This commit is contained in:
parent
fab9a3f6a1
commit
b3fdee4bcd
@ -109,6 +109,8 @@ namespace Kyoo.Controllers
|
||||
public interface IShowRepository : IRepository<Show>
|
||||
{
|
||||
Task AddShowLink(int showID, int? libraryID, int? collectionID);
|
||||
|
||||
Task<string> GetSlug(int showID);
|
||||
}
|
||||
|
||||
public interface ISeasonRepository : IRepository<Season>
|
||||
|
@ -1,7 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Models.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ComposedSlugAttribute : Attribute { }
|
||||
}
|
@ -5,11 +5,11 @@ using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
[ComposedSlug]
|
||||
public class Episode : IResource, IOnMerge
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public string Slug => Show != null ? GetSlug(Show.Slug, SeasonNumber, EpisodeNumber) : ID.ToString();
|
||||
public string Slug => GetSlug(ShowSlug, SeasonNumber, EpisodeNumber);
|
||||
[SerializeIgnore] public string ShowSlug { private get; set; }
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
[LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; }
|
||||
[SerializeIgnore] public int? SeasonID { get; set; }
|
||||
@ -30,9 +30,7 @@ namespace Kyoo.Models
|
||||
[LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
[LoadableRelation] public virtual ICollection<Track> Tracks { get; set; }
|
||||
|
||||
public string ShowTitle => Show?.Title;
|
||||
|
||||
|
||||
|
||||
public Episode() { }
|
||||
|
||||
@ -78,6 +76,8 @@ namespace Kyoo.Models
|
||||
|
||||
public static string GetSlug(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
if (showSlug == null)
|
||||
throw new ArgumentException("Show's slug is null. Can't find episode's slug.");
|
||||
if (seasonNumber == -1)
|
||||
return showSlug;
|
||||
return $"{showSlug}-s{seasonNumber}e{episodeNumber}";
|
||||
|
@ -4,15 +4,16 @@ using Kyoo.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Models
|
||||
{
|
||||
[ComposedSlug]
|
||||
public class Season : IResource
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public string Slug => $"{ShowSlug}-s{SeasonNumber}";
|
||||
[SerializeIgnore] public int ShowID { get; set; }
|
||||
[SerializeIgnore] public string ShowSlug { private get; set; }
|
||||
[LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; }
|
||||
|
||||
public int SeasonNumber { get; set; } = -1;
|
||||
|
||||
public string Slug => Show != null ? $"{Show.Slug}-s{SeasonNumber}" : ID.ToString();
|
||||
public string Title { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public int? Year { get; set; }
|
||||
@ -21,7 +22,6 @@ namespace Kyoo.Models
|
||||
public string Thumb => $"/api/seasons/{Slug}/thumb";
|
||||
[EditableRelation] [LoadableRelation] public virtual ICollection<MetadataID> ExternalIDs { get; set; }
|
||||
|
||||
[LoadableRelation(nameof(ShowID))] public virtual Show Show { get; set; }
|
||||
[LoadableRelation] public virtual ICollection<Episode> Episodes { get; set; }
|
||||
|
||||
public Season() { }
|
||||
|
@ -25,7 +25,7 @@ namespace Kyoo.Models
|
||||
|
||||
public class WatchItem
|
||||
{
|
||||
public readonly int EpisodeID = -1;
|
||||
public readonly int EpisodeID;
|
||||
|
||||
public string ShowTitle;
|
||||
public string ShowSlug;
|
||||
@ -41,9 +41,9 @@ namespace Kyoo.Models
|
||||
|
||||
public string Container;
|
||||
public Track Video;
|
||||
public IEnumerable<Track> Audios;
|
||||
public IEnumerable<Track> Subtitles;
|
||||
public IEnumerable<Chapter> Chapters;
|
||||
public ICollection<Track> Audios;
|
||||
public ICollection<Track> Subtitles;
|
||||
public ICollection<Chapter> Chapters;
|
||||
|
||||
public WatchItem() { }
|
||||
|
||||
@ -78,8 +78,8 @@ namespace Kyoo.Models
|
||||
DateTime? releaseDate,
|
||||
string path,
|
||||
Track video,
|
||||
IEnumerable<Track> audios,
|
||||
IEnumerable<Track> subtitles)
|
||||
ICollection<Track> audios,
|
||||
ICollection<Track> subtitles)
|
||||
: this(episodeID, showTitle, showSlug, seasonNumber, episodeNumber, title, releaseDate, path)
|
||||
{
|
||||
Video = video;
|
||||
@ -120,8 +120,8 @@ namespace Kyoo.Models
|
||||
ep.ReleaseDate,
|
||||
ep.Path,
|
||||
ep.Tracks.FirstOrDefault(x => x.Type == StreamType.Video),
|
||||
ep.Tracks.Where(x => x.Type == StreamType.Audio),
|
||||
ep.Tracks.Where(x => x.Type == StreamType.Subtitle))
|
||||
ep.Tracks.Where(x => x.Type == StreamType.Audio).ToArray(),
|
||||
ep.Tracks.Where(x => x.Type == StreamType.Subtitle).ToArray())
|
||||
{
|
||||
IsMovie = show.IsMovie,
|
||||
PreviousEpisode = previous,
|
||||
@ -130,7 +130,7 @@ namespace Kyoo.Models
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<Chapter>> GetChapters(string episodePath)
|
||||
private static async Task<ICollection<Chapter>> GetChapters(string episodePath)
|
||||
{
|
||||
string path = PathIO.Combine(
|
||||
PathIO.GetDirectoryName(episodePath)!,
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Attributes;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
@ -40,8 +42,13 @@ namespace Kyoo.Controllers
|
||||
protected override JsonContract CreateContract(Type objectType)
|
||||
{
|
||||
JsonContract contract = base.CreateContract(objectType);
|
||||
contract.OnSerializingCallbacks.Add((_, _) => _depth++);
|
||||
contract.OnSerializedCallbacks.Add((_, _) => _depth--);
|
||||
if (Utility.GetGenericDefinition(objectType, typeof(Page<>)) == null
|
||||
&& !objectType.IsAssignableTo(typeof(IEnumerable)))
|
||||
{
|
||||
contract.OnSerializingCallbacks.Add((_, _) => _depth++);
|
||||
contract.OnSerializedCallbacks.Add((_, _) => _depth--);
|
||||
}
|
||||
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.Slug))
|
||||
throw new ArgumentException("Resource can't have null as a slug.");
|
||||
if (int.TryParse(resource.Slug, out int _) && typeof(T).GetCustomAttribute<ComposedSlugAttribute>() == null)
|
||||
if (int.TryParse(resource.Slug, out int _))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -5,7 +5,6 @@ using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Kyoo.Controllers
|
||||
@ -15,13 +14,16 @@ namespace Kyoo.Controllers
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IProviderRepository _providers;
|
||||
private readonly IShowRepository _shows;
|
||||
protected override Expression<Func<Episode, object>> DefaultSort => x => x.EpisodeNumber;
|
||||
|
||||
|
||||
public EpisodeRepository(DatabaseContext database, IProviderRepository providers) : base(database)
|
||||
public EpisodeRepository(DatabaseContext database, IProviderRepository providers, IShowRepository shows)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
_shows = shows;
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +34,7 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
_shows.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
@ -42,6 +45,15 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
await _shows.DisposeAsync();
|
||||
}
|
||||
|
||||
public override async Task<Episode> Get(int id)
|
||||
{
|
||||
Episode ret = await base.Get(id);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override Task<Episode> Get(string slug)
|
||||
@ -54,45 +66,81 @@ namespace Kyoo.Controllers
|
||||
int.Parse(match.Groups["season"].Value),
|
||||
int.Parse(match.Groups["episode"].Value));
|
||||
}
|
||||
|
||||
public Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
|
||||
|
||||
public override async Task<Episode> Get(Expression<Func<Episode, bool>> predicate)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
Episode ret = await base.Get(predicate);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Episode> Get(int showID, int seasonNumber, int episodeNumber)
|
||||
public async Task<Episode> Get(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = showSlug;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Episode> Get(int seasonID, int episodeNumber)
|
||||
public async Task<Episode> Get(int showID, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.SeasonID == seasonID
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(showID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||
public async Task<Episode> Get(int seasonID, int episodeNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.SeasonID == seasonID
|
||||
&& x.EpisodeNumber == episodeNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
|
||||
public async Task<Episode> GetAbsolute(int showID, int absoluteNumber)
|
||||
{
|
||||
return _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(showID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task<Episode> GetAbsolute(string showSlug, int absoluteNumber)
|
||||
{
|
||||
Episode ret = await _database.Episodes.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.AbsoluteNumber == absoluteNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = showSlug;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override async Task<ICollection<Episode>> Search(string query)
|
||||
{
|
||||
return await _database.Episodes
|
||||
List<Episode> episodes = await _database.Episodes
|
||||
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
foreach (Episode episode in episodes)
|
||||
episode.ShowSlug = await _shows.GetSlug(episode.ShowID);
|
||||
return episodes;
|
||||
}
|
||||
|
||||
public override async Task<ICollection<Episode>> GetAll(Expression<Func<Episode, bool>> where = null,
|
||||
Sort<Episode> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<Episode> episodes = await base.GetAll(where, sort, limit);
|
||||
foreach (Episode episode in episodes)
|
||||
episode.ShowSlug = await _shows.GetSlug(episode.ShowID);
|
||||
return episodes;
|
||||
}
|
||||
|
||||
public override async Task<Episode> Create(Episode obj)
|
||||
|
@ -5,7 +5,6 @@ using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -16,17 +15,20 @@ namespace Kyoo.Controllers
|
||||
private bool _disposed;
|
||||
private readonly DatabaseContext _database;
|
||||
private readonly IProviderRepository _providers;
|
||||
private readonly IShowRepository _shows;
|
||||
private readonly Lazy<IEpisodeRepository> _episodes;
|
||||
protected override Expression<Func<Season, object>> DefaultSort => x => x.SeasonNumber;
|
||||
|
||||
|
||||
public SeasonRepository(DatabaseContext database,
|
||||
IProviderRepository providers,
|
||||
IShowRepository shows,
|
||||
IServiceProvider services)
|
||||
: base(database)
|
||||
{
|
||||
_database = database;
|
||||
_providers = providers;
|
||||
_shows = shows;
|
||||
_episodes = new Lazy<IEpisodeRepository>(services.GetRequiredService<IEpisodeRepository>);
|
||||
}
|
||||
|
||||
@ -38,6 +40,7 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
_database.Dispose();
|
||||
_providers.Dispose();
|
||||
_shows.Dispose();
|
||||
if (_episodes.IsValueCreated)
|
||||
_episodes.Value.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
@ -50,10 +53,27 @@ namespace Kyoo.Controllers
|
||||
_disposed = true;
|
||||
await _database.DisposeAsync();
|
||||
await _providers.DisposeAsync();
|
||||
await _shows.DisposeAsync();
|
||||
if (_episodes.IsValueCreated)
|
||||
await _episodes.Value.DisposeAsync();
|
||||
}
|
||||
|
||||
public override async Task<Season> Get(int id)
|
||||
{
|
||||
Season ret = await base.Get(id);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override async Task<Season> Get(Expression<Func<Season, bool>> predicate)
|
||||
{
|
||||
Season ret = await base.Get(predicate);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override Task<Season> Get(string slug)
|
||||
{
|
||||
Match match = Regex.Match(slug, @"(?<show>.*)-s(?<season>\d*)");
|
||||
@ -63,24 +83,43 @@ namespace Kyoo.Controllers
|
||||
return Get(match.Groups["show"].Value, int.Parse(match.Groups["season"].Value));
|
||||
}
|
||||
|
||||
public Task<Season> Get(int showID, int seasonNumber)
|
||||
public async Task<Season> Get(int showID, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
Season ret = await _database.Seasons.FirstOrDefaultAsync(x => x.ShowID == showID
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = await _shows.GetSlug(showID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<Season> Get(string showSlug, int seasonNumber)
|
||||
public async Task<Season> Get(string showSlug, int seasonNumber)
|
||||
{
|
||||
return _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
Season ret = await _database.Seasons.FirstOrDefaultAsync(x => x.Show.Slug == showSlug
|
||||
&& x.SeasonNumber == seasonNumber);
|
||||
if (ret != null)
|
||||
ret.ShowSlug = showSlug;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override async Task<ICollection<Season>> Search(string query)
|
||||
{
|
||||
return await _database.Seasons
|
||||
List<Season> seasons = await _database.Seasons
|
||||
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%"))
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
foreach (Season season in seasons)
|
||||
season.ShowSlug = await _shows.GetSlug(season.ShowID);
|
||||
return seasons;
|
||||
}
|
||||
|
||||
public override async Task<ICollection<Season>> GetAll(Expression<Func<Season, bool>> where = null,
|
||||
Sort<Season> sort = default,
|
||||
Pagination limit = default)
|
||||
{
|
||||
ICollection<Season> seasons = await base.GetAll(where, sort, limit);
|
||||
foreach (Season season in seasons)
|
||||
season.ShowSlug = await _shows.GetSlug(season.ShowID);
|
||||
return seasons;
|
||||
}
|
||||
|
||||
public override async Task<Season> Create(Season obj)
|
||||
|
@ -151,6 +151,13 @@ namespace Kyoo.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
public Task<string> GetSlug(int showID)
|
||||
{
|
||||
return _database.Shows.Where(x => x.ID == showID)
|
||||
.Select(x => x.Slug)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public override async Task Delete(Show obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
@ -82,7 +82,7 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
if (obj.EpisodeID <= 0)
|
||||
{
|
||||
obj.EpisodeID = obj.Episode?.ID ?? -1;
|
||||
obj.EpisodeID = obj.Episode?.ID ?? 0;
|
||||
if (obj.EpisodeID <= 0)
|
||||
throw new InvalidOperationException($"Can't store a track not related to any episode (episodeID: {obj.EpisodeID}).");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user