Explorar o código

Merge branch 'master' of github.com:MediaBrowser/MediaBrowser

Luis Miguel Almánzar %!s(int64=11) %!d(string=hai) anos
pai
achega
744867d1c6

+ 1 - 0
CONTRIBUTORS.md

@@ -55,3 +55,4 @@
  - [jordy1955] (https://github.com/jordy1955)
  - [jordy1955] (https://github.com/jordy1955)
  - [JoshFink] (https://github.com/JoshFink)
  - [JoshFink] (https://github.com/JoshFink)
  - [Detector1](https://github.com/Detector1)
  - [Detector1](https://github.com/Detector1)
+ - [BlackIce013](https://github.com/blackice013)

+ 3 - 3
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -146,9 +146,9 @@ namespace MediaBrowser.Api.Playback.Hls
             builder.AppendLine(playlistUrl);
             builder.AppendLine(playlistUrl);
 
 
             // Low bitrate stream
             // Low bitrate stream
-            builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=64000");
-            playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "-low/stream.m3u8");
-            builder.AppendLine(playlistUrl);
+            //builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=64000");
+            //playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "-low/stream.m3u8");
+            //builder.AppendLine(playlistUrl);
 
 
             return builder.ToString();
             return builder.ToString();
         }
         }

+ 9 - 0
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -172,6 +172,9 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string AdjacentTo { get; set; }
         public string AdjacentTo { get; set; }
 
 
+        [ApiMember(Name = "MinIndexNumber", Description = "Optional filter index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? MinIndexNumber { get; set; }
+        
         /// <summary>
         /// <summary>
         /// Gets the order by.
         /// Gets the order by.
         /// </summary>
         /// </summary>
@@ -511,6 +514,12 @@ namespace MediaBrowser.Api.UserLibrary
                 items = items.Where(i => i.Id == previousId || i.Id == nextId);
                 items = items.Where(i => i.Id == previousId || i.Id == nextId);
             }
             }
 
 
+            // Min index number
+            if (request.MinIndexNumber.HasValue)
+            {
+                items = items.Where(i => i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value);
+            }
+
             // Min official rating
             // Min official rating
             if (!string.IsNullOrEmpty(request.MinOfficialRating))
             if (!string.IsNullOrEmpty(request.MinOfficialRating))
             {
             {

+ 34 - 9
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Controller.Session;
@@ -325,7 +326,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// Class GetSpecialFeatures
     /// Class GetSpecialFeatures
     /// </summary>
     /// </summary>
     [Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET")]
     [Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET")]
-    [Api(Description = "Gets special features for a movie")]
+    [Api(Description = "Gets special features for an item")]
     public class GetSpecialFeatures : IReturn<List<BaseItemDto>>
     public class GetSpecialFeatures : IReturn<List<BaseItemDto>>
     {
     {
         /// <summary>
         /// <summary>
@@ -404,16 +405,40 @@ namespace MediaBrowser.Api.UserLibrary
             // Get everything
             // Get everything
             var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList();
             var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList();
 
 
-            var movie = (Movie)item;
-
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo);
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo);
 
 
-            var tasks = movie.SpecialFeatureIds
-                .Select(_itemRepo.RetrieveItem)
-                .OrderBy(i => i.SortName)
-                .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user, movie));
+            var movie = item as Movie;
 
 
-            return Task.WhenAll(tasks);
+            // Get them from the db
+            if (movie != null)
+            {
+                // Avoid implicitly captured closure
+                var movie1 = movie;
+
+                var tasks = movie.SpecialFeatureIds
+                    .Select(_itemRepo.RetrieveItem)
+                    .OrderBy(i => i.SortName)
+                    .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user, movie1));
+
+                return Task.WhenAll(tasks);
+            }
+
+            var series = item as Series;
+
+            // Get them from the child tree
+            if (series != null)
+            {
+                var tasks = series
+                    .RecursiveChildren
+                    .OfType<Episode>()
+                    .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
+                    .OrderBy(i => i.SortName)
+                    .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user));
+
+                return Task.WhenAll(tasks);
+            }
+
+            throw new ArgumentException("The item does not support special features");
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -589,7 +614,7 @@ namespace MediaBrowser.Api.UserLibrary
 
 
             return DtoBuilder.GetUserItemDataDto(data);
             return DtoBuilder.GetUserItemDataDto(data);
         }
         }
