Browse Source

Merge pull request #1809 from MediaBrowser/dev

Dev
Luke 9 years ago
parent
commit
71dc1d3395

+ 2 - 1
MediaBrowser.Api/StartupWizardService.cs

@@ -113,7 +113,8 @@ namespace MediaBrowser.Api
             config.EnableCustomPathSubFolders = true;
             config.EnableCustomPathSubFolders = true;
             config.EnableStandaloneMusicKeys = true;
             config.EnableStandaloneMusicKeys = true;
             config.EnableCaseSensitiveItemIds = true;
             config.EnableCaseSensitiveItemIds = true;
-            config.SchemaVersion = 87;
+            config.EnableFolderView = true;
+            config.SchemaVersion = 89;
         }
         }
 
 
         public void Post(UpdateStartupConfiguration request)
         public void Post(UpdateStartupConfiguration request)

+ 23 - 69
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -107,9 +107,11 @@ namespace MediaBrowser.Controller.Entities.TV
         {
         {
             get
             get
             {
             {
-                if (EnablePooling())
+                var userdatakeys = GetUserDataKeys();
+
+                if (userdatakeys.Count > 1)
                 {
                 {
-                    return GetUserDataKeys().First();
+                    return userdatakeys[0];
                 }
                 }
                 return base.PresentationUniqueKey;
                 return base.PresentationUniqueKey;
             }
             }
@@ -207,33 +209,13 @@ namespace MediaBrowser.Controller.Entities.TV
         {
         {
             IEnumerable<Season> seasons;
             IEnumerable<Season> seasons;
 
 
-            if (EnablePooling())
+            seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
             {
             {
-                var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
-                {
-                    PresentationUniqueKey = PresentationUniqueKey,
-                    IncludeItemTypes = new[] { typeof(Series).Name }
-                });
+                AncestorWithPresentationUniqueKey = PresentationUniqueKey,
+                IncludeItemTypes = new[] { typeof(Season).Name },
+                SortBy = new[] { ItemSortBy.SortName }
 
 
-                if (seriesIds.Count > 1)
-                {
-                    seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
-                    {
-                        AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
-                        IncludeItemTypes = new[] { typeof(Season).Name },
-                        SortBy = new[] { ItemSortBy.SortName }
-
-                    }).Cast<Season>();
-                }
-                else
-                {
-                    seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>();
-                }
-            }
-            else
-            {
-                seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>();
-            }
+            }).Cast<Season>();
 
 
             if (!includeMissingSeasons)
             if (!includeMissingSeasons)
             {
             {
@@ -256,9 +238,16 @@ namespace MediaBrowser.Controller.Entities.TV
 
 
         public IEnumerable<Episode> GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired)
         public IEnumerable<Episode> GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired)
         {
         {
-            var allSeriesEpisodes = GetAllEpisodes(user).ToList();
+            var allItems = LibraryManager.GetItemList(new InternalItemsQuery(user)
+            {
+                AncestorWithPresentationUniqueKey = PresentationUniqueKey,
+                IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
+                SortBy = new[] { ItemSortBy.SortName }
+            }).ToList();
+
+            var allSeriesEpisodes = allItems.OfType<Episode>().ToList();
 
 
-            var allEpisodes = GetSeasons(user, true, true)
+            var allEpisodes = allItems.OfType<Season>()
                 .SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes))
                 .SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes))
                 .Reverse()
                 .Reverse()
                 .ToList();
                 .ToList();
@@ -343,50 +332,15 @@ namespace MediaBrowser.Controller.Entities.TV
             return GetEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
             return GetEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
         }
         }
 
 
