// 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, /// /// The stream is an attachement (a font, an image or something else). /// Only fonts are handled by kyoo but they are not saved to the database. /// Attachment = 4 } /// /// A video, audio or subtitle track for an episode. /// public class Track : IResource { /// public int ID { get; set; } /// [Computed] public string Slug { get { string type = Type.ToString().ToLower(); string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty; string episode = EpisodeSlug ?? Episode?.Slug ?? EpisodeID.ToString(); 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) : 0; IsForced = match.Groups["forced"].Success; Type = Enum.Parse(match.Groups["type"].Value, true); } } /// /// 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 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; set; } /// /// 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; } } // 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()}"; } } }