// 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()}";
}
}
}