-        private bool EnablePooling()
-        {
-            return false;
-        }
-
         private IEnumerable<Episode> GetAllEpisodes(User user)
         private IEnumerable<Episode> GetAllEpisodes(User user)
         {
         {
-            IEnumerable<Episode> episodes;
-
-            if (EnablePooling())
+            return LibraryManager.GetItemList(new InternalItemsQuery(user)
             {
             {
-                var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
-                {
-                    PresentationUniqueKey = PresentationUniqueKey,
-                    IncludeItemTypes = new[] { typeof(Series).Name }
-                });
-
-                if (seriesIds.Count > 1)
-                {
-                    episodes = LibraryManager.GetItemList(new InternalItemsQuery(user)
-                    {
-                        AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
-                        IncludeItemTypes = new[] { typeof(Episode).Name },
-                        SortBy = new[] { ItemSortBy.SortName }
-
-                    }).Cast<Episode>();
-                }
-                else
-                {
-                    episodes = GetRecursiveChildren(user, new InternalItemsQuery(user)
-                    {
-                        IncludeItemTypes = new[] { typeof(Episode).Name }
-                    }).Cast<Episode>();
-                }
-            }
-            else
-            {
-                episodes = GetRecursiveChildren(user, new InternalItemsQuery(user)
-                {
-                    IncludeItemTypes = new[] { typeof(Episode).Name }
-                }).Cast<Episode>();
-            }
+                AncestorWithPresentationUniqueKey = PresentationUniqueKey,
+                IncludeItemTypes = new[] { typeof(Episode).Name },
+                SortBy = new[] { ItemSortBy.SortName }
 
 
-            return episodes;
+            }).Cast<Episode>();
         }
         }
 
 
         public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
         public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)

+ 1 - 0
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -198,6 +198,7 @@ namespace MediaBrowser.Model.Configuration
         public bool EnableAnonymousUsageReporting { get; set; }
         public bool EnableAnonymousUsageReporting { get; set; }
         public bool EnableStandaloneMusicKeys { get; set; }
         public bool EnableStandaloneMusicKeys { get; set; }
         public bool EnableLocalizedGuids { get; set; }
         public bool EnableLocalizedGuids { get; set; }
+        public bool EnableFolderView { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.

+ 27 - 21
MediaBrowser.Providers/Omdb/OmdbImageProvider.cs

@@ -1,4 +1,6 @@
-using MediaBrowser.Common.Net;
+using CommonIO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
 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.Entities.TV;
@@ -6,7 +8,10 @@ using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Providers;
+using MediaBrowser.Model.Serialization;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.IO;
+using System.Text;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
@@ -15,10 +20,16 @@ namespace MediaBrowser.Providers.Omdb
     public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
     public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
     {
     {
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
+        private readonly IJsonSerializer _jsonSerializer;
+        private readonly IFileSystem _fileSystem;
+        private readonly IServerConfigurationManager _configurationManager;
 
 
-        public OmdbImageProvider(IHttpClient httpClient)
+        public OmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
         {
         {
+            _jsonSerializer = jsonSerializer;
             _httpClient = httpClient;
             _httpClient = httpClient;
+            _fileSystem = fileSystem;
+            _configurationManager = configurationManager;
         }
         }
 
 
         public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
         public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
@@ -29,22 +40,29 @@ namespace MediaBrowser.Providers.Omdb
             };
             };
         }
         }
 
 
-        public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
         {
         {
             var imdbId = item.GetProviderId(MetadataProviders.Imdb);
             var imdbId = item.GetProviderId(MetadataProviders.Imdb);
 
 
             var list = new List<RemoteImageInfo>();
             var list = new List<RemoteImageInfo>();
 
 
+            var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager);
+
             if (!string.IsNullOrWhiteSpace(imdbId))
             if (!string.IsNullOrWhiteSpace(imdbId))
             {
             {
-                list.Add(new RemoteImageInfo
+                OmdbProvider.RootObject rootObject = await provider.GetRootObject(imdbId, cancellationToken).ConfigureAwait(false);
+
+                if (!string.IsNullOrEmpty(rootObject.Poster))
                 {
                 {
-                    ProviderName = Name,
-                    Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId)
-                });
+                    list.Add(new RemoteImageInfo
+                    {
+                        ProviderName = Name,
+                        Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId)
+                    });
+                }
             }
             }
 
 
-            return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
+            return list;
         }
         }
 
 
         public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
         public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
