Adding the missing documentation on every classes and methods

This commit is contained in:
Zoe Roux 2021-10-07 21:48:40 +02:00
parent 6ee5f3ef24
commit fd50a2dedc
18 changed files with 159 additions and 47 deletions

View File

@ -45,7 +45,6 @@
<PropertyGroup Condition="$(CheckCodingStyle) == true"> <PropertyGroup Condition="$(CheckCodingStyle) == true">
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>CS1591;SA1600;SA1601</NoWarn>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)../Kyoo.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)../Kyoo.ruleset</CodeAnalysisRuleSet>
<!-- <AnalysisMode>AllEnabledByDefault</AnalysisMode>--> <!-- <AnalysisMode>AllEnabledByDefault</AnalysisMode>-->

View File

@ -105,9 +105,17 @@ namespace Kyoo.Abstractions.Models
return CreateReference(path, typeof(T)); return CreateReference(path, typeof(T));
} }
/// <summary>
/// Return a <see cref="ConfigurationReference"/> meaning that the given path is of any type.
/// It means that the type can't be edited.
/// </summary>
/// <param name="path">
/// The path that will be untyped (separated by ':' or "__". If empty, it will start at root).
/// </param>
/// <returns>A configuration reference representing a path of any type.</returns>
public static ConfigurationReference CreateUntyped(string path) public static ConfigurationReference CreateUntyped(string path)
{ {
return new(path, null); return new ConfigurationReference(path, null);
} }
} }
} }

View File

