Using nullables instead of -1

This commit is contained in:
Zoe Roux 2021-06-26 16:13:06 +02:00
parent fdb6386462
commit db5ae6f285
16 changed files with 113 additions and 98 deletions

View File

@ -16,6 +16,6 @@ namespace Kyoo.Controllers
Task<Season> GetSeason(Show show, int seasonNumber);
Task<Episode> GetEpisode(Show show, int seasonNumber, int episodeNumber, int absoluteNumber);
Task<Episode> GetEpisode(Show show, int? seasonNumber, int? episodeNumber, int? absoluteNumber);
}
}

View File

@ -11,7 +11,7 @@ namespace Kyoo.Controllers
Task<Show> SearchShow(string showName, bool isMovie, Library library);
Task<IEnumerable<Show>> SearchShows(string showName, bool isMovie, Library library);
Task<Season> GetSeason(Show show, int seasonNumber, Library library);
Task<Episode> GetEpisode(Show show, string episodePath, int seasonNumber, int episodeNumber, int absoluteNumber, Library library);
Task<Episode> GetEpisode(Show show, string episodePath, int? seasonNumber, int? episodeNumber, int? absoluteNumber, Library library);
Task<ICollection<PeopleRole>> GetPeople(Show show, Library library);
}
}

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Kyoo.Controllers;
@ -10,9 +9,8 @@ namespace Kyoo.Models
{
/// <summary>
/// A class to represent a single show's episode.
/// This is also used internally for movies (their number is juste set to -1).
/// </summary>
public class Episode : IResource, IOnMerge
public class Episode : IResource
{
/// <inheritdoc />
public int ID { get; set; }
@ -31,6 +29,7 @@ namespace Kyoo.Models
if (value == null)
throw new ArgumentNullException(nameof(value));
Console.WriteLine(value);
Match match = Regex.Match(value, @"(?<show>.+)-s(?<season>\d+)e(?<episode>\d+)");
if (match.Success)
@ -44,13 +43,13 @@ namespace Kyoo.Models
match = Regex.Match(value, @"(?<show>.*)-(?<absolute>\d*)");
if (match.Success)
{
ShowSlug = match.Groups["Show"].Value;
ShowSlug = match.Groups["show"].Value;
AbsoluteNumber = int.Parse(match.Groups["absolute"].Value);
}
else
ShowSlug = value;
SeasonNumber = -1;
EpisodeNumber = -1;
SeasonNumber = null;
EpisodeNumber = null;
}
}
}
@ -82,20 +81,17 @@ namespace Kyoo.Models
/// <summary>
/// The season in witch this episode is in.
/// </summary>
[DefaultValue(-1)]
public int SeasonNumber { get; set; } = -1;
public int? SeasonNumber { get; set; }
/// <summary>
/// The number of this episode is it's season.
/// </summary>
[DefaultValue(-1)]
public int EpisodeNumber { get; set; } = -1;
public int? EpisodeNumber { get; set; }
/// <summary>
/// The absolute number of this episode. It's an episode number that is not reset to 1 after a new season.
/// </summary>
[DefaultValue(-1)]
public int AbsoluteNumber { get; set; } = -1;
public int? AbsoluteNumber { get; set; }
/// <summary>
/// The path of the video file for this episode. Any format supported by a <see cref="IFileManager"/> is allowed.
@ -141,43 +137,31 @@ namespace Kyoo.Models
/// <param name="showSlug">The slug of the show. It can't be null.</param>
/// <param name="seasonNumber">
/// The season in which the episode is.
/// If this is a movie or if the episode should be referred by it's absolute number, set this to -1.
/// If this is a movie or if the episode should be referred by it's absolute number, set this to null.
/// </param>
/// <param name="episodeNumber">
/// The number of the episode in it's season.
/// If this is a movie or if the episode should be referred by it's absolute number, set this to -1.
/// If this is a movie or if the episode should be referred by it's absolute number, set this to null.
/// </param>
/// <param name="absoluteNumber">
/// The absolute number of this show.
/// If you don't know it or this is a movie, use -1
/// If you don't know it or this is a movie, use null
/// </param>
/// <returns>The slug corresponding to the given arguments</returns>
/// <exception cref="ArgumentNullException">The given show slug was null.</exception>
public static string GetSlug([NotNull] string showSlug,
int seasonNumber = -1,
int episodeNumber = -1,
int absoluteNumber = -1)
int? seasonNumber,
int? episodeNumber,
int? absoluteNumber = null)
{
if (showSlug == null)
throw new ArgumentNullException(nameof(showSlug));
return seasonNumber switch
{
-1 when absoluteNumber == -1 => showSlug,
-1 => $"{showSlug}-{absoluteNumber}",
null when absoluteNumber == null => showSlug,
null => $"{showSlug}-{absoluteNumber}",
_ => $"{showSlug}-s{seasonNumber}e{episodeNumber}"
};
}
/// <inheritdoc />
public void OnMerge(object merged)
{
Episode other = (Episode)merged;
if (SeasonNumber == -1 && other.SeasonNumber != -1)
SeasonNumber = other.SeasonNumber;
if (EpisodeNumber == -1 && other.EpisodeNumber != -1)
EpisodeNumber = other.EpisodeNumber;
if (AbsoluteNumber == -1 && other.AbsoluteNumber != -1)
AbsoluteNumber = other.AbsoluteNumber;
}
}
}