@@ -65,18 +83,6 @@ namespace MediaBrowser.Providers.Omdb
 
 
         public bool Supports(IHasImages item)
         public bool Supports(IHasImages item)
         {
         {
-            // We'll hammer Omdb if we enable this
-            if (item is Person)
-            {
-                return false;
-            }
-
-            // Save the http requests since we know it's not currently supported
-            if (item is Season || item is Episode)
-            {
-                return false;
-            }
-
             // Supports images for tv movies
             // Supports images for tv movies
             var tvProgram = item as LiveTvProgram;
             var tvProgram = item as LiveTvProgram;
             if (tvProgram != null && tvProgram.IsMovie)
             if (tvProgram != null && tvProgram.IsMovie)
@@ -84,7 +90,7 @@ namespace MediaBrowser.Providers.Omdb
                 return true;
                 return true;
             }
             }
 
 
-            return item is Movie || item is Trailer;
+            return item is Movie || item is Trailer || item is Episode;
         }
         }
 
 
         public int Order
         public int Order

+ 10 - 4
MediaBrowser.Providers/Omdb/OmdbItemProvider.cs

@@ -1,4 +1,6 @@
-using MediaBrowser.Common.Net;
+using CommonIO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
 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.Entities.TV;
@@ -26,13 +28,17 @@ namespace MediaBrowser.Providers.Omdb
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
+        private readonly IFileSystem _fileSystem;
+        private readonly IServerConfigurationManager _configurationManager;
 
 
-        public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager)
+        public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
         {
         {
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
             _httpClient = httpClient;
             _httpClient = httpClient;
             _logger = logger;
             _logger = logger;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
+            _fileSystem = fileSystem;
+            _configurationManager = configurationManager;
         }
         }
 
 
         public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
         public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
@@ -220,7 +226,7 @@ namespace MediaBrowser.Providers.Omdb
                 result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
                 result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
                 result.HasMetadata = true;
                 result.HasMetadata = true;
 
 
-                await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+                await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
             return result;
             return result;
@@ -259,7 +265,7 @@ namespace MediaBrowser.Providers.Omdb
                 result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
                 result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
                 result.HasMetadata = true;
                 result.HasMetadata = true;
 
 
-                await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+                await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
             return result;
             return result;

+ 83 - 29
MediaBrowser.Providers/Omdb/OmdbProvider.cs

@@ -1,4 +1,7 @@
-using MediaBrowser.Common.Net;
+using CommonIO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
@@ -17,17 +20,17 @@ namespace MediaBrowser.Providers.Omdb
     {
     {
         internal static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
         internal static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
+        private readonly IFileSystem _fileSystem;
+        private readonly IServerConfigurationManager _configurationManager;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
 
-        public static OmdbProvider Current;
-
-        public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
+        public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
         {
         {
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
             _httpClient = httpClient;
             _httpClient = httpClient;
-
-            Current = this;
+            _fileSystem = fileSystem;
+            _configurationManager = configurationManager;
         }
         }
 
 
         public async Task Fetch(BaseItem item, string imdbId, string language, string country, CancellationToken cancellationToken)
         public async Task Fetch(BaseItem item, string imdbId, string language, string country, CancellationToken cancellationToken)
@@ -37,28 +40,7 @@ namespace MediaBrowser.Providers.Omdb
                 throw new ArgumentNullException("imdbId");
                 throw new ArgumentNullException("imdbId");
             }
             }
 
 
-            var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId;
-
-            var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam);
-
-            using (var stream = await _httpClient.Get(new HttpRequestOptions
-            {
-                Url = url,
-                ResourcePool = ResourcePool,
-                CancellationToken = cancellationToken
-
-            }).ConfigureAwait(false))
-            {
-                string resultString;
-
-                using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
-                {
-                    resultString = reader.ReadToEnd();
-                }
-
-                resultString = resultString.Replace("\"N/A\"", "\"\"");
-
-                var result = _jsonSerializer.DeserializeFromString<RootObject>(resultString);
+            var result = await GetRootObject(imdbId, cancellationToken);
 
 
                 // Only take the name and rating if the user's language is set to english, since Omdb has no localization
                 // Only take the name and rating if the user's language is set to english, since Omdb has no localization
                 if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
@@ -130,7 +112,79 @@ namespace MediaBrowser.Providers.Omdb
                 }
                 }
 
 
                 ParseAdditionalMetadata(item, result);
                 ParseAdditionalMetadata(item, result);
