diff options
Diffstat (limited to 'MediaBrowser.Controller/Entities/Audio')
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/Audio.cs | 216 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs | 15 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs | 9 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs | 272 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/MusicArtist.cs | 275 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/MusicGenre.cs | 145 |
6 files changed, 932 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs new file mode 100644 index 000000000..d07e31d8a --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -0,0 +1,216 @@ +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.MediaInfo; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Controller.Entities.Audio +{ + /// <summary> + /// Class Audio + /// </summary> + public class Audio : BaseItem, + IHasAlbumArtist, + IHasArtist, + IHasMusicGenres, + IHasLookupInfo<SongInfo>, + IHasMediaSources + { + /// <summary> + /// Gets or sets the artist. + /// </summary> + /// <value>The artist.</value> + [IgnoreDataMember] + public string[] Artists { get; set; } + + [IgnoreDataMember] + public string[] AlbumArtists { get; set; } + + public Audio() + { + Artists = new string[] {}; + AlbumArtists = new string[] {}; + } + + public override double GetDefaultPrimaryImageAspectRatio() + { + return 1; + } + + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return true; + } + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get { return false; } + } + + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get { return true; } + } + + [IgnoreDataMember] + protected override bool SupportsOwnedItems + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override Folder LatestItemsIndexContainer + { + get + { + return AlbumEntity; + } + } + + public override bool CanDownload() + { + return IsFileProtocol; + } + + [IgnoreDataMember] + public string[] AllArtists + { + get + { + var list = new string[AlbumArtists.Length + Artists.Length]; + + var index = 0; + foreach (var artist in AlbumArtists) + { + list[index] = artist; + index++; + } + foreach (var artist in Artists) + { + list[index] = artist; + index++; + } + + return list; + + } + } + + [IgnoreDataMember] + public MusicAlbum AlbumEntity + { + get { return FindParent<MusicAlbum>(); } + } + + /// <summary> + /// Gets the type of the media. + /// </summary> + /// <value>The type of the media.</value> + [IgnoreDataMember] + public override string MediaType + { + get + { + return Model.Entities.MediaType.Audio; + } + } + + /// <summary> + /// Creates the name of the sort. + /// </summary> + /// <returns>System.String.</returns> + protected override string CreateSortName() + { + return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "") + + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name; + } + + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty; + + + if (ParentIndexNumber.HasValue) + { + songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey; + } + songKey += Name; + + if (!string.IsNullOrEmpty(Album)) + { + songKey = Album + "-" + songKey; + } + + var albumArtist = AlbumArtists.Length == 0 ? null : AlbumArtists[0]; + if (!string.IsNullOrEmpty(albumArtist)) + { + songKey = albumArtist + "-" + songKey; + } + + list.Insert(0, songKey); + + return list; + } + + public override UnratedItem GetBlockUnratedType() + { + if (SourceType == SourceType.Library) + { + return UnratedItem.Music; + } + return base.GetBlockUnratedType(); + } + + public List<MediaStream> GetMediaStreams(MediaStreamType type) + { + return MediaSourceManager.GetMediaStreams(new MediaStreamQuery + { + ItemId = Id, + Type = type + }); + } + + public SongInfo GetLookupInfo() + { + var info = GetItemLookupInfo<SongInfo>(); + + info.AlbumArtists = AlbumArtists; + info.Album = Album; + info.Artists = Artists; + + return info; + } + + protected override List<Tuple<BaseItem, MediaSourceType>> GetAllItemsForMediaSources() + { + var list = new List<Tuple<BaseItem, MediaSourceType>>(); + list.Add(new Tuple<BaseItem, MediaSourceType>(this, MediaSourceType.Default)); + return list; + } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs new file mode 100644 index 000000000..b2dedada4 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/IHasAlbumArtist.cs @@ -0,0 +1,15 @@ + +namespace MediaBrowser.Controller.Entities.Audio +{ + public interface IHasAlbumArtist + { + string[] AlbumArtists { get; set; } + } + + public interface IHasArtist + { + string[] AllArtists { get; } + + string[] Artists { get; set; } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs new file mode 100644 index 000000000..2200d4b75 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/IHasMusicGenres.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Entities.Audio +{ + public interface IHasMusicGenres + { + string[] Genres { get; } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs new file mode 100644 index 000000000..48b5c64b2 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -0,0 +1,272 @@ +using System; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Users; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Model.Serialization; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Controller.Entities.Audio +{ + /// <summary> + /// Class MusicAlbum + /// </summary> + public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer + { + public string[] AlbumArtists { get; set; } + public string[] Artists { get; set; } + + public MusicAlbum() + { + Artists = new string[] {}; + AlbumArtists = new string[] {}; + } + + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get { return true; } + } + + [IgnoreDataMember] + public MusicArtist MusicArtist + { + get { return GetMusicArtist(new DtoOptions(true)); } + } + + public MusicArtist GetMusicArtist(DtoOptions options) + { + var parents = GetParents(); + foreach (var parent in parents) + { + var artist = parent as MusicArtist; + if (artist != null) + { + return artist; + } + } + + var name = AlbumArtist; + if (!string.IsNullOrEmpty(name)) + { + return LibraryManager.GetArtist(name, options); + } + return null; + } + + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + + [IgnoreDataMember] + public string[] AllArtists + { + get + { + var list = new string[AlbumArtists.Length + Artists.Length]; + + var index = 0; + foreach (var artist in AlbumArtists) + { + list[index] = artist; + index++; + } + foreach (var artist in Artists) + { + list[index] = artist; + index++; + } + + return list; + } + } + + [IgnoreDataMember] + public string AlbumArtist + { + get { return AlbumArtists.Length == 0 ? null : AlbumArtists[0]; } + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get { return false; } + } + + /// <summary> + /// Gets the tracks. + /// </summary> + /// <value>The tracks.</value> + [IgnoreDataMember] + public IEnumerable<BaseItem> Tracks + { + get + { + return GetRecursiveChildren(i => i is Audio); + } + } + + protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user) + { + return Tracks; + } + + public override double GetDefaultPrimaryImageAspectRatio() + { + return 1; + } + + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + var albumArtist = AlbumArtist; + if (!string.IsNullOrEmpty(albumArtist)) + { + list.Insert(0, albumArtist + "-" + Name); + } + + var id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum); + + if (!string.IsNullOrEmpty(id)) + { + list.Insert(0, "MusicAlbum-Musicbrainz-" + id); + } + + id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + if (!string.IsNullOrEmpty(id)) + { + list.Insert(0, "MusicAlbum-MusicBrainzReleaseGroup-" + id); + } + + return list; + } + + protected override bool GetBlockUnratedValue(UserPolicy config) + { + return config.BlockUnratedItems.Contains(UnratedItem.Music); + } + + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Music; + } + + public AlbumInfo GetLookupInfo() + { + var id = GetItemLookupInfo<AlbumInfo>(); + + id.AlbumArtists = AlbumArtists; + + var artist = GetMusicArtist(new DtoOptions(false)); + + if (artist != null) + { + id.ArtistProviderIds = artist.ProviderIds; + } + + id.SongInfos = GetRecursiveChildren(i => i is Audio) + .Cast<Audio>() + .Select(i => i.GetLookupInfo()) + .ToList(); + + var album = id.SongInfos + .Select(i => i.Album) + .FirstOrDefault(i => !string.IsNullOrEmpty(i)); + + if (!string.IsNullOrEmpty(album)) + { + id.Name = album; + } + + return id; + } + + public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) + { + var items = GetRecursiveChildren(); + + var totalItems = items.Count; + var numComplete = 0; + + var childUpdateType = ItemUpdateType.None; + + // Refresh songs + foreach (var item in items) + { + cancellationToken.ThrowIfCancellationRequested(); + + var updateType = await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + childUpdateType = childUpdateType | updateType; + + numComplete++; + double percent = numComplete; + percent /= totalItems; + progress.Report(percent * 95); + } + + var parentRefreshOptions = refreshOptions; + if (childUpdateType > ItemUpdateType.None) + { + parentRefreshOptions = new MetadataRefreshOptions(refreshOptions); + parentRefreshOptions.MetadataRefreshMode = MetadataRefreshMode.FullRefresh; + } + + // Refresh current item + await RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false); + + if (!refreshOptions.IsAutomated) + { + await RefreshArtists(refreshOptions, cancellationToken).ConfigureAwait(false); + } + } + + private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) + { + var all = AllArtists; + foreach (var i in all) + { + // This should not be necessary but we're seeing some cases of it + if (string.IsNullOrEmpty(i)) + { + continue; + } + + var artist = LibraryManager.GetArtist(i); + + if (!artist.IsAccessedByName) + { + continue; + } + + await artist.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + } + } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs new file mode 100644 index 000000000..82dece84b --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -0,0 +1,275 @@ +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Users; +using System; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Model.Serialization; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Extensions; +using MediaBrowser.Model.Extensions; + +namespace MediaBrowser.Controller.Entities.Audio +{ + /// <summary> + /// Class MusicArtist + /// </summary> + public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo<ArtistInfo> + { + [IgnoreDataMember] + public bool IsAccessedByName + { + get { return ParentId.Equals(Guid.Empty); } + } + + [IgnoreDataMember] + public override bool IsFolder + { + get + { + return !IsAccessedByName; + } + } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + + [IgnoreDataMember] + public override bool IsDisplayedAsFolder + { + get + { + return true; + } + } + + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + + public override double GetDefaultPrimaryImageAspectRatio() + { + return 1; + } + + public override bool CanDelete() + { + return !IsAccessedByName; + } + + public IList<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + if (query.IncludeItemTypes.Length == 0) + { + query.IncludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicVideo).Name, typeof(MusicAlbum).Name }; + query.ArtistIds = new[] { Id }; + } + + return LibraryManager.GetItemList(query); + } + + [IgnoreDataMember] + public override IEnumerable<BaseItem> Children + { + get + { + if (IsAccessedByName) + { + return new List<BaseItem>(); + } + + return base.Children; + } + } + + public override int GetChildCount(User user) + { + if (IsAccessedByName) + { + return 0; + } + return base.GetChildCount(user); + } + + public override bool IsSaveLocalMetadataEnabled() + { + if (IsAccessedByName) + { + return true; + } + + return base.IsSaveLocalMetadataEnabled(); + } + + private readonly Task _cachedTask = Task.FromResult(true); + protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + { + if (IsAccessedByName) + { + // Should never get in here anyway + return _cachedTask; + } + + return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService); + } + + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + list.InsertRange(0, GetUserDataKeys(this)); + return list; + } + + /// <summary> + /// Returns the folder containing the item. + /// If the item is a folder, it returns the folder itself + /// </summary> + /// <value>The containing folder path.</value> + [IgnoreDataMember] + public override string ContainingFolderPath + { + get + { + return Path; + } + } + + /// <summary> + /// Gets the user data key. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>System.String.</returns> + private static List<string> GetUserDataKeys(MusicArtist item) + { + var list = new List<string>(); + var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist); + + if (!string.IsNullOrEmpty(id)) + { + list.Add("Artist-Musicbrainz-" + id); + } + + list.Add("Artist-" + (item.Name ?? string.Empty).RemoveDiacritics()); + return list; + } + public override string CreatePresentationUniqueKey() + { + return "Artist-" + (Name ?? string.Empty).RemoveDiacritics(); + } + protected override bool GetBlockUnratedValue(UserPolicy config) + { + return config.BlockUnratedItems.Contains(UnratedItem.Music); + } + + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Music; + } + + public ArtistInfo GetLookupInfo() + { + var info = GetItemLookupInfo<ArtistInfo>(); + + info.SongInfos = GetRecursiveChildren(i => i is Audio) + .Cast<Audio>() + .Select(i => i.GetLookupInfo()) + .ToList(); + + return info; + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } + + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) + { + // Trim the period at the end because windows will have a hard time with that + var validName = normalizeName ? + FileSystem.GetValidFilename(name).Trim().TrimEnd('.') : + name; + + return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.ArtistsPath, validName); + } + + private string GetRebasedPath() + { + return GetPath(System.IO.Path.GetFileName(Path), false); + } + + public override bool RequiresRefresh() + { + if (IsAccessedByName) + { + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath); + return true; + } + } + return base.RequiresRefresh(); + } + + /// <summary> + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// </summary> + public override bool BeforeMetadataRefresh(bool replaceAllMetdata) + { + var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata); + + if (IsAccessedByName) + { + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Path = newPath; + hasChanges = true; + } + } + + return hasChanges; + } + } +} diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs new file mode 100644 index 000000000..d60ce83ad --- /dev/null +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Extensions; +using MediaBrowser.Model.Extensions; + +namespace MediaBrowser.Controller.Entities.Audio +{ + /// <summary> + /// Class MusicGenre + /// </summary> + public class MusicGenre : BaseItem, IItemByName + { + public override List<string> GetUserDataKeys() + { + var list = base.GetUserDataKeys(); + + list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); + return list; + } + public override string CreatePresentationUniqueKey() + { + return GetUserDataKeys()[0]; + } + + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool SupportsAncestors + { + get + { + return false; + } + } + + [IgnoreDataMember] + public override bool IsDisplayedAsFolder + { + get + { + return true; + } + } + + /// <summary> + /// Returns the folder containing the item. + /// If the item is a folder, it returns the folder itself + /// </summary> + /// <value>The containing folder path.</value> + [IgnoreDataMember] + public override string ContainingFolderPath + { + get + { + return Path; + } + } + + public override double GetDefaultPrimaryImageAspectRatio() + { + return 1; + } + + public override bool CanDelete() + { + return false; + } + + public override bool IsSaveLocalMetadataEnabled() + { + return true; + } + + [IgnoreDataMember] + public override bool SupportsPeople + { + get + { + return false; + } + } + + public IList<BaseItem> GetTaggedItems(InternalItemsQuery query) + { + query.GenreIds = new[] { Id }; + query.IncludeItemTypes = new[] { typeof(MusicVideo).Name, typeof(Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name }; + + return LibraryManager.GetItemList(query); + } + + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) + { + // Trim the period at the end because windows will have a hard time with that + var validName = normalizeName ? + FileSystem.GetValidFilename(name).Trim().TrimEnd('.') : + name; + + return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.MusicGenrePath, validName); + } + + private string GetRebasedPath() + { + return GetPath(System.IO.Path.GetFileName(Path), false); + } + + public override bool RequiresRefresh() + { + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath); + return true; + } + return base.RequiresRefresh(); + } + + /// <summary> + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// </summary> + public override bool BeforeMetadataRefresh(bool replaceAllMetdata) + { + var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata); + + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Path = newPath; + hasChanges = true; + } + + return hasChanges; + } + } +} |
