diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index e295f3b3a6..daaa6343d1 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -423,23 +423,14 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException("No series exists with Id " + request.Id); } - var seasons = series.GetSeasons(user); - - if (request.IsSpecialSeason.HasValue) + var seasons = (await series.GetItems(new InternalItemsQuery(user) { - var val = request.IsSpecialSeason.Value; - - seasons = seasons.Where(i => i.IsSpecialSeason == val); - } + IsMissing = request.IsMissing, + IsVirtualUnaired = request.IsVirtualUnaired, + IsSpecialSeason = request.IsSpecialSeason, + AdjacentTo = request.AdjacentTo - seasons = FilterVirtualSeasons(request, seasons); - - // This must be the last filter - if (!string.IsNullOrEmpty(request.AdjacentTo)) - { - seasons = UserViewBuilder.FilterForAdjacency(seasons, request.AdjacentTo) - .Cast(); - } + }).ConfigureAwait(false)).Items.OfType(); var dtoOptions = GetDtoOptions(request); @@ -453,23 +444,6 @@ namespace MediaBrowser.Api }; } - private IEnumerable FilterVirtualSeasons(GetSeasons request, IEnumerable items) - { - if (request.IsMissing.HasValue) - { - var val = request.IsMissing.Value; - items = items.Where(i => (i.IsMissingSeason) == val); - } - - if (request.IsVirtualUnaired.HasValue) - { - var val = request.IsVirtualUnaired.Value; - items = items.Where(i => i.IsVirtualUnaired == val); - } - - return items; - } - public async Task Get(GetEpisodes request) { var user = _userManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 56f9a5b888..81d1deaa23 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -270,5 +270,54 @@ namespace MediaBrowser.Controller.Entities.Audio return false; } } + + public static string GetPath(string name, bool normalizeName = true) + { + // 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(); + } + + /// + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// + public override bool BeforeMetadataRefresh() + { + var hasChanges = base.BeforeMetadataRefresh(); + + 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 index 9aa5625fac..bd991d9f47 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -92,5 +92,48 @@ namespace MediaBrowser.Controller.Entities.Audio return LibraryManager.GetItemList(query); } + + public static string GetPath(string name, bool normalizeName = true) + { + // 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(); + } + + /// + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// + public override bool BeforeMetadataRefresh() + { + var hasChanges = base.BeforeMetadataRefresh(); + + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Path = newPath; + hasChanges = true; + } + + return hasChanges; + } } } diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs index 5d66bf3abc..6448828fb3 100644 --- a/MediaBrowser.Controller/Entities/GameGenre.cs +++ b/MediaBrowser.Controller/Entities/GameGenre.cs @@ -84,5 +84,48 @@ namespace MediaBrowser.Controller.Entities return false; } } + + public static string GetPath(string name, bool normalizeName = true) + { + // 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.GameGenrePath, 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(); + } + + /// + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// + public override bool BeforeMetadataRefresh() + { + var hasChanges = base.BeforeMetadataRefresh(); + + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Path = newPath; + hasChanges = true; + } + + return hasChanges; + } } } diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index c7fe25a962..1736ba8c76 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -87,5 +87,48 @@ namespace MediaBrowser.Controller.Entities return false; } } + + public static string GetPath(string name, bool normalizeName = true) + { + // 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.GenrePath, 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(); + } + + /// + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// + public override bool BeforeMetadataRefresh() + { + var hasChanges = base.BeforeMetadataRefresh(); + + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Path = newPath; + hasChanges = true; + } + + return hasChanges; + } } } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 69cab5ec53..deea631127 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -37,6 +37,7 @@ namespace MediaBrowser.Controller.Entities public string[] Genres { get; set; } public string[] Keywords { get; set; } + public bool? IsSpecialSeason { get; set; } public bool? IsMissing { get; set; } public bool? IsUnaired { get; set; } public bool? IsVirtualUnaired { get; set; } @@ -50,6 +51,7 @@ namespace MediaBrowser.Controller.Entities public string PresentationUniqueKey { get; set; } public string Path { get; set; } + public string PathNotStartsWith { get; set; } public string Name { get; set; } public string SlugName { get; set; } diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 4ee140b2b1..f21bc0a71e 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -122,6 +122,64 @@ namespace MediaBrowser.Controller.Entities return false; } } + + public static string GetPath(string name, bool normalizeName = true) + { + // Trim the period at the end because windows will have a hard time with that + var validFilename = normalizeName ? + FileSystem.GetValidFilename(name).Trim().TrimEnd('.') : + name; + + string subFolderPrefix = null; + + foreach (char c in validFilename) + { + if (char.IsLetterOrDigit(c)) + { + subFolderPrefix = c.ToString(); + break; + } + } + + var path = ConfigurationManager.ApplicationPaths.PeoplePath; + + return string.IsNullOrEmpty(subFolderPrefix) ? + System.IO.Path.Combine(path, validFilename) : + System.IO.Path.Combine(path, subFolderPrefix, validFilename); + } + + 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(); + } + + /// + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// + public override bool BeforeMetadataRefresh() + { + var hasChanges = base.BeforeMetadataRefresh(); + + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Path = newPath; + hasChanges = true; + } + + return hasChanges; + } } /// diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 7e3d6fe8e1..04b09b7442 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -85,5 +85,48 @@ namespace MediaBrowser.Controller.Entities return false; } } + + public static string GetPath(string name, bool normalizeName = true) + { + // 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.StudioPath, 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(); + } + + /// + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// + public override bool BeforeMetadataRefresh() + { + var hasChanges = base.BeforeMetadataRefresh(); + + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Path = newPath; + hasChanges = true; + } + + return hasChanges; + } } } diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 628e27c02e..842b2fd602 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -141,24 +141,6 @@ namespace MediaBrowser.Controller.Entities.TV return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name; } - [IgnoreDataMember] - public bool IsMissingSeason - { - get { return (IsVirtualItem) && !IsUnaired; } - } - - [IgnoreDataMember] - public bool IsVirtualUnaired - { - get { return (IsVirtualItem) && IsUnaired; } - } - - [IgnoreDataMember] - public bool IsSpecialSeason - { - get { return (IndexNumber ?? -1) == 0; } - } - protected override Task> GetItemsInternal(InternalItemsQuery query) { if (query.User == null) @@ -189,19 +171,17 @@ namespace MediaBrowser.Controller.Entities.TV /// IEnumerable{Episode}. public IEnumerable GetEpisodes(User user) { - var config = user.Configuration; - - return GetEpisodes(Series, user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); + return GetEpisodes(Series, user); } - public IEnumerable GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) + public IEnumerable GetEpisodes(Series series, User user) { - return GetEpisodes(series, user, includeMissingEpisodes, includeVirtualUnairedEpisodes, null); + return GetEpisodes(series, user, null); } - public IEnumerable GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable allSeriesEpisodes) + public IEnumerable GetEpisodes(Series series, User user, IEnumerable allSeriesEpisodes) { - return series.GetSeasonEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes, allSeriesEpisodes); + return series.GetSeasonEpisodes(user, this, allSeriesEpisodes); } public IEnumerable GetEpisodes() diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index e48c32a25e..4915cfedc7 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -207,7 +207,30 @@ namespace MediaBrowser.Controller.Entities.TV { var config = user.Configuration; - return GetSeasons(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); + var seriesKey = GetUniqueSeriesKey(this); + + Logger.Debug("GetSeasons SeriesKey: {0}", seriesKey); + var query = new InternalItemsQuery(user) + { + AncestorWithPresentationUniqueKey = seriesKey, + IncludeItemTypes = new[] {typeof (Season).Name}, + SortBy = new[] {ItemSortBy.SortName} + }; + + if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) + { + query.IsVirtualItem = false; + } + else if (!config.DisplayMissingEpisodes) + { + query.IsMissing = false; + } + else if (!config.DisplayUnairedEpisodes) + { + query.IsVirtualUnaired = false; + } + + return LibraryManager.GetItemList(query).Cast(); } protected override Task> GetItemsInternal(InternalItemsQuery query) @@ -241,59 +264,39 @@ namespace MediaBrowser.Controller.Entities.TV return Task.FromResult(result); } - public IEnumerable GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired) + public IEnumerable GetEpisodes(User user) { var seriesKey = GetUniqueSeriesKey(this); + Logger.Debug("GetEpisodes seriesKey: {0}", seriesKey); - Logger.Debug("GetSeasons SeriesKey: {0}", seriesKey); - var seasons = LibraryManager.GetItemList(new InternalItemsQuery(user) + var query = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = seriesKey, - IncludeItemTypes = new[] { typeof(Season).Name }, - SortBy = new[] { ItemSortBy.SortName } - - }).Cast().ToList(); - - - if (!includeMissingSeasons) + IncludeItemTypes = new[] {typeof (Episode).Name, typeof (Season).Name}, + SortBy = new[] {ItemSortBy.SortName} + }; + var config = user.Configuration; + if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) { - seasons = seasons.Where(i => !(i.IsMissingSeason)).ToList(); + query.IsVirtualItem = false; } - - if (!includeVirtualUnaired) + else if (!config.DisplayMissingEpisodes) { - seasons = seasons.Where(i => !i.IsVirtualUnaired).ToList(); + query.IsMissing = false; } - - return seasons; - } - - public IEnumerable GetEpisodes(User user) - { - var config = user.Configuration; - - return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); - } - - public IEnumerable GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired) - { - var seriesKey = GetUniqueSeriesKey(this); - Logger.Debug("GetEpisodes seriesKey: {0}", seriesKey); - - var allItems = LibraryManager.GetItemList(new InternalItemsQuery(user) + else if (!config.DisplayUnairedEpisodes) { - AncestorWithPresentationUniqueKey = seriesKey, - IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name }, - SortBy = new[] { ItemSortBy.SortName } + query.IsVirtualUnaired = false; + } - }).ToList(); + var allItems = LibraryManager.GetItemList(query).ToList(); Logger.Debug("GetEpisodes return {0} items from database", allItems.Count); var allSeriesEpisodes = allItems.OfType().ToList(); var allEpisodes = allItems.OfType() - .SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes)) + .SelectMany(i => i.GetEpisodes(this, user, allSeriesEpisodes)) .Reverse() .ToList(); @@ -370,13 +373,6 @@ namespace MediaBrowser.Controller.Entities.TV progress.Report(100); } - public IEnumerable GetSeasonEpisodes(User user, Season season) - { - var config = user.Configuration; - - return GetSeasonEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); - } - private IEnumerable GetAllEpisodes(User user) { Logger.Debug("Series.GetAllEpisodes entering GetItemList"); @@ -394,62 +390,51 @@ namespace MediaBrowser.Controller.Entities.TV return result; } - public IEnumerable GetSeasonEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) + public IEnumerable GetSeasonEpisodes(User user, Season parentSeason) { - IEnumerable episodes = GetAllEpisodes(user); - - return GetSeasonEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes); - } + var seriesKey = GetUniqueSeriesKey(this); + Logger.Debug("GetSeasonEpisodes seriesKey: {0}", seriesKey); - public IEnumerable GetSeasonEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable allSeriesEpisodes) - { - if (allSeriesEpisodes == null) + var query = new InternalItemsQuery(user) { - Logger.Debug("GetSeasonEpisodes allSeriesEpisodes is null"); - return GetSeasonEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes); + AncestorWithPresentationUniqueKey = seriesKey, + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { ItemSortBy.SortName } + }; + var config = user.Configuration; + if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) + { + query.IsVirtualItem = false; } - - Logger.Debug("GetSeasonEpisodes FilterEpisodesBySeason"); - var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons); - - if (!includeMissingEpisodes) + else if (!config.DisplayMissingEpisodes) { - episodes = episodes.Where(i => !i.IsMissingEpisode); + query.IsMissing = false; } - if (!includeVirtualUnairedEpisodes) + else if (!config.DisplayUnairedEpisodes) { - episodes = episodes.Where(i => !i.IsVirtualUnaired); + query.IsVirtualUnaired = false; } - var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder; + var allItems = LibraryManager.GetItemList(query).OfType(); - return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending) - .Cast(); + return GetSeasonEpisodes(user, parentSeason, allItems); } - /// - /// Filters the episodes by season. - /// - public static IEnumerable FilterEpisodesBySeason(IEnumerable episodes, int seasonNumber, bool includeSpecials) + public IEnumerable GetSeasonEpisodes(User user, Season parentSeason, IEnumerable allSeriesEpisodes) { - if (!includeSpecials || seasonNumber < 1) + if (allSeriesEpisodes == null) { - return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber); + Logger.Debug("GetSeasonEpisodes allSeriesEpisodes is null"); + return GetSeasonEpisodes(user, parentSeason); } - return episodes.Where(i => - { - var episode = i; - - if (episode != null) - { - var currentSeasonNumber = episode.AiredSeasonNumber; + Logger.Debug("GetSeasonEpisodes FilterEpisodesBySeason"); + var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons); - return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber; - } + var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder; - return false; - }); + return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending) + .Cast(); } /// @@ -480,6 +465,32 @@ namespace MediaBrowser.Controller.Entities.TV }); } + /// + /// Filters the episodes by season. + /// + public static IEnumerable FilterEpisodesBySeason(IEnumerable episodes, int seasonNumber, bool includeSpecials) + { + if (!includeSpecials || seasonNumber < 1) + { + return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber); + } + + return episodes.Where(i => + { + var episode = i; + + if (episode != null) + { + var currentSeasonNumber = episode.AiredSeasonNumber; + + return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber; + } + + return false; + }); + } + + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Series); diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 6128192ea2..9f3acc3fc3 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1100,8 +1100,6 @@ namespace MediaBrowser.Controller.Entities bool? isVirtualUnaired, bool? isUnaired) { - items = FilterVirtualSeasons(items, isMissing, isVirtualUnaired, isUnaired); - if (isMissing.HasValue) { var val = isMissing.Value; @@ -1147,57 +1145,6 @@ namespace MediaBrowser.Controller.Entities return items; } - private static IEnumerable FilterVirtualSeasons( - IEnumerable items, - bool? isMissing, - bool? isVirtualUnaired, - bool? isUnaired) - { - if (isMissing.HasValue) - { - var val = isMissing.Value; - items = items.Where(i => - { - var e = i as Season; - if (e != null) - { - return (e.IsMissingSeason) == val; - } - return true; - }); - } - - if (isUnaired.HasValue) - { - var val = isUnaired.Value; - items = items.Where(i => - { - var e = i as Season; - if (e != null) - { - return e.IsUnaired == val; - } - return true; - }); - } - - if (isVirtualUnaired.HasValue) - { - var val = isVirtualUnaired.Value; - items = items.Where(i => - { - var e = i as Season; - if (e != null) - { - return e.IsVirtualUnaired == val; - } - return true; - }); - } - - return items; - } - public static QueryResult SortAndPage(IEnumerable items, int? totalRecordLimit, InternalItemsQuery query, diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index db896f1fc7..4197ea93e5 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -112,5 +112,48 @@ namespace MediaBrowser.Controller.Entities return false; } } + + public static string GetPath(string name, bool normalizeName = true) + { + // 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.YearPath, 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(); + } + + /// + /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// + public override bool BeforeMetadataRefresh() + { + var hasChanges = base.BeforeMetadataRefresh(); + + var newPath = GetRebasedPath(); + if (!string.Equals(Path, newPath, StringComparison.Ordinal)) + { + Path = newPath; + hasChanges = true; + } + + return hasChanges; + } } } diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs index c07934d0b1..c89a60a6f8 100644 --- a/MediaBrowser.Controller/IServerApplicationPaths.cs +++ b/MediaBrowser.Controller/IServerApplicationPaths.cs @@ -106,5 +106,7 @@ namespace MediaBrowser.Controller /// /// The internal metadata path. string InternalMetadataPath { get; } + + string ArtistsPath { get; } } } \ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index c8a28e832a..e90f6bdc36 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -343,14 +343,16 @@ namespace MediaBrowser.MediaEncoding.Encoder // If that doesn't pan out, then do a recursive search var files = Directory.GetFiles(path); - var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase)); - var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase)); + var excludeExtensions = new[] { ".c" }; + + var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); + var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath)) { files = Directory.GetFiles(path, "*", SearchOption.AllDirectories); - ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase)); + ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty)); if (!string.IsNullOrWhiteSpace(ffmpegPath)) { diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 04437cff79..2a69948b1a 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -42,6 +42,13 @@ namespace MediaBrowser.Providers.Manager var config = ProviderManager.GetMetadataOptions(item); var updateType = ItemUpdateType.None; + var requiresRefresh = false; + + if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) + { + // TODO: If this returns true, should we instead just change metadata refresh mode to Full? + requiresRefresh = item.RequiresRefresh(); + } var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem); var localImagesFailed = false; @@ -70,14 +77,10 @@ namespace MediaBrowser.Providers.Manager bool hasRefreshedMetadata = true; bool hasRefreshedImages = true; - var requiresRefresh = false; // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { - // TODO: If this returns true, should we instead just change metadata refresh mode to Full? - requiresRefresh = item.RequiresRefresh(); - var providers = GetProviders(item, refreshOptions, requiresRefresh) .ToList(); diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 3d257d1947..be68162caf 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -955,20 +955,23 @@ namespace MediaBrowser.Server.Implementations.Dto dto.Genres = item.Genres; } - dto.ImageTags = new Dictionary(); - - // Prevent implicitly captured closure - var currentItem = item; - foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type)) - .ToList()) + if (options.EnableImages) { - if (options.GetImageLimit(image.Type) > 0) - { - var tag = GetImageCacheTag(item, image); + dto.ImageTags = new Dictionary(); - if (tag != null) + // Prevent implicitly captured closure + var currentItem = item; + foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type)) + .ToList()) + { + if (options.GetImageLimit(image.Type) > 0) { - dto.ImageTags[image.Type] = tag; + var tag = GetImageCacheTag(item, image); + + if (tag != null) + { + dto.ImageTags[image.Type] = tag; + } } } } @@ -1527,97 +1530,6 @@ namespace MediaBrowser.Server.Implementations.Dto } } - /// - /// Since it can be slow to make all of these calculations independently, this method will provide a way to do them all at once - /// - /// The folder. - /// The user. - /// The dto. - /// The fields. - /// The synchronize progress. - /// Task. - private async Task SetSpecialCounts(Folder folder, User user, BaseItemDto dto, List fields, Dictionary syncProgress) - { - var recursiveItemCount = 0; - var unplayed = 0; - - double totalPercentPlayed = 0; - double totalSyncPercent = 0; - - var children = await folder.GetItems(new InternalItemsQuery - { - IsFolder = false, - Recursive = true, - ExcludeLocationTypes = new[] { LocationType.Virtual }, - User = user - - }).ConfigureAwait(false); - - // Loop through each recursive child - foreach (var child in children.Items) - { - var userdata = _userDataRepository.GetUserData(user, child); - - recursiveItemCount++; - - var isUnplayed = true; - - // Incrememt totalPercentPlayed - if (userdata != null) - { - if (userdata.Played) - { - totalPercentPlayed += 100; - - isUnplayed = false; - } - else if (userdata.PlaybackPositionTicks > 0 && child.RunTimeTicks.HasValue && child.RunTimeTicks.Value > 0) - { - double itemPercent = userdata.PlaybackPositionTicks; - itemPercent /= child.RunTimeTicks.Value; - totalPercentPlayed += itemPercent; - } - } - - if (isUnplayed) - { - unplayed++; - } - - double percent = 0; - SyncJobItemStatus syncItemProgress; - if (syncProgress.TryGetValue(child.Id.ToString("N"), out syncItemProgress)) - { - switch (syncItemProgress) - { - case SyncJobItemStatus.Synced: - percent = 100; - break; - case SyncJobItemStatus.Converting: - case SyncJobItemStatus.ReadyToTransfer: - case SyncJobItemStatus.Transferring: - percent = 50; - break; - } - } - totalSyncPercent += percent; - } - - dto.RecursiveItemCount = recursiveItemCount; - dto.UserData.UnplayedItemCount = unplayed; - - if (recursiveItemCount > 0) - { - dto.UserData.PlayedPercentage = totalPercentPlayed / recursiveItemCount; - - var pct = totalSyncPercent / recursiveItemCount; - if (pct > 0) - { - dto.SyncPercent = pct; - } - } - } - /// /// Attaches the primary image aspect ratio. /// diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index a19f70e68c..a5e6c1b92d 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -829,7 +829,7 @@ namespace MediaBrowser.Server.Implementations.Library /// Task{Person}. public Person GetPerson(string name) { - return GetItemByName(ConfigurationManager.ApplicationPaths.PeoplePath, name); + return CreateItemByName(Person.GetPath(name), name); } /// @@ -839,7 +839,7 @@ namespace MediaBrowser.Server.Implementations.Library /// Task{Studio}. public Studio GetStudio(string name) { - return GetItemByName(ConfigurationManager.ApplicationPaths.StudioPath, name); + return CreateItemByName(Studio.GetPath(name), name); } /// @@ -849,7 +849,7 @@ namespace MediaBrowser.Server.Implementations.Library /// Task{Genre}. public Genre GetGenre(string name) { - return GetItemByName(ConfigurationManager.ApplicationPaths.GenrePath, name); + return CreateItemByName(Genre.GetPath(name), name); } /// @@ -859,7 +859,7 @@ namespace MediaBrowser.Server.Implementations.Library /// Task{MusicGenre}. public MusicGenre GetMusicGenre(string name) { - return GetItemByName(ConfigurationManager.ApplicationPaths.MusicGenrePath, name); + return CreateItemByName(MusicGenre.GetPath(name), name); } /// @@ -869,14 +869,9 @@ namespace MediaBrowser.Server.Implementations.Library /// Task{GameGenre}. public GameGenre GetGameGenre(string name) { - return GetItemByName(ConfigurationManager.ApplicationPaths.GameGenrePath, name); + return CreateItemByName(GameGenre.GetPath(name), name); } - /// - /// The us culture - /// - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - /// /// Gets a Year /// @@ -890,19 +885,9 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentOutOfRangeException("Years less than or equal to 0 are invalid."); } - return GetItemByName(ConfigurationManager.ApplicationPaths.YearPath, value.ToString(UsCulture)); - } + var name = value.ToString(CultureInfo.InvariantCulture); - /// - /// Gets the artists path. - /// - /// The artists path. - public string ArtistsPath - { - get - { - return Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "artists"); - } + return CreateItemByName(Year.GetPath(name), name); } /// @@ -912,48 +897,7 @@ namespace MediaBrowser.Server.Implementations.Library /// Task{Genre}. public MusicArtist GetArtist(string name) { - return GetItemByName(ArtistsPath, name); - } - - private T GetItemByName(string path, string name) - where T : BaseItem, new() - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException("name"); - } - - // Trim the period at the end because windows will have a hard time with that - var validFilename = _fileSystem.GetValidFilename(name) - .Trim() - .TrimEnd('.'); - - string subFolderPrefix = null; - - var type = typeof(T); - - if (type == typeof(Person)) - { - foreach (char c in validFilename) - { - if (char.IsLetterOrDigit(c)) - { - subFolderPrefix = c.ToString(); - break; - } - } - } - - var fullPath = string.IsNullOrEmpty(subFolderPrefix) ? - Path.Combine(path, validFilename) : - Path.Combine(path, subFolderPrefix, validFilename); - - return CreateItemByName(fullPath, name); + return CreateItemByName(MusicArtist.GetPath(name), name); } private T CreateItemByName(string path, string name) diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs index cf6f070d03..a6d6b5cb8b 100644 --- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs +++ b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs @@ -166,12 +166,12 @@ namespace MediaBrowser.Server.Implementations.Library ExcludeItemTypes = excludeItemTypes.ToArray(), IncludeItemTypes = includeItemTypes.ToArray(), Limit = query.Limit, - IncludeItemsByName = true - + IncludeItemsByName = true, + IsVirtualItem = false }); // Add search hints based on item name - hints.AddRange(mediaItems.Where(IncludeInSearch).Select(item => + hints.AddRange(mediaItems.Select(item => { var index = GetIndex(item.Name, searchTerm, terms); @@ -187,20 +187,6 @@ namespace MediaBrowser.Server.Implementations.Library return Task.FromResult(returnValue); } - private bool IncludeInSearch(BaseItem item) - { - var episode = item as Episode; - - if (episode != null) - { - if (episode.IsMissingEpisode) - { - return false; - } - } - return true; - } - /// /// Gets the index. /// diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index cf8e2fe36c..8006e2b08d 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -3095,6 +3095,17 @@ namespace MediaBrowser.Server.Implementations.Persistence whereClauses.Add("LocationType<>'Virtual'"); } } + if (query.IsSpecialSeason.HasValue) + { + if (query.IsSpecialSeason.Value) + { + whereClauses.Add("IndexNumber = 0"); + } + else + { + whereClauses.Add("IndexNumber <> 0"); + } + } if (query.IsUnaired.HasValue) { if (query.IsUnaired.Value) diff --git a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs index 8a04f29a2c..237d49fdae 100644 --- a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs +++ b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs @@ -88,6 +88,14 @@ namespace MediaBrowser.Server.Implementations } } + public string ArtistsPath + { + get + { + return Path.Combine(ItemsByNamePath, "artists"); + } + } + /// /// Gets the path to the Genre directory /// diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 84aab5e1f8..f495e557a3 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -1001,7 +1001,8 @@ namespace MediaBrowser.Server.Implementations.Session var series = episode.Series; if (series != null) { - var episodes = series.GetEpisodes(user, false, false) + var episodes = series.GetEpisodes(user) + .Where(i => !i.IsVirtualItem) .SkipWhile(i => i.Id != episode.Id) .ToList();