using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Entities { /// /// Class Video /// public class Video : BaseItem, IHasMediaStreams, IHasAspectRatio, IHasTags { public bool IsMultiPart { get; set; } public List AdditionalPartIds { get; set; } public Video() { PlayableStreamFileNames = new List(); AdditionalPartIds = new List(); Tags = new List(); } /// /// Gets or sets a value indicating whether this instance has subtitles. /// /// true if this instance has subtitles; otherwise, false. public bool HasSubtitles { get; set; } /// /// Gets or sets the tags. /// /// The tags. public List Tags { get; set; } /// /// Gets or sets the video bit rate. /// /// The video bit rate. public int? VideoBitRate { get; set; } /// /// Gets or sets the default index of the video stream. /// /// The default index of the video stream. public int? DefaultVideoStreamIndex { get; set; } /// /// Gets or sets the type of the video. /// /// The type of the video. public VideoType VideoType { get; set; } /// /// Gets or sets the type of the iso. /// /// The type of the iso. public IsoType? IsoType { get; set; } /// /// Gets or sets the video3 D format. /// /// The video3 D format. public Video3DFormat? Video3DFormat { get; set; } /// /// If the video is a folder-rip, this will hold the file list for the largest playlist /// public List PlayableStreamFileNames { get; set; } /// /// Gets the playable stream files. /// /// List{System.String}. public List GetPlayableStreamFiles() { return GetPlayableStreamFiles(Path); } /// /// Gets or sets the aspect ratio. /// /// The aspect ratio. public string AspectRatio { get; set; } [IgnoreDataMember] public override string ContainingFolderPath { get { if (IsMultiPart) { return System.IO.Path.GetDirectoryName(Path); } if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd || VideoType == VideoType.HdDvd) { return Path; } return base.ContainingFolderPath; } } public string MainFeaturePlaylistName { get; set; } /// /// Gets the playable stream files. /// /// The root path. /// List{System.String}. public List GetPlayableStreamFiles(string rootPath) { if (PlayableStreamFileNames == null) { return null; } var allFiles = Directory.EnumerateFiles(rootPath, "*", SearchOption.AllDirectories).ToList(); return PlayableStreamFileNames.Select(name => allFiles.FirstOrDefault(f => string.Equals(System.IO.Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase))) .Where(f => !string.IsNullOrEmpty(f)) .ToList(); } /// /// Gets a value indicating whether [is3 D]. /// /// true if [is3 D]; otherwise, false. [IgnoreDataMember] public bool Is3D { get { return Video3DFormat.HasValue; } } public bool IsHD { get; set; } /// /// Gets the type of the media. /// /// The type of the media. public override string MediaType { get { return Model.Entities.MediaType.Video; } } protected override async Task BeforeRefreshMetadata(MetadataRefreshOptions options, List fileSystemChildren, CancellationToken cancellationToken) { await base.BeforeRefreshMetadata(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); // Must have a parent to have additional parts // In other words, it must be part of the Parent/Child tree // The additional parts won't have additional parts themselves if (IsMultiPart && LocationType == LocationType.FileSystem && Parent != null) { var additionalPartsChanged = await RefreshAdditionalParts(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); if (additionalPartsChanged) { options.ForceSave = true; } } } /// /// Refreshes the additional parts. /// /// The options. /// The file system children. /// The cancellation token. /// Task{System.Boolean}. private async Task RefreshAdditionalParts(MetadataRefreshOptions options, List fileSystemChildren, CancellationToken cancellationToken) { var newItems = LoadAdditionalParts(fileSystemChildren).ToList(); var newItemIds = newItems.Select(i => i.Id).ToList(); var itemsChanged = !AdditionalPartIds.SequenceEqual(newItemIds); var tasks = newItems.Select(i => i.RefreshMetadata(options, cancellationToken)); await Task.WhenAll(tasks).ConfigureAwait(false); AdditionalPartIds = newItemIds; return itemsChanged; } /// /// Loads the additional parts. /// /// IEnumerable{Video}. private IEnumerable