View File

@ -50,9 +50,9 @@ namespace Kyoo.Models
[LoadableRelation(nameof(ShowID))] public Show Show { get; set; }
/// <summary>
/// The number of this season. This can be set to 0 to indicate specials. This defaults to -1 for unset.
/// The number of this season. This can be set to 0 to indicate specials.
/// </summary>
public int SeasonNumber { get; set; } = -1;
public int SeasonNumber { get; set; }
/// <summary>
/// The title of this season.

View File

@ -118,7 +118,8 @@ namespace Kyoo.Models
[LoadableRelation] public ICollection<Season> Seasons { get; set; }
/// <summary>
/// The list of episodes in this show. If this is a movie, there will be a unique episode (with the seasonNumber and episodeNumber set to -1).
/// The list of episodes in this show.
/// If this is a movie, there will be a unique episode (with the seasonNumber and episodeNumber set to null).
/// Having an episode is necessary to store metadata and tracks.
/// </summary>
[LoadableRelation] public ICollection<Episode> Episodes { get; set; }

View File

@ -63,8 +63,8 @@ namespace Kyoo.Models
}
EpisodeSlug = Episode.GetSlug(match.Groups["show"].Value,
match.Groups["season"].Success ? int.Parse(match.Groups["season"].Value) : -1,
match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : -1);
match.Groups["season"].Success ? int.Parse(match.Groups["season"].Value) : null,
match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : null);
Language = match.Groups["language"].Value;
IsForced = match.Groups["forced"].Success;
if (match.Groups["type"].Success)

View File

@ -63,7 +63,7 @@ namespace Kyoo.Models
public class WatchedEpisode : Link<User, Episode>
{
/// <summary>
/// Where the player has stopped watching the episode (-1 if not started, else between 0 and 100).
/// Where the player has stopped watching the episode (between 0 and 100).
/// </summary>
public int WatchedPercentage { get; set; }
}

View File

