using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Localization; 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.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using CommonIO; namespace MediaBrowser.Controller.Entities { /// /// Class BaseItem /// public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData, IHasMetadata, IHasLookupInfo { protected BaseItem() { Genres = new List(); Studios = new List(); ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); LockedFields = new List(); ImageInfos = new List(); } /// /// The supported image extensions /// public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" }; 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"; public string PreferredMetadataCountryCode { get; set; } public string PreferredMetadataLanguage { get; set; } public List ImageInfos { get; set; } /// /// Gets or sets the channel identifier. /// /// The channel identifier. [IgnoreDataMember] public string 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. public bool IsInMixedFolder { get; set; } [IgnoreDataMember] public virtual bool SupportsRemoteImageDownloading { get { return true; } } private string _name; /// /// Gets or sets the name. /// /// The name. public string Name { get { return _name; } set { _name = value; // lazy load this again _sortName = null; } } /// /// Gets or sets the id. /// /// The id. public Guid Id { get; set; } /// /// Gets or sets a value indicating whether this instance is hd. /// /// true if this instance is hd; otherwise, false. public bool? IsHD { 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. public virtual string Path { get; set; } [IgnoreDataMember] public bool IsOffline { get; set; } /// /// 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 System.IO.Path.GetDirectoryName(Path); } } /// /// Id of the program. /// [IgnoreDataMember] public string ExternalId { get { return this.GetProviderId("ProviderExternalId"); } set { this.SetProviderId("ProviderExternalId", value); } } /// /// Supply the image path if it can be accessed directly from the file system /// /// The image path. [IgnoreDataMember] public string ExternalImagePath { get; set; } /// /// Gets or sets the etag. /// /// The etag. [IgnoreDataMember] public string ExternalEtag { get; set; } [IgnoreDataMember] public virtual bool IsHidden { get { return false; } } public virtual bool IsHiddenFromUser(User user) { return false; } [IgnoreDataMember] public virtual bool IsOwnedItem { get { // Local trailer, special feature, theme video, etc. // An item that belongs to another item but is not part of the Parent-Child tree return !IsFolder && ParentId == Guid.Empty && LocationType == LocationType.FileSystem; } } /// /// Gets or sets the type of the location. /// /// The type of the location. [IgnoreDataMember] public virtual LocationType LocationType { get { if (IsOffline) { return LocationType.Offline; } if (string.IsNullOrWhiteSpace(Path)) { return LocationType.Virtual; } return FileSystem.IsPathFile(Path) ? LocationType.FileSystem : LocationType.Remote; } } [IgnoreDataMember] public virtual bool SupportsLocalMetadata { get { var locationType = LocationType; return locationType != LocationType.Remote && locationType != LocationType.Virtual; } } [IgnoreDataMember] public virtual string FileNameWithoutExtension { get { if (LocationType == LocationType.FileSystem) { return System.IO.Path.GetFileNameWithoutExtension(Path); } return null; } } [IgnoreDataMember] public virtual bool EnableAlphaNumericSorting { get { return true; } } /// /// This is just a helper for convenience /// /// The primary image path. [IgnoreDataMember] public string PrimaryImagePath { get { return this.GetImagePath(ImageType.Primary); } } public virtual bool IsInternetMetadataEnabled() { return ConfigurationManager.Configuration.EnableInternetProviders; } public virtual bool CanDelete() { var locationType = LocationType; return locationType != LocationType.Remote && locationType != LocationType.Virtual; } public virtual bool IsAuthorizedToDelete(User user) { return user.Policy.EnableContentDeletion; } public bool CanDelete(User user) { return CanDelete() && IsAuthorizedToDelete(user); } 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. public DateTime DateCreated { get; set; } /// /// Gets or sets the date modified. /// /// The date modified. public DateTime DateModified { get; set; } 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 ILiveTvManager LiveTvManager { get; set; } public static IChannelManager ChannelManager { get; set; } public static ICollectionManager CollectionManager { get; set; } public static IImageProcessor ImageProcessor { 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. public List 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 IEnumerable PhysicalLocations { get { var locationType = LocationType; if (locationType == LocationType.Remote || locationType == LocationType.Virtual) { return new string[] { }; } return new[] { Path }; } } private string _forcedSortName; /// /// Gets or sets the name of the forced sort. /// /// The name of the forced sort. 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 (!string.IsNullOrWhiteSpace(ForcedSortName)) { return ForcedSortName; } return _sortName ?? (_sortName = CreateSortName()); } set { _sortName = value; } } public string GetInternalMetadataPath() { var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath; return GetInternalMetadataPath(basePath); } protected virtual string GetInternalMetadataPath(string basePath) { var idString = Id.ToString("N"); if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder) { 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(); sortable = ConfigurationManager.Configuration.SortRemoveCharacters.Aggregate(sortable, (current, search) => current.Replace(search.ToLower(), string.Empty)); sortable = ConfigurationManager.Configuration.SortReplaceCharacters.Aggregate(sortable, (current, search) => current.Replace(search.ToLower(), " ")); foreach (var search in ConfigurationManager.Configuration.SortRemoveWords) { var searchLower = search.ToLower(); // Remove from beginning if a space follows if (sortable.StartsWith(searchLower + " ")) { sortable = sortable.Remove(0, searchLower.Length + 1); } // Remove from middle if surrounded by spaces sortable = sortable.Replace(" " + searchLower + " ", " "); // Remove from end if followed by a space if (sortable.EndsWith(" " + searchLower)) { sortable = sortable.Remove(sortable.Length - (searchLower.Length + 1)); } } return sortable; } public Guid ParentId { get; set; } /// /// Gets or sets the parent. /// /// The parent. [IgnoreDataMember] public Folder Parent { get { if (ParentId != Guid.Empty) { return LibraryManager.GetItemById(ParentId) as Folder; } return null; } set { } } public void SetParent(Folder parent) { ParentId = parent == null ? Guid.Empty : parent.Id; } [IgnoreDataMember] public IEnumerable Parents { get { var parent = Parent; while (parent != null) { yield return parent; parent = parent.Parent; } } } /// /// Finds a parent of a given type /// /// /// ``0. public T FindParent() where T : Folder { return Parents.OfType().FirstOrDefault(); } [IgnoreDataMember] public virtual BaseItem DisplayParent { get { return Parent; } } /// /// When the item first debuted. For movies this could be premiere date, episodes would be first aired /// /// The premiere date. public DateTime? PremiereDate { get; set; } /// /// Gets or sets the end date. /// /// The end date. [IgnoreDataMember] public DateTime? EndDate { get; set; } /// /// Gets or sets the display type of the media. /// /// The display type of the media. public string DisplayMediaType { get; set; } /// /// Gets or sets the official rating. /// /// The official rating. public string OfficialRating { get; set; } /// /// Gets or sets the official rating description. /// /// The official rating description. public string OfficialRatingDescription { get; set; } /// /// Gets or sets the custom rating. /// /// The custom rating. [IgnoreDataMember] public string CustomRating { get; set; } /// /// Gets or sets the overview. /// /// The overview. public string Overview { get; set; } /// /// Gets or sets the studios. /// /// The studios. public List Studios { get; set; } /// /// Gets or sets the genres. /// /// The genres. public List Genres { get; set; } /// /// Gets or sets the home page URL. /// /// The home page URL. public string HomePageUrl { get; set; } /// /// Gets or sets the community rating. /// /// The community rating. [IgnoreDataMember] public float? CommunityRating { get; set; } /// /// Gets or sets the community rating vote count. /// /// The community rating vote count. public int? VoteCount { get; set; } /// /// Gets or sets the run time ticks. /// /// The run time ticks. public long? RunTimeTicks { get; set; } /// /// Gets or sets the production year. /// /// The production year. 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. public int? ParentIndexNumber { get; set; } [IgnoreDataMember] public virtual string OfficialRatingForComparison { get { return OfficialRating; } } [IgnoreDataMember] public string CustomRatingForComparison { get { 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; } /// /// Loads the theme songs. /// /// List{Audio.Audio}. private IEnumerable LoadThemeSongs(List fileSystemChildren, IDirectoryService directoryService) { var files = fileSystemChildren.OfType() .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) .SelectMany(i => directoryService.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) .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; } audio.ExtraType = ExtraType.ThemeSong; return audio; // Sort them so that the list can be easily compared for changes }).OrderBy(i => i.Path).ToList(); } /// /// Loads the video backdrops. /// /// List{Video}. private IEnumerable