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
{
public bool IsMultiPart { get; set; }
public List AdditionalPartIds { get; set; }
public Video()
{
MediaStreams = new List();
PlayableStreamFileNames = new List();
AdditionalPartIds = new List();
}
///
/// 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; }
///
/// Gets or sets the media streams.
///
/// The media streams.
public List MediaStreams { 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);
}
///
/// Should be overridden to return the proper folder where metadata lives
///
/// The meta location.
[IgnoreDataMember]
public override string MetaLocation
{
get
{
return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso || IsMultiPart ? System.IO.Path.GetDirectoryName(Path) : Path;
}
}
///
/// Needed because the resolver stops at the movie folder and we find the video inside.
///
/// true if [use parent path to create resolve args]; otherwise, false.
protected override bool UseParentPathToCreateResolveArgs
{
get
{
if (IsInMixedFolder)
{
return false;
}
return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso || IsMultiPart;
}
}
///
/// 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();
}
///
/// The default video stream for this video. Use this to determine media info for this item.
///
/// The default video stream.
[IgnoreDataMember]
public MediaStream DefaultVideoStream
{
get { return MediaStreams != null ? MediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video) : null; }
}
///
/// Gets a value indicating whether [is3 D].
///
/// true if [is3 D]; otherwise, false.
[IgnoreDataMember]
public bool Is3D
{
get { return Video3DFormat.HasValue; }
}
///
/// Gets the type of the media.
///
/// The type of the media.
public override string MediaType
{
get
{
return Model.Entities.MediaType.Video;
}
}
///
/// Overrides the base implementation to refresh metadata for local trailers
///
/// The cancellation token.
/// if set to true [is new item].
/// if set to true [force].
/// if set to true [allow slow providers].
/// The reset resolve args.
/// true if a provider reports we changed
public override async Task RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true)
{
// Kick off a task to refresh the main item
var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false);
var additionalPartsChanged = 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)
{
try
{
additionalPartsChanged = await RefreshAdditionalParts(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
}
catch (IOException ex)
{
Logger.ErrorException("Error loading additional parts for {0}.", ex, Name);
}
}
return additionalPartsChanged || result;
}
///
/// Refreshes the additional parts.
///
/// The cancellation token.
/// if set to true [force save].
/// if set to true [force refresh].
/// if set to true [allow slow providers].
/// Task{System.Boolean}.
private async Task RefreshAdditionalParts(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
{
var newItems = LoadAdditionalParts().ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !AdditionalPartIds.SequenceEqual(newItemIds);
var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders));
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
AdditionalPartIds = newItemIds;
return itemsChanged || results.Contains(true);
}
///
/// Loads the additional parts.
///
/// IEnumerable{Video}.
private IEnumerable