-        
+
         /// <summary>
         /// <summary>
         /// Posts the specified request.
         /// Posts the specified request.
         /// </summary>
         /// </summary>

+ 20 - 154
MediaBrowser.Controller/Dto/DtoBuilder.cs

@@ -104,35 +104,11 @@ namespace MediaBrowser.Controller.Dto
 
 
             if (fields.Contains(ItemFields.SoundtrackIds))
             if (fields.Contains(ItemFields.SoundtrackIds))
             {
             {
-                var series = item as Series;
-
-                if (series != null)
-                {
-                    AttachSoundtrackIds(dto, series, user);
-                }
-
-                var movie = item as Movie;
-
-                if (movie != null)
-                {
-                    AttachSoundtrackIds(dto, movie, user);
-                }
-
-                var album = item as MusicAlbum;
-
-                if (album != null)
-                {
-                    AttachSoundtrackIds(dto, album, user);
-                }
-
-                var game = item as Game;
-
-                if (game != null)
-                {
-                    AttachSoundtrackIds(dto, game, user);
-                }
+                dto.SoundtrackIds = item.SoundtrackIds
+                    .Select(i => i.ToString("N"))
+                    .ToArray();
             }
             }
-            
+
             // Make sure all the tasks we kicked off have completed.
             // Make sure all the tasks we kicked off have completed.
             if (tasks.Count > 0)
             if (tasks.Count > 0)
             {
             {
@@ -142,132 +118,6 @@ namespace MediaBrowser.Controller.Dto
             return dto;
             return dto;
         }
         }
 
 
-        private void AttachSoundtrackIds(BaseItemDto dto, Movie item, User user)
-        {
-            var tmdb = item.GetProviderId(MetadataProviders.Tmdb);
-
-            if (string.IsNullOrEmpty(tmdb))
-            {
-                return;
-            }
-
-            var recursiveChildren = user == null
-                                        ? _libraryManager.RootFolder.RecursiveChildren
-                                        : user.RootFolder.GetRecursiveChildren(user);
-
-            dto.SoundtrackIds = recursiveChildren
-                .Where(i =>
-                {
-                    if (!string.IsNullOrEmpty(tmdb) &&
-                        string.Equals(tmdb, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase) &&
-                        i is MusicAlbum)
-                    {
-                        return true;
-                    }
-                    return false;
-                })
-                .Select(GetClientItemId)
-                .ToArray();
-        }
-
-        private void AttachSoundtrackIds(BaseItemDto dto, Series item, User user)
-        {
-            var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
-
-            if (string.IsNullOrEmpty(tvdb))
-            {
-                return;
-            }
-
-            var recursiveChildren = user == null
-                                        ? _libraryManager.RootFolder.RecursiveChildren
-                                        : user.RootFolder.GetRecursiveChildren(user);
-
-            dto.SoundtrackIds = recursiveChildren
-                .Where(i =>
-                {
-                    if (!string.IsNullOrEmpty(tvdb) &&
-                        string.Equals(tvdb, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase) &&
-                        i is MusicAlbum)
-                    {
-                        return true;
-                    }
-                    return false;
-                })
-                .Select(GetClientItemId)
-                .ToArray();
-        }
-
-        private void AttachSoundtrackIds(BaseItemDto dto, Game item, User user)
-        {
-            var gamesdb = item.GetProviderId(MetadataProviders.Gamesdb);
-
-            if (string.IsNullOrEmpty(gamesdb))
-            {
-                return;
-            }
-
-            var recursiveChildren = user == null
-                                        ? _libraryManager.RootFolder.RecursiveChildren
-                                        : user.RootFolder.GetRecursiveChildren(user);
-
-            dto.SoundtrackIds = recursiveChildren
-                .Where(i =>
-                {
-                    if (!string.IsNullOrEmpty(gamesdb) &&
-                        string.Equals(gamesdb, i.GetProviderId(MetadataProviders.Gamesdb), StringComparison.OrdinalIgnoreCase) &&
-                        i is MusicAlbum)
-                    {
-                        return true;
-                    }
-                    return false;
-                })
-                .Select(GetClientItemId)
-                .ToArray();
-        }
-
-        private void AttachSoundtrackIds(BaseItemDto dto, MusicAlbum item, User user)
-        {
-            var tmdb = item.GetProviderId(MetadataProviders.Tmdb);
-            var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
-            var gamesdb = item.GetProviderId(MetadataProviders.Gamesdb);
-
-            if (string.IsNullOrEmpty(tmdb) && string.IsNullOrEmpty(tvdb) && string.IsNullOrEmpty(gamesdb))
-            {
-                return;
-            }
-
-            var recursiveChildren = user == null
-                                        ? _libraryManager.RootFolder.RecursiveChildren
-                                        : user.RootFolder.GetRecursiveChildren(user);
-
-            dto.SoundtrackIds = recursiveChildren
-                .Where(i =>
-                {
-                    if (!string.IsNullOrEmpty(tmdb) && 
-                        string.Equals(tmdb, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase) &&
-                        i is Movie)
-                    {
-                        return true;
-                    }
-                    if (!string.IsNullOrEmpty(tvdb) &&
-                        string.Equals(tvdb, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase) &&
-                        i is Series)
-                    {
-                        return true;
-                    }
-                    if (!string.IsNullOrEmpty(gamesdb) &&
-                        string.Equals(gamesdb, i.GetProviderId(MetadataProviders.Gamesdb), StringComparison.OrdinalIgnoreCase) &&
-                        i is Game)
-                    {
-                        return true;
-                    }
-                    return false;
-                })
-                .Select(GetClientItemId)
-                .ToArray();
-        }
-
         /// <summary>
         /// <summary>
         /// Attaches the user specific info.
         /// Attaches the user specific info.
         /// </summary>
         /// </summary>