+        }
+
+        internal async Task<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken)
+        {
+            var path = await EnsureItemInfo(imdbId, cancellationToken);
+
+            string resultString;
+
+            using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072))
+            {
+                using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
+                {
+                    resultString = reader.ReadToEnd();
+                    resultString = resultString.Replace("\"N/A\"", "\"\"");
+                }
+            }
+
+            var result = _jsonSerializer.DeserializeFromString<RootObject>(resultString);
+            return result;
+        }
+
+        private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
+        {
+            if (string.IsNullOrWhiteSpace(imdbId))
+            {
+                throw new ArgumentNullException("imdbId");
+            }
+
+            var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId;
+
+            var path = GetDataFilePath(imdbParam);
+
+            var fileInfo = _fileSystem.GetFileSystemInfo(path);
+
+            if (fileInfo.Exists)
+            {
+                // If it's recent or automatic updates are enabled, don't re-download
+                if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
+                {
+                    return path;
+                }
             }
             }
+
+            var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam);
+
+            using (var stream = await _httpClient.Get(new HttpRequestOptions
+            {
+                Url = url,
+                ResourcePool = ResourcePool,
+                CancellationToken = cancellationToken
+
+            }).ConfigureAwait(false))
+            {
+                var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
+                _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+                _jsonSerializer.SerializeToFile(rootObject, path);
+            }
+
+            return path;
+        }
+
+        internal string GetDataFilePath(string imdbId)
+        {
+            if (string.IsNullOrEmpty(imdbId))
+            {
+                throw new ArgumentNullException("imdbId");
+            }
+
+            var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
+
+            var filename = string.Format("{0}.json", imdbId);
+
+            return Path.Combine(dataPath, filename);
         }
         }
 
 
         private void ParseAdditionalMetadata(BaseItem item, RootObject result)
         private void ParseAdditionalMetadata(BaseItem item, RootObject result)
@@ -184,7 +238,7 @@ namespace MediaBrowser.Providers.Omdb
             return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase);
             return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase);
         }
         }
 
 
-        private class RootObject
+        internal class RootObject
         {
         {
             public string Title { get; set; }
             public string Title { get; set; }
             public string Year { get; set; }
             public string Year { get; set; }

+ 10 - 4
MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs

@@ -1,4 +1,6 @@
-using MediaBrowser.Common.Net;
+using CommonIO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
@@ -21,12 +23,16 @@ namespace MediaBrowser.Providers.TV
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
         private readonly OmdbItemProvider _itemProvider;
         private readonly OmdbItemProvider _itemProvider;
+        private readonly IFileSystem _fileSystem;
+        private readonly IServerConfigurationManager _configurationManager;
 
 
-        public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager)
+        public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
         {
         {
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
             _httpClient = httpClient;
             _httpClient = httpClient;
-            _itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager);
+            _fileSystem = fileSystem;
+            _configurationManager = configurationManager;
+            _itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager, fileSystem, configurationManager);
         }
         }
 
 
         public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
         public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
@@ -58,7 +64,7 @@ namespace MediaBrowser.Providers.TV
                 result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
                 result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
                 result.HasMetadata = true;
                 result.HasMetadata = true;
 
 
-                await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+                await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
             return result;
             return result;

+ 16 - 0
MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs

@@ -114,6 +114,22 @@ namespace MediaBrowser.Providers.TV
                 item.CommunityRating = (float)response.vote_average;
                 item.CommunityRating = (float)response.vote_average;
                 item.VoteCount = response.vote_count;
                 item.VoteCount = response.vote_count;
 
 