@ -37,20 +37,19 @@ namespace Kyoo.Models
public string ShowSlug { get; set; }
/// <summary>
/// The season in witch this episode is in. This defaults to -1 if not specified.
/// The season in witch this episode is in.
/// </summary>
public int SeasonNumber { get; set; }
public int? SeasonNumber { get; set; }
/// <summary>
/// The number of this episode is it's season. This defaults to -1 if not specified.
/// The number of this episode is it's season.
/// </summary>
public int EpisodeNumber { get; set; }
public int? EpisodeNumber { get; set; }
/// <summary>
/// The absolute number of this episode. It's an episode number that is not reset to 1 after a new season.
/// This defaults to -1 if not specified.
/// </summary>
public int AbsoluteNumber { get; set; }
public int? AbsoluteNumber { get; set; }
/// <summary>
/// The title of this episode.
@ -148,21 +147,30 @@ namespace Kyoo.Models
await library.Load(ep, x => x.Show);
await library.Load(ep, x => x.Tracks);
if (!ep.Show.IsMovie)
if (!ep.Show.IsMovie && ep.SeasonNumber != null && ep.EpisodeNumber != null)
{
if (ep.EpisodeNumber > 1)
previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber - 1);
previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber.Value, ep.EpisodeNumber.Value - 1);
else if (ep.SeasonNumber > 1)
{
int count = await library.GetCount<Episode>(x => x.ShowID == ep.ShowID
&& x.SeasonNumber == ep.SeasonNumber - 1);
previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber - 1, count);
previous = (await library.GetAll(x => x.ShowID == ep.ShowID
&& x.SeasonNumber == ep.SeasonNumber.Value - 1,
limit: 1,
sort: new Sort<Episode>(x => x.EpisodeNumber, true))
).FirstOrDefault();
}
if (ep.EpisodeNumber >= await library.GetCount<Episode>(x => x.SeasonID == ep.SeasonID))
next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber + 1, 1);
next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber.Value + 1, 1);
else
next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber + 1);
next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber.Value, ep.EpisodeNumber.Value + 1);
}
else if (!ep.Show.IsMovie && ep.AbsoluteNumber != null)
{
previous = await library.GetOrDefault<Episode>(x => x.ShowID == ep.ShowID
&& x.AbsoluteNumber == ep.EpisodeNumber + 1);
next = await library.GetOrDefault<Episode>(x => x.ShowID == ep.ShowID
&& x.AbsoluteNumber == ep.AbsoluteNumber + 1);
}
return new WatchItem

View File

@ -25,14 +25,25 @@ namespace Kyoo.SqLite.Migrations
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
UPDATE Episodes
SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) ||
CASE (SeasonNumber)
WHEN NULL THEN '-' || AbsoluteNumber
ELSE '-s' || SeasonNumber || 'e' || EpisodeNumber
END
WHERE ID == new.ID;
END");
// language=SQLite
migrationBuilder.Sql(@"
CREATE TRIGGER EpisodeSlugUpdate AFTER UPDATE OF EpisodeNumber, SeasonNumber, ShowID ON Episodes FOR EACH ROW
CREATE TRIGGER EpisodeSlugUpdate AFTER UPDATE OF AbsoluteNumber, EpisodeNumber, SeasonNumber, ShowID
ON Episodes FOR EACH ROW
BEGIN
UPDATE Episodes SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber || 'e' || EpisodeNumber
UPDATE Episodes
SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) ||
CASE (SeasonNumber)
WHEN NULL THEN '-' || AbsoluteNumber
ELSE '-s' || SeasonNumber || 'e' || EpisodeNumber
END
WHERE ID == new.ID;
END");
@ -42,7 +53,13 @@ namespace Kyoo.SqLite.Migrations
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;
UPDATE Episodes
SET Slug = new.Slug ||
CASE (SeasonNumber)
WHEN NULL THEN '-' || AbsoluteNumber
ELSE '-s' || SeasonNumber || 'e' || EpisodeNumber
END
WHERE ShowID = new.ID;
END;");
}

View File