@@ -682,6 +532,10 @@ namespace MediaBrowser.Controller.Dto
                 dto.AirDays = series.AirDays;
                 dto.AirDays = series.AirDays;
                 dto.AirTime = series.AirTime;
                 dto.AirTime = series.AirTime;
                 dto.Status = series.Status;
                 dto.Status = series.Status;
+
+                dto.SpecialFeatureCount = series.SpecialFeatureIds.Count;
+
+                dto.SeasonCount = series.SeasonCount;
             }
             }
 
 
             if (episode != null)
             if (episode != null)
@@ -716,6 +570,18 @@ namespace MediaBrowser.Controller.Dto
             {
             {
                 SetMusicVideoProperties(dto, musicVideo);
                 SetMusicVideoProperties(dto, musicVideo);
             }
             }
+
+            var book = item as Book;
+
+            if (book != null)
+            {
+                SetBookProperties(dto, book);
+            }
+        }
+
+        private void SetBookProperties(BaseItemDto dto, Book item)
+        {
+            dto.SeriesName = item.SeriesName;
         }
         }
 
 
         private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item)
         private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item)

+ 6 - 0
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -23,6 +23,11 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     /// </summary>
     public abstract class BaseItem : IHasProviderIds
     public abstract class BaseItem : IHasProviderIds
     {
     {
+        /// <summary>
+        /// MusicAlbums in the library that are the soundtrack for this item
+        /// </summary>
+        public List<Guid> SoundtrackIds { get; set; }
+        
         protected BaseItem()
         protected BaseItem()
         {
         {
             Genres = new List<string>();
             Genres = new List<string>();
@@ -38,6 +43,7 @@ namespace MediaBrowser.Controller.Entities
             Tags = new List<string>();
             Tags = new List<string>();
             ThemeSongIds = new List<Guid>();
             ThemeSongIds = new List<Guid>();
             ThemeVideoIds = new List<Guid>();
             ThemeVideoIds = new List<Guid>();
+            SoundtrackIds = new List<Guid>();
             LocalTrailerIds = new List<Guid>();
             LocalTrailerIds = new List<Guid>();
             LockedFields = new List<MetadataFields>();
             LockedFields = new List<MetadataFields>();
         }
         }

+ 35 - 0
MediaBrowser.Controller/Entities/Book.cs

@@ -0,0 +1,35 @@
+
+namespace MediaBrowser.Controller.Entities
+{
+    public class Book : BaseItem
+    {
+        public override string MediaType
+        {
+            get
+            {
+                return Model.Entities.MediaType.Book;
+            }
+        }
+
+        public string SeriesName { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public override string MetaLocation
+        {
+            get
+            {
+                return System.IO.Path.GetDirectoryName(Path);
+            }
+        }
+
+        protected override bool UseParentPathToCreateResolveArgs
+        {
+            get
+            {
+                return !IsInMixedFolder;
+            }
+        }
+    }
+}

+ 6 - 0
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -13,9 +13,15 @@ namespace MediaBrowser.Controller.Entities.TV
     /// </summary>
     /// </summary>
     public class Series : Folder
     public class Series : Folder
     {
     {
+        public List<Guid> SpecialFeatureIds { get; set; }
+
+        public int SeasonCount { get; set; }
+
         public Series()
         public Series()
         {
         {
             AirDays = new List<DayOfWeek>();
             AirDays = new List<DayOfWeek>();
+
+            SpecialFeatureIds = new List<Guid>();
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -72,6 +72,7 @@
       <Link>Properties\SharedVersion.cs</Link>
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
     </Compile>
     <Compile Include="Entities\AdultVideo.cs" />
     <Compile Include="Entities\AdultVideo.cs" />
+    <Compile Include="Entities\Book.cs" />
     <Compile Include="Notifications\Configuration\IServerConfigurationManager.cs" />
     <Compile Include="Notifications\Configuration\IServerConfigurationManager.cs" />
     <Compile Include="Dto\SessionInfoDtoBuilder.cs" />
     <Compile Include="Dto\SessionInfoDtoBuilder.cs" />
     <Compile Include="Entities\Audio\MusicAlbumDisc.cs" />
     <Compile Include="Entities\Audio\MusicAlbumDisc.cs" />

+ 2 - 0
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -138,6 +138,8 @@ namespace MediaBrowser.Model.Dto
         /// <value>The production year.</value>
         /// <value>The production year.</value>
         public int? ProductionYear { get; set; }
         public int? ProductionYear { get; set; }
 
 
+        public int? SeasonCount { get; set; }
+        
         /// <summary>
         /// <summary>
         /// Gets or sets the players supported by a game.
         /// Gets or sets the players supported by a game.
         /// </summary>
         /// </summary>

+ 2 - 0
MediaBrowser.Model/Querying/ItemQuery.cs

@@ -176,6 +176,8 @@ namespace MediaBrowser.Model.Querying
         /// <value>The max official rating.</value>
         /// <value>The max official rating.</value>
         public string MaxOfficialRating { get; set; }
         public string MaxOfficialRating { get; set; }
 
 
+        public int? MinIndexNumber { get; set; }
+        
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ItemQuery"/> class.
         /// Initializes a new instance of the <see cref="ItemQuery"/> class.
         /// </summary>
         /// </summary>

+ 2 - 0
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -81,6 +81,7 @@
     <Compile Include="Music\LastfmBaseProvider.cs" />
     <Compile Include="Music\LastfmBaseProvider.cs" />
     <Compile Include="Music\LastfmHelper.cs" />
     <Compile Include="Music\LastfmHelper.cs" />
     <Compile Include="Music\MusicVideoXmlParser.cs" />
     <Compile Include="Music\MusicVideoXmlParser.cs" />
+    <Compile Include="Music\SoundtrackPostScanTask.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Savers\AlbumXmlSaver.cs" />
     <Compile Include="Savers\AlbumXmlSaver.cs" />
     <Compile Include="Savers\ArtistXmlSaver.cs" />
     <Compile Include="Savers\ArtistXmlSaver.cs" />
@@ -106,6 +107,7 @@
     <Compile Include="TV\SeasonProviderFromXml.cs" />
     <Compile Include="TV\SeasonProviderFromXml.cs" />
     <Compile Include="TV\SeriesProviderFromXml.cs" />
     <Compile Include="TV\SeriesProviderFromXml.cs" />
     <Compile Include="TV\SeriesXmlParser.cs" />
     <Compile Include="TV\SeriesXmlParser.cs" />
+    <Compile Include="TV\SeriesPostScanTask.cs" />
     <Compile Include="TV\TvdbPersonImageProvider.cs" />
     <Compile Include="TV\TvdbPersonImageProvider.cs" />
     <Compile Include="TV\TvdbPrescanTask.cs" />
     <Compile Include="TV\TvdbPrescanTask.cs" />
     <Compile Include="TV\TvdbSeriesImageProvider.cs" />
     <Compile Include="TV\TvdbSeriesImageProvider.cs" />

+ 1 - 1
MediaBrowser.Providers/Movies/MovieXmlParser.cs

@@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Movies
             {
             {
                 case "Chapters":
                 case "Chapters":
 
 
-                    _chaptersTask = FetchChaptersFromXmlNode(item, reader.ReadSubtree(), _itemRepo, CancellationToken.None);
+                    //_chaptersTask = FetchChaptersFromXmlNode(item, reader.ReadSubtree(), _itemRepo, CancellationToken.None);
                     break;
                     break;
 
 
                 default:
                 default:

+ 159 - 0
MediaBrowser.Providers/Music/SoundtrackPostScanTask.cs

@@ -0,0 +1,159 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.Music
+{
+    public class SoundtrackPostScanTask : ILibraryPostScanTask
+    {
+        private readonly ILibraryManager _libraryManager;
+
+        public SoundtrackPostScanTask(ILibraryManager libraryManager)
+        {
+            _libraryManager = libraryManager;
+        }
+
+        public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            return Task.Run(() => RunInternal(progress, cancellationToken));
+        }
+
+        private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            var allItems = _libraryManager.RootFolder
+                .RecursiveChildren
+                .ToList();
+
+            var musicAlbums = allItems
+                .OfType<MusicAlbum>()
+                .ToList();
+
+            AttachMovieSoundtracks(allItems, musicAlbums, cancellationToken);
+
+            progress.Report(25);
+
+            AttachTvSoundtracks(allItems, musicAlbums, cancellationToken);
+
+            progress.Report(50);
+
+            AttachGameSoundtracks(allItems, musicAlbums, cancellationToken);
+
+            progress.Report(75);
+
+            AttachAlbumLinks(allItems, musicAlbums, cancellationToken);
+
+            progress.Report(100);
+        }
+
+        private void AttachMovieSoundtracks(IEnumerable<BaseItem> allItems, List<MusicAlbum> allAlbums, CancellationToken cancellationToken)
+        {
+            foreach (var movie in allItems
+                .Where(i => (i is Movie) || (i is Trailer)))
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var tmdbId = movie.GetProviderId(MetadataProviders.Tmdb);
+
+                if (string.IsNullOrEmpty(tmdbId))
+                {
+                    movie.SoundtrackIds = new List<Guid>();
+                    continue;
+                }
+
+                movie.SoundtrackIds = allAlbums
+                .Where(i => string.Equals(tmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase))
+                .Select(i => i.Id)
+                .ToList();
+            }
+        }
+
+        private void AttachTvSoundtracks(IEnumerable<BaseItem> allItems, List<MusicAlbum> allAlbums, CancellationToken cancellationToken)
+        {
+            foreach (var series in allItems.OfType<Series>())
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var tvdbId = series.GetProviderId(MetadataProviders.Tvdb);
+
+                if (string.IsNullOrEmpty(tvdbId))
+                {
+                    series.SoundtrackIds = new List<Guid>();
+                    continue;
+                }
+
+                series.SoundtrackIds = allAlbums
+                .Where(i => string.Equals(tvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase))
+                .Select(i => i.Id)
+                .ToList();
+            }
+        }
+
+        private void AttachGameSoundtracks(IEnumerable<BaseItem> allItems, List<MusicAlbum> allAlbums, CancellationToken cancellationToken)
+        {
+            foreach (var game in allItems.OfType<Game>())
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var gamesdb = game.GetProviderId(MetadataProviders.Gamesdb);
+
+                if (string.IsNullOrEmpty(gamesdb))
+                {
+                    game.SoundtrackIds = new List<Guid>();
+                    continue;
+                }
+
+                game.SoundtrackIds = allAlbums
+                .Where(i => string.Equals(gamesdb, i.GetProviderId(MetadataProviders.Gamesdb), StringComparison.OrdinalIgnoreCase))
+                .Select(i => i.Id)
+                .ToList();
+            }
+        }
+
+        private void AttachAlbumLinks(List<BaseItem> allItems, IEnumerable<MusicAlbum> allAlbums, CancellationToken cancellationToken)
+        {
+            foreach (var album in allAlbums)
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var tmdb = album.GetProviderId(MetadataProviders.Tmdb);
+                var tvdb = album.GetProviderId(MetadataProviders.Tvdb);
+                var gamesdb = album.GetProviderId(MetadataProviders.Gamesdb);
+
+                if (string.IsNullOrEmpty(tmdb) && string.IsNullOrEmpty(tvdb) && string.IsNullOrEmpty(gamesdb))
+                {
+                    album.SoundtrackIds = new List<Guid>();
+                    continue;
+                }
+
+                album.SoundtrackIds = allItems.
+                Where(i =>
+                {
+                    if (!string.IsNullOrEmpty(tmdb) && string.Equals(tmdb, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase) && i is Movie)
+                    {
+                        return true;
+                    }
+                    if (!string.IsNullOrEmpty(tmdb) && string.Equals(tmdb, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase) && i is Trailer)
+                    {
+                        return true;
+                    }
+                    if (!string.IsNullOrEmpty(tvdb) && string.Equals(tvdb, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase) && i is Series)
+                    {
+                        return true;
+                    }
+
+                    return !string.IsNullOrEmpty(gamesdb) && string.Equals(gamesdb, i.GetProviderId(MetadataProviders.Gamesdb), StringComparison.OrdinalIgnoreCase) && i is Game;
+                })
+                    .Select(i => i.Id)
+                    .ToList();
+            }
+        }
+    }
+}

+ 1 - 2
MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs

@@ -81,8 +81,7 @@ namespace MediaBrowser.Providers.Savers
             }
             }
 
 
             XmlSaverHelpers.AddCommonNodes(item, builder);
             XmlSaverHelpers.AddCommonNodes(item, builder);
-            XmlSaverHelpers.AddMediaInfo(episode, builder);
-            XmlSaverHelpers.AddChapters((Video)item, builder, _itemRepository);
+            XmlSaverHelpers.AddMediaInfo(episode, builder, _itemRepository);
 
 
             builder.Append("</Item>");
             builder.Append("</Item>");
 
 

+ 1 - 3
MediaBrowser.Providers/Savers/MovieXmlSaver.cs

@@ -97,9 +97,7 @@ namespace MediaBrowser.Providers.Savers
 
 
             var video = (Video)item;
             var video = (Video)item;
 
 
-            XmlSaverHelpers.AddMediaInfo(video, builder);
-
-            XmlSaverHelpers.AddChapters(video, builder, _itemRepository);
+            XmlSaverHelpers.AddMediaInfo(video, builder, _itemRepository);
 
 
             builder.Append("</Title>");
             builder.Append("</Title>");
 
 

+ 8 - 3
MediaBrowser.Providers/Savers/XmlSaverHelpers.cs

@@ -451,9 +451,11 @@ namespace MediaBrowser.Providers.Savers
         /// <typeparam name="T"></typeparam>
         /// <typeparam name="T"></typeparam>
         /// <param name="item">The item.</param>
         /// <param name="item">The item.</param>
         /// <param name="builder">The builder.</param>
         /// <param name="builder">The builder.</param>
-        public static void AddMediaInfo<T>(T item, StringBuilder builder)
+        public static void AddMediaInfo<T>(T item, StringBuilder builder, IItemRepository itemRepository)
             where T : BaseItem, IHasMediaStreams
             where T : BaseItem, IHasMediaStreams
         {
         {
+            var video = item as Video;
+
             builder.Append("<MediaInfo>");
             builder.Append("<MediaInfo>");
 
 
             foreach (var stream in item.MediaStreams)
             foreach (var stream in item.MediaStreams)
@@ -526,8 +528,6 @@ namespace MediaBrowser.Providers.Savers
                         builder.Append("<DurationSeconds>" + Convert.ToInt32(timespan.TotalSeconds).ToString(UsCulture) + "</DurationSeconds>");
                         builder.Append("<DurationSeconds>" + Convert.ToInt32(timespan.TotalSeconds).ToString(UsCulture) + "</DurationSeconds>");
                     }
                     }
 
 
-                    var video = item as Video;
-
                     if (video != null && video.Video3DFormat.HasValue)
                     if (video != null && video.Video3DFormat.HasValue)
                     {
                     {
                         switch (video.Video3DFormat.Value)
                         switch (video.Video3DFormat.Value)
@@ -552,6 +552,11 @@ namespace MediaBrowser.Providers.Savers
             }
             }
 
 
             builder.Append("</MediaInfo>");
             builder.Append("</MediaInfo>");
+
+            if (video != null)
+            {
+                AddChapters(video, builder, itemRepository);
+            }
         }
         }
     }
     }
 }
 }

