diff --git a/back/src/Directory.Build.props b/back/src/Directory.Build.props index 396e44e6..c0c382c2 100644 --- a/back/src/Directory.Build.props +++ b/back/src/Directory.Build.props @@ -29,13 +29,6 @@ - true - true - true - - - true - true diff --git a/back/src/Kyoo.Abstractions/Controllers/IFileSystem.cs b/back/src/Kyoo.Abstractions/Controllers/IFileSystem.cs deleted file mode 100644 index f70a577b..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/IFileSystem.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using JetBrains.Annotations; -using Kyoo.Abstractions.Models; -using Microsoft.AspNetCore.Mvc; - -namespace Kyoo.Abstractions.Controllers -{ - /// - /// A service to abstract the file system to allow custom file systems - /// (like distant file systems or external providers). - /// - public interface IFileSystem - { - /// - /// Used for http queries returning a file. This should be used to return local files - /// or proxy them from a distant server. - /// - /// - /// If no file exists at the given path or if the path is null, a NotFoundResult is returned - /// to handle it gracefully. - /// - /// The path of the file. - /// - /// Should the file be downloaded at once or is the client allowed to request only part of the file - /// - /// - /// You can manually specify the content type of your file. - /// For example you can force a file to be returned as plain text using text/plain. - /// If the type is not specified, it will be deduced automatically (from the extension or by sniffing the file). - /// - /// An representing the file returned. - IActionResult FileResult([CanBeNull] string path, bool rangeSupport = false, string type = null); - - /// - /// Read a file present at . The reader can be used in an arbitrary context. - /// To return files from an http endpoint, use . - /// - /// The path of the file - /// If the file could not be found. - /// A reader to read the file. - Task GetReader([NotNull] string path); - - /// - /// Read a file present at . The reader can be used in an arbitrary context. - /// To return files from an http endpoint, use . - /// - /// The path of the file - /// The mime type of the opened file. - /// If the file could not be found. - /// A reader to read the file. - Task GetReader([NotNull] string path, AsyncRef mime); - - /// - /// Create a new file at . - /// - /// The path of the new file. - /// A writer to write to the new file. - Task NewFile([NotNull] string path); - - /// - /// Create a new directory at the given path - /// - /// The path of the directory - /// The path of the newly created directory is returned. - Task CreateDirectory([NotNull] string path); - - /// - /// Combine multiple paths. - /// - /// The paths to combine - /// The combined path. - string Combine(params string[] paths); - - /// - /// List files in a directory. - /// - /// The path of the directory - /// Should the search be recursive or not. - /// A list of files's path. - Task> ListFiles([NotNull] string path, - SearchOption options = SearchOption.TopDirectoryOnly); - - /// - /// Check if a file exists at the given path. - /// - /// The path to check - /// True if the path exists, false otherwise - Task Exists([NotNull] string path); - - /// - /// Get the extra directory of a resource . - /// This method is in this system to allow a filesystem to use a different metadata policy for one. - /// It can be useful if the filesystem is readonly. - /// - /// The resource to proceed - /// The type of the resource. - /// The extra directory of the resource. - Task GetExtraDirectory([NotNull] T resource); - - /// - /// Retrieve tracks for a specific episode. - /// Subtitles, chapters and fonts should also be extracted and cached when calling this method. - /// - /// The episode to retrieve tracks for. - /// Should the cache be invalidated and subtitles and others be re-extracted? - /// The list of tracks available for this episode. - Task> ExtractInfos([NotNull] Episode episode, bool reExtract); - - /// - /// Transmux the selected episode to hls. - /// - /// The episode to transmux. - /// The master file (m3u8) of the transmuxed hls file. - IActionResult Transmux([NotNull] Episode episode); - - // Maybe add options for to select the codec. - // IActionResult Transcode(Episode episode); - } -} diff --git a/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs b/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs index 624f72eb..792e1978 100644 --- a/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs +++ b/back/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs @@ -70,11 +70,6 @@ namespace Kyoo.Abstractions.Controllers /// IEpisodeRepository EpisodeRepository { get; } - /// - /// The repository that handle tracks. - /// - ITrackRepository TrackRepository { get; } - /// /// The repository that handle people. /// diff --git a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs index 56e91cd7..a45827f0 100644 --- a/back/src/Kyoo.Abstractions/Controllers/IRepository.cs +++ b/back/src/Kyoo.Abstractions/Controllers/IRepository.cs @@ -330,11 +330,6 @@ namespace Kyoo.Abstractions.Controllers Task GetAbsolute(string showSlug, int absoluteNumber); } - /// - /// A repository to handle tracks - /// - public interface ITrackRepository : IRepository { } - /// /// A repository to handle libraries. /// diff --git a/back/src/Kyoo.Abstractions/Controllers/IThumbnailsManager.cs b/back/src/Kyoo.Abstractions/Controllers/IThumbnailsManager.cs index eb26335a..69ffe0bc 100644 --- a/back/src/Kyoo.Abstractions/Controllers/IThumbnailsManager.cs +++ b/back/src/Kyoo.Abstractions/Controllers/IThumbnailsManager.cs @@ -17,9 +17,10 @@ // along with Kyoo. If not, see . using System.Threading.Tasks; -using JetBrains.Annotations; using Kyoo.Abstractions.Models; +#nullable enable + namespace Kyoo.Abstractions.Controllers { /// @@ -39,17 +40,17 @@ namespace Kyoo.Abstractions.Controllers /// /// The type of the item /// true if an image has been downloaded, false otherwise. - Task DownloadImages([NotNull] T item, bool alwaysDownload = false) + Task DownloadImages(T item, bool alwaysDownload = false) where T : IThumbnails; /// /// Retrieve the local path of an image of the given item. /// /// The item to retrieve the poster from. - /// The ID of the image. See for values. + /// The ID of the image. See for values. /// The type of the item /// The path of the image for the given resource or null if it does not exists. - Task GetImagePath([NotNull] T item, int imageID) + string? GetImagePath(T item, int imageId) where T : IThumbnails; } } diff --git a/back/src/Kyoo.Abstractions/Controllers/ITranscoder.cs b/back/src/Kyoo.Abstractions/Controllers/ITranscoder.cs deleted file mode 100644 index b87a8d5d..00000000 --- a/back/src/Kyoo.Abstractions/Controllers/ITranscoder.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Threading.Tasks; -using JetBrains.Annotations; -using Kyoo.Abstractions.Models; -using Microsoft.AspNetCore.Mvc; - -namespace Kyoo.Abstractions.Controllers -{ - /// - /// Transcoder responsible of handling low level video details. - /// - public interface ITranscoder - { - /// - /// Retrieve tracks for a specific episode. - /// Subtitles, chapters and fonts should also be extracted and cached when calling this method. - /// - /// The episode to retrieve tracks for. - /// Should the cache be invalidated and subtitles and others be re-extracted? - /// The list of tracks available for this episode. - Task> ExtractInfos(Episode episode, bool reExtract); - - /// - /// List fonts assosiated with this episode. - /// - /// Th episode to list fonts for. - /// The list of attachements for this epiosode. - Task> ListFonts(Episode episode); - - /// - /// Get the specified font for this episode. - /// - /// The episode to list fonts for. - /// The slug of the specific font to retrive. - /// The with the given slug or null. - [ItemCanBeNull] Task GetFont(Episode episode, string slug); - - /// - /// Transmux the selected episode to hls. - /// - /// The episode to transmux. - /// The master file (m3u8) of the transmuxed hls file. - IActionResult Transmux(Episode episode); - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/FileSystemMetadataAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/FileSystemMetadataAttribute.cs deleted file mode 100644 index 207ab91c..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/FileSystemMetadataAttribute.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using Kyoo.Abstractions.Controllers; - -namespace Kyoo.Abstractions.Models.Attributes -{ - /// - /// An attribute to inform how a works. - /// - [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class)] - public class FileSystemMetadataAttribute : Attribute - { - /// - /// The scheme(s) used to identify this path. - /// It can be something like http, https, ftp, file and so on. - /// - /// - /// If multiples files with the same schemes exists, an exception will be thrown. - /// - public string[] Scheme { get; } - - /// - /// true if the scheme should be removed from the path before calling - /// methods of this , false otherwise. - /// - public bool StripScheme { get; set; } - - /// - /// Create a new using the specified schemes. - /// - /// The schemes to use. - public FileSystemMetadataAttribute(string[] schemes) - { - Scheme = schemes; - } - - /// - /// Create a new using a dictionary of metadata. - /// - /// - /// The dictionary of metadata. This method expect the dictionary to contain a field - /// per property in this attribute, with the same types as the properties of this attribute. - /// - public FileSystemMetadataAttribute(IDictionary metadata) - { - Scheme = (string[])metadata[nameof(Scheme)]; - StripScheme = (bool)metadata[nameof(StripScheme)]; - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Attributes/TaskMetadataAttribute.cs b/back/src/Kyoo.Abstractions/Models/Attributes/TaskMetadataAttribute.cs deleted file mode 100644 index 3683fe77..00000000 --- a/back/src/Kyoo.Abstractions/Models/Attributes/TaskMetadataAttribute.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using Kyoo.Abstractions.Controllers; - -namespace Kyoo.Abstractions.Models.Attributes -{ - /// - /// An attribute to inform how a works. - /// - [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class)] - public class TaskMetadataAttribute : Attribute - { - /// - /// The slug of the task, used to start it. - /// - public string Slug { get; } - - /// - /// The name of the task that will be displayed to the user. - /// - public string Name { get; } - - /// - /// A quick description of what this task will do. - /// - public string Description { get; } - - /// - /// Should this task be automatically run at app startup? - /// - public bool RunOnStartup { get; set; } - - /// - /// The priority of this task. Only used if is true. - /// It allow one to specify witch task will be started first as tasked are run on a Priority's descending order. - /// - public int Priority { get; set; } - - /// - /// true if this task should not be displayed to the user, false otherwise. - /// - public bool IsHidden { get; set; } - - /// - /// Create a new with the given slug, name and description. - /// - /// The slug of the task, used to start it. - /// The name of the task that will be displayed to the user. - /// A quick description of what this task will do. - public TaskMetadataAttribute(string slug, string name, string description) - { - Slug = slug; - Name = name; - Description = description; - } - - /// - /// Create a new using a dictionary of metadata. - /// - /// - /// The dictionary of metadata. This method expect the dictionary to contain a field - /// per property in this attribute, with the same types as the properties of this attribute. - /// - public TaskMetadataAttribute(IDictionary metadata) - { - Slug = (string)metadata[nameof(Slug)]; - Name = (string)metadata[nameof(Name)]; - Description = (string)metadata[nameof(Description)]; - RunOnStartup = (bool)metadata[nameof(RunOnStartup)]; - Priority = (int)metadata[nameof(Priority)]; - IsHidden = (bool)metadata[nameof(IsHidden)]; - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Chapter.cs b/back/src/Kyoo.Abstractions/Models/Chapter.cs deleted file mode 100644 index c03e1045..00000000 --- a/back/src/Kyoo.Abstractions/Models/Chapter.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Abstractions.Models -{ - /// - /// A chapter to split an episode in multiple parts. - /// - public class Chapter - { - /// - /// The start time of the chapter (in second from the start of the episode). - /// - public float StartTime { get; set; } - - /// - /// The end time of the chapter (in second from the start of the episode). - /// - public float EndTime { get; set; } - - /// - /// The name of this chapter. This should be a human-readable name that could be presented to the user. - /// There should be well-known chapters name for commonly used chapters. - /// For example, use "Opening" for the introduction-song and "Credits" for the end chapter with credits. - /// - public string Name { get; set; } - - /// - /// Create a new . - /// - /// The start time of the chapter (in second) - /// The end time of the chapter (in second) - /// The name of this chapter - public Chapter(float startTime, float endTime, string name) - { - StartTime = startTime; - EndTime = endTime; - Name = name; - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Exceptions/HealthException.cs b/back/src/Kyoo.Abstractions/Models/Exceptions/HealthException.cs deleted file mode 100644 index 6b4d86e2..00000000 --- a/back/src/Kyoo.Abstractions/Models/Exceptions/HealthException.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Runtime.Serialization; - -namespace Kyoo.Abstractions.Models.Exceptions -{ - /// - /// An exception thrown when a part of the app has a fatal issue. - /// - [Serializable] - public class HealthException : Exception - { - /// - /// Create a new with a custom message. - /// - /// The message to use. - public HealthException(string message) - : base(message) - { } - - /// - /// The serialization constructor - /// - /// Serialization infos - /// The serialization context - protected HealthException(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Font.cs b/back/src/Kyoo.Abstractions/Models/Font.cs deleted file mode 100644 index ca9c4680..00000000 --- a/back/src/Kyoo.Abstractions/Models/Font.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Utils; -using PathIO = System.IO.Path; - -namespace Kyoo.Abstractions.Models -{ - /// - /// A font of an . - /// - public class Font : ILink - { - /// - /// A human-readable identifier, used in the URL. - /// - public string Slug { get; set; } - - /// - /// The name of the font file (with the extension). - /// - public string File { get; set; } - - /// - /// The format of this font (the extension). - /// - public string Format { get; set; } - - /// - /// The path of the font. - /// - [SerializeIgnore] public string Path { get; set; } - - /// - public object Link { get; init; } - - /// - /// Create a new empty . - /// - public Font() { } - - /// - /// Create a new from a path. - /// - /// The path of the font. - /// The slug of the episode that contains this font. - public Font(string path, string episodeSlug) - { - Slug = Utility.ToSlug(PathIO.GetFileNameWithoutExtension(path)); - Path = path; - File = PathIO.GetFileName(path); - Format = PathIO.GetExtension(path).Replace(".", string.Empty); - Link = $"/watch/{episodeSlug}/font/{Slug}.{Format}"; - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs index 619b802e..f6dcb41e 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Episode.cs @@ -123,7 +123,7 @@ namespace Kyoo.Abstractions.Models public int? AbsoluteNumber { get; set; } /// - /// The path of the video file for this episode. Any format supported by a is allowed. + /// The path of the video file for this episode. /// public string Path { get; set; } @@ -148,11 +148,6 @@ namespace Kyoo.Abstractions.Models /// [EditableRelation][LoadableRelation] public ICollection ExternalIDs { get; set; } - /// - /// The list of tracks this episode has. This lists video, audio and subtitles available. - /// - [EditableRelation][LoadableRelation] public ICollection Tracks { get; set; } - /// /// Get the slug of an episode. /// diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs index 6a84977f..071ddc35 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Interfaces/IThumbnails.cs @@ -28,7 +28,6 @@ namespace Kyoo.Abstractions.Models { /// /// The list of images mapped to a certain index. - /// The string value should be a path supported by the . /// /// /// An arbitrary index should not be used, instead use indexes from diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs index 90600d0d..646dee9f 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/Show.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/Show.cs @@ -46,7 +46,6 @@ namespace Kyoo.Abstractions.Models /// /// The path of the root directory of this show. - /// This can be any kind of path supported by /// [SerializeIgnore] public string Path { get; set; } @@ -61,7 +60,7 @@ namespace Kyoo.Abstractions.Models public Status Status { get; set; } /// - /// An URL to a trailer. This could be any path supported by the . + /// An URL to a trailer. /// /// TODO for now, this is set to a youtube url. It should be cached and converted to a local file. [Obsolete("Use Images instead of this, this is only kept for the API response.")] diff --git a/back/src/Kyoo.Abstractions/Models/Resources/Track.cs b/back/src/Kyoo.Abstractions/Models/Resources/Track.cs deleted file mode 100644 index f838c813..00000000 --- a/back/src/Kyoo.Abstractions/Models/Resources/Track.cs +++ /dev/null @@ -1,229 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Globalization; -using System.Linq; -using System.Text.RegularExpressions; -using JetBrains.Annotations; -using Kyoo.Abstractions.Models.Attributes; - -namespace Kyoo.Abstractions.Models -{ - /// - /// The list of available stream types. - /// Attachments are only used temporarily by the transcoder but are not stored in a database. - /// - public enum StreamType - { - /// - /// The type of the stream is not known. - /// - Unknown = 0, - - /// - /// The stream is a video. - /// - Video = 1, - - /// - /// The stream is an audio. - /// - Audio = 2, - - /// - /// The stream is a subtitle. - /// - Subtitle = 3, - } - - /// - /// A video, audio or subtitle track for an episode. - /// - public class Track : IResource, ILink - { - /// - public int ID { get; set; } - - /// - [Computed] - public string Slug - { - get - { - string type = Type.ToString().ToLowerInvariant(); - string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty; - string episode = EpisodeSlug ?? Episode?.Slug ?? EpisodeID.ToString(CultureInfo.InvariantCulture); - return $"{episode}.{Language ?? "und"}{index}{(IsForced ? ".forced" : string.Empty)}.{type}"; - } - - [UsedImplicitly] - private set - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - Match match = Regex.Match(value, - @"(?[^\.]+)\.(?\w{0,3})(-(?\d+))?(\.(?forced))?\.(?\w+)(\.\w*)?"); - - if (!match.Success) - { - throw new ArgumentException( - "Invalid track slug. " + - "Format: {episodeSlug}.{language}[-{index}][.forced].{type}[.{extension}]" - ); - } - - EpisodeSlug = match.Groups["ep"].Value; - Language = match.Groups["lang"].Value; - if (Language == "und") - Language = null; - TrackIndex = match.Groups["index"].Success ? int.Parse(match.Groups["index"].Value, CultureInfo.InvariantCulture) : 0; - IsForced = match.Groups["forced"].Success; - Type = Enum.Parse(match.Groups["type"].Value, true); - } - } - - /// - /// The title of the stream. - /// - public string Title { get; set; } - - /// - /// The language of this stream (as a ISO-639-2 language code) - /// - public string Language { get; set; } - - /// - /// The codec of this stream. - /// - public string Codec { get; set; } - - /// - /// Is this stream the default one of it's type? - /// - public bool IsDefault { get; set; } - - /// - /// Is this stream tagged as forced? - /// - public bool IsForced { get; set; } - - /// - /// Is this track extern to the episode's file? - /// - public bool IsExternal { get; set; } - - /// - /// The path of this track. - /// - [SerializeIgnore] public string Path { get; set; } - - /// - /// The type of this stream. - /// - [SerializeIgnore] public StreamType Type { get; set; } - - /// - /// The ID of the episode that uses this track. - /// - [SerializeIgnore] public int EpisodeID { get; set; } - - /// - /// The episode that uses this track. - /// - [LoadableRelation(nameof(EpisodeID))] - public Episode Episode - { - get => _episode; - set - { - _episode = value; - if (_episode != null) - EpisodeSlug = _episode.Slug; - } - } - - /// - /// The index of this track on the episode. - /// - public int TrackIndex { get; set; } - - /// - /// A user-friendly name for this track. It does not include the track type. - /// - public string DisplayName - { - get - { - string language = _GetLanguage(Language); - - if (language == null) - return $"Unknown (index: {TrackIndex})"; - CultureInfo info = CultureInfo.GetCultures(CultureTypes.NeutralCultures) - .FirstOrDefault(x => x.ThreeLetterISOLanguageName == language); - string name = info?.EnglishName ?? language; - if (IsForced) - name += " Forced"; - if (IsExternal) - name += " (External)"; - if (Title is { Length: > 1 }) - name += " - " + Title; - return name; - } - } - - /// - /// The slug of the episode that contain this track. If this is not set, this track is ill-formed. - /// - [SerializeIgnore] public string EpisodeSlug { private get; set; } - - /// - /// The episode that uses this track. - /// This is the baking field of . - /// - [SerializeIgnore] private Episode _episode; - - /// - public object Link => Type == StreamType.Subtitle ? $"/subtitle/{Slug}" : null; - - // Converting mkv track language to c# system language tag. - private static string _GetLanguage(string mkvLanguage) - { - // TODO delete this and have a real way to get the language string from the ISO-639-2. - return mkvLanguage switch - { - "fre" => "fra", - null => "und", - _ => mkvLanguage - }; - } - - /// - /// Utility method to create a track slug from a incomplete slug (only add the type of the track). - /// - /// The slug to edit - /// The new type of this - /// The completed slug. - public static string BuildSlug(string baseSlug, StreamType type) - { - return baseSlug.EndsWith($".{type}", StringComparison.InvariantCultureIgnoreCase) - ? baseSlug - : $"{baseSlug}.{type.ToString().ToLowerInvariant()}"; - } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/Utils/AsyncRef.cs b/back/src/Kyoo.Abstractions/Models/Utils/AsyncRef.cs deleted file mode 100644 index 18ffd7d1..00000000 --- a/back/src/Kyoo.Abstractions/Models/Utils/AsyncRef.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Abstractions.Models -{ - /// - /// A class wrapping a value that will be set after the completion of the task it is related to. - /// - /// - /// This class replace the use of an out parameter on a task since tasks and out can't be combined. - /// - /// The type of the value - public class AsyncRef - { - /// - /// The value that will be set before the completion of the task. - /// - public T Value { get; set; } - } -} diff --git a/back/src/Kyoo.Abstractions/Models/WatchItem.cs b/back/src/Kyoo.Abstractions/Models/WatchItem.cs index 13e61ae0..bd5d989a 100644 --- a/back/src/Kyoo.Abstractions/Models/WatchItem.cs +++ b/back/src/Kyoo.Abstractions/Models/WatchItem.cs @@ -19,12 +19,12 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.IO; using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models.Attributes; -using PathIO = System.IO.Path; namespace Kyoo.Abstractions.Models { @@ -85,11 +85,6 @@ namespace Kyoo.Abstractions.Models /// public DateTime? ReleaseDate { get; set; } - /// - /// The path of the video file for this episode. Any format supported by a is allowed. - /// - [SerializeIgnore] public string Path { get; set; } - /// /// The episode that come before this one if you follow usual watch orders. /// If this is the first episode or this is a movie, it will be null. @@ -111,35 +106,9 @@ namespace Kyoo.Abstractions.Models public Dictionary Images { get; set; } /// - /// The container of the video file of this episode. - /// Common containers are mp4, mkv, avi and so on. + /// The transcoder's info for this item. This include subtitles, fonts, chapters... /// - public string Container { get; set; } - - /// - /// The video track. See for more information. - /// - public Track Video { get; set; } - - /// - /// The list of audio tracks. See for more information. - /// - public ICollection Audios { get; set; } - - /// - /// The list of subtitles tracks. See for more information. - /// - public ICollection Subtitles { get; set; } - - /// - /// The list of fonts that can be used to draw the subtitles. - /// - public ICollection Fonts { get; set; } - - /// - /// The list of chapters. See for more information. - /// - public ICollection Chapters { get; set; } + public object Info { get; set; } [SerializeIgnore] private string _Type => IsMovie ? "movie" : "episode"; @@ -158,13 +127,11 @@ namespace Kyoo.Abstractions.Models /// /// A library manager to retrieve the next and previous episode and load the show and tracks of the episode. /// - /// A file system used to retrieve chapters informations. - /// The transcoder used to list fonts. + /// A http client to reach the transcoder. /// A new WatchItem representing the given episode. - public static async Task FromEpisode(Episode ep, ILibraryManager library, IFileSystem fs, ITranscoder transcoder) + public static async Task FromEpisode(Episode ep, ILibraryManager library, HttpClient client) { await library.Load(ep, x => x.Show); - await library.Load(ep, x => x.Tracks); return new WatchItem { @@ -178,13 +145,7 @@ namespace Kyoo.Abstractions.Models Title = ep.Title, Overview = ep.Overview, ReleaseDate = ep.ReleaseDate, - Path = ep.Path, Images = ep.Show.Images, - Container = PathIO.GetExtension(ep.Path).Replace(".", string.Empty), - Video = ep.Tracks.FirstOrDefault(x => x.Type == StreamType.Video), - Audios = ep.Tracks.Where(x => x.Type == StreamType.Audio).ToArray(), - Subtitles = ep.Tracks.Where(x => x.Type == StreamType.Subtitle).ToArray(), - Fonts = await transcoder.ListFonts(ep), PreviousEpisode = ep.Show.IsMovie ? null : (await library.GetAll( @@ -197,50 +158,16 @@ namespace Kyoo.Abstractions.Models where: x => x.ShowID == ep.ShowID, limit: new Pagination(1, ep.ID) )).FirstOrDefault(), - Chapters = await _GetChapters(ep, fs), - IsMovie = ep.Show.IsMovie + IsMovie = ep.Show.IsMovie, + Info = await _GetInfo(ep, client), }; } - // TODO move this method in a controller to support abstraction. - private static async Task> _GetChapters(Episode episode, IFileSystem fs) + private static async Task _GetInfo(Episode ep, HttpClient client) { - string path = fs.Combine( - await fs.GetExtraDirectory(episode), - "Chapters", - PathIO.GetFileNameWithoutExtension(episode.Path) + ".txt" + return await client.GetFromJsonAsync( + $"http://transcoder:7666/info/{(ep.Show.IsMovie ? "movie" : "episode")}/${ep.Slug}/info" ); - if (!await fs.Exists(path)) - return Array.Empty(); - try - { - using StreamReader sr = new(await fs.GetReader(path)); - string chapters = await sr.ReadToEndAsync(); - return chapters.Split('\n') - .Select(x => - { - string[] values = x.Split(' '); - if ( - values.Length < 3 - || !float.TryParse(values[0], out float start) - || !float.TryParse(values[1], out float end) - ) - return null; - return new Chapter( - start, - end, - string.Join(' ', values.Skip(2)) - ); - }) - .Where(x => x != null) - .ToArray(); - } - catch (Exception ex) - { - await Console.Error.WriteLineAsync($"Invalid chapter file at {path}"); - Console.Error.WriteLine(ex.ToString()); - return Array.Empty(); - } } /// diff --git a/back/src/Kyoo.Core/Controllers/FileSystems/HttpFileSystem.cs b/back/src/Kyoo.Core/Controllers/FileSystems/HttpFileSystem.cs deleted file mode 100644 index e004b796..00000000 --- a/back/src/Kyoo.Core/Controllers/FileSystems/HttpFileSystem.cs +++ /dev/null @@ -1,168 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Microsoft.AspNetCore.Mvc; - -namespace Kyoo.Core.Controllers -{ - /// - /// A for http/https links. - /// - [FileSystemMetadata(new[] { "http", "https" })] - public class HttpFileSystem : IFileSystem - { - /// - /// The http client factory used to create clients. - /// - private readonly IHttpClientFactory _clientFactory; - - /// - /// Create a using the given client factory. - /// - /// The http client factory used to create clients. - public HttpFileSystem(IHttpClientFactory factory) - { - _clientFactory = factory; - } - - /// - public IActionResult FileResult(string path, bool rangeSupport = false, string type = null) - { - if (path == null) - return new NotFoundResult(); - return new HttpForwardResult(new Uri(path), rangeSupport, type); - } - - /// - public Task GetReader(string path) - { - HttpClient client = _clientFactory.CreateClient(); - return client.GetStreamAsync(path); - } - - /// - public async Task GetReader(string path, AsyncRef mime) - { - HttpClient client = _clientFactory.CreateClient(); - HttpResponseMessage response = await client.GetAsync(path); - response.EnsureSuccessStatusCode(); - mime.Value = response.Content.Headers.ContentType?.MediaType; - return await response.Content.ReadAsStreamAsync(); - } - - /// - public Task NewFile(string path) - { - throw new NotSupportedException("An http filesystem is readonly, a new file can't be created."); - } - - /// - public Task CreateDirectory(string path) - { - throw new NotSupportedException("An http filesystem is readonly, a directory can't be created."); - } - - /// - public string Combine(params string[] paths) - { - return Path.Combine(paths); - } - - /// - public Task> ListFiles(string path, SearchOption options = SearchOption.TopDirectoryOnly) - { - throw new NotSupportedException("Listing files is not supported on an http filesystem."); - } - - /// - public Task Exists(string path) - { - throw new NotSupportedException("Checking if a file exists is not supported on an http filesystem."); - } - - /// - public Task GetExtraDirectory(T resource) - { - throw new NotSupportedException("Extras can not be stored inside an http filesystem."); - } - - /// - public Task> ExtractInfos(Episode episode, bool reExtract) - { - throw new NotSupportedException("Extracting infos is not supported on an http filesystem."); - } - - /// - public IActionResult Transmux(Episode episode) - { - throw new NotSupportedException("Transmuxing is not supported on an http filesystem."); - } - - /// - /// An to proxy an http request. - /// - // TODO remove this suppress message once the class has been implemented. - [SuppressMessage("ReSharper", "NotAccessedField.Local", Justification = "Not Implemented Yet.")] - public class HttpForwardResult : IActionResult - { - /// - /// The path of the request to forward. - /// - private readonly Uri _path; - - /// - /// Should the proxied result support ranges requests? - /// - private readonly bool _rangeSupport; - - /// - /// If not null, override the content type of the resulting request. - /// - private readonly string _type; - - /// - /// Create a new . - /// - /// The path of the request to forward. - /// Should the proxied result support ranges requests? - /// If not null, override the content type of the resulting request. - public HttpForwardResult(Uri path, bool rangeSupport, string type = null) - { - _path = path; - _rangeSupport = rangeSupport; - _type = type; - } - - /// - public Task ExecuteResultAsync(ActionContext context) - { - // TODO implement that, example: https://github.com/twitchax/AspNetCore.Proxy/blob/14dd0f212d7abb43ca1bf8c890d5efb95db66acb/src/Core/Extensions/Http.cs#L15 - throw new NotImplementedException(); - } - } - } -} diff --git a/back/src/Kyoo.Core/Controllers/FileSystems/LocalFileSystem.cs b/back/src/Kyoo.Core/Controllers/FileSystems/LocalFileSystem.cs deleted file mode 100644 index 42d77f92..00000000 --- a/back/src/Kyoo.Core/Controllers/FileSystems/LocalFileSystem.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Core.Models.Options; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.StaticFiles; -using Microsoft.Extensions.Options; - -namespace Kyoo.Core.Controllers -{ - /// - /// A for the local filesystem (using System.IO). - /// - [FileSystemMetadata(new[] { "", "file" }, StripScheme = true)] - public class LocalFileSystem : IFileSystem - { - /// - /// An extension provider to get content types from files extensions. - /// - private readonly IContentTypeProvider _provider; - - /// - /// The transcoder of local files. - /// - private readonly ITranscoder _transcoder; - - /// - /// Options to check if the metadata should be kept in the show directory or in a kyoo's directory. - /// - private readonly IOptionsMonitor _options; - - /// - /// Create a new with the specified options. - /// - /// The options to use. - /// An extension provider to get content types from files extensions. - /// The transcoder of local files. - public LocalFileSystem(IOptionsMonitor options, - IContentTypeProvider provider, - ITranscoder transcoder) - { - _options = options; - _provider = provider; - _transcoder = transcoder; - } - - /// - /// Get the content type of a file using it's extension. - /// - /// The path of the file - /// The extension of the file is not known. - /// The content type of the file - private string _GetContentType(string path) - { - if (_provider.TryGetContentType(path, out string contentType)) - return contentType; - throw new NotImplementedException($"Can't get the content type of the file at: {path}"); - } - - /// - public IActionResult FileResult(string path, bool rangeSupport = false, string type = null) - { - if (path == null) - return new NotFoundResult(); - if (!File.Exists(path)) - return new NotFoundResult(); - return new PhysicalFileResult(Path.GetFullPath(path), type ?? _GetContentType(path)) - { - EnableRangeProcessing = rangeSupport - }; - } - - /// - public Task GetReader(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - return Task.FromResult(File.OpenRead(path)); - } - - /// - public Task GetReader(string path, AsyncRef mime) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - _provider.TryGetContentType(path, out string mimeValue); - mime.Value = mimeValue; - return Task.FromResult(File.OpenRead(path)); - } - - /// - public Task NewFile(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - return Task.FromResult(File.Create(path)); - } - - /// - public Task CreateDirectory(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - Directory.CreateDirectory(path); - return Task.FromResult(path); - } - - /// - public string Combine(params string[] paths) - { - return Path.Combine(paths); - } - - /// - public Task> ListFiles(string path, SearchOption options = SearchOption.TopDirectoryOnly) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - string[] ret = Directory.Exists(path) - ? Directory.GetFiles(path, "*", options) - : Array.Empty(); - return Task.FromResult>(ret); - } - - /// - public Task Exists(string path) - { - return Task.FromResult(File.Exists(path) || Directory.Exists(path)); - } - - /// - public Task GetExtraDirectory(T resource) - { - string path = resource switch - { - IResource res => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLower(), res.Slug), - _ => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLower()) - }; - return CreateDirectory(path); - } - - /// - public Task> ExtractInfos(Episode episode, bool reExtract) - { - return _transcoder.ExtractInfos(episode, reExtract); - } - - /// - public IActionResult Transmux(Episode episode) - { - return _transcoder.Transmux(episode); - } - } -} diff --git a/back/src/Kyoo.Core/Controllers/LibraryManager.cs b/back/src/Kyoo.Core/Controllers/LibraryManager.cs index d07a5da3..453d6b10 100644 --- a/back/src/Kyoo.Core/Controllers/LibraryManager.cs +++ b/back/src/Kyoo.Core/Controllers/LibraryManager.cs @@ -57,9 +57,6 @@ namespace Kyoo.Core.Controllers /// public IEpisodeRepository EpisodeRepository { get; } - /// - public ITrackRepository TrackRepository { get; } - /// public IPeopleRepository PeopleRepository { get; } @@ -89,7 +86,6 @@ namespace Kyoo.Core.Controllers ShowRepository = GetRepository() as IShowRepository; SeasonRepository = GetRepository() as ISeasonRepository; EpisodeRepository = GetRepository() as IEpisodeRepository; - TrackRepository = GetRepository() as ITrackRepository; PeopleRepository = GetRepository() as IPeopleRepository; StudioRepository = GetRepository() as IStudioRepository; GenreRepository = GetRepository() as IGenreRepository; @@ -354,11 +350,6 @@ namespace Kyoo.Core.Controllers (x, y) => x.ExternalIDs = y, (x, y) => { x.ResourceID = y.ID; }), - (Episode e, nameof(Episode.Tracks)) => _SetRelation(e, - TrackRepository.GetAll(x => x.Episode.ID == obj.ID), - (x, y) => x.Tracks = y, - (x, y) => { x.Episode = y; x.EpisodeID = y.ID; }), - (Episode e, nameof(Episode.Show)) => ShowRepository .GetOrDefault(x => x.Episodes.Any(y => y.ID == obj.ID)) .Then(x => @@ -376,15 +367,6 @@ namespace Kyoo.Core.Controllers }), - (Track t, nameof(Track.Episode)) => EpisodeRepository - .GetOrDefault(x => x.Tracks.Any(y => y.ID == obj.ID)) - .Then(x => - { - t.Episode = x; - t.EpisodeID = x?.ID ?? 0; - }), - - (Genre g, nameof(Genre.Shows)) => ShowRepository .GetAll(x => x.Genres.Any(y => y.ID == obj.ID)) .Then(x => g.Shows = x), diff --git a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs index ab8bc7f5..e5fefb8f 100644 --- a/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs +++ b/back/src/Kyoo.Core/Controllers/Repositories/EpisodeRepository.cs @@ -44,11 +44,6 @@ namespace Kyoo.Core.Controllers /// private readonly IProviderRepository _providers; - /// - /// A track repository to handle creation and deletion of tracks related to the current episode. - /// - private readonly Lazy _tracks; - /// // Use absolute numbers by default and fallback to season/episodes if it does not exists. protected override Sort DefaultSort => new Sort.Conglomerate( @@ -63,16 +58,13 @@ namespace Kyoo.Core.Controllers /// The database handle to use. /// A show repository /// A provider repository - /// A track repository public EpisodeRepository(DatabaseContext database, IShowRepository shows, - IProviderRepository providers, - Lazy tracks) + IProviderRepository providers) : base(database) { _database = database; _providers = providers; - _tracks = tracks; // Edit episode slugs when the show's slug changes. shows.OnEdited += (show) => @@ -162,7 +154,7 @@ namespace Kyoo.Core.Controllers ? Get(obj.ShowID, obj.SeasonNumber.Value, obj.EpisodeNumber.Value) : GetAbsolute(obj.ShowID, obj.AbsoluteNumber.Value)); OnResourceCreated(obj); - return await _ValidateTracks(obj); + return obj; } /// @@ -170,13 +162,6 @@ namespace Kyoo.Core.Controllers { await Validate(changed); - if (changed.Tracks != null || resetOld) - { - await _tracks.Value.DeleteAll(x => x.EpisodeID == resource.ID); - resource.Tracks = changed.Tracks; - await _ValidateTracks(resource); - } - if (changed.ExternalIDs != null || resetOld) { await Database.Entry(resource).Collection(x => x.ExternalIDs).LoadAsync(); @@ -184,25 +169,6 @@ namespace Kyoo.Core.Controllers } } - /// - /// Set track's index and ensure that every tracks is well-formed. - /// - /// The resource to fix. - /// The parameter is returned. - private async Task _ValidateTracks(Episode resource) - { - if (resource.Tracks == null) - return resource; - - resource.Tracks = await resource.Tracks.SelectAsync(x => - { - x.Episode = resource; - return _tracks.Value.Create(x); - }).ToListAsync(); - _database.Tracks.AttachRange(resource.Tracks); - return resource; - } - /// protected override async Task Validate(Episode resource) { @@ -236,7 +202,6 @@ namespace Kyoo.Core.Controllers throw new ArgumentNullException(nameof(obj)); _database.Entry(obj).State = EntityState.Deleted; - 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); diff --git a/back/src/Kyoo.Core/Controllers/Repositories/TrackRepository.cs b/back/src/Kyoo.Core/Controllers/Repositories/TrackRepository.cs deleted file mode 100644 index 83945c2e..00000000 --- a/back/src/Kyoo.Core/Controllers/Repositories/TrackRepository.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; - -namespace Kyoo.Core.Controllers -{ - /// - /// A local repository to handle tracks. - /// - public class TrackRepository : LocalRepository, ITrackRepository - { - /// - /// The database handle - /// - private readonly DatabaseContext _database; - - /// - protected override Sort DefaultSort => new Sort.By(x => x.TrackIndex); - - /// - /// Create a new . - /// - /// The database handle - /// The episode repository - public TrackRepository(DatabaseContext database, IEpisodeRepository episodes) - : base(database) - { - _database = database; - - // Edit tracks slugs when the episodes's slug changes. - episodes.OnEdited += (ep) => - { - List tracks = _database.Tracks.AsTracking().Where(x => x.EpisodeID == ep.ID).ToList(); - foreach (Track track in tracks) - { - track.EpisodeSlug = ep.Slug; - _database.SaveChanges(); - OnResourceEdited(track); - } - }; - } - - /// - public override Task> Search(string query) - { - throw new InvalidOperationException("Tracks do not support the search method."); - } - - /// - protected override async Task Validate(Track resource) - { - await base.Validate(resource); - if (resource.EpisodeID <= 0) - { - resource.EpisodeID = resource.Episode?.ID ?? 0; - if (resource.EpisodeID <= 0) - { - throw new ArgumentException("Can't store a track not related to any episode " + - $"(episodeID: {resource.EpisodeID})."); - } - } - } - - /// - public override async Task Create(Track obj) - { - if (obj == null) - throw new ArgumentNullException(nameof(obj)); - - await base.Create(obj); - obj.EpisodeSlug = _database.Episodes.First(x => x.ID == obj.EpisodeID).Slug; - _database.Entry(obj).State = EntityState.Added; - await _database.SaveChangesAsync(); - OnResourceCreated(obj); - return obj; - } - - /// - public override async Task Delete(Track obj) - { - if (obj == null) - throw new ArgumentNullException(nameof(obj)); - - _database.Entry(obj).State = EntityState.Deleted; - await _database.SaveChangesAsync(); - await base.Delete(obj); - } - } -} diff --git a/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs b/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs index 45cefc35..9dbb7c40 100644 --- a/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs +++ b/back/src/Kyoo.Core/Controllers/ThumbnailsManager.cs @@ -19,13 +19,15 @@ using System; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading.Tasks; -using JetBrains.Annotations; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Logging; +#nullable enable + namespace Kyoo.Core.Controllers { /// @@ -33,30 +35,27 @@ namespace Kyoo.Core.Controllers /// public class ThumbnailsManager : IThumbnailsManager { - /// - /// The file manager used to download the image if the file is distant - /// - private readonly IFileSystem _files; - /// /// A logger to report errors. /// private readonly ILogger _logger; + private readonly IHttpClientFactory _clientFactory; + /// /// Create a new . /// - /// The file manager to use. + /// Client factory /// A logger to report errors - public ThumbnailsManager(IFileSystem files, + public ThumbnailsManager(IHttpClientFactory clientFactory, ILogger logger) { - _files = files; + _clientFactory = clientFactory; _logger = logger; } /// - /// An helper function to download an image using a . + /// An helper function to download an image. /// /// The distant url of the image /// The local path of the image @@ -70,12 +69,17 @@ namespace Kyoo.Core.Controllers try { _logger.LogInformation("Downloading image {What}", what); - AsyncRef mime = new(); - await using Stream reader = await _files.GetReader(url, mime); + + HttpClient client = _clientFactory.CreateClient(); + HttpResponseMessage response = await client.GetAsync(url); + response.EnsureSuccessStatusCode(); + string mime = response.Content.Headers.ContentType?.MediaType!; + await using Stream reader = await response.Content.ReadAsStreamAsync(); + string extension = new FileExtensionContentTypeProvider() - .Mappings.FirstOrDefault(x => x.Value == mime.Value) + .Mappings.FirstOrDefault(x => x.Value == mime) .Key; - await using Stream local = await _files.NewFile(localPath + extension); + await using Stream local = File.Create(localPath + extension); await reader.CopyToAsync(local); return true; } @@ -101,8 +105,8 @@ namespace Kyoo.Core.Controllers foreach ((int id, string image) in item.Images.Where(x => x.Value != null)) { - string localPath = await _GetPrivateImagePath(item, id); - if (alwaysDownload || !await _files.Exists(localPath)) + string localPath = _GetPrivateImagePath(item, id); + if (alwaysDownload || !Path.Exists(localPath)) ret |= await _DownloadImage(image, localPath, $"The image n {id} of {name}"); } @@ -113,34 +117,41 @@ namespace Kyoo.Core.Controllers /// Retrieve the local path of an image of the given item without an extension. /// /// The item to retrieve the poster from. - /// The ID of the image. See for values. + /// The ID of the image. See for values. /// The type of the item /// The path of the image for the given resource, even if it does not exists - private async Task _GetPrivateImagePath([NotNull] T item, int imageID) + private static string _GetPrivateImagePath(T item, int imageId) { if (item == null) throw new ArgumentNullException(nameof(item)); - string directory = await _files.GetExtraDirectory(item); - string imageName = imageID switch + string directory = item switch + { + IResource res => Path.Combine("/metadata", typeof(T).Name.ToLowerInvariant(), res.Slug), + _ => Path.Combine("/metadata", typeof(T).Name.ToLowerInvariant()) + }; + Directory.CreateDirectory(directory); + string imageName = imageId switch { Images.Poster => "poster", Images.Logo => "logo", Images.Thumbnail => "thumbnail", Images.Trailer => "trailer", - _ => $"{imageID}" + _ => $"{imageId}" }; - return _files.Combine(directory, imageName); + return Path.Combine(directory, imageName); } /// - public async Task GetImagePath(T item, int imageID) + public string? GetImagePath(T item, int imageId) where T : IThumbnails { - string basePath = await _GetPrivateImagePath(item, imageID); - string directory = Path.GetDirectoryName(basePath); + string basePath = _GetPrivateImagePath(item, imageId); + string directory = Path.GetDirectoryName(basePath)!; string baseFile = Path.GetFileName(basePath); - return (await _files.ListFiles(directory!)) + if (!Directory.Exists(directory)) + return null; + return Directory.GetFiles(directory, "*", SearchOption.TopDirectoryOnly) .FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == baseFile); } } diff --git a/back/src/Kyoo.Core/Controllers/Transcoder.cs b/back/src/Kyoo.Core/Controllers/Transcoder.cs deleted file mode 100644 index 04bb3414..00000000 --- a/back/src/Kyoo.Core/Controllers/Transcoder.cs +++ /dev/null @@ -1,342 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Exceptions; -using Kyoo.Core.Models.Options; -using Kyoo.Core.Models.Watch; -using Kyoo.Utils; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace Kyoo.Core.Controllers -{ - /// - /// The transcoder used by the . - /// - public class Transcoder : ITranscoder - { -#pragma warning disable IDE1006 - /// - /// The class that interact with the transcoder written in C. - /// - private static class TranscoderAPI - { - /// - /// The name of the library. For windows '.dll' should be appended, on linux or macos it should be prefixed - /// by 'lib' and '.so' or '.dylib' should be appended. - /// - private const string TranscoderPath = "transcoder"; - - /// - /// Initialize the C library, setup the logger and return the size of a . - /// - /// The size of a - [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] - private static extern int init(); - - /// - /// Initialize the C library, setup the logger and return the size of a . - /// - /// The size of a - public static int Init() => init(); - - /// - /// Transmux the file at the specified path. The path must be a local one with '/' as a separator. - /// - /// The path of a local file with '/' as a separators. - /// The path of the hls output file. - /// - /// The number of seconds currently playable. This is incremented as the file gets transmuxed. - /// - /// 0 on success, non 0 on failure. - [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl, - CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)] - private static extern int transmux(string path, string outPath, out float playableDuration); - - /// - /// Transmux the file at the specified path. The path must be a local one. - /// - /// The path of a local file. - /// The path of the hls output file. - /// - /// The number of seconds currently playable. This is incremented as the file gets transmuxed. - /// - /// 0 on success, non 0 on failure. - public static int Transmux(string path, string outPath, out float playableDuration) - { - path = path.Replace('\\', '/'); - outPath = outPath.Replace('\\', '/'); - return transmux(path, outPath, out playableDuration); - } - - /// - /// Retrieve tracks from a video file and extract subtitles, fonts and chapters to an external file. - /// - /// - /// The path of the video file to analyse. This must be a local path with '/' as a separator. - /// - /// The directory that will be used to store extracted files. - /// The size of the returned array. - /// The number of tracks in the returned array. - /// Should the cache be invalidated and information re-extracted or not? - /// A pointer to an array of - [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl, - CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)] - private static extern IntPtr extract_infos(string path, - string outPath, - out uint length, - out uint trackCount, - bool reExtract); - - /// - /// An helper method to free an array of . - /// - /// A pointer to the first element of the array - /// The number of items in the array. - [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] - private static extern void free_streams(IntPtr streams, uint count); - - /// - /// Retrieve tracks from a video file and extract subtitles, fonts and chapters to an external file. - /// - /// The path of the video file to analyse. This must be a local path. - /// The directory that will be used to store extracted files. - /// Should the cache be invalidated and information re-extracted or not? - /// An array of . - public static Track[] ExtractInfos(string path, string outPath, bool reExtract) - { - path = path.Replace('\\', '/'); - outPath = outPath.Replace('\\', '/'); - - int size = Marshal.SizeOf(); - IntPtr ptr = extract_infos(path, outPath, out uint arrayLength, out uint trackCount, reExtract); - IntPtr streamsPtr = ptr; - Track[] tracks; - - if (trackCount > 0 && ptr != IntPtr.Zero) - { - tracks = new Track[trackCount]; - - int j = 0; - for (int i = 0; i < arrayLength; i++, streamsPtr += size) - { - FTrack stream = Marshal.PtrToStructure(streamsPtr); - if (stream!.Type == FTrackType.Unknown || stream.Type == FTrackType.Attachment) - continue; - tracks[j] = stream.ToTrack(); - j++; - } - Array.Resize(ref tracks, j); - } - else - tracks = Array.Empty(); - - if (ptr != IntPtr.Zero) - free_streams(ptr, trackCount); - return tracks; - } - } -#pragma warning restore IDE1006 - - /// - /// The file system used to retrieve the extra directory of shows to know where to extract information. - /// - private readonly IFileSystem _files; - - /// - /// Options to know where to cache transmuxed/transcoded episodes. - /// - private readonly IOptions _options; - - /// - /// The logger to use. This is also used by the wrapped C library. - /// - private readonly ILogger _logger; - - /// - /// if the C library has been checked, otherwise. - /// - private bool _initialized; - - /// - /// Create a new . - /// - /// - /// The file system used to retrieve the extra directory of shows to know where to extract information. - /// - /// Options to know where to cache transmuxed/transcoded episodes. - /// The logger to use. This is also used by the wrapped C library. - public Transcoder(IFileSystem files, IOptions options, ILogger logger) - { - _files = files; - _options = options; - _logger = logger; - } - - /// - /// Check if the C library can be used or if there is an issue with it. - /// - /// Should the healthcheck be abborted if the transcoder was already initialized? - /// If the transcoder is corrupted, this exception in thrown. - public void CheckHealth(bool fastStop = false) - { - if (fastStop && _initialized) - return; - if (TranscoderAPI.Init() != Marshal.SizeOf()) - { - _logger.LogCritical("The transcoder library could not be initialized correctly"); - throw new HealthException("The transcoder library is corrupted or invalid."); - } - _initialized = true; - } - - /// - public async Task> ExtractInfos(Episode episode, bool reExtract) - { - CheckHealth(true); - - string dir = await _files.GetExtraDirectory(episode); - if (dir == null) - throw new ArgumentException("Invalid path."); - return await Task.Factory.StartNew( - () => TranscoderAPI.ExtractInfos(episode.Path, dir, reExtract), - TaskCreationOptions.LongRunning - ); - } - - /// - public async Task> ListFonts(Episode episode) - { - string path = _files.Combine(await _files.GetExtraDirectory(episode), "Attachments"); - return (await _files.ListFiles(path)) - .Select(x => new Font(x, episode.Slug)) - .ToArray(); - } - - /// - public async Task GetFont(Episode episode, string slug) - { - string path = _files.Combine(await _files.GetExtraDirectory(episode), "Attachments"); - string font = (await _files.ListFiles(path)) - .FirstOrDefault(x => Utility.ToSlug(Path.GetFileNameWithoutExtension(x)) == slug); - if (font == null) - return null; - return new Font(font, episode.Slug); - } - - /// - public IActionResult Transmux(Episode episode) - { - CheckHealth(true); - - string folder = Path.Combine(_options.Value.TransmuxPath, episode.Slug); - string manifest = Path.GetFullPath(Path.Combine(folder, episode.Slug + ".m3u8")); - - try - { - Directory.CreateDirectory(folder); - if (File.Exists(manifest)) - return new PhysicalFileResult(manifest, "application/x-mpegurl"); - } - catch (UnauthorizedAccessException) - { - _logger.LogCritical("Access to the path {Manifest} is denied. " + - "Please change your transmux path in the config", manifest); - return new StatusCodeResult(500); - } - - return new TransmuxResult(episode.Path, manifest, _logger); - } - - /// - /// An action result that runs the transcoder and return the created manifest file after a few seconds of - /// the video has been proceeded. If the transcoder fails, it returns a 500 error code. - /// - private class TransmuxResult : IActionResult - { - /// - /// The path of the episode to transmux. It must be a local one. - /// - private readonly string _path; - - /// - /// The path of the manifest file to create. It must be a local one. - /// - private readonly string _manifest; - - /// - /// The logger to use in case of issue. - /// - private readonly ILogger _logger; - - /// - /// Create a new . - /// - /// The path of the episode to transmux. It must be a local one. - /// The path of the manifest file to create. It must be a local one. - /// The logger to use in case of issue. - public TransmuxResult(string path, string manifest, ILogger logger) - { - _path = path; - _manifest = Path.GetFullPath(manifest); - _logger = logger; - } - - // We use threads so tasks are not always awaited. -#pragma warning disable 4014 - - /// - public async Task ExecuteResultAsync(ActionContext context) - { - float playableDuration = 0; - bool transmuxFailed = false; - - Task.Factory.StartNew(() => - { - transmuxFailed = TranscoderAPI.Transmux(_path, _manifest, out playableDuration) != 0; - }, TaskCreationOptions.LongRunning); - - while (playableDuration < 10 || (!File.Exists(_manifest) && !transmuxFailed)) - await Task.Delay(10); - - if (!transmuxFailed) - { - new PhysicalFileResult(_manifest, "application/x-mpegurl") - .ExecuteResultAsync(context); - } - else - { - _logger.LogCritical("The transmuxing failed on the C library"); - new StatusCodeResult(500) - .ExecuteResultAsync(context); - } - } - -#pragma warning restore 4014 - } - } -} diff --git a/back/src/Kyoo.Core/CoreModule.cs b/back/src/Kyoo.Core/CoreModule.cs index 4160b9a5..5c55c01b 100644 --- a/back/src/Kyoo.Core/CoreModule.cs +++ b/back/src/Kyoo.Core/CoreModule.cs @@ -27,7 +27,6 @@ using Kyoo.Core.Controllers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Newtonsoft.Json; @@ -46,10 +45,6 @@ namespace Kyoo.Core /// public void Configure(ContainerBuilder builder) { - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - - builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); @@ -59,22 +54,11 @@ namespace Kyoo.Core builder.RegisterRepository(); builder.RegisterRepository(); builder.RegisterRepository(); - builder.RegisterRepository(); builder.RegisterRepository(); builder.RegisterRepository(); builder.RegisterRepository(); builder.RegisterRepository(); builder.RegisterRepository(); - - builder.RegisterType().As().SingleInstance() - .OnActivating(x => - { - x.Instance.Mappings[".data"] = "application/octet-stream"; - x.Instance.Mappings[".mkv"] = "video/x-matroska"; - x.Instance.Mappings[".ass"] = "text/x-ssa"; - x.Instance.Mappings[".srt"] = "application/x-subrip"; - x.Instance.Mappings[".m3u8"] = "application/x-mpegurl"; - }); } /// diff --git a/back/src/Kyoo.Core/Kyoo.Core.csproj b/back/src/Kyoo.Core/Kyoo.Core.csproj index 88166a30..917b6986 100644 --- a/back/src/Kyoo.Core/Kyoo.Core.csproj +++ b/back/src/Kyoo.Core/Kyoo.Core.csproj @@ -2,13 +2,6 @@ Kyoo.Core Kyoo.Core - ../Kyoo.Transcoder/ - - - - transcoder.dll - libtranscoder.dylib - libtranscoder.so @@ -22,21 +15,4 @@ - - - - - - - - - - - - - - PreserveNewest - false - - diff --git a/back/src/Kyoo.Core/Models/FTrack.cs b/back/src/Kyoo.Core/Models/FTrack.cs deleted file mode 100644 index cb779e23..00000000 --- a/back/src/Kyoo.Core/Models/FTrack.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Runtime.InteropServices; -using Kyoo.Abstractions.Models; - -namespace Kyoo.Core.Models.Watch -{ - /// - /// The list of available stream types. - /// Attachments are only used temporarily by the transcoder but are not stored in a database. - /// This is another enum used internally to communicate with ffmpeg. - /// - public enum FTrackType - { - /// - /// The type of the stream is not known. - /// - Unknown = StreamType.Unknown, - - /// - /// The stream is a video. - /// - Video = StreamType.Video, - - /// - /// The stream is an audio. - /// - Audio = StreamType.Audio, - - /// - /// The stream is a subtitle. - /// - Subtitle = StreamType.Subtitle, - - /// - /// The stream is an attachment (a font, an image or something else). - /// Only fonts are handled by kyoo but they are not saved to the database. - /// - Attachment - } - - /// - /// The unmanaged stream that the transcoder will return. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - public struct FTrack - { - /// - /// The title of the stream. - /// - public string Title; - - /// - /// The language of this stream (as a ISO-639-2 language code) - /// - public string Language; - - /// - /// The codec of this stream. - /// - public string Codec; - - /// - /// Is this stream the default one of it's type? - /// - [MarshalAs(UnmanagedType.I1)] public bool IsDefault; - - /// - /// Is this stream tagged as forced? - /// - [MarshalAs(UnmanagedType.I1)] public bool IsForced; - - /// - /// The path of this track. - /// - public string Path; - - /// - /// The type of this stream. - /// - public FTrackType Type; - - /// - /// Create a track from this stream. - /// - /// A new track that represent this stream. - public Track ToTrack() - { - return new() - { - Title = Title, - Language = Language, - Codec = Codec, - IsDefault = IsDefault, - IsForced = IsForced, - Path = Path, - Type = Type < FTrackType.Attachment ? (StreamType)Type : StreamType.Unknown, - IsExternal = false - }; - } - } -} diff --git a/back/src/Kyoo.Core/Models/Options/BasicOptions.cs b/back/src/Kyoo.Core/Models/Options/BasicOptions.cs deleted file mode 100644 index 056a8686..00000000 --- a/back/src/Kyoo.Core/Models/Options/BasicOptions.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Core.Models.Options -{ - /// - /// The typed list of basic/global options for Kyoo - /// - public class BasicOptions - { - /// - /// The path of this list of options - /// - public const string Path = "Basics"; - - /// - /// The temporary folder to cache transmuxed file. - /// - public string TransmuxPath { get; set; } = "cached/transmux"; - - /// - /// The temporary folder to cache transcoded file. - /// - public string TranscodePath { get; set; } = "cached/transcode"; - - /// - /// The path where metadata is stored. - /// - public string MetadataPath { get; set; } = "metadata/"; - } -} diff --git a/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs b/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs index 8bff58e8..20a9715c 100644 --- a/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs +++ b/back/src/Kyoo.Core/Views/Helper/CrudThumbsApi.cs @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . +using System; +using System.IO; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; @@ -23,6 +25,7 @@ using Kyoo.Abstractions.Models.Permissions; using Kyoo.Abstractions.Models.Utils; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; using static Kyoo.Abstractions.Models.Utils.Constants; namespace Kyoo.Core.Api @@ -37,11 +40,6 @@ namespace Kyoo.Core.Api public class CrudThumbsApi : CrudApi where T : class, IResource, IThumbnails { - /// - /// The file manager used to send images. - /// - private readonly IFileSystem _files; - /// /// The thumbnail manager used to retrieve images paths. /// @@ -53,17 +51,28 @@ namespace Kyoo.Core.Api /// /// The repository to use as a baking store for the type . /// - /// The file manager used to send images. /// The thumbnail manager used to retrieve images paths. public CrudThumbsApi(IRepository repository, - IFileSystem files, IThumbnailsManager thumbs) : base(repository) { - _files = files; _thumbs = thumbs; } + /// + /// Get the content type of a file using it's extension. + /// + /// The path of the file + /// The extension of the file is not known. + /// The content type of the file + private static string _GetContentType(string path) + { + FileExtensionContentTypeProvider provider = new(); + if (provider.TryGetContentType(path, out string contentType)) + return contentType; + throw new NotImplementedException($"Can't get the content type of the file at: {path}"); + } + /// /// Get image /// @@ -80,11 +89,7 @@ namespace Kyoo.Core.Api /// The number of the image to retrieve. /// The image asked. /// No item exist with the specific identifier or the image does not exists on kyoo. - [HttpGet("{identifier:id}/image-{image:int}")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetImage(Identifier identifier, int image) + private async Task _GetImage(Identifier identifier, int image) { T resource = await identifier.Match( id => Repository.GetOrDefault(id), @@ -92,8 +97,10 @@ namespace Kyoo.Core.Api ); if (resource == null) return NotFound(); - string path = await _thumbs.GetImagePath(resource, image); - return _files.FileResult(path); + string path = _thumbs.GetImagePath(resource, image); + if (path == null || !System.IO.File.Exists(path)) + return NotFound(); + return PhysicalFile(Path.GetFullPath(path), _GetContentType(path), true); } /// @@ -113,7 +120,7 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public Task GetPoster(Identifier identifier) { - return GetImage(identifier, Images.Poster); + return _GetImage(identifier, Images.Poster); } /// @@ -133,7 +140,7 @@ namespace Kyoo.Core.Api [ProducesResponseType(StatusCodes.Status404NotFound)] public Task GetLogo(Identifier identifier) { - return GetImage(identifier, Images.Logo); + return _GetImage(identifier, Images.Logo); } /// @@ -151,7 +158,7 @@ namespace Kyoo.Core.Api [HttpGet("{identifier:id}/thumbnail", Order = AlternativeRoute)] public Task GetBackdrop(Identifier identifier) { - return GetImage(identifier, Images.Thumbnail); + return _GetImage(identifier, Images.Thumbnail); } /// diff --git a/back/src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs b/back/src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs index 796f9f70..c272e942 100644 --- a/back/src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs +++ b/back/src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs @@ -16,7 +16,6 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . -using Kyoo.Core.Models.Options; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; diff --git a/back/src/Kyoo.Core/Views/Metadata/ProviderApi.cs b/back/src/Kyoo.Core/Views/Metadata/ProviderApi.cs deleted file mode 100644 index 97369ddf..00000000 --- a/back/src/Kyoo.Core/Views/Metadata/ProviderApi.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api -{ - /// - /// Information about one or multiple . - /// Providers are links to external websites or database. - /// They are mostly linked to plugins that provide metadata from those websites. - /// - [Route("providers")] - [Route("provider", Order = AlternativeRoute)] - [ApiController] - [ResourceView] - [PartialPermission(nameof(Provider))] - [ApiDefinition("Providers", Group = MetadataGroup)] - public class ProviderApi : CrudThumbsApi - { - /// - /// Create a new . - /// - /// - /// The library manager used to modify or retrieve information about the data store. - /// - /// The file manager used to send images and fonts. - /// The thumbnail manager used to retrieve images paths. - public ProviderApi(ILibraryManager libraryManager, - IFileSystem files, - IThumbnailsManager thumbnails) - : base(libraryManager.ProviderRepository, files, thumbnails) - { } - } -} diff --git a/back/src/Kyoo.Core/Views/Metadata/StaffApi.cs b/back/src/Kyoo.Core/Views/Metadata/StaffApi.cs index 682b027a..afcfe3b0 100644 --- a/back/src/Kyoo.Core/Views/Metadata/StaffApi.cs +++ b/back/src/Kyoo.Core/Views/Metadata/StaffApi.cs @@ -53,12 +53,10 @@ namespace Kyoo.Core.Api /// /// The library manager used to modify or retrieve information about the data store. /// - /// The file manager used to send images and fonts. /// The thumbnail manager used to retrieve images paths. public StaffApi(ILibraryManager libraryManager, - IFileSystem files, IThumbnailsManager thumbs) - : base(libraryManager.PeopleRepository, files, thumbs) + : base(libraryManager.PeopleRepository, thumbs) { _libraryManager = libraryManager; } diff --git a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs index e09ba2e5..ffd13752 100644 --- a/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/CollectionApi.cs @@ -51,12 +51,10 @@ namespace Kyoo.Core.Api /// /// The library manager used to modify or retrieve information about the data store. /// - /// The file manager used to send images. /// The thumbnail manager used to retrieve images paths. public CollectionApi(ILibraryManager libraryManager, - IFileSystem files, IThumbnailsManager thumbs) - : base(libraryManager.CollectionRepository, files, thumbs) + : base(libraryManager.CollectionRepository, thumbs) { _libraryManager = libraryManager; } diff --git a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs index 188f3c81..6b73a246 100644 --- a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs @@ -16,15 +16,12 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using Kyoo.Abstractions.Models.Attributes; using Kyoo.Abstractions.Models.Permissions; using Kyoo.Abstractions.Models.Utils; -using Kyoo.Utils; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using static Kyoo.Abstractions.Models.Utils.Constants; @@ -47,43 +44,18 @@ namespace Kyoo.Core.Api /// private readonly ILibraryManager _libraryManager; - /// - /// The transcoder used to retrive fonts. - /// - private readonly ITranscoder _transcoder; - - /// - /// The file system used to send fonts. - /// - private readonly IFileSystem _files; - /// /// Create a new . /// /// /// The library manager used to modify or retrieve information in the data store. /// - /// The transcoder used to retrive fonts - /// The file manager used to send images. /// The thumbnail manager used to retrieve images paths. public EpisodeApi(ILibraryManager libraryManager, - ITranscoder transcoder, - IFileSystem files, IThumbnailsManager thumbnails) - : base(libraryManager.EpisodeRepository, files, thumbnails) + : base(libraryManager.EpisodeRepository, thumbnails) { _libraryManager = libraryManager; - _transcoder = transcoder; - _files = files; - } - - /// - public override async Task> Create([FromBody] Episode resource) - { - // TODO: Remove this method and use a websocket API to do that. - resource.Tracks = await _transcoder.ExtractInfos(resource, false); - ActionResult ret = await base.Create(resource); - return ret; } /// @@ -132,43 +104,5 @@ namespace Kyoo.Core.Api ? NotFound() : NoContent(); } - - /// - /// Get tracks - /// - /// - /// List the tracks (video, audio and subtitles) available for this episode. - /// This endpoint provide the list of raw tracks, without transcode on it. To get a schema easier to watch - /// on a player, see the [/watch endpoint](#/watch). - /// - /// The ID or slug of the . - /// A key to sort tracks by. - /// An optional list of filters. - /// The number of tracks to return. - /// A page of tracks. - /// The filters or the sort parameters are invalid. - /// No episode with the given ID or slug could be found. - /// TODO fix the /watch endpoint link (when operations ID are specified). - [HttpGet("{identifier:id}/tracks")] - [HttpGet("{identifier:id}/track", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetEpisode(Identifier identifier, - [FromQuery] string sortBy, - [FromQuery] Dictionary where, - [FromQuery] Pagination pagination) - { - ICollection resources = await _libraryManager.GetAll( - ApiHelper.ParseWhere(where, identifier.Matcher(x => x.EpisodeID, x => x.Episode.Slug)), - Sort.From(sortBy), - pagination - ); - - if (!resources.Any() && await _libraryManager.GetOrDefault(identifier.IsSame()) == null) - return NotFound(); - return Page(resources, pagination.Limit); - } } } diff --git a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs index ca0acff5..d336e22c 100644 --- a/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/SeasonApi.cs @@ -51,12 +51,10 @@ namespace Kyoo.Core.Api /// /// The library manager used to modify or retrieve information in the data store. /// - /// The file manager used to send images. /// The thumbnail manager used to retrieve images paths. public SeasonApi(ILibraryManager libraryManager, - IFileSystem files, IThumbnailsManager thumbs) - : base(libraryManager.SeasonRepository, files, thumbs) + : base(libraryManager.SeasonRepository, thumbs) { _libraryManager = libraryManager; } diff --git a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs index 7fb6a843..95a9492e 100644 --- a/back/src/Kyoo.Core/Views/Resources/ShowApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/ShowApi.cs @@ -48,7 +48,6 @@ namespace Kyoo.Core.Api /// The library manager used to modify or retrieve information in the data store. /// private readonly ILibraryManager _libraryManager; - private readonly ITranscoder _transcoder; /// /// Create a new . @@ -56,17 +55,12 @@ namespace Kyoo.Core.Api /// /// The library manager used to modify or retrieve information about the data store. /// - /// The file manager used to send images and fonts. /// The thumbnail manager used to retrieve images paths. - /// TODO: Remove this. public ShowApi(ILibraryManager libraryManager, - IFileSystem files, - IThumbnailsManager thumbs, - ITranscoder transcoder) - : base(libraryManager.ShowRepository, files, thumbs) + IThumbnailsManager thumbs) + : base(libraryManager.ShowRepository, thumbs) { _libraryManager = libraryManager; - _transcoder = transcoder; } /// @@ -82,7 +76,6 @@ namespace Kyoo.Core.Api Path = ret.Value.Path }; - episode.Tracks = await _transcoder.ExtractInfos(episode, false); await _libraryManager.Create(episode); } return ret; diff --git a/back/src/Kyoo.Core/Views/Watch/SubtitleApi.cs b/back/src/Kyoo.Core/Views/Watch/SubtitleApi.cs deleted file mode 100644 index 7e89bef6..00000000 --- a/back/src/Kyoo.Core/Views/Watch/SubtitleApi.cs +++ /dev/null @@ -1,205 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api -{ - /// - /// An endpoint to retrieve subtitles for a specific episode. - /// - [Route("subtitles")] - [Route("subtitle", Order = AlternativeRoute)] - [PartialPermission("subtitle")] - [ApiController] - [ApiDefinition("Subtitles", Group = WatchGroup)] - public class SubtitleApi : ControllerBase - { - /// - /// The library manager used to modify or retrieve information about the data store. - /// - private readonly ILibraryManager _libraryManager; - - /// - /// The file manager used to send subtitles files. - /// - private readonly IFileSystem _files; - - /// - /// Create a new . - /// - /// The library manager used to interact with the data store. - /// The file manager used to send subtitle files. - public SubtitleApi(ILibraryManager libraryManager, IFileSystem files) - { - _libraryManager = libraryManager; - _files = files; - } - - /// - /// Get subtitle - /// - /// - /// Get the subtitle file with the given identifier. - /// The extension is optional and can be used to ask Kyoo to convert the subtitle file on the fly. - /// - /// - /// The ID or slug of the subtitle (the same as the corresponding ). - /// - /// An optional extension for the subtitle file. - /// The subtitle file - /// No subtitle exist with the given ID or slug. - [HttpGet("{identifier:int}", Order = AlternativeRoute)] - [HttpGet("{identifier:id}.{extension}")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [SuppressMessage("ReSharper", "RouteTemplates.ParameterTypeAndConstraintsMismatch", - Justification = "An indentifier can be constructed with an int.")] - public async Task GetSubtitle(Identifier identifier, string extension) - { - Track subtitle = await identifier.Match( - id => _libraryManager.GetOrDefault(id), - slug => - { - if (slug.Count(x => x == '.') == 3) - { - int idx = slug.LastIndexOf('.'); - extension = slug[(idx + 1)..]; - slug = slug[..idx]; - } - return _libraryManager.GetOrDefault(Track.BuildSlug(slug, StreamType.Subtitle)); - }); - - if (subtitle == null) - return NotFound(); - if (subtitle.Codec == "subrip" && extension == "vtt") - return new ConvertSubripToVtt(subtitle.Path, _files); - return _files.FileResult(subtitle.Path); - } - - /// - /// An action result that convert a subrip subtitle to vtt. - /// - private class ConvertSubripToVtt : IActionResult - { - /// - /// The path of the file to convert. It can be any path supported by a . - /// - private readonly string _path; - - /// - /// The file system used to manipulate the given file. - /// - private readonly IFileSystem _files; - - /// - /// Create a new . - /// - /// - /// The path of the subtitle file. It can be any path supported by the given . - /// - /// - /// The file system used to interact with the file at the given . - /// - public ConvertSubripToVtt(string subtitlePath, IFileSystem files) - { - _path = subtitlePath; - _files = files; - } - - /// - public async Task ExecuteResultAsync(ActionContext context) - { - List lines = new(); - - context.HttpContext.Response.StatusCode = 200; - context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt"); - - await using (StreamWriter writer = new(context.HttpContext.Response.Body)) - { - await writer.WriteLineAsync("WEBVTT"); - await writer.WriteLineAsync(string.Empty); - await writer.WriteLineAsync(string.Empty); - - using StreamReader reader = new(await _files.GetReader(_path)); - string line; - while ((line = await reader.ReadLineAsync()) != null) - { - if (line == string.Empty) - { - lines.Add(string.Empty); - IEnumerable processedBlock = _ConvertBlock(lines); - foreach (string t in processedBlock) - await writer.WriteLineAsync(t); - lines.Clear(); - } - else - lines.Add(line); - } - } - - await context.HttpContext.Response.Body.FlushAsync(); - } - - /// - /// Convert a block from subrip to vtt. - /// - /// All the lines in the block. - /// The given block, converted to vtt. - private static IList _ConvertBlock(IList lines) - { - if (lines.Count < 3) - return lines; - lines[1] = lines[1].Replace(',', '.'); - if (lines[2].Length > 5) - { - lines[1] += lines[2].Substring(0, 6) switch - { - "{\\an1}" => " line:93% position:15%", - "{\\an2}" => " line:93%", - "{\\an3}" => " line:93% position:85%", - "{\\an4}" => " line:50% position:15%", - "{\\an5}" => " line:50%", - "{\\an6}" => " line:50% position:85%", - "{\\an7}" => " line:7% position:15%", - "{\\an8}" => " line:7%", - "{\\an9}" => " line:7% position:85%", - _ => " line:93%" - }; - } - - if (lines[2].StartsWith("{\\an")) - lines[2] = lines[2][6..]; - return lines; - } - } - } -} diff --git a/back/src/Kyoo.Core/Views/Watch/TrackApi.cs b/back/src/Kyoo.Core/Views/Watch/TrackApi.cs deleted file mode 100644 index 29d31323..00000000 --- a/back/src/Kyoo.Core/Views/Watch/TrackApi.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Abstractions.Models.Permissions; -using Kyoo.Abstractions.Models.Utils; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using static Kyoo.Abstractions.Models.Utils.Constants; - -namespace Kyoo.Core.Api -{ - /// - /// Information about one or multiple . - /// A track contain metadata about a video, an audio or a subtitles. - /// - [Route("tracks")] - [Route("track", Order = AlternativeRoute)] - [ApiController] - [ResourceView] - [PartialPermission(nameof(Track))] - [ApiDefinition("Tracks", Group = WatchGroup)] - public class TrackApi : CrudApi - { - /// - /// The library manager used to modify or retrieve information in the data store. - /// - private readonly ILibraryManager _libraryManager; - - /// - /// Create a new . - /// - /// - /// The library manager used to modify or retrieve information in the data store. - /// - public TrackApi(ILibraryManager libraryManager) - : base(libraryManager.TrackRepository) - { - _libraryManager = libraryManager; - } - - /// - /// Get track's episode - /// - /// - /// Get the episode that uses this track. - /// - /// The ID or slug of the . - /// The episode that uses this track. - /// No track with the given ID or slug could be found. - [HttpGet("{identifier:id}/episode")] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetEpisode(Identifier identifier) - { - return await _libraryManager.Get(identifier.IsContainedIn(x => x.Tracks)); - } - } -} diff --git a/back/src/Kyoo.Core/Views/Watch/WatchApi.cs b/back/src/Kyoo.Core/Views/Watch/WatchApi.cs index 506176ad..24f53174 100644 --- a/back/src/Kyoo.Core/Views/Watch/WatchApi.cs +++ b/back/src/Kyoo.Core/Views/Watch/WatchApi.cs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . +using System.Net.Http; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; @@ -46,14 +47,9 @@ namespace Kyoo.Core.Api private readonly ILibraryManager _libraryManager; /// - /// A file system used to retrieve chapters informations. + /// The http client to reach transcoder. /// - private readonly IFileSystem _files; - - /// - /// The transcoder used to list fonts. - /// - private readonly ITranscoder _transcoder; + private readonly HttpClient _client; /// /// Create a new . @@ -61,13 +57,11 @@ namespace Kyoo.Core.Api /// /// The library manager used to modify or retrieve information in the data store. /// - /// A file system used to retrieve chapters informations. - /// The transcoder used to list fonts. - public WatchApi(ILibraryManager libraryManager, IFileSystem fs, ITranscoder transcoder) + /// The http client to reach transcoder. + public WatchApi(ILibraryManager libraryManager, HttpClient client) { _libraryManager = libraryManager; - _files = fs; - _transcoder = transcoder; + _client = client; } /// @@ -91,38 +85,7 @@ namespace Kyoo.Core.Api ); if (item == null) return NotFound(); - return await WatchItem.FromEpisode(item, _libraryManager, _files, _transcoder); - } - - /// - /// Get font - /// - /// - /// Get a font file that is used in subtitles of this episode. - /// - /// The ID or slug of the . - /// The slug of the font to retrieve. - /// A page of collections. - /// No show with the given ID/slug could be found or the font does not exist. - [HttpGet("{identifier:id}/fonts/{slug}")] - [HttpGet("{identifier:id}/font/{slug}", Order = AlternativeRoute)] - [PartialPermission(Kind.Read)] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task GetFont(Identifier identifier, string slug) - { - Episode episode = await identifier.Match( - id => _libraryManager.GetOrDefault(id), - slug => _libraryManager.GetOrDefault(slug) - ); - if (episode == null) - return NotFound(); - if (slug.Contains('.')) - slug = slug[..slug.LastIndexOf('.')]; - Font font = await _transcoder.GetFont(episode, slug); - if (font == null) - return NotFound(); - return _files.FileResult(font.Path); + return await WatchItem.FromEpisode(item, _libraryManager, _client); } } } diff --git a/back/src/Kyoo.Host/Contollers/FileSystemComposite.cs b/back/src/Kyoo.Host/Contollers/FileSystemComposite.cs deleted file mode 100644 index 71e5ff22..00000000 --- a/back/src/Kyoo.Host/Contollers/FileSystemComposite.cs +++ /dev/null @@ -1,192 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Autofac.Features.Metadata; -using JetBrains.Annotations; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Kyoo.Abstractions.Models.Attributes; -using Kyoo.Core.Models.Options; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; - -namespace Kyoo.Host.Controllers -{ - /// - /// A composite that merge every available - /// using . - /// - public class FileSystemComposite : IFileSystem - { - /// - /// The list of mapped to their metadata. - /// - private readonly ICollection, FileSystemMetadataAttribute>> _fileSystems; - - /// - /// Options to check if the metadata should be kept in the show directory or in a kyoo's directory. - /// - private readonly IOptionsMonitor _options; - - /// - /// Create a new from a list of mapped to their - /// metadata. - /// - /// The list of filesystem mapped to their metadata. - /// The options to use. - public FileSystemComposite(ICollection, FileSystemMetadataAttribute>> fileSystems, - IOptionsMonitor options) - { - _fileSystems = fileSystems; - _options = options; - } - - /// - /// Retrieve the file system that should be used for a given path. - /// - /// - /// The path that was requested. - /// - /// - /// The path that the returned file system wants - /// (respecting ). - /// - /// No file system was registered for the given path. - /// The file system that should be used for a given path - [NotNull] - private IFileSystem _GetFileSystemForPath([NotNull] string path, [NotNull] out string usablePath) - { - Regex schemeMatcher = new(@"(.+)://(.*)", RegexOptions.Compiled); - Match match = schemeMatcher.Match(path); - - if (!match.Success) - { - usablePath = path; - Meta, FileSystemMetadataAttribute> defaultFs = _fileSystems - .SingleOrDefault(x => x.Metadata.Scheme.Contains(string.Empty)); - if (defaultFs == null) - throw new ArgumentException($"No file system registered for the default scheme."); - return defaultFs.Value.Invoke(); - } - string scheme = match.Groups[1].Value; - Meta, FileSystemMetadataAttribute> ret = _fileSystems - .SingleOrDefault(x => x.Metadata.Scheme.Contains(scheme)); - if (ret == null) - throw new ArgumentException($"No file system registered for the scheme: {scheme}."); - usablePath = ret.Metadata.StripScheme ? match.Groups[2].Value : path; - return ret.Value.Invoke(); - } - - /// - public IActionResult FileResult(string path, bool rangeSupport = false, string type = null) - { - if (path == null) - return new NotFoundResult(); - return _GetFileSystemForPath(path, out string relativePath) - .FileResult(relativePath, rangeSupport, type); - } - - /// - public Task GetReader(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - return _GetFileSystemForPath(path, out string relativePath) - .GetReader(relativePath); - } - - /// - public Task GetReader(string path, AsyncRef mime) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - return _GetFileSystemForPath(path, out string relativePath) - .GetReader(relativePath, mime); - } - - /// - public Task NewFile(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - return _GetFileSystemForPath(path, out string relativePath) - .NewFile(relativePath); - } - - /// - public Task CreateDirectory(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - return _GetFileSystemForPath(path, out string relativePath) - .CreateDirectory(relativePath); - } - - /// - public string Combine(params string[] paths) - { - return _GetFileSystemForPath(paths[0], out string relativePath) - .Combine(paths[1..].Prepend(relativePath).ToArray()); - } - - /// - public Task> ListFiles(string path, SearchOption options = SearchOption.TopDirectoryOnly) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - return _GetFileSystemForPath(path, out string relativePath) - .ListFiles(relativePath, options); - } - - /// - public Task Exists(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - return _GetFileSystemForPath(path, out string relativePath) - .Exists(relativePath); - } - - /// - public Task GetExtraDirectory(T resource) - { - IFileSystem fs = _GetFileSystemForPath(_options.CurrentValue.MetadataPath, out string path); - return fs.GetExtraDirectory(resource); - } - - /// - public Task> ExtractInfos(Episode episode, bool reExtract) - { - IFileSystem fs = _GetFileSystemForPath(episode.Path, out string _); - return fs.ExtractInfos(episode, reExtract); - } - - /// - public IActionResult Transmux(Episode episode) - { - IFileSystem fs = _GetFileSystemForPath(episode.Path, out string _); - return fs.Transmux(episode); - } - } -} diff --git a/back/src/Kyoo.Host/Contollers/PluginManager.cs b/back/src/Kyoo.Host/Contollers/PluginManager.cs index 01a68311..6305c1b1 100644 --- a/back/src/Kyoo.Host/Contollers/PluginManager.cs +++ b/back/src/Kyoo.Host/Contollers/PluginManager.cs @@ -20,10 +20,8 @@ using System; using System.Collections.Generic; using System.Linq; using Kyoo.Abstractions.Controllers; -using Kyoo.Core.Models.Options; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; namespace Kyoo.Host.Controllers { @@ -38,11 +36,6 @@ namespace Kyoo.Host.Controllers /// private readonly IServiceProvider _provider; - /// - /// The configuration to get the plugin's directory. - /// - private readonly IOptions _options; - /// /// The logger used by this class. /// @@ -57,14 +50,11 @@ namespace Kyoo.Host.Controllers /// Create a new instance. /// /// A service container to allow initialization of plugins - /// The configuration instance, to get the plugin's directory path. /// The logger used by this class. public PluginManager(IServiceProvider provider, - IOptions options, ILogger logger) { _provider = provider; - _options = options; _logger = logger; } diff --git a/back/src/Kyoo.Host/HostModule.cs b/back/src/Kyoo.Host/HostModule.cs index 4f83bb62..3b9b4c55 100644 --- a/back/src/Kyoo.Host/HostModule.cs +++ b/back/src/Kyoo.Host/HostModule.cs @@ -20,11 +20,7 @@ using System.Collections.Generic; using Autofac; using Autofac.Extras.AttributeMetadata; using Kyoo.Abstractions.Controllers; -using Kyoo.Core.Models.Options; -using Kyoo.Host.Controllers; using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Serilog; namespace Kyoo.Host @@ -42,20 +38,13 @@ namespace Kyoo.Host /// private readonly IPluginManager _plugins; - /// - /// The configuration used to register options. - /// - private readonly IConfiguration _configuration; - /// /// Create a new . /// /// The plugin manager that loaded all plugins. - /// The configuration used to register options. - public HostModule(IPluginManager plugins, IConfiguration configuration) + public HostModule(IPluginManager plugins) { _plugins = plugins; - _configuration = configuration; } /// @@ -63,7 +52,6 @@ namespace Kyoo.Host { builder.RegisterModule(); builder.RegisterInstance(_plugins).As().ExternallyOwned(); - builder.RegisterComposite().InstancePerLifetimeScope(); } /// diff --git a/back/src/Kyoo.Host/PluginsStartup.cs b/back/src/Kyoo.Host/PluginsStartup.cs index 45c857ab..076e4ff6 100644 --- a/back/src/Kyoo.Host/PluginsStartup.cs +++ b/back/src/Kyoo.Host/PluginsStartup.cs @@ -24,7 +24,6 @@ using Autofac; using Kyoo.Abstractions.Controllers; using Kyoo.Authentication; using Kyoo.Core; -using Kyoo.Core.Models.Options; using Kyoo.Host.Controllers; using Kyoo.Postgresql; using Kyoo.Swagger; @@ -35,7 +34,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; namespace Kyoo.Host { @@ -49,11 +47,6 @@ namespace Kyoo.Host /// private readonly IPluginManager _plugins; - /// - /// The configuration used to register and so on for plugin's specified types. - /// - private readonly IConfiguration _configuration; - /// /// The plugin that adds controllers and tasks specific to this host. /// @@ -63,14 +56,10 @@ namespace Kyoo.Host /// Created from the DI container, those services are needed to load information and instantiate plugins.s /// /// The plugin manager to use to load new plugins and configure the host. - /// - /// The configuration used to register and so on for plugin's specified types. - /// - public PluginsStartup(IPluginManager plugins, IConfiguration configuration) + public PluginsStartup(IPluginManager plugins) { _plugins = plugins; - _configuration = configuration; - _hostModule = new HostModule(_plugins, configuration); + _hostModule = new HostModule(_plugins); _plugins.LoadPlugins( typeof(CoreModule), typeof(AuthenticationModule), @@ -94,10 +83,9 @@ namespace Kyoo.Host HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger); PluginManager plugins = new( hostProvider, - Options.Create(host.Configuration.GetSection(BasicOptions.Path).Get()), logger.CreateLogger() ); - return new PluginsStartup(plugins, host.Configuration); + return new PluginsStartup(plugins); } /// diff --git a/back/src/Kyoo.Postgresql/DatabaseContext.cs b/back/src/Kyoo.Postgresql/DatabaseContext.cs index 5a49edc1..19303e75 100644 --- a/back/src/Kyoo.Postgresql/DatabaseContext.cs +++ b/back/src/Kyoo.Postgresql/DatabaseContext.cs @@ -65,11 +65,6 @@ namespace Kyoo.Postgresql /// public DbSet Episodes { get; set; } - /// - /// All tracks of Kyoo. See . - /// - public DbSet Tracks { get; set; } - /// /// All genres of Kyoo. See . /// @@ -268,10 +263,6 @@ namespace Kyoo.Postgresql .HasMany(x => x.Episodes) .WithOne(x => x.Season) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasMany(x => x.Tracks) - .WithOne(x => x.Episode) - .OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity() .HasOne(x => x.Studio) @@ -307,7 +298,6 @@ namespace Kyoo.Postgresql modelBuilder.Entity().Property(x => x.Slug).IsRequired(); modelBuilder.Entity().Property(x => x.Slug).IsRequired(); modelBuilder.Entity().Property(x => x.Slug).IsRequired(); - modelBuilder.Entity().Property(x => x.Slug).IsRequired(); modelBuilder.Entity().Property(x => x.Slug).IsRequired(); modelBuilder.Entity().Property(x => x.Slug).IsRequired(); @@ -344,12 +334,6 @@ namespace Kyoo.Postgresql modelBuilder.Entity() .HasIndex(x => x.Slug) .IsUnique(); - modelBuilder.Entity() - .HasIndex(x => new { x.EpisodeID, x.Type, x.Language, x.TrackIndex, x.IsForced }) - .IsUnique(); - modelBuilder.Entity() - .HasIndex(x => x.Slug) - .IsUnique(); modelBuilder.Entity() .HasIndex(x => x.Slug) .IsUnique(); diff --git a/back/src/Kyoo.Postgresql/Migrations/20210801171613_Initial.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20210801171613_Initial.Designer.cs deleted file mode 100644 index 02a66b0d..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20210801171613_Initial.Designer.cs +++ /dev/null @@ -1,1259 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20210801171613_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasPostgresEnum(null, "item_type", new[] { "show", "movie", "collection" }) - .HasPostgresEnum(null, "status", new[] { "unknown", "finished", "airing", "planned" }) - .HasPostgresEnum(null, "stream_type", new[] { "unknown", "video", "audio", "subtitle", "attachment" }) - .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.8") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - modelBuilder.Entity("Kyoo.Models.Collection", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections"); - }); - - modelBuilder.Entity("Kyoo.Models.Episode", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("release_date"); - - b.Property("SeasonID") - .HasColumnType("integer") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Slug") - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_episodes"); - - b.HasIndex("SeasonID") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowID", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes"); - }); - - modelBuilder.Entity("Kyoo.Models.Genre", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_genres"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_genres_slug"); - - b.ToTable("genres"); - }); - - modelBuilder.Entity("Kyoo.Models.Library", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Paths") - .HasColumnType("text[]") - .HasColumnName("paths"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_libraries"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_libraries_slug"); - - b.ToTable("libraries"); - }); - - modelBuilder.Entity("Kyoo.Models.LibraryItem", b => - { - b.Property("ID") - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("EndAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_air"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.Property("Type") - .HasColumnType("item_type") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_library_items"); - - b.ToView("library_items"); - }); - - modelBuilder.Entity("Kyoo.Models.People", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_people"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_people_slug"); - - b.ToTable("people"); - }); - - modelBuilder.Entity("Kyoo.Models.PeopleRole", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("PeopleID") - .HasColumnType("integer") - .HasColumnName("people_id"); - - b.Property("Role") - .HasColumnType("text") - .HasColumnName("role"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Type") - .HasColumnType("text") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_people_roles"); - - b.HasIndex("PeopleID") - .HasDatabaseName("ix_people_roles_people_id"); - - b.HasIndex("ShowID") - .HasDatabaseName("ix_people_roles_show_id"); - - b.ToTable("people_roles"); - }); - - modelBuilder.Entity("Kyoo.Models.Provider", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_providers"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_providers_slug"); - - b.ToTable("providers"); - }); - - modelBuilder.Entity("Kyoo.Models.Season", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("EndDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_date"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Slug") - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_date"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowID", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons"); - }); - - modelBuilder.Entity("Kyoo.Models.Show", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Aliases") - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_air"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("IsMovie") - .HasColumnType("boolean") - .HasColumnName("is_movie"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioID") - .HasColumnType("integer") - .HasColumnName("studio_id"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioID") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows"); - }); - - modelBuilder.Entity("Kyoo.Models.Studio", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios"); - }); - - modelBuilder.Entity("Kyoo.Models.Track", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Codec") - .HasColumnType("text") - .HasColumnName("codec"); - - b.Property("EpisodeID") - .HasColumnType("integer") - .HasColumnName("episode_id"); - - b.Property("IsDefault") - .HasColumnType("boolean") - .HasColumnName("is_default"); - - b.Property("IsExternal") - .HasColumnType("boolean") - .HasColumnName("is_external"); - - b.Property("IsForced") - .HasColumnType("boolean") - .HasColumnName("is_forced"); - - b.Property("Language") - .HasColumnType("text") - .HasColumnName("language"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Slug") - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.Property("TrackIndex") - .HasColumnType("integer") - .HasColumnName("track_index"); - - b.Property("Type") - .HasColumnType("stream_type") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_tracks"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tracks_slug"); - - b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced") - .IsUnique() - .HasDatabaseName("ix_tracks_episode_id_type_language_track_index_is_forced"); - - b.ToTable("tracks"); - }); - - modelBuilder.Entity("Kyoo.Models.User", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Email") - .HasColumnType("text") - .HasColumnName("email"); - - b.Property>("ExtraData") - .HasColumnType("jsonb") - .HasColumnName("extra_data"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Username") - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("ID") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users"); - }); - - modelBuilder.Entity("Kyoo.Models.WatchedEpisode", b => - { - b.Property("UserID") - .HasColumnType("integer") - .HasColumnName("user_id"); - - b.Property("EpisodeID") - .HasColumnType("integer") - .HasColumnName("episode_id"); - - b.Property("WatchedPercentage") - .HasColumnType("integer") - .HasColumnName("watched_percentage"); - - b.HasKey("UserID", "EpisodeID") - .HasName("pk_watched_episodes"); - - b.HasIndex("EpisodeID") - .HasDatabaseName("ix_watched_episodes_episode_id"); - - b.ToTable("watched_episodes"); - }); - - modelBuilder.Entity("ShowUser", b => - { - b.Property("UsersID") - .HasColumnType("integer") - .HasColumnName("users_id"); - - b.Property("WatchedID") - .HasColumnType("integer") - .HasColumnName("watched_id"); - - b.HasKey("UsersID", "WatchedID") - .HasName("pk_link_user_show"); - - b.HasIndex("WatchedID") - .HasDatabaseName("ix_link_user_show_watched_id"); - - b.ToTable("link_user_show"); - }); - - modelBuilder.Entity("collection_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_collection_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_collection_metadata_id_provider_id"); - - b.ToTable("collection_metadata_id"); - }); - - modelBuilder.Entity("episode_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_episode_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_episode_metadata_id_provider_id"); - - b.ToTable("episode_metadata_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("integer") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show"); - }); - - modelBuilder.Entity("link_library_collection", b => - { - b.Property("collection_id") - .HasColumnType("integer") - .HasColumnName("collection_id"); - - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.HasKey("collection_id", "library_id") - .HasName("pk_link_library_collection"); - - b.HasIndex("library_id") - .HasDatabaseName("ix_link_library_collection_library_id"); - - b.ToTable("link_library_collection"); - }); - - modelBuilder.Entity("link_library_provider", b => - { - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.Property("provider_id") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.HasKey("library_id", "provider_id") - .HasName("pk_link_library_provider"); - - b.HasIndex("provider_id") - .HasDatabaseName("ix_link_library_provider_provider_id"); - - b.ToTable("link_library_provider"); - }); - - modelBuilder.Entity("link_library_show", b => - { - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("library_id", "show_id") - .HasName("pk_link_library_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_library_show_show_id"); - - b.ToTable("link_library_show"); - }); - - modelBuilder.Entity("link_show_genre", b => - { - b.Property("genre_id") - .HasColumnType("integer") - .HasColumnName("genre_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("genre_id", "show_id") - .HasName("pk_link_show_genre"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_show_genre_show_id"); - - b.ToTable("link_show_genre"); - }); - - modelBuilder.Entity("people_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_people_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_people_metadata_id_provider_id"); - - b.ToTable("people_metadata_id"); - }); - - modelBuilder.Entity("season_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_season_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_season_metadata_id_provider_id"); - - b.ToTable("season_metadata_id"); - }); - - modelBuilder.Entity("show_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_show_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_show_metadata_id_provider_id"); - - b.ToTable("show_metadata_id"); - }); - - modelBuilder.Entity("studio_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_studio_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_studio_metadata_id_provider_id"); - - b.ToTable("studio_metadata_id"); - }); - - modelBuilder.Entity("Kyoo.Models.Episode", b => - { - b.HasOne("Kyoo.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonID") - .HasConstraintName("fk_episodes_seasons_season_id") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Kyoo.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowID") - .HasConstraintName("fk_episodes_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Season"); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Models.PeopleRole", b => - { - b.HasOne("Kyoo.Models.People", "People") - .WithMany("Roles") - .HasForeignKey("PeopleID") - .HasConstraintName("fk_people_roles_people_people_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", "Show") - .WithMany("People") - .HasForeignKey("ShowID") - .HasConstraintName("fk_people_roles_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("People"); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Models.Season", b => - { - b.HasOne("Kyoo.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowID") - .HasConstraintName("fk_seasons_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Models.Show", b => - { - b.HasOne("Kyoo.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioID") - .HasConstraintName("fk_shows_studios_studio_id") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Studio"); - }); - - modelBuilder.Entity("Kyoo.Models.Track", b => - { - b.HasOne("Kyoo.Models.Episode", "Episode") - .WithMany("Tracks") - .HasForeignKey("EpisodeID") - .HasConstraintName("fk_tracks_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Episode"); - }); - - modelBuilder.Entity("Kyoo.Models.WatchedEpisode", b => - { - b.HasOne("Kyoo.Models.Episode", "Episode") - .WithMany() - .HasForeignKey("EpisodeID") - .HasConstraintName("fk_watched_episodes_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.User", null) - .WithMany("CurrentlyWatching") - .HasForeignKey("UserID") - .HasConstraintName("fk_watched_episodes_users_user_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Episode"); - }); - - modelBuilder.Entity("ShowUser", b => - { - b.HasOne("Kyoo.Models.User", null) - .WithMany() - .HasForeignKey("UsersID") - .HasConstraintName("fk_link_user_show_users_users_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany() - .HasForeignKey("WatchedID") - .HasConstraintName("fk_link_user_show_shows_watched_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("collection_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_collection_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Collection", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_collection_metadata_id_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("episode_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_episode_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Episode", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_episode_metadata_id_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .HasConstraintName("fk_link_collection_show_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_collection_show_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_collection", b => - { - b.HasOne("Kyoo.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .HasConstraintName("fk_link_library_collection_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_collection_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_provider", b => - { - b.HasOne("Kyoo.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_provider_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Provider", null) - .WithMany() - .HasForeignKey("provider_id") - .HasConstraintName("fk_link_library_provider_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_show", b => - { - b.HasOne("Kyoo.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_show_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_library_show_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_show_genre", b => - { - b.HasOne("Kyoo.Models.Genre", null) - .WithMany() - .HasForeignKey("genre_id") - .HasConstraintName("fk_link_show_genre_genres_genre_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_show_genre_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("people_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_people_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.People", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_people_metadata_id_people_people_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("season_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_season_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Season", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_season_metadata_id_seasons_season_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("show_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_show_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_show_metadata_id_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("studio_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_studio_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Studio", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_studio_metadata_id_studios_studio_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Kyoo.Models.Collection", b => - { - b.Navigation("ExternalIDs"); - }); - - modelBuilder.Entity("Kyoo.Models.Episode", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Tracks"); - }); - - modelBuilder.Entity("Kyoo.Models.People", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Roles"); - }); - - modelBuilder.Entity("Kyoo.Models.Season", b => - { - b.Navigation("Episodes"); - - b.Navigation("ExternalIDs"); - }); - - modelBuilder.Entity("Kyoo.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("ExternalIDs"); - - b.Navigation("People"); - - b.Navigation("Seasons"); - }); - - modelBuilder.Entity("Kyoo.Models.Studio", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Shows"); - }); - - modelBuilder.Entity("Kyoo.Models.User", b => - { - b.Navigation("CurrentlyWatching"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20210801171613_Initial.cs b/back/src/Kyoo.Postgresql/Migrations/20210801171613_Initial.cs index 0724cfd8..5321a0a1 100644 --- a/back/src/Kyoo.Postgresql/Migrations/20210801171613_Initial.cs +++ b/back/src/Kyoo.Postgresql/Migrations/20210801171613_Initial.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using Kyoo.Abstractions.Models; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; @@ -27,6 +28,8 @@ namespace Kyoo.Postgresql.Migrations /// /// The initial migration that build most of the database. /// + [DbContext(typeof(PostgresContext))] + [Migration("20210801171613_Initial")] public partial class Initial : Migration { /// @@ -577,7 +580,7 @@ namespace Kyoo.Postgresql.Migrations is_forced = table.Column(type: "boolean", nullable: false), is_external = table.Column(type: "boolean", nullable: false), path = table.Column(type: "text", nullable: true), - type = table.Column(type: "stream_type", nullable: false), + type = table.Column(type: "stream_type", nullable: false), episode_id = table.Column(type: "integer", nullable: false), track_index = table.Column(type: "integer", nullable: false) }, diff --git a/back/src/Kyoo.Postgresql/Migrations/20210801171641_Triggers.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20210801171641_Triggers.Designer.cs deleted file mode 100644 index de520a04..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20210801171641_Triggers.Designer.cs +++ /dev/null @@ -1,1259 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20210801171641_Triggers")] - partial class Triggers - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasPostgresEnum(null, "item_type", new[] { "show", "movie", "collection" }) - .HasPostgresEnum(null, "status", new[] { "unknown", "finished", "airing", "planned" }) - .HasPostgresEnum(null, "stream_type", new[] { "unknown", "video", "audio", "subtitle", "attachment" }) - .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.8") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - modelBuilder.Entity("Kyoo.Models.Collection", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections"); - }); - - modelBuilder.Entity("Kyoo.Models.Episode", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("release_date"); - - b.Property("SeasonID") - .HasColumnType("integer") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Slug") - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_episodes"); - - b.HasIndex("SeasonID") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowID", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes"); - }); - - modelBuilder.Entity("Kyoo.Models.Genre", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_genres"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_genres_slug"); - - b.ToTable("genres"); - }); - - modelBuilder.Entity("Kyoo.Models.Library", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Paths") - .HasColumnType("text[]") - .HasColumnName("paths"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_libraries"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_libraries_slug"); - - b.ToTable("libraries"); - }); - - modelBuilder.Entity("Kyoo.Models.LibraryItem", b => - { - b.Property("ID") - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("EndAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_air"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.Property("Type") - .HasColumnType("item_type") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_library_items"); - - b.ToView("library_items"); - }); - - modelBuilder.Entity("Kyoo.Models.People", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_people"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_people_slug"); - - b.ToTable("people"); - }); - - modelBuilder.Entity("Kyoo.Models.PeopleRole", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("PeopleID") - .HasColumnType("integer") - .HasColumnName("people_id"); - - b.Property("Role") - .HasColumnType("text") - .HasColumnName("role"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Type") - .HasColumnType("text") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_people_roles"); - - b.HasIndex("PeopleID") - .HasDatabaseName("ix_people_roles_people_id"); - - b.HasIndex("ShowID") - .HasDatabaseName("ix_people_roles_show_id"); - - b.ToTable("people_roles"); - }); - - modelBuilder.Entity("Kyoo.Models.Provider", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_providers"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_providers_slug"); - - b.ToTable("providers"); - }); - - modelBuilder.Entity("Kyoo.Models.Season", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("EndDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_date"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Slug") - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_date"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowID", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons"); - }); - - modelBuilder.Entity("Kyoo.Models.Show", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Aliases") - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_air"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("IsMovie") - .HasColumnType("boolean") - .HasColumnName("is_movie"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioID") - .HasColumnType("integer") - .HasColumnName("studio_id"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioID") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows"); - }); - - modelBuilder.Entity("Kyoo.Models.Studio", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios"); - }); - - modelBuilder.Entity("Kyoo.Models.Track", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Codec") - .HasColumnType("text") - .HasColumnName("codec"); - - b.Property("EpisodeID") - .HasColumnType("integer") - .HasColumnName("episode_id"); - - b.Property("IsDefault") - .HasColumnType("boolean") - .HasColumnName("is_default"); - - b.Property("IsExternal") - .HasColumnType("boolean") - .HasColumnName("is_external"); - - b.Property("IsForced") - .HasColumnType("boolean") - .HasColumnName("is_forced"); - - b.Property("Language") - .HasColumnType("text") - .HasColumnName("language"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Slug") - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.Property("TrackIndex") - .HasColumnType("integer") - .HasColumnName("track_index"); - - b.Property("Type") - .HasColumnType("stream_type") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_tracks"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tracks_slug"); - - b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced") - .IsUnique() - .HasDatabaseName("ix_tracks_episode_id_type_language_track_index_is_forced"); - - b.ToTable("tracks"); - }); - - modelBuilder.Entity("Kyoo.Models.User", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Email") - .HasColumnType("text") - .HasColumnName("email"); - - b.Property>("ExtraData") - .HasColumnType("jsonb") - .HasColumnName("extra_data"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Username") - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("ID") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users"); - }); - - modelBuilder.Entity("Kyoo.Models.WatchedEpisode", b => - { - b.Property("UserID") - .HasColumnType("integer") - .HasColumnName("user_id"); - - b.Property("EpisodeID") - .HasColumnType("integer") - .HasColumnName("episode_id"); - - b.Property("WatchedPercentage") - .HasColumnType("integer") - .HasColumnName("watched_percentage"); - - b.HasKey("UserID", "EpisodeID") - .HasName("pk_watched_episodes"); - - b.HasIndex("EpisodeID") - .HasDatabaseName("ix_watched_episodes_episode_id"); - - b.ToTable("watched_episodes"); - }); - - modelBuilder.Entity("ShowUser", b => - { - b.Property("UsersID") - .HasColumnType("integer") - .HasColumnName("users_id"); - - b.Property("WatchedID") - .HasColumnType("integer") - .HasColumnName("watched_id"); - - b.HasKey("UsersID", "WatchedID") - .HasName("pk_link_user_show"); - - b.HasIndex("WatchedID") - .HasDatabaseName("ix_link_user_show_watched_id"); - - b.ToTable("link_user_show"); - }); - - modelBuilder.Entity("collection_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_collection_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_collection_metadata_id_provider_id"); - - b.ToTable("collection_metadata_id"); - }); - - modelBuilder.Entity("episode_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_episode_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_episode_metadata_id_provider_id"); - - b.ToTable("episode_metadata_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("integer") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show"); - }); - - modelBuilder.Entity("link_library_collection", b => - { - b.Property("collection_id") - .HasColumnType("integer") - .HasColumnName("collection_id"); - - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.HasKey("collection_id", "library_id") - .HasName("pk_link_library_collection"); - - b.HasIndex("library_id") - .HasDatabaseName("ix_link_library_collection_library_id"); - - b.ToTable("link_library_collection"); - }); - - modelBuilder.Entity("link_library_provider", b => - { - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.Property("provider_id") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.HasKey("library_id", "provider_id") - .HasName("pk_link_library_provider"); - - b.HasIndex("provider_id") - .HasDatabaseName("ix_link_library_provider_provider_id"); - - b.ToTable("link_library_provider"); - }); - - modelBuilder.Entity("link_library_show", b => - { - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("library_id", "show_id") - .HasName("pk_link_library_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_library_show_show_id"); - - b.ToTable("link_library_show"); - }); - - modelBuilder.Entity("link_show_genre", b => - { - b.Property("genre_id") - .HasColumnType("integer") - .HasColumnName("genre_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("genre_id", "show_id") - .HasName("pk_link_show_genre"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_show_genre_show_id"); - - b.ToTable("link_show_genre"); - }); - - modelBuilder.Entity("people_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_people_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_people_metadata_id_provider_id"); - - b.ToTable("people_metadata_id"); - }); - - modelBuilder.Entity("season_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_season_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_season_metadata_id_provider_id"); - - b.ToTable("season_metadata_id"); - }); - - modelBuilder.Entity("show_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_show_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_show_metadata_id_provider_id"); - - b.ToTable("show_metadata_id"); - }); - - modelBuilder.Entity("studio_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_studio_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_studio_metadata_id_provider_id"); - - b.ToTable("studio_metadata_id"); - }); - - modelBuilder.Entity("Kyoo.Models.Episode", b => - { - b.HasOne("Kyoo.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonID") - .HasConstraintName("fk_episodes_seasons_season_id") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Kyoo.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowID") - .HasConstraintName("fk_episodes_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Season"); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Models.PeopleRole", b => - { - b.HasOne("Kyoo.Models.People", "People") - .WithMany("Roles") - .HasForeignKey("PeopleID") - .HasConstraintName("fk_people_roles_people_people_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", "Show") - .WithMany("People") - .HasForeignKey("ShowID") - .HasConstraintName("fk_people_roles_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("People"); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Models.Season", b => - { - b.HasOne("Kyoo.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowID") - .HasConstraintName("fk_seasons_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Models.Show", b => - { - b.HasOne("Kyoo.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioID") - .HasConstraintName("fk_shows_studios_studio_id") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Studio"); - }); - - modelBuilder.Entity("Kyoo.Models.Track", b => - { - b.HasOne("Kyoo.Models.Episode", "Episode") - .WithMany("Tracks") - .HasForeignKey("EpisodeID") - .HasConstraintName("fk_tracks_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Episode"); - }); - - modelBuilder.Entity("Kyoo.Models.WatchedEpisode", b => - { - b.HasOne("Kyoo.Models.Episode", "Episode") - .WithMany() - .HasForeignKey("EpisodeID") - .HasConstraintName("fk_watched_episodes_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.User", null) - .WithMany("CurrentlyWatching") - .HasForeignKey("UserID") - .HasConstraintName("fk_watched_episodes_users_user_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Episode"); - }); - - modelBuilder.Entity("ShowUser", b => - { - b.HasOne("Kyoo.Models.User", null) - .WithMany() - .HasForeignKey("UsersID") - .HasConstraintName("fk_link_user_show_users_users_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany() - .HasForeignKey("WatchedID") - .HasConstraintName("fk_link_user_show_shows_watched_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("collection_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_collection_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Collection", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_collection_metadata_id_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("episode_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_episode_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Episode", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_episode_metadata_id_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .HasConstraintName("fk_link_collection_show_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_collection_show_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_collection", b => - { - b.HasOne("Kyoo.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .HasConstraintName("fk_link_library_collection_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_collection_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_provider", b => - { - b.HasOne("Kyoo.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_provider_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Provider", null) - .WithMany() - .HasForeignKey("provider_id") - .HasConstraintName("fk_link_library_provider_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_show", b => - { - b.HasOne("Kyoo.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_show_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_library_show_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_show_genre", b => - { - b.HasOne("Kyoo.Models.Genre", null) - .WithMany() - .HasForeignKey("genre_id") - .HasConstraintName("fk_link_show_genre_genres_genre_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_show_genre_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("people_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_people_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.People", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_people_metadata_id_people_people_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("season_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_season_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Season", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_season_metadata_id_seasons_season_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("show_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_show_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Show", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_show_metadata_id_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("studio_metadata_id", b => - { - b.HasOne("Kyoo.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_studio_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Models.Studio", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_studio_metadata_id_studios_studio_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Kyoo.Models.Collection", b => - { - b.Navigation("ExternalIDs"); - }); - - modelBuilder.Entity("Kyoo.Models.Episode", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Tracks"); - }); - - modelBuilder.Entity("Kyoo.Models.People", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Roles"); - }); - - modelBuilder.Entity("Kyoo.Models.Season", b => - { - b.Navigation("Episodes"); - - b.Navigation("ExternalIDs"); - }); - - modelBuilder.Entity("Kyoo.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("ExternalIDs"); - - b.Navigation("People"); - - b.Navigation("Seasons"); - }); - - modelBuilder.Entity("Kyoo.Models.Studio", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Shows"); - }); - - modelBuilder.Entity("Kyoo.Models.User", b => - { - b.Navigation("CurrentlyWatching"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20210801171641_Triggers.cs b/back/src/Kyoo.Postgresql/Migrations/20210801171641_Triggers.cs index 94a2ad80..cc0468a5 100644 --- a/back/src/Kyoo.Postgresql/Migrations/20210801171641_Triggers.cs +++ b/back/src/Kyoo.Postgresql/Migrations/20210801171641_Triggers.cs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; namespace Kyoo.Postgresql.Migrations @@ -23,6 +24,8 @@ namespace Kyoo.Postgresql.Migrations /// /// A migration that adds postgres triggers to update slugs. /// + [DbContext(typeof(PostgresContext))] + [Migration("20210801171641_Triggers")] public partial class Triggers : Migration { /// diff --git a/back/src/Kyoo.Postgresql/Migrations/20230724144449_RemoveTriggers.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20230724144449_RemoveTriggers.Designer.cs deleted file mode 100644 index 896a679a..00000000 --- a/back/src/Kyoo.Postgresql/Migrations/20230724144449_RemoveTriggers.Designer.cs +++ /dev/null @@ -1,1258 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Kyoo.Abstractions.Models; -using Kyoo.Postgresql; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace Kyoo.Postgresql.Migrations -{ - [DbContext(typeof(PostgresContext))] - [Migration("20230724144449_RemoveTriggers")] - partial class RemoveTriggers - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasPostgresEnum(null, "item_type", new[] { "show", "movie", "collection" }) - .HasPostgresEnum(null, "status", new[] { "unknown", "finished", "airing", "planned" }) - .HasPostgresEnum(null, "stream_type", new[] { "unknown", "video", "audio", "subtitle" }) - .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.7") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_collections"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_collections_slug"); - - b.ToTable("collections"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("AbsoluteNumber") - .HasColumnType("integer") - .HasColumnName("absolute_number"); - - b.Property("EpisodeNumber") - .HasColumnType("integer") - .HasColumnName("episode_number"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("ReleaseDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("release_date"); - - b.Property("SeasonID") - .HasColumnType("integer") - .HasColumnName("season_id"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_episodes"); - - b.HasIndex("SeasonID") - .HasDatabaseName("ix_episodes_season_id"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_episodes_slug"); - - b.HasIndex("ShowID", "SeasonNumber", "EpisodeNumber", "AbsoluteNumber") - .IsUnique() - .HasDatabaseName("ix_episodes_show_id_season_number_episode_number_absolute_numb"); - - b.ToTable("episodes"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Genre", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_genres"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_genres_slug"); - - b.ToTable("genres"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Library", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Paths") - .HasColumnType("text[]") - .HasColumnName("paths"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_libraries"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_libraries_slug"); - - b.ToTable("libraries"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.LibraryItem", b => - { - b.Property("ID") - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("EndAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_air"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Slug") - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.Property("Type") - .HasColumnType("item_type") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_library_items"); - - b.ToView("library_items"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.People", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_people"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_people_slug"); - - b.ToTable("people"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.PeopleRole", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("PeopleID") - .HasColumnType("integer") - .HasColumnName("people_id"); - - b.Property("Role") - .HasColumnType("text") - .HasColumnName("role"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Type") - .HasColumnType("text") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_people_roles"); - - b.HasIndex("PeopleID") - .HasDatabaseName("ix_people_roles_people_id"); - - b.HasIndex("ShowID") - .HasDatabaseName("ix_people_roles_show_id"); - - b.ToTable("people_roles"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Provider", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_providers"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_providers_slug"); - - b.ToTable("providers"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("EndDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_date"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("SeasonNumber") - .HasColumnType("integer") - .HasColumnName("season_number"); - - b.Property("ShowID") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartDate") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_date"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_seasons"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_seasons_slug"); - - b.HasIndex("ShowID", "SeasonNumber") - .IsUnique() - .HasDatabaseName("ix_seasons_show_id_season_number"); - - b.ToTable("seasons"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Aliases") - .HasColumnType("text[]") - .HasColumnName("aliases"); - - b.Property("EndAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("end_air"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("IsMovie") - .HasColumnType("boolean") - .HasColumnName("is_movie"); - - b.Property("Overview") - .HasColumnType("text") - .HasColumnName("overview"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("StartAir") - .HasColumnType("timestamp without time zone") - .HasColumnName("start_air"); - - b.Property("Status") - .HasColumnType("status") - .HasColumnName("status"); - - b.Property("StudioID") - .HasColumnType("integer") - .HasColumnName("studio_id"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.HasKey("ID") - .HasName("pk_shows"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_shows_slug"); - - b.HasIndex("StudioID") - .HasDatabaseName("ix_shows_studio_id"); - - b.ToTable("shows"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Name") - .HasColumnType("text") - .HasColumnName("name"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.HasKey("ID") - .HasName("pk_studios"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_studios_slug"); - - b.ToTable("studios"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Track", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Codec") - .HasColumnType("text") - .HasColumnName("codec"); - - b.Property("EpisodeID") - .HasColumnType("integer") - .HasColumnName("episode_id"); - - b.Property("IsDefault") - .HasColumnType("boolean") - .HasColumnName("is_default"); - - b.Property("IsExternal") - .HasColumnType("boolean") - .HasColumnName("is_external"); - - b.Property("IsForced") - .HasColumnType("boolean") - .HasColumnName("is_forced"); - - b.Property("Language") - .HasColumnType("text") - .HasColumnName("language"); - - b.Property("Path") - .HasColumnType("text") - .HasColumnName("path"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Title") - .HasColumnType("text") - .HasColumnName("title"); - - b.Property("TrackIndex") - .HasColumnType("integer") - .HasColumnName("track_index"); - - b.Property("Type") - .HasColumnType("stream_type") - .HasColumnName("type"); - - b.HasKey("ID") - .HasName("pk_tracks"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_tracks_slug"); - - b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced") - .IsUnique() - .HasDatabaseName("ix_tracks_episode_id_type_language_track_index_is_forced"); - - b.ToTable("tracks"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasColumnName("id") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b.Property("Email") - .HasColumnType("text") - .HasColumnName("email"); - - b.Property>("ExtraData") - .HasColumnType("jsonb") - .HasColumnName("extra_data"); - - b.Property>("Images") - .HasColumnType("jsonb") - .HasColumnName("images"); - - b.Property("Password") - .HasColumnType("text") - .HasColumnName("password"); - - b.Property("Permissions") - .HasColumnType("text[]") - .HasColumnName("permissions"); - - b.Property("Slug") - .IsRequired() - .HasColumnType("text") - .HasColumnName("slug"); - - b.Property("Username") - .HasColumnType("text") - .HasColumnName("username"); - - b.HasKey("ID") - .HasName("pk_users"); - - b.HasIndex("Slug") - .IsUnique() - .HasDatabaseName("ix_users_slug"); - - b.ToTable("users"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.WatchedEpisode", b => - { - b.Property("UserID") - .HasColumnType("integer") - .HasColumnName("user_id"); - - b.Property("EpisodeID") - .HasColumnType("integer") - .HasColumnName("episode_id"); - - b.Property("WatchedPercentage") - .HasColumnType("integer") - .HasColumnName("watched_percentage"); - - b.HasKey("UserID", "EpisodeID") - .HasName("pk_watched_episodes"); - - b.HasIndex("EpisodeID") - .HasDatabaseName("ix_watched_episodes_episode_id"); - - b.ToTable("watched_episodes"); - }); - - modelBuilder.Entity("ShowUser", b => - { - b.Property("UsersID") - .HasColumnType("integer") - .HasColumnName("users_id"); - - b.Property("WatchedID") - .HasColumnType("integer") - .HasColumnName("watched_id"); - - b.HasKey("UsersID", "WatchedID") - .HasName("pk_link_user_show"); - - b.HasIndex("WatchedID") - .HasDatabaseName("ix_link_user_show_watched_id"); - - b.ToTable("link_user_show"); - }); - - modelBuilder.Entity("collection_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_collection_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_collection_metadata_id_provider_id"); - - b.ToTable("collection_metadata_id"); - }); - - modelBuilder.Entity("episode_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_episode_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_episode_metadata_id_provider_id"); - - b.ToTable("episode_metadata_id"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.Property("collection_id") - .HasColumnType("integer") - .HasColumnName("collection_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("collection_id", "show_id") - .HasName("pk_link_collection_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_collection_show_show_id"); - - b.ToTable("link_collection_show"); - }); - - modelBuilder.Entity("link_library_collection", b => - { - b.Property("collection_id") - .HasColumnType("integer") - .HasColumnName("collection_id"); - - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.HasKey("collection_id", "library_id") - .HasName("pk_link_library_collection"); - - b.HasIndex("library_id") - .HasDatabaseName("ix_link_library_collection_library_id"); - - b.ToTable("link_library_collection"); - }); - - modelBuilder.Entity("link_library_provider", b => - { - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.Property("provider_id") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.HasKey("library_id", "provider_id") - .HasName("pk_link_library_provider"); - - b.HasIndex("provider_id") - .HasDatabaseName("ix_link_library_provider_provider_id"); - - b.ToTable("link_library_provider"); - }); - - modelBuilder.Entity("link_library_show", b => - { - b.Property("library_id") - .HasColumnType("integer") - .HasColumnName("library_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("library_id", "show_id") - .HasName("pk_link_library_show"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_library_show_show_id"); - - b.ToTable("link_library_show"); - }); - - modelBuilder.Entity("link_show_genre", b => - { - b.Property("genre_id") - .HasColumnType("integer") - .HasColumnName("genre_id"); - - b.Property("show_id") - .HasColumnType("integer") - .HasColumnName("show_id"); - - b.HasKey("genre_id", "show_id") - .HasName("pk_link_show_genre"); - - b.HasIndex("show_id") - .HasDatabaseName("ix_link_show_genre_show_id"); - - b.ToTable("link_show_genre"); - }); - - modelBuilder.Entity("people_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_people_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_people_metadata_id_provider_id"); - - b.ToTable("people_metadata_id"); - }); - - modelBuilder.Entity("season_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_season_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_season_metadata_id_provider_id"); - - b.ToTable("season_metadata_id"); - }); - - modelBuilder.Entity("show_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_show_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_show_metadata_id_provider_id"); - - b.ToTable("show_metadata_id"); - }); - - modelBuilder.Entity("studio_metadata_id", b => - { - b.Property("ResourceID") - .HasColumnType("integer") - .HasColumnName("resource_id"); - - b.Property("ProviderID") - .HasColumnType("integer") - .HasColumnName("provider_id"); - - b.Property("DataID") - .HasColumnType("text") - .HasColumnName("data_id"); - - b.Property("Link") - .HasColumnType("text") - .HasColumnName("link"); - - b.HasKey("ResourceID", "ProviderID") - .HasName("pk_studio_metadata_id"); - - b.HasIndex("ProviderID") - .HasDatabaseName("ix_studio_metadata_id_provider_id"); - - b.ToTable("studio_metadata_id"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Season", "Season") - .WithMany("Episodes") - .HasForeignKey("SeasonID") - .HasConstraintName("fk_episodes_seasons_season_id") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Episodes") - .HasForeignKey("ShowID") - .HasConstraintName("fk_episodes_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Season"); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.PeopleRole", b => - { - b.HasOne("Kyoo.Abstractions.Models.People", "People") - .WithMany("Roles") - .HasForeignKey("PeopleID") - .HasConstraintName("fk_people_roles_people_people_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("People") - .HasForeignKey("ShowID") - .HasConstraintName("fk_people_roles_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("People"); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.HasOne("Kyoo.Abstractions.Models.Show", "Show") - .WithMany("Seasons") - .HasForeignKey("ShowID") - .HasConstraintName("fk_seasons_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Show"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Studio", "Studio") - .WithMany("Shows") - .HasForeignKey("StudioID") - .HasConstraintName("fk_shows_studios_studio_id") - .OnDelete(DeleteBehavior.SetNull); - - b.Navigation("Studio"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Track", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany("Tracks") - .HasForeignKey("EpisodeID") - .HasConstraintName("fk_tracks_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Episode"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.WatchedEpisode", b => - { - b.HasOne("Kyoo.Abstractions.Models.Episode", "Episode") - .WithMany() - .HasForeignKey("EpisodeID") - .HasConstraintName("fk_watched_episodes_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.User", null) - .WithMany("CurrentlyWatching") - .HasForeignKey("UserID") - .HasConstraintName("fk_watched_episodes_users_user_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Episode"); - }); - - modelBuilder.Entity("ShowUser", b => - { - b.HasOne("Kyoo.Abstractions.Models.User", null) - .WithMany() - .HasForeignKey("UsersID") - .HasConstraintName("fk_link_user_show_users_users_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("WatchedID") - .HasConstraintName("fk_link_user_show_shows_watched_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("collection_metadata_id", b => - { - b.HasOne("Kyoo.Abstractions.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_collection_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_collection_metadata_id_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("episode_metadata_id", b => - { - b.HasOne("Kyoo.Abstractions.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_episode_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Episode", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_episode_metadata_id_episodes_episode_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("link_collection_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .HasConstraintName("fk_link_collection_show_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_collection_show_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_collection", b => - { - b.HasOne("Kyoo.Abstractions.Models.Collection", null) - .WithMany() - .HasForeignKey("collection_id") - .HasConstraintName("fk_link_library_collection_collections_collection_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_collection_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_provider", b => - { - b.HasOne("Kyoo.Abstractions.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_provider_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Provider", null) - .WithMany() - .HasForeignKey("provider_id") - .HasConstraintName("fk_link_library_provider_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_library_show", b => - { - b.HasOne("Kyoo.Abstractions.Models.Library", null) - .WithMany() - .HasForeignKey("library_id") - .HasConstraintName("fk_link_library_show_libraries_library_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_library_show_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("link_show_genre", b => - { - b.HasOne("Kyoo.Abstractions.Models.Genre", null) - .WithMany() - .HasForeignKey("genre_id") - .HasConstraintName("fk_link_show_genre_genres_genre_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany() - .HasForeignKey("show_id") - .HasConstraintName("fk_link_show_genre_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("people_metadata_id", b => - { - b.HasOne("Kyoo.Abstractions.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_people_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.People", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_people_metadata_id_people_people_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("season_metadata_id", b => - { - b.HasOne("Kyoo.Abstractions.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_season_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Season", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_season_metadata_id_seasons_season_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("show_metadata_id", b => - { - b.HasOne("Kyoo.Abstractions.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_show_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Show", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_show_metadata_id_shows_show_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("studio_metadata_id", b => - { - b.HasOne("Kyoo.Abstractions.Models.Provider", "Provider") - .WithMany() - .HasForeignKey("ProviderID") - .HasConstraintName("fk_studio_metadata_id_providers_provider_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Kyoo.Abstractions.Models.Studio", null) - .WithMany("ExternalIDs") - .HasForeignKey("ResourceID") - .HasConstraintName("fk_studio_metadata_id_studios_studio_id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Provider"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Collection", b => - { - b.Navigation("ExternalIDs"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Episode", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Tracks"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.People", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Roles"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Season", b => - { - b.Navigation("Episodes"); - - b.Navigation("ExternalIDs"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Show", b => - { - b.Navigation("Episodes"); - - b.Navigation("ExternalIDs"); - - b.Navigation("People"); - - b.Navigation("Seasons"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.Studio", b => - { - b.Navigation("ExternalIDs"); - - b.Navigation("Shows"); - }); - - modelBuilder.Entity("Kyoo.Abstractions.Models.User", b => - { - b.Navigation("CurrentlyWatching"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/back/src/Kyoo.Postgresql/Migrations/20230724144449_RemoveTriggers.cs b/back/src/Kyoo.Postgresql/Migrations/20230724144449_RemoveTriggers.cs index 171f40ab..575f2276 100644 --- a/back/src/Kyoo.Postgresql/Migrations/20230724144449_RemoveTriggers.cs +++ b/back/src/Kyoo.Postgresql/Migrations/20230724144449_RemoveTriggers.cs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; namespace Kyoo.Postgresql.Migrations @@ -23,6 +24,8 @@ namespace Kyoo.Postgresql.Migrations /// /// Remove triggers /// + [DbContext(typeof(PostgresContext))] + [Migration("20230724144449_RemoveTriggers")] public partial class RemoveTriggers : Migration { /// diff --git a/back/src/Kyoo.Postgresql/Migrations/20230726100747_Timestamp.Designer.cs b/back/src/Kyoo.Postgresql/Migrations/20230726100747_Timestamp.Designer.cs index a50fbd67..a486764a 100644 --- a/back/src/Kyoo.Postgresql/Migrations/20230726100747_Timestamp.Designer.cs +++ b/back/src/Kyoo.Postgresql/Migrations/20230726100747_Timestamp.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using Kyoo.Abstractions.Models; @@ -13,8 +13,6 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Kyoo.Postgresql.Migrations { - [DbContext(typeof(PostgresContext))] - [Migration("20230726100747_Timestamp")] partial class Timestamp { /// @@ -545,7 +543,7 @@ namespace Kyoo.Postgresql.Migrations .HasColumnType("integer") .HasColumnName("track_index"); - b.Property("Type") + b.Property("Type") .HasColumnType("stream_type") .HasColumnName("type"); diff --git a/back/src/Kyoo.Postgresql/Migrations/20230726100747_Timestamp.cs b/back/src/Kyoo.Postgresql/Migrations/20230726100747_Timestamp.cs index 804af4f2..3fd9cd6b 100644 --- a/back/src/Kyoo.Postgresql/Migrations/20230726100747_Timestamp.cs +++ b/back/src/Kyoo.Postgresql/Migrations/20230726100747_Timestamp.cs @@ -17,6 +17,7 @@ // along with Kyoo. If not, see . using System; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -24,6 +25,8 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace Kyoo.Postgresql.Migrations { /// + [DbContext(typeof(PostgresContext))] + [Migration("20230726100747_Timestamp")] public partial class Timestamp : Migration { /// diff --git a/back/src/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs b/back/src/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs index 4a5cca16..5fc6685b 100644 --- a/back/src/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs +++ b/back/src/Kyoo.Postgresql/Migrations/PostgresContextModelSnapshot.cs @@ -542,7 +542,7 @@ namespace Kyoo.Postgresql.Migrations .HasColumnType("integer") .HasColumnName("track_index"); - b.Property("Type") + b.Property("Type") .HasColumnType("stream_type") .HasColumnName("type"); diff --git a/back/src/Kyoo.Postgresql/PostgresContext.cs b/back/src/Kyoo.Postgresql/PostgresContext.cs index 7c3bc8f2..8266fa1f 100644 --- a/back/src/Kyoo.Postgresql/PostgresContext.cs +++ b/back/src/Kyoo.Postgresql/PostgresContext.cs @@ -54,7 +54,6 @@ namespace Kyoo.Postgresql { NpgsqlConnection.GlobalTypeMapper.MapEnum(); NpgsqlConnection.GlobalTypeMapper.MapEnum(); - NpgsqlConnection.GlobalTypeMapper.MapEnum(); } /// @@ -108,7 +107,6 @@ namespace Kyoo.Postgresql { modelBuilder.HasPostgresEnum(); modelBuilder.HasPostgresEnum(); - modelBuilder.HasPostgresEnum(); modelBuilder.Entity() .ToView("library_items") diff --git a/back/tests/Kyoo.Tests/Database/RepositoryActivator.cs b/back/tests/Kyoo.Tests/Database/RepositoryActivator.cs index 6fe144ea..e320fd52 100644 --- a/back/tests/Kyoo.Tests/Database/RepositoryActivator.cs +++ b/back/tests/Kyoo.Tests/Database/RepositoryActivator.cs @@ -48,8 +48,7 @@ namespace Kyoo.Tests.Database SeasonRepository season = new(_NewContext(), show, provider); LibraryItemRepository libraryItem = new(_NewContext(), new Lazy(() => LibraryManager.LibraryRepository)); - EpisodeRepository episode = new(_NewContext(), show, provider, new Lazy(() => LibraryManager.TrackRepository)); - TrackRepository track = new(_NewContext(), episode); + EpisodeRepository episode = new(_NewContext(), show, provider); UserRepository user = new(_NewContext()); LibraryManager = new LibraryManager(new IBaseRepository[] { @@ -60,7 +59,6 @@ namespace Kyoo.Tests.Database show, season, episode, - track, people, studio, genre, diff --git a/back/tests/Kyoo.Tests/Database/SpecificTests/TrackTests.cs b/back/tests/Kyoo.Tests/Database/SpecificTests/TrackTests.cs deleted file mode 100644 index 84f46bad..00000000 --- a/back/tests/Kyoo.Tests/Database/SpecificTests/TrackTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Threading.Tasks; -using Kyoo.Abstractions.Controllers; -using Kyoo.Abstractions.Models; -using Xunit; -using Xunit.Abstractions; - -namespace Kyoo.Tests.Database -{ - namespace PostgreSQL - { - [Collection(nameof(Postgresql))] - public class TrackTests : ATrackTests - { - public TrackTests(PostgresFixture postgres, ITestOutputHelper output) - : base(new RepositoryActivator(output, postgres)) { } - } - } - - public abstract class ATrackTests : RepositoryTests - { - private readonly ITrackRepository _repository; - - protected ATrackTests(RepositoryActivator repositories) - : base(repositories) - { - _repository = repositories.LibraryManager.TrackRepository; - } - - [Fact] - public async Task SlugEditTest() - { - await Repositories.LibraryManager.ShowRepository.Edit(new Show - { - ID = 1, - Slug = "new-slug" - }, false); - Track track = await _repository.Get(1); - Assert.Equal("new-slug-s1e1.eng-1.subtitle", track.Slug); - } - - [Fact] - public async Task UndefinedLanguageSlugTest() - { - await _repository.Create(new Track - { - ID = 5, - TrackIndex = 0, - Type = StreamType.Video, - Language = null, - EpisodeID = TestSample.Get().ID - }); - Track track = await _repository.Get(5); - Assert.Equal("anohana-s1e1.und.video", track.Slug); - } - } -} diff --git a/back/tests/Kyoo.Tests/Database/TestSample.cs b/back/tests/Kyoo.Tests/Database/TestSample.cs index ba330165..709b44b5 100644 --- a/back/tests/Kyoo.Tests/Database/TestSample.cs +++ b/back/tests/Kyoo.Tests/Database/TestSample.cs @@ -239,29 +239,6 @@ namespace Kyoo.Tests ReleaseDate = new DateTime(2020, 06, 05).ToUniversalTime() } }, - { - typeof(Track), - () => - { - Track ret = new() - { - ID = 1, - EpisodeID = 1, - Codec = "subrip", - Language = "eng", - Path = "/path", - Title = "Subtitle track", - Episode = Get(), - Type = StreamType.Subtitle, - IsDefault = true, - IsExternal = false, - IsForced = false, - TrackIndex = 1 - }; - ret.Episode = null; - return ret; - } - }, { typeof(People), () => new People @@ -359,12 +336,6 @@ namespace Kyoo.Tests episode.Season = season; context.Episodes.Add(episode); - Track track = Get(); - track.ID = 0; - track.EpisodeID = 0; - track.Episode = episode; - context.Tracks.Add(track); - Studio studio = Get(); studio.ID = 0; studio.Shows = new List { show }; diff --git a/front/packages/models/src/resources/watch-item.ts b/front/packages/models/src/resources/watch-item.ts index 76995234..fdaa7e33 100644 --- a/front/packages/models/src/resources/watch-item.ts +++ b/front/packages/models/src/resources/watch-item.ts @@ -20,13 +20,17 @@ import { z } from "zod"; import { zdate } from "../utils"; -import { ResourceP, ImagesP, imageFn } from "../traits"; +import { ImagesP, imageFn } from "../traits"; import { EpisodeP } from "./episode"; /** - * A video, audio or subtitle track for an episode. + * A audio or subtitle track. */ -export const TrackP = ResourceP.extend({ +export const TrackP = z.object({ + /** + * The index of this track on the episode. + */ + index: z.number(), /** * The title of the stream. */ @@ -47,44 +51,16 @@ export const TrackP = ResourceP.extend({ * Is this stream tagged as forced? */ isForced: z.boolean(), - /** - * Is this track extern to the episode's file? - */ - isExternal: z.boolean(), - /** - * The index of this track on the episode. - */ - trackIndex: z.number(), - /** - * A user-friendly name for this track. It does not include the track type. - */ - displayName: z.string(), +}); +export type Audio = z.infer; + +export const SubtitleP = TrackP.extend({ /* * The url of this track (only if this is a subtitle).. */ - link: z.string().transform(imageFn).nullable(), -}); -export type Track = z.infer; - -export const FontP = z.object({ - /* - * A human-readable identifier, used in the URL. - */ - slug: z.string(), - /* - * The name of the font file (with the extension). - */ - file: z.string(), - /* - * The format of this font (the extension). - */ - format: z.string(), - /* - * The url of the font. - */ link: z.string().transform(imageFn), }); -export type Font = z.infer; +export type Subtitle = z.infer; export const ChapterP = z.object({ /** @@ -128,31 +104,41 @@ const WatchMovieP = z.preprocess( * The release date of this episode. It can be null if unknown. */ releaseDate: zdate().nullable(), + /** - * The container of the video file of this episode. Common containers are mp4, mkv, avi and so - * on. + * The transcoder's info for this item. This include subtitles, fonts, chapters... */ - container: z.string(), - /** - * The video track. See Track for more information. - */ - video: TrackP, - /** - * The list of audio tracks. See Track for more information. - */ - audios: z.array(TrackP), - /** - * The list of subtitles tracks. See Track for more information. - */ - subtitles: z.array(TrackP), - /** - * The list of fonts that can be used to display subtitles. - */ - fonts: z.array(FontP), - /** - * The list of chapters. See Chapter for more information. - */ - chapters: z.array(ChapterP), + info: z.object({ + /** + * The sha1 of the video file. + */ + sha: z.string(), + /** + * The internal path of the video file. + */ + path: z.string(), + /** + * The container of the video file of this episode. Common containers are mp4, mkv, avi and so + * on. + */ + container: z.string(), + /** + * The list of audio tracks. + */ + audios: z.array(TrackP), + /** + * The list of subtitles tracks. + */ + subtitles: z.array(SubtitleP), + /** + * The list of fonts that can be used to display subtitles. + */ + fonts: z.array(z.string().transform(imageFn)), + /** + * The list of chapters. See Chapter for more information. + */ + chapters: z.array(ChapterP), + }), /** * The links to the videos of this watch item. */ diff --git a/front/packages/ui/src/player/components/right-buttons.tsx b/front/packages/ui/src/player/components/right-buttons.tsx index fb181feb..4cc8d5ac 100644 --- a/front/packages/ui/src/player/components/right-buttons.tsx +++ b/front/packages/ui/src/player/components/right-buttons.tsx @@ -18,7 +18,7 @@ * along with Kyoo. If not, see . */ -import { Font, Track, WatchItem } from "@kyoo/models"; +import { Subtitle, WatchItem } from "@kyoo/models"; import { IconButton, tooltip, Menu, ts } from "@kyoo/primitives"; import { useAtom } from "jotai"; import { Platform, View } from "react-native"; @@ -40,8 +40,8 @@ export const RightButtons = ({ onMenuClose, ...props }: { - subtitles?: Track[]; - fonts?: Font[]; + subtitles?: Subtitle[]; + fonts?: string[]; qualities?: WatchItem["link"]; onMenuOpen: () => void; onMenuClose: () => void; @@ -71,7 +71,7 @@ export const RightButtons = ({ /> {subtitles.map((x) => ( setSubtitle(x)} diff --git a/front/packages/ui/src/player/index.tsx b/front/packages/ui/src/player/index.tsx index 22a80522..8a96fba9 100644 --- a/front/packages/ui/src/player/index.tsx +++ b/front/packages/ui/src/player/index.tsx @@ -51,9 +51,9 @@ const mapData = ( href: data ? (data.isMovie ? `/movie/${data.slug}` : `/show/${data.showSlug}`) : "#", poster: data.poster, qualities: data.link, - subtitles: data.subtitles, - chapters: data.chapters, - fonts: data.fonts, + subtitles: data.info.subtitles, + chapters: data.info.chapters, + fonts: data.info.fonts, previousSlug, nextSlug, }; @@ -92,7 +92,7 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => { const next = data && !data.isMovie && data.nextEpisode ? `/watch/${data.nextEpisode.slug}` : undefined; - useVideoKeyboard(data?.subtitles, data?.fonts, previous, next); + useVideoKeyboard(data?.info.subtitles, data?.info.fonts, previous, next); const [isFullscreen, setFullscreen] = useAtom(fullscreenAtom); const [isPlaying, setPlay] = useAtom(playAtom); @@ -191,9 +191,9 @@ export const Player: QueryPage<{ slug: string }> = ({ slug }) => { >