From b487794860d9d59d6b7f09594d248f58320a70b9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 25 Oct 2013 16:58:31 -0400 Subject: [PATCH 01/25] fixed virtual episode image saving --- MediaBrowser.Controller/Entities/BaseItem.cs | 3 ++- .../TV/SeriesPostScanTask.cs | 2 +- .../Providers/ImageSaver.cs | 24 ++++++------------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 77cc541675..67221a9ac9 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1530,7 +1530,8 @@ namespace MediaBrowser.Controller.Entities } // Refresh metadata - return RefreshMetadata(CancellationToken.None, forceSave: true); + // Need to disable slow providers or the image might get re-downloaded + return RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false); } /// diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index 2e9910aebd..6e99edb126 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -211,7 +211,7 @@ namespace MediaBrowser.Providers.TV else if (airDate.Value > now) { // tvdb has a lot of nearly blank episodes - _logger.Info("Creating virtual future episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2); + _logger.Info("Creating virtual unaired episode {0} {1}x{2}", series.Name, tuple.Item1, tuple.Item2); await AddEpisode(series, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs index a967c535e8..3bdef9226e 100644 --- a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs +++ b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs @@ -67,30 +67,20 @@ namespace MediaBrowser.Server.Implementations.Providers throw new ArgumentNullException("mimeType"); } - var saveLocally = _config.Configuration.SaveLocalMeta; + var saveLocally = _config.Configuration.SaveLocalMeta || item is IItemByName || item is User; - if (item is IItemByName) - { - saveLocally = true; - } - else if (item is User) - { - saveLocally = true; - } - else if (item is Audio || item.Parent == null || string.IsNullOrEmpty(item.MetaLocation)) + if (item is Audio || item.Parent == null) { saveLocally = false; } - if (type != ImageType.Primary) + if (type != ImageType.Primary && item is Episode) { - if (item is Episode) - { - saveLocally = false; - } + saveLocally = false; } - if (item.LocationType == LocationType.Remote || item.LocationType == LocationType.Virtual) + var locationType = item.LocationType; + if (locationType == LocationType.Remote || locationType == LocationType.Virtual) { saveLocally = false; } @@ -373,7 +363,7 @@ namespace MediaBrowser.Server.Implementations.Providers path = GetSavePathForItemInMixedFolder(item, type, filename, extension); } - if (string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(item.MetaLocation)) + if (string.IsNullOrEmpty(path)) { path = Path.Combine(item.MetaLocation, filename + extension); } From a4a3800de56f8cb8185e95a9c343b74db0bbdcea Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 26 Oct 2013 00:12:20 -0400 Subject: [PATCH 02/25] fix tvdb providers grabbing last poster --- .../TV/RemoteSeasonProvider.cs | 12 ++++++++++-- .../TV/TvdbSeriesImageProvider.cs | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs index c0ff760549..a5408c8dee 100644 --- a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs @@ -325,11 +325,19 @@ namespace MediaBrowser.Providers.TV { if (string.Equals(bannerType2, "season", StringComparison.OrdinalIgnoreCase)) { - data.Poster = url; + // Just grab the first + if (string.IsNullOrWhiteSpace(data.Poster)) + { + data.Poster = url; + } } else if (string.Equals(bannerType2, "seasonwide", StringComparison.OrdinalIgnoreCase)) { - data.Banner = url; + // Just grab the first + if (string.IsNullOrWhiteSpace(data.Banner)) + { + data.Banner = url; + } } } else if (string.Equals(bannerType, "fanart", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs index fa42257607..cc8e970400 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs @@ -1,5 +1,4 @@ -using System.Linq; -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -11,6 +10,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -373,11 +373,19 @@ namespace MediaBrowser.Providers.TV { if (string.Equals(type, "poster", StringComparison.OrdinalIgnoreCase)) { - data.Poster = url; + // Just grab the first + if (string.IsNullOrWhiteSpace(data.Poster)) + { + data.Poster = url; + } } else if (string.Equals(type, "series", StringComparison.OrdinalIgnoreCase)) { - data.Banner = url; + // Just grab the first + if (string.IsNullOrWhiteSpace(data.Banner)) + { + data.Banner = url; + } } else if (string.Equals(type, "fanart", StringComparison.OrdinalIgnoreCase)) { From 976523afa92420de1b690e3a7e35e003e1899ff9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 26 Oct 2013 18:01:21 -0400 Subject: [PATCH 03/25] added new params for missing/unaired --- MediaBrowser.Api/LibraryService.cs | 4 +- MediaBrowser.Api/TvShowsService.cs | 67 +++----- MediaBrowser.Api/UserLibrary/ItemsService.cs | 149 +++++++++++++----- .../Entities/TV/Episode.cs | 16 +- MediaBrowser.Controller/Entities/TV/Season.cs | 18 ++- .../Configuration/UserConfiguration.cs | 6 +- .../Entities/MetadataProviders.cs | 4 +- MediaBrowser.Model/Querying/ItemQuery.cs | 19 ++- MediaBrowser.Model/Querying/NextUpQuery.cs | 25 +-- .../TV/RemoteEpisodeProvider.cs | 21 +-- .../Library/Resolvers/TV/SeriesResolver.cs | 3 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 14 files changed, 204 insertions(+), 138 deletions(-) diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index 82089536cb..202d372e83 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -335,7 +335,9 @@ namespace MediaBrowser.Api /// System.Object. public object Get(GetItemCounts request) { - var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager).ToList(); + var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager) + .Where(i => i.LocationType != LocationType.Virtual) + .ToList(); var filteredItems = request.UserId.HasValue ? FilterItems(items, request, request.UserId.Value).ToList() : items; diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index fad17814ec..0ac181dea2 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -1,10 +1,9 @@ -using System.Collections; -using System.Globalization; -using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; @@ -48,18 +47,9 @@ namespace MediaBrowser.Api [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, OverviewHtml, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } - [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ExcludeLocationTypes { get; set; } + [ApiMember(Name = "SeriesId", Description = "Optional. Filter by series id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string SeriesId { get; set; } - [ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string MinPremiereDate { get; set; } - - [ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string MaxPremiereDate { get; set; } - - [ApiMember(Name = "HasPremiereDate", Description = "Optional filter by items with premiere dates.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? HasPremiereDate { get; set; } - /// /// Gets the item fields. /// @@ -170,10 +160,15 @@ namespace MediaBrowser.Api { var user = _userManager.GetUserById(request.UserId); - var itemsList = user.RootFolder - .GetRecursiveChildren(user, i => i is Series) + var items = user.RootFolder + .GetRecursiveChildren(user) + .OfType(); + + items = FilterSeries(request, items); + + var itemsList = items .AsParallel() - .Select(i => GetNextUp((Series)i, user, request)) + .Select(i => GetNextUp(i, user, request)) .ToList(); itemsList = itemsList @@ -264,35 +259,19 @@ namespace MediaBrowser.Api private IEnumerable FilterItems(GetNextUpEpisodes request, IEnumerable items) { - // ExcludeLocationTypes - if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) + // Make this configurable when needed + items = items.Where(i => i.LocationType != LocationType.Virtual); + + return items; + } + + private IEnumerable FilterSeries(GetNextUpEpisodes request, IEnumerable items) + { + if (!string.IsNullOrWhiteSpace(request.SeriesId)) { - var vals = request.ExcludeLocationTypes.Split(','); + var id = new Guid(request.SeriesId); - items = items - .Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)) - .ToList(); - } - - if (!string.IsNullOrEmpty(request.MinPremiereDate)) - { - var date = DateTime.ParseExact(request.MinPremiereDate, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); - - items = items.Where(i => !i.PremiereDate.HasValue || i.PremiereDate.Value >= date); - } - - if (!string.IsNullOrEmpty(request.MaxPremiereDate)) - { - var date = DateTime.ParseExact(request.MaxPremiereDate, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); - - items = items.Where(i => !i.PremiereDate.HasValue || i.PremiereDate.Value <= date); - } - - if (request.HasPremiereDate.HasValue) - { - var val = request.HasPremiereDate.Value; - - items = items.Where(i => i.PremiereDate.HasValue == val); + items = items.Where(i => i.Id == id); } return items; diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index cb01dae733..4db7cf9707 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; @@ -181,22 +180,19 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IsHD", Description = "Optional filter by items that are HD or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsHD { get; set; } - [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ExcludeLocationTypes { get; set; } - [ApiMember(Name = "LocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string LocationTypes { get; set; } - [ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string MinPremiereDate { get; set; } - - [ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string MaxPremiereDate { get; set; } - - [ApiMember(Name = "HasPremiereDate", Description = "Optional filter by items with premiere dates.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? HasPremiereDate { get; set; } + [ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string ExcludeLocationTypes { get; set; } public bool IncludeIndexContainers { get; set; } + + [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsMissing { get; set; } + + [ApiMember(Name = "IsUnaired", Description = "Optional filter by items that are unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsUnaired { get; set; } } /// @@ -270,6 +266,8 @@ namespace MediaBrowser.Api.UserLibrary items = ApplyFilter(items, filter, user, _userDataRepository); } + items = FilterVirtualEpisodes(request, items, user); + items = items.AsEnumerable(); items = ApplySearchTerm(request, items); @@ -440,6 +438,93 @@ namespace MediaBrowser.Api.UserLibrary return items; } + private IEnumerable FilterVirtualEpisodes(GetItems request, IEnumerable items, User user) + { + items = FilterVirtualSeasons(request, items, user); + + if (request.IsMissing.HasValue) + { + var val = request.IsMissing.Value; + items = items.Where(i => + { + var e = i as Episode; + if (e != null) + { + return e.IsMissingEpisode == val; + } + return true; + }); + } + + if (request.IsUnaired.HasValue) + { + var val = request.IsUnaired.Value; + items = items.Where(i => + { + var e = i as Episode; + if (e != null) + { + return e.IsUnaired == val; + } + return true; + }); + } + + return items; + } + + private IEnumerable FilterVirtualSeasons(GetItems request, IEnumerable items, User user) + { + if (request.IsMissing.HasValue && request.IsUnaired.HasValue) + { + var isMissing = request.IsMissing.Value; + var isUnaired = request.IsUnaired.Value; + + if (!isMissing && !isUnaired) + { + return items.Where(i => + { + var e = i as Season; + if (e != null) + { + return !e.IsMissingOrUnaired; + } + return true; + }); + } + } + + if (request.IsMissing.HasValue) + { + var val = request.IsMissing.Value; + items = items.Where(i => + { + var e = i as Season; + if (e != null) + { + return e.IsMissingSeason == val; + } + return true; + }); + } + + if (request.IsUnaired.HasValue) + { + var val = request.IsUnaired.Value; + items = items.Where(i => + { + var e = i as Season; + if (e != null) + { + return e.IsUnaired == val; + } + return true; + }); + } + + return items; + } + /// /// Applies the additional filters. /// @@ -593,13 +678,6 @@ namespace MediaBrowser.Api.UserLibrary items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)); } - // ExcludeLocationTypes - if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) - { - var vals = request.ExcludeLocationTypes.Split(','); - items = items.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); - } - // LocationTypes if (!string.IsNullOrEmpty(request.LocationTypes)) { @@ -607,6 +685,13 @@ namespace MediaBrowser.Api.UserLibrary items = items.Where(f => vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); } + // ExcludeLocationTypes + if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) + { + var vals = request.ExcludeLocationTypes.Split(','); + items = items.Where(f => !vals.Contains(f.LocationType.ToString(), StringComparer.OrdinalIgnoreCase)); + } + if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater)) { items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1); @@ -826,7 +911,8 @@ namespace MediaBrowser.Api.UserLibrary if (request.IsHD.HasValue) { - items = items.OfType TmdbCollection, MusicBrainzReleaseGroup, - Zap2It + Zap2It, + NesBox, + NesBoxRom } } diff --git a/MediaBrowser.Model/Querying/ItemQuery.cs b/MediaBrowser.Model/Querying/ItemQuery.cs index d351474eb7..ba51bc46dd 100644 --- a/MediaBrowser.Model/Querying/ItemQuery.cs +++ b/MediaBrowser.Model/Querying/ItemQuery.cs @@ -241,16 +241,25 @@ namespace MediaBrowser.Model.Querying /// /// The location types. public LocationType[] LocationTypes { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is missing episode. + /// + /// null if [is missing episode] contains no value, true if [is missing episode]; otherwise, false. + public bool? IsMissing { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is unaired episode. + /// + /// null if [is unaired episode] contains no value, true if [is unaired episode]; otherwise, false. + public bool? IsUnaired { get; set; } + /// /// Gets or sets the exclude location types. /// /// The exclude location types. public LocationType[] ExcludeLocationTypes { get; set; } - - public bool? HasPremiereDate { get; set; } - public DateTime? MinPremiereDate { get; set; } - public DateTime? MaxPremiereDate { get; set; } - + /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs index 1b7b45ca30..4f5d47a04e 100644 --- a/MediaBrowser.Model/Querying/NextUpQuery.cs +++ b/MediaBrowser.Model/Querying/NextUpQuery.cs @@ -1,6 +1,4 @@ -using MediaBrowser.Model.Entities; -using System; - + namespace MediaBrowser.Model.Querying { public class NextUpQuery @@ -11,6 +9,12 @@ namespace MediaBrowser.Model.Querying /// The user id. public string UserId { get; set; } + /// + /// Gets or sets the series id. + /// + /// The series id. + public string SeriesId { get; set; } + /// /// Skips over a given number of items within the results. Use for paging. /// @@ -28,20 +32,5 @@ namespace MediaBrowser.Model.Querying /// /// The fields. public ItemFields[] Fields { get; set; } - - /// - /// Gets or sets the exclude location types. - /// - /// The exclude location types. - public LocationType[] ExcludeLocationTypes { get; set; } - - public bool? HasPremiereDate { get; set; } - public DateTime? MinPremiereDate { get; set; } - public DateTime? MaxPremiereDate { get; set; } - - public NextUpQuery() - { - ExcludeLocationTypes = new LocationType[] { }; - } } } diff --git a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs index e8b4b0f3de..7ac51e76d8 100644 --- a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs @@ -240,17 +240,16 @@ namespace MediaBrowser.Providers.TV { cancellationToken.ThrowIfCancellationRequested(); + var status = ProviderRefreshStatus.Success; + var episode = (Episode)item; var seriesId = episode.Series != null ? episode.Series.GetProviderId(MetadataProviders.Tvdb) : null; if (!string.IsNullOrEmpty(seriesId)) { - var seriesDataPath = RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, - seriesId); + var seriesDataPath = RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId); - var status = ProviderRefreshStatus.Success; - try { status = await FetchEpisodeData(episode, seriesDataPath, cancellationToken).ConfigureAwait(false); @@ -259,20 +258,10 @@ namespace MediaBrowser.Providers.TV { // Don't fail the provider because this will just keep on going and going. } - - BaseProviderInfo data; - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - SetLastRefreshed(item, DateTime.UtcNow, status); - return true; } - Logger.Info("Episode provider not fetching because series does not have a tvdb id: " + item.Path); - return false; + SetLastRefreshed(item, DateTime.UtcNow, status); + return true; } diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 9a2f6c6373..adf914766b 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -52,7 +52,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV // If there's a collection type and it's not tv, it can't be a series if (!string.IsNullOrEmpty(collectionType) && - !string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) + !string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) && + !string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase)) { return null; } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 1dd6ed0f10..1446dbec26 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.232 + 3.0.233 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index cc3d1c7e59..6da43af68b 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.232 + 3.0.233 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 0e7fa7fbfe..fab5c80931 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.232 + 3.0.233 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From e7a1f523734d1fc6e7808c2f703dbe543410ffe1 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 26 Oct 2013 18:40:53 -0400 Subject: [PATCH 04/25] account for unaired physical episodes --- MediaBrowser.Api/UserLibrary/ItemsService.cs | 33 ++++++++++++++++++- .../Entities/TV/Episode.cs | 7 +++- MediaBrowser.Controller/Entities/TV/Season.cs | 11 +++++-- MediaBrowser.Model/Querying/ItemQuery.cs | 2 ++ Nuget/MediaBrowser.Common.Internal.nuspec | 4 +-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +-- 7 files changed, 53 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 4db7cf9707..9c3ea7bf04 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -193,6 +193,9 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IsUnaired", Description = "Optional filter by items that are unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsUnaired { get; set; } + + [ApiMember(Name = "IsVirtualUnaired", Description = "Optional filter by items that are virtual unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool? IsVirtualUnaired { get; set; } } /// @@ -470,6 +473,20 @@ namespace MediaBrowser.Api.UserLibrary }); } + if (request.IsVirtualUnaired.HasValue) + { + var val = request.IsVirtualUnaired.Value; + items = items.Where(i => + { + var e = i as Episode; + if (e != null) + { + return e.IsVirtualUnaired == val; + } + return true; + }); + } + return items; } @@ -487,7 +504,7 @@ namespace MediaBrowser.Api.UserLibrary var e = i as Season; if (e != null) { - return !e.IsMissingOrUnaired; + return !e.IsMissingOrVirtualUnaired; } return true; }); @@ -522,6 +539,20 @@ namespace MediaBrowser.Api.UserLibrary }); } + if (request.IsVirtualUnaired.HasValue) + { + var val = request.IsVirtualUnaired.Value; + items = items.Where(i => + { + var e = i as Season; + if (e != null) + { + return e.IsVirtualUnaired == val; + } + return true; + }); + } + return items; } diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index e15a7d4d02..1176fca52b 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -203,7 +203,12 @@ namespace MediaBrowser.Controller.Entities.TV public bool IsUnaired { - get { return LocationType == Model.Entities.LocationType.Virtual && PremiereDate.HasValue && PremiereDate.Value >= DateTime.UtcNow; } + get { return PremiereDate.HasValue && PremiereDate.Value >= DateTime.UtcNow; } + } + + public bool IsVirtualUnaired + { + get { return LocationType == Model.Entities.LocationType.Virtual && IsUnaired; } } } } diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 9a0959cad6..5a53e8c0fe 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -156,12 +156,17 @@ namespace MediaBrowser.Controller.Entities.TV public bool IsUnaired { - get { return LocationType == Model.Entities.LocationType.Virtual && Children.OfType().All(i => i.IsUnaired); } + get { return Children.OfType().All(i => i.IsUnaired); } } - public bool IsMissingOrUnaired + public bool IsVirtualUnaired { - get { return LocationType == Model.Entities.LocationType.Virtual && Children.OfType().All(i => i.IsUnaired || i.IsMissingEpisode); } + get { return LocationType == Model.Entities.LocationType.Virtual && IsUnaired; } + } + + public bool IsMissingOrVirtualUnaired + { + get { return LocationType == Model.Entities.LocationType.Virtual && Children.OfType().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); } } } } diff --git a/MediaBrowser.Model/Querying/ItemQuery.cs b/MediaBrowser.Model/Querying/ItemQuery.cs index ba51bc46dd..f4a1d20d2d 100644 --- a/MediaBrowser.Model/Querying/ItemQuery.cs +++ b/MediaBrowser.Model/Querying/ItemQuery.cs @@ -254,6 +254,8 @@ namespace MediaBrowser.Model.Querying /// null if [is unaired episode] contains no value, true if [is unaired episode]; otherwise, false. public bool? IsUnaired { get; set; } + public bool? IsVirtualUnaired { get; set; } + /// /// Gets or sets the exclude location types. /// diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 1446dbec26..b93286b127 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.233 + 3.0.234 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 6da43af68b..6712fad0c3 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.233 + 3.0.234 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index fab5c80931..c4704f9b33 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.233 + 3.0.234 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - + From 676579d4bde6e5e98d4e21f3b542310f9d8662cd Mon Sep 17 00:00:00 2001 From: tikuf Date: Mon, 28 Oct 2013 08:09:22 +1100 Subject: [PATCH 05/25] Add Topfield .rec files, Make sure scale w x h is divisible by 2 --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 6462301600..31166ae2aa 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -269,7 +269,7 @@ namespace MediaBrowser.Api.Playback // If fixed dimensions were supplied if (request.Width.HasValue && request.Height.HasValue) { - return string.Format(" -vf \"scale={0}:{1}{2}\"", request.Width.Value, request.Height.Value, assSubtitleParam); + return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", request.Width.Value, request.Height.Value, assSubtitleParam); } var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index 1b3aba1026..dbac826b34 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -44,7 +44,8 @@ namespace MediaBrowser.Controller.Resolvers ".f4v", ".3gp", ".webm", - ".mts" + ".mts", + ".rec" }; private static readonly Dictionary VideoFileExtensionsDictionary = VideoFileExtensions.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); From ae543e4253338f4e4a7db123bea799284c683c9a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 27 Oct 2013 17:40:10 -0400 Subject: [PATCH 06/25] fixes #602 - Support .rec video extension --- MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index 1b3aba1026..0e394e3239 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -43,6 +43,7 @@ namespace MediaBrowser.Controller.Resolvers ".flv", ".f4v", ".3gp", + ".rec", ".webm", ".mts" }; From 85568474ce62a15999c7d87edeadceea92cb6d38 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 27 Oct 2013 17:40:42 -0400 Subject: [PATCH 07/25] add GameSystem to GameSystem dto --- .../Dto/DtoService.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 65a161821c..19e19f8ea4 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -373,6 +373,11 @@ namespace MediaBrowser.Server.Implementations.Dto dto.GameSystem = item.GameSystem; } + private void SetGameSystemProperties(BaseItemDto dto, GameSystem item) + { + dto.GameSystem = item.GameSystemName; + } + /// /// Gets the backdrop image tags. /// @@ -1064,6 +1069,13 @@ namespace MediaBrowser.Server.Implementations.Dto SetGameProperties(dto, game); } + var gameSystem = item as GameSystem; + + if (gameSystem != null) + { + SetGameSystemProperties(dto, gameSystem); + } + var musicVideo = item as MusicVideo; if (musicVideo != null) From 2d9f7d678ca15b42c8e45431b10af40cd583c1b9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 27 Oct 2013 17:42:11 -0400 Subject: [PATCH 08/25] fix merge duplication --- MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index 574251423d..dbac826b34 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -43,7 +43,6 @@ namespace MediaBrowser.Controller.Resolvers ".flv", ".f4v", ".3gp", - ".rec", ".webm", ".mts", ".rec" From fed320f322ec120ff0e540dd5e750808deaeb8c6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 27 Oct 2013 17:53:35 -0400 Subject: [PATCH 09/25] show today's episodes on upcoming --- MediaBrowser.Controller/Entities/TV/Episode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 1176fca52b..0cee747b8f 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -203,7 +203,7 @@ namespace MediaBrowser.Controller.Entities.TV public bool IsUnaired { - get { return PremiereDate.HasValue && PremiereDate.Value >= DateTime.UtcNow; } + get { return PremiereDate.HasValue && PremiereDate.Value >= DateTime.UtcNow.Date; } } public bool IsVirtualUnaired From c2228c9a7ffb977efc2a4cfbe7332d6ff2ce2992 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 27 Oct 2013 18:19:23 -0400 Subject: [PATCH 10/25] fix wrong season banners getting downloaded --- .../TV/RemoteSeasonProvider.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs index a5408c8dee..4eba7dbb4e 100644 --- a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs @@ -275,6 +275,7 @@ namespace MediaBrowser.Providers.TV string url = null; int? bannerSeason = null; string resolution = null; + string language = null; while (reader.Read()) { @@ -282,6 +283,12 @@ namespace MediaBrowser.Providers.TV { switch (reader.Name) { + case "Language": + { + language = reader.ReadElementContentAsString() ?? string.Empty; + break; + } + case "BannerType": { bannerType = reader.ReadElementContentAsString() ?? string.Empty; @@ -333,10 +340,21 @@ namespace MediaBrowser.Providers.TV } else if (string.Equals(bannerType2, "seasonwide", StringComparison.OrdinalIgnoreCase)) { - // Just grab the first - if (string.IsNullOrWhiteSpace(data.Banner)) + if (string.IsNullOrWhiteSpace(language) || string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { - data.Banner = url; + // Just grab the first + if (string.IsNullOrWhiteSpace(data.Banner)) + { + data.Banner = url; + } + } + else if (string.Equals(language, ConfigurationManager.Configuration.PreferredMetadataLanguage, StringComparison.OrdinalIgnoreCase)) + { + // Just grab the first + if (string.IsNullOrWhiteSpace(data.LanguageBanner)) + { + data.LanguageBanner = url; + } } } } From 0b01f9d282345d0d23e46171cc0dc3c57d36495b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 28 Oct 2013 08:26:00 -0400 Subject: [PATCH 11/25] adjust unaired cutoff --- MediaBrowser.Controller/Entities/TV/Episode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 0cee747b8f..96b120b8fe 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -203,7 +203,7 @@ namespace MediaBrowser.Controller.Entities.TV public bool IsUnaired { - get { return PremiereDate.HasValue && PremiereDate.Value >= DateTime.UtcNow.Date; } + get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; } } public bool IsVirtualUnaired From b8f642a2d4c45cee7d56a37c0bc818d07e079347 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 28 Oct 2013 08:27:26 -0400 Subject: [PATCH 12/25] removed shortcut migration --- MediaBrowser.ServerApplication/MainStartup.cs | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 3d45f143bc..d3eed5c484 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -214,8 +214,6 @@ namespace MediaBrowser.ServerApplication SystemEvents.SessionEnding += SystemEvents_SessionEnding; SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; - MigrateShortcuts(appPaths.RootFolderPath); - _appHost = new ApplicationHost(appPaths, logManager); _app = new App(_appHost, _appHost.LogManager.GetLogger("App"), runService); @@ -537,34 +535,5 @@ namespace MediaBrowser.ServerApplication /// SEM_NOOPENFILEERRORBOX = 0x8000 } - - private static void MigrateShortcuts(string directory) - { - Directory.CreateDirectory(directory); - - foreach (var file in Directory.EnumerateFiles(directory, "*.lnk", SearchOption.AllDirectories).ToList()) - { - MigrateShortcut(file); - } - } - - private static void MigrateShortcut(string file) - { - var newFile = Path.ChangeExtension(file, ".mblink"); - - try - { - var resolvedPath = FileSystem.ResolveShortcut(file); - - if (!string.IsNullOrEmpty(resolvedPath)) - { - FileSystem.CreateShortcut(newFile, resolvedPath); - } - } - finally - { - File.Delete(file); - } - } } } From ef1dcd3423d9a14018e9953349de603380157f8a Mon Sep 17 00:00:00 2001 From: Thomas Gillen Date: Mon, 28 Oct 2013 13:59:13 +0000 Subject: [PATCH 13/25] tvdb series provider now considers alias names --- .../TV/RemoteSeriesProvider.cs | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs b/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs index 322fcd2280..5d518c0ea2 100644 --- a/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs @@ -1109,21 +1109,37 @@ namespace MediaBrowser.Providers.TV var nodes = doc.SelectNodes("//Series"); var comparableName = GetComparableName(name); if (nodes != null) + { foreach (XmlNode node in nodes) { - var n = node.SelectSingleNode("./SeriesName"); - if (n != null && string.Equals(GetComparableName(n.InnerText), comparableName, StringComparison.OrdinalIgnoreCase)) + var titles = new List(); + + var nameNode = node.SelectSingleNode("./SeriesName"); + if (nameNode != null) { - n = node.SelectSingleNode("./seriesid"); - if (n != null) - return n.InnerText; + titles.Add(GetComparableName(nameNode.InnerText)); } - else + + var aliasNode = node.SelectSingleNode("./AliasNames"); + if (aliasNode != null) { - if (n != null) - Logger.Info("TVDb Provider - " + n.InnerText + " did not match " + comparableName); + var alias = aliasNode.InnerText.Split('|').Select(GetComparableName); + titles.AddRange(alias); + } + + if (titles.Any(t => string.Equals(t, comparableName, StringComparison.OrdinalIgnoreCase))) + { + var id = node.SelectSingleNode("./seriesid"); + if (id != null) + return id.InnerText; + } + + foreach (var title in titles) + { + Logger.Info("TVDb Provider - " + title + " did not match " + comparableName); } } + } } // Try stripping off the year if it was supplied From 8bd7df410ce1c10cbcc5a223138226bb6986e8b8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 28 Oct 2013 10:56:57 -0400 Subject: [PATCH 14/25] mark games played when played through nesbox --- MediaBrowser.Controller/Entities/BaseItem.cs | 7 ++ .../Providers/BaseItemXmlParser.cs | 39 ------- .../Games/GameProviderFromXml.cs | 4 +- MediaBrowser.Providers/Games/GameXmlParser.cs | 110 ++++++++++++++++++ .../MediaBrowser.Providers.csproj | 1 + MediaBrowser.Providers/Savers/GameXmlSaver.cs | 19 ++- MediaBrowser.WebDashboard/ApiClient.js | 50 +++++--- MediaBrowser.WebDashboard/packages.config | 2 +- 8 files changed, 176 insertions(+), 56 deletions(-) create mode 100644 MediaBrowser.Providers/Games/GameXmlParser.cs diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 67221a9ac9..8174dded26 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1337,6 +1337,13 @@ namespace MediaBrowser.Controller.Entities var data = userManager.GetUserData(user.Id, key); + if (datePlayed.HasValue) + { + // Incremenet + data.PlayCount++; + } + + // Ensure it's at least one data.PlayCount = Math.Max(data.PlayCount, 1); data.LastPlayedDate = datePlayed ?? data.LastPlayedDate; diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index e1b38bc714..e9bb7f66d9 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -552,32 +552,6 @@ namespace MediaBrowser.Controller.Providers } break; - case "GamesDbId": - var gamesdbId = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(gamesdbId)) - { - item.SetProviderId(MetadataProviders.Gamesdb, gamesdbId); - } - break; - - case "Players": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - int num; - - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out num)) - { - var game = item as Game; - if (game != null) - { - game.PlayersSupported = num; - } - } - } - break; - } case "VoteCount": { var val = reader.ReadElementContentAsString(); @@ -592,19 +566,6 @@ namespace MediaBrowser.Controller.Providers } break; } - case "GameSystem": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - var game = item as Game; - if (game != null) - { - game.GameSystem = val; - } - } - break; - } case "MusicbrainzId": { var mbz = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.Providers/Games/GameProviderFromXml.cs b/MediaBrowser.Providers/Games/GameProviderFromXml.cs index 6292cec465..44680b8328 100644 --- a/MediaBrowser.Providers/Games/GameProviderFromXml.cs +++ b/MediaBrowser.Providers/Games/GameProviderFromXml.cs @@ -3,11 +3,11 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; +using MediaBrowser.Providers.Savers; using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Providers.Savers; namespace MediaBrowser.Providers.Games { @@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Games try { - new BaseItemXmlParser(Logger).Fetch(game, metaFile, cancellationToken); + new GameXmlParser(Logger).Fetch(game, metaFile, cancellationToken); } finally { diff --git a/MediaBrowser.Providers/Games/GameXmlParser.cs b/MediaBrowser.Providers/Games/GameXmlParser.cs new file mode 100644 index 0000000000..53cc123884 --- /dev/null +++ b/MediaBrowser.Providers/Games/GameXmlParser.cs @@ -0,0 +1,110 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace MediaBrowser.Providers.Games +{ + /// + /// Class EpisodeXmlParser + /// + public class GameXmlParser : BaseItemXmlParser + { + private Task _chaptersTask = null; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + public GameXmlParser(ILogger logger) + : base(logger) + { + } + + public async Task FetchAsync(Game item, string metadataFile, CancellationToken cancellationToken) + { + _chaptersTask = null; + + Fetch(item, metadataFile, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + if (_chaptersTask != null) + { + await _chaptersTask.ConfigureAwait(false); + } + } + + /// + /// Fetches the data from XML node. + /// + /// The reader. + /// The item. + protected override void FetchDataFromXmlNode(XmlReader reader, Game item) + { + switch (reader.Name) + { + case "GameSystem": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.GameSystem = val; + } + break; + } + + case "GamesDbId": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Gamesdb, val); + } + break; + } + + case "NesBox": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.NesBox, val); + } + break; + } + + case "NesBoxRom": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.NesBoxRom, val); + } + break; + } + + case "Players": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + int num; + + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out num)) + { + item.PlayersSupported = num; + } + } + break; + } + + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index e687e14d9d..63bd8c9534 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -49,6 +49,7 @@ + diff --git a/MediaBrowser.Providers/Savers/GameXmlSaver.cs b/MediaBrowser.Providers/Savers/GameXmlSaver.cs index 5564b71cef..f35e4d1319 100644 --- a/MediaBrowser.Providers/Savers/GameXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/GameXmlSaver.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Entities; using MediaBrowser.Providers.Movies; using System; using System.Collections.Generic; @@ -70,6 +71,20 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(game.GameSystem) + ""); } + var val = game.GetProviderId(MetadataProviders.NesBox); + + if (!string.IsNullOrEmpty(val)) + { + builder.Append("" + SecurityElement.Escape(val) + ""); + } + + val = game.GetProviderId(MetadataProviders.NesBoxRom); + + if (!string.IsNullOrEmpty(val)) + { + builder.Append("" + SecurityElement.Escape(val) + ""); + } + XmlSaverHelpers.AddCommonNodes(item, builder); builder.Append(""); @@ -79,7 +94,9 @@ namespace MediaBrowser.Providers.Savers XmlSaverHelpers.Save(builder, xmlFilePath, new List { "Players", - "GameSystem" + "GameSystem", + "NesBox", + "NesBoxRom" }); // Set last refreshed so that the provider doesn't trigger after the file save diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index 97a443e844..8e64e4300d 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -2731,16 +2731,42 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi }); }; - /** - * Marks an item as played or unplayed - * This should not be used to update playstate following playback. - * There are separate playstate check-in methods for that. This should be used for a - * separate option to reset playstate. - * @param {String} userId - * @param {String} itemId - * @param {Boolean} wasPlayed - */ - self.updatePlayedStatus = function (userId, itemId, wasPlayed) { + self.getDateParamValue = function (date) { + function formatDigit(i) { + return i < 10 ? "0" + i : i; + } + + var d = date; + + return "" + d.getFullYear() + formatDigit(d.getMonth() + 1) + formatDigit(d.getDate()) + formatDigit(d.getHours()) + formatDigit(d.getMinutes()) + formatDigit(d.getSeconds()); + }; + + self.markPlayed = function (userId, itemId, date) { + + if (!userId) { + throw new Error("null userId"); + } + + if (!itemId) { + throw new Error("null itemId"); + } + + var options = {}; + + if (date) { + options.DatePlayed = self.getDateParamValue(date); + } + + var url = self.getUrl("Users/" + userId + "/PlayedItems/" + itemId, options); + + return self.ajax({ + type: "POST", + url: url, + dataType: "json" + }); + }; + + self.markUnplayed = function (userId, itemId) { if (!userId) { throw new Error("null userId"); @@ -2752,10 +2778,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi var url = self.getUrl("Users/" + userId + "/PlayedItems/" + itemId); - var method = wasPlayed ? "POST" : "DELETE"; - return self.ajax({ - type: method, + type: "DELETE", url: url, dataType: "json" }); diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 9c48b38095..02daa2cae4 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file From d77122ba0f3c3ca71bfa4c8fa5b25bb3c3f0f1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Miguel=20Alm=C3=A1nzar?= Date: Mon, 28 Oct 2013 20:35:01 -0400 Subject: [PATCH 15/25] add support to movie names with dot notation --- .../Movies/MovieDbProvider.cs | 3 +- MediaBrowser.Tests/MediaBrowser.Tests.csproj | 5 +++ .../Providers/MovieDbProviderTests.cs | 36 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 MediaBrowser.Tests/Providers/MovieDbProviderTests.cs diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 9ed0860b2e..63a237d2c8 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -189,6 +189,7 @@ namespace MediaBrowser.Providers.Movies static readonly Regex[] NameMatches = new[] { new Regex(@"(?.*)\((?\d{4})\)"), // matches "My Movie (2001)" and gives us the name and the year + new Regex(@"(?.*)(\.(?\d{4})(\.|$)).*$"), new Regex(@"(?.*)") // last resort matches the whole string as the name }; @@ -320,7 +321,7 @@ namespace MediaBrowser.Providers.Movies /// The name. /// Name of the just. /// The year. - protected void ParseName(string name, out string justName, out int? year) + public static void ParseName(string name, out string justName, out int? year) { justName = null; year = null; diff --git a/MediaBrowser.Tests/MediaBrowser.Tests.csproj b/MediaBrowser.Tests/MediaBrowser.Tests.csproj index a7a7ac2433..95e55fda0c 100644 --- a/MediaBrowser.Tests/MediaBrowser.Tests.csproj +++ b/MediaBrowser.Tests/MediaBrowser.Tests.csproj @@ -50,6 +50,7 @@ + @@ -63,6 +64,10 @@ {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} MediaBrowser.Model + + {442B5058-DCAF-4263-BB6A-F21E31120A1B} + MediaBrowser.Providers + diff --git a/MediaBrowser.Tests/Providers/MovieDbProviderTests.cs b/MediaBrowser.Tests/Providers/MovieDbProviderTests.cs new file mode 100644 index 0000000000..f7a87c9d47 --- /dev/null +++ b/MediaBrowser.Tests/Providers/MovieDbProviderTests.cs @@ -0,0 +1,36 @@ +using MediaBrowser.Providers.Movies; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MediaBrowser.Tests.Providers { + [TestClass] + public class MovieDbProviderTests { + [TestMethod] + public void TestNameMatches() { + var name = string.Empty; + int? year = null; + MovieDbProvider.ParseName("My Movie (2013)", out name, out year); + Assert.AreEqual("My Movie", name); + Assert.AreEqual(2013, year); + name = string.Empty; + year = null; + MovieDbProvider.ParseName("My Movie 2 (2013)", out name, out year); + Assert.AreEqual("My Movie 2", name); + Assert.AreEqual(2013, year); + name = string.Empty; + year = null; + MovieDbProvider.ParseName("My Movie 2001 (2013)", out name, out year); + Assert.AreEqual("My Movie 2001", name); + Assert.AreEqual(2013, year); + name = string.Empty; + year = null; + MovieDbProvider.ParseName("My Movie - 2 (2013)", out name, out year); + Assert.AreEqual("My Movie - 2", name); + Assert.AreEqual(2013, year); + name = string.Empty; + year = null; + MovieDbProvider.ParseName("curse.of.chucky.2013.stv.unrated.multi.1080p.bluray.x264-rough", out name, out year); + Assert.AreEqual("curse.of.chucky", name); + Assert.AreEqual(2013, year); + } + } +} \ No newline at end of file From fb95d7b2a9789bd286b1cf212f8a67daac95a98b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 29 Oct 2013 14:47:47 -0400 Subject: [PATCH 16/25] fix fanart provider downloading posters as disc images --- .../MediaBrowser.Common.Implementations.csproj | 7 ++++--- MediaBrowser.Common.Implementations/packages.config | 2 +- MediaBrowser.Providers/Movies/FanArtMovieProvider.cs | 4 ++-- .../MediaBrowser.ServerApplication.csproj | 4 ++-- MediaBrowser.ServerApplication/packages.config | 2 +- Nuget/MediaBrowser.Common.Internal.nuspec | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index 3ec330d9ce..c17a549acc 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -44,6 +44,10 @@ ..\packages\sharpcompress.0.10.1.3\lib\net40\SharpCompress.dll + + False + ..\packages\SimpleInjector.2.3.6\lib\net40-client\SimpleInjector.dll + @@ -54,9 +58,6 @@ ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll - - ..\packages\SimpleInjector.2.3.5\lib\net40-client\SimpleInjector.dll - diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config index 8716325bf4..f2fe488309 100644 --- a/MediaBrowser.Common.Implementations/packages.config +++ b/MediaBrowser.Common.Implementations/packages.config @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs index f34988481e..3732d6b8ef 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs +++ b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs @@ -300,14 +300,14 @@ namespace MediaBrowser.Providers.Movies string path; - if (ConfigurationManager.Configuration.DownloadMovieImages.Disc && !item.HasImage(ImageType.Disc)) + if (ConfigurationManager.Configuration.DownloadMovieImages.Primary && !item.HasImage(ImageType.Primary)) { var node = doc.SelectSingleNode("//fanart/movie/movieposters/movieposter[@lang = \"" + language + "\"]/@url") ?? doc.SelectSingleNode("//fanart/movie/movieposters/movieposter/@url"); path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Disc, null, cancellationToken) + await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Primary, null, cancellationToken) .ConfigureAwait(false); } } diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index c39ee40a8c..96b404e004 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -157,9 +157,9 @@ False ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll - + False - ..\packages\SimpleInjector.2.3.5\lib\net40-client\SimpleInjector.dll + ..\packages\SimpleInjector.2.3.6\lib\net40-client\SimpleInjector.dll diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index 4af6fa65e0..0893a1b38a 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -8,5 +8,5 @@ - + \ No newline at end of file diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index b93286b127..0588442e1a 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -15,7 +15,7 @@ - + From cbd6c47af49145f3136cab9858b87ce98df9071a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 30 Oct 2013 10:13:09 -0400 Subject: [PATCH 17/25] updated sqlite --- .../MediaBrowser.Server.Implementations.csproj | 14 ++++++++------ .../packages.config | 2 +- MediaBrowser.ServerApplication/App.config | 4 ++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 05c5f5a826..017dc2b541 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -46,6 +46,14 @@ + + False + ..\packages\System.Data.SQLite.x86.1.0.89.0\lib\net45\System.Data.SQLite.dll + + + False + ..\packages\System.Data.SQLite.x86.1.0.89.0\lib\net45\System.Data.SQLite.Linq.dll + ..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll @@ -88,12 +96,6 @@ ..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll - - ..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.dll - - - ..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.Linq.dll - ..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.64\lib\net35\Mono.Data.Sqlite.dll diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 771a5c8b2a..eeeedfe362 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -14,5 +14,5 @@ - + \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/App.config b/MediaBrowser.ServerApplication/App.config index a5cbacb614..4f60de1457 100644 --- a/MediaBrowser.ServerApplication/App.config +++ b/MediaBrowser.ServerApplication/App.config @@ -41,6 +41,10 @@ + + + + From be7918e5f68f67ed32a50c2d86ee9cae79cf2b93 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 30 Oct 2013 10:40:14 -0400 Subject: [PATCH 18/25] fixes #567 - Deprecate native shortcut code --- MediaBrowser.Api/Library/LibraryHelpers.cs | 35 ++- .../Library/LibraryStructureService.cs | 17 +- MediaBrowser.Controller/Entities/BaseItem.cs | 5 +- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/IO/FileData.cs | 9 +- MediaBrowser.Controller/IO/FileSystem.cs | 294 +----------------- MediaBrowser.Controller/IO/IFileSystem.cs | 45 +++ .../MediaBrowser.Controller.csproj | 2 +- .../Resolvers/EntityResolutionHelper.cs | 5 +- .../ImagesByNameProvider.cs | 7 +- MediaBrowser.Providers/RefreshIntrosTask.cs | 7 +- .../IO/DirectoryWatchers.cs | 7 +- .../Library/LibraryManager.cs | 15 +- .../Library/ResolverHelper.cs | 6 +- .../ApplicationHost.cs | 8 +- .../IO/CommonFileSystem.cs | 264 ++++++++++++++++ .../IO/FileSystemFactory.cs | 19 ++ .../IO/NativeFileSystem.cs | 44 ++- .../MediaBrowser.ServerApplication.csproj | 3 + 19 files changed, 452 insertions(+), 342 deletions(-) create mode 100644 MediaBrowser.Controller/IO/IFileSystem.cs create mode 100644 MediaBrowser.ServerApplication/IO/CommonFileSystem.cs create mode 100644 MediaBrowser.ServerApplication/IO/FileSystemFactory.cs rename MediaBrowser.Controller/IO/NativeMethods.cs => MediaBrowser.ServerApplication/IO/NativeFileSystem.cs (91%) diff --git a/MediaBrowser.Api/Library/LibraryHelpers.cs b/MediaBrowser.Api/Library/LibraryHelpers.cs index 008cfb27f7..906b47458f 100644 --- a/MediaBrowser.Api/Library/LibraryHelpers.cs +++ b/MediaBrowser.Api/Library/LibraryHelpers.cs @@ -12,20 +12,27 @@ namespace MediaBrowser.Api.Library /// public static class LibraryHelpers { + /// + /// The shortcut file extension + /// private const string ShortcutFileExtension = ".mblink"; + /// + /// The shortcut file search + /// private const string ShortcutFileSearch = "*" + ShortcutFileExtension; /// /// Adds the virtual folder. /// + /// The file system. /// The name. /// Type of the collection. /// The user. /// The app paths. /// There is already a media collection with the name + name + . - public static void AddVirtualFolder(string name, string collectionType, User user, IServerApplicationPaths appPaths) + public static void AddVirtualFolder(IFileSystem fileSystem, string name, string collectionType, User user, IServerApplicationPaths appPaths) { - name = FileSystem.GetValidFilename(name); + name = fileSystem.GetValidFilename(name); var rootFolderPath = user != null ? user.RootFolderPath : appPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, name); @@ -106,12 +113,13 @@ namespace MediaBrowser.Api.Library /// /// Deletes a shortcut from within a virtual folder, within either the default view or a user view /// + /// The file system. /// Name of the virtual folder. /// The media path. /// The user. /// The app paths. /// The media folder does not exist - public static void RemoveMediaPath(string virtualFolderName, string mediaPath, User user, IServerApplicationPaths appPaths) + public static void RemoveMediaPath(IFileSystem fileSystem, string virtualFolderName, string mediaPath, User user, IServerApplicationPaths appPaths) { var rootFolderPath = user != null ? user.RootFolderPath : appPaths.DefaultUserViewsPath; var path = Path.Combine(rootFolderPath, virtualFolderName); @@ -121,7 +129,7 @@ namespace MediaBrowser.Api.Library throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName)); } - var shortcut = Directory.EnumerateFiles(path, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => FileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase)); + var shortcut = Directory.EnumerateFiles(path, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(shortcut)) { @@ -132,13 +140,14 @@ namespace MediaBrowser.Api.Library /// /// Adds an additional mediaPath to an existing virtual folder, within either the default view or a user view /// + /// The file system. /// Name of the virtual folder. /// The path. /// The user. /// The app paths. /// The path is not valid. /// The path does not exist. - public static void AddMediaPath(string virtualFolderName, string path, User user, IServerApplicationPaths appPaths) + public static void AddMediaPath(IFileSystem fileSystem, string virtualFolderName, string path, User user, IServerApplicationPaths appPaths) { if (!Path.IsPathRooted(path)) { @@ -160,7 +169,7 @@ namespace MediaBrowser.Api.Library var rootFolderPath = user != null ? user.RootFolderPath : appPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); - ValidateNewMediaPath(rootFolderPath, path, appPaths); + ValidateNewMediaPath(fileSystem, rootFolderPath, path, appPaths); var shortcutFilename = Path.GetFileNameWithoutExtension(path); @@ -172,20 +181,22 @@ namespace MediaBrowser.Api.Library lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); } - FileSystem.CreateShortcut(lnk, path); + fileSystem.CreateShortcut(lnk, path); } /// /// Validates that a new media path can be added /// + /// The file system. /// The current view root folder path. /// The media path. /// The app paths. - /// - private static void ValidateNewMediaPath(string currentViewRootFolderPath, string mediaPath, IServerApplicationPaths appPaths) + /// + /// + private static void ValidateNewMediaPath(IFileSystem fileSystem, string currentViewRootFolderPath, string mediaPath, IServerApplicationPaths appPaths) { var duplicate = Directory.EnumerateFiles(appPaths.RootFolderPath, ShortcutFileSearch, SearchOption.AllDirectories) - .Select(FileSystem.ResolveShortcut) + .Select(fileSystem.ResolveShortcut) .FirstOrDefault(p => !IsNewPathValid(mediaPath, p, false)); if (!string.IsNullOrEmpty(duplicate)) @@ -196,7 +207,7 @@ namespace MediaBrowser.Api.Library // Don't allow duplicate sub-paths within the same user library, or it will result in duplicate items // See comments in IsNewPathValid duplicate = Directory.EnumerateFiles(currentViewRootFolderPath, ShortcutFileSearch, SearchOption.AllDirectories) - .Select(FileSystem.ResolveShortcut) + .Select(fileSystem.ResolveShortcut) .FirstOrDefault(p => !IsNewPathValid(mediaPath, p, true)); if (!string.IsNullOrEmpty(duplicate)) @@ -206,7 +217,7 @@ namespace MediaBrowser.Api.Library // Make sure the current root folder doesn't already have a shortcut to the same path duplicate = Directory.EnumerateFiles(currentViewRootFolderPath, ShortcutFileSearch, SearchOption.AllDirectories) - .Select(FileSystem.ResolveShortcut) + .Select(fileSystem.ResolveShortcut) .FirstOrDefault(p => mediaPath.Equals(p, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(duplicate)) diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index e6fa1d1c0b..a5476ec8b8 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -186,6 +186,8 @@ namespace MediaBrowser.Api.Library private readonly IDirectoryWatchers _directoryWatchers; + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// @@ -193,7 +195,7 @@ namespace MediaBrowser.Api.Library /// The user manager. /// The library manager. /// appPaths - public LibraryStructureService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IDirectoryWatchers directoryWatchers) + public LibraryStructureService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem) { if (appPaths == null) { @@ -204,6 +206,7 @@ namespace MediaBrowser.Api.Library _appPaths = appPaths; _libraryManager = libraryManager; _directoryWatchers = directoryWatchers; + _fileSystem = fileSystem; } /// @@ -241,13 +244,13 @@ namespace MediaBrowser.Api.Library { if (string.IsNullOrEmpty(request.UserId)) { - LibraryHelpers.AddVirtualFolder(request.Name, request.CollectionType, null, _appPaths); + LibraryHelpers.AddVirtualFolder(_fileSystem, request.Name, request.CollectionType, null, _appPaths); } else { var user = _userManager.GetUserById(new Guid(request.UserId)); - LibraryHelpers.AddVirtualFolder(request.Name, request.CollectionType, user, _appPaths); + LibraryHelpers.AddVirtualFolder(_fileSystem, request.Name, request.CollectionType, user, _appPaths); } // Need to add a delay here or directory watchers may still pick up the changes @@ -352,13 +355,13 @@ namespace MediaBrowser.Api.Library { if (string.IsNullOrEmpty(request.UserId)) { - LibraryHelpers.AddMediaPath(request.Name, request.Path, null, _appPaths); + LibraryHelpers.AddMediaPath(_fileSystem, request.Name, request.Path, null, _appPaths); } else { var user = _userManager.GetUserById(new Guid(request.UserId)); - LibraryHelpers.AddMediaPath(request.Name, request.Path, user, _appPaths); + LibraryHelpers.AddMediaPath(_fileSystem, request.Name, request.Path, user, _appPaths); } // Need to add a delay here or directory watchers may still pick up the changes @@ -389,13 +392,13 @@ namespace MediaBrowser.Api.Library { if (string.IsNullOrEmpty(request.UserId)) { - LibraryHelpers.RemoveMediaPath(request.Name, request.Path, null, _appPaths); + LibraryHelpers.RemoveMediaPath(_fileSystem, request.Name, request.Path, null, _appPaths); } else { var user = _userManager.GetUserById(new Guid(request.UserId)); - LibraryHelpers.RemoveMediaPath(request.Name, request.Path, user, _appPaths); + LibraryHelpers.RemoveMediaPath(_fileSystem, request.Name, request.Path, user, _appPaths); } // Need to add a delay here or directory watchers may still pick up the changes diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 8174dded26..f1937a4616 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -212,6 +212,7 @@ namespace MediaBrowser.Controller.Entities public static IProviderManager ProviderManager { get; set; } public static ILocalizationManager LocalizationManager { get; set; } public static IItemRepository ItemRepository { get; set; } + public static IFileSystem FileSystem { get; set; } /// /// Returns a that represents this instance. @@ -395,7 +396,7 @@ namespace MediaBrowser.Controller.Entities // When resolving the root, we need it's grandchildren (children of user views) var flattenFolderDepth = isPhysicalRoot ? 2 : 0; - args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf); + args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf); // Need to remove subpaths that may have been resolved from shortcuts // Example: if \\server\movies exists, then strip out \\server\movies\action @@ -413,7 +414,7 @@ namespace MediaBrowser.Controller.Entities } //update our dates - EntityResolutionHelper.EnsureDates(this, args, false); + EntityResolutionHelper.EnsureDates(FileSystem, this, args, false); IsOffline = false; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index c54b812420..a4ba146165 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -693,7 +693,7 @@ namespace MediaBrowser.Controller.Entities //existing item - check if it has changed if (currentChild.HasChanged(child)) { - EntityResolutionHelper.EnsureDates(currentChild, child.ResolveArgs, false); + EntityResolutionHelper.EnsureDates(FileSystem, currentChild, child.ResolveArgs, false); validChildren.Add(new Tuple(currentChild, true)); } diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs index b1fc28e7b7..726467fa6a 100644 --- a/MediaBrowser.Controller/IO/FileData.cs +++ b/MediaBrowser.Controller/IO/FileData.cs @@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.IO /// Gets the filtered file system entries. /// /// The path. + /// The file system. /// The logger. /// The args. /// The search pattern. @@ -22,7 +23,7 @@ namespace MediaBrowser.Controller.IO /// if set to true [resolve shortcuts]. /// Dictionary{System.StringFileSystemInfo}. /// path - public static Dictionary GetFilteredFileSystemEntries(string path, ILogger logger, ItemResolveArgs args, string searchPattern = "*", int flattenFolderDepth = 0, bool resolveShortcuts = true) + public static Dictionary GetFilteredFileSystemEntries(string path, IFileSystem fileSystem, ILogger logger, ItemResolveArgs args, string searchPattern = "*", int flattenFolderDepth = 0, bool resolveShortcuts = true) { if (string.IsNullOrEmpty(path)) { @@ -56,9 +57,9 @@ namespace MediaBrowser.Controller.IO var fullName = entry.FullName; - if (resolveShortcuts && FileSystem.IsShortcut(fullName)) + if (resolveShortcuts && fileSystem.IsShortcut(fullName)) { - var newPath = FileSystem.ResolveShortcut(fullName); + var newPath = fileSystem.ResolveShortcut(fullName); if (string.IsNullOrWhiteSpace(newPath)) { @@ -77,7 +78,7 @@ namespace MediaBrowser.Controller.IO } else if (flattenFolderDepth > 0 && isDirectory) { - foreach (var child in GetFilteredFileSystemEntries(fullName, logger, args, flattenFolderDepth: flattenFolderDepth - 1, resolveShortcuts: resolveShortcuts)) + foreach (var child in GetFilteredFileSystemEntries(fullName, fileSystem, logger, args, flattenFolderDepth: flattenFolderDepth - 1, resolveShortcuts: resolveShortcuts)) { dict[child.Key] = child.Value; } diff --git a/MediaBrowser.Controller/IO/FileSystem.cs b/MediaBrowser.Controller/IO/FileSystem.cs index 1c49545be1..f31fc53de5 100644 --- a/MediaBrowser.Controller/IO/FileSystem.cs +++ b/MediaBrowser.Controller/IO/FileSystem.cs @@ -1,10 +1,6 @@ -using System.Collections.Generic; -using System.Linq; -using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Logging; using System; -using System.Collections.Specialized; using System.IO; -using System.Text; namespace MediaBrowser.Controller.IO { @@ -13,38 +9,6 @@ namespace MediaBrowser.Controller.IO /// public static class FileSystem { - /// - /// Gets the file system info. - /// - /// The path. - /// FileSystemInfo. - public static FileSystemInfo GetFileSystemInfo(string path) - { - // Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists - if (Path.HasExtension(path)) - { - var fileInfo = new FileInfo(path); - - if (fileInfo.Exists) - { - return fileInfo; - } - - return new DirectoryInfo(path); - } - else - { - var fileInfo = new DirectoryInfo(path); - - if (fileInfo.Exists) - { - return fileInfo; - } - - return new FileInfo(path); - } - } - /// /// Gets the creation time UTC. /// @@ -87,116 +51,6 @@ namespace MediaBrowser.Controller.IO } } - /// - /// The space char - /// - private const char SpaceChar = ' '; - /// - /// The invalid file name chars - /// - private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); - - /// - /// Takes a filename and removes invalid characters - /// - /// The filename. - /// System.String. - /// filename - public static string GetValidFilename(string filename) - { - if (string.IsNullOrEmpty(filename)) - { - throw new ArgumentNullException("filename"); - } - - var builder = new StringBuilder(filename); - - foreach (var c in InvalidFileNameChars) - { - builder = builder.Replace(c, SpaceChar); - } - - return builder.ToString(); - } - - /// - /// Resolves the shortcut. - /// - /// The filename. - /// System.String. - /// filename - public static string ResolveShortcut(string filename) - { - if (string.IsNullOrEmpty(filename)) - { - throw new ArgumentNullException("filename"); - } - - if (string.Equals(Path.GetExtension(filename), ".mblink", StringComparison.OrdinalIgnoreCase)) - { - return File.ReadAllText(filename); - } - - //return new WindowsShortcut(filename).ResolvedPath; - - var link = new ShellLink(); - ((IPersistFile)link).Load(filename, NativeMethods.STGM_READ); - // TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files. - // ((IShellLinkW)link).Resolve(hwnd, 0) - var sb = new StringBuilder(NativeMethods.MAX_PATH); - WIN32_FIND_DATA data; - ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0); - return sb.ToString(); - } - - /// - /// Creates a shortcut file pointing to a specified path - /// - /// The shortcut path. - /// The target. - /// shortcutPath - public static void CreateShortcut(string shortcutPath, string target) - { - if (string.IsNullOrEmpty(shortcutPath)) - { - throw new ArgumentNullException("shortcutPath"); - } - - if (string.IsNullOrEmpty(target)) - { - throw new ArgumentNullException("target"); - } - - File.WriteAllText(shortcutPath, target); - - //var link = new ShellLink(); - - //((IShellLinkW)link).SetPath(target); - - //((IPersistFile)link).Save(shortcutPath, true); - } - - private static readonly Dictionary ShortcutExtensionsDictionary = new[] { ".mblink", ".lnk" } - .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - - /// - /// Determines whether the specified filename is shortcut. - /// - /// The filename. - /// true if the specified filename is shortcut; otherwise, false. - /// filename - public static bool IsShortcut(string filename) - { - if (string.IsNullOrEmpty(filename)) - { - throw new ArgumentNullException("filename"); - } - - var extension = Path.GetExtension(filename); - - return !string.IsNullOrEmpty(extension) && ShortcutExtensionsDictionary.ContainsKey(extension); - } - /// /// Copies all. /// @@ -234,151 +88,5 @@ namespace MediaBrowser.Controller.IO CopyAll(dir, Path.Combine(target, Path.GetFileName(dir))); } } - - /// - /// Parses the ini file. - /// - /// The path. - /// NameValueCollection. - public static NameValueCollection ParseIniFile(string path) - { - var values = new NameValueCollection(); - - foreach (var line in File.ReadAllLines(path)) - { - var data = line.Split('='); - - if (data.Length < 2) continue; - - var key = data[0]; - - var value = data.Length == 2 ? data[1] : string.Join(string.Empty, data, 1, data.Length - 1); - - values[key] = value; - } - - return values; - } - } - - /// - /// Adapted from http://stackoverflow.com/questions/309495/windows-shortcut-lnk-parser-in-java - /// - internal class WindowsShortcut - { - public bool IsDirectory { get; private set; } - public bool IsLocal { get; private set; } - public string ResolvedPath { get; private set; } - - public WindowsShortcut(string file) - { - ParseLink(File.ReadAllBytes(file), Encoding.UTF8); - } - - private static bool isMagicPresent(byte[] link) - { - - const int magic = 0x0000004C; - const int magic_offset = 0x00; - - return link.Length >= 32 && bytesToDword(link, magic_offset) == magic; - } - - /** - * Gobbles up link data by parsing it and storing info in member fields - * @param link all the bytes from the .lnk file - */ - private void ParseLink(byte[] link, Encoding encoding) - { - if (!isMagicPresent(link)) - throw new IOException("Invalid shortcut; magic is missing", 0); - - // get the flags byte - byte flags = link[0x14]; - - // get the file attributes byte - const int file_atts_offset = 0x18; - byte file_atts = link[file_atts_offset]; - byte is_dir_mask = (byte)0x10; - if ((file_atts & is_dir_mask) > 0) - { - IsDirectory = true; - } - else - { - IsDirectory = false; - } - - // if the shell settings are present, skip them - const int shell_offset = 0x4c; - const byte has_shell_mask = (byte)0x01; - int shell_len = 0; - if ((flags & has_shell_mask) > 0) - { - // the plus 2 accounts for the length marker itself - shell_len = bytesToWord(link, shell_offset) + 2; - } - - // get to the file settings - int file_start = 0x4c + shell_len; - - const int file_location_info_flag_offset_offset = 0x08; - int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset]; - IsLocal = (file_location_info_flag & 2) == 0; - // get the local volume and local system values - //final int localVolumeTable_offset_offset = 0x0C; - const int basename_offset_offset = 0x10; - const int networkVolumeTable_offset_offset = 0x14; - const int finalname_offset_offset = 0x18; - int finalname_offset = link[file_start + finalname_offset_offset] + file_start; - String finalname = getNullDelimitedString(link, finalname_offset, encoding); - if (IsLocal) - { - int basename_offset = link[file_start + basename_offset_offset] + file_start; - String basename = getNullDelimitedString(link, basename_offset, encoding); - ResolvedPath = basename + finalname; - } - else - { - int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start; - int shareName_offset_offset = 0x08; - int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset] - + networkVolumeTable_offset; - String shareName = getNullDelimitedString(link, shareName_offset, encoding); - ResolvedPath = shareName + "\\" + finalname; - } - } - - private static string getNullDelimitedString(byte[] bytes, int off, Encoding encoding) - { - int len = 0; - - // count bytes until the null character (0) - while (true) - { - if (bytes[off + len] == 0) - { - break; - } - len++; - } - - return encoding.GetString(bytes, off, len); - } - - /* - * convert two bytes into a short note, this is little endian because it's - * for an Intel only OS. - */ - private static int bytesToWord(byte[] bytes, int off) - { - return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff); - } - - private static int bytesToDword(byte[] bytes, int off) - { - return (bytesToWord(bytes, off + 2) << 16) | bytesToWord(bytes, off); - } - } } diff --git a/MediaBrowser.Controller/IO/IFileSystem.cs b/MediaBrowser.Controller/IO/IFileSystem.cs new file mode 100644 index 0000000000..2654c3235e --- /dev/null +++ b/MediaBrowser.Controller/IO/IFileSystem.cs @@ -0,0 +1,45 @@ +using System.IO; + +namespace MediaBrowser.Controller.IO +{ + /// + /// Interface IFileSystem + /// + public interface IFileSystem + { + /// + /// Determines whether the specified filename is shortcut. + /// + /// The filename. + /// true if the specified filename is shortcut; otherwise, false. + bool IsShortcut(string filename); + + /// + /// Resolves the shortcut. + /// + /// The filename. + /// System.String. + string ResolveShortcut(string filename); + + /// + /// Creates the shortcut. + /// + /// The shortcut path. + /// The target. + void CreateShortcut(string shortcutPath, string target); + + /// + /// Gets the file system info. + /// + /// The path. + /// FileSystemInfo. + FileSystemInfo GetFileSystemInfo(string path); + + /// + /// Gets the valid filename. + /// + /// The filename. + /// System.String. + string GetValidFilename(string filename); + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 8154cb0a28..77db7d2c24 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -94,6 +94,7 @@ + @@ -141,7 +142,6 @@ - diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index dbac826b34..7961834d67 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -126,10 +126,11 @@ namespace MediaBrowser.Controller.Resolvers /// /// Ensures DateCreated and DateModified have values /// + /// The file system. /// The item. /// The args. /// if set to true [include creation time]. - public static void EnsureDates(BaseItem item, ItemResolveArgs args, bool includeCreationTime) + public static void EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args, bool includeCreationTime) { if (!Path.IsPathRooted(item.Path)) { @@ -152,7 +153,7 @@ namespace MediaBrowser.Controller.Resolvers } else { - var fileData = FileSystem.GetFileSystemInfo(item.Path); + var fileData = fileSystem.GetFileSystemInfo(item.Path); if (fileData.Exists) { diff --git a/MediaBrowser.Providers/ImagesByNameProvider.cs b/MediaBrowser.Providers/ImagesByNameProvider.cs index e4bfee6e33..2fdfe199ab 100644 --- a/MediaBrowser.Providers/ImagesByNameProvider.cs +++ b/MediaBrowser.Providers/ImagesByNameProvider.cs @@ -18,9 +18,12 @@ namespace MediaBrowser.Providers /// public class ImagesByNameProvider : ImageFromMediaLocationProvider { - public ImagesByNameProvider(ILogManager logManager, IServerConfigurationManager configurationManager) + private readonly IFileSystem _fileSystem; + + public ImagesByNameProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { + _fileSystem = fileSystem; } public override ItemUpdateType ItemUpdateType @@ -150,7 +153,7 @@ namespace MediaBrowser.Providers /// System.String. protected string GetLocation(BaseItem item) { - var name = FileSystem.GetValidFilename(item.Name); + var name = _fileSystem.GetValidFilename(item.Name); return Path.Combine(ConfigurationManager.ApplicationPaths.GeneralPath, name); } diff --git a/MediaBrowser.Providers/RefreshIntrosTask.cs b/MediaBrowser.Providers/RefreshIntrosTask.cs index 3ff2b40a6b..5d87dc90f8 100644 --- a/MediaBrowser.Providers/RefreshIntrosTask.cs +++ b/MediaBrowser.Providers/RefreshIntrosTask.cs @@ -22,15 +22,18 @@ namespace MediaBrowser.Providers /// private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// /// The library manager. /// The logger. - public RefreshIntrosTask(ILibraryManager libraryManager, ILogger logger) + public RefreshIntrosTask(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem) { _libraryManager = libraryManager; _logger = logger; + _fileSystem = fileSystem; } /// @@ -77,7 +80,7 @@ namespace MediaBrowser.Providers /// Task. private async Task RefreshIntro(string path, CancellationToken cancellationToken) { - var item = _libraryManager.ResolvePath(FileSystem.GetFileSystemInfo(path)); + var item = _libraryManager.ResolvePath(_fileSystem.GetFileSystemInfo(path)); if (item == null) { diff --git a/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs b/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs index a4d99ae17e..0ddd407cf0 100644 --- a/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs +++ b/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs @@ -87,10 +87,12 @@ namespace MediaBrowser.Server.Implementations.IO private ILibraryManager LibraryManager { get; set; } private IServerConfigurationManager ConfigurationManager { get; set; } + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// - public DirectoryWatchers(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager) + public DirectoryWatchers(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) { if (taskManager == null) { @@ -101,6 +103,7 @@ namespace MediaBrowser.Server.Implementations.IO TaskManager = taskManager; Logger = logManager.GetLogger("DirectoryWatchers"); ConfigurationManager = configurationManager; + _fileSystem = fileSystem; SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; } @@ -418,7 +421,7 @@ namespace MediaBrowser.Server.Implementations.IO { try { - var data = FileSystem.GetFileSystemInfo(path); + var data = _fileSystem.GetFileSystemInfo(path); if (!data.Exists || data.Attributes.HasFlag(FileAttributes.Directory) diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index cbe15aa626..eed191aab2 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -169,6 +169,8 @@ namespace MediaBrowser.Server.Implementations.Library private readonly ConcurrentDictionary _userRootFolders = new ConcurrentDictionary(); + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// @@ -177,7 +179,7 @@ namespace MediaBrowser.Server.Implementations.Library /// The user manager. /// The configuration manager. /// The user data repository. - public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func directoryWatchersFactory) + public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func directoryWatchersFactory, IFileSystem fileSystem) { _logger = logger; _taskManager = taskManager; @@ -185,6 +187,7 @@ namespace MediaBrowser.Server.Implementations.Library ConfigurationManager = configurationManager; _userDataRepository = userDataRepository; _directoryWatchersFactory = directoryWatchersFactory; + _fileSystem = fileSystem; ByReferenceItems = new ConcurrentDictionary(); ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated; @@ -417,7 +420,7 @@ namespace MediaBrowser.Server.Implementations.Library if (item != null) { - ResolverHelper.SetInitialItemValues(item, args); + ResolverHelper.SetInitialItemValues(item, args, _fileSystem); // Now handle the issue with posibly having the same item referenced from multiple physical // places within the library. Be sure we always end up with just one instance. @@ -482,7 +485,7 @@ namespace MediaBrowser.Server.Implementations.Library // When resolving the root, we need it's grandchildren (children of user views) var flattenFolderDepth = isPhysicalRoot ? 2 : 0; - args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf); + args.FileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf); // Need to remove subpaths that may have been resolved from shortcuts // Example: if \\server\movies exists, then strip out \\server\movies\action @@ -701,7 +704,7 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException(); } - var validFilename = FileSystem.GetValidFilename(name).Trim(); + var validFilename = _fileSystem.GetValidFilename(name).Trim(); string subFolderPrefix = null; @@ -1066,7 +1069,7 @@ namespace MediaBrowser.Server.Implementations.Library Name = Path.GetFileName(dir), Locations = Directory.EnumerateFiles(dir, "*.mblink", SearchOption.TopDirectoryOnly) - .Select(FileSystem.ResolveShortcut) + .Select(_fileSystem.ResolveShortcut) .OrderBy(i => i) .ToList(), @@ -1150,7 +1153,7 @@ namespace MediaBrowser.Server.Implementations.Library try { // Try to resolve the path into a video - video = ResolvePath(FileSystem.GetFileSystemInfo(info.Path)) as Video; + video = ResolvePath(_fileSystem.GetFileSystemInfo(info.Path)) as Video; if (video == null) { diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs index a6b13f8dd6..22bd2e0e6f 100644 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; using System; @@ -18,7 +19,8 @@ namespace MediaBrowser.Server.Implementations.Library /// /// The item. /// The args. - public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args) + /// The file system. + public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem) { item.ResetResolveArgs(args); @@ -48,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.Library item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1; // Make sure DateCreated and DateModified have values - EntityResolutionHelper.EnsureDates(item, args, true); + EntityResolutionHelper.EnsureDates(fileSystem, item, args, true); } /// diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 8ae5e34c28..4ec3b73122 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -47,6 +47,7 @@ using MediaBrowser.Server.Implementations.ServerManager; using MediaBrowser.Server.Implementations.Session; using MediaBrowser.Server.Implementations.WebSocket; using MediaBrowser.ServerApplication.FFMpeg; +using MediaBrowser.ServerApplication.IO; using MediaBrowser.ServerApplication.Native; using MediaBrowser.ServerApplication.Networking; using MediaBrowser.WebDashboard.Api; @@ -246,6 +247,9 @@ namespace MediaBrowser.ServerApplication RegisterSingleInstance(() => new BdInfoExaminer()); + var fileSystemManager = FileSystemFactory.CreateFileSystemManager(); + RegisterSingleInstance(fileSystemManager); + var mediaEncoderTask = RegisterMediaEncoder(); UserDataManager = new UserDataManager(LogManager); @@ -263,10 +267,10 @@ namespace MediaBrowser.ServerApplication UserManager = new UserManager(Logger, ServerConfigurationManager, UserRepository); RegisterSingleInstance(UserManager); - LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => DirectoryWatchers); + LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => DirectoryWatchers, fileSystemManager); RegisterSingleInstance(LibraryManager); - DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager); + DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, fileSystemManager); RegisterSingleInstance(DirectoryWatchers); ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager, LibraryManager); diff --git a/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs b/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs new file mode 100644 index 0000000000..b777930f3a --- /dev/null +++ b/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs @@ -0,0 +1,264 @@ +using MediaBrowser.Controller.IO; +using System; +using System.IO; +using System.Text; + +namespace MediaBrowser.ServerApplication.IO +{ + /// + /// Class CommonFileSystem + /// + public class CommonFileSystem : IFileSystem + { + /// + /// Determines whether the specified filename is shortcut. + /// + /// The filename. + /// true if the specified filename is shortcut; otherwise, false. + /// filename + public virtual bool IsShortcut(string filename) + { + if (string.IsNullOrEmpty(filename)) + { + throw new ArgumentNullException("filename"); + } + + var extension = Path.GetExtension(filename); + + return string.Equals(extension, ".mblink", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Resolves the shortcut. + /// + /// The filename. + /// System.String. + /// filename + public virtual string ResolveShortcut(string filename) + { + if (string.IsNullOrEmpty(filename)) + { + throw new ArgumentNullException("filename"); + } + + if (string.Equals(Path.GetExtension(filename), ".mblink", StringComparison.OrdinalIgnoreCase)) + { + return File.ReadAllText(filename); + } + + return null; + } + + /// + /// Creates the shortcut. + /// + /// The shortcut path. + /// The target. + /// + /// shortcutPath + /// or + /// target + /// + public void CreateShortcut(string shortcutPath, string target) + { + if (string.IsNullOrEmpty(shortcutPath)) + { + throw new ArgumentNullException("shortcutPath"); + } + + if (string.IsNullOrEmpty(target)) + { + throw new ArgumentNullException("target"); + } + + File.WriteAllText(shortcutPath, target); + } + + /// + /// Gets the file system info. + /// + /// The path. + /// FileSystemInfo. + public FileSystemInfo GetFileSystemInfo(string path) + { + // Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists + if (Path.HasExtension(path)) + { + var fileInfo = new FileInfo(path); + + if (fileInfo.Exists) + { + return fileInfo; + } + + return new DirectoryInfo(path); + } + else + { + var fileInfo = new DirectoryInfo(path); + + if (fileInfo.Exists) + { + return fileInfo; + } + + return new FileInfo(path); + } + } + + /// + /// The space char + /// + private const char SpaceChar = ' '; + /// + /// The invalid file name chars + /// + private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); + + /// + /// Takes a filename and removes invalid characters + /// + /// The filename. + /// System.String. + /// filename + public string GetValidFilename(string filename) + { + if (string.IsNullOrEmpty(filename)) + { + throw new ArgumentNullException("filename"); + } + + var builder = new StringBuilder(filename); + + foreach (var c in InvalidFileNameChars) + { + builder = builder.Replace(c, SpaceChar); + } + + return builder.ToString(); + } + } + + + /// + /// Adapted from http://stackoverflow.com/questions/309495/windows-shortcut-lnk-parser-in-java + /// + internal class WindowsShortcut + { + public bool IsDirectory { get; private set; } + public bool IsLocal { get; private set; } + public string ResolvedPath { get; private set; } + + public WindowsShortcut(string file) + { + ParseLink(File.ReadAllBytes(file), Encoding.UTF8); + } + + private static bool isMagicPresent(byte[] link) + { + + const int magic = 0x0000004C; + const int magic_offset = 0x00; + + return link.Length >= 32 && bytesToDword(link, magic_offset) == magic; + } + + /** + * Gobbles up link data by parsing it and storing info in member fields + * @param link all the bytes from the .lnk file + */ + private void ParseLink(byte[] link, Encoding encoding) + { + if (!isMagicPresent(link)) + throw new IOException("Invalid shortcut; magic is missing", 0); + + // get the flags byte + byte flags = link[0x14]; + + // get the file attributes byte + const int file_atts_offset = 0x18; + byte file_atts = link[file_atts_offset]; + byte is_dir_mask = (byte)0x10; + if ((file_atts & is_dir_mask) > 0) + { + IsDirectory = true; + } + else + { + IsDirectory = false; + } + + // if the shell settings are present, skip them + const int shell_offset = 0x4c; + const byte has_shell_mask = (byte)0x01; + int shell_len = 0; + if ((flags & has_shell_mask) > 0) + { + // the plus 2 accounts for the length marker itself + shell_len = bytesToWord(link, shell_offset) + 2; + } + + // get to the file settings + int file_start = 0x4c + shell_len; + + const int file_location_info_flag_offset_offset = 0x08; + int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset]; + IsLocal = (file_location_info_flag & 2) == 0; + // get the local volume and local system values + //final int localVolumeTable_offset_offset = 0x0C; + const int basename_offset_offset = 0x10; + const int networkVolumeTable_offset_offset = 0x14; + const int finalname_offset_offset = 0x18; + int finalname_offset = link[file_start + finalname_offset_offset] + file_start; + String finalname = getNullDelimitedString(link, finalname_offset, encoding); + if (IsLocal) + { + int basename_offset = link[file_start + basename_offset_offset] + file_start; + String basename = getNullDelimitedString(link, basename_offset, encoding); + ResolvedPath = basename + finalname; + } + else + { + int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start; + int shareName_offset_offset = 0x08; + int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset] + + networkVolumeTable_offset; + String shareName = getNullDelimitedString(link, shareName_offset, encoding); + ResolvedPath = shareName + "\\" + finalname; + } + } + + private static string getNullDelimitedString(byte[] bytes, int off, Encoding encoding) + { + int len = 0; + + // count bytes until the null character (0) + while (true) + { + if (bytes[off + len] == 0) + { + break; + } + len++; + } + + return encoding.GetString(bytes, off, len); + } + + /* + * convert two bytes into a short note, this is little endian because it's + * for an Intel only OS. + */ + private static int bytesToWord(byte[] bytes, int off) + { + return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff); + } + + private static int bytesToDword(byte[] bytes, int off) + { + return (bytesToWord(bytes, off + 2) << 16) | bytesToWord(bytes, off); + } + + } + +} diff --git a/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs b/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs new file mode 100644 index 0000000000..78a9338a4a --- /dev/null +++ b/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs @@ -0,0 +1,19 @@ +using MediaBrowser.Controller.IO; + +namespace MediaBrowser.ServerApplication.IO +{ + /// + /// Class FileSystemFactory + /// + public static class FileSystemFactory + { + /// + /// Creates the file system manager. + /// + /// IFileSystem. + public static IFileSystem CreateFileSystemManager() + { + return new NativeFileSystem(); + } + } +} diff --git a/MediaBrowser.Controller/IO/NativeMethods.cs b/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs similarity index 91% rename from MediaBrowser.Controller/IO/NativeMethods.cs rename to MediaBrowser.ServerApplication/IO/NativeFileSystem.cs index 97c7dfe875..b5ceec6196 100644 --- a/MediaBrowser.Controller/IO/NativeMethods.cs +++ b/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs @@ -4,8 +4,46 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; -namespace MediaBrowser.Controller.IO +namespace MediaBrowser.ServerApplication.IO { + public class NativeFileSystem : CommonFileSystem + { + public override bool IsShortcut(string filename) + { + return base.IsShortcut(filename) || + string.Equals(Path.GetExtension(filename), ".lnk", StringComparison.OrdinalIgnoreCase); + } + + public override string ResolveShortcut(string filename) + { + var path = base.ResolveShortcut(filename); + + if (!string.IsNullOrEmpty(path)) + { + return path; + } + + if (string.Equals(Path.GetExtension(filename), ".lnk", StringComparison.OrdinalIgnoreCase)) + { + return ResolveLnk(filename); + } + + return null; + } + + private string ResolveLnk(string filename) + { + var link = new ShellLink(); + ((IPersistFile)link).Load(filename, NativeMethods.STGM_READ); + // TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files. + // ((IShellLinkW)link).Resolve(hwnd, 0) + var sb = new StringBuilder(NativeMethods.MAX_PATH); + WIN32_FIND_DATA data; + ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0); + return sb.ToString(); + } + } + /// /// Class NativeMethods /// @@ -46,7 +84,6 @@ namespace MediaBrowser.Controller.IO public uint dwHighDateTime; } - /// /// Struct WIN32_FIND_DATA /// @@ -184,7 +221,6 @@ namespace MediaBrowser.Controller.IO SLR_INVOKE_MSI = 0x80 } - /// /// The IShellLink interface allows Shell links to be created, modified, and resolved /// @@ -311,7 +347,6 @@ namespace MediaBrowser.Controller.IO void GetClassID(out Guid pClassID); } - /// /// Interface IPersistFile /// @@ -374,4 +409,5 @@ namespace MediaBrowser.Controller.IO public class ShellLink { } + } diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 96b404e004..d191247ee0 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -188,9 +188,12 @@ + + + From ee912d4113eefca8dd374531e6f0457116684f0b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 30 Oct 2013 11:07:30 -0400 Subject: [PATCH 19/25] #567 - native shortcuts --- MediaBrowser.ServerApplication/ApplicationHost.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 4ec3b73122..c277ee5098 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -170,6 +170,8 @@ namespace MediaBrowser.ServerApplication private Task _httpServerCreationTask; + private IFileSystem FileSystemManager { get; set; } + /// /// Initializes a new instance of the class. /// @@ -247,8 +249,8 @@ namespace MediaBrowser.ServerApplication RegisterSingleInstance(() => new BdInfoExaminer()); - var fileSystemManager = FileSystemFactory.CreateFileSystemManager(); - RegisterSingleInstance(fileSystemManager); + FileSystemManager = FileSystemFactory.CreateFileSystemManager(); + RegisterSingleInstance(FileSystemManager); var mediaEncoderTask = RegisterMediaEncoder(); @@ -267,10 +269,10 @@ namespace MediaBrowser.ServerApplication UserManager = new UserManager(Logger, ServerConfigurationManager, UserRepository); RegisterSingleInstance(UserManager); - LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => DirectoryWatchers, fileSystemManager); + LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => DirectoryWatchers, FileSystemManager); RegisterSingleInstance(LibraryManager); - DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, fileSystemManager); + DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(DirectoryWatchers); ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager, LibraryManager); @@ -416,6 +418,7 @@ namespace MediaBrowser.ServerApplication User.UserManager = UserManager; LocalizedStrings.ApplicationPaths = ApplicationPaths; Folder.UserManager = UserManager; + BaseItem.FileSystem = FileSystemManager; } /// From 2f99b2e1e60f7a36c7f9c420c07fb608bb231fbf Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 30 Oct 2013 11:55:55 -0400 Subject: [PATCH 20/25] fixes #566 - Add permission configuration for remote control --- MediaBrowser.Api/SessionsService.cs | 15 ++++++++++++++- .../Configuration/UserConfiguration.cs | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Api/SessionsService.cs b/MediaBrowser.Api/SessionsService.cs index 5888d9fba3..a3f7e3037c 100644 --- a/MediaBrowser.Api/SessionsService.cs +++ b/MediaBrowser.Api/SessionsService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Session; using ServiceStack.ServiceHost; @@ -182,16 +183,18 @@ namespace MediaBrowser.Api private readonly ISessionManager _sessionManager; private readonly IDtoService _dtoService; + private readonly IUserManager _userManager; /// /// Initializes a new instance of the class. /// /// The session manager. /// The dto service. - public SessionsService(ISessionManager sessionManager, IDtoService dtoService) + public SessionsService(ISessionManager sessionManager, IDtoService dtoService, IUserManager userManager) { _sessionManager = sessionManager; _dtoService = dtoService; + _userManager = userManager; } /// @@ -208,6 +211,16 @@ namespace MediaBrowser.Api result = result.Where(i => i.SupportsRemoteControl == request.SupportsRemoteControl.Value); } + if (request.ControllableByUserId.HasValue) + { + var user = _userManager.GetUserById(request.ControllableByUserId.Value); + + if (!user.Configuration.EnableRemoteControlOfOtherUsers) + { + result = result.Where(i => i.User == null || i.User.Id == request.ControllableByUserId.Value); + } + } + return ToOptimizedResult(result.Select(_dtoService.GetSessionInfoDto).ToList()); } diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 43561cd749..b736474e0d 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -58,6 +58,7 @@ namespace MediaBrowser.Model.Configuration public bool DisplayMissingEpisodes { get; set; } public bool DisplayUnairedEpisodes { get; set; } + public bool EnableRemoteControlOfOtherUsers { get; set; } /// /// Initializes a new instance of the class. @@ -65,6 +66,7 @@ namespace MediaBrowser.Model.Configuration public UserConfiguration() { IsAdministrator = true; + EnableRemoteControlOfOtherUsers = true; BlockNotRated = false; } } From 524150331c1811e8ced5ff8444e0d0197bc6419a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 30 Oct 2013 17:33:27 -0400 Subject: [PATCH 21/25] beginning manual image providers --- MediaBrowser.Api/LibraryService.cs | 30 +++- .../MediaBrowser.Controller.csproj | 1 + .../Providers/IImageProvider.cs | 38 +++++ .../Providers/IProviderManager.cs | 13 +- .../MediaBrowser.Model.Portable.csproj | 3 + .../MediaBrowser.Model.net35.csproj | 3 + MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + .../Providers/RemoteImageInfo.cs | 51 +++++++ .../MediaBrowser.Providers.csproj | 1 + .../Movies/ManualMovieDbImageProvider.cs | 133 ++++++++++++++++++ .../Movies/MovieDbImagesProvider.cs | 62 +++----- .../Providers/ProviderManager.cs | 64 ++++++++- .../ApplicationHost.cs | 4 +- 13 files changed, 355 insertions(+), 49 deletions(-) create mode 100644 MediaBrowser.Controller/Providers/IImageProvider.cs create mode 100644 MediaBrowser.Model/Providers/RemoteImageInfo.cs create mode 100644 MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index 202d372e83..5b133fbd3e 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -5,8 +5,10 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; @@ -32,6 +34,21 @@ namespace MediaBrowser.Api public string Id { get; set; } } + [Route("/Items/{Id}/RemoteImages/{Type}", "GET")] + [Api(Description = "Gets available remote images for an item")] + public class GetRemoteImages : IReturn> + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + + [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public ImageType Type { get; set; } + } + /// /// Class GetCriticReviews /// @@ -208,6 +225,7 @@ namespace MediaBrowser.Api private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IProviderManager _providerManager; private readonly IDtoService _dtoService; @@ -215,13 +233,14 @@ namespace MediaBrowser.Api /// Initializes a new instance of the class. /// public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, - IDtoService dtoService, IUserDataManager userDataManager) + IDtoService dtoService, IUserDataManager userDataManager, IProviderManager providerManager) { _itemRepo = itemRepo; _libraryManager = libraryManager; _userManager = userManager; _dtoService = dtoService; _userDataManager = userDataManager; + _providerManager = providerManager; } public object Get(GetFile request) @@ -240,6 +259,15 @@ namespace MediaBrowser.Api return ToStaticFileResult(item.Path); } + public object Get(GetRemoteImages request) + { + var item = _dtoService.GetItemByDtoId(request.Id); + + var result = _providerManager.GetAvailableRemoteImages(item, request.Type, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + /// /// Gets the specified request. /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 77db7d2c24..c379714aa3 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -109,6 +109,7 @@ + diff --git a/MediaBrowser.Controller/Providers/IImageProvider.cs b/MediaBrowser.Controller/Providers/IImageProvider.cs new file mode 100644 index 0000000000..0764794388 --- /dev/null +++ b/MediaBrowser.Controller/Providers/IImageProvider.cs @@ -0,0 +1,38 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Providers +{ + /// + /// Interface IImageProvider + /// + public interface IImageProvider + { + /// + /// Gets the name. + /// + /// The name. + string Name { get; } + + /// + /// Supportses the specified item. + /// + /// The item. + /// Type of the image. + /// true if XXXX, false otherwise + bool Supports(BaseItem item, ImageType imageType); + + /// + /// Gets the available images. + /// + /// The item. + /// Type of the image. + /// The cancellation token. + /// Task{IEnumerable{RemoteImageInfo}}. + Task> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 6a4d132b76..2eb2be6db1 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; using System.Collections.Generic; using System.IO; using System.Threading; @@ -52,6 +53,16 @@ namespace MediaBrowser.Controller.Providers /// Adds the metadata providers. /// /// The providers. - void AddParts(IEnumerable providers); + /// The image providers. + void AddParts(IEnumerable providers, IEnumerable imageProviders); + + /// + /// Gets the available remote images. + /// + /// The item. + /// The type. + /// The cancellation token. + /// Task{IEnumerable{RemoteImageInfo}}. + Task> GetAvailableRemoteImages(BaseItem item, ImageType type, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index e88953b9c1..0f54073ef5 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -287,6 +287,9 @@ Plugins\PluginInfo.cs + + Providers\RemoteImageInfo.cs + Querying\ArtistsQuery.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 6a14817a60..3d4b8d2c7d 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -274,6 +274,9 @@ Plugins\PluginInfo.cs + + Providers\RemoteImageInfo.cs + Querying\ArtistsQuery.cs diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index caf89346cd..b048dc1c3e 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -59,6 +59,7 @@ + diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs new file mode 100644 index 0000000000..bb2a3cb69c --- /dev/null +++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs @@ -0,0 +1,51 @@ + +namespace MediaBrowser.Model.Providers +{ + /// + /// Class RemoteImageInfo + /// + public class RemoteImageInfo + { + /// + /// Gets or sets the name of the provider. + /// + /// The name of the provider. + public string ProviderName { get; set; } + + /// + /// Gets or sets the URL. + /// + /// The URL. + public string Url { get; set; } + + /// + /// Gets or sets the height. + /// + /// The height. + public int? Height { get; set; } + + /// + /// Gets or sets the width. + /// + /// The width. + public int? Width { get; set; } + + /// + /// Gets or sets the community rating. + /// + /// The community rating. + public double? CommunityRating { get; set; } + + /// + /// Gets or sets the vote count. + /// + /// The vote count. + public int? VoteCount { get; set; } + + /// + /// Gets or sets the language. + /// + /// The language. + public string Language { get; set; } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 63bd8c9534..7a8975937d 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -60,6 +60,7 @@ + diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs new file mode 100644 index 0000000000..39b7fa6c67 --- /dev/null +++ b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs @@ -0,0 +1,133 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.Movies +{ + class ManualMovieDbImageProvider : IImageProvider + { + private readonly IJsonSerializer _jsonSerializer; + private readonly IServerConfigurationManager _config; + + public ManualMovieDbImageProvider(IJsonSerializer jsonSerializer, IServerConfigurationManager config) + { + _jsonSerializer = jsonSerializer; + _config = config; + } + + public string Name + { + get { return "TheMovieDB"; } + } + + public bool Supports(BaseItem item, ImageType imageType) + { + if (MovieDbImagesProvider.SupportsItem(item)) + { + return imageType == ImageType.Primary || imageType == ImageType.Backdrop; + } + + return false; + } + + public async Task> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken) + { + var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + + var results = MovieDbImagesProvider.FetchImages(item, _jsonSerializer); + + var tmdbImageUrl = tmdbSettings.images.base_url + "original"; + + if (imageType == ImageType.Primary) + { + var sources = GetPosters(results, item); + + return sources.Select(i => new RemoteImageInfo + { + Url = tmdbImageUrl + i.file_path, + CommunityRating = i.vote_average, + VoteCount = i.vote_count, + Width = i.width, + Height = i.height, + Language = i.iso_639_1, + ProviderName = Name + }); + } + + if (imageType == ImageType.Backdrop) + { + var sources = GetBackdrops(results, item); + + return sources.Select(i => new RemoteImageInfo + { + Url = tmdbImageUrl + i.file_path, + CommunityRating = i.vote_average, + VoteCount = i.vote_count, + Width = i.width, + Height = i.height, + ProviderName = Name + }); + } + + throw new ArgumentException("Unrecognized ImageType: " + imageType); + } + + /// + /// Gets the posters. + /// + /// The images. + /// The item. + /// IEnumerable{MovieDbProvider.Poster}. + public IEnumerable GetPosters(MovieDbProvider.Images images, BaseItem item) + { + var language = _config.Configuration.PreferredMetadataLanguage; + + var eligiblePosters = images.posters == null ? + new List() : + images.posters.Where(i => i.width >= _config.Configuration.MinMoviePosterWidth) + .ToList(); + + return eligiblePosters.OrderByDescending(i => i.vote_average) + .ThenByDescending(i => + { + if (string.Equals(language, i.iso_639_1, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + if (string.Equals("en", i.iso_639_1, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } + if (string.IsNullOrEmpty(i.iso_639_1)) + { + return 1; + } + return 0; + }) + .ToList(); + } + + /// + /// Gets the backdrops. + /// + /// The images. + /// The item. + /// IEnumerable{MovieDbProvider.Backdrop}. + public IEnumerable GetBackdrops(MovieDbProvider.Images images, BaseItem item) + { + var eligibleBackdrops = images.backdrops == null ? new List() : + images.backdrops.Where(i => i.width >= _config.Configuration.MinMovieBackdropWidth) + .ToList(); + + return eligibleBackdrops.OrderByDescending(i => i.vote_average); + } + } +} diff --git a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs index 52e6c214f9..0fd61a96be 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs @@ -65,6 +65,11 @@ namespace MediaBrowser.Providers.Movies /// The item. /// true if XXXX, false otherwise public override bool Supports(BaseItem item) + { + return SupportsItem(item); + } + + public static bool SupportsItem(BaseItem item) { var trailer = item as Trailer; @@ -180,7 +185,7 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(id)) { - var images = FetchImages(item); + var images = FetchImages(item, _jsonSerializer); if (images != null) { @@ -196,8 +201,9 @@ namespace MediaBrowser.Providers.Movies /// Fetches the images. /// /// The item. + /// The json serializer. /// Task{MovieImages}. - private MovieDbProvider.Images FetchImages(BaseItem item) + internal static MovieDbProvider.Images FetchImages(BaseItem item, IJsonSerializer jsonSerializer) { var path = MovieDbProvider.Current.GetDataFilePath(item, "default"); @@ -207,7 +213,7 @@ namespace MediaBrowser.Providers.Movies if (fileInfo.Exists) { - return _jsonSerializer.DeserializeFromFile(path).images; + return jsonSerializer.DeserializeFromFile(path).images; } } @@ -227,13 +233,9 @@ namespace MediaBrowser.Providers.Movies var status = ProviderRefreshStatus.Success; - var eligiblePosters = images.posters == null ? - new List() : - images.posters.Where(i => i.width >= ConfigurationManager.Configuration.MinMoviePosterWidth) + var eligiblePosters = new ManualMovieDbImageProvider(_jsonSerializer, ConfigurationManager).GetPosters(images, item) .ToList(); - eligiblePosters = eligiblePosters.OrderByDescending(i => i.vote_average).ToList(); - // poster if (eligiblePosters.Count > 0 && !item.HasImage(ImageType.Primary)) { @@ -242,48 +244,24 @@ namespace MediaBrowser.Providers.Movies var tmdbImageUrl = tmdbSettings.images.base_url + "original"; // get highest rated poster for our language - var poster = eligiblePosters.FirstOrDefault(p => string.Equals(p.iso_639_1, ConfigurationManager.Configuration.PreferredMetadataLanguage, StringComparison.OrdinalIgnoreCase)); + var poster = eligiblePosters[0]; - if (poster == null) + var url = tmdbImageUrl + poster.file_path; + + var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions { - // couldn't find our specific language, find english - poster = eligiblePosters.FirstOrDefault(p => string.Equals(p.iso_639_1, "en", StringComparison.OrdinalIgnoreCase)); - } + Url = url, + CancellationToken = cancellationToken - if (poster == null) - { - //still couldn't find it - try highest rated null one - poster = eligiblePosters.FirstOrDefault(p => p.iso_639_1 == null); - } + }).ConfigureAwait(false); - if (poster == null) - { - //finally - just get the highest rated one - poster = eligiblePosters.FirstOrDefault(); - } - - if (poster != null) - { - var url = tmdbImageUrl + poster.file_path; - - var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken - - }).ConfigureAwait(false); - - await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(poster.file_path), ImageType.Primary, null, url, cancellationToken) - .ConfigureAwait(false); - - } + await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(poster.file_path), ImageType.Primary, null, url, cancellationToken) + .ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); - var eligibleBackdrops = images.backdrops == null ? new List() : - images.backdrops.Where(i => i.width >= ConfigurationManager.Configuration.MinMovieBackdropWidth) - .ToList(); + var eligibleBackdrops = new ManualMovieDbImageProvider(_jsonSerializer, ConfigurationManager).GetBackdrops(images, item).ToList(); var backdropLimit = ConfigurationManager.Configuration.MaxBackdrops; diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index 19230cecdb..1b086a0f91 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -13,6 +13,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Server.Implementations.Providers { @@ -48,6 +49,8 @@ namespace MediaBrowser.Server.Implementations.Providers /// The metadata providers enumerable. private BaseMetadataProvider[] MetadataProviders { get; set; } + private IImageProvider[] ImageProviders { get; set; } + /// /// Initializes a new instance of the class. /// @@ -55,8 +58,7 @@ namespace MediaBrowser.Server.Implementations.Providers /// The configuration manager. /// The directory watchers. /// The log manager. - /// The library manager. - public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager, ILibraryManager libraryManager) + public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager) { _logger = logManager.GetLogger("ProviderManager"); _httpClient = httpClient; @@ -68,9 +70,12 @@ namespace MediaBrowser.Server.Implementations.Providers /// Adds the metadata providers. /// /// The providers. - public void AddParts(IEnumerable providers) + /// The image providers. + public void AddParts(IEnumerable providers, IEnumerable imageProviders) { MetadataProviders = providers.OrderBy(e => e.Priority).ToArray(); + + ImageProviders = imageProviders.ToArray(); } /// @@ -344,5 +349,58 @@ namespace MediaBrowser.Server.Implementations.Providers { return new ImageSaver(ConfigurationManager, _directoryWatchers).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken); } + + /// + /// Gets the available remote images. + /// + /// The item. + /// The type. + /// The cancellation token. + /// Task{IEnumerable{RemoteImageInfo}}. + public async Task> GetAvailableRemoteImages(BaseItem item, ImageType type, CancellationToken cancellationToken) + { + var providers = GetSupportedImageProviders(item, type); + + var tasks = providers.Select(i => Task.Run(async () => + { + try + { + var result = await i.GetAvailableImages(item, type, cancellationToken).ConfigureAwait(false); + return result.ToList(); + } + catch (Exception ex) + { + _logger.ErrorException("{0} failed in GetAvailableImages for type {1}", ex, i.GetType().Name, item.GetType().Name); + return new List(); + } + })); + + var results = await Task.WhenAll(tasks).ConfigureAwait(false); + + return results.SelectMany(i => i); + } + + /// + /// Gets the supported image providers. + /// + /// The item. + /// The type. + /// IEnumerable{IImageProvider}. + private IEnumerable GetSupportedImageProviders(BaseItem item, ImageType type) + { + return ImageProviders.Where(i => + { + try + { + return i.Supports(item, type); + } + catch (Exception ex) + { + _logger.ErrorException("{0} failed in Supports for type {1}", ex, i.GetType().Name, item.GetType().Name); + return false; + } + + }); + } } } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index c277ee5098..4aac1bb919 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -275,7 +275,7 @@ namespace MediaBrowser.ServerApplication DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(DirectoryWatchers); - ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager, LibraryManager); + ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager); RegisterSingleInstance(ProviderManager); RegisterSingleInstance(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager)); @@ -449,7 +449,7 @@ namespace MediaBrowser.ServerApplication GetExports(), GetExports()); - ProviderManager.AddParts(GetExports()); + ProviderManager.AddParts(GetExports(), GetExports()); ImageProcessor.AddParts(GetExports()); From d5baaa1f67975c21417ebaaa193e17192e1c3936 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 30 Oct 2013 17:48:18 -0400 Subject: [PATCH 22/25] adjust image priorities --- .../Movies/ManualMovieDbImageProvider.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs index 39b7fa6c67..a8f847b79f 100644 --- a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs @@ -90,28 +90,33 @@ namespace MediaBrowser.Providers.Movies { var language = _config.Configuration.PreferredMetadataLanguage; + var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); + var eligiblePosters = images.posters == null ? new List() : images.posters.Where(i => i.width >= _config.Configuration.MinMoviePosterWidth) .ToList(); - return eligiblePosters.OrderByDescending(i => i.vote_average) - .ThenByDescending(i => + return eligiblePosters.OrderByDescending(i => { if (string.Equals(language, i.iso_639_1, StringComparison.OrdinalIgnoreCase)) { return 3; } - if (string.Equals("en", i.iso_639_1, StringComparison.OrdinalIgnoreCase)) + if (!isLanguageEn) { - return 2; + if (string.Equals("en", i.iso_639_1, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } } if (string.IsNullOrEmpty(i.iso_639_1)) { - return 1; + return isLanguageEn ? 3 : 2; } return 0; }) + .ThenByDescending(i => i.vote_average) .ToList(); } From 579b507f7fa322fdf8a746b6e787015d0567e2a6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 30 Oct 2013 19:15:58 -0400 Subject: [PATCH 23/25] use IFileSystem interface to get creation time --- MediaBrowser.Controller/IO/FileSystem.cs | 21 --------------- MediaBrowser.Controller/IO/IFileSystem.cs | 10 ++++++- .../Resolvers/EntityResolutionHelper.cs | 6 ++--- .../ImagesByNameProvider.cs | 2 +- .../Library/LibraryManager.cs | 2 +- .../ApplicationHost.cs | 2 +- .../IO/CommonFileSystem.cs | 27 +++++++++++++++++++ .../IO/FileSystemFactory.cs | 5 ++-- .../IO/NativeFileSystem.cs | 8 +++++- 9 files changed, 52 insertions(+), 31 deletions(-) diff --git a/MediaBrowser.Controller/IO/FileSystem.cs b/MediaBrowser.Controller/IO/FileSystem.cs index f31fc53de5..b08e8da274 100644 --- a/MediaBrowser.Controller/IO/FileSystem.cs +++ b/MediaBrowser.Controller/IO/FileSystem.cs @@ -30,27 +30,6 @@ namespace MediaBrowser.Controller.IO } } - /// - /// Gets the creation time UTC. - /// - /// The info. - /// The logger. - /// DateTime. - public static DateTime GetCreationTimeUtc(FileSystemInfo info, ILogger logger) - { - // This could throw an error on some file systems that have dates out of range - - try - { - return info.CreationTimeUtc; - } - catch (Exception ex) - { - logger.ErrorException("Error determining CreationTimeUtc for {0}", ex, info.FullName); - return DateTime.MinValue; - } - } - /// /// Copies all. /// diff --git a/MediaBrowser.Controller/IO/IFileSystem.cs b/MediaBrowser.Controller/IO/IFileSystem.cs index 2654c3235e..cbc5c7bf34 100644 --- a/MediaBrowser.Controller/IO/IFileSystem.cs +++ b/MediaBrowser.Controller/IO/IFileSystem.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; namespace MediaBrowser.Controller.IO { @@ -41,5 +42,12 @@ namespace MediaBrowser.Controller.IO /// The filename. /// System.String. string GetValidFilename(string filename); + + /// + /// Gets the creation time UTC. + /// + /// The info. + /// DateTime. + DateTime GetCreationTimeUtc(FileSystemInfo info); } } diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index 7961834d67..5d61e5eb9c 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -146,7 +146,7 @@ namespace MediaBrowser.Controller.Resolvers { if (includeCreationTime) { - item.DateCreated = childData.CreationTimeUtc; + item.DateCreated = fileSystem.GetCreationTimeUtc(childData); } item.DateModified = childData.LastWriteTimeUtc; @@ -159,7 +159,7 @@ namespace MediaBrowser.Controller.Resolvers { if (includeCreationTime) { - item.DateCreated = fileData.CreationTimeUtc; + item.DateCreated = fileSystem.GetCreationTimeUtc(fileData); } item.DateModified = fileData.LastWriteTimeUtc; } @@ -169,7 +169,7 @@ namespace MediaBrowser.Controller.Resolvers { if (includeCreationTime) { - item.DateCreated = args.FileInfo.CreationTimeUtc; + item.DateCreated = fileSystem.GetCreationTimeUtc(args.FileInfo); } item.DateModified = args.FileInfo.LastWriteTimeUtc; } diff --git a/MediaBrowser.Providers/ImagesByNameProvider.cs b/MediaBrowser.Providers/ImagesByNameProvider.cs index 2fdfe199ab..8c5b3635d1 100644 --- a/MediaBrowser.Providers/ImagesByNameProvider.cs +++ b/MediaBrowser.Providers/ImagesByNameProvider.cs @@ -114,7 +114,7 @@ namespace MediaBrowser.Providers return files.Select(f => { var lastWriteTime = FileSystem.GetLastWriteTimeUtc(f, Logger); - var creationTime = FileSystem.GetCreationTimeUtc(f, Logger); + var creationTime = _fileSystem.GetCreationTimeUtc(f); return creationTime > lastWriteTime ? creationTime : lastWriteTime; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index eed191aab2..0c5d55a3b7 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -771,7 +771,7 @@ namespace MediaBrowser.Server.Implementations.Library { Name = name, Id = id, - DateCreated = fileInfo.CreationTimeUtc, + DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo), DateModified = fileInfo.LastWriteTimeUtc, Path = path }; diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 4aac1bb919..196d065805 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -249,7 +249,7 @@ namespace MediaBrowser.ServerApplication RegisterSingleInstance(() => new BdInfoExaminer()); - FileSystemManager = FileSystemFactory.CreateFileSystemManager(); + FileSystemManager = FileSystemFactory.CreateFileSystemManager(LogManager); RegisterSingleInstance(FileSystemManager); var mediaEncoderTask = RegisterMediaEncoder(); diff --git a/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs b/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs index b777930f3a..dee9bd9d77 100644 --- a/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs +++ b/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Logging; using System; using System.IO; using System.Text; @@ -10,6 +11,13 @@ namespace MediaBrowser.ServerApplication.IO /// public class CommonFileSystem : IFileSystem { + protected ILogger Logger; + + public CommonFileSystem(ILogger logger) + { + Logger = logger; + } + /// /// Determines whether the specified filename is shortcut. /// @@ -137,6 +145,25 @@ namespace MediaBrowser.ServerApplication.IO return builder.ToString(); } + + /// + /// Gets the creation time UTC. + /// + /// The info. + /// DateTime. + public DateTime GetCreationTimeUtc(FileSystemInfo info) + { + // This could throw an error on some file systems that have dates out of range + try + { + return info.CreationTimeUtc; + } + catch (Exception ex) + { + Logger.ErrorException("Error determining CreationTimeUtc for {0}", ex, info.FullName); + return DateTime.MinValue; + } + } } diff --git a/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs b/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs index 78a9338a4a..ab7802e17d 100644 --- a/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs +++ b/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Logging; namespace MediaBrowser.ServerApplication.IO { @@ -11,9 +12,9 @@ namespace MediaBrowser.ServerApplication.IO /// Creates the file system manager. /// /// IFileSystem. - public static IFileSystem CreateFileSystemManager() + public static IFileSystem CreateFileSystemManager(ILogManager logManager) { - return new NativeFileSystem(); + return new NativeFileSystem(logManager.GetLogger("FileSystem")); } } } diff --git a/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs b/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs index b5ceec6196..0101b42498 100644 --- a/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs +++ b/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs @@ -1,4 +1,5 @@ -using System; +using MediaBrowser.Model.Logging; +using System; using System.IO; using System.Runtime.InteropServices; using System.Security; @@ -8,6 +9,11 @@ namespace MediaBrowser.ServerApplication.IO { public class NativeFileSystem : CommonFileSystem { + public NativeFileSystem(ILogger logger) + : base(logger) + { + } + public override bool IsShortcut(string filename) { return base.IsShortcut(filename) || From 6c8d9192985036acb3d1fe626ed57980bb862d6a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 31 Oct 2013 10:03:23 -0400 Subject: [PATCH 24/25] replace file system calls with IFileSystem when needed --- MediaBrowser.Api/Library/LibraryHelpers.cs | 3 +- .../Library/LibraryStructureService.cs | 3 +- .../Playback/BaseStreamingService.cs | 7 +- .../Playback/Hls/AudioHlsService.cs | 7 +- .../Playback/Hls/BaseHlsService.cs | 6 +- .../Playback/Hls/VideoHlsService.cs | 7 +- .../Playback/Progressive/AudioService.cs | 7 +- .../BaseProgressiveStreamingService.cs | 7 +- .../Progressive/ProgressiveStreamWriter.cs | 6 +- .../Playback/Progressive/VideoService.cs | 7 +- MediaBrowser.Api/SystemService.cs | 10 ++- .../WebSocket/LogFileWebSocketListener.cs | 11 +-- .../BaseApplicationHost.cs | 23 ++++-- .../HttpClientManager/HttpClientManager.cs | 8 ++- .../IO/CommonFileSystem.cs | 59 +++++++++++++-- ...MediaBrowser.Common.Implementations.csproj | 1 + .../Tasks/DeleteCacheFileTask.cs | 10 ++- .../ScheduledTasks/Tasks/DeleteLogFileTask.cs | 8 ++- .../IO/IFileSystem.cs | 27 ++++++- .../MediaBrowser.Common.csproj | 1 + MediaBrowser.Controller/Entities/BaseItem.cs | 5 +- MediaBrowser.Controller/Entities/User.cs | 2 +- MediaBrowser.Controller/IO/FileData.cs | 3 +- MediaBrowser.Controller/IO/FileSystem.cs | 71 ------------------- .../MediaBrowser.Controller.csproj | 2 - .../MediaInfo/FFMpegManager.cs | 8 ++- .../Resolvers/EntityResolutionHelper.cs | 9 +-- MediaBrowser.Mono.userprefs | 17 +++-- .../FolderProviderFromXml.cs | 9 ++- .../Games/GameProviderFromXml.cs | 11 +-- .../Games/GameSystemProviderFromXml.cs | 9 ++- .../ImagesByNameProvider.cs | 3 +- .../Movies/BoxSetProviderFromXml.cs | 9 ++- .../Movies/FanArtMovieProvider.cs | 9 ++- .../Movies/FanArtMovieUpdatesPrescanTask.cs | 9 ++- .../Movies/MovieDbImagesProvider.cs | 14 ++-- .../Movies/MovieDbProvider.cs | 7 +- .../Movies/MovieProviderFromXml.cs | 10 +-- .../Movies/MovieUpdatesPrescanTask.cs | 9 ++- .../Movies/PersonProviderFromXml.cs | 9 ++- .../Movies/PersonUpdatesPreScanTask.cs | 9 ++- .../Movies/TmdbPersonProvider.cs | 9 ++- .../Music/ArtistProviderFromXml.cs | 9 ++- .../Music/FanArtAlbumProvider.cs | 9 ++- .../Music/FanArtArtistByNameProvider.cs | 12 ++-- .../Music/FanArtArtistProvider.cs | 17 ++--- .../Music/FanArtUpdatesPrescanTask.cs | 9 ++- MediaBrowser.Providers/RefreshIntrosTask.cs | 3 +- .../TV/EpisodeProviderFromXml.cs | 9 ++- .../TV/FanArtSeasonProvider.cs | 9 ++- MediaBrowser.Providers/TV/FanArtTVProvider.cs | 9 ++- .../TV/FanArtTvUpdatesPrescanTask.cs | 9 ++- .../TV/RemoteEpisodeProvider.cs | 9 ++- .../TV/RemoteSeasonProvider.cs | 9 ++- .../TV/RemoteSeriesProvider.cs | 12 ++-- .../TV/SeasonProviderFromXml.cs | 9 ++- .../TV/SeriesProviderFromXml.cs | 11 +-- MediaBrowser.Providers/TV/TvdbPrescanTask.cs | 7 +- .../TV/TvdbSeriesImageProvider.cs | 10 ++- .../Drawing/ImageHeader.cs | 9 ++- .../Drawing/ImageProcessor.cs | 23 +++--- .../Notifications/RemoteNotifications.cs | 8 ++- .../HttpServer/HttpResultFactory.cs | 9 ++- .../IO/DirectoryWatchers.cs | 17 ++++- .../Library/LibraryManager.cs | 3 +- .../Library/ResolverHelper.cs | 1 + .../Localization/LocalizationManager.cs | 11 ++- .../MediaEncoder/MediaEncoder.cs | 10 +-- .../Providers/ImageSaver.cs | 6 +- .../Providers/ProviderManager.cs | 8 ++- .../IO/FileSystemFactory.cs | 21 ++++++ .../MediaBrowser.Server.Mono.csproj | 2 + MediaBrowser.Server.Mono/Program.cs | 33 +++++++++ .../ApplicationHost.cs | 25 +++---- .../FFMpeg/FFMpegDownloader.cs | 10 +-- .../IO/FileSystemFactory.cs | 3 +- .../IO/NativeFileSystem.cs | 5 +- .../MediaBrowser.ServerApplication.csproj | 1 - .../Api/DashboardService.cs | 9 ++- MediaBrowser.sln.GhostDoc.xml | 35 +++++++++ 80 files changed, 570 insertions(+), 302 deletions(-) rename {MediaBrowser.ServerApplication => MediaBrowser.Common.Implementations}/IO/CommonFileSystem.cs (81%) rename {MediaBrowser.Controller => MediaBrowser.Common}/IO/IFileSystem.cs (61%) delete mode 100644 MediaBrowser.Controller/IO/FileSystem.cs create mode 100644 MediaBrowser.Server.Mono/IO/FileSystemFactory.cs create mode 100644 MediaBrowser.sln.GhostDoc.xml diff --git a/MediaBrowser.Api/Library/LibraryHelpers.cs b/MediaBrowser.Api/Library/LibraryHelpers.cs index 906b47458f..3d0dd4e088 100644 --- a/MediaBrowser.Api/Library/LibraryHelpers.cs +++ b/MediaBrowser.Api/Library/LibraryHelpers.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using System; diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index a5476ec8b8..0126586ab7 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 31166ae2aa..4332999015 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -59,6 +59,8 @@ namespace MediaBrowser.Api.Playback protected IMediaEncoder MediaEncoder { get; private set; } protected IDtoService DtoService { get; private set; } + protected IFileSystem FileSystem { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -67,8 +69,9 @@ namespace MediaBrowser.Api.Playback /// The library manager. /// The iso manager. /// The media encoder. - protected BaseStreamingService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService) + protected BaseStreamingService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem) { + FileSystem = fileSystem; DtoService = dtoService; ApplicationPaths = appPaths; UserManager = userManager; @@ -653,7 +656,7 @@ namespace MediaBrowser.Api.Playback var logFilePath = Path.Combine(ApplicationPaths.LogDirectoryPath, "ffmpeg-" + Guid.NewGuid() + ".txt"); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - state.LogFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous); + state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); process.Exited += (sender, args) => OnFfMpegProcessExited(process, state); diff --git a/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs b/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs index 6e36ba0ad8..6636db05d2 100644 --- a/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/AudioHlsService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.MediaInfo; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; @@ -32,8 +33,8 @@ namespace MediaBrowser.Api.Playback.Hls /// The library manager. /// The iso manager. /// The media encoder. - public AudioHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService) + public AudioHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem) { } diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 787727cd3a..3e96cf2f82 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -38,8 +38,8 @@ namespace MediaBrowser.Api.Playback.Hls /// The library manager. /// The iso manager. /// The media encoder. - protected BaseHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService) + protected BaseHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem) { } @@ -209,7 +209,7 @@ namespace MediaBrowser.Api.Playback.Hls string fileText; // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written - using (var fileStream = new FileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { using (var reader = new StreamReader(fileStream)) { diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index ecc53ce349..453039ba8a 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.MediaInfo; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; @@ -39,8 +40,8 @@ namespace MediaBrowser.Api.Playback.Hls /// The iso manager. /// The media encoder. /// The dto service. - public VideoHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService) + public VideoHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem) { } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 4165055755..36a998c16d 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.MediaInfo; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -40,8 +41,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class AudioService : BaseProgressiveStreamingService { - public AudioService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo, dtoService, imageProcessor) + public AudioService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor, IFileSystem fileSystem) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo, dtoService, imageProcessor, fileSystem) { } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 0bc147a46c..a31b6af0e2 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Api.Images; +using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -27,8 +28,8 @@ namespace MediaBrowser.Api.Playback.Progressive protected readonly IItemRepository ItemRepository; protected readonly IImageProcessor ImageProcessor; - protected BaseProgressiveStreamingService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepository, IDtoService dtoService, IImageProcessor imageProcessor) : - base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService) + protected BaseProgressiveStreamingService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepository, IDtoService dtoService, IImageProcessor imageProcessor, IFileSystem fileSystem) : + base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem) { ItemRepository = itemRepository; ImageProcessor = imageProcessor; @@ -346,7 +347,7 @@ namespace MediaBrowser.Api.Playback.Progressive ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); } - var result = new ProgressiveStreamWriter(outputPath, Logger); + var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem); result.Options["Accept-Ranges"] = "none"; result.Options["Content-Type"] = contentType; diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index c967a0d015..816cab105d 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -13,6 +13,7 @@ namespace MediaBrowser.Api.Playback.Progressive { private string Path { get; set; } private ILogger Logger { get; set; } + private readonly IFileSystem _fileSystem; /// /// The _options @@ -32,10 +33,11 @@ namespace MediaBrowser.Api.Playback.Progressive /// /// The path. /// The logger. - public ProgressiveStreamWriter(string path, ILogger logger) + public ProgressiveStreamWriter(string path, ILogger logger, IFileSystem fileSystem) { Path = path; Logger = logger; + _fileSystem = fileSystem; } /// @@ -83,7 +85,7 @@ namespace MediaBrowser.Api.Playback.Progressive var eofCount = 0; long position = 0; - using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { while (eofCount < 15) { diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index c1dd7fa017..fe5d22f584 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.MediaInfo; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -54,8 +55,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo, dtoService, imageProcessor) + public VideoService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor, IFileSystem fileSystem) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo, dtoService, imageProcessor, fileSystem) { } diff --git a/MediaBrowser.Api/SystemService.cs b/MediaBrowser.Api/SystemService.cs index 9bbd6a5883..ae6c607954 100644 --- a/MediaBrowser.Api/SystemService.cs +++ b/MediaBrowser.Api/SystemService.cs @@ -1,6 +1,8 @@ using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; @@ -75,6 +77,9 @@ namespace MediaBrowser.Api /// private readonly IServerConfigurationManager _configurationManager; + private readonly IFileSystem _fileSystem; + + /// /// Initializes a new instance of the class. /// @@ -82,7 +87,7 @@ namespace MediaBrowser.Api /// The app host. /// The configuration manager. /// jsonSerializer - public SystemService(IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IServerConfigurationManager configurationManager) + public SystemService(IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base() { if (jsonSerializer == null) @@ -96,6 +101,7 @@ namespace MediaBrowser.Api _appHost = appHost; _configurationManager = configurationManager; + _fileSystem = fileSystem; _jsonSerializer = jsonSerializer; } @@ -118,7 +124,7 @@ namespace MediaBrowser.Api /// System.Object. public object Get(GetConfiguration request) { - var dateModified = File.GetLastWriteTimeUtc(_configurationManager.ApplicationPaths.SystemConfigurationFilePath); + var dateModified = _fileSystem.GetLastWriteTimeUtc(_configurationManager.ApplicationPaths.SystemConfigurationFilePath); var cacheKey = (_configurationManager.ApplicationPaths.SystemConfigurationFilePath + dateModified.Ticks).GetMD5(); diff --git a/MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs b/MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs index f11bae5239..07fa0e6100 100644 --- a/MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs +++ b/MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; @@ -27,16 +28,18 @@ namespace MediaBrowser.Api.WebSocket /// The _kernel /// private readonly ILogManager _logManager; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. /// /// The logger. /// The log manager. - public LogFileWebSocketListener(ILogger logger, ILogManager logManager) + public LogFileWebSocketListener(ILogger logger, ILogManager logManager, IFileSystem fileSystem) : base(logger) { _logManager = logManager; + _fileSystem = fileSystem; _logManager.LoggerLoaded += kernel_LoggerLoaded; } @@ -53,7 +56,7 @@ namespace MediaBrowser.Api.WebSocket state.StartLine = 0; } - var lines = await GetLogLines(state.LastLogFilePath, state.StartLine).ConfigureAwait(false); + var lines = await GetLogLines(state.LastLogFilePath, state.StartLine, _fileSystem).ConfigureAwait(false); state.StartLine += lines.Count; @@ -96,11 +99,11 @@ namespace MediaBrowser.Api.WebSocket /// The log file path. /// The start line. /// Task{IEnumerable{System.String}}. - internal static async Task> GetLogLines(string logFilePath, int startLine) + internal static async Task> GetLogLines(string logFilePath, int startLine, IFileSystem fileSystem) { var lines = new List(); - using (var fs = new FileStream(logFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, true)) + using (var fs = fileSystem.GetFileStream(logFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { using (var reader = new StreamReader(fs)) { diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index daa664a388..ee22b7baa2 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs @@ -1,5 +1,4 @@ -using System.Net; -using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; using MediaBrowser.Common.Implementations.Archiving; using MediaBrowser.Common.Implementations.IO; @@ -7,6 +6,7 @@ using MediaBrowser.Common.Implementations.ScheduledTasks; using MediaBrowser.Common.Implementations.Security; using MediaBrowser.Common.Implementations.Serialization; using MediaBrowser.Common.Implementations.Updates; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.ScheduledTasks; @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Net.Http; using System.Reflection; using System.Threading; @@ -150,15 +151,17 @@ namespace MediaBrowser.Common.Implementations /// Gets or sets the installation manager. /// /// The installation manager. - protected IInstallationManager InstallationManager { get; set; } + protected IInstallationManager InstallationManager { get; private set; } + protected IFileSystem FileSystemManager { get; private set; } + /// /// Gets or sets the zip client. /// /// The zip client. - protected IZipClient ZipClient { get; set; } + protected IZipClient ZipClient { get; private set; } - protected IIsoManager IsoManager { get; set; } + protected IIsoManager IsoManager { get; private set; } /// /// Initializes a new instance of the class. @@ -347,7 +350,10 @@ namespace MediaBrowser.Common.Implementations RegisterSingleInstance(TaskManager); - HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, Logger, CreateHttpClient); + FileSystemManager = CreateFileSystemManager(); + RegisterSingleInstance(FileSystemManager); + + HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, Logger, CreateHttpClient, FileSystemManager); RegisterSingleInstance(HttpClient); NetworkManager = CreateNetworkManager(); @@ -367,6 +373,11 @@ namespace MediaBrowser.Common.Implementations }); } + protected virtual IFileSystem CreateFileSystemManager() + { + return new CommonFileSystem(Logger, true); + } + protected abstract HttpClient CreateHttpClient(bool enableHttpCompression); /// diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index b75234107f..0d6ba5c1da 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -34,6 +34,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager public delegate HttpClient GetHttpClientHandler(bool enableHttpCompression); private readonly GetHttpClientHandler _getHttpClientHandler; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -46,7 +47,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager /// or /// logger /// - public HttpClientManager(IApplicationPaths appPaths, ILogger logger, GetHttpClientHandler getHttpClientHandler) + public HttpClientManager(IApplicationPaths appPaths, ILogger logger, GetHttpClientHandler getHttpClientHandler, IFileSystem fileSystem) { if (appPaths == null) { @@ -59,6 +60,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager _logger = logger; _getHttpClientHandler = getHttpClientHandler; + _fileSystem = fileSystem; _appPaths = appPaths; } @@ -417,7 +419,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager // We're not able to track progress using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { - using (var fs = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); } @@ -427,7 +429,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager { using (var stream = ProgressStream.CreateReadProgressStream(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), options.Progress.Report, contentLength.Value)) { - using (var fs = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs similarity index 81% rename from MediaBrowser.ServerApplication/IO/CommonFileSystem.cs rename to MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs index dee9bd9d77..ed9baf3b2f 100644 --- a/MediaBrowser.ServerApplication/IO/CommonFileSystem.cs +++ b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs @@ -1,10 +1,10 @@ -using MediaBrowser.Controller.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Model.Logging; using System; using System.IO; using System.Text; -namespace MediaBrowser.ServerApplication.IO +namespace MediaBrowser.Common.Implementations.IO { /// /// Class CommonFileSystem @@ -13,9 +13,12 @@ namespace MediaBrowser.ServerApplication.IO { protected ILogger Logger; - public CommonFileSystem(ILogger logger) + private readonly bool _supportsAsyncFileStreams; + + public CommonFileSystem(ILogger logger, bool supportsAsyncFileStreams) { Logger = logger; + _supportsAsyncFileStreams = supportsAsyncFileStreams; } /// @@ -164,8 +167,56 @@ namespace MediaBrowser.ServerApplication.IO return DateTime.MinValue; } } - } + /// + /// Gets the creation time UTC. + /// + /// The info. + /// The logger. + /// DateTime. + public DateTime GetLastWriteTimeUtc(FileSystemInfo info) + { + // This could throw an error on some file systems that have dates out of range + try + { + return info.LastWriteTimeUtc; + } + catch (Exception ex) + { + Logger.ErrorException("Error determining LastAccessTimeUtc for {0}", ex, info.FullName); + return DateTime.MinValue; + } + } + + /// + /// Gets the last write time UTC. + /// + /// The path. + /// DateTime. + public DateTime GetLastWriteTimeUtc(string path) + { + return GetLastWriteTimeUtc(GetFileSystemInfo(path)); + } + + /// + /// Gets the file stream. + /// + /// The path. + /// The mode. + /// The access. + /// The share. + /// if set to true [is asynchronous]. + /// FileStream. + public FileStream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share, bool isAsync = false) + { + if (_supportsAsyncFileStreams && isAsync) + { + return new FileStream(path, mode, access, share, 4096, true); + } + + return new FileStream(path, mode, access, share); + } + } /// /// Adapted from http://stackoverflow.com/questions/309495/windows-shortcut-lnk-parser-in-java diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index c17a549acc..9e48f3b3e9 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -69,6 +69,7 @@ + diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index 812269ea8a..e04cadfc5a 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -1,12 +1,13 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Logging; namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks { @@ -23,14 +24,17 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// /// The app paths. - public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger) + public DeleteCacheFileTask(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem) { ApplicationPaths = appPaths; _logger = logger; + _fileSystem = fileSystem; } /// @@ -94,7 +98,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress progress) { var filesToDelete = new DirectoryInfo(directory).EnumerateFiles("*", SearchOption.AllDirectories) - .Where(f => f.LastWriteTimeUtc < minDateModified) + .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified) .ToList(); var index = 0; diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs index bfd626adbc..7c7833ae64 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; using MediaBrowser.Common.ScheduledTasks; using System; using System.Collections.Generic; @@ -20,13 +21,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks /// The configuration manager. private IConfigurationManager ConfigurationManager { get; set; } + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// /// The configuration manager. - public DeleteLogFileTask(IConfigurationManager configurationManager) + public DeleteLogFileTask(IConfigurationManager configurationManager, IFileSystem fileSystem) { ConfigurationManager = configurationManager; + _fileSystem = fileSystem; } /// @@ -58,7 +62,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays)); var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories) - .Where(f => f.LastWriteTimeUtc < minDateModified) + .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified) .ToList(); var index = 0; diff --git a/MediaBrowser.Controller/IO/IFileSystem.cs b/MediaBrowser.Common/IO/IFileSystem.cs similarity index 61% rename from MediaBrowser.Controller/IO/IFileSystem.cs rename to MediaBrowser.Common/IO/IFileSystem.cs index cbc5c7bf34..d307b74e5d 100644 --- a/MediaBrowser.Controller/IO/IFileSystem.cs +++ b/MediaBrowser.Common/IO/IFileSystem.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace MediaBrowser.Controller.IO +namespace MediaBrowser.Common.IO { /// /// Interface IFileSystem @@ -49,5 +49,30 @@ namespace MediaBrowser.Controller.IO /// The info. /// DateTime. DateTime GetCreationTimeUtc(FileSystemInfo info); + + /// + /// Gets the last write time UTC. + /// + /// The information. + /// DateTime. + DateTime GetLastWriteTimeUtc(FileSystemInfo info); + + /// + /// Gets the last write time UTC. + /// + /// The path. + /// DateTime. + DateTime GetLastWriteTimeUtc(string path); + + /// + /// Gets the file stream. + /// + /// The path. + /// The mode. + /// The access. + /// The share. + /// if set to true [is asynchronous]. + /// FileStream. + FileStream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share, bool isAsync = false); } } diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 8acd1a83cb..f4d759a4d1 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -60,6 +60,7 @@ + diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f1937a4616..839fe34ffe 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; @@ -1737,7 +1738,7 @@ namespace MediaBrowser.Controller.Entities if (locationType == LocationType.Remote || locationType == LocationType.Virtual) { - return File.GetLastWriteTimeUtc(imagePath); + return FileSystem.GetLastWriteTimeUtc(imagePath); } var metaFileEntry = ResolveArgs.GetMetaFileByPath(imagePath); @@ -1754,7 +1755,7 @@ namespace MediaBrowser.Controller.Entities } // See if we can avoid a file system lookup by looking for the file in ResolveArgs - return metaFileEntry == null ? File.GetLastWriteTimeUtc(imagePath) : metaFileEntry.LastWriteTimeUtc; + return metaFileEntry == null ? FileSystem.GetLastWriteTimeUtc(imagePath) : FileSystem.GetLastWriteTimeUtc(metaFileEntry); } } } diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 9d85399067..06f50e552b 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -165,7 +165,7 @@ namespace MediaBrowser.Controller.Entities // Ensure it's been lazy loaded var config = Configuration; - return File.GetLastWriteTimeUtc(ConfigurationFilePath); + return FileSystem.GetLastWriteTimeUtc(ConfigurationFilePath); } } diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs index 726467fa6a..270afd89a1 100644 --- a/MediaBrowser.Controller/IO/FileData.cs +++ b/MediaBrowser.Controller/IO/FileData.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Library; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/IO/FileSystem.cs b/MediaBrowser.Controller/IO/FileSystem.cs deleted file mode 100644 index b08e8da274..0000000000 --- a/MediaBrowser.Controller/IO/FileSystem.cs +++ /dev/null @@ -1,71 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.IO; - -namespace MediaBrowser.Controller.IO -{ - /// - /// Class FileSystem - /// - public static class FileSystem - { - /// - /// Gets the creation time UTC. - /// - /// The info. - /// The logger. - /// DateTime. - public static DateTime GetLastWriteTimeUtc(FileSystemInfo info, ILogger logger) - { - // This could throw an error on some file systems that have dates out of range - - try - { - return info.LastWriteTimeUtc; - } - catch (Exception ex) - { - logger.ErrorException("Error determining LastAccessTimeUtc for {0}", ex, info.FullName); - return DateTime.MinValue; - } - } - - /// - /// Copies all. - /// - /// The source. - /// The target. - /// source - /// The source and target directories are the same - public static void CopyAll(string source, string target) - { - if (string.IsNullOrEmpty(source)) - { - throw new ArgumentNullException("source"); - } - if (string.IsNullOrEmpty(target)) - { - throw new ArgumentNullException("target"); - } - - if (source.Equals(target, StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException("The source and target directories are the same"); - } - - // Check if the target directory exists, if not, create it. - Directory.CreateDirectory(target); - - foreach (var file in Directory.EnumerateFiles(source)) - { - File.Copy(file, Path.Combine(target, Path.GetFileName(file)), true); - } - - // Copy each subdirectory using recursion. - foreach (var dir in Directory.EnumerateDirectories(source)) - { - CopyAll(dir, Path.Combine(target, Path.GetFileName(dir))); - } - } - } -} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index c379714aa3..978d56bd4e 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -94,7 +94,6 @@ - @@ -141,7 +140,6 @@ - diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index 87036df84e..fd1b12c2f2 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -35,6 +36,8 @@ namespace MediaBrowser.Controller.MediaInfo private readonly ILogger _logger; private readonly IItemRepository _itemRepo; + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// @@ -43,12 +46,13 @@ namespace MediaBrowser.Controller.MediaInfo /// The logger. /// The item repo. /// zipClient - public FFMpegManager(IServerApplicationPaths appPaths, IMediaEncoder encoder, ILogger logger, IItemRepository itemRepo) + public FFMpegManager(IServerApplicationPaths appPaths, IMediaEncoder encoder, ILogger logger, IItemRepository itemRepo, IFileSystem fileSystem) { _appPaths = appPaths; _encoder = encoder; _logger = logger; _itemRepo = itemRepo; + _fileSystem = fileSystem; VideoImageCache = new FileSystemRepository(VideoImagesDataPath); SubtitleCache = new FileSystemRepository(SubtitleCachePath); @@ -203,7 +207,7 @@ namespace MediaBrowser.Controller.MediaInfo if (stream.IsExternal) { - ticksParam += File.GetLastWriteTimeUtc(stream.Path).Ticks; + ticksParam += _fileSystem.GetLastWriteTimeUtc(stream.Path).Ticks; } return SubtitleCache.GetResourcePath(input.Id + "_" + subtitleStreamIndex + "_" + input.DateModified.Ticks + ticksParam, outputExtension); diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index 5d61e5eb9c..1e4fabc7c2 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using System; @@ -149,7 +150,7 @@ namespace MediaBrowser.Controller.Resolvers item.DateCreated = fileSystem.GetCreationTimeUtc(childData); } - item.DateModified = childData.LastWriteTimeUtc; + item.DateModified = fileSystem.GetLastWriteTimeUtc(childData); } else { @@ -161,7 +162,7 @@ namespace MediaBrowser.Controller.Resolvers { item.DateCreated = fileSystem.GetCreationTimeUtc(fileData); } - item.DateModified = fileData.LastWriteTimeUtc; + item.DateModified = fileSystem.GetLastWriteTimeUtc(fileData); } } } @@ -171,7 +172,7 @@ namespace MediaBrowser.Controller.Resolvers { item.DateCreated = fileSystem.GetCreationTimeUtc(args.FileInfo); } - item.DateModified = args.FileInfo.LastWriteTimeUtc; + item.DateModified = fileSystem.GetLastWriteTimeUtc(args.FileInfo); } } } diff --git a/MediaBrowser.Mono.userprefs b/MediaBrowser.Mono.userprefs index 4378247fd4..f1260b1dad 100644 --- a/MediaBrowser.Mono.userprefs +++ b/MediaBrowser.Mono.userprefs @@ -1,25 +1,24 @@  - + - + + + - - - - - - + + + - + diff --git a/MediaBrowser.Providers/FolderProviderFromXml.cs b/MediaBrowser.Providers/FolderProviderFromXml.cs index 2a22d04482..449de7450d 100644 --- a/MediaBrowser.Providers/FolderProviderFromXml.cs +++ b/MediaBrowser.Providers/FolderProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Providers; @@ -17,10 +18,12 @@ namespace MediaBrowser.Providers public class FolderProviderFromXml : BaseMetadataProvider { public static FolderProviderFromXml Current; + private readonly IFileSystem _fileSystem; - public FolderProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + public FolderProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { + _fileSystem = fileSystem; Current = this; } @@ -53,7 +56,7 @@ namespace MediaBrowser.Providers return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/Games/GameProviderFromXml.cs b/MediaBrowser.Providers/Games/GameProviderFromXml.cs index 44680b8328..724e3f5fa0 100644 --- a/MediaBrowser.Providers/Games/GameProviderFromXml.cs +++ b/MediaBrowser.Providers/Games/GameProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Providers; @@ -13,15 +14,17 @@ namespace MediaBrowser.Providers.Games { public class GameProviderFromXml : BaseMetadataProvider { + private readonly IFileSystem _fileSystem; + /// /// /// /// /// - public GameProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + public GameProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { - + _fileSystem = fileSystem; } /// @@ -45,7 +48,7 @@ namespace MediaBrowser.Providers.Games return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/Games/GameSystemProviderFromXml.cs b/MediaBrowser.Providers/Games/GameSystemProviderFromXml.cs index 526170db54..0c9d55a094 100644 --- a/MediaBrowser.Providers/Games/GameSystemProviderFromXml.cs +++ b/MediaBrowser.Providers/Games/GameSystemProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Providers; @@ -14,10 +15,12 @@ namespace MediaBrowser.Providers.Games public class GameSystemProviderFromXml : BaseMetadataProvider { internal static GameSystemProviderFromXml Current { get; private set; } + private readonly IFileSystem _fileSystem; - public GameSystemProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + public GameSystemProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { + _fileSystem = fileSystem; Current = this; } @@ -50,7 +53,7 @@ namespace MediaBrowser.Providers.Games return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/ImagesByNameProvider.cs b/MediaBrowser.Providers/ImagesByNameProvider.cs index 8c5b3635d1..6be4ee108b 100644 --- a/MediaBrowser.Providers/ImagesByNameProvider.cs +++ b/MediaBrowser.Providers/ImagesByNameProvider.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; @@ -113,7 +114,7 @@ namespace MediaBrowser.Providers return files.Select(f => { - var lastWriteTime = FileSystem.GetLastWriteTimeUtc(f, Logger); + var lastWriteTime = _fileSystem.GetLastWriteTimeUtc(f); var creationTime = _fileSystem.GetCreationTimeUtc(f); return creationTime > lastWriteTime ? creationTime : lastWriteTime; diff --git a/MediaBrowser.Providers/Movies/BoxSetProviderFromXml.cs b/MediaBrowser.Providers/Movies/BoxSetProviderFromXml.cs index e697738fe5..0b2502ba9f 100644 --- a/MediaBrowser.Providers/Movies/BoxSetProviderFromXml.cs +++ b/MediaBrowser.Providers/Movies/BoxSetProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.IO; @@ -18,10 +19,12 @@ namespace MediaBrowser.Providers.Movies public class BoxSetProviderFromXml : BaseMetadataProvider { public static BoxSetProviderFromXml Current; + private readonly IFileSystem _fileSystem; - public BoxSetProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + public BoxSetProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { + _fileSystem = fileSystem; Current = this; } @@ -54,7 +57,7 @@ namespace MediaBrowser.Providers.Movies return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs index 3732d6b8ef..3458622d34 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs +++ b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs @@ -4,6 +4,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -40,6 +41,7 @@ namespace MediaBrowser.Providers.Movies private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); internal static FanArtMovieProvider Current { get; private set; } + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -49,7 +51,7 @@ namespace MediaBrowser.Providers.Movies /// The configuration manager. /// The provider manager. /// httpClient - public FanArtMovieProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public FanArtMovieProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { if (httpClient == null) @@ -58,6 +60,7 @@ namespace MediaBrowser.Providers.Movies } HttpClient = httpClient; _providerManager = providerManager; + _fileSystem = fileSystem; Current = this; } @@ -174,7 +177,7 @@ namespace MediaBrowser.Providers.Movies { var files = new DirectoryInfo(path) .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => i.LastWriteTimeUtc) + .Select(i => _fileSystem.GetLastWriteTimeUtc(i)) .ToList(); if (files.Count > 0) @@ -275,7 +278,7 @@ namespace MediaBrowser.Providers.Movies }).ConfigureAwait(false)) { - using (var xmlFileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs index 51b77599e2..706dffa7e0 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; @@ -32,15 +33,17 @@ namespace MediaBrowser.Providers.Movies /// private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public FanArtMovieUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) + public FanArtMovieUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _config = config; _logger = logger; _httpClient = httpClient; + _fileSystem = fileSystem; } /// @@ -66,7 +69,7 @@ namespace MediaBrowser.Providers.Movies var timestampFileInfo = new FileInfo(timestampFile); // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) + if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < 1) { return; } diff --git a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs index 0fd61a96be..f2504da9ab 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -21,11 +22,6 @@ namespace MediaBrowser.Providers.Movies /// public class MovieDbImagesProvider : BaseMetadataProvider { - /// - /// The get images - /// - private const string GetImages = @"http://api.themoviedb.org/3/{2}/{0}/images?api_key={1}"; - /// /// The _provider manager /// @@ -35,6 +31,7 @@ namespace MediaBrowser.Providers.Movies /// The _json serializer /// private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -43,11 +40,12 @@ namespace MediaBrowser.Providers.Movies /// The configuration manager. /// The provider manager. /// The json serializer. - public MovieDbImagesProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IJsonSerializer jsonSerializer) + public MovieDbImagesProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem) : base(logManager, configurationManager) { _providerManager = providerManager; _jsonSerializer = jsonSerializer; + _fileSystem = fileSystem; } /// @@ -163,7 +161,7 @@ namespace MediaBrowser.Providers.Movies if (fileInfo.Exists) { - return fileInfo.LastWriteTimeUtc > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed; } } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 63a237d2c8..d7b7faeea7 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -47,6 +48,7 @@ namespace MediaBrowser.Providers.Movies /// /// The HTTP client. protected IHttpClient HttpClient { get; private set; } + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -56,12 +58,13 @@ namespace MediaBrowser.Providers.Movies /// The json serializer. /// The HTTP client. /// The provider manager. - public MovieDbProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, IProviderManager providerManager) + public MovieDbProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { JsonSerializer = jsonSerializer; HttpClient = httpClient; ProviderManager = providerManager; + _fileSystem = fileSystem; Current = this; } @@ -216,7 +219,7 @@ namespace MediaBrowser.Providers.Movies if (fileInfo.Exists) { - return fileInfo.LastWriteTimeUtc > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed; } return true; diff --git a/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs b/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs index ed92151c75..dfab655f10 100644 --- a/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs +++ b/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs @@ -1,7 +1,7 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -21,11 +21,13 @@ namespace MediaBrowser.Providers.Movies { internal static MovieProviderFromXml Current { get; private set; } private readonly IItemRepository _itemRepo; + private readonly IFileSystem _fileSystem; - public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IItemRepository itemRepo) + public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IItemRepository itemRepo, IFileSystem fileSystem) : base(logManager, configurationManager) { _itemRepo = itemRepo; + _fileSystem = fileSystem; Current = this; } @@ -71,7 +73,7 @@ namespace MediaBrowser.Providers.Movies return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs index b5d2646829..4c1838cfc5 100644 --- a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; @@ -35,6 +36,7 @@ namespace MediaBrowser.Providers.Movies /// private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _json; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -43,12 +45,13 @@ namespace MediaBrowser.Providers.Movies /// The HTTP client. /// The config. /// The json. - public MovieUpdatesPreScanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IJsonSerializer json) + public MovieUpdatesPreScanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IJsonSerializer json, IFileSystem fileSystem) { _logger = logger; _httpClient = httpClient; _config = config; _json = json; + _fileSystem = fileSystem; } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); @@ -100,7 +103,7 @@ namespace MediaBrowser.Providers.Movies var refreshDays = _config.Configuration.EnableTmdbUpdates ? 1 : 7; // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < refreshDays) + if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < refreshDays) { return; } diff --git a/MediaBrowser.Providers/Movies/PersonProviderFromXml.cs b/MediaBrowser.Providers/Movies/PersonProviderFromXml.cs index ab90675fd7..8de061b99b 100644 --- a/MediaBrowser.Providers/Movies/PersonProviderFromXml.cs +++ b/MediaBrowser.Providers/Movies/PersonProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Providers; @@ -13,10 +14,12 @@ namespace MediaBrowser.Providers.Movies class PersonProviderFromXml : BaseMetadataProvider { internal static PersonProviderFromXml Current { get; private set; } + private readonly IFileSystem _fileSystem; - public PersonProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + public PersonProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { + _fileSystem = fileSystem; Current = this; } @@ -49,7 +52,7 @@ namespace MediaBrowser.Providers.Movies return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/Movies/PersonUpdatesPreScanTask.cs b/MediaBrowser.Providers/Movies/PersonUpdatesPreScanTask.cs index d6cc39c86d..8a5e6bd9dc 100644 --- a/MediaBrowser.Providers/Movies/PersonUpdatesPreScanTask.cs +++ b/MediaBrowser.Providers/Movies/PersonUpdatesPreScanTask.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; @@ -34,6 +35,7 @@ namespace MediaBrowser.Providers.Movies /// private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _json; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -41,12 +43,13 @@ namespace MediaBrowser.Providers.Movies /// The logger. /// The HTTP client. /// The config. - public PersonUpdatesPreScanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IJsonSerializer json) + public PersonUpdatesPreScanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IJsonSerializer json, IFileSystem fileSystem) { _logger = logger; _httpClient = httpClient; _config = config; _json = json; + _fileSystem = fileSystem; } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); @@ -74,7 +77,7 @@ namespace MediaBrowser.Providers.Movies var timestampFileInfo = new FileInfo(timestampFile); // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) + if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < 1) { return; } diff --git a/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs b/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs index 4a5db1d81a..52f5ddab5c 100644 --- a/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs @@ -4,6 +4,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -30,8 +31,9 @@ namespace MediaBrowser.Providers.Movies internal static TmdbPersonProvider Current { get; private set; } const string DataFileName = "info.json"; + private readonly IFileSystem _fileSystem; - public TmdbPersonProvider(IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public TmdbPersonProvider(IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { if (jsonSerializer == null) @@ -40,6 +42,7 @@ namespace MediaBrowser.Providers.Movies } JsonSerializer = jsonSerializer; ProviderManager = providerManager; + _fileSystem = fileSystem; Current = this; } @@ -105,7 +108,7 @@ namespace MediaBrowser.Providers.Movies if (fileInfo.Exists) { - return fileInfo.LastWriteTimeUtc > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed; } return true; @@ -270,7 +273,7 @@ namespace MediaBrowser.Providers.Movies { Directory.CreateDirectory(personDataPath); - using (var fs = new FileStream(Path.Combine(personDataPath, DataFileName), FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) + using (var fs = _fileSystem.GetFileStream(Path.Combine(personDataPath, DataFileName), FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await json.CopyToAsync(fs).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Music/ArtistProviderFromXml.cs b/MediaBrowser.Providers/Music/ArtistProviderFromXml.cs index 9353d4565c..99cf925e57 100644 --- a/MediaBrowser.Providers/Music/ArtistProviderFromXml.cs +++ b/MediaBrowser.Providers/Music/ArtistProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.IO; @@ -15,10 +16,12 @@ namespace MediaBrowser.Providers.Music class ArtistProviderFromXml : BaseMetadataProvider { public static ArtistProviderFromXml Current; + private readonly IFileSystem _fileSystem; - public ArtistProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + public ArtistProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { + _fileSystem = fileSystem; Current = this; } @@ -51,7 +54,7 @@ namespace MediaBrowser.Providers.Music return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs index e454b048c9..d6c7f1dfd7 100644 --- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -37,6 +38,7 @@ namespace MediaBrowser.Providers.Music protected IHttpClient HttpClient { get; private set; } internal static FanArtAlbumProvider Current { get; private set; } + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -45,10 +47,11 @@ namespace MediaBrowser.Providers.Music /// The log manager. /// The configuration manager. /// The provider manager. - public FanArtAlbumProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public FanArtAlbumProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { _providerManager = providerManager; + _fileSystem = fileSystem; HttpClient = httpClient; Current = this; @@ -140,7 +143,7 @@ namespace MediaBrowser.Providers.Music if (file.Exists) { - return file.LastWriteTimeUtc; + return _fileSystem.GetLastWriteTimeUtc(file); } } diff --git a/MediaBrowser.Providers/Music/FanArtArtistByNameProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistByNameProvider.cs index f0dd460e6c..5d18f16ffa 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistByNameProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistByNameProvider.cs @@ -1,7 +1,9 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; @@ -15,12 +17,8 @@ namespace MediaBrowser.Providers.Music /// /// Initializes a new instance of the class. /// - /// The HTTP client. - /// The log manager. - /// The configuration manager. - /// The provider manager. - public FanArtArtistByNameProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(httpClient, logManager, configurationManager, providerManager) + public FanArtArtistByNameProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) + : base(httpClient, logManager, configurationManager, providerManager, fileSystem) { } diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index 79d53d5785..b1d97d8b59 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs @@ -1,24 +1,23 @@ -using System.Net; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; using System.Xml; -using MediaBrowser.Model.Net; namespace MediaBrowser.Providers.Music { @@ -39,6 +38,7 @@ namespace MediaBrowser.Providers.Music private readonly IProviderManager _providerManager; internal static FanArtArtistProvider Current; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Music /// The configuration manager. /// The provider manager. /// httpClient - public FanArtArtistProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public FanArtArtistProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { if (httpClient == null) @@ -57,6 +57,7 @@ namespace MediaBrowser.Providers.Music } HttpClient = httpClient; _providerManager = providerManager; + _fileSystem = fileSystem; Current = this; } @@ -167,7 +168,7 @@ namespace MediaBrowser.Providers.Music { var files = new DirectoryInfo(path) .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => i.LastWriteTimeUtc) + .Select(i => _fileSystem.GetLastWriteTimeUtc(i)) .ToList(); if (files.Count > 0) @@ -284,7 +285,7 @@ namespace MediaBrowser.Providers.Music }).ConfigureAwait(false)) { - using (var xmlFileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Music/FanArtUpdatesPrescanTask.cs b/MediaBrowser.Providers/Music/FanArtUpdatesPrescanTask.cs index 379866945a..6d9a16e874 100644 --- a/MediaBrowser.Providers/Music/FanArtUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/Music/FanArtUpdatesPrescanTask.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; @@ -31,15 +32,17 @@ namespace MediaBrowser.Providers.Music /// private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public FanArtUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) + public FanArtUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _config = config; _logger = logger; _httpClient = httpClient; + _fileSystem = fileSystem; } /// @@ -65,7 +68,7 @@ namespace MediaBrowser.Providers.Music var timestampFileInfo = new FileInfo(timestampFile); // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) + if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < 1) { return; } diff --git a/MediaBrowser.Providers/RefreshIntrosTask.cs b/MediaBrowser.Providers/RefreshIntrosTask.cs index 5d87dc90f8..3ecddf6130 100644 --- a/MediaBrowser.Providers/RefreshIntrosTask.cs +++ b/MediaBrowser.Providers/RefreshIntrosTask.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.IO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using System; diff --git a/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs b/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs index 9862f10fee..b6fdaaa831 100644 --- a/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs +++ b/MediaBrowser.Providers/TV/EpisodeProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; @@ -20,11 +21,13 @@ namespace MediaBrowser.Providers.TV { internal static EpisodeProviderFromXml Current { get; private set; } private readonly IItemRepository _itemRepo; + private readonly IFileSystem _fileSystem; - public EpisodeProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IItemRepository itemRepo) + public EpisodeProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IItemRepository itemRepo, IFileSystem fileSystem) : base(logManager, configurationManager) { _itemRepo = itemRepo; + _fileSystem = fileSystem; Current = this; } @@ -76,7 +79,7 @@ namespace MediaBrowser.Providers.TV return false; } - return FileSystem.GetLastWriteTimeUtc(file, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(file) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs index dd87db5b5c..fe316e85b7 100644 --- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -22,6 +23,7 @@ namespace MediaBrowser.Providers.TV /// The _provider manager /// private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -29,10 +31,11 @@ namespace MediaBrowser.Providers.TV /// The log manager. /// The configuration manager. /// The provider manager. - public FanArtSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public FanArtSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { _providerManager = providerManager; + _fileSystem = fileSystem; } public override ItemUpdateType ItemUpdateType @@ -76,7 +79,7 @@ namespace MediaBrowser.Providers.TV if (imagesFileInfo.Exists) { - return imagesFileInfo.LastWriteTimeUtc; + return _fileSystem.GetLastWriteTimeUtc(imagesFileInfo); } } diff --git a/MediaBrowser.Providers/TV/FanArtTVProvider.cs b/MediaBrowser.Providers/TV/FanArtTVProvider.cs index ed7ca941c2..af89bc205e 100644 --- a/MediaBrowser.Providers/TV/FanArtTVProvider.cs +++ b/MediaBrowser.Providers/TV/FanArtTVProvider.cs @@ -4,6 +4,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -31,8 +32,9 @@ namespace MediaBrowser.Providers.TV protected IHttpClient HttpClient { get; private set; } private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; - public FanArtTvProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public FanArtTvProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { if (httpClient == null) @@ -41,6 +43,7 @@ namespace MediaBrowser.Providers.TV } HttpClient = httpClient; _providerManager = providerManager; + _fileSystem = fileSystem; Current = this; } @@ -115,7 +118,7 @@ namespace MediaBrowser.Providers.TV { var files = new DirectoryInfo(path) .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => i.LastWriteTimeUtc) + .Select(i => _fileSystem.GetLastWriteTimeUtc(i)) .ToList(); if (files.Count > 0) @@ -353,7 +356,7 @@ namespace MediaBrowser.Providers.TV }).ConfigureAwait(false)) { - using (var xmlFileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs b/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs index 2a8e019741..5c1c7a69d9 100644 --- a/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; @@ -32,15 +33,17 @@ namespace MediaBrowser.Providers.TV /// private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public FanArtTvUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) + public FanArtTvUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _config = config; _logger = logger; _httpClient = httpClient; + _fileSystem = fileSystem; } /// @@ -66,7 +69,7 @@ namespace MediaBrowser.Providers.TV var timestampFileInfo = new FileInfo(timestampFile); // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) + if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < 1) { return; } diff --git a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs index 7ac51e76d8..cc6bca0b3f 100644 --- a/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteEpisodeProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -36,6 +37,7 @@ namespace MediaBrowser.Providers.TV /// /// The HTTP client. protected IHttpClient HttpClient { get; private set; } + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -44,11 +46,12 @@ namespace MediaBrowser.Providers.TV /// The log manager. /// The configuration manager. /// The provider manager. - public RemoteEpisodeProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public RemoteEpisodeProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { HttpClient = httpClient; _providerManager = providerManager; + _fileSystem = fileSystem; } /// @@ -149,7 +152,7 @@ namespace MediaBrowser.Providers.TV if (files.Count > 0) { - return files.Select(i => i.LastWriteTimeUtc).Max() > providerInfo.LastRefreshed; + return files.Select(i => _fileSystem.GetLastWriteTimeUtc(i)).Max() > providerInfo.LastRefreshed; } } diff --git a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs index 4eba7dbb4e..1f702a2d21 100644 --- a/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteSeasonProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -23,6 +24,7 @@ namespace MediaBrowser.Providers.TV /// The _provider manager /// private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -31,10 +33,11 @@ namespace MediaBrowser.Providers.TV /// The configuration manager. /// The provider manager. /// httpClient - public RemoteSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public RemoteSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { _providerManager = providerManager; + _fileSystem = fileSystem; } /// @@ -115,7 +118,7 @@ namespace MediaBrowser.Providers.TV if (imagesFileInfo.Exists) { - return imagesFileInfo.LastWriteTimeUtc > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(imagesFileInfo) > providerInfo.LastRefreshed; } } return false; diff --git a/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs b/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs index 5d518c0ea2..3e2736cbca 100644 --- a/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/RemoteSeriesProvider.cs @@ -4,6 +4,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -49,6 +50,8 @@ namespace MediaBrowser.Providers.TV /// The HTTP client. protected IHttpClient HttpClient { get; private set; } + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// @@ -57,7 +60,7 @@ namespace MediaBrowser.Providers.TV /// The configuration manager. /// The zip client. /// httpClient - public RemoteSeriesProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IZipClient zipClient) + public RemoteSeriesProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IZipClient zipClient, IFileSystem fileSystem) : base(logManager, configurationManager) { if (httpClient == null) @@ -66,6 +69,7 @@ namespace MediaBrowser.Providers.TV } HttpClient = httpClient; _zipClient = zipClient; + _fileSystem = fileSystem; Current = this; } @@ -176,7 +180,7 @@ namespace MediaBrowser.Providers.TV { var files = new DirectoryInfo(path) .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => i.LastWriteTimeUtc) + .Select(i => _fileSystem.GetLastWriteTimeUtc(i)) .ToList(); if (files.Count > 0) @@ -344,7 +348,7 @@ namespace MediaBrowser.Providers.TV { string validXml; - using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) + using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { using (var reader = new StreamReader(fileStream)) { @@ -354,7 +358,7 @@ namespace MediaBrowser.Providers.TV } } - using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) + using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { using (var writer = new StreamWriter(fileStream)) { diff --git a/MediaBrowser.Providers/TV/SeasonProviderFromXml.cs b/MediaBrowser.Providers/TV/SeasonProviderFromXml.cs index 0c8fcf066c..5f1eb5cb3f 100644 --- a/MediaBrowser.Providers/TV/SeasonProviderFromXml.cs +++ b/MediaBrowser.Providers/TV/SeasonProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; @@ -18,10 +19,12 @@ namespace MediaBrowser.Providers.TV public class SeasonProviderFromXml : BaseMetadataProvider { public static SeasonProviderFromXml Current; + private readonly IFileSystem _fileSystem; - public SeasonProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + public SeasonProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { + _fileSystem = fileSystem; Current = this; } @@ -54,7 +57,7 @@ namespace MediaBrowser.Providers.TV return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/TV/SeriesProviderFromXml.cs b/MediaBrowser.Providers/TV/SeriesProviderFromXml.cs index 67d6e423c9..c4b82d51eb 100644 --- a/MediaBrowser.Providers/TV/SeriesProviderFromXml.cs +++ b/MediaBrowser.Providers/TV/SeriesProviderFromXml.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; @@ -18,10 +19,12 @@ namespace MediaBrowser.Providers.TV public class SeriesProviderFromXml : BaseMetadataProvider { internal static SeriesProviderFromXml Current { get; private set; } - - public SeriesProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) + private readonly IFileSystem _fileSystem; + + public SeriesProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) : base(logManager, configurationManager) { + _fileSystem = fileSystem; Current = this; } @@ -54,7 +57,7 @@ namespace MediaBrowser.Providers.TV return false; } - return FileSystem.GetLastWriteTimeUtc(xml, Logger) > providerInfo.LastRefreshed; + return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed; } /// diff --git a/MediaBrowser.Providers/TV/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TvdbPrescanTask.cs index 94f857d9c9..4926b92f73 100644 --- a/MediaBrowser.Providers/TV/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TvdbPrescanTask.cs @@ -1,4 +1,5 @@ using System.Globalization; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; @@ -42,6 +43,7 @@ namespace MediaBrowser.Providers.TV /// The _config /// private readonly IServerConfigurationManager _config; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -49,11 +51,12 @@ namespace MediaBrowser.Providers.TV /// The logger. /// The HTTP client. /// The config. - public TvdbPrescanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config) + public TvdbPrescanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IFileSystem fileSystem) { _logger = logger; _httpClient = httpClient; _config = config; + _fileSystem = fileSystem; } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); @@ -81,7 +84,7 @@ namespace MediaBrowser.Providers.TV var timestampFileInfo = new FileInfo(timestampFile); // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) + if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < 1) { return; } diff --git a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs index cc8e970400..2e19a853db 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs @@ -1,7 +1,9 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -30,6 +32,7 @@ namespace MediaBrowser.Providers.TV /// The _provider manager /// private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -39,7 +42,7 @@ namespace MediaBrowser.Providers.TV /// The configuration manager. /// The provider manager. /// httpClient - public TvdbSeriesImageProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public TvdbSeriesImageProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { if (httpClient == null) @@ -48,6 +51,7 @@ namespace MediaBrowser.Providers.TV } HttpClient = httpClient; _providerManager = providerManager; + _fileSystem = fileSystem; } /// @@ -127,7 +131,7 @@ namespace MediaBrowser.Providers.TV if (imagesFileInfo.Exists) { - return imagesFileInfo.LastWriteTimeUtc; + return _fileSystem.GetLastWriteTimeUtc(imagesFileInfo); } } diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs index 4da836cc68..f9cf90787d 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Model.Logging; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.Drawing; @@ -40,9 +42,10 @@ namespace MediaBrowser.Server.Implementations.Drawing /// /// The path of the image to get the dimensions of. /// The logger. + /// The file system. /// The dimensions of the specified image. /// The image was of an unrecognised format. - public static Size GetDimensions(string path, ILogger logger) + public static Size GetDimensions(string path, ILogger logger, IFileSystem fileSystem) { try { @@ -60,7 +63,7 @@ namespace MediaBrowser.Server.Implementations.Drawing } // Buffer to memory stream to avoid image locking file - using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var fs = fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var memoryStream = new MemoryStream()) { diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 95a6448023..ace633be71 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -51,16 +52,18 @@ namespace MediaBrowser.Server.Implementations.Drawing /// The _app paths /// private readonly IServerApplicationPaths _appPaths; + private readonly IFileSystem _fileSystem; private readonly string _imageSizeCachePath; private readonly string _croppedWhitespaceImageCachePath; private readonly string _enhancedImageCachePath; private readonly string _resizedImageCachePath; - public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths) + public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem) { _logger = logger; _appPaths = appPaths; + _fileSystem = fileSystem; _imageSizeCachePath = Path.Combine(_appPaths.ImageCachePath, "image-sizes"); _croppedWhitespaceImageCachePath = Path.Combine(_appPaths.ImageCachePath, "cropped-images"); @@ -113,7 +116,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var fileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { await fileStream.CopyToAsync(toStream).ConfigureAwait(false); return; @@ -131,7 +134,7 @@ namespace MediaBrowser.Server.Implementations.Drawing // Check again in case of lock contention try { - using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var fileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { await fileStream.CopyToAsync(toStream).ConfigureAwait(false); semaphore.Release(); @@ -150,7 +153,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - using (var fileStream = new FileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) + using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { // Copy to memory stream to avoid Image locking file using (var memoryStream = new MemoryStream()) @@ -228,7 +231,7 @@ namespace MediaBrowser.Server.Implementations.Drawing Directory.CreateDirectory(parentPath); // Save to the cache location - using (var cacheFileStream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { // Save to the filestream await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); @@ -359,7 +362,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - using (var fileStream = new FileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) + using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { // Copy to memory stream to avoid Image locking file using (var memoryStream = new MemoryStream()) @@ -376,7 +379,7 @@ namespace MediaBrowser.Server.Implementations.Drawing Directory.CreateDirectory(parentPath); - using (var outputStream = new FileStream(croppedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var outputStream = _fileSystem.GetFileStream(croppedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) { croppedImage.Save(outputFormat, outputStream, 100); } @@ -525,7 +528,7 @@ namespace MediaBrowser.Server.Implementations.Drawing // Cache file doesn't exist no biggie } - var size = ImageHeader.GetDimensions(path, _logger); + var size = ImageHeader.GetDimensions(path, _logger, _fileSystem); var parentPath = Path.GetDirectoryName(fullCachePath); @@ -685,7 +688,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - using (var fileStream = new FileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) + using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { // Copy to memory stream to avoid Image locking file using (var memoryStream = new MemoryStream()) @@ -702,7 +705,7 @@ namespace MediaBrowser.Server.Implementations.Drawing Directory.CreateDirectory(parentDirectory); //And then save it in the cache - using (var outputStream = new FileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var outputStream = _fileSystem.GetFileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) { newImage.Save(ImageFormat.Png, outputStream, 100); } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs index cb60975046..723e4fdd31 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs @@ -1,5 +1,7 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Plugins; @@ -26,11 +28,12 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications private readonly IJsonSerializer _json; private readonly INotificationsRepository _notificationsRepo; private readonly IUserManager _userManager; + private readonly IFileSystem _fileSystem; private readonly TimeSpan _frequency = TimeSpan.FromHours(6); private readonly TimeSpan _maxAge = TimeSpan.FromDays(31); - public RemoteNotifications(IApplicationPaths appPaths, ILogger logger, IHttpClient httpClient, IJsonSerializer json, INotificationsRepository notificationsRepo, IUserManager userManager) + public RemoteNotifications(IApplicationPaths appPaths, ILogger logger, IHttpClient httpClient, IJsonSerializer json, INotificationsRepository notificationsRepo, IUserManager userManager, IFileSystem fileSystem) { _appPaths = appPaths; _logger = logger; @@ -38,6 +41,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications _json = json; _notificationsRepo = notificationsRepo; _userManager = userManager; + _fileSystem = fileSystem; } /// @@ -56,7 +60,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications { var dataPath = Path.Combine(_appPaths.DataPath, "remotenotifications.json"); - var lastRunTime = File.Exists(dataPath) ? File.GetLastWriteTimeUtc(dataPath) : DateTime.MinValue; + var lastRunTime = File.Exists(dataPath) ? _fileSystem.GetLastWriteTimeUtc(dataPath) : DateTime.MinValue; try { diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs index 356c6fc4d4..e6942fae6a 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.Logging; using ServiceStack.Common; using ServiceStack.Common.Web; @@ -25,13 +26,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// The _logger /// private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. /// /// The log manager. - public HttpResultFactory(ILogManager logManager) + public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem) { + _fileSystem = fileSystem; _logger = logManager.GetLogger("HttpResultFactory"); } @@ -288,7 +291,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer throw new ArgumentException("FileShare must be either Read or ReadWrite"); } - var dateModified = File.GetLastWriteTimeUtc(path); + var dateModified = _fileSystem.GetLastWriteTimeUtc(path); var cacheKey = path + dateModified.Ticks; @@ -303,7 +306,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// Stream. private Stream GetFileStream(string path, FileShare fileShare) { - return new FileStream(path, FileMode.Open, FileAccess.Read, fileShare, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous); + return _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, fileShare, true); } /// diff --git a/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs b/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs index 0ddd407cf0..330469877a 100644 --- a/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs +++ b/MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; @@ -321,6 +322,18 @@ namespace MediaBrowser.Server.Implementations.IO /// The source of the event. /// The instance containing the event data. void watcher_Changed(object sender, FileSystemEventArgs e) + { + try + { + OnWatcherChanged(e); + } + catch (IOException ex) + { + Logger.ErrorException("IOException in watcher changed", ex); + } + } + + private void OnWatcherChanged(FileSystemEventArgs e) { var name = e.Name; @@ -437,7 +450,7 @@ namespace MediaBrowser.Server.Implementations.IO try { - using (new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) + using (_fileSystem.GetFileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) { //file is not locked return false; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 0c5d55a3b7..1aa9e5b9c1 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Progress; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; @@ -772,7 +773,7 @@ namespace MediaBrowser.Server.Implementations.Library Name = name, Id = id, DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo), - DateModified = fileInfo.LastWriteTimeUtc, + DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo), Path = path }; isNew = true; diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs index 22bd2e0e6f..96057f8b7e 100644 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs index c9777e54a6..0d428742ce 100644 --- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs +++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Localization; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; @@ -30,13 +32,16 @@ namespace MediaBrowser.Server.Implementations.Localization private readonly ConcurrentDictionary> _allParentalRatings = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + private readonly IFileSystem _fileSystem; + /// /// Initializes a new instance of the class. /// /// The configuration manager. - public LocalizationManager(IServerConfigurationManager configurationManager) + public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem) { _configurationManager = configurationManager; + _fileSystem = fileSystem; ExtractAll(); } @@ -65,7 +70,7 @@ namespace MediaBrowser.Server.Implementations.Localization { using (var stream = type.Assembly.GetManifestResourceStream(resource)) { - using (var fs = new FileStream(Path.Combine(localizationPath, filename), FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var fs = _fileSystem.GetFileStream(Path.Combine(localizationPath, filename), FileMode.Create, FileAccess.Write, FileShare.Read)) { stream.CopyTo(fs); } diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index 785bbca7c1..a66f9c56ba 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; @@ -53,6 +54,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder /// The FF probe resource pool /// private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(1, 1); + private readonly IFileSystem _fileSystem; public string FFMpegPath { get; private set; } @@ -61,12 +63,13 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder public string Version { get; private set; } public MediaEncoder(ILogger logger, IApplicationPaths appPaths, - IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version) + IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IFileSystem fileSystem) { _logger = logger; _appPaths = appPaths; _jsonSerializer = jsonSerializer; Version = version; + _fileSystem = fileSystem; FFProbePath = ffProbePath; FFMpegPath = ffMpegPath; } @@ -458,8 +461,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-convert-" + Guid.NewGuid() + ".txt"); - var logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, - StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous); + var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); try { @@ -685,7 +687,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-extract-" + Guid.NewGuid() + ".txt"); - var logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous); + var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); try { diff --git a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs index 3bdef9226e..cbca2ba763 100644 --- a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs +++ b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs @@ -35,16 +35,18 @@ namespace MediaBrowser.Server.Implementations.Providers /// The _directory watchers /// private readonly IDirectoryWatchers _directoryWatchers; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. /// /// The config. /// The directory watchers. - public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers) + public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem) { _config = config; _directoryWatchers = directoryWatchers; + _fileSystem = fileSystem; _remoteImageCache = new FileSystemRepository(config.ApplicationPaths.DownloadedImagesDataPath); } @@ -176,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.Providers } } - using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index 1b086a0f91..af89122db1 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -50,6 +50,7 @@ namespace MediaBrowser.Server.Implementations.Providers private BaseMetadataProvider[] MetadataProviders { get; set; } private IImageProvider[] ImageProviders { get; set; } + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -58,12 +59,13 @@ namespace MediaBrowser.Server.Implementations.Providers /// The configuration manager. /// The directory watchers. /// The log manager. - public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager) + public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager, IFileSystem fileSystem) { _logger = logManager.GetLogger("ProviderManager"); _httpClient = httpClient; ConfigurationManager = configurationManager; _directoryWatchers = directoryWatchers; + _fileSystem = fileSystem; } /// @@ -293,7 +295,7 @@ namespace MediaBrowser.Server.Implementations.Providers { using (dataToSave) { - using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await dataToSave.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); } @@ -347,7 +349,7 @@ namespace MediaBrowser.Server.Implementations.Providers /// Task. public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, string sourceUrl, CancellationToken cancellationToken) { - return new ImageSaver(ConfigurationManager, _directoryWatchers).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken); + return new ImageSaver(ConfigurationManager, _directoryWatchers, _fileSystem).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken); } /// diff --git a/MediaBrowser.Server.Mono/IO/FileSystemFactory.cs b/MediaBrowser.Server.Mono/IO/FileSystemFactory.cs new file mode 100644 index 0000000000..4a424a2824 --- /dev/null +++ b/MediaBrowser.Server.Mono/IO/FileSystemFactory.cs @@ -0,0 +1,21 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Common.Implementations.IO; + +namespace MediaBrowser.ServerApplication.IO +{ + /// + /// Class FileSystemFactory + /// + public static class FileSystemFactory + { + /// + /// Creates the file system manager. + /// + /// IFileSystem. + public static IFileSystem CreateFileSystemManager(ILogManager logManager) + { + return new CommonFileSystem(logManager.GetLogger("FileSystem"), false); + } + } +} diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index 2c99f3370a..900169c707 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -88,6 +88,7 @@ FFMpeg\FFMpegDownloader.cs + @@ -129,6 +130,7 @@ + diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs index 6d3cbcf26b..057a2456f5 100644 --- a/MediaBrowser.Server.Mono/Program.cs +++ b/MediaBrowser.Server.Mono/Program.cs @@ -17,6 +17,7 @@ using System.Security.Cryptography.X509Certificates; using Gtk; using Gdk; using System.Threading.Tasks; +using System.Reflection; namespace MediaBrowser.Server.Mono { @@ -203,6 +204,8 @@ namespace MediaBrowser.Server.Mono logger.Info("Server: {0}", Environment.MachineName); logger.Info("Operating system: {0}", Environment.OSVersion.ToString()); + + MonoBug11817WorkAround.Apply (); } /// @@ -280,4 +283,34 @@ namespace MediaBrowser.Server.Mono return true; } } + + public class MonoBug11817WorkAround + { + public static void Apply() + { + var property = typeof(TimeZoneInfo).GetProperty("TimeZoneDirectory", BindingFlags.Static | BindingFlags.NonPublic); + + if (property == null) return; + + var zoneInfo = FindZoneInfoFolder(); + property.SetValue(null, zoneInfo, new object[0]); + } + + public static string FindZoneInfoFolder() + { + var current = new DirectoryInfo(Directory.GetCurrentDirectory()); + + while(current != null) + { + var zoneinfoTestPath = Path.Combine(current.FullName, "zoneinfo"); + + if (Directory.Exists(zoneinfoTestPath)) + return zoneinfoTestPath; + + current = current.Parent; + } + + return null; + } + } } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 196d065805..0ec1d68135 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -5,6 +5,7 @@ using MediaBrowser.Common.Constants; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Implementations; using MediaBrowser.Common.Implementations.ScheduledTasks; +using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -170,8 +171,6 @@ namespace MediaBrowser.ServerApplication private Task _httpServerCreationTask; - private IFileSystem FileSystemManager { get; set; } - /// /// Initializes a new instance of the class. /// @@ -237,7 +236,7 @@ namespace MediaBrowser.ServerApplication await base.RegisterResources().ConfigureAwait(false); - RegisterSingleInstance(new HttpResultFactory(LogManager)); + RegisterSingleInstance(new HttpResultFactory(LogManager, FileSystemManager)); RegisterSingleInstance(this); RegisterSingleInstance(ApplicationPaths); @@ -249,9 +248,6 @@ namespace MediaBrowser.ServerApplication RegisterSingleInstance(() => new BdInfoExaminer()); - FileSystemManager = FileSystemFactory.CreateFileSystemManager(LogManager); - RegisterSingleInstance(FileSystemManager); - var mediaEncoderTask = RegisterMediaEncoder(); UserDataManager = new UserDataManager(LogManager); @@ -275,7 +271,7 @@ namespace MediaBrowser.ServerApplication DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(DirectoryWatchers); - ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager); + ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager, FileSystemManager); RegisterSingleInstance(ProviderManager); RegisterSingleInstance(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager)); @@ -289,10 +285,10 @@ namespace MediaBrowser.ServerApplication ServerManager = new ServerManager(this, JsonSerializer, Logger, ServerConfigurationManager); RegisterSingleInstance(ServerManager); - LocalizationManager = new LocalizationManager(ServerConfigurationManager); + LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(LocalizationManager); - ImageProcessor = new ImageProcessor(Logger, ServerConfigurationManager.ApplicationPaths); + ImageProcessor = new ImageProcessor(Logger, ServerConfigurationManager.ApplicationPaths, FileSystemManager); RegisterSingleInstance(ImageProcessor); DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor); @@ -317,15 +313,20 @@ namespace MediaBrowser.ServerApplication return new NetworkManager(); } + protected override IFileSystem CreateFileSystemManager() + { + return FileSystemFactory.CreateFileSystemManager(LogManager); + } + /// /// Registers the media encoder. /// /// Task. private async Task RegisterMediaEncoder() { - var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient).GetFFMpegInfo().ConfigureAwait(false); + var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager).GetFFMpegInfo().ConfigureAwait(false); - MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ApplicationPaths, JsonSerializer, info.Path, info.ProbePath, info.Version); + MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ApplicationPaths, JsonSerializer, info.Path, info.ProbePath, info.Version, FileSystemManager); RegisterSingleInstance(MediaEncoder); } @@ -335,7 +336,7 @@ namespace MediaBrowser.ServerApplication private void SetKernelProperties() { Parallel.Invoke( - () => ServerKernel.FFMpegManager = new FFMpegManager(ApplicationPaths, MediaEncoder, Logger, ItemRepository), + () => ServerKernel.FFMpegManager = new FFMpegManager(ApplicationPaths, MediaEncoder, Logger, ItemRepository, FileSystemManager), () => LocalizedStrings.StringFiles = GetExports(), SetStaticProperties ); diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs index 69025c9eb0..e8af0a13e7 100644 --- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs +++ b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; @@ -20,18 +21,20 @@ namespace MediaBrowser.ServerApplication.FFMpeg private readonly IApplicationPaths _appPaths; private readonly ILogger _logger; private readonly IZipClient _zipClient; + private readonly IFileSystem _fileSystem; private readonly string[] _fontUrls = new[] { "https://www.dropbox.com/s/pj847twf7riq0j7/ARIALUNI.7z?dl=1" }; - public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient) + public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem) { _logger = logger; _appPaths = appPaths; _httpClient = httpClient; _zipClient = zipClient; + _fileSystem = fileSystem; } public async Task GetFFMpegInfo() @@ -272,9 +275,8 @@ namespace MediaBrowser.ServerApplication.FFMpeg var bytes = Encoding.UTF8.GetBytes(contents); - using (var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write, - FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, - FileOptions.Asynchronous)) + using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileMode.Create, FileAccess.Write, + FileShare.Read, true)) { await fileStream.WriteAsync(bytes, 0, bytes.Length); } diff --git a/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs b/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs index ab7802e17d..698c4b616f 100644 --- a/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs +++ b/MediaBrowser.ServerApplication/IO/FileSystemFactory.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.IO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.Logging; namespace MediaBrowser.ServerApplication.IO diff --git a/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs b/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs index 0101b42498..5320c52507 100644 --- a/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs +++ b/MediaBrowser.ServerApplication/IO/NativeFileSystem.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Logging; +using MediaBrowser.Common.Implementations.IO; +using MediaBrowser.Model.Logging; using System; using System.IO; using System.Runtime.InteropServices; @@ -10,7 +11,7 @@ namespace MediaBrowser.ServerApplication.IO public class NativeFileSystem : CommonFileSystem { public NativeFileSystem(ILogger logger) - : base(logger) + : base(logger, true) { } diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index d191247ee0..f24283e70d 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -190,7 +190,6 @@ - diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 2c6b5532b8..1c6cdad39b 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -5,6 +5,7 @@ using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Logging; @@ -118,6 +119,7 @@ namespace MediaBrowser.WebDashboard.Api private readonly ISessionManager _sessionManager; private readonly IDtoService _dtoService; + private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. @@ -126,13 +128,14 @@ namespace MediaBrowser.WebDashboard.Api /// The app host. /// The server configuration manager. /// The session manager. - public DashboardService(ITaskManager taskManager, IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, ISessionManager sessionManager, IDtoService dtoService) + public DashboardService(ITaskManager taskManager, IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, ISessionManager sessionManager, IDtoService dtoService, IFileSystem fileSystem) { _taskManager = taskManager; _appHost = appHost; _serverConfigurationManager = serverConfigurationManager; _sessionManager = sessionManager; _dtoService = dtoService; + _fileSystem = fileSystem; } /// @@ -324,7 +327,7 @@ namespace MediaBrowser.WebDashboard.Api /// Task{Stream}. private Stream GetRawResourceStream(string path) { - return new FileStream(GetDashboardResourcePath(path), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, true); + return _fileSystem.GetFileStream(GetDashboardResourcePath(path), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true); } /// @@ -611,7 +614,7 @@ namespace MediaBrowser.WebDashboard.Api { path = GetDashboardResourcePath(path); - using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, true)) + using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { using (var streamReader = new StreamReader(fs)) { diff --git a/MediaBrowser.sln.GhostDoc.xml b/MediaBrowser.sln.GhostDoc.xml new file mode 100644 index 0000000000..73f7a08ba7 --- /dev/null +++ b/MediaBrowser.sln.GhostDoc.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + D:\Development\MediaBrowser\Help + + + true + false + false + false + + + true + false + false + false + true + false + + + true + + + + + + From 882d0681e68c5e0ae663cca75752e4df765c8dd5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 31 Oct 2013 11:39:09 -0400 Subject: [PATCH 25/25] #551 - Add manual image selection for movies --- .../Providers/RemoteImageInfo.cs | 8 ++ .../Movies/ManualMovieDbImageProvider.cs | 96 ++++++++++++------- .../Movies/MovieDbImagesProvider.cs | 68 ++++--------- .../Movies/TmdbPersonProvider.cs | 1 - .../TV/SeriesPostScanTask.cs | 23 ++++- MediaBrowser.Providers/TV/TvdbPrescanTask.cs | 8 +- MediaBrowser.WebDashboard/ApiClient.js | 18 ++++ MediaBrowser.WebDashboard/packages.config | 2 +- 8 files changed, 129 insertions(+), 95 deletions(-) diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs index bb2a3cb69c..1a281f07d2 100644 --- a/MediaBrowser.Model/Providers/RemoteImageInfo.cs +++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs @@ -1,4 +1,6 @@  +using MediaBrowser.Model.Entities; + namespace MediaBrowser.Model.Providers { /// @@ -47,5 +49,11 @@ namespace MediaBrowser.Model.Providers /// /// The language. public string Language { get; set; } + + /// + /// Gets or sets the type. + /// + /// The type. + public ImageType Type { get; set; } } } diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs index a8f847b79f..f84845af7d 100644 --- a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -40,44 +41,50 @@ namespace MediaBrowser.Providers.Movies public async Task> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken) { - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); - var results = MovieDbImagesProvider.FetchImages(item, _jsonSerializer); + return images.Where(i => i.Type == imageType); + } + + public async Task> GetAllImages(BaseItem item, CancellationToken cancellationToken) + { + var list = new List(); + + var results = FetchImages(item, _jsonSerializer); + + if (results == null) + { + return list; + } + + var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.base_url + "original"; - if (imageType == ImageType.Primary) + list.AddRange(GetPosters(results, item).Select(i => new RemoteImageInfo { - var sources = GetPosters(results, item); + Url = tmdbImageUrl + i.file_path, + CommunityRating = i.vote_average, + VoteCount = i.vote_count, + Width = i.width, + Height = i.height, + Language = i.iso_639_1, + ProviderName = Name, + Type = ImageType.Primary + })); - return sources.Select(i => new RemoteImageInfo - { - Url = tmdbImageUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, - Language = i.iso_639_1, - ProviderName = Name - }); - } - - if (imageType == ImageType.Backdrop) + list.AddRange(GetBackdrops(results, item).Select(i => new RemoteImageInfo { - var sources = GetBackdrops(results, item); - - return sources.Select(i => new RemoteImageInfo - { - Url = tmdbImageUrl + i.file_path, - CommunityRating = i.vote_average, - VoteCount = i.vote_count, - Width = i.width, - Height = i.height, - ProviderName = Name - }); - } - - throw new ArgumentException("Unrecognized ImageType: " + imageType); + Url = tmdbImageUrl + i.file_path, + CommunityRating = i.vote_average, + VoteCount = i.vote_count, + Width = i.width, + Height = i.height, + ProviderName = Name, + Type = ImageType.Backdrop + })); + + return list; } /// @@ -86,7 +93,7 @@ namespace MediaBrowser.Providers.Movies /// The images. /// The item. /// IEnumerable{MovieDbProvider.Poster}. - public IEnumerable GetPosters(MovieDbProvider.Images images, BaseItem item) + private IEnumerable GetPosters(MovieDbProvider.Images images, BaseItem item) { var language = _config.Configuration.PreferredMetadataLanguage; @@ -126,7 +133,7 @@ namespace MediaBrowser.Providers.Movies /// The images. /// The item. /// IEnumerable{MovieDbProvider.Backdrop}. - public IEnumerable GetBackdrops(MovieDbProvider.Images images, BaseItem item) + private IEnumerable GetBackdrops(MovieDbProvider.Images images, BaseItem item) { var eligibleBackdrops = images.backdrops == null ? new List() : images.backdrops.Where(i => i.width >= _config.Configuration.MinMovieBackdropWidth) @@ -134,5 +141,28 @@ namespace MediaBrowser.Providers.Movies return eligibleBackdrops.OrderByDescending(i => i.vote_average); } + + /// + /// Fetches the images. + /// + /// The item. + /// The json serializer. + /// Task{MovieImages}. + private MovieDbProvider.Images FetchImages(BaseItem item, IJsonSerializer jsonSerializer) + { + var path = MovieDbProvider.Current.GetDataFilePath(item, "default"); + + if (!string.IsNullOrEmpty(path)) + { + var fileInfo = new FileInfo(path); + + if (fileInfo.Exists) + { + return jsonSerializer.DeserializeFromFile(path).images; + } + } + + return null; + } } } diff --git a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs index f2504da9ab..e34cbc54f0 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs @@ -7,6 +7,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; @@ -152,7 +153,7 @@ namespace MediaBrowser.Providers.Movies { return false; } - + var path = MovieDbProvider.Current.GetDataFilePath(item, "default"); if (!string.IsNullOrEmpty(path)) @@ -179,45 +180,18 @@ namespace MediaBrowser.Providers.Movies { var id = item.GetProviderId(MetadataProviders.Tmdb); - var status = ProviderRefreshStatus.Success; - if (!string.IsNullOrEmpty(id)) { - var images = FetchImages(item, _jsonSerializer); + var images = await new ManualMovieDbImageProvider(_jsonSerializer, ConfigurationManager).GetAllImages(item, + cancellationToken).ConfigureAwait(false); - if (images != null) - { - status = await ProcessImages(item, images, cancellationToken).ConfigureAwait(false); - } + await ProcessImages(item, images.ToList(), cancellationToken).ConfigureAwait(false); } - SetLastRefreshed(item, DateTime.UtcNow, status); + SetLastRefreshed(item, DateTime.UtcNow); return true; } - /// - /// Fetches the images. - /// - /// The item. - /// The json serializer. - /// Task{MovieImages}. - internal static MovieDbProvider.Images FetchImages(BaseItem item, IJsonSerializer jsonSerializer) - { - var path = MovieDbProvider.Current.GetDataFilePath(item, "default"); - - if (!string.IsNullOrEmpty(path)) - { - var fileInfo = new FileInfo(path); - - if (fileInfo.Exists) - { - return jsonSerializer.DeserializeFromFile(path).images; - } - } - - return null; - } - /// /// Processes the images. /// @@ -225,26 +199,20 @@ namespace MediaBrowser.Providers.Movies /// The images. /// The cancellation token /// Task. - private async Task ProcessImages(BaseItem item, MovieDbProvider.Images images, CancellationToken cancellationToken) + private async Task ProcessImages(BaseItem item, List images, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var status = ProviderRefreshStatus.Success; - - var eligiblePosters = new ManualMovieDbImageProvider(_jsonSerializer, ConfigurationManager).GetPosters(images, item) + var eligiblePosters = images + .Where(i => i.Type == ImageType.Primary) .ToList(); // poster if (eligiblePosters.Count > 0 && !item.HasImage(ImageType.Primary)) { - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.base_url + "original"; - // get highest rated poster for our language - var poster = eligiblePosters[0]; - var url = tmdbImageUrl + poster.file_path; + var url = poster.Url; var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions { @@ -253,26 +221,24 @@ namespace MediaBrowser.Providers.Movies }).ConfigureAwait(false); - await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(poster.file_path), ImageType.Primary, null, url, cancellationToken) + await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(url), ImageType.Primary, null, url, cancellationToken) .ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); - var eligibleBackdrops = new ManualMovieDbImageProvider(_jsonSerializer, ConfigurationManager).GetBackdrops(images, item).ToList(); + var eligibleBackdrops = images + .Where(i => i.Type == ImageType.Backdrop) + .ToList(); var backdropLimit = ConfigurationManager.Configuration.MaxBackdrops; // backdrops - only download if earlier providers didn't find any (fanart) if (eligibleBackdrops.Count > 0 && ConfigurationManager.Configuration.DownloadMovieImages.Backdrops && item.BackdropImagePaths.Count < backdropLimit) { - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.base_url + "original"; - for (var i = 0; i < eligibleBackdrops.Count; i++) { - var url = tmdbImageUrl + eligibleBackdrops[i].file_path; + var url = eligibleBackdrops[i].Url; if (!item.ContainsImageWithSourceUrl(url)) { @@ -283,7 +249,7 @@ namespace MediaBrowser.Providers.Movies }).ConfigureAwait(false); - await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(eligibleBackdrops[i].file_path), ImageType.Backdrop, item.BackdropImagePaths.Count, url, cancellationToken) + await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(url), ImageType.Backdrop, item.BackdropImagePaths.Count, url, cancellationToken) .ConfigureAwait(false); } @@ -293,8 +259,6 @@ namespace MediaBrowser.Providers.Movies } } } - - return status; } } } diff --git a/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs b/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs index 52f5ddab5c..7c38eb97b8 100644 --- a/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Movies/TmdbPersonProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index 6e99edb126..22ff917618 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -39,6 +39,13 @@ namespace MediaBrowser.Providers.TV private async Task RunInternal(IProgress progress, CancellationToken cancellationToken) { + if (!_config.Configuration.EnableInternetProviders || + _config.Configuration.InternetProviderExcludeTypes.Contains(typeof(Series).Name, StringComparer.OrdinalIgnoreCase)) + { + progress.Report(100); + return; + } + var seriesList = _libraryManager.RootFolder .RecursiveChildren .OfType() @@ -136,21 +143,27 @@ namespace MediaBrowser.Providers.TV .Where(i => i.Item1 != -1 && i.Item2 != -1) .ToList(); - var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(series, episodeLookup, cancellationToken).ConfigureAwait(false); + var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(series, episodeLookup, cancellationToken) + .ConfigureAwait(false); - var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(series, episodeLookup, cancellationToken).ConfigureAwait(false); + var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(series, episodeLookup, cancellationToken) + .ConfigureAwait(false); var hasNewEpisodes = false; if (_config.Configuration.EnableInternetProviders) { - hasNewEpisodes = await AddMissingEpisodes(series, seriesDataPath, episodeLookup, cancellationToken).ConfigureAwait(false); + hasNewEpisodes = await AddMissingEpisodes(series, seriesDataPath, episodeLookup, cancellationToken) + .ConfigureAwait(false); } if (hasNewEpisodes || anySeasonsRemoved || anyEpisodesRemoved) { - await series.RefreshMetadata(cancellationToken, true).ConfigureAwait(false); - await series.ValidateChildren(new Progress(), cancellationToken, true).ConfigureAwait(false); + await series.RefreshMetadata(cancellationToken, true) + .ConfigureAwait(false); + + await series.ValidateChildren(new Progress(), cancellationToken, true) + .ConfigureAwait(false); } } diff --git a/MediaBrowser.Providers/TV/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TvdbPrescanTask.cs index 4926b92f73..d77698725a 100644 --- a/MediaBrowser.Providers/TV/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TvdbPrescanTask.cs @@ -1,12 +1,13 @@ -using System.Globalization; -using MediaBrowser.Common.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -69,7 +70,8 @@ namespace MediaBrowser.Providers.TV /// Task. public async Task Run(IProgress progress, CancellationToken cancellationToken) { - if (!_config.Configuration.EnableInternetProviders) + if (!_config.Configuration.EnableInternetProviders || + _config.Configuration.InternetProviderExcludeTypes.Contains(typeof(Series).Name, StringComparer.OrdinalIgnoreCase)) { progress.Report(100); return; diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index 8e64e4300d..3606396652 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -306,6 +306,24 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi }); }; + self.getAvailableRemoteImages = function (itemId, imageType) { + + if (!itemId) { + throw new Error("null itemId"); + } + if (!imageType) { + throw new Error("null imageType"); + } + + var url = self.getUrl("Items/" + itemId + "/RemoteImages/" + imageType); + + return self.ajax({ + type: "GET", + url: url, + dataType: "json" + }); + }; + /** * Gets the current server status */ diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 02daa2cae4..22560b1331 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file