+                if (response.videos != null && response.videos.results != null)
+                {
+                    foreach (var video in response.videos.results)
+                    {
+                        if (video.type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase) 
+                            || video.type.Equals("clip", System.StringComparison.OrdinalIgnoreCase))
+                        {
+                            if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
+                            {
+                                var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key);
+                                item.AddTrailerUrl(videoUrl, true);
+                            }
+                        }
+                    }
+                }
+
                 result.ResetPeople();
                 result.ResetPeople();
 
 
                 var credits = response.credits;
                 var credits = response.credits;

+ 13 - 1
MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs

@@ -210,7 +210,19 @@ namespace MediaBrowser.Providers.TV
 
 
         public class Videos
         public class Videos
         {
         {
-            public List<object> results { get; set; }
+            public List<Video> results { get; set; }
+        }
+
+        public class Video
+        {
+            public string id { get; set; }
+            public string iso_639_1 { get; set; }
+            public string iso_3166_1 { get; set; }
+            public string key { get; set; }
+            public string name { get; set; }
+            public string site { get; set; }
+            public string size { get; set; }
+            public string type { get; set; }
         }
         }
 
 
         public class RootObject
         public class RootObject

+ 1 - 1
MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs

@@ -58,7 +58,7 @@ namespace MediaBrowser.Providers.TV
 
 
                     result.HasMetadata = true;
                     result.HasMetadata = true;
                     result.Item = new Season();
                     result.Item = new Season();
-                    result.Item.Name = info.Name;
+                    result.Item.Name = seasonInfo.name;
                     result.Item.IndexNumber = seasonNumber;
                     result.Item.IndexNumber = seasonNumber;
 
 
                     result.Item.Overview = seasonInfo.overview;
                     result.Item.Overview = seasonInfo.overview;

+ 30 - 3
MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs

@@ -168,7 +168,7 @@ namespace MediaBrowser.Providers.TV
             {
             {
                 cancellationToken.ThrowIfCancellationRequested();
                 cancellationToken.ThrowIfCancellationRequested();
 
 
-                result.Item = await FetchMovieData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+                result.Item = await FetchSeriesData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
 
 
                 result.HasMetadata = result.Item != null;
                 result.HasMetadata = result.Item != null;
             }
             }
@@ -176,7 +176,7 @@ namespace MediaBrowser.Providers.TV
             return result;
             return result;
         }
         }
 
 
-        private async Task<Series> FetchMovieData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
+        private async Task<Series> FetchSeriesData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
         {
         {
             string dataFilePath = null;
             string dataFilePath = null;
             RootObject seriesInfo = null;
             RootObject seriesInfo = null;
@@ -285,6 +285,21 @@ namespace MediaBrowser.Providers.TV
                 series.OfficialRating = minimumRelease.rating;
                 series.OfficialRating = minimumRelease.rating;
             }
             }
 
 
+            if (seriesInfo.videos != null && seriesInfo.videos.results != null)
+            {
+                foreach (var video in seriesInfo.videos.results)
+                {
+                    if (video.type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase)
+                        || video.type.Equals("clip", System.StringComparison.OrdinalIgnoreCase))
+                    {
+                        if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
+                        {
+                            var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key);
+                            series.AddTrailerUrl(videoUrl, true);
+                        }
+                    }
+                }
+            }
         }
         }
 
 
         internal static string GetSeriesDataPath(IApplicationPaths appPaths, string tmdbId)
         internal static string GetSeriesDataPath(IApplicationPaths appPaths, string tmdbId)
@@ -555,7 +570,19 @@ namespace MediaBrowser.Providers.TV
 
 
         public class Videos
         public class Videos
         {
         {
-            public List<object> results { get; set; }
+            public List<Video> results { get; set; }
+        }
+
+        public class Video
+        {
+            public string id { get; set; }
+            public string iso_639_1 { get; set; }
+            public string iso_3166_1 { get; set; }
+            public string key { get; set; }
+            public string name { get; set; }
+            public string site { get; set; }
+            public string size { get; set; }
+            public string type { get; set; }
         }
         }
 
 
         public class ContentRating
         public class ContentRating

