Browse Source

update season queries

Luke Pulverenti 8 years ago
parent
commit
cc62faa1c2

+ 6 - 32
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<Season>();
-            }
+            }).ConfigureAwait(false)).Items.OfType<Season>();
 
             var dtoOptions = GetDtoOptions(request);
 
@@ -453,23 +444,6 @@ namespace MediaBrowser.Api
             };
         }
 
-        private IEnumerable<Season> FilterVirtualSeasons(GetSeasons request, IEnumerable<Season> 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<object> Get(GetEpisodes request)
         {
             var user = _userManager.GetUserById(request.UserId);

+ 49 - 0
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();
+        }
+
+        /// <summary>
+        /// This is called before any metadata refresh and returns true or false indicating if changes were made
+        /// </summary>
+        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;
+        }
     }
 }

+ 43 - 0
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();
+        }
+
+        /// <summary>
+        /// This is called before any metadata refresh and returns true or false indicating if changes were made
+        /// </summary>
+        public override bool BeforeMetadataRefresh()
+        {
+            var hasChanges = base.BeforeMetadataRefresh();
+
+            var newPath = GetRebasedPath();
+            if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+            {
+                Path = newPath;
+                hasChanges = true;
+            }
+
+            return hasChanges;
+        }
     }
 }

+ 43 - 0
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();
+        }
+
+        /// <summary>
+        /// This is called before any metadata refresh and returns true or false indicating if changes were made
+        /// </summary>
+        public override bool BeforeMetadataRefresh()
+        {
+            var hasChanges = base.BeforeMetadataRefresh();
+
+            var newPath = GetRebasedPath();
+            if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+            {
+                Path = newPath;
+                hasChanges = true;
+            }
+
+            return hasChanges;
+        }
     }
 }

+ 43 - 0
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();
+        }
+
+        /// <summary>
+        /// This is called before any metadata refresh and returns true or false indicating if changes were made
+        /// </summary>
+        public override bool BeforeMetadataRefresh()
+        {
+            var hasChanges = base.BeforeMetadataRefresh();
+
+            var newPath = GetRebasedPath();
+            if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+            {
+                Path = newPath;
+                hasChanges = true;
+            }
+
+            return hasChanges;
+        }
     }
 }

+ 2 - 0
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; }
 

+ 58 - 0
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();
+        }
+
+        /// <summary>
+        /// This is called before any metadata refresh and returns true or false indicating if changes were made
+        /// </summary>
+        public override bool BeforeMetadataRefresh()
+        {
+            var hasChanges = base.BeforeMetadataRefresh();
+
+            var newPath = GetRebasedPath();
+            if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+            {
+                Path = newPath;
+                hasChanges = true;
+            }
+
+            return hasChanges;
+        }
     }
 
     /// <summary>

+ 43 - 0
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();
+        }
+
+        /// <summary>
+        /// This is called before any metadata refresh and returns true or false indicating if changes were made
+        /// </summary>
+        public override bool BeforeMetadataRefresh()
+        {
+            var hasChanges = base.BeforeMetadataRefresh();
+
+            var newPath = GetRebasedPath();
+            if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+            {
+                Path = newPath;
+                hasChanges = true;
+            }
+
+            return hasChanges;
+        }
     }
 }

+ 5 - 25
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<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query)
         {
             if (query.User == null)
@@ -189,19 +171,17 @@ namespace MediaBrowser.Controller.Entities.TV
         /// <returns>IEnumerable{Episode}.</returns>
         public IEnumerable<Episode> GetEpisodes(User user)
         {
-            var config = user.Configuration;
-
-            return GetEpisodes(Series, user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
+            return GetEpisodes(Series, user);
         }
 
-        public IEnumerable<Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
+        public IEnumerable<Episode> GetEpisodes(Series series, User user)
         {
-            return GetEpisodes(series, user, includeMissingEpisodes, includeVirtualUnairedEpisodes, null);
+            return GetEpisodes(series, user, null);
         }
 
-        public IEnumerable<Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> allSeriesEpisodes)
+        public IEnumerable<Episode> GetEpisodes(Series series, User user, IEnumerable<Episode> allSeriesEpisodes)
         {
-            return series.GetSeasonEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes, allSeriesEpisodes);
+            return series.GetSeasonEpisodes(user, this, allSeriesEpisodes);
         }
 
         public IEnumerable<Episode> GetEpisodes()

