From 9f0e6d17c8653328316cde3a1b0b3ce63b5960ff Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 12 Jun 2017 03:11:54 -0400 Subject: add hevc cuvid --- Emby.Server.Implementations/Dto/DtoService.cs | 12 +----------- .../LiveTv/Listings/SchedulesDirect.cs | 2 +- Emby.Server.Implementations/LiveTv/LiveTvManager.cs | 17 ++++++++++------- Emby.Server.Implementations/Session/SessionManager.cs | 1 - 4 files changed, 12 insertions(+), 20 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 6bf58455f9..ae988f938e 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1058,18 +1058,8 @@ namespace Emby.Server.Implementations.Dto dto.CommunityRating = item.CommunityRating; } - //if (item.IsFolder) - //{ - // var folder = (Folder)item; - - // if (fields.Contains(ItemFields.IndexOptions)) - // { - // dto.IndexOptions = folder.IndexByOptionStrings.ToArray(); - // } - //} - var supportsPlaceHolders = item as ISupportsPlaceHolders; - if (supportsPlaceHolders != null) + if (supportsPlaceHolders != null && supportsPlaceHolders.IsPlaceHolder) { dto.IsPlaceHolder = supportsPlaceHolders.IsPlaceHolder; } diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index bba625cd12..97197a0abb 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -174,7 +174,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase)).ToList(); var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase)).ToList(); - double desiredAspect = IsMovie(programEntry) ? 0.666666667 : wideAspect; + double desiredAspect = 0.666666667; programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, desiredAspect) ?? GetProgramImage(ApiUrl, allImages, true, desiredAspect); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 4ad411c19b..1a5e6bddd4 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -742,7 +742,7 @@ namespace Emby.Server.Implementations.LiveTv else { // Increment this whenver some internal change deems it necessary - var etag = info.Etag + "4"; + var etag = info.Etag + "5"; if (!string.Equals(etag, item.ExternalEtag, StringComparison.OrdinalIgnoreCase)) { @@ -1422,13 +1422,16 @@ namespace Emby.Server.Implementations.LiveTv await _libraryManager.UpdateItem(program, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } - foreach (var program in newPrograms) + if (!(service is EmbyTV.EmbyTV)) { - _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); - } - foreach (var program in updatedPrograms) - { - _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); + foreach (var program in newPrograms) + { + _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); + } + foreach (var program in updatedPrograms) + { + _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); + } } currentChannel.IsMovie = isMovie; diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index a5582ddc55..6bfa90498f 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1671,7 +1671,6 @@ namespace Emby.Server.Implementations.Session dtoOptions.Fields.Remove(ItemFields.DisplayPreferencesId); dtoOptions.Fields.Remove(ItemFields.Etag); dtoOptions.Fields.Remove(ItemFields.ExternalEtag); - dtoOptions.Fields.Remove(ItemFields.IndexOptions); dtoOptions.Fields.Remove(ItemFields.InheritedParentalRatingValue); dtoOptions.Fields.Remove(ItemFields.ItemCounts); dtoOptions.Fields.Remove(ItemFields.Keywords); -- cgit v1.2.3 From 1663b258a71a164046007831e09ddd5f389b28a4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 13 Jun 2017 02:33:29 -0400 Subject: update home sections --- .../Data/SqliteItemRepository.cs | 9 -- .../Emby.Server.Implementations.csproj | 1 - .../LiveTv/LiveTvManager.cs | 12 --- .../LiveTv/ProgramImageProvider.cs | 103 --------------------- MediaBrowser.Api/BaseApiService.cs | 84 ++++++++--------- .../Entities/InternalItemsQuery.cs | 1 - MediaBrowser.Controller/LiveTv/LiveTvProgram.cs | 6 -- .../MediaEncoding/EncodingHelper.cs | 7 +- .../Encoder/EncoderValidator.cs | 8 +- MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 3 - MediaBrowser.Model/Users/UserPolicy.cs | 2 + 11 files changed, 57 insertions(+), 179 deletions(-) delete mode 100644 Emby.Server.Implementations/LiveTv/ProgramImageProvider.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index df3cecbf9d..e999f57532 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3929,15 +3929,6 @@ namespace Emby.Server.Implementations.Data } } - if (!string.IsNullOrWhiteSpace(query.SlugName)) - { - whereClauses.Add("CleanName=@SlugName"); - if (statement != null) - { - statement.TryBind("@SlugName", GetCleanValue(query.SlugName)); - } - } - if (!string.IsNullOrWhiteSpace(query.MinSortName)) { whereClauses.Add("SortName>=@MinSortName"); diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index df70680f15..4482bb13b2 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -167,7 +167,6 @@ - diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 1a5e6bddd4..8f62670e41 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1422,18 +1422,6 @@ namespace Emby.Server.Implementations.LiveTv await _libraryManager.UpdateItem(program, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } - if (!(service is EmbyTV.EmbyTV)) - { - foreach (var program in newPrograms) - { - _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); - } - foreach (var program in updatedPrograms) - { - _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem), RefreshPriority.Low); - } - } - currentChannel.IsMovie = isMovie; currentChannel.IsNews = isNews; currentChannel.IsSports = isSports; diff --git a/Emby.Server.Implementations/LiveTv/ProgramImageProvider.cs b/Emby.Server.Implementations/LiveTv/ProgramImageProvider.cs deleted file mode 100644 index 5cff88a676..0000000000 --- a/Emby.Server.Implementations/LiveTv/ProgramImageProvider.cs +++ /dev/null @@ -1,103 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace Emby.Server.Implementations.LiveTv -{ - public class ProgramImageProvider : IDynamicImageProvider, IHasItemChangeMonitor, IHasOrder - { - private readonly ILiveTvManager _liveTvManager; - - public ProgramImageProvider(ILiveTvManager liveTvManager) - { - _liveTvManager = liveTvManager; - } - - public IEnumerable GetSupportedImages(IHasImages item) - { - return new[] { ImageType.Primary }; - } - - private string GetItemExternalId(BaseItem item) - { - var externalId = item.ExternalId; - - if (string.IsNullOrWhiteSpace(externalId)) - { - externalId = item.GetProviderId("ProviderExternalId"); - } - - return externalId; - } - - public async Task GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken) - { - var liveTvItem = (LiveTvProgram)item; - - var imageResponse = new DynamicImageResponse(); - - var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, liveTvItem.ServiceName, StringComparison.OrdinalIgnoreCase)); - - if (service != null) - { - try - { - var channel = _liveTvManager.GetInternalChannel(liveTvItem.ChannelId); - - if (channel != null) - { - var response = await service.GetProgramImageAsync(GetItemExternalId(liveTvItem), GetItemExternalId(channel), cancellationToken).ConfigureAwait(false); - - if (response != null) - { - imageResponse.HasImage = true; - imageResponse.Stream = response.Stream; - imageResponse.Format = response.Format; - } - } - } - catch (NotImplementedException) - { - } - } - - return imageResponse; - } - - public string Name - { - get { return "Live TV Service Provider"; } - } - - public bool Supports(IHasImages item) - { - return item is LiveTvProgram; - } - - public int Order - { - get - { - // Let the better providers run first - return 100; - } - } - - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) - { - var liveTvItem = item as LiveTvProgram; - - if (liveTvItem != null) - { - return !liveTvItem.HasImage(ImageType.Primary); - } - return false; - } - } -} diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 0f1d240d0c..d3cc18d4be 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -178,13 +178,7 @@ namespace MediaBrowser.Api { if (name.IndexOf(BaseItem.SlugChar) != -1) { - var result = libraryManager.GetItemList(new InternalItemsQuery - { - SlugName = name, - IncludeItemTypes = new[] { typeof(MusicArtist).Name }, - DtoOptions = dtoOptions - - }).OfType().FirstOrDefault(); + var result = GetItemFromSlugName(libraryManager, name, dtoOptions); if (result != null) { @@ -199,13 +193,7 @@ namespace MediaBrowser.Api { if (name.IndexOf(BaseItem.SlugChar) != -1) { - var result = libraryManager.GetItemList(new InternalItemsQuery - { - SlugName = name, - IncludeItemTypes = new[] { typeof(Studio).Name }, - DtoOptions = dtoOptions - - }).OfType().FirstOrDefault(); + var result = GetItemFromSlugName(libraryManager, name, dtoOptions); if (result != null) { @@ -220,13 +208,7 @@ namespace MediaBrowser.Api { if (name.IndexOf(BaseItem.SlugChar) != -1) { - var result = libraryManager.GetItemList(new InternalItemsQuery - { - SlugName = name, - IncludeItemTypes = new[] { typeof(Genre).Name }, - DtoOptions = dtoOptions - - }).OfType().FirstOrDefault(); + var result = GetItemFromSlugName(libraryManager, name, dtoOptions); if (result != null) { @@ -241,13 +223,7 @@ namespace MediaBrowser.Api { if (name.IndexOf(BaseItem.SlugChar) != -1) { - var result = libraryManager.GetItemList(new InternalItemsQuery - { - SlugName = name, - IncludeItemTypes = new[] { typeof(MusicGenre).Name }, - DtoOptions = dtoOptions - - }).OfType().FirstOrDefault(); + var result = GetItemFromSlugName(libraryManager, name, dtoOptions); if (result != null) { @@ -262,13 +238,7 @@ namespace MediaBrowser.Api { if (name.IndexOf(BaseItem.SlugChar) != -1) { - var result = libraryManager.GetItemList(new InternalItemsQuery - { - SlugName = name, - IncludeItemTypes = new[] { typeof(GameGenre).Name }, - DtoOptions = dtoOptions - - }).OfType().FirstOrDefault(); + var result = GetItemFromSlugName(libraryManager, name, dtoOptions); if (result != null) { @@ -283,13 +253,7 @@ namespace MediaBrowser.Api { if (name.IndexOf(BaseItem.SlugChar) != -1) { - var result = libraryManager.GetItemList(new InternalItemsQuery - { - SlugName = name, - IncludeItemTypes = new[] { typeof(Person).Name }, - DtoOptions = dtoOptions - - }).OfType().FirstOrDefault(); + var result = GetItemFromSlugName(libraryManager, name, dtoOptions); if (result != null) { @@ -300,6 +264,42 @@ namespace MediaBrowser.Api return libraryManager.GetPerson(name); } + private T GetItemFromSlugName(ILibraryManager libraryManager, string name, DtoOptions dtoOptions) + where T : BaseItem, new() + { + var result = libraryManager.GetItemList(new InternalItemsQuery + { + Name = name.Replace(BaseItem.SlugChar, '&'), + IncludeItemTypes = new[] { typeof(T).Name }, + DtoOptions = dtoOptions + + }).OfType().FirstOrDefault(); + + if (result == null) + { + result = libraryManager.GetItemList(new InternalItemsQuery + { + Name = name.Replace(BaseItem.SlugChar, '/'), + IncludeItemTypes = new[] { typeof(T).Name }, + DtoOptions = dtoOptions + + }).OfType().FirstOrDefault(); + } + + if (result == null) + { + result = libraryManager.GetItemList(new InternalItemsQuery + { + Name = name.Replace(BaseItem.SlugChar, '?'), + IncludeItemTypes = new[] { typeof(T).Name }, + DtoOptions = dtoOptions + + }).OfType().FirstOrDefault(); + } + + return result as T; + } + protected string GetPathValue(int index) { var pathInfo = Parse(Request.PathInfo); diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 4d96c082fb..608e3f56c3 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -56,7 +56,6 @@ namespace MediaBrowser.Controller.Entities public string Path { get; set; } public string PathNotStartsWith { get; set; } public string Name { get; set; } - public string SlugName { get; set; } public string Person { get; set; } public string[] PersonIds { get; set; } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 87fc581307..9f55a9ff23 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -221,12 +221,6 @@ namespace MediaBrowser.Controller.LiveTv public override bool IsInternetMetadataEnabled() { - if (IsMovie) - { - var options = (LiveTvOptions)ConfigurationManager.GetConfiguration("livetv"); - return options.EnableMovieProviders; - } - return false; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c14b1f416b..60583b95bf 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -233,7 +233,12 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - return codec; + if (_mediaEncoder.SupportsDecoder(codec)) + { + return codec; + } + + return null; } /// diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 338f58c167..59f3576ec8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -92,7 +92,13 @@ namespace MediaBrowser.MediaEncoding.Encoder "mpeg2_qsv", "vc1_qsv", "h264_cuvid", - "hevc_cuvid" + "hevc_cuvid", + "dts", + "ac3", + "aac", + "mp3", + "h264", + "hevc" }; foreach (var codec in required) diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 91ea977f7e..32153a11ca 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -1,13 +1,11 @@ using System.Collections.Generic; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Extensions; namespace MediaBrowser.Model.LiveTv { public class LiveTvOptions { public int? GuideDays { get; set; } - public bool EnableMovieProviders { get; set; } public string RecordingPath { get; set; } public string MovieRecordingPath { get; set; } public string SeriesRecordingPath { get; set; } @@ -30,7 +28,6 @@ namespace MediaBrowser.Model.LiveTv public LiveTvOptions() { - EnableMovieProviders = true; TunerHosts = new List(); ListingProviders = new List(); MediaLocationsCreated = new string[] { }; diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 2a987ceb14..e2a75c56be 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -70,6 +70,8 @@ namespace MediaBrowser.Model.Users public UserPolicy() { + EnableContentDeletion = true; + EnableSyncTranscoding = true; EnableMediaPlayback = true; -- cgit v1.2.3 From ccb5b14d772c2d1028fa9003ee64fde3cd4196b3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 15 Jun 2017 13:22:05 -0400 Subject: update series resolver --- Emby.Server.Core/ApplicationHost.cs | 2 - .../Emby.Server.Implementations.csproj | 2 - .../HttpServer/ResponseFilter.cs | 2 +- .../Library/LibraryManager.cs | 61 +- .../Library/Resolvers/Audio/AudioResolver.cs | 8 + .../Library/Resolvers/TV/SeriesResolver.cs | 11 +- .../Migrations/GuideMigration.cs | 49 -- .../Migrations/LibraryScanMigration.cs | 49 -- .../Configuration/ServerConfiguration.cs | 2 - MediaBrowser.Model/Net/MimeTypes.cs | 16 + SharedVersion.cs | 2 +- SocketHttpListener/Net/BoundaryType.cs | 17 + SocketHttpListener/Net/EntitySendFormat.cs | 14 + SocketHttpListener/Net/HttpConnection.cs | 8 +- SocketHttpListener/Net/HttpListenerContext.cs | 2 +- .../Net/HttpListenerResponse.Managed.cs | 329 +++++++++++ SocketHttpListener/Net/HttpListenerResponse.cs | 652 ++++++--------------- .../Net/HttpRequestStream.Managed.cs | 32 +- .../Net/HttpResponseStream.Managed.cs | 33 +- SocketHttpListener/Net/HttpStatusDescription.cs | 75 +++ SocketHttpListener/Net/WebHeaderEncoding.cs | 131 +++++ SocketHttpListener/SocketHttpListener.csproj | 6 + 22 files changed, 883 insertions(+), 620 deletions(-) delete mode 100644 Emby.Server.Implementations/Migrations/GuideMigration.cs delete mode 100644 Emby.Server.Implementations/Migrations/LibraryScanMigration.cs create mode 100644 SocketHttpListener/Net/BoundaryType.cs create mode 100644 SocketHttpListener/Net/EntitySendFormat.cs create mode 100644 SocketHttpListener/Net/HttpListenerResponse.Managed.cs create mode 100644 SocketHttpListener/Net/HttpStatusDescription.cs create mode 100644 SocketHttpListener/Net/WebHeaderEncoding.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs index 5e9024e1d3..7019fe0ded 100644 --- a/Emby.Server.Core/ApplicationHost.cs +++ b/Emby.Server.Core/ApplicationHost.cs @@ -511,8 +511,6 @@ namespace Emby.Server.Core { var migrations = new List { - new LibraryScanMigration(ServerConfigurationManager, TaskManager), - new GuideMigration(ServerConfigurationManager, TaskManager) }; foreach (var task in migrations) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 4482bb13b2..210448cd95 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -182,8 +182,6 @@ - - diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index 57eef5db03..ac36f8f516 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.HttpServer { if (!hasHeaders.Headers.ContainsKey("Server")) { - hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1, UPnP/1.0 DLNADOC/1.50"; + hasHeaders.Headers["Server"] = "Microsoft-NetCore/2.0, UPnP/1.0 DLNADOC/1.50"; //hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1"; } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a08c744748..15efd3d39c 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2505,9 +2505,32 @@ namespace Emby.Server.Implementations.Library public NamingOptions GetNamingOptions() { + return GetNamingOptions(true); + } + + public NamingOptions GetNamingOptions(bool allowOptimisticEpisodeDetection) + { + if (!allowOptimisticEpisodeDetection) + { + if (_namingOptionsWithoutOptimisticEpisodeDetection == null) + { + var namingOptions = new ExtendedNamingOptions(); + + InitNamingOptions(namingOptions); + namingOptions.EpisodeExpressions = namingOptions.EpisodeExpressions + .Where(i => i.IsNamed && !i.IsOptimistic) + .ToList(); + + _namingOptionsWithoutOptimisticEpisodeDetection = namingOptions; + } + + return _namingOptionsWithoutOptimisticEpisodeDetection; + } + return GetNamingOptions(new LibraryOptions()); } + private NamingOptions _namingOptionsWithoutOptimisticEpisodeDetection; private NamingOptions _namingOptions; private string[] _videoFileExtensions; public NamingOptions GetNamingOptions(LibraryOptions libraryOptions) @@ -2516,23 +2539,8 @@ namespace Emby.Server.Implementations.Library { var options = new ExtendedNamingOptions(); - // These cause apps to have problems - options.AudioFileExtensions.Remove(".m3u"); - options.AudioFileExtensions.Remove(".wpl"); - - //if (!libraryOptions.EnableArchiveMediaFiles) - { - options.AudioFileExtensions.Remove(".rar"); - options.AudioFileExtensions.Remove(".zip"); - } + InitNamingOptions(options); - //if (!libraryOptions.EnableArchiveMediaFiles) - { - options.VideoFileExtensions.Remove(".rar"); - options.VideoFileExtensions.Remove(".zip"); - } - - options.VideoFileExtensions.Add(".tp"); _namingOptions = options; _videoFileExtensions = _namingOptions.VideoFileExtensions.ToArray(); } @@ -2540,6 +2548,27 @@ namespace Emby.Server.Implementations.Library return _namingOptions; } + private void InitNamingOptions(NamingOptions options) + { + // These cause apps to have problems + options.AudioFileExtensions.Remove(".m3u"); + options.AudioFileExtensions.Remove(".wpl"); + + //if (!libraryOptions.EnableArchiveMediaFiles) + { + options.AudioFileExtensions.Remove(".rar"); + options.AudioFileExtensions.Remove(".zip"); + } + + //if (!libraryOptions.EnableArchiveMediaFiles) + { + options.VideoFileExtensions.Remove(".rar"); + options.VideoFileExtensions.Remove(".zip"); + } + + options.VideoFileExtensions.Add(".tp"); + } + public ItemLookupInfo ParseName(string name) { var resolver = new VideoResolver(GetNamingOptions(), new NullLogger()); diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 2e3d81474a..e2f2946db1 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using System; using MediaBrowser.Controller.Entities; +using System.IO; namespace Emby.Server.Implementations.Library.Resolvers.Audio { @@ -42,6 +43,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio if (_libraryManager.IsAudioFile(args.Path, libraryOptions)) { + if (string.Equals(Path.GetExtension(args.Path), ".cue", StringComparison.OrdinalIgnoreCase)) + { + // if audio file exists of same name, return null + + return null; + } + var collectionType = args.GetCollectionType(); var isMixed = string.IsNullOrWhiteSpace(collectionType); diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 60260e98a9..e1c18c9131 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -160,15 +160,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return true; } - var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(); - - // In mixed folders we need to be conservative and avoid expressions that may result in false positives (e.g. movies with numbers in the title) - if (!isTvContentType) - { - namingOptions.EpisodeExpressions = namingOptions.EpisodeExpressions - .Where(i => i.IsNamed && !i.IsOptimistic) - .ToList(); - } + var allowOptimisticEpisodeDetection = isTvContentType; + var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions(allowOptimisticEpisodeDetection); var episodeResolver = new MediaBrowser.Naming.TV.EpisodeResolver(namingOptions, new NullLogger()); var episodeInfo = episodeResolver.Resolve(fullName, false, false); diff --git a/Emby.Server.Implementations/Migrations/GuideMigration.cs b/Emby.Server.Implementations/Migrations/GuideMigration.cs deleted file mode 100644 index 78fb6c222d..0000000000 --- a/Emby.Server.Implementations/Migrations/GuideMigration.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; -using MediaBrowser.Common.Updates; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Tasks; -using MediaBrowser.Model.Updates; -using System.Linq; - -namespace Emby.Server.Implementations.Migrations -{ - public class GuideMigration : IVersionMigration - { - private readonly IServerConfigurationManager _config; - private readonly ITaskManager _taskManager; - - public GuideMigration(IServerConfigurationManager config, ITaskManager taskManager) - { - _config = config; - _taskManager = taskManager; - } - - public Task Run() - { - var name = "GuideRefresh3"; - - if (!_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase)) - { - Task.Run(() => - { - _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask) - .First(i => string.Equals(i.Key, "RefreshGuide", StringComparison.OrdinalIgnoreCase))); - }); - - var list = _config.Configuration.Migrations.ToList(); - list.Add(name); - _config.Configuration.Migrations = list.ToArray(); - _config.SaveConfiguration(); - } - - return Task.FromResult(true); - } - } -} diff --git a/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs b/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs deleted file mode 100644 index 9d7f67a4f7..0000000000 --- a/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; -using MediaBrowser.Common.Updates; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Tasks; -using MediaBrowser.Model.Updates; -using System.Linq; - -namespace Emby.Server.Implementations.Migrations -{ - public class LibraryScanMigration : IVersionMigration - { - private readonly IServerConfigurationManager _config; - private readonly ITaskManager _taskManager; - - public LibraryScanMigration(IServerConfigurationManager config, ITaskManager taskManager) - { - _config = config; - _taskManager = taskManager; - } - - public Task Run() - { - var name = "LibraryScan6"; - - if (!_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase)) - { - Task.Run(() => - { - _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask) - .First(i => string.Equals(i.Key, "RefreshLibrary", StringComparison.OrdinalIgnoreCase))); - }); - - var list = _config.Configuration.Migrations.ToList(); - list.Add(name); - _config.Configuration.Migrations = list.ToArray(); - _config.SaveConfiguration(); - } - - return Task.FromResult(true); - } - } -} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 4529de59a4..a570f7b10c 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -187,7 +187,6 @@ namespace MediaBrowser.Model.Configuration public bool DisplayCollectionsView { get; set; } public string[] LocalNetworkAddresses { get; set; } public string[] CodecsUsed { get; set; } - public string[] Migrations { get; set; } public bool EnableChannelView { get; set; } public bool EnableExternalContentInSuggestions { get; set; } @@ -203,7 +202,6 @@ namespace MediaBrowser.Model.Configuration { LocalNetworkAddresses = new string[] { }; CodecsUsed = new string[] { }; - Migrations = new string[] { }; ImageExtractionTimeoutMs = 0; EnableLocalizedGuids = true; PathSubstitutions = new PathSubstitution[] { }; diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 790f8a8f61..3c3c2bbc78 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -248,6 +248,22 @@ namespace MediaBrowser.Model.Net { return "audio/ac3"; } + if (StringHelper.EqualsIgnoreCase(ext, ".dsf")) + { + return "audio/dsf"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".m4b")) + { + return "audio/m4b"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".xsp")) + { + return "audio/xsp"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".dsp")) + { + return "audio/dsp"; + } // Playlists if (StringHelper.EqualsIgnoreCase(ext, ".m3u8")) diff --git a/SharedVersion.cs b/SharedVersion.cs index f362517731..adeff35086 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.3")] +[assembly: AssemblyVersion("3.2.20.4")] diff --git a/SocketHttpListener/Net/BoundaryType.cs b/SocketHttpListener/Net/BoundaryType.cs new file mode 100644 index 0000000000..c3ac00c0fd --- /dev/null +++ b/SocketHttpListener/Net/BoundaryType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SocketHttpListener.Net +{ + internal enum BoundaryType + { + ContentLength = 0, // Content-Length: XXX + Chunked = 1, // Transfer-Encoding: chunked + Multipart = 3, + None = 4, + Invalid = 5, + } +} diff --git a/SocketHttpListener/Net/EntitySendFormat.cs b/SocketHttpListener/Net/EntitySendFormat.cs new file mode 100644 index 0000000000..6e585bdc9b --- /dev/null +++ b/SocketHttpListener/Net/EntitySendFormat.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SocketHttpListener.Net +{ + internal enum EntitySendFormat + { + ContentLength = 0, // Content-Length: XXX + Chunked = 1, // Transfer-Encoding: chunked + } +} diff --git a/SocketHttpListener/Net/HttpConnection.cs b/SocketHttpListener/Net/HttpConnection.cs index 9c87ff0763..79491d6f9c 100644 --- a/SocketHttpListener/Net/HttpConnection.cs +++ b/SocketHttpListener/Net/HttpConnection.cs @@ -25,7 +25,7 @@ namespace SocketHttpListener.Net StringBuilder _currentLine; ListenerPrefix _prefix; HttpRequestStream _requestStream; - Stream _responseStream; + HttpResponseStream _responseStream; bool _chunked; int _reuses; bool _contextBound; @@ -202,7 +202,7 @@ namespace SocketHttpListener.Net return _requestStream; } - public Stream GetResponseStream(bool isExpect100Continue = false) + public HttpResponseStream GetResponseStream(bool isExpect100Continue = false) { // TODO: can we get this _stream before reading the input? if (_responseStream == null) @@ -423,14 +423,14 @@ namespace SocketHttpListener.Net HttpListenerResponse response = _context.Response; response.StatusCode = status; response.ContentType = "text/html"; - string description = HttpListenerResponse.GetStatusDescription(status); + string description = HttpStatusDescription.Get(status); string str; if (msg != null) str = string.Format("