+ 6 - 0
MediaBrowser.Server.Implementations/Library/UserViewManager.cs

@@ -105,6 +105,12 @@ namespace MediaBrowser.Server.Implementations.Library
                 }
                 }
             }
             }
 
 
+            if (_config.Configuration.EnableFolderView)
+            {
+                var name = _localizationManager.GetLocalizedString("ViewType" + CollectionType.Folders);
+                list.Add(await _libraryManager.GetNamedView(name, CollectionType.Folders, string.Empty, cancellationToken).ConfigureAwait(false));
+            }
+
             if (query.IncludeExternalContent)
             if (query.IncludeExternalContent)
             {
             {
                 var channelResult = await _channelManager.GetChannelsInternal(new ChannelQuery
                 var channelResult = await _channelManager.GetChannelsInternal(new ChannelQuery

+ 2 - 1
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -94,7 +94,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         private IDbCommand _updateInheritedRatingCommand;
         private IDbCommand _updateInheritedRatingCommand;
         private IDbCommand _updateInheritedTagsCommand;
         private IDbCommand _updateInheritedTagsCommand;
 
 
-        public const int LatestSchemaVersion = 87;
+        public const int LatestSchemaVersion = 89;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
@@ -137,6 +137,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
                                 "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID, Path TEXT)",
                                 "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID, Path TEXT)",
                                 "create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
                                 "create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
                                 "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
                                 "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
+                                "create index if not exists idx_TypedBaseItems2 on TypedBaseItems(Type,Guid)",
 
 
                                 "create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
                                 "create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
                                 "create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
                                 "create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",

+ 4 - 12
MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs

@@ -125,7 +125,7 @@ namespace MediaBrowser.Server.Implementations.TV
         private Tuple<Episode, DateTime, bool> GetNextUp(Series series, User user)
         private Tuple<Episode, DateTime, bool> GetNextUp(Series series, User user)
         {
         {
             // Get them in display order, then reverse
             // Get them in display order, then reverse
-            var allEpisodes = series.GetEpisodes(user, true, true)
+            var allEpisodes = series.GetEpisodes(user, false, false)
                 .Where(i => !i.ParentIndexNumber.HasValue || i.ParentIndexNumber.Value != 0)
                 .Where(i => !i.ParentIndexNumber.HasValue || i.ParentIndexNumber.Value != 0)
                 .Reverse()
                 .Reverse()
                 .ToList();
                 .ToList();
@@ -134,8 +134,6 @@ namespace MediaBrowser.Server.Implementations.TV
             var lastWatchedDate = DateTime.MinValue;
             var lastWatchedDate = DateTime.MinValue;
             Episode nextUp = null;
             Episode nextUp = null;
 
 
-            var includeMissing = user.Configuration.DisplayMissingEpisodes;
-
             var unplayedEpisodes = new List<Episode>();
             var unplayedEpisodes = new List<Episode>();
 
 
             // Go back starting with the most recent episodes
             // Go back starting with the most recent episodes
@@ -157,10 +155,7 @@ namespace MediaBrowser.Server.Implementations.TV
                 {
                 {
                     unplayedEpisodes.Add(episode);
                     unplayedEpisodes.Add(episode);
 
 
-                    if (!episode.IsVirtualUnaired && (includeMissing || !episode.IsMissingEpisode))
-                    {
-                        nextUp = episode;
-                    }
+                    nextUp = episode;
                 }
                 }
             }
             }
 
 
@@ -175,11 +170,8 @@ namespace MediaBrowser.Server.Implementations.TV
             {
             {
                 var unplayedEpisode = unplayedEpisodes[i];
                 var unplayedEpisode = unplayedEpisodes[i];
 
 
-                if (!unplayedEpisode.IsVirtualUnaired && (includeMissing || !unplayedEpisode.IsMissingEpisode))
-                {
-                    firstEpisode = unplayedEpisode;
-                    break;
-                }
+                firstEpisode = unplayedEpisode;
+                break;
             }
             }
 
 
             // Return the first episode
             // Return the first episode

+ 3 - 2
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -380,7 +380,8 @@ namespace MediaBrowser.Server.Startup.Common
             {
             {
                 new OmdbEpisodeProviderMigration(ServerConfigurationManager),
                 new OmdbEpisodeProviderMigration(ServerConfigurationManager),
                 new MovieDbEpisodeProviderMigration(ServerConfigurationManager),
                 new MovieDbEpisodeProviderMigration(ServerConfigurationManager),
-                new DbMigration(ServerConfigurationManager, TaskManager)
+                new DbMigration(ServerConfigurationManager, TaskManager),
+                new FolderViewSettingMigration(ServerConfigurationManager, UserManager)
             };
             };
 
 
             foreach (var task in migrations)
             foreach (var task in migrations)
@@ -568,7 +569,7 @@ namespace MediaBrowser.Server.Startup.Common
 
 
             SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager);
             SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager);
             RegisterSingleInstance(SubtitleEncoder);
             RegisterSingleInstance(SubtitleEncoder);
-            
+
             await displayPreferencesRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
             await displayPreferencesRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
             await ConfigureUserDataRepositories().ConfigureAwait(false);
             await ConfigureUserDataRepositories().ConfigureAwait(false);
             await itemRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
             await itemRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);

+ 1 - 0
MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj

@@ -71,6 +71,7 @@
     <Compile Include="FFMpeg\FFmpegValidator.cs" />
     <Compile Include="FFMpeg\FFmpegValidator.cs" />
     <Compile Include="INativeApp.cs" />
     <Compile Include="INativeApp.cs" />
     <Compile Include="MbLinkShortcutHandler.cs" />
     <Compile Include="MbLinkShortcutHandler.cs" />
+    <Compile Include="Migrations\FolderViewSettingMigration.cs" />
     <Compile Include="Migrations\IVersionMigration.cs" />
     <Compile Include="Migrations\IVersionMigration.cs" />
     <Compile Include="Migrations\DbMigration.cs" />
     <Compile Include="Migrations\DbMigration.cs" />
     <Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />
     <Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />

+ 37 - 0
MediaBrowser.Server.Startup.Common/Migrations/FolderViewSettingMigration.cs

@@ -0,0 +1,37 @@
+using System.Linq;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Library;
+
+namespace MediaBrowser.Server.Startup.Common.Migrations
+{
+    public class FolderViewSettingMigration : IVersionMigration
+    {
+        private readonly IServerConfigurationManager _config;
+        private readonly IUserManager _userManager;
+
+        public FolderViewSettingMigration(IServerConfigurationManager config, IUserManager userManager)
+        {
+            _config = config;
+            _userManager = userManager;
+        }
+
+        public void Run()
+        {
+            var migrationKey = this.GetType().Name;
+            var migrationKeyList = _config.Configuration.Migrations.ToList();
+
+            if (!migrationKeyList.Contains(migrationKey))
+            {
+                if (_config.Configuration.IsStartupWizardCompleted)
+                {
+                    _config.Configuration.EnableFolderView = _userManager.Users.Any(i => i.Configuration.DisplayFoldersView);
+                }
+
+                migrationKeyList.Add(migrationKey);
+                _config.Configuration.Migrations = migrationKeyList.ToArray();
+                _config.SaveConfiguration();
+            }
+
+        }
+    }
+}

+ 6 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -272,6 +272,9 @@
     <Content Include="dashboard-ui\legacy\selectmenu.js">
     <Content Include="dashboard-ui\legacy\selectmenu.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
+    <Content Include="dashboard-ui\librarydisplay.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\livetvguideprovider.html">
     <Content Include="dashboard-ui\livetvguideprovider.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
@@ -308,6 +311,9 @@
     <Content Include="dashboard-ui\scripts\homeupcoming.js">
     <Content Include="dashboard-ui\scripts\homeupcoming.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
+    <Content Include="dashboard-ui\scripts\librarydisplay.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\livetvguideprovider.js">
     <Content Include="dashboard-ui\scripts\livetvguideprovider.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>