+ 93 - 82
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<Season>();
         }
 
         protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query)
@@ -241,59 +264,39 @@ namespace MediaBrowser.Controller.Entities.TV
             return Task.FromResult(result);
         }
 
-        public IEnumerable<Season> GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired)
+        public IEnumerable<Episode> 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<Season>().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<Episode> GetEpisodes(User user)
-        {
-            var config = user.Configuration;
-
-            return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
-        }
-
-        public IEnumerable<Episode> 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<Episode>().ToList();
 
             var allEpisodes = allItems.OfType<Season>()
-                .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<Episode> GetSeasonEpisodes(User user, Season season)
-        {
-            var config = user.Configuration;
-
-            return GetSeasonEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
-        }
-
         private IEnumerable<Episode> GetAllEpisodes(User user)
         {
             Logger.Debug("Series.GetAllEpisodes entering GetItemList");
@@ -394,62 +390,51 @@ namespace MediaBrowser.Controller.Entities.TV
             return result;
         }
 
-        public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
+        public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason)
         {
-            IEnumerable<Episode> episodes = GetAllEpisodes(user);
-
-            return GetSeasonEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
-        }
+            var seriesKey = GetUniqueSeriesKey(this);
+            Logger.Debug("GetSeasonEpisodes seriesKey: {0}", seriesKey);
 
-        public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> 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<Episode>();
 
-            return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending)
-                .Cast<Episode>();
+            return GetSeasonEpisodes(user, parentSeason, allItems);
         }
 
-        /// <summary>
-        /// Filters the episodes by season.
-        /// </summary>
-        public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials)
+        public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason, IEnumerable<Episode> 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<Episode>();
         }
 
         /// <summary>
@@ -480,6 +465,32 @@ namespace MediaBrowser.Controller.Entities.TV
             });
         }
 
+        /// <summary>
+        /// Filters the episodes by season.
+        /// </summary>
+        public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> 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);

+ 0 - 53
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<BaseItem> FilterVirtualSeasons(
-            IEnumerable<BaseItem> 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<BaseItem> SortAndPage(IEnumerable<BaseItem> items,
             int? totalRecordLimit,
             InternalItemsQuery query,

+ 43 - 0
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();
+        }
+
+        /// <summary>
+        /// This is called before any metadata refresh and returns true or false indicating if changes were made
+        /// </summary>
+        public override bool BeforeMetadataRefresh()
+        {
+            var hasChanges = base.BeforeMetadataRefresh();
+
+            var newPath = GetRebasedPath();
+            if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+            {
+                Path = newPath;
+                hasChanges = true;
+            }
+
+            return hasChanges;
+        }
     }
 }

+ 2 - 0
MediaBrowser.Controller/IServerApplicationPaths.cs

@@ -106,5 +106,7 @@ namespace MediaBrowser.Controller
         /// </summary>
         /// <value>The internal metadata path.</value>
         string InternalMetadataPath { get; }
+
+        string ArtistsPath { get; }
     }
 }

+ 5 - 3
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))
                 {

+ 7 - 4
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();
 

+ 14 - 102
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -955,20 +955,23 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.Genres = item.Genres;
             }
 
-            dto.ImageTags = new Dictionary<ImageType, string>();
-
-            // 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<ImageType, string>();
 
-                    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
             }
         }
 