+ 1 - 1
MediaBrowser.Providers/TV/EpisodeXmlParser.cs

@@ -49,7 +49,7 @@ namespace MediaBrowser.Providers.TV
             {
             {
                 case "Chapters":
                 case "Chapters":
 
 
-                    _chaptersTask = FetchChaptersFromXmlNode(item, reader.ReadSubtree(), _itemRepo, CancellationToken.None);
+                    //_chaptersTask = FetchChaptersFromXmlNode(item, reader.ReadSubtree(), _itemRepo, CancellationToken.None);
                     break;
                     break;
 
 
                 case "Episode":
                 case "Episode":

+ 64 - 0
MediaBrowser.Providers/TV/SeriesPostScanTask.cs

@@ -0,0 +1,64 @@
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.TV
+{
+    class SeriesPostScanTask : ILibraryPostScanTask
+    {
+        /// <summary>
+        /// The _library manager
+        /// </summary>
+        private readonly ILibraryManager _libraryManager;
+
+        public SeriesPostScanTask(ILibraryManager libraryManager)
+        {
+            _libraryManager = libraryManager;
+        }
+
+        public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            return Task.Run(() => RunInternal(progress, cancellationToken));
+        }
+
+        private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            var seriesList = _libraryManager.RootFolder
+                .RecursiveChildren
+                .OfType<Series>()
+                .ToList();
+
+            var numComplete = 0;
+
+            foreach (var series in seriesList)
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var episodes = series.RecursiveChildren
+                    .OfType<Episode>()
+                    .ToList();
+
+                series.SpecialFeatureIds = episodes
+                    .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
+                    .Select(i => i.Id)
+                    .ToList();
+
+                series.SeasonCount = episodes
+                    .Select(i => i.ParentIndexNumber ?? 0)
+                    .Where(i => i != 0)
+                    .Distinct()
+                    .Count();
+
+                numComplete++;
+                double percent = numComplete;
+                percent /= seriesList.Count;
+                percent *= 100;
+
+                progress.Report(percent);
+            }
+        }
+    }
+}

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.188</version>
+        <version>3.0.189</version>
         <title>MediaBrowser.Common.Internal</title>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.188" />
+            <dependency id="MediaBrowser.Common" version="3.0.189" />
             <dependency id="NLog" version="2.0.1.2" />
             <dependency id="NLog" version="2.0.1.2" />
             <dependency id="ServiceStack.Text" version="3.9.55" />
             <dependency id="ServiceStack.Text" version="3.9.55" />
             <dependency id="SimpleInjector" version="2.3.0" />
             <dependency id="SimpleInjector" version="2.3.0" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Common</id>
         <id>MediaBrowser.Common</id>
-        <version>3.0.188</version>
+        <version>3.0.189</version>
         <title>MediaBrowser.Common</title>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Server.Core</id>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.188</version>
+        <version>3.0.189</version>
         <title>Media Browser.Server.Core</title>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.188" />
+            <dependency id="MediaBrowser.Common" version="3.0.189" />
         </dependencies>
         </dependencies>
     </metadata>
     </metadata>
     <files>
     <files>