@ -425,6 +425,11 @@ namespace Kyoo.Utils
return (T)method.MakeGenericMethod(types).Invoke(instance, args.ToArray()); return (T)method.MakeGenericMethod(types).Invoke(instance, args.ToArray());
} }
/// <summary>
/// Convert a dictionary to a query string.
/// </summary>
/// <param name="query">The list of query parameters.</param>
/// <returns>A valid query string with all items in the dictionary.</returns>
public static string ToQueryString(this Dictionary<string, string> query) public static string ToQueryString(this Dictionary<string, string> query)
{ {
if (!query.Any()) if (!query.Any())
@ -432,6 +437,11 @@ namespace Kyoo.Utils
return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}")); return "?" + string.Join('&', query.Select(x => $"{x.Key}={x.Value}"));
} }
/// <summary>
/// Rethrow the exception without modifying the stack trace.
/// This is similar to the <c>rethrow;</c> code but is useful when the exception is not in a catch block.
/// </summary>
/// <param name="ex">The exception to rethrow.</param>
[System.Diagnostics.CodeAnalysis.DoesNotReturn] [System.Diagnostics.CodeAnalysis.DoesNotReturn]
public static void ReThrow([NotNull] this Exception ex) public static void ReThrow([NotNull] this Exception ex)
{ {

View File

@ -23,6 +23,9 @@ using IdentityModel;
namespace Kyoo.Authentication namespace Kyoo.Authentication
{ {
/// <summary>
/// Some functions to handle password management.
/// </summary>
public static class PasswordUtils public static class PasswordUtils
{ {
/// <summary> /// <summary>

View File

@ -32,6 +32,11 @@ using Newtonsoft.Json.Linq;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers
{ {
/// <summary>
/// A class to ease configuration management. This work WITH Microsoft's package, you can still use IOptions patterns
/// to access your options, this manager ease dynamic work and editing.
/// It works with <see cref="ConfigurationReference"/>.
/// </summary>
public class ConfigurationManager : IConfigurationManager public class ConfigurationManager : IConfigurationManager
{ {
/// <summary> /// <summary>

View File

@ -165,11 +165,13 @@ namespace Kyoo.Core.Controllers
}); });
} }
/// <inheritdoc />
public Task<ICollection<Track>> ExtractInfos(Episode episode, bool reExtract) public Task<ICollection<Track>> ExtractInfos(Episode episode, bool reExtract)
{ {
return _transcoder.ExtractInfos(episode, reExtract); return _transcoder.ExtractInfos(episode, reExtract);
} }
/// <inheritdoc />
public IActionResult Transmux(Episode episode) public IActionResult Transmux(Episode episode)
{ {
return _transcoder.Transmux(episode); return _transcoder.Transmux(episode);

View File

@ -29,6 +29,9 @@ using Kyoo.Utils;
namespace Kyoo.Core.Controllers namespace Kyoo.Core.Controllers
{ {
/// <summary>
/// An class to interact with the database. Every repository is mapped through here.
/// </summary>
public class LibraryManager : ILibraryManager public class LibraryManager : ILibraryManager
{ {
/// <summary> /// <summary>

View File

@ -29,6 +29,10 @@ namespace Kyoo.Core.Controllers
/// </summary> /// </summary>
public class PassthroughPermissionValidator : IPermissionValidator public class PassthroughPermissionValidator : IPermissionValidator
{ {
/// <summary>
/// Create a new <see cref="PassthroughPermissionValidator"/>.
/// </summary>
/// <param name="logger">The logger used to warn that no real permission validator exists.</param>
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor", [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor",
Justification = "ILogger should include the typeparam for context.")] Justification = "ILogger should include the typeparam for context.")]
public PassthroughPermissionValidator(ILogger<PassthroughPermissionValidator> logger) public PassthroughPermissionValidator(ILogger<PassthroughPermissionValidator> logger)

View File

@ -22,6 +22,9 @@ using Newtonsoft.Json;
namespace Kyoo.Core namespace Kyoo.Core
{ {
/// <summary>
/// A class containing helper methods.
/// </summary>
public static class Helper public static class Helper
{ {
/// <summary> /// <summary>

View File

@ -22,24 +22,53 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using JetBrains.Annotations;
using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api
{ {
/// <summary>
/// A static class containing methods to parse the <c>where</c> query string.
/// </summary>
public static class ApiHelper public static class ApiHelper
{ {
public static Expression StringCompatibleExpression(Func<Expression, Expression, BinaryExpression> operand, /// <summary>
Expression left, /// Make an expression (like
Expression right) /// <see cref="Expression.LessThan(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)"/>
/// compatible with strings). If the expressions are not strings, the given <paramref name="operand"/> is
/// constructed but if the expressions are strings, this method make the <param name="operand"/> compatible with
/// strings.
/// </summary>
/// <param name="operand">
/// The expression to make compatible. It should be something like
/// <see cref="Expression.LessThan(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)"/> or
/// <see cref="Expression.Equal(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)"/>.
/// </param>
/// <param name="left">The first parameter to compare.</param>
/// <param name="right">The second parameter to compare.</param>
/// <returns>A comparison expression compatible with strings</returns>
public static BinaryExpression StringCompatibleExpression(
[NotNull] Func<Expression, Expression, BinaryExpression> operand,
[NotNull] Expression left,
[NotNull] Expression right)
{ {
if (left is MemberExpression member && ((PropertyInfo)member.Member).PropertyType == typeof(string)) if (left is not MemberExpression member || ((PropertyInfo)member.Member).PropertyType != typeof(string))
{ return operand(left, right);
MethodCallExpression call = Expression.Call(typeof(string), "Compare", null, left, right); MethodCallExpression call = Expression.Call(typeof(string), "Compare", null, left, right);
return operand(call, Expression.Constant(0)); return operand(call, Expression.Constant(0));
}
return operand(left, right);
} }
/// <summary>
/// Parse a <c>where</c> query for the given <typeparamref name="T"/>. Items can be filtered by any property
/// of the given type.
/// </summary>
/// <param name="where">The list of filters.</param>
/// <param name="defaultWhere">
/// A custom expression to initially filter a collection. It will be combined with the parsed expression.
/// </param>
/// <typeparam name="T">The type to create filters for.</typeparam>
/// <exception cref="ArgumentException">A filter is invalid.</exception>
/// <returns>An expression representing the filters that can be used anywhere or compiled</returns>
public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where, public static Expression<Func<T, bool>> ParseWhere<T>(Dictionary<string, string> where,
Expression<Func<T, bool>> defaultWhere = null) Expression<Func<T, bool>> defaultWhere = null)
{ {
@ -96,18 +125,17 @@ namespace Kyoo.Core.Api
"not" when valueExpr == null => _ResourceEqual(propertyExpr, value, true), "not" when valueExpr == null => _ResourceEqual(propertyExpr, value, true),
"eq" => Expression.Equal(propertyExpr, valueExpr), "eq" => Expression.Equal(propertyExpr, valueExpr),
"not" => Expression.NotEqual(propertyExpr, valueExpr!), "not" => Expression.NotEqual(propertyExpr, valueExpr),
"lt" => StringCompatibleExpression(Expression.LessThan, propertyExpr, valueExpr), "lt" => StringCompatibleExpression(Expression.LessThan, propertyExpr, valueExpr!),
"lte" => StringCompatibleExpression(Expression.LessThanOrEqual, propertyExpr, valueExpr), "lte" => StringCompatibleExpression(Expression.LessThanOrEqual, propertyExpr, valueExpr!),
"gt" => StringCompatibleExpression(Expression.GreaterThan, propertyExpr, valueExpr), "gt" => StringCompatibleExpression(Expression.GreaterThan, propertyExpr, valueExpr!),
"gte" => StringCompatibleExpression(Expression.GreaterThanOrEqual, propertyExpr, valueExpr), "gte" => StringCompatibleExpression(Expression.GreaterThanOrEqual, propertyExpr, valueExpr!),
_ => throw new ArgumentException($"Invalid operand: {operand}") _ => throw new ArgumentException($"Invalid operand: {operand}")
}; };
if (expression != null) expression = expression != null
expression = Expression.AndAlso(expression, condition); ? Expression.AndAlso(expression, condition)
else : condition;
expression = condition;
} }
return Expression.Lambda<Func<T, bool>>(expression!, param); return Expression.Lambda<Func<T, bool>>(expression!, param);

View File

@ -27,7 +27,10 @@ using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api
{ {
public class BaseApi : ControllerBase /// <summary>
/// A common API containing custom methods to help inheritors.
/// </summary>
public abstract class BaseApi : ControllerBase
{ {
/// <summary> /// <summary>
/// Construct and return a page from an api. /// Construct and return a page from an api.

View File

@ -32,8 +32,13 @@ using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api
{ {
/// <summary>
/// An attribute to put on most controllers. It handle fields loading (only retuning fields requested and if they
/// are requested, load them) and help for the <c>where</c> query parameter.
/// </summary>
public class ResourceViewAttribute : ActionFilterAttribute public class ResourceViewAttribute : ActionFilterAttribute
{ {
/// <inheritdoc />
public override void OnActionExecuting(ActionExecutingContext context) public override void OnActionExecuting(ActionExecutingContext context)
{ {
if (context.ActionArguments.TryGetValue("where", out object dic) && dic is Dictionary<string, string> where) if (context.ActionArguments.TryGetValue("where", out object dic) && dic is Dictionary<string, string> where)
@ -92,6 +97,7 @@ namespace Kyoo.Core.Api
base.OnActionExecuting(context); base.OnActionExecuting(context);
} }
/// <inheritdoc />
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{ {
if (context.Result is ObjectResult result) if (context.Result is ObjectResult result)

View File

@ -24,8 +24,13 @@ using Newtonsoft.Json.Linq;
namespace Kyoo.Core.Api namespace Kyoo.Core.Api
{ {
/// <summary>
/// A custom role's convertor to inline the person or the show depending on the value of
/// <see cref="PeopleRole.ForPeople"/>.
/// </summary>
public class PeopleRoleConverter : JsonConverter<PeopleRole> public class PeopleRoleConverter : JsonConverter<PeopleRole>
{ {
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, PeopleRole value, JsonSerializer serializer) public override void WriteJson(JsonWriter writer, PeopleRole value, JsonSerializer serializer)
{ {
ICollection<PeopleRole> oldPeople = value.Show?.People; ICollection<PeopleRole> oldPeople = value.Show?.People;
@ -46,6 +51,7 @@ namespace Kyoo.Core.Api
value.People.Roles = oldRoles; value.People.Roles = oldRoles;
} }
/// <inheritdoc />
public override PeopleRole ReadJson(JsonReader reader, public override PeopleRole ReadJson(JsonReader reader,
Type objectType, Type objectType,
PeopleRole existingValue, PeopleRole existingValue,

View File

@ -41,9 +41,21 @@ namespace Kyoo.Core.Api
[ApiDefinition("Videos", Group = WatchGroup)] [ApiDefinition("Videos", Group = WatchGroup)]
public class VideoApi : Controller public class VideoApi : Controller
{ {
/// <summary>
/// The library manager used to modify or retrieve information in the data store.
/// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
/// <summary>
/// The file system used to send video files.
/// </summary>
private readonly IFileSystem _files; private readonly IFileSystem _files;
/// <summary>
/// Create a new <see cref="VideoApi"/>.
/// </summary>
/// <param name="libraryManager">The library manager used to retrieve episodes.</param>
/// <param name="files">The file manager used to send video files.</param>
public VideoApi(ILibraryManager libraryManager, public VideoApi(ILibraryManager libraryManager,
IFileSystem files) IFileSystem files)
{ {

View File

@ -24,8 +24,12 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Kyoo.Postgresql.Migrations namespace Kyoo.Postgresql.Migrations
{ {
/// <summary>
/// The initial migration that build most of the database.
/// </summary>
public partial class Initial : Migration public partial class Initial : Migration
{ {
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.AlterDatabase() migrationBuilder.AlterDatabase()
@ -783,6 +787,7 @@ namespace Kyoo.Postgresql.Migrations
column: "episode_id"); column: "episode_id");
} }
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable( migrationBuilder.DropTable(

View File

@ -20,8 +20,12 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Kyoo.Postgresql.Migrations namespace Kyoo.Postgresql.Migrations
{ {
/// <summary>
/// A migration that adds postgres triggers to update slugs.
/// </summary>
public partial class Triggers : Migration public partial class Triggers : Migration
{ {
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
// language=PostgreSQL // language=PostgreSQL
@ -42,7 +46,7 @@ namespace Kyoo.Postgresql.Migrations
// language=PostgreSQL // language=PostgreSQL
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER season_slug_trigger BEFORE INSERT OR UPDATE OF season_number, show_id ON seasons CREATE TRIGGER season_slug_trigger BEFORE INSERT OR UPDATE OF season_number, show_id ON seasons
FOR EACH ROW EXECUTE PROCEDURE season_slug_update();"); FOR EACH ROW EXECUTE PROCEDURE season_slug_update();");
// language=PostgreSQL // language=PostgreSQL
@ -66,7 +70,7 @@ namespace Kyoo.Postgresql.Migrations
// language=PostgreSQL // language=PostgreSQL
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER episode_slug_trigger CREATE TRIGGER episode_slug_trigger
BEFORE INSERT OR UPDATE OF absolute_number, episode_number, season_number, show_id ON episodes BEFORE INSERT OR UPDATE OF absolute_number, episode_number, season_number, show_id ON episodes
FOR EACH ROW EXECUTE PROCEDURE episode_slug_update();"); FOR EACH ROW EXECUTE PROCEDURE episode_slug_update();");
@ -80,7 +84,7 @@ namespace Kyoo.Postgresql.Migrations
UPDATE seasons SET slug = CONCAT(NEW.slug, '-s', season_number) WHERE show_id = NEW.id; UPDATE seasons SET slug = CONCAT(NEW.slug, '-s', season_number) WHERE show_id = NEW.id;
UPDATE episodes SET slug = CASE UPDATE episodes SET slug = CASE
WHEN season_number IS NULL AND episode_number IS NULL THEN NEW.slug WHEN season_number IS NULL AND episode_number IS NULL THEN NEW.slug
WHEN season_number IS NULL THEN CONCAT(NEW.slug, '-', absolute_number) WHEN season_number IS NULL THEN CONCAT(NEW.slug, '-', absolute_number)
ELSE CONCAT(NEW.slug, '-s', season_number, 'e', episode_number) ELSE CONCAT(NEW.slug, '-s', season_number, 'e', episode_number)
END WHERE show_id = NEW.id; END WHERE show_id = NEW.id;
RETURN NEW; RETURN NEW;
@ -128,7 +132,7 @@ namespace Kyoo.Postgresql.Migrations
BEGIN BEGIN
IF NEW.track_index = 0 THEN IF NEW.track_index = 0 THEN
NEW.track_index := (SELECT COUNT(*) FROM tracks NEW.track_index := (SELECT COUNT(*) FROM tracks
WHERE episode_id = NEW.episode_id AND type = NEW.type WHERE episode_id = NEW.episode_id AND type = NEW.type
AND language = NEW.language AND is_forced = NEW.is_forced); AND language = NEW.language AND is_forced = NEW.is_forced);
END IF; END IF;
NEW.slug := CONCAT( NEW.slug := CONCAT(
@ -149,7 +153,7 @@ namespace Kyoo.Postgresql.Migrations
$$;"); $$;");
// language=PostgreSQL // language=PostgreSQL
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER track_slug_trigger CREATE TRIGGER track_slug_trigger
BEFORE INSERT OR UPDATE OF episode_id, is_forced, language, track_index, type ON tracks BEFORE INSERT OR UPDATE OF episode_id, is_forced, language, track_index, type ON tracks
FOR EACH ROW EXECUTE PROCEDURE track_slug_update();"); FOR EACH ROW EXECUTE PROCEDURE track_slug_update();");
@ -167,11 +171,12 @@ namespace Kyoo.Postgresql.Migrations
INNER JOIN collections AS c ON l.collection_id = c.id INNER JOIN collections AS c ON l.collection_id = c.id
WHERE s.id = l.show_id)) WHERE s.id = l.show_id))
UNION ALL UNION ALL
SELECT -c0.id, c0.slug, c0.name AS title, c0.overview, 'unknown'::status AS status, SELECT -c0.id, c0.slug, c0.name AS title, c0.overview, 'unknown'::status AS status,
NULL AS start_air, NULL AS end_air, c0.images, 'collection'::item_type AS type NULL AS start_air, NULL AS end_air, c0.images, 'collection'::item_type AS type
FROM collections AS c0"); FROM collections AS c0");
} }
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
// language=PostgreSQL // language=PostgreSQL

View File

@ -21,8 +21,12 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Kyoo.SqLite.Migrations namespace Kyoo.SqLite.Migrations
{ {
/// <summary>
/// The initial migration that build most of the database.
/// </summary>
public partial class Initial : Migration public partial class Initial : Migration
{ {
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
@ -775,6 +779,7 @@ namespace Kyoo.SqLite.Migrations
column: "EpisodeID"); column: "EpisodeID");
} }
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.DropTable( migrationBuilder.DropTable(

View File

@ -20,31 +20,35 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Kyoo.SqLite.Migrations namespace Kyoo.SqLite.Migrations
{ {
/// <summary>
/// A migration that adds sqlite triggers to update slugs.
/// </summary>
public partial class Triggers : Migration public partial class Triggers : Migration
{ {
/// <inheritdoc/>
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
// language=SQLite // language=SQLite
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER SeasonSlugInsert AFTER INSERT ON Seasons FOR EACH ROW CREATE TRIGGER SeasonSlugInsert AFTER INSERT ON Seasons FOR EACH ROW
BEGIN BEGIN
UPDATE Seasons SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber UPDATE Seasons SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber
WHERE ID == new.ID; WHERE ID == new.ID;
END"); END");
// language=SQLite // language=SQLite
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER SeasonSlugUpdate AFTER UPDATE OF SeasonNumber, ShowID ON Seasons FOR EACH ROW CREATE TRIGGER SeasonSlugUpdate AFTER UPDATE OF SeasonNumber, ShowID ON Seasons FOR EACH ROW
BEGIN BEGIN
UPDATE Seasons SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber UPDATE Seasons SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || '-s' || SeasonNumber
WHERE ID == new.ID; WHERE ID == new.ID;
END"); END");
// language=SQLite // language=SQLite
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER EpisodeSlugInsert AFTER INSERT ON Episodes FOR EACH ROW CREATE TRIGGER EpisodeSlugInsert AFTER INSERT ON Episodes FOR EACH ROW
BEGIN BEGIN
UPDATE Episodes UPDATE Episodes
SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) ||
CASE CASE
WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN '' WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN ''
WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber
@ -54,11 +58,11 @@ namespace Kyoo.SqLite.Migrations
END"); END");
// language=SQLite // language=SQLite
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER EpisodeSlugUpdate AFTER UPDATE OF AbsoluteNumber, EpisodeNumber, SeasonNumber, ShowID CREATE TRIGGER EpisodeSlugUpdate AFTER UPDATE OF AbsoluteNumber, EpisodeNumber, SeasonNumber, ShowID
ON Episodes FOR EACH ROW ON Episodes FOR EACH ROW
BEGIN BEGIN
UPDATE Episodes UPDATE Episodes
SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) || SET Slug = (SELECT Slug from Shows WHERE ID = ShowID) ||
CASE CASE
WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN '' WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN ''
WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber
@ -69,7 +73,7 @@ namespace Kyoo.SqLite.Migrations
// language=SQLite // language=SQLite
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER TrackSlugInsert CREATE TRIGGER TrackSlugInsert
AFTER INSERT ON Tracks AFTER INSERT ON Tracks
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
@ -98,7 +102,7 @@ namespace Kyoo.SqLite.Migrations
END;"); END;");
// language=SQLite // language=SQLite
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER TrackSlugUpdate CREATE TRIGGER TrackSlugUpdate
AFTER UPDATE OF EpisodeID, IsForced, Language, TrackIndex, Type ON Tracks AFTER UPDATE OF EpisodeID, IsForced, Language, TrackIndex, Type ON Tracks
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
@ -107,7 +111,7 @@ namespace Kyoo.SqLite.Migrations
WHERE EpisodeID = new.EpisodeID AND Type = new.Type WHERE EpisodeID = new.EpisodeID AND Type = new.Type
AND Language = new.Language AND IsForced = new.IsForced AND Language = new.Language AND IsForced = new.IsForced
) WHERE ID = new.ID AND TrackIndex = 0; ) WHERE ID = new.ID AND TrackIndex = 0;
UPDATE Tracks SET Slug = UPDATE Tracks SET Slug =
(SELECT Slug FROM Episodes WHERE ID = EpisodeID) || (SELECT Slug FROM Episodes WHERE ID = EpisodeID) ||
'.' || Language || '.' || Language ||
CASE (TrackIndex) CASE (TrackIndex)
@ -128,7 +132,7 @@ namespace Kyoo.SqLite.Migrations
END;"); END;");
// language=SQLite // language=SQLite
migrationBuilder.Sql(@" migrationBuilder.Sql(@"
CREATE TRIGGER EpisodeUpdateTracksSlug CREATE TRIGGER EpisodeUpdateTracksSlug
AFTER UPDATE OF Slug ON Episodes AFTER UPDATE OF Slug ON Episodes
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
@ -157,8 +161,8 @@ namespace Kyoo.SqLite.Migrations
CREATE TRIGGER ShowSlugUpdate AFTER UPDATE OF Slug ON Shows FOR EACH ROW CREATE TRIGGER ShowSlugUpdate AFTER UPDATE OF Slug ON Shows FOR EACH ROW
BEGIN BEGIN
UPDATE Seasons SET Slug = new.Slug || '-s' || SeasonNumber WHERE ShowID = new.ID; UPDATE Seasons SET Slug = new.Slug || '-s' || SeasonNumber WHERE ShowID = new.ID;
UPDATE Episodes UPDATE Episodes
SET Slug = new.Slug || SET Slug = new.Slug ||
CASE CASE
WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN '' WHEN SeasonNumber IS NULL AND AbsoluteNumber IS NULL THEN ''
WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber WHEN SeasonNumber IS NULL THEN '-' || AbsoluteNumber
@ -181,11 +185,12 @@ namespace Kyoo.SqLite.Migrations
INNER JOIN Collections AS c ON l.CollectionID = c.ID INNER JOIN Collections AS c ON l.CollectionID = c.ID
WHERE s.ID = l.ShowID)) WHERE s.ID = l.ShowID))
UNION ALL UNION ALL
SELECT -c0.ID, c0.Slug, c0.Name AS Title, c0.Overview, 0 AS Status, SELECT -c0.ID, c0.Slug, c0.Name AS Title, c0.Overview, 0 AS Status,
NULL AS StartAir, NULL AS EndAir, c0.Images, 2 AS Type NULL AS StartAir, NULL AS EndAir, c0.Images, 2 AS Type
FROM collections AS c0"); FROM collections AS c0");
} }
/// <inheritdoc/>
protected override void Down(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder)
{ {
// language=SQLite // language=SQLite