From a86b71899ec52c44ddc6c3018e8cc5e9d7ff4d62 Mon Sep 17 00:00:00 2001 From: Andrew Rabert Date: Thu, 27 Dec 2018 18:27:57 -0500 Subject: Add GPL modules --- MediaBrowser.Controller/Entities/BaseItem.cs | 2960 ++++++++++++++++++++++++++ 1 file changed, 2960 insertions(+) create mode 100644 MediaBrowser.Controller/Entities/BaseItem.cs (limited to 'MediaBrowser.Controller/Entities/BaseItem.cs') diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs new file mode 100644 index 000000000..053ee1b96 --- /dev/null +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -0,0 +1,2960 @@ +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Collections; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Library; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Users; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Extensions; +using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Sorting; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.LiveTv; +using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.MediaInfo; + +namespace MediaBrowser.Controller.Entities +{ + /// + /// Class BaseItem + /// + public abstract class BaseItem : IHasProviderIds, IHasLookupInfo + { + protected static MetadataFields[] EmptyMetadataFieldsArray = new MetadataFields[] { }; + protected static MediaUrl[] EmptyMediaUrlArray = new MediaUrl[] { }; + protected static ItemImageInfo[] EmptyItemImageInfoArray = new ItemImageInfo[] { }; + public static readonly LinkedChild[] EmptyLinkedChildArray = new LinkedChild[] { }; + + protected BaseItem() + { + ThemeSongIds = new Guid[] {}; + ThemeVideoIds = new Guid[] {}; + Tags = new string[] {}; + Genres = new string[] {}; + Studios = new string[] {}; + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + LockedFields = EmptyMetadataFieldsArray; + ImageInfos = EmptyItemImageInfoArray; + ProductionLocations = new string[] {}; + RemoteTrailers = new MediaUrl[] { }; + ExtraIds = new Guid[] {}; + } + + public static readonly char[] SlugReplaceChars = { '?', '/', '&' }; + public static char SlugChar = '-'; + + /// + /// The supported image extensions + /// + public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn", ".gif" }; + public static readonly List SupportedImageExtensionsList = SupportedImageExtensions.ToList(); + + /// + /// The trailer folder name + /// + public static string TrailerFolderName = "trailers"; + public static string ThemeSongsFolderName = "theme-music"; + public static string ThemeSongFilename = "theme"; + public static string ThemeVideosFolderName = "backdrops"; + + [IgnoreDataMember] + public Guid[] ThemeSongIds { get; set; } + [IgnoreDataMember] + public Guid[] ThemeVideoIds { get; set; } + + [IgnoreDataMember] + public string PreferredMetadataCountryCode { get; set; } + [IgnoreDataMember] + public string PreferredMetadataLanguage { get; set; } + + public long? Size { get; set; } + public string Container { get; set; } + + [IgnoreDataMember] + public string Tagline { get; set; } + + [IgnoreDataMember] + public virtual ItemImageInfo[] ImageInfos { get; set; } + + [IgnoreDataMember] + public bool IsVirtualItem { get; set; } + + /// + /// Gets or sets the album. + /// + /// The album. + [IgnoreDataMember] + public string Album { get; set; } + + /// + /// Gets or sets the channel identifier. + /// + /// The channel identifier. + [IgnoreDataMember] + public Guid ChannelId { get; set; } + + [IgnoreDataMember] + public virtual bool SupportsAddingToPlaylist + { + get + { + return false; + } + } + + [IgnoreDataMember] + public virtual bool AlwaysScanInternalMetadataPath + { + get { return false; } + } + + /// + /// Gets a value indicating whether this instance is in mixed folder. + /// + /// true if this instance is in mixed folder; otherwise, false. + [IgnoreDataMember] + public bool IsInMixedFolder { get; set; } + + [IgnoreDataMember] + public virtual bool SupportsPlayedStatus + { + get + { + return false; + } + } + + [IgnoreDataMember] + public virtual bool SupportsPositionTicksResume + { + get + { + return false; + } + } + + [IgnoreDataMember] + public virtual bool SupportsRemoteImageDownloading + { + get + { + return true; + } + } + + private string _name; + /// + /// Gets or sets the name. + /// + /// The name. + [IgnoreDataMember] + public virtual string Name + { + get + { + return _name; + } + set + { + _name = value; + + // lazy load this again + _sortName = null; + } + } + + [IgnoreDataMember] + public bool IsUnaired + { + get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; } + } + + [IgnoreDataMember] + public int? TotalBitrate { get; set; } + [IgnoreDataMember] + public ExtraType? ExtraType { get; set; } + + [IgnoreDataMember] + public bool IsThemeMedia + { + get + { + return ExtraType.HasValue && (ExtraType.Value == Model.Entities.ExtraType.ThemeSong || ExtraType.Value == Model.Entities.ExtraType.ThemeVideo); + } + } + + [IgnoreDataMember] + public string OriginalTitle { get; set; } + + /// + /// Gets or sets the id. + /// + /// The id. + [IgnoreDataMember] + public Guid Id { get; set; } + + [IgnoreDataMember] + public Guid OwnerId { get; set; } + + /// + /// Gets or sets the audio. + /// + /// The audio. + [IgnoreDataMember] + public ProgramAudio? Audio { get; set; } + + /// + /// Return the id that should be used to key display prefs for this item. + /// Default is based on the type for everything except actual generic folders. + /// + /// The display prefs id. + [IgnoreDataMember] + public virtual Guid DisplayPreferencesId + { + get + { + var thisType = GetType(); + return thisType == typeof(Folder) ? Id : thisType.FullName.GetMD5(); + } + } + + /// + /// Gets or sets the path. + /// + /// The path. + [IgnoreDataMember] + public virtual string Path { get; set; } + + [IgnoreDataMember] + public virtual SourceType SourceType + { + get + { + if (!ChannelId.Equals(Guid.Empty)) + { + return SourceType.Channel; + } + + return SourceType.Library; + } + } + + /// + /// Returns the folder containing the item. + /// If the item is a folder, it returns the folder itself + /// + [IgnoreDataMember] + public virtual string ContainingFolderPath + { + get + { + if (IsFolder) + { + return Path; + } + + return FileSystem.GetDirectoryName(Path); + } + } + + /// + /// Gets or sets the name of the service. + /// + /// The name of the service. + [IgnoreDataMember] + public string ServiceName { get; set; } + + /// + /// If this content came from an external service, the id of the content on that service + /// + [IgnoreDataMember] + public string ExternalId { get; set; } + + [IgnoreDataMember] + public string ExternalSeriesId { get; set; } + + /// + /// Gets or sets the etag. + /// + /// The etag. + [IgnoreDataMember] + public string ExternalEtag { get; set; } + + [IgnoreDataMember] + public virtual bool IsHidden + { + get + { + return false; + } + } + + public BaseItem GetOwner() + { + var ownerId = OwnerId; + return ownerId.Equals(Guid.Empty) ? null : LibraryManager.GetItemById(ownerId); + } + + /// + /// Gets or sets the type of the location. + /// + /// The type of the location. + [IgnoreDataMember] + public virtual LocationType LocationType + { + get + { + //if (IsOffline) + //{ + // return LocationType.Offline; + //} + + var path = Path; + if (string.IsNullOrEmpty(path)) + { + if (SourceType == SourceType.Channel) + { + return LocationType.Remote; + } + + return LocationType.Virtual; + } + + return FileSystem.IsPathFile(path) ? LocationType.FileSystem : LocationType.Remote; + } + } + + [IgnoreDataMember] + public MediaProtocol? PathProtocol + { + get + { + var path = Path; + + if (string.IsNullOrEmpty(path)) + { + return null; + } + + return MediaSourceManager.GetPathProtocol(path); + } + } + + public bool IsPathProtocol(MediaProtocol protocol) + { + var current = PathProtocol; + + return current.HasValue && current.Value == protocol; + } + + [IgnoreDataMember] + public bool IsFileProtocol + { + get + { + return IsPathProtocol(MediaProtocol.File); + } + } + + [IgnoreDataMember] + public bool HasPathProtocol + { + get + { + return PathProtocol.HasValue; + } + } + + [IgnoreDataMember] + public virtual bool SupportsLocalMetadata + { + get + { + if (SourceType == SourceType.Channel) + { + return false; + } + + return IsFileProtocol; + } + } + + [IgnoreDataMember] + public virtual string FileNameWithoutExtension + { + get + { + if (IsFileProtocol) + { + return System.IO.Path.GetFileNameWithoutExtension(Path); + } + + return null; + } + } + + [IgnoreDataMember] + public virtual bool EnableAlphaNumericSorting + { + get + { + return true; + } + } + + private List> GetSortChunks(string s1) + { + var list = new List>(); + + int thisMarker = 0, thisNumericChunk = 0; + + while (thisMarker < s1.Length) + { + if (thisMarker >= s1.Length) + { + break; + } + char thisCh = s1[thisMarker]; + + StringBuilder thisChunk = new StringBuilder(); + + while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0]))) + { + thisChunk.Append(thisCh); + thisMarker++; + + if (thisMarker < s1.Length) + { + thisCh = s1[thisMarker]; + } + } + + var isNumeric = thisChunk.Length > 0 && char.IsDigit(thisChunk[0]); + list.Add(new Tuple(thisChunk, isNumeric)); + } + + return list; + } + + /// + /// This is just a helper for convenience + /// + /// The primary image path. + [IgnoreDataMember] + public string PrimaryImagePath + { + get { return this.GetImagePath(ImageType.Primary); } + } + + public bool IsMetadataFetcherEnabled(LibraryOptions libraryOptions, string name) + { + if (SourceType == SourceType.Channel) + { + // hack alert + return !EnableMediaSourceDisplay; + } + + var typeOptions = libraryOptions.GetTypeOptions(GetType().Name); + if (typeOptions != null) + { + return typeOptions.MetadataFetchers.Contains(name, StringComparer.OrdinalIgnoreCase); + } + + if (!libraryOptions.EnableInternetProviders) + { + return false; + } + + var itemConfig = ConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, GetType().Name, StringComparison.OrdinalIgnoreCase)); + + return itemConfig == null || !itemConfig.DisabledMetadataFetchers.Contains(name, StringComparer.OrdinalIgnoreCase); + } + + public bool IsImageFetcherEnabled(LibraryOptions libraryOptions, string name) + { + if (this is Channel) + { + // hack alert + return true; + } + if (SourceType == SourceType.Channel) + { + // hack alert + return !EnableMediaSourceDisplay; + } + + var typeOptions = libraryOptions.GetTypeOptions(GetType().Name); + if (typeOptions != null) + { + return typeOptions.ImageFetchers.Contains(name, StringComparer.OrdinalIgnoreCase); + } + + if (!libraryOptions.EnableInternetProviders) + { + return false; + } + + var itemConfig = ConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, GetType().Name, StringComparison.OrdinalIgnoreCase)); + + return itemConfig == null || !itemConfig.DisabledImageFetchers.Contains(name, StringComparer.OrdinalIgnoreCase); + } + + public virtual bool CanDelete() + { + if (SourceType == SourceType.Channel) + { + return ChannelManager.CanDelete(this); + } + + return IsFileProtocol; + } + + public virtual bool IsAuthorizedToDelete(User user, List allCollectionFolders) + { + if (user.Policy.EnableContentDeletion) + { + return true; + } + + var allowed = user.Policy.EnableContentDeletionFromFolders; + + if (SourceType == SourceType.Channel) + { + return allowed.Contains(ChannelId.ToString(""), StringComparer.OrdinalIgnoreCase); + } + else + { + var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders); + + foreach (var folder in collectionFolders) + { + if (allowed.Contains(folder.Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + { + return true; + } + } + } + + return false; + } + + public bool CanDelete(User user, List allCollectionFolders) + { + return CanDelete() && IsAuthorizedToDelete(user, allCollectionFolders); + } + + public bool CanDelete(User user) + { + var allCollectionFolders = LibraryManager.GetUserRootFolder().Children.OfType().ToList(); + + return CanDelete(user, allCollectionFolders); + } + + public virtual bool CanDownload() + { + return false; + } + + public virtual bool IsAuthorizedToDownload(User user) + { + return user.Policy.EnableContentDownloading; + } + + public bool CanDownload(User user) + { + return CanDownload() && IsAuthorizedToDownload(user); + } + + /// + /// Gets or sets the date created. + /// + /// The date created. + [IgnoreDataMember] + public DateTime DateCreated { get; set; } + + /// + /// Gets or sets the date modified. + /// + /// The date modified. + [IgnoreDataMember] + public DateTime DateModified { get; set; } + + [IgnoreDataMember] + public DateTime DateLastSaved { get; set; } + + [IgnoreDataMember] + public DateTime DateLastRefreshed { get; set; } + + /// + /// The logger + /// + public static ILogger Logger { get; set; } + public static ILibraryManager LibraryManager { get; set; } + public static IServerConfigurationManager ConfigurationManager { get; set; } + public static IProviderManager ProviderManager { get; set; } + public static ILocalizationManager LocalizationManager { get; set; } + public static IItemRepository ItemRepository { get; set; } + public static IFileSystem FileSystem { get; set; } + public static IUserDataManager UserDataManager { get; set; } + public static IChannelManager ChannelManager { get; set; } + public static IMediaSourceManager MediaSourceManager { get; set; } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return Name; + } + + [IgnoreDataMember] + public bool IsLocked { get; set; } + + /// + /// Gets or sets the locked fields. + /// + /// The locked fields. + [IgnoreDataMember] + public MetadataFields[] LockedFields { get; set; } + + /// + /// Gets the type of the media. + /// + /// The type of the media. + [IgnoreDataMember] + public virtual string MediaType + { + get + { + return null; + } + } + + [IgnoreDataMember] + public virtual string[] PhysicalLocations + { + get + { + if (!IsFileProtocol) + { + return new string[] { }; + } + + return new[] { Path }; + } + } + + private string _forcedSortName; + /// + /// Gets or sets the name of the forced sort. + /// + /// The name of the forced sort. + [IgnoreDataMember] + public string ForcedSortName + { + get { return _forcedSortName; } + set { _forcedSortName = value; _sortName = null; } + } + + private string _sortName; + /// + /// Gets the name of the sort. + /// + /// The name of the sort. + [IgnoreDataMember] + public string SortName + { + get + { + if (_sortName == null) + { + if (!string.IsNullOrEmpty(ForcedSortName)) + { + // Need the ToLower because that's what CreateSortName does + _sortName = ModifySortChunks(ForcedSortName).ToLower(); + } + else + { + _sortName = CreateSortName(); + } + } + return _sortName; + } + set + { + _sortName = value; + } + } + + public string GetInternalMetadataPath() + { + var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath; + + return GetInternalMetadataPath(basePath); + } + + protected virtual string GetInternalMetadataPath(string basePath) + { + if (SourceType == SourceType.Channel) + { + return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N"), Id.ToString("N")); + } + + var idString = Id.ToString("N"); + + basePath = System.IO.Path.Combine(basePath, "library"); + + return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString); + } + + /// + /// Creates the name of the sort. + /// + /// System.String. + protected virtual string CreateSortName() + { + if (Name == null) return null; //some items may not have name filled in properly + + if (!EnableAlphaNumericSorting) + { + return Name.TrimStart(); + } + + var sortable = Name.Trim().ToLower(); + + foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters) + { + sortable = sortable.Replace(removeChar, string.Empty); + } + + foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters) + { + sortable = sortable.Replace(replaceChar, " "); + } + + foreach (var search in ConfigurationManager.Configuration.SortRemoveWords) + { + // Remove from beginning if a space follows + if (sortable.StartsWith(search + " ")) + { + sortable = sortable.Remove(0, search.Length + 1); + } + // Remove from middle if surrounded by spaces + sortable = sortable.Replace(" " + search + " ", " "); + + // Remove from end if followed by a space + if (sortable.EndsWith(" " + search)) + { + sortable = sortable.Remove(sortable.Length - (search.Length + 1)); + } + } + + return ModifySortChunks(sortable); + } + + private string ModifySortChunks(string name) + { + var chunks = GetSortChunks(name); + + var builder = new StringBuilder(); + + foreach (var chunk in chunks) + { + var chunkBuilder = chunk.Item1; + + // This chunk is numeric + if (chunk.Item2) + { + while (chunkBuilder.Length < 10) + { + chunkBuilder.Insert(0, '0'); + } + } + + builder.Append(chunkBuilder); + } + //Logger.Debug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString()); + return builder.ToString().RemoveDiacritics(); + } + + [IgnoreDataMember] + public bool EnableMediaSourceDisplay + { + get + { + if (SourceType == SourceType.Channel) + { + return ChannelManager.EnableMediaSourceDisplay(this); + } + + return true; + } + } + + [IgnoreDataMember] + public Guid ParentId { get; set; } + + /// + /// Gets or sets the parent. + /// + /// The parent. + [IgnoreDataMember] + public Folder Parent + { + get { return GetParent() as Folder; } + set + { + + } + } + + public void SetParent(Folder parent) + { + ParentId = parent == null ? Guid.Empty : parent.Id; + } + + public BaseItem GetParent() + { + var parentId = ParentId; + if (!parentId.Equals(Guid.Empty)) + { + return LibraryManager.GetItemById(parentId); + } + + return null; + } + + public IEnumerable GetParents() + { + var parent = GetParent(); + + while (parent != null) + { + yield return parent; + + parent = parent.GetParent(); + } + } + + /// + /// Finds a parent of a given type + /// + /// + /// ``0. + public T FindParent() + where T : Folder + { + foreach (var parent in GetParents()) + { + var item = parent as T; + if (item != null) + { + return item; + } + } + return null; + } + + [IgnoreDataMember] + public virtual Guid DisplayParentId + { + get + { + var parentId = ParentId; + return parentId; + } + } + + [IgnoreDataMember] + public BaseItem DisplayParent + { + get + { + var id = DisplayParentId; + if (id.Equals(Guid.Empty)) + { + return null; + } + return LibraryManager.GetItemById(id); + } + } + + /// + /// When the item first debuted. For movies this could be premiere date, episodes would be first aired + /// + /// The premiere date. + [IgnoreDataMember] + public DateTime? PremiereDate { get; set; } + + /// + /// Gets or sets the end date. + /// + /// The end date. + [IgnoreDataMember] + public DateTime? EndDate { get; set; } + + /// + /// Gets or sets the official rating. + /// + /// The official rating. + [IgnoreDataMember] + public string OfficialRating { get; set; } + + [IgnoreDataMember] + public int InheritedParentalRatingValue { get; set; } + + /// + /// Gets or sets the critic rating. + /// + /// The critic rating. + [IgnoreDataMember] + public float? CriticRating { get; set; } + + /// + /// Gets or sets the custom rating. + /// + /// The custom rating. + [IgnoreDataMember] + public string CustomRating { get; set; } + + /// + /// Gets or sets the overview. + /// + /// The overview. + [IgnoreDataMember] + public string Overview { get; set; } + + /// + /// Gets or sets the studios. + /// + /// The studios. + [IgnoreDataMember] + public string[] Studios { get; set; } + + /// + /// Gets or sets the genres. + /// + /// The genres. + [IgnoreDataMember] + public string[] Genres { get; set; } + + /// + /// Gets or sets the tags. + /// + /// The tags. + [IgnoreDataMember] + public string[] Tags { get; set; } + + [IgnoreDataMember] + public string[] ProductionLocations { get; set; } + + /// + /// Gets or sets the home page URL. + /// + /// The home page URL. + [IgnoreDataMember] + public string HomePageUrl { get; set; } + + /// + /// Gets or sets the community rating. + /// + /// The community rating. + [IgnoreDataMember] + public float? CommunityRating { get; set; } + + /// + /// Gets or sets the run time ticks. + /// + /// The run time ticks. + [IgnoreDataMember] + public long? RunTimeTicks { get; set; } + + /// + /// Gets or sets the production year. + /// + /// The production year. + [IgnoreDataMember] + public int? ProductionYear { get; set; } + + /// + /// If the item is part of a series, this is it's number in the series. + /// This could be episode number, album track number, etc. + /// + /// The index number. + [IgnoreDataMember] + public int? IndexNumber { get; set; } + + /// + /// For an episode this could be the season number, or for a song this could be the disc number. + /// + /// The parent index number. + [IgnoreDataMember] + public int? ParentIndexNumber { get; set; } + + [IgnoreDataMember] + public virtual bool HasLocalAlternateVersions + { + get { return false; } + } + + [IgnoreDataMember] + public string OfficialRatingForComparison + { + get + { + var officialRating = OfficialRating; + if (!string.IsNullOrEmpty(officialRating)) + { + return officialRating; + } + + var parent = DisplayParent; + if (parent != null) + { + return parent.OfficialRatingForComparison; + } + + return null; + } + } + + [IgnoreDataMember] + public string CustomRatingForComparison + { + get + { + var customRating = CustomRating; + if (!string.IsNullOrEmpty(customRating)) + { + return customRating; + } + + var parent = DisplayParent; + if (parent != null) + { + return parent.CustomRatingForComparison; + } + + return null; + } + } + + /// + /// Gets the play access. + /// + /// The user. + /// PlayAccess. + public PlayAccess GetPlayAccess(User user) + { + if (!user.Policy.EnableMediaPlayback) + { + return PlayAccess.None; + } + + //if (!user.IsParentalScheduleAllowed()) + //{ + // return PlayAccess.None; + //} + + return PlayAccess.Full; + } + + public virtual List GetMediaStreams() + { + return MediaSourceManager.GetMediaStreams(new MediaStreamQuery + { + ItemId = Id + }); + } + + protected virtual bool IsActiveRecording() + { + return false; + } + + public virtual List GetMediaSources(bool enablePathSubstitution) + { + if (SourceType == SourceType.Channel) + { + var sources = ChannelManager.GetStaticMediaSources(this, CancellationToken.None) + .ToList(); + + if (sources.Count > 0) + { + return sources; + } + } + + var list = GetAllItemsForMediaSources(); + var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item1, i.Item2)).ToList(); + + if (IsActiveRecording()) + { + foreach (var mediaSource in result) + { + mediaSource.Type = MediaSourceType.Placeholder; + } + } + + return result.OrderBy(i => + { + if (i.VideoType == VideoType.VideoFile) + { + return 0; + } + + return 1; + + }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0) + .ThenByDescending(i => + { + var stream = i.VideoStream; + + return stream == null || stream.Width == null ? 0 : stream.Width.Value; + }) + .ToList(); + } + + protected virtual List> GetAllItemsForMediaSources() + { + return new List>(); + } + + private MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, BaseItem item, MediaSourceType type) + { + if (item == null) + { + throw new ArgumentNullException("media"); + } + + var protocol = item.PathProtocol; + + var info = new MediaSourceInfo + { + Id = item.Id.ToString("N"), + Protocol = protocol ?? MediaProtocol.File, + MediaStreams = MediaSourceManager.GetMediaStreams(item.Id), + Name = GetMediaSourceName(item), + Path = enablePathSubstitution ? GetMappedPath(item, item.Path, protocol) : item.Path, + RunTimeTicks = item.RunTimeTicks, + Container = item.Container, + Size = item.Size, + Type = type + }; + + if (string.IsNullOrEmpty(info.Path)) + { + info.Type = MediaSourceType.Placeholder; + } + + if (info.Protocol == MediaProtocol.File) + { + info.ETag = item.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N"); + } + + var video = item as Video; + if (video != null) + { + info.IsoType = video.IsoType; + info.VideoType = video.VideoType; + info.Video3DFormat = video.Video3DFormat; + info.Timestamp = video.Timestamp; + + if (video.IsShortcut) + { + info.IsRemote = true; + info.Path = video.ShortcutPath; + info.Protocol = MediaSourceManager.GetPathProtocol(info.Path); + } + + if (string.IsNullOrEmpty(info.Container)) + { + if (video.VideoType == VideoType.VideoFile || video.VideoType == VideoType.Iso) + { + if (protocol.HasValue && protocol.Value == MediaProtocol.File) + { + info.Container = System.IO.Path.GetExtension(item.Path).TrimStart('.'); + } + } + } + } + + if (string.IsNullOrEmpty(info.Container)) + { + if (protocol.HasValue && protocol.Value == MediaProtocol.File) + { + info.Container = System.IO.Path.GetExtension(item.Path).TrimStart('.'); + } + } + + if (info.SupportsDirectStream && !string.IsNullOrEmpty(info.Path)) + { + info.SupportsDirectStream = MediaSourceManager.SupportsDirectStream(info.Path, info.Protocol); + } + + if (video != null && video.VideoType != VideoType.VideoFile) + { + info.SupportsDirectStream = false; + } + + info.Bitrate = item.TotalBitrate; + info.InferTotalBitrate(); + + return info; + } + + private string GetMediaSourceName(BaseItem item) + { + var terms = new List(); + + var path = item.Path; + if (item.IsFileProtocol && !string.IsNullOrEmpty(path)) + { + if (HasLocalAlternateVersions) + { + var displayName = System.IO.Path.GetFileNameWithoutExtension(path) + .Replace(System.IO.Path.GetFileName(ContainingFolderPath), string.Empty, StringComparison.OrdinalIgnoreCase) + .TrimStart(new char[] { ' ', '-' }); + + if (!string.IsNullOrEmpty(displayName)) + { + terms.Add(displayName); + } + } + + if (terms.Count == 0) + { + var displayName = System.IO.Path.GetFileNameWithoutExtension(path); + terms.Add(displayName); + } + } + + if (terms.Count == 0) + { + terms.Add(item.Name); + } + + var video = item as Video; + if (video != null) + { + if (video.Video3DFormat.HasValue) + { + terms.Add("3D"); + } + + if (video.VideoType == VideoType.BluRay) + { + terms.Add("Bluray"); + } + else if (video.VideoType == VideoType.Dvd) + { + terms.Add("DVD"); + } + else if (video.VideoType == VideoType.Iso) + { + if (video.IsoType.HasValue) + { + if (video.IsoType.Value == Model.Entities.IsoType.BluRay) + { + terms.Add("Bluray"); + } + else if (video.IsoType.Value == Model.Entities.IsoType.Dvd) + { + terms.Add("DVD"); + } + } + else + { + terms.Add("ISO"); + } + } + } + + return string.Join("/", terms.ToArray(terms.Count)); + } + + /// + /// Loads the theme songs. + /// + /// List{Audio.Audio}. + private static Audio.Audio[] LoadThemeSongs(List fileSystemChildren, IDirectoryService directoryService) + { + var files = fileSystemChildren.Where(i => i.IsDirectory) + .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => FileSystem.GetFiles(i.FullName)) + .ToList(); + + // Support plex/xbmc convention + files.AddRange(fileSystemChildren + .Where(i => !i.IsDirectory && string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase)) + ); + + return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions()) + .OfType() + .Select(audio => + { + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio; + + if (dbItem != null) + { + audio = dbItem; + } + else + { + // item is new + audio.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeSong; + } + + return audio; + + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToArray(); + } + + /// + /// Loads the video backdrops. + /// + /// List{Video}. + private static Video[] LoadThemeVideos(IEnumerable fileSystemChildren, IDirectoryService directoryService) + { + var files = fileSystemChildren.Where(i => i.IsDirectory) + .Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => FileSystem.GetFiles(i.FullName)); + + return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions()) + .OfType