@ -55,11 +55,12 @@ namespace Kyoo.Tests.Library
{
Episode episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
await _repository.Edit(new Episode
episode = await _repository.Edit(new Episode
{
ID = 1,
SeasonNumber = 2
}, false);
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e2", episode.Slug);
episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get<Show>().Slug}-s2e1", episode.Slug);
}
@ -69,11 +70,12 @@ namespace Kyoo.Tests.Library
{
Episode episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e1", episode.Slug);
await _repository.Edit(new Episode
episode = await _repository.Edit(new Episode
{
ID = 1,
EpisodeNumber = 2
}, false);
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
}
@ -127,12 +129,13 @@ namespace Kyoo.Tests.Library
public async Task AbsoluteNumberEditTest()
{
await _repository.Create(TestSample.GetAbsoluteEpisode());
await _repository.Edit(new Episode
Episode episode = await _repository.Edit(new Episode
{
ID = 1,
ID = 2,
AbsoluteNumber = 56
}, false);
Episode episode = await _repository.Get(2);
Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
episode = await _repository.Get(2);
Assert.Equal($"{TestSample.Get<Show>().Slug}-56", episode.Slug);
}
@ -140,27 +143,28 @@ namespace Kyoo.Tests.Library
public async Task AbsoluteToNormalEditTest()
{
await _repository.Create(TestSample.GetAbsoluteEpisode());
await _repository.Edit(new Episode
Episode episode = await _repository.Edit(new Episode
{
ID = 1,
ID = 2,
SeasonNumber = 1,
EpisodeNumber = 2
}, false);
Episode episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
episode = await _repository.Get(2);
Assert.Equal($"{TestSample.Get<Show>().Slug}-s1e2", episode.Slug);
}
[Fact]
public async Task NormalToAbsoluteEditTest()
{
await _repository.Create(TestSample.GetAbsoluteEpisode());
await _repository.Edit(new Episode
Episode episode = await _repository.Edit(new Episode
{
ID = 1,
SeasonNumber = -1,
SeasonNumber = null,
AbsoluteNumber = 12
}, false);
Episode episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
episode = await _repository.Get(1);
Assert.Equal($"{TestSample.Get<Show>().Slug}-12", episode.Slug);
}
}

View File

@ -129,9 +129,8 @@ namespace Kyoo.Tests
ID = 2,
ShowSlug = "anohana",
ShowID = 1,
SeasonID = -1,
SeasonNumber = -1,
EpisodeNumber = -1,
SeasonNumber = null,
EpisodeNumber = null,
AbsoluteNumber = 3,
Path = "/home/kyoo/anohana-3",
Thumb = "thumbnail",

View File

@ -122,16 +122,15 @@ namespace Kyoo.Controllers
season.Show = show;
season.ShowID = show.ID;
season.ShowSlug = show.Slug;
season.SeasonNumber = season.SeasonNumber == -1 ? seasonNumber : season.SeasonNumber;
season.Title ??= $"Season {season.SeasonNumber}";
return season;
}
public async Task<Episode> GetEpisode(Show show,
string episodePath,
int seasonNumber,
int episodeNumber,
int absoluteNumber,
int? seasonNumber,
int? episodeNumber,
int? absoluteNumber,
Library library)
{
Episode episode = await GetMetadata(
@ -142,9 +141,9 @@ namespace Kyoo.Controllers
episode.ShowID = show.ID;
episode.ShowSlug = show.Slug;
episode.Path = episodePath;
episode.SeasonNumber = episode.SeasonNumber != -1 ? episode.SeasonNumber : seasonNumber;
episode.EpisodeNumber = episode.EpisodeNumber != -1 ? episode.EpisodeNumber : episodeNumber;
episode.AbsoluteNumber = episode.AbsoluteNumber != -1 ? episode.AbsoluteNumber : absoluteNumber;
episode.SeasonNumber ??= seasonNumber;
episode.EpisodeNumber ??= episodeNumber;
episode.AbsoluteNumber ??= absoluteNumber;
return episode;
}

View File

@ -99,7 +99,7 @@ namespace Kyoo.Controllers
public override async Task<ICollection<Episode>> Search(string query)
{
return await _database.Episodes
.Where(x => x.EpisodeNumber != -1)
.Where(x => x.EpisodeNumber != null)
.Where(_database.Like<Episode>(x => x.Title, $"%{query}%"))
.OrderBy(DefaultSort)
.Take(20)

View File

@ -67,8 +67,8 @@ namespace Kyoo.Controllers
}
string showSlug = match.Groups["show"].Value;
int seasonNumber = match.Groups["season"].Success ? int.Parse(match.Groups["season"].Value) : -1;
int episodeNumber = match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : -1;
int? seasonNumber = match.Groups["season"].Success ? int.Parse(match.Groups["season"].Value) : null;
int? episodeNumber = match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : null;
string language = match.Groups["language"].Value;
bool forced = match.Groups["forced"].Success;
if (match.Groups["type"].Success)

View File

@ -47,8 +47,8 @@ namespace Kyoo
// TODO remove postgres from here and load it like a normal plugin.
_plugins.LoadPlugins(new IPlugin[] {
new CoreModule(configuration),
new PostgresModule(configuration, host),
// new SqLiteModule(configuration, host),
// new PostgresModule(configuration, host),
new SqLiteModule(configuration, host),
new AuthenticationModule(configuration, loggerFactory, host)
});
}

View File

@ -210,18 +210,20 @@ namespace Kyoo.Tasks
string showPath = Path.GetDirectoryName(path);
string collectionName = match.Groups["Collection"].Value;
string showName = match.Groups["Show"].Value;
int seasonNumber = int.TryParse(match.Groups["Season"].Value, out int tmp) ? tmp : -1;
int episodeNumber = int.TryParse(match.Groups["Episode"].Value, out tmp) ? tmp : -1;
int absoluteNumber = int.TryParse(match.Groups["Absolute"].Value, out tmp) ? tmp : -1;
int? seasonNumber = int.TryParse(match.Groups["Season"].Value, out int tmp) ? tmp : null;
int? episodeNumber = int.TryParse(match.Groups["Episode"].Value, out tmp) ? tmp : null;
int? absoluteNumber = int.TryParse(match.Groups["Absolute"].Value, out tmp) ? tmp : null;
Collection collection = await GetCollection(libraryManager, collectionName, library);
bool isMovie = seasonNumber == -1 && episodeNumber == -1 && absoluteNumber == -1;
bool isMovie = seasonNumber == null && episodeNumber == null && absoluteNumber == null;
Show show = await GetShow(libraryManager, showName, showPath, isMovie, library);
if (isMovie)
await libraryManager!.Create(await GetMovie(show, path));
else
{
Season season = await GetSeason(libraryManager, show, seasonNumber, library);
Season season = seasonNumber != null
? await GetSeason(libraryManager, show, seasonNumber.Value, library)
: null;
Episode episode = await GetEpisode(libraryManager,
show,
season,
@ -315,8 +317,6 @@ namespace Kyoo.Tasks
int seasonNumber,
Library library)
{
if (seasonNumber == -1)
return default;
try
{
Season season = await libraryManager.Get(show.Slug, seasonNumber);
@ -343,21 +343,24 @@ namespace Kyoo.Tasks
private async Task<Episode> GetEpisode(ILibraryManager libraryManager,
Show show,
Season season,
int episodeNumber,
int absoluteNumber,
int? episodeNumber,
int? absoluteNumber,
string episodePath,
Library library)
{
Episode episode = await MetadataProvider.GetEpisode(show,
episodePath,
season?.SeasonNumber ?? -1,
season?.SeasonNumber,
episodeNumber,
absoluteNumber,
library);
season ??= await GetSeason(libraryManager, show, episode.SeasonNumber, library);
if (episode.SeasonNumber != null)
{
season ??= await GetSeason(libraryManager, show, episode.SeasonNumber.Value, library);
episode.Season = season;
episode.SeasonID = season?.ID;
}
await ThumbnailsManager.Validate(episode);
await GetTracks(episode);
return episode;