-        /// <summary>
-        /// Since it can be slow to make all of these calculations independently, this method will provide a way to do them all at once
-        /// </summary>
-        /// <param name="folder">The folder.</param>
-        /// <param name="user">The user.</param>
-        /// <param name="dto">The dto.</param>
-        /// <param name="fields">The fields.</param>
-        /// <param name="syncProgress">The synchronize progress.</param>
-        /// <returns>Task.</returns>
-        private async Task SetSpecialCounts(Folder folder, User user, BaseItemDto dto, List<ItemFields> fields, Dictionary<string, SyncJobItemStatus> 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;
-                }
-            }
-        }
-
         /// <summary>
         /// Attaches the primary image aspect ratio.
         /// </summary>

+ 8 - 64
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -829,7 +829,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <returns>Task{Person}.</returns>
         public Person GetPerson(string name)
         {
-            return GetItemByName<Person>(ConfigurationManager.ApplicationPaths.PeoplePath, name);
+            return CreateItemByName<Person>(Person.GetPath(name), name);
         }
 
         /// <summary>
@@ -839,7 +839,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <returns>Task{Studio}.</returns>
         public Studio GetStudio(string name)
         {
-            return GetItemByName<Studio>(ConfigurationManager.ApplicationPaths.StudioPath, name);
+            return CreateItemByName<Studio>(Studio.GetPath(name), name);
         }
 
         /// <summary>
@@ -849,7 +849,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <returns>Task{Genre}.</returns>
         public Genre GetGenre(string name)
         {
-            return GetItemByName<Genre>(ConfigurationManager.ApplicationPaths.GenrePath, name);
+            return CreateItemByName<Genre>(Genre.GetPath(name), name);
         }
 
         /// <summary>
@@ -859,7 +859,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <returns>Task{MusicGenre}.</returns>
         public MusicGenre GetMusicGenre(string name)
         {
-            return GetItemByName<MusicGenre>(ConfigurationManager.ApplicationPaths.MusicGenrePath, name);
+            return CreateItemByName<MusicGenre>(MusicGenre.GetPath(name), name);
         }
 
         /// <summary>
@@ -869,14 +869,9 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <returns>Task{GameGenre}.</returns>
         public GameGenre GetGameGenre(string name)
         {
-            return GetItemByName<GameGenre>(ConfigurationManager.ApplicationPaths.GameGenrePath, name);
+            return CreateItemByName<GameGenre>(GameGenre.GetPath(name), name);
         }
 
-        /// <summary>
-        /// The us culture
-        /// </summary>
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
         /// <summary>
         /// Gets a Year
         /// </summary>
@@ -890,19 +885,9 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentOutOfRangeException("Years less than or equal to 0 are invalid.");
             }
 
-            return GetItemByName<Year>(ConfigurationManager.ApplicationPaths.YearPath, value.ToString(UsCulture));
-        }
+            var name = value.ToString(CultureInfo.InvariantCulture);
 
-        /// <summary>
-        /// Gets the artists path.
-        /// </summary>
-        /// <value>The artists path.</value>
-        public string ArtistsPath
-        {
-            get
-            {
-                return Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "artists");
-            }
+            return CreateItemByName<Year>(Year.GetPath(name), name);
         }
 
         /// <summary>
@@ -912,48 +897,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <returns>Task{Genre}.</returns>
         public MusicArtist GetArtist(string name)
         {
-            return GetItemByName<MusicArtist>(ArtistsPath, name);
-        }
-
-        private T GetItemByName<T>(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<T>(fullPath, name);
+            return CreateItemByName<MusicArtist>(MusicArtist.GetPath(name), name);
         }
 
         private T CreateItemByName<T>(string path, string name)

+ 3 - 17
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;
-        }
-
         /// <summary>
         /// Gets the index.
         /// </summary>

+ 11 - 0
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)

+ 8 - 0
MediaBrowser.Server.Implementations/ServerApplicationPaths.cs

@@ -88,6 +88,14 @@ namespace MediaBrowser.Server.Implementations
             }
         }
 
+        public string ArtistsPath
+        {
+            get
+            {
+                return Path.Combine(ItemsByNamePath, "artists");
+            }
+        }
+
         /// <summary>
         /// Gets the path to the Genre directory
         /// </summary>

+ 2 - 1
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();