{0} ({1})

", description, msg); else str = string.Format("

{0}

", description); - byte[] error = Encoding.Default.GetBytes(str); + byte[] error = _textEncoding.GetDefaultEncoding().GetBytes(str); response.Close(error, false); } catch diff --git a/SocketHttpListener/Net/HttpListenerContext.cs b/SocketHttpListener/Net/HttpListenerContext.cs index 58d769f22a..1bf39589d9 100644 --- a/SocketHttpListener/Net/HttpListenerContext.cs +++ b/SocketHttpListener/Net/HttpListenerContext.cs @@ -29,7 +29,7 @@ namespace SocketHttpListener.Net _memoryStreamFactory = memoryStreamFactory; _textEncoding = textEncoding; request = new HttpListenerRequest(this, _textEncoding); - response = new HttpListenerResponse(this, logger, _textEncoding, fileSystem); + response = new HttpListenerResponse(this, _textEncoding); } internal int ErrorStatus diff --git a/SocketHttpListener/Net/HttpListenerResponse.Managed.cs b/SocketHttpListener/Net/HttpListenerResponse.Managed.cs new file mode 100644 index 0000000000..52576fdf2f --- /dev/null +++ b/SocketHttpListener/Net/HttpListenerResponse.Managed.cs @@ -0,0 +1,329 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Model.Text; +using SocketHttpListener.Primitives; +using System.Threading; +using MediaBrowser.Model.IO; + +namespace SocketHttpListener.Net +{ + public sealed partial class HttpListenerResponse : IDisposable + { + private long _contentLength; + private Version _version = HttpVersion.Version11; + private int _statusCode = 200; + internal object _headersLock = new object(); + private bool _forceCloseChunked; + private ITextEncoding _textEncoding; + + internal HttpListenerResponse(HttpListenerContext context, ITextEncoding textEncoding) + { + _httpContext = context; + _textEncoding = textEncoding; + } + + internal bool ForceCloseChunked => _forceCloseChunked; + + private void EnsureResponseStream() + { + if (_responseStream == null) + { + _responseStream = _httpContext.Connection.GetResponseStream(); + } + } + + public Version ProtocolVersion + { + get => _version; + set + { + CheckDisposed(); + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1)) + { + throw new ArgumentException("Wrong version"); + } + + _version = new Version(value.Major, value.Minor); // match Windows behavior, trimming to just Major.Minor + } + } + + public int StatusCode + { + get => _statusCode; + set + { + CheckDisposed(); + + if (value < 100 || value > 999) + throw new ProtocolViolationException("Invalid status"); + + _statusCode = value; + } + } + + private void Dispose() => Close(true); + + public void Close() + { + if (Disposed) + return; + + Close(false); + } + + public void Abort() + { + if (Disposed) + return; + + Close(true); + } + + private void Close(bool force) + { + Disposed = true; + _httpContext.Connection.Close(force); + } + + public void Close(byte[] responseEntity, bool willBlock) + { + CheckDisposed(); + + if (responseEntity == null) + { + throw new ArgumentNullException(nameof(responseEntity)); + } + + if (!SentHeaders && _boundaryType != BoundaryType.Chunked) + { + ContentLength64 = responseEntity.Length; + } + + if (willBlock) + { + try + { + OutputStream.Write(responseEntity, 0, responseEntity.Length); + } + finally + { + Close(false); + } + } + else + { + OutputStream.BeginWrite(responseEntity, 0, responseEntity.Length, iar => + { + var thisRef = (HttpListenerResponse)iar.AsyncState; + try + { + thisRef.OutputStream.EndWrite(iar); + } + finally + { + thisRef.Close(false); + } + }, this); + } + } + + public void CopyFrom(HttpListenerResponse templateResponse) + { + _webHeaders.Clear(); + //_webHeaders.Add(templateResponse._webHeaders); + _contentLength = templateResponse._contentLength; + _statusCode = templateResponse._statusCode; + _statusDescription = templateResponse._statusDescription; + _keepAlive = templateResponse._keepAlive; + _version = templateResponse._version; + } + + internal void SendHeaders(bool closing, MemoryStream ms, bool isWebSocketHandshake = false) + { + if (!isWebSocketHandshake) + { + if (_webHeaders["Server"] == null) + { + _webHeaders.Set("Server", "Microsoft-NetCore/2.0"); + } + + if (_webHeaders["Date"] == null) + { + _webHeaders.Set("Date", DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture)); + } + + if (_boundaryType == BoundaryType.None) + { + if (HttpListenerRequest.ProtocolVersion <= HttpVersion.Version10) + { + _keepAlive = false; + } + else + { + _boundaryType = BoundaryType.Chunked; + } + + if (CanSendResponseBody(_httpContext.Response.StatusCode)) + { + _contentLength = -1; + } + else + { + _boundaryType = BoundaryType.ContentLength; + _contentLength = 0; + } + } + + if (_boundaryType != BoundaryType.Chunked) + { + if (_boundaryType != BoundaryType.ContentLength && closing) + { + _contentLength = CanSendResponseBody(_httpContext.Response.StatusCode) ? -1 : 0; + } + + if (_boundaryType == BoundaryType.ContentLength) + { + _webHeaders.Set("Content-Length", _contentLength.ToString("D", CultureInfo.InvariantCulture)); + } + } + + /* Apache forces closing the connection for these status codes: + * HttpStatusCode.BadRequest 400 + * HttpStatusCode.RequestTimeout 408 + * HttpStatusCode.LengthRequired 411 + * HttpStatusCode.RequestEntityTooLarge 413 + * HttpStatusCode.RequestUriTooLong 414 + * HttpStatusCode.InternalServerError 500 + * HttpStatusCode.ServiceUnavailable 503 + */ + bool conn_close = (_statusCode == (int)HttpStatusCode.BadRequest || _statusCode == (int)HttpStatusCode.RequestTimeout + || _statusCode == (int)HttpStatusCode.LengthRequired || _statusCode == (int)HttpStatusCode.RequestEntityTooLarge + || _statusCode == (int)HttpStatusCode.RequestUriTooLong || _statusCode == (int)HttpStatusCode.InternalServerError + || _statusCode == (int)HttpStatusCode.ServiceUnavailable); + + if (!conn_close) + { + conn_close = !_httpContext.Request.KeepAlive; + } + + // They sent both KeepAlive: true and Connection: close + if (!_keepAlive || conn_close) + { + _webHeaders.Set("Connection", "Close"); + conn_close = true; + } + + if (SendChunked) + { + _webHeaders.Set("Transfer-Encoding", "Chunked"); + } + + int reuses = _httpContext.Connection.Reuses; + if (reuses >= 100) + { + _forceCloseChunked = true; + if (!conn_close) + { + _webHeaders.Set("Connection", "Close"); + conn_close = true; + } + } + + if (HttpListenerRequest.ProtocolVersion <= HttpVersion.Version10) + { + if (_keepAlive) + { + Headers["Keep-Alive"] = "true"; + } + + if (!conn_close) + { + _webHeaders.Set("Connection", "Keep-Alive"); + } + } + + ComputeCookies(); + } + + Encoding encoding = _textEncoding.GetDefaultEncoding(); + StreamWriter writer = new StreamWriter(ms, encoding, 256); + writer.Write("HTTP/1.1 {0} ", _statusCode); // "1.1" matches Windows implementation, which ignores the response version + writer.Flush(); + byte[] statusDescriptionBytes = WebHeaderEncoding.GetBytes(StatusDescription); + ms.Write(statusDescriptionBytes, 0, statusDescriptionBytes.Length); + writer.Write("\r\n"); + + writer.Write(FormatHeaders(_webHeaders)); + writer.Flush(); + int preamble = encoding.GetPreamble().Length; + EnsureResponseStream(); + + /* Assumes that the ms was at position 0 */ + ms.Position = preamble; + SentHeaders = !isWebSocketHandshake; + } + + private static bool HeaderCanHaveEmptyValue(string name) => + !string.Equals(name, "Location", StringComparison.OrdinalIgnoreCase); + + private static string FormatHeaders(WebHeaderCollection headers) + { + var sb = new StringBuilder(); + + for (int i = 0; i < headers.Count; i++) + { + string key = headers.GetKey(i); + string[] values = headers.GetValues(i); + + int startingLength = sb.Length; + + sb.Append(key).Append(": "); + bool anyValues = false; + for (int j = 0; j < values.Length; j++) + { + string value = values[j]; + if (!string.IsNullOrWhiteSpace(value)) + { + if (anyValues) + { + sb.Append(", "); + } + sb.Append(value); + anyValues = true; + } + } + + if (anyValues || HeaderCanHaveEmptyValue(key)) + { + // Complete the header + sb.Append("\r\n"); + } + else + { + // Empty header; remove it. + sb.Length = startingLength; + } + } + + return sb.Append("\r\n").ToString(); + } + + private bool Disposed { get; set; } + internal bool SentHeaders { get; set; } + + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) + { + return ((HttpResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken); + } + } +} diff --git a/SocketHttpListener/Net/HttpListenerResponse.cs b/SocketHttpListener/Net/HttpListenerResponse.cs index da7aff0818..240c67930c 100644 --- a/SocketHttpListener/Net/HttpListenerResponse.cs +++ b/SocketHttpListener/Net/HttpListenerResponse.cs @@ -1,572 +1,302 @@ -using System; -using System.Globalization; +using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using System.Text; -using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Text; -using SocketHttpListener.Primitives; +using System.Globalization; +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Diagnostics; +using Microsoft.Win32.SafeHandles; namespace SocketHttpListener.Net { - public sealed class HttpListenerResponse : IDisposable + public sealed unsafe partial class HttpListenerResponse : IDisposable { - bool disposed; - Encoding content_encoding; - long content_length; - bool cl_set; - string content_type; - CookieCollection cookies; - WebHeaderCollection headers = new WebHeaderCollection(); - bool keep_alive = true; - Stream output_stream; - Version version = HttpVersion.Version11; - string location; - int status_code = 200; - string status_description = "OK"; - bool chunked; - HttpListenerContext context; - - internal bool HeadersSent; - internal object headers_lock = new object(); - - private readonly ILogger _logger; - private readonly ITextEncoding _textEncoding; - private readonly IFileSystem _fileSystem; - - internal HttpListenerResponse(HttpListenerContext context, ILogger logger, ITextEncoding textEncoding, IFileSystem fileSystem) - { - this.context = context; - _logger = logger; - _textEncoding = textEncoding; - _fileSystem = fileSystem; - } - - internal bool CloseConnection - { - get - { - return headers["Connection"] == "close"; - } - } - - public bool ForceCloseChunked - { - get { return false; } - } + private BoundaryType _boundaryType = BoundaryType.None; + private CookieCollection _cookies; + private HttpListenerContext _httpContext; + private bool _keepAlive = true; + private HttpResponseStream _responseStream; + private string _statusDescription; + private WebHeaderCollection _webHeaders = new WebHeaderCollection(); - public Encoding ContentEncoding + public WebHeaderCollection Headers { - get - { - if (content_encoding == null) - content_encoding = _textEncoding.GetDefaultEncoding(); - return content_encoding; - } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - content_encoding = value; - } + get => _webHeaders; } - public long ContentLength64 - { - get { return content_length; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - if (HeadersSent) - throw new InvalidOperationException("Cannot be changed after headers are sent."); - - if (value < 0) - throw new ArgumentOutOfRangeException("Must be >= 0", "value"); - - cl_set = true; - content_length = value; - } - } + public Encoding ContentEncoding { get; set; } public string ContentType { - get { return content_type; } + get => Headers["Content-Type"]; set { - // TODO: is null ok? - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - content_type = value; + CheckDisposed(); + if (string.IsNullOrEmpty(value)) + { + Headers.Remove("Content-Type"); + } + else + { + Headers.Set("Content-Type", value); + } } } - // RFC 2109, 2965 + the netscape specification at http://wp.netscape.com/newsref/std/cookie_spec.html - public CookieCollection Cookies - { - get - { - if (cookies == null) - cookies = new CookieCollection(); - return cookies; - } - set { cookies = value; } // null allowed? - } + private HttpListenerContext HttpListenerContext => _httpContext; - public WebHeaderCollection Headers - { - get { return headers; } - set - { - /** - * "If you attempt to set a Content-Length, Keep-Alive, Transfer-Encoding, or - * WWW-Authenticate header using the Headers property, an exception will be - * thrown. Use the KeepAlive or ContentLength64 properties to set these headers. - * You cannot set the Transfer-Encoding or WWW-Authenticate headers manually." - */ - // TODO: check if this is marked readonly after headers are sent. - headers = value; - } - } + private HttpListenerRequest HttpListenerRequest => HttpListenerContext.Request; - public bool KeepAlive + internal EntitySendFormat EntitySendFormat { - get { return keep_alive; } + get => (EntitySendFormat)_boundaryType; set { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - keep_alive = value; + CheckDisposed(); + CheckSentHeaders(); + if (value == EntitySendFormat.Chunked && HttpListenerRequest.ProtocolVersion.Minor == 0) + { + throw new ProtocolViolationException("net_nochunkuploadonhttp10"); + } + _boundaryType = (BoundaryType)value; + if (value != EntitySendFormat.ContentLength) + { + _contentLength = -1; + } } } - public Stream OutputStream + public bool SendChunked { - get - { - if (output_stream == null) - output_stream = context.Connection.GetResponseStream(); - return output_stream; - } + get => EntitySendFormat == EntitySendFormat.Chunked; + set => EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength; } - public Version ProtocolVersion + // We MUST NOT send message-body when we send responses with these Status codes + private static readonly int[] s_noResponseBody = { 100, 101, 204, 205, 304 }; + + private static bool CanSendResponseBody(int responseCode) { - get { return version; } - set + for (int i = 0; i < s_noResponseBody.Length; i++) { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - if (value == null) - throw new ArgumentNullException("value"); - - if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1)) - throw new ArgumentException("Must be 1.0 or 1.1", "value"); - - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - version = value; + if (responseCode == s_noResponseBody[i]) + { + return false; + } } + return true; } - public string RedirectLocation + public long ContentLength64 { - get { return location; } + get => _contentLength; set { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - location = value; + CheckDisposed(); + CheckSentHeaders(); + if (value >= 0) + { + _contentLength = value; + _boundaryType = BoundaryType.ContentLength; + } + else + { + throw new ArgumentOutOfRangeException("net_clsmall"); + } } } - public bool SendChunked + public CookieCollection Cookies { - get { return chunked; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - chunked = value; - } + get => _cookies ?? (_cookies = new CookieCollection()); + set => _cookies = value; } - public int StatusCode + public bool KeepAlive { - get { return status_code; } + get => _keepAlive; set { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - if (value < 100 || value > 999) - throw new ProtocolViolationException("StatusCode must be between 100 and 999."); - status_code = value; - status_description = GetStatusDescription(value); + CheckDisposed(); + _keepAlive = value; } } - internal static string GetStatusDescription(int code) + public Stream OutputStream { - switch (code) + get { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 102: return "Processing"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-Uri Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "Http Version Not Supported"; - case 507: return "Insufficient Storage"; + CheckDisposed(); + EnsureResponseStream(); + return _responseStream; } - return ""; } - public string StatusDescription + public string RedirectLocation { - get { return status_description; } + get => Headers["Location"]; set { - status_description = value; - } - } - - void IDisposable.Dispose() - { - Close(true); //TODO: Abort or Close? - } - - public void Abort() - { - if (disposed) - return; - - Close(true); - } - - public void AddHeader(string name, string value) - { - if (name == null) - throw new ArgumentNullException("name"); - - if (name == "") - throw new ArgumentException("'name' cannot be empty", "name"); - - //TODO: check for forbidden headers and invalid characters - if (value.Length > 65535) - throw new ArgumentOutOfRangeException("value"); - - headers.Set(name, value); - } - - public void AppendCookie(Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException("cookie"); - - Cookies.Add(cookie); - } - - public void AppendHeader(string name, string value) - { - if (name == null) - throw new ArgumentNullException("name"); - - if (name == "") - throw new ArgumentException("'name' cannot be empty", "name"); - - if (value.Length > 65535) - throw new ArgumentOutOfRangeException("value"); - - headers.Add(name, value); - } - - private void Close(bool force) - { - if (force) - { - _logger.Debug("HttpListenerResponse force closing HttpConnection"); + // note that this doesn't set the status code to a redirect one + CheckDisposed(); + if (string.IsNullOrEmpty(value)) + { + Headers.Remove("Location"); + } + else + { + Headers.Set("Location", value); + } } - disposed = true; - context.Connection.Close(force); } - public void Close(byte[] responseEntity, bool willBlock) + public string StatusDescription { - //CheckDisposed(); - - if (responseEntity == null) - { - throw new ArgumentNullException(nameof(responseEntity)); - } - - //if (_boundaryType != BoundaryType.Chunked) - { - ContentLength64 = responseEntity.Length; - } - - if (willBlock) + get { - try + if (_statusDescription == null) { - OutputStream.Write(responseEntity, 0, responseEntity.Length); + // if the user hasn't set this, generated on the fly, if possible. + // We know this one is safe, no need to verify it as in the setter. + _statusDescription = HttpStatusDescription.Get(StatusCode); } - finally + if (_statusDescription == null) { - Close(false); + _statusDescription = string.Empty; } + return _statusDescription; } - else + set { - OutputStream.BeginWrite(responseEntity, 0, responseEntity.Length, iar => + CheckDisposed(); + if (value == null) { - var thisRef = (HttpListenerResponse)iar.AsyncState; - try - { - thisRef.OutputStream.EndWrite(iar); - } - finally + throw new ArgumentNullException(nameof(value)); + } + + // Need to verify the status description doesn't contain any control characters except HT. We mask off the high + // byte since that's how it's encoded. + for (int i = 0; i < value.Length; i++) + { + char c = (char)(0x000000ff & (uint)value[i]); + if ((c <= 31 && c != (byte)'\t') || c == 127) { - thisRef.Close(false); + throw new ArgumentException("net_WebHeaderInvalidControlChars"); } - }, this); + } + + _statusDescription = value; } } - public void Close() + public void AddHeader(string name, string value) { - if (disposed) - return; - - Close(false); + Headers.Set(name, value); } - public void Redirect(string url) + public void AppendHeader(string name, string value) { - StatusCode = 302; // Found - location = url; + Headers.Add(name, value); } - bool FindCookie(Cookie cookie) + public void AppendCookie(Cookie cookie) { - string name = cookie.Name; - string domain = cookie.Domain; - string path = cookie.Path; - foreach (Cookie c in cookies) + if (cookie == null) { - if (name != c.Name) - continue; - if (domain != c.Domain) - continue; - if (path == c.Path) - return true; + throw new ArgumentNullException(nameof(cookie)); } - - return false; + Cookies.Add(cookie); } - public void DetermineIfChunked() + private void ComputeCookies() { - if (chunked) + if (_cookies != null) { - return; - } - - Version v = context.Request.ProtocolVersion; - if (!cl_set && !chunked && v >= HttpVersion.Version11) - chunked = true; - if (!chunked && string.Equals(headers["Transfer-Encoding"], "chunked")) - { - chunked = true; + // now go through the collection, and concatenate all the cookies in per-variant strings + //string setCookie2 = null, setCookie = null; + //for (int index = 0; index < _cookies.Count; index++) + //{ + // Cookie cookie = _cookies[index]; + // string cookieString = cookie.ToServerString(); + // if (cookieString == null || cookieString.Length == 0) + // { + // continue; + // } + + // if (cookie.IsRfc2965Variant()) + // { + // setCookie2 = setCookie2 == null ? cookieString : setCookie2 + ", " + cookieString; + // } + // else + // { + // setCookie = setCookie == null ? cookieString : setCookie + ", " + cookieString; + // } + //} + + //if (!string.IsNullOrEmpty(setCookie)) + //{ + // Headers.Set(HttpKnownHeaderNames.SetCookie, setCookie); + // if (string.IsNullOrEmpty(setCookie2)) + // { + // Headers.Remove(HttpKnownHeaderNames.SetCookie2); + // } + //} + + //if (!string.IsNullOrEmpty(setCookie2)) + //{ + // Headers.Set(HttpKnownHeaderNames.SetCookie2, setCookie2); + // if (string.IsNullOrEmpty(setCookie)) + // { + // Headers.Remove(HttpKnownHeaderNames.SetCookie); + // } + //} } } - internal void SendHeaders(bool closing, MemoryStream ms) + public void Redirect(string url) { - Encoding encoding = content_encoding; - if (encoding == null) - encoding = _textEncoding.GetDefaultEncoding(); - - if (content_type != null) - { - if (content_encoding != null && content_type.IndexOf("charset=", StringComparison.OrdinalIgnoreCase) == -1) - { - string enc_name = content_encoding.WebName; - headers.SetInternal("Content-Type", content_type + "; charset=" + enc_name); - } - else - { - headers.SetInternal("Content-Type", content_type); - } - } - - if (headers["Server"] == null) - headers.SetInternal("Server", "Mono-HTTPAPI/1.0"); - - CultureInfo inv = CultureInfo.InvariantCulture; - if (headers["Date"] == null) - headers.SetInternal("Date", DateTime.UtcNow.ToString("r", inv)); - - if (!chunked) - { - if (!cl_set && closing) - { - cl_set = true; - content_length = 0; - } - - if (cl_set) - headers.SetInternal("Content-Length", content_length.ToString(inv)); - } + Headers["Location"] = url; + StatusCode = (int)HttpStatusCode.Redirect; + StatusDescription = "Found"; + } - Version v = context.Request.ProtocolVersion; - if (!cl_set && !chunked && v >= HttpVersion.Version11) - chunked = true; - - /* Apache forces closing the connection for these status codes: - * HttpStatusCode.BadRequest 400 - * HttpStatusCode.RequestTimeout 408 - * HttpStatusCode.LengthRequired 411 - * HttpStatusCode.RequestEntityTooLarge 413 - * HttpStatusCode.RequestUriTooLong 414 - * HttpStatusCode.InternalServerError 500 - * HttpStatusCode.ServiceUnavailable 503 - */ - bool conn_close = status_code == 400 || status_code == 408 || status_code == 411 || - status_code == 413 || status_code == 414 || - status_code == 500 || - status_code == 503; - - if (conn_close == false) - conn_close = !context.Request.KeepAlive; - - // They sent both KeepAlive: true and Connection: close!? - if (!keep_alive || conn_close) + public void SetCookie(Cookie cookie) + { + if (cookie == null) { - headers.SetInternal("Connection", "close"); - conn_close = true; + throw new ArgumentNullException(nameof(cookie)); } - if (chunked) - headers.SetInternal("Transfer-Encoding", "chunked"); + //Cookie newCookie = cookie.Clone(); + //int added = Cookies.InternalAdd(newCookie, true); - //int reuses = context.Connection.Reuses; - //if (reuses >= 100) + //if (added != 1) //{ - // _logger.Debug("HttpListenerResponse - keep alive has exceeded 100 uses and will be closed."); - - // force_close_chunked = true; - // if (!conn_close) - // { - // headers.SetInternal("Connection", "close"); - // conn_close = true; - // } + // // The Cookie already existed and couldn't be replaced. + // throw new ArgumentException("Cookie exists"); //} + } - if (!conn_close) - { - if (context.Request.ProtocolVersion <= HttpVersion.Version10) - headers.SetInternal("Connection", "keep-alive"); - } - - if (location != null) - headers.SetInternal("Location", location); - - if (cookies != null) - { - foreach (Cookie cookie in cookies) - headers.SetInternal("Set-Cookie", cookie.ToString()); - } - - headers.SetInternal("Status", status_code.ToString(CultureInfo.InvariantCulture)); + void IDisposable.Dispose() => Dispose(); - using (StreamWriter writer = new StreamWriter(ms, encoding, 256, true)) + private void CheckDisposed() + { + if (Disposed) { - writer.Write("HTTP/{0} {1} {2}\r\n", version, status_code, status_description); - string headers_str = headers.ToStringMultiValue(); - writer.Write(headers_str); - writer.Flush(); + throw new ObjectDisposedException(GetType().FullName); } - - int preamble = encoding.GetPreamble().Length; - if (output_stream == null) - output_stream = context.Connection.GetResponseStream(); - - /* Assumes that the ms was at position 0 */ - ms.Position = preamble; - HeadersSent = true; } - public void SetCookie(Cookie cookie) + private void CheckSentHeaders() { - if (cookie == null) - throw new ArgumentNullException("cookie"); - - if (cookies != null) + if (SentHeaders) { - if (FindCookie(cookie)) - throw new ArgumentException("The cookie already exists."); + throw new InvalidOperationException(); } - else - { - cookies = new CookieCollection(); - } - - cookies.Add(cookie); - } - - public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) - { - return ((HttpResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken); } } -} \ No newline at end of file +} diff --git a/SocketHttpListener/Net/HttpRequestStream.Managed.cs b/SocketHttpListener/Net/HttpRequestStream.Managed.cs index cb02a4d5a2..2b5dfc8382 100644 --- a/SocketHttpListener/Net/HttpRequestStream.Managed.cs +++ b/SocketHttpListener/Net/HttpRequestStream.Managed.cs @@ -104,9 +104,24 @@ namespace SocketHttpListener.Net return nread; } + if (_remainingBody > 0) + { + size = (int)Math.Min(_remainingBody, (long)size); + } + nread = _stream.Read(buffer, offset, size); - if (nread > 0 && _remainingBody > 0) + + if (_remainingBody > 0) + { + if (nread == 0) + { + throw new Exception("Bad request"); + } + + //Debug.Assert(nread <= _remainingBody); _remainingBody -= nread; + } + return nread; } @@ -139,7 +154,7 @@ namespace SocketHttpListener.Net // for HTTP pipelining if (_remainingBody >= 0 && size > _remainingBody) { - size = (int)Math.Min(int.MaxValue, _remainingBody); + size = (int)Math.Min(_remainingBody, (long)size); } return _stream.BeginRead(buffer, offset, size, cback, state); @@ -150,9 +165,7 @@ namespace SocketHttpListener.Net if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - var r = asyncResult as HttpStreamAsyncResult; - - if (r != null) + if (asyncResult is HttpStreamAsyncResult r) { if (!ReferenceEquals(this, r._parent)) { @@ -160,7 +173,7 @@ namespace SocketHttpListener.Net } if (r._endCalled) { - throw new InvalidOperationException("Invalid end call"); + throw new InvalidOperationException("invalid end call"); } r._endCalled = true; @@ -185,8 +198,13 @@ namespace SocketHttpListener.Net throw e.InnerException; } - if (_remainingBody > 0 && nread > 0) + if (_remainingBody > 0) { + if (nread == 0) + { + throw new Exception("Bad request"); + } + _remainingBody -= nread; } diff --git a/SocketHttpListener/Net/HttpResponseStream.Managed.cs b/SocketHttpListener/Net/HttpResponseStream.Managed.cs index 42db03e476..116c3280a7 100644 --- a/SocketHttpListener/Net/HttpResponseStream.Managed.cs +++ b/SocketHttpListener/Net/HttpResponseStream.Managed.cs @@ -132,27 +132,28 @@ namespace SocketHttpListener.Net private MemoryStream GetHeaders(bool closing, bool isWebSocketHandshake = false) { + //// SendHeaders works on shared headers + //lock (_response.headers_lock) + //{ + // if (_response.HeadersSent) + // return null; + // var ms = _memoryStreamFactory.CreateNew(); + // _response.SendHeaders(closing, ms); + // return ms; + //} + // SendHeaders works on shared headers - lock (_response.headers_lock) + lock (_response._headersLock) { - if (_response.HeadersSent) + if (_response.SentHeaders) + { return null; - var ms = _memoryStreamFactory.CreateNew(); - _response.SendHeaders(closing, ms); + } + + MemoryStream ms = new MemoryStream(); + _response.SendHeaders(closing, ms, isWebSocketHandshake); return ms; } - - //lock (_response._headersLock) - //{ - // if (_response.SentHeaders) - // { - // return null; - // } - - // MemoryStream ms = new MemoryStream(); - // _response.SendHeaders(closing, ms, isWebSocketHandshake); - // return ms; - //} } private static byte[] s_crlf = new byte[] { 13, 10 }; diff --git a/SocketHttpListener/Net/HttpStatusDescription.cs b/SocketHttpListener/Net/HttpStatusDescription.cs new file mode 100644 index 0000000000..8d490c5117 --- /dev/null +++ b/SocketHttpListener/Net/HttpStatusDescription.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SocketHttpListener.Net +{ + internal static class HttpStatusDescription + { + internal static string Get(HttpStatusCode code) + { + return Get((int)code); + } + + internal static string Get(int code) + { + switch (code) + { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 102: return "Processing"; + + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 307: return "Temporary Redirect"; + + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-Uri Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Requested Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 426: return "Upgrade Required"; // RFC 2817 + + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "Http Version Not Supported"; + case 507: return "Insufficient Storage"; + } + return null; + } + } +} diff --git a/SocketHttpListener/Net/WebHeaderEncoding.cs b/SocketHttpListener/Net/WebHeaderEncoding.cs new file mode 100644 index 0000000000..64330c1b43 --- /dev/null +++ b/SocketHttpListener/Net/WebHeaderEncoding.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SocketHttpListener.Net +{ + // we use this static class as a helper class to encode/decode HTTP headers. + // what we need is a 1-1 correspondence between a char in the range U+0000-U+00FF + // and a byte in the range 0x00-0xFF (which is the range that can hit the network). + // The Latin-1 encoding (ISO-88591-1) (GetEncoding(28591)) works for byte[] to string, but is a little slow. + // It doesn't work for string -> byte[] because of best-fit-mapping problems. + internal static class WebHeaderEncoding + { + // We don't want '?' replacement characters, just fail. + private static readonly Encoding s_utf8Decoder = Encoding.GetEncoding("utf-8", EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); + + internal static unsafe string GetString(byte[] bytes, int byteIndex, int byteCount) + { + fixed (byte* pBytes = bytes) + return GetString(pBytes + byteIndex, byteCount); + } + + internal static unsafe string GetString(byte* pBytes, int byteCount) + { + if (byteCount < 1) + return ""; + + string s = new string('\0', byteCount); + + fixed (char* pStr = s) + { + char* pString = pStr; + while (byteCount >= 8) + { + pString[0] = (char)pBytes[0]; + pString[1] = (char)pBytes[1]; + pString[2] = (char)pBytes[2]; + pString[3] = (char)pBytes[3]; + pString[4] = (char)pBytes[4]; + pString[5] = (char)pBytes[5]; + pString[6] = (char)pBytes[6]; + pString[7] = (char)pBytes[7]; + pString += 8; + pBytes += 8; + byteCount -= 8; + } + for (int i = 0; i < byteCount; i++) + { + pString[i] = (char)pBytes[i]; + } + } + + return s; + } + + internal static int GetByteCount(string myString) + { + return myString.Length; + } + internal static unsafe void GetBytes(string myString, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + if (myString.Length == 0) + { + return; + } + fixed (byte* bufferPointer = bytes) + { + byte* newBufferPointer = bufferPointer + byteIndex; + int finalIndex = charIndex + charCount; + while (charIndex < finalIndex) + { + *newBufferPointer++ = (byte)myString[charIndex++]; + } + } + } + internal static unsafe byte[] GetBytes(string myString) + { + byte[] bytes = new byte[myString.Length]; + if (myString.Length != 0) + { + GetBytes(myString, 0, myString.Length, bytes, 0); + } + return bytes; + } + + // The normal client header parser just casts bytes to chars (see GetString). + // Check if those bytes were actually utf-8 instead of ASCII. + // If not, just return the input value. + internal static string DecodeUtf8FromString(string input) + { + if (string.IsNullOrWhiteSpace(input)) + { + return input; + } + + bool possibleUtf8 = false; + for (int i = 0; i < input.Length; i++) + { + if (input[i] > (char)255) + { + return input; // This couldn't have come from the wire, someone assigned it directly. + } + else if (input[i] > (char)127) + { + possibleUtf8 = true; + break; + } + } + if (possibleUtf8) + { + byte[] rawBytes = new byte[input.Length]; + for (int i = 0; i < input.Length; i++) + { + if (input[i] > (char)255) + { + return input; // This couldn't have come from the wire, someone assigned it directly. + } + rawBytes[i] = (byte)input[i]; + } + try + { + return s_utf8Decoder.GetString(rawBytes); + } + catch (ArgumentException) { } // Not actually Utf-8 + } + return input; + } + } +} diff --git a/SocketHttpListener/SocketHttpListener.csproj b/SocketHttpListener/SocketHttpListener.csproj index fde6ed544e..9fb7c50619 100644 --- a/SocketHttpListener/SocketHttpListener.csproj +++ b/SocketHttpListener/SocketHttpListener.csproj @@ -21,6 +21,7 @@ DEBUG;TRACE prompt 4 + true pdbonly @@ -56,27 +57,32 @@ + + + + + -- cgit v1.2.3 From 07b5c6a750a118405a840277dd0280f25c911924 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 15 Jun 2017 13:26:48 -0400 Subject: update logging --- Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 79209d438b..28c23b7665 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -598,9 +598,10 @@ namespace Emby.Server.Implementations.HttpServer { ErrorHandler(ex, httpReq, false); } + catch (Exception ex) { - ErrorHandler(ex, httpReq); + ErrorHandler(ex, httpReq, !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase)); } finally { -- cgit v1.2.3 From ce47f6338950853110ecc1d6bbd312b532b2ed8a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 18 Jun 2017 03:11:55 -0400 Subject: 3.2.20.5 --- .../TextEncoding/TextEncoding.cs | 18 +++++++++--------- Emby.Server.Implementations/Dto/DtoService.cs | 6 +++--- .../ServerManager/WebSocketConnection.cs | 2 +- .../Subtitles/SubtitleEncoder.cs | 4 ++-- MediaBrowser.Model/Text/ITextEncoding.cs | 4 ++-- SharedVersion.cs | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Common.Implementations/TextEncoding/TextEncoding.cs b/Emby.Common.Implementations/TextEncoding/TextEncoding.cs index a5caae3917..021caec754 100644 --- a/Emby.Common.Implementations/TextEncoding/TextEncoding.cs +++ b/Emby.Common.Implementations/TextEncoding/TextEncoding.cs @@ -68,7 +68,7 @@ namespace Emby.Common.Implementations.TextEncoding } private bool _langDetectInitialized; - public string GetDetectedEncodingName(byte[] bytes, string language) + public string GetDetectedEncodingName(byte[] bytes, string language, bool enableLanguageDetection) { var encoding = GetInitialEncoding(bytes); @@ -77,14 +77,14 @@ namespace Emby.Common.Implementations.TextEncoding return "utf-8"; } - if (!_langDetectInitialized) + if (string.IsNullOrWhiteSpace(language) && enableLanguageDetection) { - _langDetectInitialized = true; - LanguageDetector.Initialize(_json); - } + if (!_langDetectInitialized) + { + _langDetectInitialized = true; + LanguageDetector.Initialize(_json); + } - if (string.IsNullOrWhiteSpace(language)) - { language = DetectLanguage(bytes); if (!string.IsNullOrWhiteSpace(language)) @@ -167,9 +167,9 @@ namespace Emby.Common.Implementations.TextEncoding } } - public Encoding GetDetectedEncoding(byte[] bytes, string language) + public Encoding GetDetectedEncoding(byte[] bytes, string language, bool enableLanguageDetection) { - var charset = GetDetectedEncodingName(bytes, language); + var charset = GetDetectedEncodingName(bytes, language, enableLanguageDetection); return GetEncodingFromCharset(charset); } diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index ae988f938e..7352073bd3 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -778,16 +778,16 @@ namespace Emby.Server.Implementations.Dto .Select(i => new NameIdPair { Name = i, - Id = GetStudioId(i, item) + Id = GetGenreId(i, item) }) .ToArray(); } - private string GetStudioId(string name, BaseItem owner) + private string GetGenreId(string name, BaseItem owner) { if (owner is IHasMusicGenres) { - return _libraryManager.GetGameGenreId(name).ToString("N"); + return _libraryManager.GetMusicGenreId(name).ToString("N"); } if (owner is Game || owner is GameSystem) diff --git a/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs b/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs index e4392d7e64..4d5192feaf 100644 --- a/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs +++ b/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs @@ -136,7 +136,7 @@ namespace Emby.Server.Implementations.ServerManager return; } - var charset = _textEncoding.GetDetectedEncodingName(bytes, null); + var charset = _textEncoding.GetDetectedEncodingName(bytes, null, false); if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index f8f95684d6..247c5274f6 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -198,7 +198,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { var bytes = await GetBytes(path, protocol, cancellationToken).ConfigureAwait(false); - var charset = _textEncoding.GetDetectedEncodingName(bytes, language); + var charset = _textEncoding.GetDetectedEncodingName(bytes, language, true); _logger.Debug("charset {0} detected for {1}", charset ?? "null", path); if (!string.IsNullOrEmpty(charset)) @@ -705,7 +705,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { var bytes = await GetBytes(path, protocol, cancellationToken).ConfigureAwait(false); - var charset = _textEncoding.GetDetectedEncodingName(bytes, language); + var charset = _textEncoding.GetDetectedEncodingName(bytes, language, true); _logger.Debug("charset {0} detected for {1}", charset ?? "null", path); diff --git a/MediaBrowser.Model/Text/ITextEncoding.cs b/MediaBrowser.Model/Text/ITextEncoding.cs index 7424f00f5c..96dca0c045 100644 --- a/MediaBrowser.Model/Text/ITextEncoding.cs +++ b/MediaBrowser.Model/Text/ITextEncoding.cs @@ -7,8 +7,8 @@ namespace MediaBrowser.Model.Text { Encoding GetASCIIEncoding(); - string GetDetectedEncodingName(byte[] bytes, string language); - Encoding GetDetectedEncoding(byte[] bytes, string language); + string GetDetectedEncodingName(byte[] bytes, string language, bool enableLanguageDetection); + Encoding GetDetectedEncoding(byte[] bytes, string language, bool enableLanguageDetection); Encoding GetEncodingFromCharset(string charset); } } diff --git a/SharedVersion.cs b/SharedVersion.cs index adeff35086..939f3f3887 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.4")] +[assembly: AssemblyVersion("3.2.20.5")] -- cgit v1.2.3 From 1de6b4607f82b2a3e18d8f1f4efa9248f7b0c98b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 19 Jun 2017 14:14:15 -0400 Subject: increase live stream analyze duration --- Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs | 7 ++++--- SocketHttpListener/Net/HttpListenerResponse.Managed.cs | 13 ++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs index 12da1464bc..75f4231816 100644 --- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs @@ -16,7 +16,8 @@ namespace Emby.Server.Implementations.LiveTv private readonly IMediaEncoder _mediaEncoder; private readonly ILogger _logger; - const int AnalyzeDurationMs = 1000; + const int ProbeAnalyzeDurationMs = 2000; + const int PlaybackAnalyzeDurationMs = 1000; public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger) { @@ -36,7 +37,7 @@ namespace Emby.Server.Implementations.LiveTv Protocol = mediaSource.Protocol, MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video, ExtractChapters = false, - AnalyzeDurationMs = AnalyzeDurationMs + AnalyzeDurationMs = ProbeAnalyzeDurationMs }, cancellationToken).ConfigureAwait(false); @@ -106,7 +107,7 @@ namespace Emby.Server.Implementations.LiveTv // Try to estimate this mediaSource.InferTotalBitrate(true); - mediaSource.AnalyzeDurationMs = AnalyzeDurationMs; + mediaSource.AnalyzeDurationMs = PlaybackAnalyzeDurationMs; } } } diff --git a/SocketHttpListener/Net/HttpListenerResponse.Managed.cs b/SocketHttpListener/Net/HttpListenerResponse.Managed.cs index 9adf0d435d..83fcc09cae 100644 --- a/SocketHttpListener/Net/HttpListenerResponse.Managed.cs +++ b/SocketHttpListener/Net/HttpListenerResponse.Managed.cs @@ -130,11 +130,18 @@ namespace SocketHttpListener.Net var thisRef = (HttpListenerResponse)iar.AsyncState; try { - thisRef.OutputStream.EndWrite(iar); + try + { + thisRef.OutputStream.EndWrite(iar); + } + finally + { + thisRef.Close(false); + } } - finally + catch (Exception) { - thisRef.Close(false); + // In case response was disposed during this time } }, this); } -- cgit v1.2.3 From 018df6d4c63a1ef5fb6815694b28b707791de0ff Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 20 Jun 2017 15:37:52 -0400 Subject: update libs --- Emby.Common.Implementations/Emby.Common.Implementations.csproj | 3 ++- Emby.Common.Implementations/packages.config | 2 +- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 6 +++--- Emby.Server.Implementations/packages.config | 2 +- MediaBrowser.Server.Mac/Emby.Server.Mac.csproj | 6 +++--- MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj | 7 ++++--- MediaBrowser.Server.Mono/packages.config | 6 +++--- .../MediaBrowser.ServerApplication.csproj | 7 ++++--- MediaBrowser.ServerApplication/packages.config | 6 +++--- 9 files changed, 24 insertions(+), 21 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Common.Implementations/Emby.Common.Implementations.csproj b/Emby.Common.Implementations/Emby.Common.Implementations.csproj index 6efc37603d..00c90d16e4 100644 --- a/Emby.Common.Implementations/Emby.Common.Implementations.csproj +++ b/Emby.Common.Implementations/Emby.Common.Implementations.csproj @@ -32,7 +32,8 @@ - ..\packages\NLog.4.4.10\lib\net45\NLog.dll + ..\packages\NLog.4.4.11\lib\net45\NLog.dll + True ..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll diff --git a/Emby.Common.Implementations/packages.config b/Emby.Common.Implementations/packages.config index 1e6b10c018..a255465cc7 100644 --- a/Emby.Common.Implementations/packages.config +++ b/Emby.Common.Implementations/packages.config @@ -1,6 +1,6 @@  - + diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 210448cd95..58d44d2614 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -306,12 +306,12 @@ ..\packages\SQLitePCL.pretty.1.1.0\lib\portable-net45+netcore45+wpa81+wp8\SQLitePCL.pretty.dll True + + - ..\packages\SQLitePCLRaw.core.1.1.5\lib\portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLitePCLRaw.core.dll + ..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll True - - diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config index 5ce754d3ff..817e3eedb7 100644 --- a/Emby.Server.Implementations/packages.config +++ b/Emby.Server.Implementations/packages.config @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj index 25609af3e6..5f13eee0e4 100644 --- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj +++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj @@ -93,7 +93,7 @@ ..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll - ..\packages\NLog.4.4.10\lib\net45\NLog.dll + ..\packages\NLog.4.4.11\lib\net45\NLog.dll True @@ -116,10 +116,10 @@ ..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll - ..\packages\SQLitePCLRaw.core.1.1.5\lib\net45\SQLitePCLRaw.core.dll + ..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll - ..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.5\lib\net45\SQLitePCLRaw.provider.sqlite3.dll + ..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.6\lib\net45\SQLitePCLRaw.provider.sqlite3.dll ..\ThirdParty\emby\Emby.Server.Connect.dll diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index 872330e9fa..722d7c4503 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -50,7 +50,8 @@ ..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll - ..\packages\NLog.4.4.10\lib\net45\NLog.dll + ..\packages\NLog.4.4.11\lib\net45\NLog.dll + True ..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll @@ -64,11 +65,11 @@ ..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll - ..\packages\SQLitePCLRaw.core.1.1.5\lib\net45\SQLitePCLRaw.core.dll + ..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll True - ..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.5\lib\net45\SQLitePCLRaw.provider.sqlite3.dll + ..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.6\lib\net45\SQLitePCLRaw.provider.sqlite3.dll True diff --git a/MediaBrowser.Server.Mono/packages.config b/MediaBrowser.Server.Mono/packages.config index 85f3d58b08..3eea8d674c 100644 --- a/MediaBrowser.Server.Mono/packages.config +++ b/MediaBrowser.Server.Mono/packages.config @@ -1,10 +1,10 @@  - + - - + + \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 4cc3fd6a57..7dd00f185a 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -78,7 +78,8 @@ ..\packages\ImageMagickSharp.1.0.0.18\lib\net45\ImageMagickSharp.dll - ..\packages\NLog.4.4.10\lib\net45\NLog.dll + ..\packages\NLog.4.4.11\lib\net45\NLog.dll + True ..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll @@ -95,11 +96,11 @@ ..\packages\SkiaSharp.1.58.0\lib\net45\SkiaSharp.dll - ..\packages\SQLitePCLRaw.core.1.1.5\lib\net45\SQLitePCLRaw.core.dll + ..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll True - ..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.5\lib\net45\SQLitePCLRaw.provider.sqlite3.dll + ..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.6\lib\net45\SQLitePCLRaw.provider.sqlite3.dll True diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index 9abd370f35..ca382ffb3c 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -1,11 +1,11 @@  - + - - + + \ No newline at end of file -- cgit v1.2.3 From 905f02e6d9dc9785c9a39904a30b5933898e66dd Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 20 Jun 2017 15:38:42 -0400 Subject: 3.2.20.7 --- .../TextEncoding/TextEncoding.cs | 3 ++ .../LiveTv/LiveStreamHelper.cs | 2 +- .../Session/HttpSessionController.cs | 37 +++++++++++----------- SharedVersion.cs | 2 +- 4 files changed, 24 insertions(+), 20 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Common.Implementations/TextEncoding/TextEncoding.cs b/Emby.Common.Implementations/TextEncoding/TextEncoding.cs index 021caec754..54c47d62c1 100644 --- a/Emby.Common.Implementations/TextEncoding/TextEncoding.cs +++ b/Emby.Common.Implementations/TextEncoding/TextEncoding.cs @@ -214,7 +214,10 @@ namespace Emby.Common.Implementations.TextEncoding case "ota": case "tur": return "windows-1254"; + // bulgarian + case "bul": case "bgr": + return "windows-1251"; case "rus": return "windows-1251"; case "vie": diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs index 75f4231816..428b6202b4 100644 --- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs @@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.LiveTv private readonly ILogger _logger; const int ProbeAnalyzeDurationMs = 2000; - const int PlaybackAnalyzeDurationMs = 1000; + const int PlaybackAnalyzeDurationMs = 2000; public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger) { diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs index 92fa6c4242..dbac76bb49 100644 --- a/Emby.Server.Implementations/Session/HttpSessionController.cs +++ b/Emby.Server.Implementations/Session/HttpSessionController.cs @@ -159,24 +159,25 @@ namespace Emby.Server.Implementations.Session public Task SendMessage(string name, T data, CancellationToken cancellationToken) { - var url = PostUrl + "/" + name; - - var options = new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false - }; - - options.RequestContent = _json.SerializeToString(data); - options.RequestContentType = "application/json"; - - return _httpClient.Post(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false - }); + return Task.FromResult(true); + //var url = PostUrl + "/" + name; + + //var options = new HttpRequestOptions + //{ + // Url = url, + // CancellationToken = cancellationToken, + // BufferContent = false + //}; + + //options.RequestContent = _json.SerializeToString(data); + //options.RequestContentType = "application/json"; + + //return _httpClient.Post(new HttpRequestOptions + //{ + // Url = url, + // CancellationToken = cancellationToken, + // BufferContent = false + //}); } private string ToQueryString(Dictionary nvc) diff --git a/SharedVersion.cs b/SharedVersion.cs index b7341d9eb2..38f901e92b 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.6")] +[assembly: AssemblyVersion("3.2.20.7")] -- cgit v1.2.3 From 98ee28bda46eed9d3cceaf963a3808ae1f3de0be Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 21 Jun 2017 10:51:11 -0400 Subject: update image magick encoder --- Emby.Drawing.ImageMagick/ImageMagickEncoder.cs | 17 +++++++++++++++-- .../LiveTv/Listings/SchedulesDirect.cs | 14 ++++++-------- Emby.Server.Implementations/LiveTv/LiveTvManager.cs | 2 +- MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 5 +++++ MediaBrowser.Server.Mono/ImageEncoderHelper.cs | 6 ++++-- 5 files changed, 31 insertions(+), 13 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs index 958ca85fdc..ea8687de01 100644 --- a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs @@ -9,6 +9,7 @@ using System; using System.IO; using System.Linq; using MediaBrowser.Model.IO; +using MediaBrowser.Model.System; namespace Emby.Drawing.ImageMagick { @@ -18,13 +19,15 @@ namespace Emby.Drawing.ImageMagick private readonly IApplicationPaths _appPaths; private readonly Func _httpClientFactory; private readonly IFileSystem _fileSystem; + private readonly IEnvironmentInfo _environment; - public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, Func httpClientFactory, IFileSystem fileSystem) + public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, Func httpClientFactory, IFileSystem fileSystem, IEnvironmentInfo environment) { _logger = logger; _appPaths = appPaths; _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; + _environment = environment; LogVersion(); } @@ -337,7 +340,17 @@ namespace Emby.Drawing.ImageMagick public bool SupportsImageCollageCreation { - get { return true; } + get + { + // too heavy. seeing crashes on RPI. + if (_environment.SystemArchitecture == Architecture.Arm || + _environment.SystemArchitecture == Architecture.Arm64) + { + return false; + } + + return true; + } } public bool SupportsImageEncoding diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 97197a0abb..e12acf1408 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -237,8 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings private bool IsMovie(ScheduleDirect.ProgramDetails programInfo) { - var showType = programInfo.showType ?? string.Empty; - return showType.IndexOf("movie", StringComparison.OrdinalIgnoreCase) != -1 || showType.IndexOf("film", StringComparison.OrdinalIgnoreCase) != -1; + return string.Equals(programInfo.entityType, "movie", StringComparison.OrdinalIgnoreCase); } private ProgramInfo GetProgram(string channelId, ScheduleDirect.Program programInfo, ScheduleDirect.ProgramDetails details) @@ -280,8 +279,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings episodeTitle = details.episodeTitle150; } - var showType = details.showType ?? string.Empty; - var info = new ProgramInfo { ChannelId = channelId, @@ -294,11 +291,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings EpisodeTitle = episodeTitle, Audio = audioType, IsRepeat = repeat, - IsSeries = showType.IndexOf("series", StringComparison.OrdinalIgnoreCase) != -1, + IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase), ImageUrl = details.primaryImage, ThumbImageUrl = details.thumbImage, IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase), - IsSports = showType.IndexOf("sports", StringComparison.OrdinalIgnoreCase) != -1, + IsSports = string.Equals(details.entityType, "sports", StringComparison.OrdinalIgnoreCase), IsMovie = IsMovie(details), Etag = programInfo.md5 }; @@ -882,7 +879,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings foreach (ScheduleDirect.Map map in root.map) { var channelNumber = GetChannelNumber(map); - + var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase)); if (station == null) { @@ -906,7 +903,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { channelInfo.Name = station.name; } - + channelInfo.Id = station.stationID; channelInfo.CallSign = station.callsign; @@ -1199,6 +1196,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings public List contentRating { get; set; } public List cast { get; set; } public List crew { get; set; } + public string entityType { get; set; } public string showType { get; set; } public bool hasImageArtwork { get; set; } public string primaryImage { get; set; } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 8f62670e41..69def362b2 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -742,7 +742,7 @@ namespace Emby.Server.Implementations.LiveTv else { // Increment this whenver some internal change deems it necessary - var etag = info.Etag + "5"; + var etag = info.Etag + "6"; if (!string.Equals(etag, item.ExternalEtag, StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 406a99f8b7..8d440e51b8 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -118,6 +118,11 @@ namespace MediaBrowser.Controller.Entities.Movies { get { + if (string.IsNullOrWhiteSpace(Path)) + { + return false; + } + return !FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, Path); } } diff --git a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs index 02593fb674..a74a18f37a 100644 --- a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs +++ b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using Emby.Drawing.Skia; +using MediaBrowser.Model.System; namespace MediaBrowser.Server.Startup.Common { @@ -19,7 +20,8 @@ namespace MediaBrowser.Server.Startup.Common IFileSystem fileSystem, StartupOptions startupOptions, Func httpClient, - IApplicationPaths appPaths) + IApplicationPaths appPaths, + IEnvironmentInfo environment) { if (!startupOptions.ContainsOption("-enablegdi")) { @@ -34,7 +36,7 @@ namespace MediaBrowser.Server.Startup.Common try { - return new ImageMagickEncoder(logManager.GetLogger("ImageMagick"), appPaths, httpClient, fileSystem); + return new ImageMagickEncoder(logManager.GetLogger("ImageMagick"), appPaths, httpClient, fileSystem, environment); } catch { -- cgit v1.2.3 From f1fa4251a88d4f45492d5dae4d6df960d64a29a7 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 21 Jun 2017 14:21:12 -0400 Subject: update logging --- Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs | 2 +- MediaBrowser.Server.Mono/ImageEncoderHelper.cs | 2 +- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs b/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs index 0744fc0d9d..17f9b491dc 100644 --- a/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs +++ b/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs @@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.Photos return items; } - protected string CreateImage(IHasImages item, List itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) + protected override string CreateImage(IHasImages item, List itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) { return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary); } diff --git a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs index a74a18f37a..12df23c1e0 100644 --- a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs +++ b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs @@ -31,7 +31,7 @@ namespace MediaBrowser.Server.Startup.Common } catch (Exception ex) { - logger.Error("Error loading Skia: {0}. Will revert to ImageMagick.", ex.Message); + logger.Info("Error loading Skia: {0}. Will revert to ImageMagick.", ex.Message); } try diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index ee911f0e98..7d825875d6 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -101,7 +101,7 @@ namespace MediaBrowser.WebDashboard.Api var parent = _fileSystem.GetDirectoryName(path); return string.Equals(_basePath, parent, StringComparison.OrdinalIgnoreCase) || - string.Equals(Path.Combine(_basePath, "voice"), parent, StringComparison.OrdinalIgnoreCase); + string.Equals(Path.Combine(_basePath, "offline"), parent, StringComparison.OrdinalIgnoreCase); } /// -- cgit v1.2.3 From 1e5c3db9eba730fe8b52995e5c699c22983fa62a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 23 Jun 2017 12:04:45 -0400 Subject: support individual library refreshing --- .../ScheduledTasks/ScheduledTaskWorker.cs | 3 +- Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs | 4 +- Emby.Drawing.Skia/PlayedIndicatorDrawer.cs | 3 +- .../Channels/ChannelManager.cs | 4 +- .../Channels/ChannelPostScanTask.cs | 2 +- .../Channels/RefreshChannelsScheduledTask.cs | 3 +- .../Emby.Server.Implementations.csproj | 1 - .../EntryPoints/LibraryChangedNotifier.cs | 116 +++++++++++- Emby.Server.Implementations/IO/FileRefresher.cs | 4 +- .../Library/LibraryManager.cs | 40 +++-- .../Library/Validators/PeopleValidator.cs | 4 - .../Library/Validators/YearsPostScanTask.cs | 57 ------ .../LiveTv/EmbyTV/EmbyTV.cs | 4 +- .../LiveTv/Listings/XmlTvListingsProvider.cs | 3 +- .../LiveTv/LiveTvManager.cs | 6 +- Emby.Server.Implementations/News/NewsEntryPoint.cs | 3 +- .../Notifications/WebSocketNotifier.cs | 1 - .../ScheduledTasks/PluginUpdateTask.cs | 3 +- .../ScheduledTasks/SystemUpdateTask.cs | 5 +- .../Updates/InstallationManager.cs | 2 +- MediaBrowser.Api/ItemRefreshService.cs | 7 +- MediaBrowser.Api/Library/LibraryService.cs | 7 +- .../Library/LibraryStructureService.cs | 10 +- MediaBrowser.Api/PackageService.cs | 5 +- MediaBrowser.Common/Progress/ActionableProgress.cs | 46 +++-- MediaBrowser.Controller/Channels/Channel.cs | 3 +- MediaBrowser.Controller/Dto/DtoOptions.cs | 3 +- .../Entities/Audio/MusicAlbum.cs | 2 - .../Entities/Audio/MusicArtist.cs | 2 - MediaBrowser.Controller/Entities/BaseItem.cs | 38 +++- .../Entities/CollectionFolder.cs | 30 ++++ MediaBrowser.Controller/Entities/Folder.cs | 196 +++++++++++++-------- MediaBrowser.Controller/Entities/TV/Series.cs | 2 - MediaBrowser.Controller/Library/ILibraryManager.cs | 4 +- .../MediaBrowser.Controller.csproj | 1 - .../Providers/IProviderManager.cs | 19 +- .../Providers/ProviderRefreshStatus.cs | 22 --- .../Encoder/FontConfigLoader.cs | 3 +- MediaBrowser.Model/Entities/VirtualFolderInfo.cs | 3 + MediaBrowser.Model/Querying/ItemFields.cs | 3 +- MediaBrowser.Providers/ImagesByName/ImageUtils.cs | 3 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 99 ++++++++++- MediaBrowser.Providers/TV/DummySeasonProvider.cs | 2 +- .../TV/MissingEpisodeProvider.cs | 4 +- 44 files changed, 522 insertions(+), 260 deletions(-) delete mode 100644 Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs delete mode 100644 MediaBrowser.Controller/Providers/ProviderRefreshStatus.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index ac1c55b6b3..c373ffddb9 100644 --- a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Progress; using MediaBrowser.Model.Events; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -402,7 +403,7 @@ namespace Emby.Common.Implementations.ScheduledTasks throw new InvalidOperationException("Cannot execute a Task that is already running"); } - var progress = new Progress(); + var progress = new SimpleProgress(); CurrentCancellationTokenSource = new CancellationTokenSource(); diff --git a/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs b/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs index 2ec58ff288..4dccca0c3d 100644 --- a/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs +++ b/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs @@ -5,7 +5,7 @@ using MediaBrowser.Model.Drawing; using System; using System.IO; using System.Threading.Tasks; - +using MediaBrowser.Common.Progress; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; @@ -104,7 +104,7 @@ namespace Emby.Drawing.ImageMagick var tempPath = await httpClient.GetTempFile(new HttpRequestOptions { Url = url, - Progress = new Progress() + Progress = new SimpleProgress() }).ConfigureAwait(false); diff --git a/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs b/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs index ad3b5226ca..48f2da62bc 100644 --- a/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs +++ b/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using System.Reflection; +using MediaBrowser.Common.Progress; namespace Emby.Drawing.Skia { @@ -99,7 +100,7 @@ namespace Emby.Drawing.Skia var tempPath = await httpClient.GetTempFile(new HttpRequestOptions { Url = url, - Progress = new Progress() + Progress = new SimpleProgress() }).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 73878160c9..85f6c66673 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -23,7 +23,7 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; - +using MediaBrowser.Common.Progress; using MediaBrowser.Model.IO; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; @@ -980,7 +980,7 @@ namespace Emby.Server.Implementations.Channels ? null : _userManager.GetUserById(query.UserId); - var internalResult = await GetChannelItemsInternal(query, new Progress(), cancellationToken).ConfigureAwait(false); + var internalResult = await GetChannelItemsInternal(query, new SimpleProgress(), cancellationToken).ConfigureAwait(false); var dtoOptions = new DtoOptions() { diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs index aef06bd1d6..ae31fcf3f5 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.Channels StartIndex = totalRetrieved, FolderId = folderId - }, new Progress(), cancellationToken); + }, new SimpleProgress(), cancellationToken); folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N"))); diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index d5ec86445d..4a33012529 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -4,6 +4,7 @@ using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.Threading.Tasks; +using MediaBrowser.Common.Progress; using MediaBrowser.Model.Tasks; namespace Emby.Server.Implementations.Channels @@ -42,7 +43,7 @@ namespace Emby.Server.Implementations.Channels { var manager = (ChannelManager)_channelManager; - await manager.RefreshChannels(new Progress(), cancellationToken).ConfigureAwait(false); + await manager.RefreshChannels(new SimpleProgress(), cancellationToken).ConfigureAwait(false); await new ChannelPostScanTask(_channelManager, _userManager, _logger, _libraryManager).Run(progress, cancellationToken) .ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 58d44d2614..231ffe7913 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -148,7 +148,6 @@ - diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 91142f9284..23ae4cee9e 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -6,9 +6,14 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Events; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Threading; @@ -49,13 +54,16 @@ namespace Emby.Server.Implementations.EntryPoints /// private const int LibraryUpdateDuration = 5000; - public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, ITimerFactory timerFactory) + private readonly IProviderManager _providerManager; + + public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, ITimerFactory timerFactory, IProviderManager providerManager) { _libraryManager = libraryManager; _sessionManager = sessionManager; _userManager = userManager; _logger = logger; _timerFactory = timerFactory; + _providerManager = providerManager; } public void Run() @@ -64,6 +72,108 @@ namespace Emby.Server.Implementations.EntryPoints _libraryManager.ItemUpdated += libraryManager_ItemUpdated; _libraryManager.ItemRemoved += libraryManager_ItemRemoved; + _providerManager.RefreshCompleted += _providerManager_RefreshCompleted; + _providerManager.RefreshStarted += _providerManager_RefreshStarted; + _providerManager.RefreshProgress += _providerManager_RefreshProgress; + } + + private Dictionary _lastProgressMessageTimes = new Dictionary(); + + private void _providerManager_RefreshProgress(object sender, GenericEventArgs> e) + { + var item = e.Argument.Item1; + + if (!EnableRefreshMessage(item)) + { + return; + } + + var progress = e.Argument.Item2; + + DateTime lastMessageSendTime; + if (_lastProgressMessageTimes.TryGetValue(item.Id, out lastMessageSendTime)) + { + if (progress > 0 && progress < 100 && (DateTime.UtcNow - lastMessageSendTime).TotalMilliseconds < 1000) + { + return; + } + } + + _lastProgressMessageTimes[item.Id] = DateTime.UtcNow; + + var dict = new Dictionary(); + dict["ItemId"] = item.Id.ToString("N"); + dict["Progress"] = progress.ToString(CultureInfo.InvariantCulture); + + try + { + _sessionManager.SendMessageToAdminSessions("RefreshProgress", dict, CancellationToken.None); + + _logger.Info("Sending refresh progress {0} {1}", item.Id.ToString("N"), progress); + } + catch + { + } + + var collectionFolders = _libraryManager.GetCollectionFolders(item).ToList(); + + foreach (var collectionFolder in collectionFolders) + { + var collectionFolderDict = new Dictionary(); + collectionFolderDict["ItemId"] = collectionFolder.Id.ToString("N"); + collectionFolderDict["Progress"] = (collectionFolder.GetRefreshProgress() ?? 0).ToString(CultureInfo.InvariantCulture); + + try + { + _sessionManager.SendMessageToAdminSessions("RefreshProgress", collectionFolderDict, CancellationToken.None); + } + catch + { + + } + } + } + + private void _providerManager_RefreshStarted(object sender, GenericEventArgs e) + { + _providerManager_RefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 0))); + } + + private void _providerManager_RefreshCompleted(object sender, GenericEventArgs e) + { + _providerManager_RefreshProgress(sender, new GenericEventArgs>(new Tuple(e.Argument, 100))); + } + + private bool EnableRefreshMessage(BaseItem item) + { + var folder = item as Folder; + + if (folder == null) + { + return false; + } + + if (folder.IsRoot) + { + return false; + } + + if (folder is AggregateFolder || folder is UserRootFolder) + { + return false; + } + + if (folder is UserView || folder is Channel) + { + return false; + } + + if (!folder.IsTopParent) + { + return false; + } + + return true; } /// @@ -218,8 +328,8 @@ namespace Emby.Server.Implementations.EntryPoints try { - info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, - foldersRemovedFrom, id); + info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, + foldersRemovedFrom, id); } catch (Exception ex) { diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index 9606b60b80..d4914e734e 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; using MediaBrowser.Common.Events; - +using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; @@ -170,7 +170,7 @@ namespace Emby.Server.Implementations.IO // If the root folder changed, run the library task so the user can see it if (itemsToRefresh.Any(i => i is AggregateFolder)) { - LibraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); + LibraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); return; } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 15efd3d39c..1a909549e7 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -462,7 +462,7 @@ namespace Emby.Server.Implementations.Library if (parent != null) { - await parent.ValidateChildren(new Progress(), CancellationToken.None, new MetadataRefreshOptions(_fileSystem), false).ConfigureAwait(false); + await parent.ValidateChildren(new SimpleProgress(), CancellationToken.None, new MetadataRefreshOptions(_fileSystem), false).ConfigureAwait(false); } } else if (parent != null) @@ -1113,13 +1113,13 @@ namespace Emby.Server.Implementations.Library progress.Report(.5); // Start by just validating the children of the root, but go no further - await RootFolder.ValidateChildren(new Progress(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false); + await RootFolder.ValidateChildren(new SimpleProgress(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false); progress.Report(1); await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); - await GetUserRootFolder().ValidateChildren(new Progress(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false); + await GetUserRootFolder().ValidateChildren(new SimpleProgress(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false); progress.Report(2); // Quickly scan CollectionFolders for changes @@ -1204,25 +1204,24 @@ namespace Emby.Server.Implementations.Library /// Gets the default view. /// /// IEnumerable{VirtualFolderInfo}. - public IEnumerable GetVirtualFolders() + public List GetVirtualFolders() { - return GetView(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath); + return GetVirtualFolders(false); } - /// - /// Gets the view. - /// - /// The path. - /// IEnumerable{VirtualFolderInfo}. - private IEnumerable GetView(string path) + public List GetVirtualFolders(bool includeRefreshState) { var topLibraryFolders = GetUserRootFolder().Children.ToList(); - return _fileSystem.GetDirectoryPaths(path) - .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders)); + var refreshQueue = includeRefreshState ? _providerManagerFactory().GetRefreshQueue() : null; + + return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath) + .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue)) + .OrderBy(i => i.Name) + .ToList(); } - private VirtualFolderInfo GetVirtualFolderInfo(string dir, List allCollectionFolders) + private VirtualFolderInfo GetVirtualFolderInfo(string dir, List allCollectionFolders, Dictionary refreshQueue) { var info = new VirtualFolderInfo { @@ -1248,6 +1247,13 @@ namespace Emby.Server.Implementations.Library { info.ItemId = libraryFolder.Id.ToString("N"); info.LibraryOptions = GetLibraryOptions(libraryFolder); + + if (refreshQueue != null) + { + info.RefreshProgress = libraryFolder.GetRefreshProgress(); + + info.RefreshStatus = info.RefreshProgress.HasValue ? "Active" : refreshQueue.ContainsKey(libraryFolder.Id) ? "Queued" : "Idle"; + } } return info; @@ -2947,7 +2953,7 @@ namespace Emby.Server.Implementations.Library // No need to start if scanning the library because it will handle it if (refreshLibrary) { - ValidateMediaLibrary(new Progress(), CancellationToken.None); + ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); } else { @@ -3075,7 +3081,7 @@ namespace Emby.Server.Implementations.Library private void SyncLibraryOptionsToLocations(string virtualFolderPath, LibraryOptions options) { var topLibraryFolders = GetUserRootFolder().Children.ToList(); - var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders); + var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders, null); if (info.Locations.Count > 0 && info.Locations.Count != options.PathInfos.Length) { @@ -3125,7 +3131,7 @@ namespace Emby.Server.Implementations.Library // No need to start if scanning the library because it will handle it if (refreshLibrary) { - ValidateMediaLibrary(new Progress(), CancellationToken.None); + ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); } else { diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs index ef3b86abf7..c2eb30ee41 100644 --- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs @@ -55,10 +55,6 @@ namespace Emby.Server.Implementations.Library.Validators /// Task. public async Task ValidatePeople(CancellationToken cancellationToken, IProgress progress) { - var innerProgress = new ActionableProgress(); - - innerProgress.RegisterAction(pct => progress.Report(pct * .15)); - var people = _libraryManager.GetPeople(new InternalPeopleQuery()); var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs deleted file mode 100644 index 4afb4c04a7..0000000000 --- a/Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs +++ /dev/null @@ -1,57 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Emby.Server.Implementations.Library.Validators -{ - public class YearsPostScanTask : ILibraryPostScanTask - { - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - - public YearsPostScanTask(ILibraryManager libraryManager, ILogger logger) - { - _libraryManager = libraryManager; - _logger = logger; - } - - public async Task Run(IProgress progress, CancellationToken cancellationToken) - { - var yearNumber = 1900; - var maxYear = DateTime.UtcNow.Year + 3; - var count = maxYear - yearNumber + 1; - var numComplete = 0; - - while (yearNumber < maxYear) - { - cancellationToken.ThrowIfCancellationRequested(); - - try - { - var year = _libraryManager.GetYear(yearNumber); - - await year.RefreshMetadata(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // Don't clutter the log - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error refreshing year {0}", ex, yearNumber); - } - - numComplete++; - double percent = numComplete; - percent /= count; - percent *= 100; - - progress.Report(percent); - yearNumber++; - } - } - } -} diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index a4c5b338e2..b55e4412be 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -28,7 +28,7 @@ using System.Xml; using MediaBrowser.Model.IO; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; - +using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -240,7 +240,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (requiresRefresh) { - _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); + _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); } } diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index b50f5ac926..9bf9f140d4 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -15,6 +15,7 @@ using Emby.XmlTv.Classes; using Emby.XmlTv.Entities; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; +using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -75,7 +76,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { CancellationToken = cancellationToken, Url = path, - Progress = new Progress(), + Progress = new SimpleProgress(), DecompressionMethod = CompressionMethod.Gzip, // It's going to come back gzipped regardless of this value diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 69def362b2..24afc03daf 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1273,8 +1273,8 @@ namespace Emby.Server.Implementations.LiveTv if (coreService != null) { - await coreService.RefreshSeriesTimers(cancellationToken, new Progress()).ConfigureAwait(false); - await coreService.RefreshTimers(cancellationToken, new Progress()).ConfigureAwait(false); + await coreService.RefreshSeriesTimers(cancellationToken, new SimpleProgress()).ConfigureAwait(false); + await coreService.RefreshTimers(cancellationToken, new SimpleProgress()).ConfigureAwait(false); } // Load these now which will prefetch metadata @@ -1549,7 +1549,7 @@ namespace Emby.Server.Implementations.LiveTv var idList = await Task.WhenAll(recordingTasks).ConfigureAwait(false); - await CleanDatabaseInternal(idList.ToList(), new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new Progress(), cancellationToken).ConfigureAwait(false); + await CleanDatabaseInternal(idList.ToList(), new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new SimpleProgress(), cancellationToken).ConfigureAwait(false); _lastRecordingRefreshTime = DateTime.UtcNow; } diff --git a/Emby.Server.Implementations/News/NewsEntryPoint.cs b/Emby.Server.Implementations/News/NewsEntryPoint.cs index 53c862d470..3c9a3bbf1d 100644 --- a/Emby.Server.Implementations/News/NewsEntryPoint.cs +++ b/Emby.Server.Implementations/News/NewsEntryPoint.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml; +using MediaBrowser.Common.Progress; using MediaBrowser.Model.IO; using MediaBrowser.Model.Threading; @@ -82,7 +83,7 @@ namespace Emby.Server.Implementations.News var requestOptions = new HttpRequestOptions { Url = "http://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog", - Progress = new Progress(), + Progress = new SimpleProgress(), UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.42 Safari/537.36", BufferContent = false }; diff --git a/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs index 8b3367217c..0239ee3365 100644 --- a/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs +++ b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs @@ -23,7 +23,6 @@ namespace Emby.Server.Implementations.Notifications public void Run() { _notificationsRepo.NotificationAdded += _notificationsRepo_NotificationAdded; - _notificationsRepo.NotificationsMarkedRead += _notificationsRepo_NotificationsMarkedRead; } diff --git a/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs index e619b68649..9f887ba030 100644 --- a/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Progress; using MediaBrowser.Model.Tasks; namespace Emby.Server.Implementations.ScheduledTasks @@ -77,7 +78,7 @@ namespace Emby.Server.Implementations.ScheduledTasks try { - await _installationManager.InstallPackage(i, true, new Progress(), cancellationToken).ConfigureAwait(false); + await _installationManager.InstallPackage(i, true, new SimpleProgress(), cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs index 28fd8b68cb..a41e21a4b7 100644 --- a/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Progress; using MediaBrowser.Model.Tasks; namespace Emby.Server.Implementations.ScheduledTasks @@ -70,7 +71,7 @@ namespace Emby.Server.Implementations.ScheduledTasks EventHandler innerProgressHandler = (sender, e) => progress.Report(e * .1); // Create a progress object for the update check - var innerProgress = new Progress(); + var innerProgress = new SimpleProgress(); innerProgress.ProgressChanged += innerProgressHandler; var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false); @@ -97,7 +98,7 @@ namespace Emby.Server.Implementations.ScheduledTasks innerProgressHandler = (sender, e) => progress.Report(e * .9 + .1); - innerProgress = new Progress(); + innerProgress = new SimpleProgress(); innerProgress.ProgressChanged += innerProgressHandler; await _appHost.UpdateApplication(updateInfo.Package, cancellationToken, innerProgress).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index eed17fb66d..717416da1b 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -246,7 +246,7 @@ namespace Emby.Server.Implementations.Updates { Url = "https://www.mb3admin.com/admin/service/MB3Packages.json", CancellationToken = cancellationToken, - Progress = new Progress() + Progress = new SimpleProgress() }).ConfigureAwait(false); diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs index 534089848e..d26fb768a3 100644 --- a/MediaBrowser.Api/ItemRefreshService.cs +++ b/MediaBrowser.Api/ItemRefreshService.cs @@ -65,7 +65,7 @@ namespace MediaBrowser.Api _providerManager.QueueRefresh(item.Id, options, RefreshPriority.High); } - private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request) + private MetadataRefreshOptions GetRefreshOptions(RefreshItem request) { return new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) { @@ -73,8 +73,9 @@ namespace MediaBrowser.Api ImageRefreshMode = request.ImageRefreshMode, ReplaceAllImages = request.ReplaceAllImages, ReplaceAllMetadata = request.ReplaceAllMetadata, - ForceSave = true, - IsAutomated = false + ForceSave = request.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || request.ImageRefreshMode == ImageRefreshMode.FullRefresh || request.ReplaceAllImages || request.ReplaceAllMetadata, + IsAutomated = false, + ValidateChildren = request.Recursive }; } } diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index d9fc7143f9..80d8c072ee 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -28,6 +28,7 @@ using MediaBrowser.Controller.IO; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Services; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Progress; namespace MediaBrowser.Api.Library { @@ -445,7 +446,7 @@ namespace MediaBrowser.Api.Library } else { - Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None)); + Task.Run(() => _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None)); } } @@ -483,7 +484,7 @@ namespace MediaBrowser.Api.Library } else { - Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None)); + Task.Run(() => _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None)); } } @@ -696,7 +697,7 @@ namespace MediaBrowser.Api.Library { try { - _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); + _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); } catch (Exception ex) { diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index 8e396ff57a..ae488f066c 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -8,7 +8,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; - +using MediaBrowser.Common.Progress; using MediaBrowser.Model.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -208,7 +208,7 @@ namespace MediaBrowser.Api.Library /// System.Object. public object Get(GetVirtualFolders request) { - var result = _libraryManager.GetVirtualFolders().OrderBy(i => i.Name).ToList(); + var result = _libraryManager.GetVirtualFolders(true); return ToOptimizedSerializedResultUsingCache(result); } @@ -290,7 +290,7 @@ namespace MediaBrowser.Api.Library // No need to start if scanning the library because it will handle it if (request.RefreshLibrary) { - _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); + _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); } else { @@ -347,7 +347,7 @@ namespace MediaBrowser.Api.Library // No need to start if scanning the library because it will handle it if (request.RefreshLibrary) { - _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); + _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); } else { @@ -400,7 +400,7 @@ namespace MediaBrowser.Api.Library // No need to start if scanning the library because it will handle it if (request.RefreshLibrary) { - _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); + _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); } else { diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index 125cec681b..64424795fc 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Progress; using MediaBrowser.Model.Services; namespace MediaBrowser.Api @@ -156,7 +157,7 @@ namespace MediaBrowser.Api else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase)) { - var updateCheckResult = _appHost.CheckForApplicationUpdate(CancellationToken.None, new Progress()).Result; + var updateCheckResult = _appHost.CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress()).Result; if (updateCheckResult.IsUpdateAvailable) { @@ -233,7 +234,7 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name)); } - Task.Run(() => _installationManager.InstallPackage(package, true, new Progress(), CancellationToken.None)); + Task.Run(() => _installationManager.InstallPackage(package, true, new SimpleProgress(), CancellationToken.None)); } /// diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs index 39f46ce057..503f3f407a 100644 --- a/MediaBrowser.Common/Progress/ActionableProgress.cs +++ b/MediaBrowser.Common/Progress/ActionableProgress.cs @@ -7,12 +7,13 @@ namespace MediaBrowser.Common.Progress /// Class ActionableProgress /// /// - public class ActionableProgress : Progress, IDisposable + public class ActionableProgress : IProgress, IDisposable { /// /// The _actions /// private readonly List> _actions = new List>(); + public event EventHandler ProgressChanged; /// /// Registers the action. @@ -21,22 +22,6 @@ namespace MediaBrowser.Common.Progress public void RegisterAction(Action action) { _actions.Add(action); - - ProgressChanged -= ActionableProgress_ProgressChanged; - ProgressChanged += ActionableProgress_ProgressChanged; - } - - /// - /// Actionables the progress_ progress changed. - /// - /// The sender. - /// The e. - void ActionableProgress_ProgressChanged(object sender, T e) - { - foreach (var action in _actions) - { - action(e); - } } /// @@ -55,9 +40,34 @@ namespace MediaBrowser.Common.Progress { if (disposing) { - ProgressChanged -= ActionableProgress_ProgressChanged; _actions.Clear(); } } + + public void Report(T value) + { + if (ProgressChanged != null) + { + ProgressChanged(this, value); + } + + foreach (var action in _actions) + { + action(value); + } + } + } + + public class SimpleProgress : IProgress + { + public event EventHandler ProgressChanged; + + public void Report(T value) + { + if (ProgressChanged != null) + { + ProgressChanged(this, value); + } + } } } diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index 862c8e5ace..8e2faa20e2 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -6,6 +6,7 @@ using System.Linq; using MediaBrowser.Model.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Progress; namespace MediaBrowser.Controller.Channels { @@ -51,7 +52,7 @@ namespace MediaBrowser.Controller.Channels SortBy = query.SortBy, SortOrder = query.SortOrder - }, new Progress(), CancellationToken.None).Result; + }, new SimpleProgress(), CancellationToken.None).Result; } catch { diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs index b9d9d7ddad..098ba558f8 100644 --- a/MediaBrowser.Controller/Dto/DtoOptions.cs +++ b/MediaBrowser.Controller/Dto/DtoOptions.cs @@ -10,7 +10,8 @@ namespace MediaBrowser.Controller.Dto { private static readonly List DefaultExcludedFields = new List { - ItemFields.SeasonUserData + ItemFields.SeasonUserData, + ItemFields.RefreshState }; public List Fields { get; set; } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index e9eca19cf7..516ab50530 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -235,8 +235,6 @@ namespace MediaBrowser.Controller.Entities.Audio { await RefreshArtists(refreshOptions, cancellationToken).ConfigureAwait(false); } - - progress.Report(100); } private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 4b232de499..ebd83205e5 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -250,8 +250,6 @@ namespace MediaBrowser.Controller.Entities.Audio percent /= totalItems; progress.Report(percent * 100); } - - progress.Report(100); } public ArtistInfo GetLookupInfo() diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 4efea94d8e..b4a3d89eac 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1058,6 +1058,16 @@ namespace MediaBrowser.Controller.Entities return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)), cancellationToken); } + protected virtual void TriggerOnRefreshStart() + { + + } + + protected virtual void TriggerOnRefreshComplete() + { + + } + /// /// Overrides the base implementation to refresh metadata for local trailers /// @@ -1066,6 +1076,8 @@ namespace MediaBrowser.Controller.Entities /// true if a provider reports we changed public async Task RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken) { + TriggerOnRefreshStart(); + var locationType = LocationType; var requiresSave = false; @@ -1091,14 +1103,21 @@ namespace MediaBrowser.Controller.Entities } } - var refreshOptions = requiresSave - ? new MetadataRefreshOptions(options) - { - ForceSave = true - } - : options; + try + { + var refreshOptions = requiresSave + ? new MetadataRefreshOptions(options) + { + ForceSave = true + } + : options; - return await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); + return await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); + } + finally + { + TriggerOnRefreshComplete(); + } } [IgnoreDataMember] @@ -2421,5 +2440,10 @@ namespace MediaBrowser.Controller.Entities { return new List(); } + + public virtual double? GetRefreshProgress() + { + return null; + } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 8bc23a5819..0bd8606b96 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; @@ -199,6 +200,30 @@ namespace MediaBrowser.Controller.Entities return changed; } + public override double? GetRefreshProgress() + { + var folders = GetPhysicalFolders(true).ToList(); + double totalProgresses = 0; + var foldersWithProgress = 0; + + foreach (var folder in folders) + { + var progress = ProviderManager.GetRefreshProgress(folder.Id); + if (progress.HasValue) + { + totalProgresses += progress.Value; + foldersWithProgress++; + } + } + + if (foldersWithProgress == 0) + { + return null; + } + + return (totalProgresses / foldersWithProgress); + } + protected override bool RefreshLinkedChildren(IEnumerable fileSystemChildren) { return RefreshLinkedChildrenInternal(true); @@ -321,6 +346,11 @@ namespace MediaBrowser.Controller.Entities return GetPhysicalFolders(true).SelectMany(c => c.Children); } + public IEnumerable GetPhysicalFolders() + { + return GetPhysicalFolders(true); + } + private IEnumerable GetPhysicalFolders(bool enableCache) { if (enableCache) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 727b7dbebe..34b33fde0d 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -271,6 +271,11 @@ namespace MediaBrowser.Controller.Entities return GetCachedChildren(); } + public override double? GetRefreshProgress() + { + return ProviderManager.GetRefreshProgress(Id); + } + public Task ValidateChildren(IProgress progress, CancellationToken cancellationToken) { return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem))); @@ -318,6 +323,14 @@ namespace MediaBrowser.Controller.Entities return current.IsValidFromResolver(newItem); } + protected override void TriggerOnRefreshStart() + { + } + + protected override void TriggerOnRefreshComplete() + { + } + /// /// Validates the children internal. /// @@ -328,7 +341,27 @@ namespace MediaBrowser.Controller.Entities /// The refresh options. /// The directory service. /// Task. - protected async virtual Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + protected virtual async Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + { + if (recursive) + { + ProviderManager.OnRefreshStart(this); + } + + try + { + await ValidateChildrenInternal2(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService).ConfigureAwait(false); + } + finally + { + if (recursive) + { + ProviderManager.OnRefreshComplete(this); + } + } + } + + private async Task ValidateChildrenInternal2(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { var locationType = LocationType; @@ -360,6 +393,11 @@ namespace MediaBrowser.Controller.Entities progress.Report(5); + if (recursive) + { + ProviderManager.OnRefreshProgress(this, 5); + } + //build a dictionary of the current children we have now by Id so we can compare quickly and easily var currentChildren = GetActualChildrenDictionary(); @@ -424,76 +462,99 @@ namespace MediaBrowser.Controller.Entities await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); } } + else + { + if (recursive || refreshChildMetadata) + { + // used below + validChildren = Children.ToList(); + } + } progress.Report(10); - cancellationToken.ThrowIfCancellationRequested(); - if (recursive) { - await ValidateSubFolders(Children.ToList().OfType().ToList(), directoryService, progress, cancellationToken).ConfigureAwait(false); + ProviderManager.OnRefreshProgress(this, 10); } - progress.Report(20); + cancellationToken.ThrowIfCancellationRequested(); - if (refreshChildMetadata) + if (recursive) { - var container = this as IMetadataContainer; + using (var innerProgress = new ActionableProgress()) + { + var folder = this; + innerProgress.RegisterAction(p => + { + double newPct = .70 * p + 10; + progress.Report(newPct); + ProviderManager.OnRefreshProgress(folder, newPct); + }); - var innerProgress = new ActionableProgress(); + await ValidateSubFolders(validChildren.OfType().ToList(), directoryService, innerProgress, cancellationToken).ConfigureAwait(false); + } + } - innerProgress.RegisterAction(p => progress.Report(.80 * p + 20)); + if (refreshChildMetadata) + { + progress.Report(80); - if (container != null) + if (recursive) { - await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false); + ProviderManager.OnRefreshProgress(this, 80); } - else + + var container = this as IMetadataContainer; + + using (var innerProgress = new ActionableProgress()) { - await RefreshMetadataRecursive(refreshOptions, recursive, innerProgress, cancellationToken); + var folder = this; + innerProgress.RegisterAction(p => + { + double newPct = .20 * p + 80; + progress.Report(newPct); + if (recursive) + { + ProviderManager.OnRefreshProgress(folder, newPct); + } + }); + + if (container != null) + { + await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false); + } + else + { + await RefreshMetadataRecursive(validChildren, refreshOptions, recursive, innerProgress, cancellationToken); + } } } - - progress.Report(100); } - private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress progress, CancellationToken cancellationToken) + private async Task RefreshMetadataRecursive(List children, MetadataRefreshOptions refreshOptions, bool recursive, IProgress progress, CancellationToken cancellationToken) { - var children = Children.ToList(); - - var percentages = new Dictionary(children.Count); var numComplete = 0; var count = children.Count; + double currentPercent = 0; foreach (var child in children) { cancellationToken.ThrowIfCancellationRequested(); - if (child.IsFolder) + using (var innerProgress = new ActionableProgress()) { - var innerProgress = new ActionableProgress(); - // Avoid implicitly captured closure - var currentChild = child; + var currentInnerPercent = currentPercent; + innerProgress.RegisterAction(p => { - lock (percentages) - { - percentages[currentChild.Id] = p / 100; - - var innerPercent = percentages.Values.Sum(); - innerPercent /= count; - innerPercent *= 100; - progress.Report(innerPercent); - } + double innerPercent = currentInnerPercent; + innerPercent += p / (count); + progress.Report(innerPercent); }); - await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken) - .ConfigureAwait(false); - } - else - { - await RefreshChildMetadata(child, refreshOptions, false, new Progress(), cancellationToken) + await RefreshChildMetadata(child, refreshOptions, recursive && child.IsFolder, innerProgress, cancellationToken) .ConfigureAwait(false); } @@ -501,11 +562,10 @@ namespace MediaBrowser.Controller.Entities double percent = numComplete; percent /= count; percent *= 100; + currentPercent = percent; progress.Report(percent); } - - progress.Report(100); } private async Task RefreshChildMetadata(BaseItem child, MetadataRefreshOptions refreshOptions, bool recursive, IProgress progress, CancellationToken cancellationToken) @@ -526,11 +586,10 @@ namespace MediaBrowser.Controller.Entities if (folder != null) { - await folder.RefreshMetadataRecursive(refreshOptions, true, progress, cancellationToken); + await folder.RefreshMetadataRecursive(folder.Children.ToList(), refreshOptions, true, progress, cancellationToken); } } } - progress.Report(100); } /// @@ -543,47 +602,40 @@ namespace MediaBrowser.Controller.Entities /// Task. private async Task ValidateSubFolders(IList children, IDirectoryService directoryService, IProgress progress, CancellationToken cancellationToken) { - var list = children; - var childCount = list.Count; - - var percentages = new Dictionary(list.Count); + var numComplete = 0; + var count = children.Count; + double currentPercent = 0; - foreach (var item in list) + foreach (var child in children) { cancellationToken.ThrowIfCancellationRequested(); - var child = item; - - var innerProgress = new ActionableProgress(); - - innerProgress.RegisterAction(p => + using (var innerProgress = new ActionableProgress()) { - lock (percentages) + // Avoid implicitly captured closure + var currentInnerPercent = currentPercent; + + innerProgress.RegisterAction(p => { - percentages[child.Id] = p / 100; + double innerPercent = currentInnerPercent; + innerPercent += p / (count); + progress.Report(innerPercent); + }); - var percent = percentages.Values.Sum(); - percent /= childCount; + await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService) + .ConfigureAwait(false); + } - progress.Report(10 * percent + 10); - } - }); + numComplete++; + double percent = numComplete; + percent /= count; + percent *= 100; + currentPercent = percent; - await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService) - .ConfigureAwait(false); + progress.Report(percent); } } - /// - /// Determines whether the specified path is offline. - /// - /// The path. - /// true if the specified path is offline; otherwise, false. - public static bool IsPathOffline(string path) - { - return IsPathOffline(path, LibraryManager.GetVirtualFolders().SelectMany(i => i.Locations).ToList()); - } - public static bool IsPathOffline(string path, List allLibraryPaths) { if (FileSystem.FileExists(path)) @@ -926,7 +978,7 @@ namespace MediaBrowser.Controller.Entities SortBy = query.SortBy, SortOrder = query.SortOrder - }, new Progress(), CancellationToken.None).Result; + }, new SimpleProgress(), CancellationToken.None).Result; } catch { diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 0b96624505..1e4b3fdad3 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -414,8 +414,6 @@ namespace MediaBrowser.Controller.Entities.TV refreshOptions = new MetadataRefreshOptions(refreshOptions); refreshOptions.IsPostRecursiveRefresh = true; await ProviderManager.RefreshSingleItem(this, refreshOptions, cancellationToken).ConfigureAwait(false); - - progress.Report(100); } public IEnumerable GetSeasonEpisodes(Season parentSeason, User user, DtoOptions options) diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 5a702b4f08..b726c267c6 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -132,7 +132,9 @@ namespace MediaBrowser.Controller.Library /// Gets the default view. /// /// IEnumerable{VirtualFolderInfo}. - IEnumerable GetVirtualFolders(); + List GetVirtualFolders(); + + List GetVirtualFolders(bool includeRefreshState); /// /// Gets the item by id. diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 348cfd343f..22f94695d8 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -320,7 +320,6 @@ - diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index c0bc902140..0ba573da8b 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Events; namespace MediaBrowser.Controller.Providers { @@ -30,7 +31,7 @@ namespace MediaBrowser.Controller.Providers /// The cancellation token. /// Task. Task RefreshFullItem(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken); - + /// /// Refreshes the metadata. /// @@ -68,7 +69,7 @@ namespace MediaBrowser.Controller.Providers /// /// Task. Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken); - + /// /// Adds the metadata providers. /// @@ -128,7 +129,7 @@ namespace MediaBrowser.Controller.Providers /// The savers. /// Task. Task SaveMetadata(IHasMetadata item, ItemUpdateType updateType, IEnumerable savers); - + /// /// Gets the metadata options. /// @@ -158,6 +159,18 @@ namespace MediaBrowser.Controller.Providers /// The cancellation token. /// Task{HttpResponseInfo}. Task GetSearchImage(string providerName, string url, CancellationToken cancellationToken); + + Dictionary GetRefreshQueue(); + + void OnRefreshStart(BaseItem item); + void OnRefreshProgress(BaseItem item, double progress); + void OnRefreshComplete(BaseItem item); + + double? GetRefreshProgress(Guid id); + + event EventHandler> RefreshStarted; + event EventHandler> RefreshCompleted; + event EventHandler>> RefreshProgress; } public enum RefreshPriority diff --git a/MediaBrowser.Controller/Providers/ProviderRefreshStatus.cs b/MediaBrowser.Controller/Providers/ProviderRefreshStatus.cs deleted file mode 100644 index 6523dc4173..0000000000 --- a/MediaBrowser.Controller/Providers/ProviderRefreshStatus.cs +++ /dev/null @@ -1,22 +0,0 @@ - -namespace MediaBrowser.Controller.Providers -{ - /// - /// Enum ProviderRefreshStatus - /// - public enum ProviderRefreshStatus - { - /// - /// The success - /// - Success = 0, - /// - /// The completed with errors - /// - CompletedWithErrors = 1, - /// - /// The failure - /// - Failure = 2 - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs index 1b5b4b4e41..e21292cbd7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs +++ b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs @@ -7,6 +7,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; +using MediaBrowser.Common.Progress; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -62,7 +63,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // Kick this off, but no need to wait on it var task = Task.Run(async () => { - await DownloadFontFile(fontsDirectory, fontFilename, new Progress()).ConfigureAwait(false); + await DownloadFontFile(fontsDirectory, fontFilename, new SimpleProgress()).ConfigureAwait(false); await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); }); diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs index d8ec04ff64..374d8d028d 100644 --- a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs +++ b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs @@ -47,5 +47,8 @@ namespace MediaBrowser.Model.Entities /// /// The primary image item identifier. public string PrimaryImageItemId { get; set; } + + public double? RefreshProgress { get; set; } + public string RefreshStatus { get; set; } } } diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 5873293f72..c6f34d58a6 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -228,6 +228,7 @@ ExternalSeriesId, SeriesPresentationUniqueKey, DateLastRefreshed, - DateLastSaved + DateLastSaved, + RefreshState } } diff --git a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs index 566378dd02..499fd2e0b3 100644 --- a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs +++ b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Progress; using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.ImagesByName @@ -30,7 +31,7 @@ namespace MediaBrowser.Providers.ImagesByName var temp = await httpClient.GetTempFile(new HttpRequestOptions { CancellationToken = cancellationToken, - Progress = new Progress(), + Progress = new SimpleProgress(), Url = url }).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 3cdda2cbb8..ce995fe647 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -18,8 +18,10 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Progress; using MediaBrowser.Model.IO; using MediaBrowser.Controller.Dto; +using MediaBrowser.Model.Events; using MediaBrowser.Model.Serialization; using Priority_Queue; @@ -67,6 +69,10 @@ namespace MediaBrowser.Providers.Manager private readonly IMemoryStreamFactory _memoryStreamProvider; private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); + public event EventHandler> RefreshStarted; + public event EventHandler> RefreshCompleted; + public event EventHandler>> RefreshProgress; + /// /// Initializes a new instance of the class. /// @@ -849,6 +855,89 @@ namespace MediaBrowser.Providers.Manager }); } + private Dictionary _activeRefreshes = new Dictionary(); + + public Dictionary GetRefreshQueue() + { + lock (_refreshQueueLock) + { + var dict = new Dictionary(); + + foreach (var item in _refreshQueue) + { + dict[item.Item1] = item.Item1; + } + return dict; + } + } + + public void OnRefreshStart(BaseItem item) + { + //_logger.Info("OnRefreshStart {0}", item.Id.ToString("N")); + var id = item.Id; + + lock (_activeRefreshes) + { + _activeRefreshes[id] = 0; + } + + if (RefreshStarted != null) + { + RefreshStarted(this, new GenericEventArgs(item)); + } + } + + public void OnRefreshComplete(BaseItem item) + { + //_logger.Info("OnRefreshComplete {0}", item.Id.ToString("N")); + lock (_activeRefreshes) + { + _activeRefreshes.Remove(item.Id); + } + + if (RefreshCompleted != null) + { + RefreshCompleted(this, new GenericEventArgs(item)); + } + } + + public double? GetRefreshProgress(Guid id) + { + lock (_activeRefreshes) + { + double value; + if (_activeRefreshes.TryGetValue(id, out value)) + { + return value; + } + + return null; + } + } + + public void OnRefreshProgress(BaseItem item, double progress) + { + //_logger.Info("OnRefreshProgress {0} {1}", item.Id.ToString("N"), progress); + var id = item.Id; + + lock (_activeRefreshes) + { + if (_activeRefreshes.ContainsKey(id)) + { + _activeRefreshes[id] = progress; + + if (RefreshProgress != null) + { + RefreshProgress(this, new GenericEventArgs>(new Tuple(item, progress))); + } + } + else + { + throw new Exception(string.Format("Refresh for item {0} {1} is not in progress", item.GetType().Name, item.Id.ToString("N"))); + } + } + } + private readonly SimplePriorityQueue> _refreshQueue = new SimplePriorityQueue>(); @@ -906,7 +995,7 @@ namespace MediaBrowser.Providers.Manager var folder = item as Folder; if (folder != null) { - await folder.ValidateChildren(new Progress(), cancellationToken).ConfigureAwait(false); + await folder.ValidateChildren(new SimpleProgress(), cancellationToken).ConfigureAwait(false); } } @@ -951,14 +1040,14 @@ namespace MediaBrowser.Providers.Manager if (folder != null) { - await folder.ValidateChildren(new Progress(), cancellationToken, options).ConfigureAwait(false); + await folder.ValidateChildren(new SimpleProgress(), cancellationToken, options).ConfigureAwait(false); } } } private async Task RefreshCollectionFolderChildren(MetadataRefreshOptions options, CollectionFolder collectionFolder, CancellationToken cancellationToken) { - foreach (var child in collectionFolder.Children.ToList()) + foreach (var child in collectionFolder.GetPhysicalFolders().ToList()) { await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); @@ -966,7 +1055,7 @@ namespace MediaBrowser.Providers.Manager { var folder = (Folder)child; - await folder.ValidateChildren(new Progress(), cancellationToken, options, true).ConfigureAwait(false); + await folder.ValidateChildren(new SimpleProgress(), cancellationToken, options, true).ConfigureAwait(false); } } } @@ -991,7 +1080,7 @@ namespace MediaBrowser.Providers.Manager .Where(i => i != null) .ToList(); - var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new Progress(), cancellationToken, options, true)); + var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new SimpleProgress(), cancellationToken, options, true)); await Task.WhenAll(musicArtistRefreshTasks).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index 3b2d674a66..aef29abbea 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.TV //await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false); - //await series.ValidateChildren(new Progress(), cancellationToken, new MetadataRefreshOptions(directoryService)) + //await series.ValidateChildren(new SimpleProgress(), cancellationToken, new MetadataRefreshOptions(directoryService)) // .ConfigureAwait(false); } } diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index da5a1f2d1e..51ad12364c 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -13,7 +13,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; - +using MediaBrowser.Common.Progress; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Globalization; @@ -162,7 +162,7 @@ namespace MediaBrowser.Providers.TV await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false); - await series.ValidateChildren(new Progress(), cancellationToken, new MetadataRefreshOptions(directoryService), true) + await series.ValidateChildren(new SimpleProgress(), cancellationToken, new MetadataRefreshOptions(directoryService), true) .ConfigureAwait(false); } } -- cgit v1.2.3 From 023952d04df3765ee5574915f0a84903bb97a748 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 23 Jun 2017 12:07:32 -0400 Subject: 3.2.20.11 --- Emby.Server.Implementations/Dto/DtoService.cs | 24 ++++++++++++++++++++---- SharedVersion.cs | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 7352073bd3..52ebd5e308 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -105,9 +105,13 @@ namespace Emby.Server.Implementations.Dto var programTuples = new List>(); var channelTuples = new List>(); + var refreshQueue = options.Fields.Contains(ItemFields.RefreshState) + ? _providerManager.GetRefreshQueue() + : null; + foreach (var item in items) { - var dto = GetBaseItemDtoInternal(item, options, user, owner); + var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user, owner); var tvChannel = item as LiveTvChannel; if (tvChannel != null) @@ -160,7 +164,11 @@ namespace Emby.Server.Implementations.Dto { var syncDictionary = GetSyncedItemProgress(options); - var dto = GetBaseItemDtoInternal(item, options, user, owner); + var refreshQueue = options.Fields.Contains(ItemFields.RefreshState) + ? _providerManager.GetRefreshQueue() + : null; + + var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user, owner); var tvChannel = item as LiveTvChannel; if (tvChannel != null) { @@ -292,7 +300,7 @@ namespace Emby.Server.Implementations.Dto } } - private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) + private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, Dictionary currentRefreshQueue, User user = null, BaseItem owner = null) { var fields = options.Fields; @@ -392,6 +400,11 @@ namespace Emby.Server.Implementations.Dto dto.Etag = item.GetEtag(user); } + if (currentRefreshQueue != null) + { + //dto.RefreshState = item.GetRefreshState(currentRefreshQueue); + } + if (item is ILiveTvRecording) { _livetvManager().AddInfoToRecordingDto(item, dto, user); @@ -402,7 +415,10 @@ namespace Emby.Server.Implementations.Dto public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List taggedItems, Dictionary syncProgress, User user = null) { - var dto = GetBaseItemDtoInternal(item, options, user); + var refreshQueue = options.Fields.Contains(ItemFields.RefreshState) + ? _providerManager.GetRefreshQueue() + : null; + var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user); if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts)) { diff --git a/SharedVersion.cs b/SharedVersion.cs index 7df961c8de..ec801ba7fe 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.10")] +[assembly: AssemblyVersion("3.2.20.11")] -- cgit v1.2.3 From 6b2445aa2c224b9d03e6a1d295e43ee6363a5d5f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 24 Jun 2017 14:32:22 -0400 Subject: add stream loop option for m3u --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 7 ++----- .../LiveTv/Listings/XmlTvListingsProvider.cs | 13 +++++++++---- .../LiveTv/TunerHosts/M3UTunerHost.cs | 2 +- MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 2 ++ 4 files changed, 14 insertions(+), 10 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index e999f57532..1144bd3c66 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4297,12 +4297,9 @@ namespace Emby.Server.Implementations.Data } } - if (query.HasDeadParentId.HasValue) + if (query.HasDeadParentId.HasValue && query.HasDeadParentId.Value) { - if (query.HasDeadParentId.Value) - { - whereClauses.Add("ParentId NOT NULL AND ParentId NOT IN (select guid from TypedBaseItems)"); - } + whereClauses.Add("ParentId NOT NULL AND ParentId NOT IN (select guid from TypedBaseItems)"); } if (query.Years.Length == 1) diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 9bf9f140d4..fb8308cda5 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -49,8 +49,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings get { return "xmltv"; } } - private string GetLanguage() + private string GetLanguage(ListingsProviderInfo info) { + if (!string.IsNullOrWhiteSpace(info.PreferredLanguage)) + { + return info.PreferredLanguage; + } + return _config.Configuration.PreferredMetadataLanguage; } @@ -152,7 +157,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings _logger.Debug("Getting xmltv programs for channel {0}", channelId); var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false); - var reader = new XmlTvReader(path, GetLanguage()); + var reader = new XmlTvReader(path, GetLanguage(info)); var results = reader.GetProgrammes(channelId, startDateUtc, endDateUtc, cancellationToken); return results.Select(p => GetProgramInfo(p, info)); @@ -254,7 +259,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { // In theory this should never be called because there is always only one lineup var path = await GetXml(info.Path, CancellationToken.None).ConfigureAwait(false); - var reader = new XmlTvReader(path, GetLanguage()); + var reader = new XmlTvReader(path, GetLanguage(info)); var results = reader.GetChannels(); // Should this method be async? @@ -265,7 +270,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { // In theory this should never be called because there is always only one lineup var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false); - var reader = new XmlTvReader(path, GetLanguage()); + var reader = new XmlTvReader(path, GetLanguage(info)); var results = reader.GetChannels(); // Should this method be async? diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 12b7901f93..8bf7a052e9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -148,7 +148,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts }, RequiresOpening = true, RequiresClosing = true, - RequiresLooping = true, + RequiresLooping = info.EnableStreamLooping, ReadAtNativeFramerate = false, diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 32153a11ca..fb8c34034e 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -47,6 +47,7 @@ namespace MediaBrowser.Model.LiveTv public bool ImportFavoritesOnly { get; set; } public bool AllowHWTranscoding { get; set; } public bool EnableTvgId { get; set; } + public bool EnableStreamLooping { get; set; } public TunerHostInfo() { @@ -74,6 +75,7 @@ namespace MediaBrowser.Model.LiveTv public NameValuePair[] ChannelMappings { get; set; } public string MoviePrefix { get; set; } public bool EnableNewProgramIds { get; set; } + public string PreferredLanguage { get; set; } public ListingsProviderInfo() { -- cgit v1.2.3 From 02889f751dfed291bb13f6a56575f67464e03faa Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 26 Jun 2017 13:23:03 -0400 Subject: improve artist cleanup --- .../Library/Validators/ArtistsValidator.cs | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs index d4be2dabed..1a53ad672f 100644 --- a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs +++ b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs @@ -2,11 +2,14 @@ using MediaBrowser.Controller.Library; 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.Controller.Dto; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Persistence; namespace Emby.Server.Implementations.Library.Validators @@ -78,6 +81,35 @@ namespace Emby.Server.Implementations.Library.Validators progress.Report(percent); } + names = names.Select(i => i.RemoveDiacritics()).DistinctNames().ToList(); + + var artistEntities = _libraryManager.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(MusicArtist).Name } + + }).Cast().ToList(); + + foreach (var artist in artistEntities) + { + if (!artist.IsAccessedByName) + { + continue; + } + + var name = (artist.Name ?? string.Empty).RemoveDiacritics(); + + if (!names.Contains(name, StringComparer.OrdinalIgnoreCase)) + { + _logger.Info("Deleting dead artist {0} {1}.", artist.Id.ToString("N"), artist.Name); + + await _libraryManager.DeleteItem(artist, new DeleteOptions + { + DeleteFileLocation = false + + }).ConfigureAwait(false); + } + } + progress.Report(100); } } -- cgit v1.2.3 From 9a4fe6625d88a48e3328b7a9a1374615581d588e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 27 Jun 2017 16:55:22 -0400 Subject: xml tv fixes --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 4 ++-- Emby.Server.Implementations/Library/LibraryManager.cs | 8 ++++---- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 2 +- Emby.Server.Implementations/packages.config | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 231ffe7913..a056a57d01 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -293,8 +293,8 @@ {1d74413b-e7cf-455b-b021-f52bdf881542} SocketHttpListener - - ..\packages\Emby.XmlTv.1.0.8\lib\portable-net45+win8\Emby.XmlTv.dll + + ..\packages\Emby.XmlTv.1.0.9\lib\portable-net45+win8\Emby.XmlTv.dll True diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 1a909549e7..4846a57682 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2365,7 +2365,7 @@ namespace Emby.Server.Implementations.Library public bool IsVideoFile(string path, LibraryOptions libraryOptions) { - var resolver = new VideoResolver(GetNamingOptions(libraryOptions), new NullLogger()); + var resolver = new VideoResolver(GetNamingOptions(), new NullLogger()); return resolver.IsVideoFile(path); } @@ -2376,7 +2376,7 @@ namespace Emby.Server.Implementations.Library public bool IsAudioFile(string path, LibraryOptions libraryOptions) { - var parser = new AudioFileParser(GetNamingOptions(libraryOptions)); + var parser = new AudioFileParser(GetNamingOptions()); return parser.IsAudioFile(path); } @@ -2533,13 +2533,13 @@ namespace Emby.Server.Implementations.Library return _namingOptionsWithoutOptimisticEpisodeDetection; } - return GetNamingOptions(new LibraryOptions()); + return GetNamingOptionsInternal(); } private NamingOptions _namingOptionsWithoutOptimisticEpisodeDetection; private NamingOptions _namingOptions; private string[] _videoFileExtensions; - public NamingOptions GetNamingOptions(LibraryOptions libraryOptions) + private NamingOptions GetNamingOptionsInternal() { if (_namingOptions == null) { diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 806e20f00a..70fe8d9ef7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -163,7 +163,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio private bool IsMultiDiscFolder(string path, LibraryOptions libraryOptions) { - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(libraryOptions); + var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); var parser = new AlbumParser(namingOptions, new NullLogger()); var result = parser.ParseMultiPart(path); diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config index 817e3eedb7..468bf925fd 100644 --- a/Emby.Server.Implementations/packages.config +++ b/Emby.Server.Implementations/packages.config @@ -1,6 +1,6 @@  - + -- cgit v1.2.3 From bbb43a6525ab0ee1d4b69d1507624bf1c25108d2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 28 Jun 2017 15:26:55 -0400 Subject: update logging --- Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs | 2 -- 1 file changed, 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 23ae4cee9e..69a205dda9 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -108,8 +108,6 @@ namespace Emby.Server.Implementations.EntryPoints try { _sessionManager.SendMessageToAdminSessions("RefreshProgress", dict, CancellationToken.None); - - _logger.Info("Sending refresh progress {0} {1}", item.Id.ToString("N"), progress); } catch { -- cgit v1.2.3 From dcaf8356e6400543e10ff2ee89fc9b3bdf97ef77 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 29 Jun 2017 15:10:58 -0400 Subject: update inherited images --- .../Data/SqliteItemRepository.cs | 4 ++-- Emby.Server.Implementations/Dto/DtoService.cs | 17 ++++++++++++----- Emby.Server.Implementations/Session/SessionManager.cs | 2 ++ MediaBrowser.Controller/Channels/Channel.cs | 9 +++++++++ .../Collections/ManualCollectionsFolder.cs | 11 +++++++++++ MediaBrowser.Controller/Entities/Audio/MusicArtist.cs | 9 +++++++++ MediaBrowser.Controller/Entities/BasePluginFolder.cs | 10 ++++++++++ MediaBrowser.Controller/Entities/CollectionFolder.cs | 9 +++++++++ MediaBrowser.Controller/Entities/Folder.cs | 9 +++++++++ MediaBrowser.Controller/Entities/GameSystem.cs | 9 +++++++++ MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 10 ++++++++++ MediaBrowser.Controller/Entities/PhotoAlbum.cs | 9 +++++++++ MediaBrowser.Controller/Entities/TV/Series.cs | 9 +++++++++ MediaBrowser.Controller/Entities/UserRootFolder.cs | 9 +++++++++ MediaBrowser.Controller/Entities/UserView.cs | 9 +++++++++ MediaBrowser.Controller/Entities/Video.cs | 9 +++++++++ MediaBrowser.Controller/LiveTv/RecordingGroup.cs | 9 +++++++++ MediaBrowser.Controller/Playlists/Playlist.cs | 9 +++++++++ MediaBrowser.Model/Session/PlaystateCommand.cs | 3 ++- MediaBrowser.Model/Session/SessionInfoDto.cs | 2 ++ .../Devices/CameraUploadsFolder.cs | 9 +++++++++ .../Playlists/ManualPlaylistsFolder.cs | 9 +++++++++ SharedVersion.cs | 2 +- 23 files changed, 178 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 1144bd3c66..52abbae3e2 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3758,10 +3758,10 @@ namespace Emby.Server.Implementations.Data if (query.MinDateLastSavedForUser.HasValue) { - whereClauses.Add("DateLastSaved>=@MinDateLastSaved"); + whereClauses.Add("DateLastSaved>=@MinDateLastSavedForUser"); if (statement != null) { - statement.TryBind("@MinDateLastSaved", query.MinDateLastSavedForUser.Value); + statement.TryBind("@MinDateLastSavedForUser", query.MinDateLastSavedForUser.Value); } } diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 52ebd5e308..9767de9e0a 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1464,9 +1464,9 @@ namespace Emby.Server.Implementations.Dto } } - private BaseItem GetImageDisplayParent(BaseItem item) + private BaseItem GetImageDisplayParent(BaseItem currentItem, BaseItem originalItem) { - var musicAlbum = item as MusicAlbum; + var musicAlbum = currentItem as MusicAlbum; if (musicAlbum != null) { var artist = musicAlbum.GetMusicArtist(new DtoOptions(false)); @@ -1476,7 +1476,14 @@ namespace Emby.Server.Implementations.Dto } } - return item.DisplayParent ?? item.GetParent(); + var parent = currentItem.DisplayParent ?? currentItem.GetParent(); + + if (parent == null && !(originalItem is UserRootFolder) && !(originalItem is UserView) && !(originalItem is AggregateFolder) && !(originalItem is ICollectionFolder) && !(originalItem is Channel)) + { + parent = _libraryManager.GetCollectionFolders(originalItem).FirstOrDefault(); + } + + return parent; } private void AddInheritedImages(BaseItemDto dto, BaseItem item, DtoOptions options, BaseItem owner) @@ -1503,7 +1510,7 @@ namespace Emby.Server.Implementations.Dto var isFirst = true; while (((!dto.HasLogo && logoLimit > 0) || (!dto.HasArtImage && artLimit > 0) || (!dto.HasThumb && thumbLimit > 0) || parent is Series) && - (parent = parent ?? (isFirst ? GetImageDisplayParent(item) ?? owner : parent)) != null) + (parent = parent ?? (isFirst ? GetImageDisplayParent(item, item) ?? owner : parent)) != null) { if (parent == null) { @@ -1560,7 +1567,7 @@ namespace Emby.Server.Implementations.Dto break; } - parent = GetImageDisplayParent(parent); + parent = GetImageDisplayParent(parent, item); } } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 6bfa90498f..68ab259470 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1622,6 +1622,8 @@ namespace Emby.Server.Implementations.Session TranscodingInfo = session.NowPlayingItem == null ? null : session.TranscodingInfo }; + dto.ServerId = _appHost.SystemId; + if (session.UserId.HasValue) { dto.UserId = session.UserId.Value.ToString("N"); diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index 8e2faa20e2..c6e750a0c0 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -32,6 +32,15 @@ namespace MediaBrowser.Controller.Channels return base.IsVisible(user); } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + [IgnoreDataMember] public override SourceType SourceType { diff --git a/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs index d2d28e5047..160a788f1c 100644 --- a/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs +++ b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Controller.Collections { @@ -18,11 +19,21 @@ namespace MediaBrowser.Controller.Collections } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + public bool IsHiddenFromUser(User user) { return !ConfigurationManager.Configuration.DisplayCollectionsView; } + [IgnoreDataMember] public override string CollectionType { get { return Model.Entities.CollectionType.BoxSets; } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index ebd83205e5..7a37b2e02e 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -35,6 +35,15 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + [IgnoreDataMember] public override bool SupportsCumulativeRunTimeTicks { diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index 7dbea317c8..a61862f280 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -9,6 +9,7 @@ namespace MediaBrowser.Controller.Entities /// public abstract class BasePluginFolder : Folder, ICollectionFolder { + [IgnoreDataMember] public virtual string CollectionType { get { return null; } @@ -24,6 +25,15 @@ namespace MediaBrowser.Controller.Entities return true; } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + [IgnoreDataMember] public override bool SupportsPeople { diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 0bd8606b96..d88b7da346 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -49,6 +49,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + public override bool CanDelete() { return false; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index ea442ba1ec..5d74cf218b 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -65,6 +65,15 @@ namespace MediaBrowser.Controller.Entities get { return false; } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return true; + } + } + [IgnoreDataMember] public override bool SupportsPlayedStatus { diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs index d8879155af..bbaec14a15 100644 --- a/MediaBrowser.Controller/Entities/GameSystem.cs +++ b/MediaBrowser.Controller/Entities/GameSystem.cs @@ -35,6 +35,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + /// /// Gets or sets the game system. /// diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 8d440e51b8..071ed405f1 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -29,6 +29,7 @@ namespace MediaBrowser.Controller.Entities.Movies Shares = new List(); } + [IgnoreDataMember] protected override bool FilterLinkedChildrenPerUser { get @@ -37,6 +38,15 @@ namespace MediaBrowser.Controller.Entities.Movies } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + public List LocalTrailerIds { get; set; } public List RemoteTrailerIds { get; set; } diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs index dd3cd98fb9..af9d8c801d 100644 --- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs +++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs @@ -21,5 +21,14 @@ namespace MediaBrowser.Controller.Entities return false; } } + + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 1e4b3fdad3..8b73b80b02 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -53,6 +53,15 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + public List LocalTrailerIds { get; set; } public List RemoteTrailerIds { get; set; } diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 3e4931e774..d351563450 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -33,6 +33,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + [IgnoreDataMember] public override bool SupportsPlayedStatus { diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 27be696ebd..0d2d69c949 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -40,6 +40,15 @@ namespace MediaBrowser.Controller.Entities return list; } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + [IgnoreDataMember] public override bool SupportsPlayedStatus { diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index b6887940ed..7978f47613 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -43,6 +43,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return true; + } + } + [IgnoreDataMember] public override bool SupportsPositionTicksResume { diff --git a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs index b54ca89747..3ee0613605 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs @@ -26,6 +26,15 @@ namespace MediaBrowser.Controller.LiveTv } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + [IgnoreDataMember] public override SourceType SourceType { diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 854f8d7a2f..c992ac56a7 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -32,6 +32,15 @@ namespace MediaBrowser.Controller.Playlists } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + [IgnoreDataMember] public override bool SupportsPlayedStatus { diff --git a/MediaBrowser.Model/Session/PlaystateCommand.cs b/MediaBrowser.Model/Session/PlaystateCommand.cs index 2af4f26e37..3b70d54542 100644 --- a/MediaBrowser.Model/Session/PlaystateCommand.cs +++ b/MediaBrowser.Model/Session/PlaystateCommand.cs @@ -37,6 +37,7 @@ namespace MediaBrowser.Model.Session /// /// The fast forward /// - FastForward + FastForward, + PlayPause } } \ No newline at end of file diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs index b21a089aab..3081d7ee3b 100644 --- a/MediaBrowser.Model/Session/SessionInfoDto.cs +++ b/MediaBrowser.Model/Session/SessionInfoDto.cs @@ -26,6 +26,8 @@ namespace MediaBrowser.Model.Session /// The id. public string Id { get; set; } + public string ServerId { get; set; } + /// /// Gets or sets the user id. /// diff --git a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs index 57254eed49..ae700e250f 100644 --- a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs +++ b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs @@ -33,6 +33,15 @@ namespace MediaBrowser.Server.Implementations.Devices get { return Model.Entities.CollectionType.Photos; } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + public override string GetClientTypeName() { return typeof(CollectionFolder).Name; diff --git a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs index bd16dc2e9e..236dbde9cc 100644 --- a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs +++ b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs @@ -37,6 +37,15 @@ namespace MediaBrowser.Server.Implementations.Playlists } } + [IgnoreDataMember] + public override bool SupportsInheritedParentImages + { + get + { + return false; + } + } + [IgnoreDataMember] public override string CollectionType { diff --git a/SharedVersion.cs b/SharedVersion.cs index d713641489..1af2abc27f 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.16")] +[assembly: AssemblyVersion("3.2.20.17")] -- cgit v1.2.3 From 1f96841e0452620763808b0b470e99ff188ef8f0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 30 Jun 2017 15:58:53 -0400 Subject: add perfect match indicator to subtitle editor --- .../Channels/ChannelManager.cs | 80 +++++++++++++++++++++- .../Data/CleanDatabaseScheduledTask.cs | 19 +---- MediaBrowser.Api/Subtitles/SubtitleService.cs | 6 +- .../Subtitles/ISubtitleManager.cs | 5 +- MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 1 + .../Subtitles/SubtitleManager.cs | 8 +-- 6 files changed, 89 insertions(+), 30 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 85f6c66673..2adf6a37ca 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -29,13 +29,15 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Tasks; namespace Emby.Server.Implementations.Channels { public class ChannelManager : IChannelManager { - private IChannel[] _channels; + internal IChannel[] Channels { get; private set; } private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; @@ -76,12 +78,12 @@ namespace Emby.Server.Implementations.Channels public void AddParts(IEnumerable channels) { - _channels = channels.ToArray(); + Channels = channels.ToArray(); } private IEnumerable GetAllChannels() { - return _channels + return Channels .OrderBy(i => i.Name); } @@ -1559,4 +1561,76 @@ namespace Emby.Server.Implementations.Channels return await _libraryManager.GetNamedView(name, "channels", "zz_" + name, cancellationToken).ConfigureAwait(false); } } + + public class ChannelsEntryPoint : IServerEntryPoint + { + private readonly IServerConfigurationManager _config; + private readonly IChannelManager _channelManager; + private readonly ITaskManager _taskManager; + private readonly IFileSystem _fileSystem; + + public ChannelsEntryPoint(IChannelManager channelManager, ITaskManager taskManager, IServerConfigurationManager config, IFileSystem fileSystem) + { + _channelManager = channelManager; + _taskManager = taskManager; + _config = config; + _fileSystem = fileSystem; + } + + public void Run() + { + var channels = ((ChannelManager)_channelManager).Channels + .Select(i => i.GetType().FullName.GetMD5().ToString("N")) + .ToArray(); + + var channelsString = string.Join(",", channels); + + if (!string.Equals(channelsString, GetSavedLastChannels(), StringComparison.OrdinalIgnoreCase)) + { + _taskManager.QueueIfNotRunning(); + + SetSavedLastChannels(channelsString); + } + } + + private string DataPath + { + get { return Path.Combine(_config.ApplicationPaths.DataPath, "channels.txt"); } + } + + private string GetSavedLastChannels() + { + try + { + return _fileSystem.ReadAllText(DataPath); + } + catch + { + return string.Empty; + } + } + + private void SetSavedLastChannels(string value) + { + try + { + if (string.IsNullOrWhiteSpace(value)) + { + _fileSystem.DeleteFile(DataPath); + + } + else + { + _fileSystem.WriteAllText(DataPath, value); + } + } + catch + { + } + } + + public void Dispose() + { + } + } } \ No newline at end of file diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs index e259557822..df8d1ac451 100644 --- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs +++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs @@ -56,24 +56,9 @@ namespace Emby.Server.Implementations.Data var rootChildren = _libraryManager.RootFolder.Children.ToList(); rootChildren = _libraryManager.GetUserRootFolder().Children.ToList(); - var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(p => - { - double newPercentCommplete = .45 * p; - progress.Report(newPercentCommplete); - }); - await CleanDeadItems(cancellationToken, innerProgress).ConfigureAwait(false); - progress.Report(45); - - innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(p => - { - double newPercentCommplete = 45 + .55 * p; - progress.Report(newPercentCommplete); - }); + await CleanDeadItems(cancellationToken, progress).ConfigureAwait(false); - await _itemRepo.UpdateInheritedValues(cancellationToken).ConfigureAwait(false); - progress.Report(100); + //await _itemRepo.UpdateInheritedValues(cancellationToken).ConfigureAwait(false); } private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress progress) diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 7dde974ae6..645aacdec6 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -46,6 +46,8 @@ namespace MediaBrowser.Api.Subtitles [ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Language { get; set; } + + public bool? IsPerfectMatch { get; set; } } [Route("/Items/{Id}/RemoteSearch/Subtitles/Providers", "GET")] @@ -247,11 +249,11 @@ namespace MediaBrowser.Api.Subtitles CancellationToken.None); } - public object Get(SearchRemoteSubtitles request) + public async Task Get(SearchRemoteSubtitles request) { var video = (Video)_libraryManager.GetItemById(request.Id); - var response = _subtitleManager.SearchSubtitles(video, request.Language, CancellationToken.None).Result; + var response = await _subtitleManager.SearchSubtitles(video, request.Language, request.IsPerfectMatch, CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(response); } diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index 0c814c0d4c..d1d5f27bef 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -28,12 +28,9 @@ namespace MediaBrowser.Controller.Subtitles /// /// Searches the subtitles. /// - /// The video. - /// The language. - /// The cancellation token. - /// Task{IEnumerable{RemoteSubtitleInfo}}. Task> SearchSubtitles(Video video, string language, + bool? isPerfectMatch, CancellationToken cancellationToken); /// diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index fb8c34034e..8555f9c380 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -52,6 +52,7 @@ namespace MediaBrowser.Model.LiveTv public TunerHostInfo() { AllowHWTranscoding = true; + EnableTvgId = true; } } diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index be2ecf04ae..f7071f519e 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -173,7 +173,7 @@ namespace MediaBrowser.Providers.Subtitles } } - public Task> SearchSubtitles(Video video, string language, CancellationToken cancellationToken) + public Task> SearchSubtitles(Video video, string language, bool? isPerfectMatch, CancellationToken cancellationToken) { if (video.LocationType != LocationType.FileSystem || video.VideoType != VideoType.VideoFile) @@ -207,7 +207,8 @@ namespace MediaBrowser.Providers.Subtitles ParentIndexNumber = video.ParentIndexNumber, ProductionYear = video.ProductionYear, ProviderIds = video.ProviderIds, - RuntimeTicks = video.RunTimeTicks + RuntimeTicks = video.RunTimeTicks, + IsPerfectMatch = isPerfectMatch ?? false }; var episode = video as Episode; @@ -261,8 +262,7 @@ namespace MediaBrowser.Providers.Subtitles _monitor.ReportFileSystemChangeComplete(path, false); } - _libraryManager.GetItemById(itemId).ChangedExternally(); - return Task.FromResult(true); + return _libraryManager.GetItemById(itemId).RefreshMetadata(CancellationToken.None); } public Task GetRemoteSubtitles(string id, CancellationToken cancellationToken) -- cgit v1.2.3 From f6237d4d058f0a6ff3918d2ab6301cc729311fbc Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 30 Jun 2017 23:15:04 -0400 Subject: fixes #1759 - Custom intro path change requires library scan --- .../Emby.Server.Implementations.csproj | 1 - .../ScheduledTasks/RefreshIntrosTask.cs | 105 --------------------- 2 files changed, 106 deletions(-) delete mode 100644 Emby.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index a056a57d01..5486f404f3 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -202,7 +202,6 @@ - diff --git a/Emby.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs b/Emby.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs deleted file mode 100644 index 9bf6f2824d..0000000000 --- a/Emby.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs +++ /dev/null @@ -1,105 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; - -namespace Emby.Server.Implementations.ScheduledTasks -{ - /// - /// Class RefreshIntrosTask - /// - public class RefreshIntrosTask : ILibraryPostScanTask - { - /// - /// The _library manager - /// - private readonly ILibraryManager _libraryManager; - /// - /// The _logger - /// - private readonly ILogger _logger; - - private readonly IFileSystem _fileSystem; - - /// - /// Initializes a new instance of the class. - /// - /// The library manager. - /// The logger. - /// The file system. - public RefreshIntrosTask(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem) - { - _libraryManager = libraryManager; - _logger = logger; - _fileSystem = fileSystem; - } - - /// - /// Runs the specified progress. - /// - /// The progress. - /// The cancellation token. - /// Task. - public async Task Run(IProgress progress, CancellationToken cancellationToken) - { - var files = _libraryManager.GetAllIntroFiles().ToList(); - - var numComplete = 0; - - foreach (var file in files) - { - cancellationToken.ThrowIfCancellationRequested(); - - try - { - await RefreshIntro(file, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error refreshing intro {0}", ex, file); - } - - numComplete++; - double percent = numComplete; - percent /= files.Count; - progress.Report(percent * 100); - } - } - - /// - /// Refreshes the intro. - /// - /// The path. - /// The cancellation token. - /// Task. - private async Task RefreshIntro(string path, CancellationToken cancellationToken) - { - var item = _libraryManager.ResolvePath(_fileSystem.GetFileSystemInfo(path)); - - if (item == null) - { - _logger.Error("Intro resolver returned null for {0}", path); - return; - } - - var dbItem = _libraryManager.GetItemById(item.Id); - - if (dbItem != null) - { - item = dbItem; - } - - // Force the save if it's a new item - await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); - } - } -} -- cgit v1.2.3