From 06967d540fd0ea3cbf293de6f6dff41daf1d0fe6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 11 Jun 2017 18:21:51 -0400 Subject: [PATCH 01/56] 3.2.20.1 --- SharedVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index 5425bf2a4d..851db9ca88 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.19.9")] +[assembly: AssemblyVersion("3.2.20.1")] From ceed0241fd1db8b417e4148f2599368779ba8b14 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 12 Jun 2017 02:11:19 -0400 Subject: [PATCH 02/56] fixes #2578 - [VAAPI Bug] HEVC Decoding not possible on Intel hardware without this ability --- .../MediaEncoding/EncodingHelper.cs | 23 +++++++++++++------ .../Configuration/EncodingOptions.cs | 4 ++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c348ffda72..c14b1f416b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -42,6 +42,11 @@ namespace MediaBrowser.Controller.MediaEncoding { var hwType = encodingOptions.HardwareAccelerationType; + if (!encodingOptions.EnableHardwareEncoding) + { + hwType = null; + } + if (string.Equals(hwType, "qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(hwType, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { @@ -1761,14 +1766,11 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec)) + if (state.VideoStream != null && + !string.IsNullOrWhiteSpace(state.VideoStream.Codec) && + !string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType) && + encodingOptions.EnableHardwareDecoding) { - if (!string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType)) - { - // causing unpredictable results - //return "-hwaccel auto"; - } - if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { switch (state.MediaSource.VideoStream.Codec.ToLower()) @@ -1818,6 +1820,13 @@ namespace MediaBrowser.Controller.MediaEncoding return "-c:v h264_cuvid "; } break; + case "hevc": + case "h265": + if (_mediaEncoder.SupportsDecoder("hevc_cuvid")) + { + return "-c:v hevc_cuvid "; + } + break; } } } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 9a0b2d35b9..e1b0514e7a 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -13,6 +13,8 @@ namespace MediaBrowser.Model.Configuration public string VaapiDevice { get; set; } public int H264Crf { get; set; } public string H264Preset { get; set; } + public bool EnableHardwareDecoding { get; set; } + public bool EnableHardwareEncoding { get; set; } public EncodingOptions() { @@ -22,6 +24,8 @@ namespace MediaBrowser.Model.Configuration EncodingThreadCount = -1; VaapiDevice = "/dev/dri/card0"; H264Crf = 23; + EnableHardwareDecoding = true; + EnableHardwareEncoding = true; } } } From 099c422a853bd2266e54fa6bd1f06f224d749dae Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 12 Jun 2017 03:02:09 -0400 Subject: [PATCH 03/56] fixes #1255 - [BUG] NFO handler doesn't handle NFOs with multiple episode details --- .../Parsers/BaseNfoParser.cs | 4 +- .../Parsers/EpisodeNfoParser.cs | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index d424cc0ca1..3b642eca2d 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -107,7 +107,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// The metadata file. /// The settings. /// The cancellation token. - private void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken) + protected virtual void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken) { if (!SupportsUrlAfterClosingXmlTag) { @@ -233,7 +233,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers get { return "themoviedb.org/movie/"; } } - private void ParseProviderLinks(T item, string xml) + protected void ParseProviderLinks(T item, string xml) { //Look for a match for the Regex pattern "tt" followed by 7 digits Match m = Regex.Match(xml, @"tt([0-9]{7})", RegexOptions.IgnoreCase); diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index d10a55e07c..953b59f463 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -9,6 +9,8 @@ using System.Threading; using System.Xml; using MediaBrowser.Model.IO; using MediaBrowser.Model.Xml; +using System.IO; +using System.Text; namespace MediaBrowser.XbmcMetadata.Parsers { @@ -24,6 +26,65 @@ namespace MediaBrowser.XbmcMetadata.Parsers private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + protected override void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken) + { + using (var fileStream = FileSystem.OpenRead(metadataFile)) + { + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) + { + item.ResetPeople(); + + var xml = streamReader.ReadToEnd(); + + var srch = ""; + var index = xml.IndexOf(srch, StringComparison.OrdinalIgnoreCase); + + if (index != -1) + { + xml = xml.Substring(0, index + srch.Length); + } + + using (var ms = new MemoryStream()) + { + var bytes = Encoding.UTF8.GetBytes(xml); + + ms.Write(bytes, 0, bytes.Length); + ms.Position = 0; + + // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions + try + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create(ms, settings)) + { + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(reader, item); + } + else + { + reader.Read(); + } + } + } + } + catch (XmlException) + { + + } + } + } + } + } + /// /// Fetches the data from XML node. /// From 9f0e6d17c8653328316cde3a1b0b3ce63b5960ff Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 12 Jun 2017 03:11:54 -0400 Subject: [PATCH 04/56] add hevc cuvid --- Emby.Server.Implementations/Dto/DtoService.cs | 12 +----- .../LiveTv/Listings/SchedulesDirect.cs | 2 +- .../LiveTv/LiveTvManager.cs | 17 +++++---- .../Session/SessionManager.cs | 1 - MediaBrowser.Controller/Entities/Folder.cs | 27 ------------- MediaBrowser.Controller/Entities/TV/Season.cs | 11 ------ MediaBrowser.Controller/Entities/TV/Series.cs | 11 ------ .../LiveTv/LiveTvProgram.cs | 7 +++- .../Encoder/EncoderValidator.cs | 3 +- MediaBrowser.Model/Querying/ItemFields.cs | 5 --- .../MediaBrowser.Providers.csproj | 1 - .../Movies/LiveTvMovieDbProvider.cs | 38 ------------------- .../Movies/MovieDbImageProvider.cs | 7 ---- .../Omdb/OmdbImageProvider.cs | 7 ---- .../Omdb/OmdbItemProvider.cs | 21 +--------- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 17 files changed, 24 insertions(+), 152 deletions(-) delete mode 100644 MediaBrowser.Providers/Movies/LiveTvMovieDbProvider.cs 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); diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 48c9b83aa2..727b7dbebe 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -211,33 +211,6 @@ namespace MediaBrowser.Controller.Entities item.SetParent(null); } - /// - /// Returns the valid set of index by options for this folder type. - /// Override or extend to modify. - /// - /// Dictionary{System.StringFunc{UserIEnumerable{BaseItem}}}. - protected virtual IEnumerable GetIndexByOptions() - { - return new List { - {"None"}, - {"Performer"}, - {"Genre"}, - {"Director"}, - {"Year"}, - {"Studio"} - }; - } - - /// - /// Get the list of indexy by choices for this folder (localized). - /// - /// The index by option strings. - [IgnoreDataMember] - public IEnumerable IndexByOptionStrings - { - get { return GetIndexByOptions(); } - } - /// /// Gets the actual children. /// diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 17494eb087..b681fdcb10 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -66,17 +66,6 @@ namespace MediaBrowser.Controller.Entities.TV return series == null ? SeriesName : series.SortName; } - // Genre, Rating and Stuido will all be the same - protected override IEnumerable GetIndexByOptions() - { - return new List { - {"None"}, - {"Performer"}, - {"Director"}, - {"Year"}, - }; - } - public override List GetUserDataKeys() { var list = base.GetUserDataKeys(); diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index c54f93f11f..0b96624505 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -227,17 +227,6 @@ namespace MediaBrowser.Controller.Entities.TV return list; } - // Studio, Genre and Rating will all be the same so makes no sense to index by these - protected override IEnumerable GetIndexByOptions() - { - return new List { - {"None"}, - {"Performer"}, - {"Director"}, - {"Year"}, - }; - } - [IgnoreDataMember] public bool ContainsEpisodesWithoutSeasonFolders { diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 665640dfd1..87fc581307 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -57,8 +57,13 @@ namespace MediaBrowser.Controller.LiveTv return value; } + else + { + double value = 2; + value /= 3; - return null; + return value; + } } [IgnoreDataMember] diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 43e463902d..338f58c167 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -91,7 +91,8 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_qsv", "mpeg2_qsv", "vc1_qsv", - "h264_cuvid" + "h264_cuvid", + "hevc_cuvid" }; foreach (var codec in required) diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 85d0a0bafa..5873293f72 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -87,11 +87,6 @@ /// HomePageUrl, - /// - /// The fields that the server supports indexing on - /// - IndexOptions, - /// /// The item counts /// diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index d16ae91617..65791ed102 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -72,7 +72,6 @@ - diff --git a/MediaBrowser.Providers/Movies/LiveTvMovieDbProvider.cs b/MediaBrowser.Providers/Movies/LiveTvMovieDbProvider.cs deleted file mode 100644 index a2d35b5004..0000000000 --- a/MediaBrowser.Providers/Movies/LiveTvMovieDbProvider.cs +++ /dev/null @@ -1,38 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Providers; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Movies -{ - public class LiveTvMovieDbProvider : IRemoteMetadataProvider, IHasOrder - { - public Task> GetSearchResults(LiveTvProgramLookupInfo searchInfo, CancellationToken cancellationToken) - { - return MovieDbProvider.Current.GetMovieSearchResults(searchInfo, cancellationToken); - } - - public Task> GetMetadata(LiveTvProgramLookupInfo info, CancellationToken cancellationToken) - { - return MovieDbProvider.Current.GetItemMetadata(info, cancellationToken); - } - - public string Name - { - get { return "LiveTvMovieDbProvider"; } - } - - public Task GetImageResponse(string url, CancellationToken cancellationToken) - { - return MovieDbProvider.Current.GetImageResponse(url, cancellationToken); - } - - public int Order - { - get { return 1; } - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs index 59b4b5198b..215b391817 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs @@ -43,13 +43,6 @@ namespace MediaBrowser.Providers.Movies public bool Supports(IHasImages item) { - // Supports images for tv movies - var tvProgram = item as LiveTvProgram; - if (tvProgram != null && tvProgram.IsMovie) - { - return true; - } - return item is Movie || item is MusicVideo || item is Trailer; } diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs index 04d2184722..f8b3ba1551 100644 --- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs @@ -93,13 +93,6 @@ namespace MediaBrowser.Providers.Omdb public bool Supports(IHasImages item) { - // Supports images for tv movies - var tvProgram = item as LiveTvProgram; - if (tvProgram != null && tvProgram.IsMovie) - { - return true; - } - return item is Movie || item is Trailer || item is Episode; } diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs index 02cb3dbf34..e769c8cab2 100644 --- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs @@ -22,7 +22,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Omdb { public class OmdbItemProvider : IRemoteMetadataProvider, - IRemoteMetadataProvider, IRemoteMetadataProvider, IRemoteMetadataProvider + IRemoteMetadataProvider, IRemoteMetadataProvider { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; @@ -51,16 +51,6 @@ namespace MediaBrowser.Providers.Omdb return GetSearchResults(searchInfo, "movie", cancellationToken); } - public Task> GetSearchResults(LiveTvProgramLookupInfo searchInfo, CancellationToken cancellationToken) - { - if (!searchInfo.IsMovie) - { - return Task.FromResult>(new List()); - } - - return GetSearchResults(searchInfo, "movie", cancellationToken); - } - public Task> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken) { return GetSearchResultsInternal(searchInfo, type, true, cancellationToken); @@ -230,15 +220,6 @@ namespace MediaBrowser.Providers.Omdb return result; } - public Task> GetMetadata(LiveTvProgramLookupInfo info, CancellationToken cancellationToken) - { - if (!info.IsMovie) - { - return Task.FromResult(new MetadataResult()); - } - return GetMovieResult(info, cancellationToken); - } - public Task> GetMetadata(MovieInfo info, CancellationToken cancellationToken) { return GetMovieResult(info, cancellationToken); diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 90663b500a..ff58c13ac9 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.703 + 3.0.704 Emby.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index e5cb120d33..a5707a2654 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.703 + 3.0.704 Emby.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - + From b5aca50c9e56580fd843f1e3baba32f9cf0231f1 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 12 Jun 2017 03:12:24 -0400 Subject: [PATCH 05/56] 3.2.20.2 --- SharedVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index 851db9ca88..ca38f525e4 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.1")] +[assembly: AssemblyVersion("3.2.20.2")] From 1663b258a71a164046007831e09ddd5f389b28a4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 13 Jun 2017 02:33:29 -0400 Subject: [PATCH 06/56] 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 - .../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 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; From b615a2aeb16a527fe56a88a352574d76e77783e8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 13 Jun 2017 02:33:49 -0400 Subject: [PATCH 07/56] 3.2.20.3 --- SharedVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index ca38f525e4..f362517731 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.2")] +[assembly: AssemblyVersion("3.2.20.3")] From ccb5b14d772c2d1028fa9003ee64fde3cd4196b3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 15 Jun 2017 13:22:05 -0400 Subject: [PATCH 08/56] 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 +++++++++ .../Net/HttpListenerResponse.cs | 696 ++++++------------ .../Net/HttpRequestStream.Managed.cs | 32 +- .../Net/HttpResponseStream.Managed.cs | 35 +- .../Net/HttpStatusDescription.cs | 75 ++ SocketHttpListener/Net/WebHeaderEncoding.cs | 131 ++++ SocketHttpListener/SocketHttpListener.csproj | 6 + 22 files changed, 906 insertions(+), 643 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 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"); + InitNamingOptions(options); - //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"); _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,149 +1,128 @@ -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; + 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(); - 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) + public WebHeaderCollection Headers { - this.context = context; - _logger = logger; - _textEncoding = textEncoding; - _fileSystem = fileSystem; + get => _webHeaders; } - internal bool CloseConnection - { - get - { - return headers["Connection"] == "close"; - } - } + public Encoding ContentEncoding { get; set; } - public bool ForceCloseChunked + public string ContentType { - get { return false; } - } - - public Encoding ContentEncoding - { - get - { - if (content_encoding == null) - content_encoding = _textEncoding.GetDefaultEncoding(); - return content_encoding; - } + get => Headers["Content-Type"]; set { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - content_encoding = value; + CheckDisposed(); + if (string.IsNullOrEmpty(value)) + { + Headers.Remove("Content-Type"); + } + else + { + Headers.Set("Content-Type", value); + } } } + private HttpListenerContext HttpListenerContext => _httpContext; + + private HttpListenerRequest HttpListenerRequest => HttpListenerContext.Request; + + internal EntitySendFormat EntitySendFormat + { + get => (EntitySendFormat)_boundaryType; + set + { + 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 bool SendChunked + { + get => EntitySendFormat == EntitySendFormat.Chunked; + set => EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength; + } + + // 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) + { + for (int i = 0; i < s_noResponseBody.Length; i++) + { + if (responseCode == s_noResponseBody[i]) + { + return false; + } + } + return true; + } + public long ContentLength64 { - get { return content_length; } + get => _contentLength; 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; + CheckDisposed(); + CheckSentHeaders(); + if (value >= 0) + { + _contentLength = value; + _boundaryType = BoundaryType.ContentLength; + } + else + { + throw new ArgumentOutOfRangeException("net_clsmall"); + } } } - public string ContentType - { - get { return content_type; } - set - { - // TODO: is null ok? - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - 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? - } - - 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; - } + get => _cookies ?? (_cookies = new CookieCollection()); + set => _cookies = value; } public bool KeepAlive { - get { return keep_alive; } + get => _keepAlive; set { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - keep_alive = value; + CheckDisposed(); + _keepAlive = value; } } @@ -151,422 +130,173 @@ namespace SocketHttpListener.Net { get { - if (output_stream == null) - output_stream = context.Connection.GetResponseStream(); - return output_stream; - } - } - - public Version ProtocolVersion - { - get { return version; } - set - { - 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; + CheckDisposed(); + EnsureResponseStream(); + return _responseStream; } } public string RedirectLocation { - get { return location; } + get => Headers["Location"]; set { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - location = value; + // 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); + } } } - public bool SendChunked - { - get { return chunked; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - chunked = value; - } - } - - public int StatusCode - { - get { return status_code; } - 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); - } - } - - internal static string GetStatusDescription(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 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 ""; - } - public string StatusDescription { - get { return status_description; } + get + { + if (_statusDescription == null) + { + // 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); + } + if (_statusDescription == null) + { + _statusDescription = string.Empty; + } + return _statusDescription; + } set { - status_description = value; + CheckDisposed(); + if (value == null) + { + 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) + { + throw new ArgumentException("net_WebHeaderInvalidControlChars"); + } + } + + _statusDescription = 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"); + Headers.Set(name, value); + } - 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 AppendHeader(string name, string value) + { + Headers.Add(name, value); } public void AppendCookie(Cookie cookie) { if (cookie == null) - throw new ArgumentNullException("cookie"); - + { + throw new ArgumentNullException(nameof(cookie)); + } Cookies.Add(cookie); } - public void AppendHeader(string name, string value) + private void ComputeCookies() { - 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) + if (_cookies != null) { - _logger.Debug("HttpListenerResponse force closing HttpConnection"); + // 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); + // } + //} } - disposed = true; - context.Connection.Close(force); - } - - public void Close(byte[] responseEntity, bool willBlock) - { - //CheckDisposed(); - - if (responseEntity == null) - { - throw new ArgumentNullException(nameof(responseEntity)); - } - - //if (_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 Close() - { - if (disposed) - return; - - Close(false); } public void Redirect(string url) { - StatusCode = 302; // Found - location = url; - } - - bool FindCookie(Cookie cookie) - { - string name = cookie.Name; - string domain = cookie.Domain; - string path = cookie.Path; - foreach (Cookie c in cookies) - { - if (name != c.Name) - continue; - if (domain != c.Domain) - continue; - if (path == c.Path) - return true; - } - - return false; - } - - public void DetermineIfChunked() - { - if (chunked) - { - 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; - } - } - - internal void SendHeaders(bool closing, MemoryStream ms) - { - 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)); - } - - 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) - { - headers.SetInternal("Connection", "close"); - conn_close = true; - } - - if (chunked) - headers.SetInternal("Transfer-Encoding", "chunked"); - - //int reuses = context.Connection.Reuses; - //if (reuses >= 100) - //{ - // _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; - // } - //} - - 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)); - - using (StreamWriter writer = new StreamWriter(ms, encoding, 256, true)) - { - writer.Write("HTTP/{0} {1} {2}\r\n", version, status_code, status_description); - string headers_str = headers.ToStringMultiValue(); - writer.Write(headers_str); - writer.Flush(); - } - - 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; + Headers["Location"] = url; + StatusCode = (int)HttpStatusCode.Redirect; + StatusDescription = "Found"; } public void SetCookie(Cookie cookie) { if (cookie == null) - throw new ArgumentNullException("cookie"); - - if (cookies != null) { - if (FindCookie(cookie)) - throw new ArgumentException("The cookie already exists."); - } - else - { - cookies = new CookieCollection(); + throw new ArgumentNullException(nameof(cookie)); } - cookies.Add(cookie); + //Cookie newCookie = cookie.Clone(); + //int added = Cookies.InternalAdd(newCookie, true); + + //if (added != 1) + //{ + // // The Cookie already existed and couldn't be replaced. + // throw new ArgumentException("Cookie exists"); + //} } - public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) + void IDisposable.Dispose() => Dispose(); + + private void CheckDisposed() { - return ((HttpResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken); + if (Disposed) + { + throw new ObjectDisposedException(GetType().FullName); + } + } + + private void CheckSentHeaders() + { + if (SentHeaders) + { + throw new InvalidOperationException(); + } } } -} \ 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; - } - - //lock (_response._headersLock) + //// SendHeaders works on shared headers + //lock (_response.headers_lock) //{ - // if (_response.SentHeaders) - // { + // if (_response.HeadersSent) // return null; - // } - - // MemoryStream ms = new MemoryStream(); - // _response.SendHeaders(closing, ms, isWebSocketHandshake); + // var ms = _memoryStreamFactory.CreateNew(); + // _response.SendHeaders(closing, ms); // return ms; //} + + // SendHeaders works on shared headers + 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 @@ + + + + + From 07b5c6a750a118405a840277dd0280f25c911924 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 15 Jun 2017 13:26:48 -0400 Subject: [PATCH 09/56] update logging --- Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 { From a4c25b4bb89d74d739f7317871b27716484ee229 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 15 Jun 2017 13:34:05 -0400 Subject: [PATCH 10/56] add message --- MediaBrowser.Server.Mono/ImageEncoderHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs index 92eaf418be..02593fb674 100644 --- a/MediaBrowser.Server.Mono/ImageEncoderHelper.cs +++ b/MediaBrowser.Server.Mono/ImageEncoderHelper.cs @@ -27,9 +27,9 @@ namespace MediaBrowser.Server.Startup.Common { return new SkiaEncoder(logManager.GetLogger("Skia"), appPaths, httpClient, fileSystem); } - catch + catch (Exception ex) { - logger.Error("Error loading Skia. Will revert to ImageMagick."); + logger.Error("Error loading Skia: {0}. Will revert to ImageMagick.", ex.Message); } try From 7b6f07b2602e33fe68969d78ac2f4ac76dafbc9c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 15 Jun 2017 13:40:38 -0400 Subject: [PATCH 11/56] update methods --- .../Net/HttpListenerResponse.Managed.cs | 9 ++++-- .../Net/HttpListenerResponse.cs | 29 ++++++++++--------- .../Net/HttpRequestStream.Managed.cs | 3 +- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/SocketHttpListener/Net/HttpListenerResponse.Managed.cs b/SocketHttpListener/Net/HttpListenerResponse.Managed.cs index 52576fdf2f..9adf0d435d 100644 --- a/SocketHttpListener/Net/HttpListenerResponse.Managed.cs +++ b/SocketHttpListener/Net/HttpListenerResponse.Managed.cs @@ -40,7 +40,7 @@ namespace SocketHttpListener.Net public Version ProtocolVersion { - get => _version; + get { return _version; } set { CheckDisposed(); @@ -59,7 +59,7 @@ namespace SocketHttpListener.Net public int StatusCode { - get => _statusCode; + get { return _statusCode; } set { CheckDisposed(); @@ -71,7 +71,10 @@ namespace SocketHttpListener.Net } } - private void Dispose() => Close(true); + private void Dispose() + { + Close(true); + } public void Close() { diff --git a/SocketHttpListener/Net/HttpListenerResponse.cs b/SocketHttpListener/Net/HttpListenerResponse.cs index 240c67930c..fc57e84857 100644 --- a/SocketHttpListener/Net/HttpListenerResponse.cs +++ b/SocketHttpListener/Net/HttpListenerResponse.cs @@ -25,14 +25,14 @@ namespace SocketHttpListener.Net public WebHeaderCollection Headers { - get => _webHeaders; + get { return _webHeaders; } } public Encoding ContentEncoding { get; set; } public string ContentType { - get => Headers["Content-Type"]; + get { return Headers["Content-Type"]; } set { CheckDisposed(); @@ -47,13 +47,13 @@ namespace SocketHttpListener.Net } } - private HttpListenerContext HttpListenerContext => _httpContext; + private HttpListenerContext HttpListenerContext { get { return _httpContext; } } - private HttpListenerRequest HttpListenerRequest => HttpListenerContext.Request; + private HttpListenerRequest HttpListenerRequest { get { return HttpListenerContext.Request; } } internal EntitySendFormat EntitySendFormat { - get => (EntitySendFormat)_boundaryType; + get { return (EntitySendFormat)_boundaryType; } set { CheckDisposed(); @@ -72,8 +72,8 @@ namespace SocketHttpListener.Net public bool SendChunked { - get => EntitySendFormat == EntitySendFormat.Chunked; - set => EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength; + get { return EntitySendFormat == EntitySendFormat.Chunked; ; } + set { EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength; } } // We MUST NOT send message-body when we send responses with these Status codes @@ -93,7 +93,7 @@ namespace SocketHttpListener.Net public long ContentLength64 { - get => _contentLength; + get { return _contentLength; } set { CheckDisposed(); @@ -112,13 +112,13 @@ namespace SocketHttpListener.Net public CookieCollection Cookies { - get => _cookies ?? (_cookies = new CookieCollection()); - set => _cookies = value; + get { return _cookies ?? (_cookies = new CookieCollection()); } + set { _cookies = value; } } public bool KeepAlive { - get => _keepAlive; + get { return _keepAlive; } set { CheckDisposed(); @@ -138,7 +138,7 @@ namespace SocketHttpListener.Net public string RedirectLocation { - get => Headers["Location"]; + get { return Headers["Location"]; } set { // note that this doesn't set the status code to a redirect one @@ -281,7 +281,10 @@ namespace SocketHttpListener.Net //} } - void IDisposable.Dispose() => Dispose(); + void IDisposable.Dispose() + { + Dispose(); + } private void CheckDisposed() { diff --git a/SocketHttpListener/Net/HttpRequestStream.Managed.cs b/SocketHttpListener/Net/HttpRequestStream.Managed.cs index 2b5dfc8382..92f4bbb02d 100644 --- a/SocketHttpListener/Net/HttpRequestStream.Managed.cs +++ b/SocketHttpListener/Net/HttpRequestStream.Managed.cs @@ -165,7 +165,8 @@ namespace SocketHttpListener.Net if (asyncResult == null) throw new ArgumentNullException(nameof(asyncResult)); - if (asyncResult is HttpStreamAsyncResult r) + var r = asyncResult as HttpStreamAsyncResult; + if (r != null) { if (!ReferenceEquals(this, r._parent)) { From c9d7eb9b0482ab26c2d288c460cd5fba3c993e7a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 15 Jun 2017 13:43:37 -0400 Subject: [PATCH 12/56] update project --- SocketHttpListener/SocketHttpListener.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/SocketHttpListener/SocketHttpListener.csproj b/SocketHttpListener/SocketHttpListener.csproj index 9fb7c50619..9cdc953c51 100644 --- a/SocketHttpListener/SocketHttpListener.csproj +++ b/SocketHttpListener/SocketHttpListener.csproj @@ -30,6 +30,7 @@ TRACE prompt 4 + true From 0e7cbb04651bd9b65668ca1635a4625640639560 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 17 Jun 2017 18:59:17 -0400 Subject: [PATCH 13/56] add subtitle language detection --- .../Emby.Common.Implementations.csproj | 72 ++++ .../IO/SharpCifs/Util/Sharpen/Extensions.cs | 18 - .../TextEncoding/NLangDetect/Detector.cs | 371 +++++++++++++++++ .../NLangDetect/DetectorFactory.cs | 127 ++++++ .../TextEncoding/NLangDetect/ErrorCode.cs | 15 + .../NLangDetect/Extensions/CharExtensions.cs | 374 ++++++++++++++++++ .../Extensions/RandomExtensions.cs | 51 +++ .../Extensions/StringExtensions.cs | 25 ++ .../NLangDetect/Extensions/UnicodeBlock.cs | 131 ++++++ .../TextEncoding/NLangDetect/GenProfile.cs | 67 ++++ .../NLangDetect/InternalException.cs | 22 ++ .../TextEncoding/NLangDetect/Language.cs | 45 +++ .../NLangDetect/LanguageDetector.cs | 37 ++ .../NLangDetect/NLangDetectException.cs | 23 ++ .../TextEncoding/NLangDetect/ProbVector.cs | 35 ++ .../TextEncoding/NLangDetect/Profiles/afr | 1 + .../TextEncoding/NLangDetect/Profiles/ara | 1 + .../TextEncoding/NLangDetect/Profiles/ben | 1 + .../TextEncoding/NLangDetect/Profiles/bul | 1 + .../TextEncoding/NLangDetect/Profiles/ces | 1 + .../TextEncoding/NLangDetect/Profiles/dan | 1 + .../TextEncoding/NLangDetect/Profiles/deu | 1 + .../TextEncoding/NLangDetect/Profiles/ell | 1 + .../TextEncoding/NLangDetect/Profiles/eng | 1 + .../TextEncoding/NLangDetect/Profiles/est | 1 + .../TextEncoding/NLangDetect/Profiles/fas | 1 + .../TextEncoding/NLangDetect/Profiles/fin | 1 + .../TextEncoding/NLangDetect/Profiles/fra | 1 + .../TextEncoding/NLangDetect/Profiles/guj | 1 + .../TextEncoding/NLangDetect/Profiles/heb | 1 + .../TextEncoding/NLangDetect/Profiles/hin | 1 + .../TextEncoding/NLangDetect/Profiles/hrv | 1 + .../TextEncoding/NLangDetect/Profiles/hun | 1 + .../TextEncoding/NLangDetect/Profiles/ind | 1 + .../TextEncoding/NLangDetect/Profiles/ita | 1 + .../TextEncoding/NLangDetect/Profiles/jpn | 1 + .../TextEncoding/NLangDetect/Profiles/kan | 1 + .../TextEncoding/NLangDetect/Profiles/kor | 1 + .../TextEncoding/NLangDetect/Profiles/lav | 1 + .../TextEncoding/NLangDetect/Profiles/lit | 1 + .../TextEncoding/NLangDetect/Profiles/mal | 1 + .../TextEncoding/NLangDetect/Profiles/mar | 1 + .../TextEncoding/NLangDetect/Profiles/mkd | 1 + .../TextEncoding/NLangDetect/Profiles/nep | 1 + .../TextEncoding/NLangDetect/Profiles/nld | 1 + .../TextEncoding/NLangDetect/Profiles/nor | 1 + .../TextEncoding/NLangDetect/Profiles/pan | 1 + .../TextEncoding/NLangDetect/Profiles/pol | 1 + .../TextEncoding/NLangDetect/Profiles/por | 1 + .../TextEncoding/NLangDetect/Profiles/ron | 1 + .../TextEncoding/NLangDetect/Profiles/rus | 1 + .../TextEncoding/NLangDetect/Profiles/slk | 1 + .../TextEncoding/NLangDetect/Profiles/slv | 1 + .../TextEncoding/NLangDetect/Profiles/som | 1 + .../TextEncoding/NLangDetect/Profiles/spa | 1 + .../TextEncoding/NLangDetect/Profiles/sqi | 1 + .../TextEncoding/NLangDetect/Profiles/swa | 1 + .../TextEncoding/NLangDetect/Profiles/swe | 1 + .../TextEncoding/NLangDetect/Profiles/tam | 1 + .../TextEncoding/NLangDetect/Profiles/tel | 1 + .../TextEncoding/NLangDetect/Profiles/tgl | 1 + .../TextEncoding/NLangDetect/Profiles/tha | 1 + .../TextEncoding/NLangDetect/Profiles/tur | 1 + .../TextEncoding/NLangDetect/Profiles/ukr | 1 + .../TextEncoding/NLangDetect/Profiles/urd | 1 + .../TextEncoding/NLangDetect/Profiles/vie | 1 + .../TextEncoding/NLangDetect/Profiles/zh-cn | 1 + .../TextEncoding/NLangDetect/Profiles/zh-tw | 1 + .../NLangDetect/Utils/LangProfile.cs | 118 ++++++ .../NLangDetect/Utils/Messages.cs | 91 +++++ .../TextEncoding/NLangDetect/Utils/NGram.cs | 330 ++++++++++++++++ .../NLangDetect/Utils/TagExtractor.cs | 76 ++++ .../NLangDetect/Utils/messages.properties | 128 ++++++ .../TextEncoding/TextEncoding.cs | 63 ++- Emby.Server.Core/ApplicationHost.cs | 2 +- .../UserLibrary/BaseItemsRequest.cs | 8 +- 76 files changed, 2256 insertions(+), 26 deletions(-) create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Detector.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/DetectorFactory.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/ErrorCode.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Extensions/CharExtensions.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Extensions/RandomExtensions.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Extensions/StringExtensions.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Extensions/UnicodeBlock.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/GenProfile.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/InternalException.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Language.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/LanguageDetector.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/NLangDetectException.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/ProbVector.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/afr create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/ara create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/ben create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/bul create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/ces create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/dan create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/deu create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/ell create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/eng create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/est create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/fas create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/fin create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/fra create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/guj create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/heb create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/hin create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/hrv create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/hun create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/ind create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/ita create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/jpn create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/kan create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/kor create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/lav create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/lit create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/mal create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/mar create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/mkd create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/nep create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/nld create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/nor create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/pan create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/pol create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/por create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/ron create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/rus create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/slk create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/slv create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/som create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/spa create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/sqi create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/swa create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/swe create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/tam create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/tel create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/tgl create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/tha create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/tur create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/ukr create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/urd create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/vie create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/zh-cn create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Profiles/zh-tw create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Utils/LangProfile.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Utils/Messages.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Utils/NGram.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Utils/TagExtractor.cs create mode 100644 Emby.Common.Implementations/TextEncoding/NLangDetect/Utils/messages.properties diff --git a/Emby.Common.Implementations/Emby.Common.Implementations.csproj b/Emby.Common.Implementations/Emby.Common.Implementations.csproj index 50d0d05ec4..6efc37603d 100644 --- a/Emby.Common.Implementations/Emby.Common.Implementations.csproj +++ b/Emby.Common.Implementations/Emby.Common.Implementations.csproj @@ -317,6 +317,23 @@ + + + + + + + + + + + + + + + + + @@ -368,7 +385,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - 17, 17 - - - 130, 17 - - - - AAABAAgAMDAAAAEAIACoJQAAhgAAADAwAAABAAgAqA4AAC4mAAAgIAAAAQAgAKgQAADWNAAAICAAAAEA - CACoCAAAfkUAABgYAAABACAAiAkAACZOAAAYGAAAAQAIAMgGAACuVwAAEBAAAAEAIABoBAAAdl4AABAQ - AAABAAgAaAUAAN5iAAAoAAAAMAAAAGAAAAABACAAAAAAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu2UkpItlMxAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7RQM0u1Uv9LtVL/S7NSJQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKtFIfS7VS/0u1 - Uv9LtVL/S7VS/0i2SAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEuz - Uz1LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9ItkgHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAS7NRTku1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7NSJQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABLs1FOS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0i2 - UzEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEuzUz1LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv/OtgAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASrRSH0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/xsYACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtFAzS7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqqAAMAAAAAAAAAAEu2UkpLtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv/atgAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv/MuwAPTLVSUEu1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/0rwAFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/9K8ABcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/atgAOAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/8Dkwf9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////bsBk/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////////9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////// - /////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//// - ///////////////////////////////////d6sP/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/////////////////////////////////////////////////0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////////////////////////////////////// - ////////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////////////////////// - //////////////////////////////////9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////// - ////////////////////////////////////////////////////////0uOr/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAA/wABS7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//// - ////////////////////////////////////////////////////////////////////////V7lZ/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/////////////////////////////////////////////////////////////////7Dd - rP9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////////////////////////////////////// - ////////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9ItlMxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////////////////////// - /////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0uzUiUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//////////////////// - //////////////////9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/SLZIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAD/AAFLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//// - ////////////////////////+fff/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9ItkgHAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS//////////////////////9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/9S4ABIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/1L8AGAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/1q6Xf9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv/OtgAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/8bGAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/atgAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/9K8ABcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtFHOAAAAAEu1Uv9LtVL/0rwAFwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0q0UdQAAAAAAAAAAAAAAADatgAOAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/SrVS7AAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0q0 - UuIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/SrVS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9KtVLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0q0UuIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9KtVLsAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEq0UdQAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD///+f//8AAP///w///wAA///+B/// - AAD///wD//8AAP//+AH//wAA///wAP//AAD//+AAf/8AAP//wAA//wAA//+AAB//AAD/8wAAD/8AAP/g - AAAH/wAA/8AAAAP/AAD/gAAAA/8AAP8AAAAH/wAA/gAAAAP/AAD8AAAAAf8AAPgAAAAA/wAA8AAAAAB/ - AADgAAAAAD8AAMAAAAAAHwAAgAAAAAAPAACAAAAAAAcAAMAAAAAAAwAA4AAAAAABAADwAAAAAAAAAPgA - AAAAAQAA/AAAAAADAAD+AAAAAAcAAP8AAAAADwAA/4AAAAAfAAD/wAAAAD8AAP/gAAAAfwAA/+AAAAD/ - AAD/wAAAAf8AAP/AAAAD/wAA/+AAAAf/AAD/8AAAT/8AAP/4AAD//wAA//wAAf//AAD//gAD//8AAP// - AAf//wAA//+AD///AAD//8Af//8AAP//4D///wAA///wf///AAD///j///8AAP///f///wAAKAAAADAA - AABgAAAAAQAIAAAAAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAP9vtzz/cLc8/1+2Rv9RtU//S7VS/0y1 - Uf9NtVH/TrVQ/0+1UP9NtlP/TbZU/062VP9Rt1f/VLla/1W5W/9WuVz/Y7ZD/2C2Rf9htkX/YrZE/2O2 - RP9otkD/Yb5m/2S/aP9mv2j/aMBq/3jHef95x3z/e8h8/3zIfP9+yX//f8qD/4PMiP+R0ZL/ktGS/5TS - lP+V0pX/mdSY/6jaqP+s3Kv/r92t/7Ddrv/G5sL/xufF/8jnxv/L6cj/3fDb/9/x3f/g8d3/4/Pg/+/4 - 7f/x+e//8vnv//r8+P/7/fn//v/+/////v8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAABQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUFAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUFBQUFBQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQUFBQUFBQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAUFBQUFBQUFBQUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUF - BQUFBQUFBQUFBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQUFBQUFBQUFBQUFBQMA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUFBQUFBQUFBQUFBQYDAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAUFBQUFBQUFBQUFBQUFBQUGAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAUEgAABQUFBQUFBQUFBQUFBQUFBQUFBgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUHBhIFBQUFBQUF - BQUFBQUFBQUFBQUFBQYDAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQcFBQYFBQUFBQUFBQUFBQUFBQUFBQUF - BQUGAwAAAAAAAAAAAAAAAAAAAAAAAAAVBwUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBhIAAAAAAAAA - AAAAAAAAAAAAABUHBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCBUAAAAAAAAAAAAAAAAAAAAAFQcF - BQUFBQUFBQUGBQUFBQUFBQUFBQUFBQUFBQUFEwAAAAAAAAAAAAAAAAAAAAAVBwUFBQUFBQUFBQUgKg8F - BQUFBQUFBQUFBQUFBQUFBQUAAAAAAAAAAAAAAAAAABUHBQUFBQUFBQUFBQUhOTUkDAUFBQUFBQUFBQUF - BQUFBQUFAAAAAAAAAAAAAAAAFQcFBQUFBQUFBQUFBQUhOTk5MR4FBQUFBQUFBQUFBQUFBQUFBQAAAAAA - AAAAAAASBwUFBQUFBQUFBQUFBQUhOTk5OTgsGAUFBQUFBQUFBQUFBQUFBQUAAAAAAAAAAAUFBQUFBQUF - BQUFBQUFBQUhOTk5OTk5NygOBQUFBQUFBQUFBQUFBQUFAAAAAAAABQUFBQUFBQUFBQUFBQUFBQUhOTk5 - OTk5OTkzIwsFBQUFBQUFBQUFBQUFBQAAAAAFBQUFBQUFBQUFBQUFBQUFBQUhOTk5OTk5OTk5OS8bBQUF - BQUFBQUFBQUFBQUAAAAFBQUFBQUFBQUFBQUFBQUFBQUhOTk5OTk5OTk5OTk4KxcFBQUFBQUFBQUFBQUF - AAAABQUFBQUFBQUFBQUFBQUFBQUhOTk5OTk5OTk5OTk5OTYnDQUFBQUFBQUFBQUFBQAAAAUFBQUFBQUF - BQUFBQUFBQUhOTk5OTk5OTk5OTk5OTMiCwUFBQUFBQUFBQUFBQUAAAAFBQUFBQUFBQUFBQUFBQUhOTk5 - OTk5OTk5OTk2KQ4FBQUFBQUFBQUFBQUFBQUAAAAABQUFBQUFBQUFBQUFBQUhOTk5OTk5OTk5OS0ZBQUF - BQUFBQUFBQUFBQUFBQUAAAAAAAUFBQUFBQUFBQUFBQUhOTk5OTk5OTkwHQUFBQUFBQUFBQUFBQUFBQUF - BQAAAAAAAAAFBQUFBQUFBQUFBQUhOTk5OTk5NCUKBQUFBQUFBQUFBQUFBQUFBQUFAAAAAAAAAAAABQUF - BQUFBQUFBQUhOTk5OTcpEAUFBQUFBQUFBQUFBQUFBQUFBQUAAAAAAAAAAAAAAAUFBQUFBQUFBQUhOTk5 - LhoFBQUFBQUFBQUFBQUFBQUFBQUGAwAAAAAAAAAAAAAAAAAFBQUFBQUFBQUhOTIfBQUFBQUFBQUFBQUF - BQUFBQUFBQYDAAAAAAAAAAAAAAAAAAAABQUFBQUFBQUcJgsFBQUFBQUFBQUFBQUFBQUFBQUFBgMAAAAA - AAAAAAAAAAAAAAAAFgcFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUGAwAAAAAAAAAAAAAAAAAAAAAW - CQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQYDAAAAAAAAAAAAAAAAAAAAAAARBwUFBQUFBQUFBQUF - BQUFBQUFBQUFBQUFBQUFBgMAAAAAAAAAAAAAAAAAAAAAAAAAFQcFBQUFBQUFBQUFBQUFBQUFBQUFBQUH - BQUGAwAAAAAAAAAAAAAAAAAAAAAAAAAAABUHBQUFBQUFBQUFBQUFBQUFBQUFBQUBBAYDAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAUBwUFBQUFBQUFBQUFBQUFBQUFBQUAAhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAFAcFBQUFBQUFBQUFBQUFBQUFBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQHBQUFBQUF - BQUFBQUFBQUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVBwUFBQUFBQUFBQUFBQUAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQcFBQUFBQUFBQUFBQAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAABQHBQUFBQUFBQUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAVBwUFBQUFBQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQcF - BQUFBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUHBQUFAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVBwUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAD///////8AAP///5///wAA////D///AAD///4H//8AAP///AP//wAA///4Af//AAD///AA//8AAP// - 4AB//wAA///AAD//AAD//4AAH/8AAP/zAAAP/wAA/+AAAAf/AAD/wAAAA/8AAP+AAAAD/wAA/wAAAAf/ - AAD+AAAAA/8AAPwAAAAB/wAA+AAAAAD/AADwAAAAAH8AAOAAAAAAPwAAwAAAAAAfAACAAAAAAA8AAIAA - AAAABwAAwAAAAAADAADgAAAAAAEAAPAAAAAAAAAA+AAAAAABAAD8AAAAAAMAAP4AAAAABwAA/wAAAAAP - AAD/gAAAAB8AAP/AAAAAPwAA/+AAAAB/AAD/4AAAAP8AAP/AAAAB/wAA/8AAAAP/AAD/4AAAB/8AAP/w - AABP/wAA//gAAP//AAD//AAB//8AAP/+AAP//wAA//8AB///AAD//4AP//8AAP//wB///wAA///gP/// - AAD///B///8AAP//+P///wAA///9////AAAoAAAAIAAAAEAAAAABACAAAAAAAAAgAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABLtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/AAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtFH+S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv/8/fz/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//z9/P///////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVH/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL//P38//////////////////////9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/8/fz///////////////////////// - ///q8dP/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//z9/P////////////// - ////////////////////////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL//P38//// - /////////////////////////////////////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv/8/fz/////////////////////////////////////////////////S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS//z9/P//////////////////////////////////////S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL//P38////////////////////////////mspu/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/8/fz//////////////////v7+/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS//z9/P///////////0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL//P38/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAEu1 - Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAEu0Uf5LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uf9LtVL/S7VS/0u1 - Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//v////x////4P///8B///+AP///AB//9gAP/+AAB//AAAP/gA - AH/wAAB/4AAAP8AAAB+AAAAPAAAAB4AAAAPAAAAB4AAAAPAAAAH4AAAD/AAAB/4AAA/+AAAf/AAAP/4A - AH//AAb//4AP///AH///4D////B////4/////f//KAAAACAAAABAAAAAAQAIAAAAAAAACAAAAAAAAAAA - AAAAAAAAAAAAAAAAAP95tzb/erc2/3u3Nf9wtzz/VrZL/1C1T/9RtU7/UrVO/1O1Tf9VtUz/VbZM/0u1 - Uv9NtVH/TrVQ/0+1UP9NtlT/ULdW/1a5W/9gtkb/Y7ZE/2i2QP9bu2D/Zb9p/2vCb/9vw3L/d8d6/33J - fv+NuCr/jrgq/4+4Kf+SuSf/k7kn/5C5Kf+SuSj/hcyG/4fNif+U0pX/l9OY/5bUmf+e15//n9eh/63d - rv+s3bD/uOG5/8jox//Q68//0+zS/97x3f/h8t//5vTl//D47//1+/T/+/36//3+/f//////AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAADAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAADAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwMDAYCAAAAAAAAAAAAAAAAAAAA - AAAAAAAMDAwMDAwMDAwMDAcBAAAAAAAAAAAAAAAAAAAAIAsTDAwMDAwMDAwMDAwMDAcDAAAAAAAAAAAA - AAAAACAJDA4MDAwMDAwMDAwMDAwMDAcDAAAAAAAAAAAAAAAdCQwMDAwMDAwMDAwMDAwMDAwMDAoAAAAA - AAAAAAAAHgkMDAwMDAwMDAwMDAwMDAwMDAwNFAAAAAAAAAAAACAJDAwMDAwMDCkmEAwMDAwMDAwMDAwM - AAAAAAAAAAAhCQwMDAwMDAwMKzcxGwwMDAwMDAwMDAwMAAAAAAAADAcMDAwMDAwMDAwrNzc3LRcMDAwM - DAwMDAwMAAAAAAwMDAwMDAwMDAwMDCs3Nzc3NSoSDAwMDAwMDAwMAAAADAwMDAwMDAwMDAwMKzc3Nzc3 - NzMlEAwMDAwMDAwMAAAMDAwMDAwMDAwMDAwrNzc3Nzc3NzcwGgwMDAwMDAwMAAAMDAwMDAwMDAwMDCs3 - Nzc3Nzc3Ny4YDAwMDAwMDAwMAAAMDAwMDAwMDAwMKzc3Nzc3NzIjDAwMDAwMDAwMDAwAAAAMDAwMDAwM - DAwrNzc3NzQoEQwMDAwMDAwMDAwMDAAAAAAMDAwMDAwMDCs3NzYsFgwMDAwMDAwMDAwMDAwAAAAAAAAM - DAwMDAwMKzcvGQwMDAwMDAwMDAwMDAcCAAAAAAAAAAAMDAwMDAwnJAwMDAwMDAwMDAwMDAwHAwAAAAAA - AAAAABUPDAwMDAwMDAwMDAwMDAwMDAwMBwIAAAAAAAAAAAAABQwMDAwMDAwMDAwMDAwMDAwMDAcBAAAA - AAAAAAAAAAAfCQwMDAwMDAwMDAwMDAwMDwwHAgAAAAAAAAAAAAAAAAAfCQwMDAwMDAwMDAwMDAwEBQIA - AAAAAAAAAAAAAAAAAAAcCQwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAeCQwMDAwMDAwMDAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAgCQwMDAwMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeCQwMDAwM - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdCQwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi - CAwAAAAAAAAAAAAAAAAAAAAA//+/////H////g////wH///4A///8AH//2AA//4AAH/8AAA/+AAAf/AA - AH/gAAA/wAAAH4AAAA8AAAAHgAAAA8AAAAHgAAAA8AAAAfgAAAP8AAAH/gAAD/4AAB/8AAA//gAAf/8A - Bv//gA///8Af///gP///8H////j////9//8oAAAAGAAAADAAAAABACAAAAAAAAASAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASrRSQUm1 - Ty0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJtU8tS7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEuz - UVFLtVL/S7VS/0u1Uv9LtVL/SbVPLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASbVPLUu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/8+/ - ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABKtFJBS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv/UqgAGAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/2S1QV5LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/1L8AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL///////////9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL//////////////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL///////////////////////// - ///29+b/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL//////////////////////////////////////0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL///////// - /////////////////////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL///////////////////////////+l1ZP/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9JtU8tAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1 - Uv9LtVL//////////////////////0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL///////////9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/0LkAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv/PvwAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/9SqAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u0 - UtJLtVL/1L8AGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/SrVR3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9KtVLyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u0Uc4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1 - Uv9LtVL/SrVS8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKtVHeAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP/z/wD/4f8A/8D/AP+AfwD9AD8A+AAfAPAA - HwDgAA8AwAAHAIAAAwCAAAEAwAAAAOAAAQDwAAMA+AAHAPwADwD4AB8A/AA/AP4A/wD/Af8A/4P/AP/H - /wD/7/8AKAAAABgAAAAwAAAAAQAIAAAAAACABAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9UtUz/VbZM/0u1 - Uv9MtVH/TbVR/061UP9PtVD/TLZT/062VP9SuFj/Vrlc/2W2Qv9htkX/Xr1j/2XAav9qwW7/ccR1/3LF - dv99yYD/iM6L/5PSlv+j2aX/r92w/73kvv/B5sP/yejJ/9ju1//h8uD/6vbp//H58P/3/Pb/+/36//7+ - /v//////AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAA - AAADAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAAAAAAAA - AAAAAAAAAAAAAAADAwMDAwMDAQAAAAAAAAAAAAAAAAANAAMDAwMDAwMDAwEAAAAAAAAAAAAAAAIEBgMD - AwMDAwMDAwMBAAAAAAAAAAAAAgQDAwMDAwMDAwMDAwMEDQAAAAAAAAACBAMDAwMRCQMDAwMDAwMFAwAA - AAAAAAIEAwMDAwMZHBMDAwMDAwMDAwMAAAAABwQDAwMDAwMZIiIaDwMDAwMDAwMDAAADAwMDAwMDAwMZ - IiIiIBcLAwMDAwMDAwADAwMDAwMDAwMZIiIiIiIeFQgDAwMDAwMAAwMDAwMDAwMZIiIiIiIdFAMDAwMD - AwMAAAMDAwMDAwMZIiIiHxYKAwMDAwMDAwMAAAADAwMDAwMZIiEYDgMDAwMDAwMDAwAAAAAAAwMDAwMZ - GxIDAwMDAwMDAwMBAAAAAAAAAAYDAwMQBAMDAwMDAwMDAwEAAAAAAAAADAUDAwMDAwMDAwMDAwMDAQAA - AAAAAAAAAAIEAwMDAwMDAwMDBQUBAAAAAAAAAAAAAAACBAMDAwMDAwMDAwwAAAAAAAAAAAAAAAAAAgQD - AwMDAwMDAAAAAAAAAAAAAAAAAAAAAAIEAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAACBAMDAwAAAAAAAAAA - AAAAAAAAAAAAAAAAAgQDAAAAAAAAAAAAAAD///8A//P/AP/h/wD/wP8A/4B/AP0APwD4AB8A8AAfAOAA - DwDAAAcAgAADAIAAAQDAAAAA4AABAPAAAwD4AAcA/AAPAPgAHwD8AD8A/gD/AP8B/wD/g/8A/8f/AP/v - /wAoAAAAEAAAACAAAAABACAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABLtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv//////S7VS/0u1Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAA - AAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/////////////////S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv8AAAAAAAAAAEu1Uv9LtVL/S7VS/0u1Uv9LtVL/S7VS////////////////////////////S7VS/0u1 - Uv9LtVL/S7VS/wAAAAAAAAAAS7VS/0u1Uv9LtVL/S7VS/0u1Uv//////////////////////sN2s/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/////////////////S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS//////9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAABLtVL/S7VS/0u1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEu1Uv9LtVL/S7VS/0u1 - Uv9LtVL/S7VS/0u1Uv9LtVL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS7VS/0u1 - Uv9LtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABLtVL/S7VS/0u1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAEu1Uv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9/AAD+PwAA/B8AAPAP - AADgBwAAwAcAAIADAAAAAQAAgAAAAMABAADgAwAA4AcAAPAPAAD4PwAA/H8AAP7/AAAoAAAAEAAAACAA - AAABAAgAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAA/0+1T/9QtU//VLVM/1W2TP9LtVL/TLVR/021 - Uf9PtVD/U7hZ/1a5XP9hvmb/ZsBr/3bGev99yYH/qNuq/6/esf+y37T/uOK6/8Lmw//K6cr/1u7X//n8 - +f/7/fv////+/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAFBQUAAAAAAAAAAAAAAAAFBQUF - BQAAAAAAAAAAAAAFBQUFBQYDAAAAAAAAAAQBBQUFBQUFBgMAAAAAAAQGBQUFBQUFBQUIAAAAAAQGBQUF - Eg4FBQUFBQUAAAUGBQUFBRUYFAwFBQUFBQAFBQUFBQUVGBgXEAoFBQUFBQUFBQUFFRgYFg8JBQUFBQAF - BQUFBRUYEwsFBQUFBQUAAAUFBQURDQUFBQUFBgMAAAAAAgUFBQUFBQUFBgMAAAAAAAQGBQUFBQUFAQMA - AAAAAAAABAYFBQUFBQAAAAAAAAAAAAAEBgUFBQAAAAAAAAAAAAAAAAQHBQAAAAAAAAD/fwAA/j8AAPwf - AADwDwAA4AcAAMAHAACAAwAAAAEAAIAAAADAAQAA4AMAAOAHAADwDwAA+D8AAPx/AAD+/wAA - - - \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index a0494b3e5c..8cd8138fdd 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -53,31 +53,10 @@ namespace MediaBrowser.ServerApplication private static IFileSystem FileSystem; - public static bool TryGetLocalFromUncDirectory(string local, out string unc) - { - if ((local == null) || (local == "")) - { - unc = ""; - throw new ArgumentNullException("local"); - } - - ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Name FROM Win32_share WHERE path ='" + local.Replace("\\", "\\\\") + "'"); - ManagementObjectCollection coll = searcher.Get(); - if (coll.Count == 1) - { - foreach (ManagementObject share in searcher.Get()) - { - unc = share["Name"] as String; - unc = "\\\\" + SystemInformation.ComputerName + "\\" + unc; - return true; - } - } - unc = ""; - return false; - } /// /// Defines the entry point of the application. /// + [STAThread] public static void Main() { var options = new StartupOptions(Environment.GetCommandLineArgs()); @@ -321,8 +300,6 @@ namespace MediaBrowser.ServerApplication } } - private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource(); - /// /// Runs the application. /// @@ -394,9 +371,6 @@ namespace MediaBrowser.ServerApplication HideSplashScreen(); ShowTrayIcon(); - - task = ApplicationTaskCompletionSource.Task; - Task.WaitAll(task); } } @@ -487,7 +461,6 @@ namespace MediaBrowser.ServerApplication /// The instance containing the event data. static void service_Disposed(object sender, EventArgs e) { - ApplicationTaskCompletionSource.SetResult(true); OnServiceShutdown(); } @@ -706,14 +679,10 @@ namespace MediaBrowser.ServerApplication _serverNotifyIcon = null; } - //_logger.Info("Calling Application.Exit"); + _logger.Info("Calling Application.Exit"); //Application.Exit(); - - _logger.Info("Calling Environment.Exit"); + Environment.Exit(0); - - _logger.Info("Calling ApplicationTaskCompletionSource.SetResult"); - ApplicationTaskCompletionSource.SetResult(true); } private static void ShutdownWindowsService() diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 1c82a3c8d9..19f4921d3b 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -130,12 +130,6 @@ Component
- - Form - - - MainForm.cs - @@ -167,9 +161,6 @@ - - MainForm.cs - ResXFileCodeGenerator Resources.Designer.cs @@ -197,7 +188,7 @@ PreserveNewest - + PreserveNewest diff --git a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs index c421dd9ebd..cc8656f23d 100644 --- a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs +++ b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs @@ -45,7 +45,6 @@ namespace MediaBrowser.ServerApplication components = new System.ComponentModel.Container(); - var resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); contextMenuStrip1 = new ContextMenuStrip(components); notifyIcon1 = new NotifyIcon(components); @@ -62,7 +61,7 @@ namespace MediaBrowser.ServerApplication // notifyIcon1 // notifyIcon1.ContextMenuStrip = contextMenuStrip1; - notifyIcon1.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + notifyIcon1.Icon = new System.Drawing.Icon(GetType().Assembly.GetManifestResourceStream(GetType().Namespace + ".Icon.ico")); notifyIcon1.Text = "Emby"; notifyIcon1.Visible = true; // From f57dfc6a619efac5d902c1ab8cfd8cbfc38f8e94 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 24 Jun 2017 14:31:13 -0400 Subject: [PATCH 35/56] specify tiff mime types --- MediaBrowser.Model/Dto/UserDto.cs | 10 ---------- MediaBrowser.Model/Net/MimeTypes.cs | 1 + 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs index 37a5541666..99f69e2030 100644 --- a/MediaBrowser.Model/Dto/UserDto.cs +++ b/MediaBrowser.Model/Dto/UserDto.cs @@ -122,16 +122,6 @@ namespace MediaBrowser.Model.Dto /// The primary image aspect ratio. public double? PrimaryImageAspectRatio { get; set; } - /// - /// Gets a value indicating whether this instance has primary image. - /// - /// true if this instance has primary image; otherwise, false. - [IgnoreDataMember] - public bool HasPrimaryImage - { - get { return PrimaryImageTag != null; } - } - /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 3c3c2bbc78..2f132cb373 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -72,6 +72,7 @@ namespace MediaBrowser.Model.Net dict.Add(".tbn", "image/jpeg"); dict.Add(".png", "image/png"); dict.Add(".gif", "image/gif"); + dict.Add(".tiff", "image/tiff"); dict.Add(".webp", "image/webp"); dict.Add(".ico", "image/vnd.microsoft.icon"); dict.Add(".mpg", "video/mpeg"); From 6b2445aa2c224b9d03e6a1d295e43ee6363a5d5f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 24 Jun 2017 14:32:22 -0400 Subject: [PATCH 36/56] add stream loop option for m3u --- .../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(-) 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() { From 8dcfda89d110109a7fd283983ad9cac88d2da07a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 24 Jun 2017 14:32:57 -0400 Subject: [PATCH 37/56] update image size responses --- MediaBrowser.Api/Images/ImageService.cs | 2 +- MediaBrowser.Controller/Drawing/IImageProcessor.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 882967a53c..221755916b 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -326,7 +326,7 @@ namespace MediaBrowser.Api.Images var fileInfo = _fileSystem.GetFileInfo(info.Path); length = fileInfo.Length; - var size = _imageProcessor.GetImageSize(info); + var size = _imageProcessor.GetImageSize(info, true); width = Convert.ToInt32(size.Width); height = Convert.ToInt32(size.Height); diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index d98638d556..2832462a8d 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -33,6 +33,8 @@ namespace MediaBrowser.Controller.Drawing /// ImageSize. ImageSize GetImageSize(ItemImageInfo info); + ImageSize GetImageSize(ItemImageInfo info, bool allowSlowMethods); + /// /// Gets the size of the image. /// From a107ff0369e5cade3dc2848f190a95b5be302a0b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 24 Jun 2017 14:33:19 -0400 Subject: [PATCH 38/56] capture more transcoding info --- MediaBrowser.Api/ApiEntryPoint.cs | 3 +- .../Playback/BaseStreamingService.cs | 4 + .../Playback/Hls/DynamicHlsService.cs | 5 + MediaBrowser.Api/Playback/MediaInfoService.cs | 2 + .../Playback/UniversalAudioService.cs | 7 +- MediaBrowser.Controller/Entities/Folder.cs | 8 +- .../MediaEncoding/EncodingJobInfo.cs | 22 +- .../MediaEncoding/EncodingJobOptions.cs | 2 + .../Configuration/UserConfiguration.cs | 1 - MediaBrowser.Model/Dlna/ProfileCondition.cs | 1 - MediaBrowser.Model/Dlna/StreamBuilder.cs | 271 ++++++++++++++++-- MediaBrowser.Model/Dlna/StreamInfo.cs | 20 +- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 4 + MediaBrowser.Model/Session/TranscodingInfo.cs | 33 +++ 14 files changed, 338 insertions(+), 45 deletions(-) diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index e4abe96f3a..34d0bd413a 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -265,7 +265,8 @@ namespace MediaBrowser.Api Height = state.OutputHeight, AudioChannels = state.OutputAudioChannels, IsAudioDirect = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase), - IsVideoDirect = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) + IsVideoDirect = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase), + TranscodeReasons = state.TranscodeReasons }); } } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index bbee361995..e4ef294d17 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -583,6 +583,10 @@ namespace MediaBrowser.Api.Playback videoRequest.DeInterlace = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } } + else if (i == 33) + { + request.TranscodeReasons = val; + } } } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index ddd2d8cd29..c6282bbad5 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -541,6 +541,11 @@ namespace MediaBrowser.Api.Playback.Hls { queryString += "&SegmentContainer=" + state.Request.SegmentContainer; } + // from universal audio service + if (!string.IsNullOrWhiteSpace(state.Request.TranscodeReasons) && queryString.IndexOf("TranscodeReasons=", StringComparison.OrdinalIgnoreCase) == -1) + { + queryString += "&TranscodeReasons=" + state.Request.TranscodeReasons; + } // Main stream var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8"; diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index bba8094b64..6853ddbebf 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -513,6 +513,8 @@ namespace MediaBrowser.Api.Playback var profiles = info.GetSubtitleProfiles(false, "-", accessToken); mediaSource.DefaultSubtitleStreamIndex = info.SubtitleStreamIndex; + mediaSource.TranscodeReasons = info.TranscodeReasons; + foreach (var profile in profiles) { foreach (var stream in mediaSource.MediaStreams) diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index fffec69489..b9bcd106e1 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Threading.Tasks; using MediaBrowser.Api.Playback.Hls; using MediaBrowser.Api.Playback.Progressive; @@ -265,7 +266,8 @@ namespace MediaBrowser.Api.Playback Static = isStatic, SegmentContainer = request.TranscodingContainer, AudioSampleRate = request.MaxAudioSampleRate, - BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames + BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames, + TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()) }; if (isHeadRequest) @@ -307,7 +309,8 @@ namespace MediaBrowser.Api.Playback PlaySessionId = playbackInfoResult.PlaySessionId, StartTimeTicks = request.StartTimeTicks, Static = isStatic, - AudioSampleRate = request.MaxAudioSampleRate + AudioSampleRate = request.MaxAudioSampleRate, + TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()) }; if (isHeadRequest) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 34b33fde0d..ea442ba1ec 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -487,7 +487,7 @@ namespace MediaBrowser.Controller.Entities var folder = this; innerProgress.RegisterAction(p => { - double newPct = .70 * p + 10; + double newPct = .80 * p + 10; progress.Report(newPct); ProviderManager.OnRefreshProgress(folder, newPct); }); @@ -498,11 +498,11 @@ namespace MediaBrowser.Controller.Entities if (refreshChildMetadata) { - progress.Report(80); + progress.Report(90); if (recursive) { - ProviderManager.OnRefreshProgress(this, 80); + ProviderManager.OnRefreshProgress(this, 90); } var container = this as IMetadataContainer; @@ -512,7 +512,7 @@ namespace MediaBrowser.Controller.Entities var folder = this; innerProgress.RegisterAction(p => { - double newPct = .20 * p + 80; + double newPct = .10 * p + 90; progress.Report(newPct); if (recursive) { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 57c81ddf76..a83a6a15ea 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; @@ -9,6 +10,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Session; namespace MediaBrowser.Controller.MediaEncoding { @@ -40,6 +42,24 @@ namespace MediaBrowser.Controller.MediaEncoding public bool ReadInputAtNativeFramerate { get; set; } + private List _transcodeReasons = null; + public List TranscodeReasons + { + get + { + if (_transcodeReasons == null) + { + _transcodeReasons = (BaseRequest.TranscodeReasons ?? string.Empty) + .Split(',') + .Where(i => !string.IsNullOrWhiteSpace(i)) + .Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true)) + .ToList(); + } + + return _transcodeReasons; + } + } + public bool IgnoreInputDts { get @@ -251,7 +271,7 @@ namespace MediaBrowser.Controller.MediaEncoding { return AudioStream.SampleRate; } - } + } else if (BaseRequest.AudioSampleRate.HasValue) { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index 632c350ad0..28ef665669 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -204,6 +204,8 @@ namespace MediaBrowser.Controller.MediaEncoding public string SubtitleCodec { get; set; } + public string TranscodeReasons { get; set; } + /// /// Gets or sets the index of the audio stream. /// diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 5567063fe3..30b5f384fb 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -55,7 +55,6 @@ namespace MediaBrowser.Model.Configuration HidePlayedInLatest = true; PlayDefaultAudioTrack = true; - DisplayMissingEpisodes = true; LatestItemsExcludes = new string[] { }; OrderedViews = new string[] { }; diff --git a/MediaBrowser.Model/Dlna/ProfileCondition.cs b/MediaBrowser.Model/Dlna/ProfileCondition.cs index 3d104f9c41..9234a27136 100644 --- a/MediaBrowser.Model/Dlna/ProfileCondition.cs +++ b/MediaBrowser.Model/Dlna/ProfileCondition.cs @@ -1,5 +1,4 @@ using System.Xml.Serialization; -using MediaBrowser.Model.Dlna; namespace MediaBrowser.Model.Dlna { diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 0ecc413f24..b758d5ed53 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -105,8 +105,99 @@ namespace MediaBrowser.Model.Dlna return null; } + private TranscodeReason? GetTranscodeReasonForFailedCondition(ProfileCondition condition) + { + switch (condition.Property) + { + case ProfileConditionValue.AudioBitrate: + if (condition.Condition == ProfileConditionType.LessThanEqual) + { + return TranscodeReason.AudioBitrateNotSupported; + } + return TranscodeReason.AudioBitrateNotSupported; + + case ProfileConditionValue.AudioChannels: + if (condition.Condition == ProfileConditionType.LessThanEqual) + { + return TranscodeReason.AudioChannelsNotSupported; + } + return TranscodeReason.AudioChannelsNotSupported; + + case ProfileConditionValue.AudioProfile: + return TranscodeReason.AudioProfileNotSupported; + + case ProfileConditionValue.AudioSampleRate: + return TranscodeReason.AudioSampleRateNotSupported; + + case ProfileConditionValue.Has64BitOffsets: + // TODO + return null; + + case ProfileConditionValue.Height: + return TranscodeReason.VideoResolutionNotSupported; + + case ProfileConditionValue.IsAnamorphic: + return TranscodeReason.AnamorphicVideoNotSupported; + + case ProfileConditionValue.IsAvc: + // TODO + return null; + + case ProfileConditionValue.IsInterlaced: + return TranscodeReason.InterlacedVideoNotSupported; + + case ProfileConditionValue.IsSecondaryAudio: + return TranscodeReason.SecondaryAudioNotSupported; + + case ProfileConditionValue.NumAudioStreams: + // TODO + return null; + + case ProfileConditionValue.NumVideoStreams: + // TODO + return null; + + case ProfileConditionValue.PacketLength: + // TODO + return null; + + case ProfileConditionValue.RefFrames: + return TranscodeReason.RefFramesNotSupported; + + case ProfileConditionValue.VideoBitDepth: + return TranscodeReason.VideoBitDepthNotSupported; + + case ProfileConditionValue.VideoBitrate: + return TranscodeReason.VideoBitrateNotSupported; + + case ProfileConditionValue.VideoCodecTag: + return TranscodeReason.VideoCodecNotSupported; + + case ProfileConditionValue.VideoFramerate: + return TranscodeReason.VideoFramerateNotSupported; + + case ProfileConditionValue.VideoLevel: + return TranscodeReason.VideoLevelNotSupported; + + case ProfileConditionValue.VideoProfile: + return TranscodeReason.VideoProfileNotSupported; + + case ProfileConditionValue.VideoTimestamp: + // TODO + return null; + + case ProfileConditionValue.Width: + return TranscodeReason.VideoResolutionNotSupported; + + default: + return null; + } + } + private StreamInfo BuildAudioItem(MediaSourceInfo item, AudioOptions options) { + List transcodeReasons = new List(); + StreamInfo playlistItem = new StreamInfo { ItemId = options.ItemId, @@ -133,7 +224,10 @@ namespace MediaBrowser.Model.Dlna MediaStream audioStream = item.GetDefaultAudioStream(null); - List directPlayMethods = GetAudioDirectPlayMethods(item, audioStream, options); + var directPlayInfo = GetAudioDirectPlayMethods(item, audioStream, options); + + List directPlayMethods = directPlayInfo.Item1; + transcodeReasons.AddRange(directPlayInfo.Item2); ConditionProcessor conditionProcessor = new ConditionProcessor(); @@ -180,6 +274,11 @@ namespace MediaBrowser.Model.Dlna if (!conditionProcessor.IsAudioConditionSatisfied(c, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate)) { LogConditionFailure(options.Profile, "AudioCodecProfile", c, item); + var transcodeReason = GetTranscodeReasonForFailedCondition(c); + if (transcodeReason.HasValue) + { + transcodeReasons.Add(transcodeReason.Value); + } all = false; break; } @@ -292,9 +391,9 @@ namespace MediaBrowser.Model.Dlna var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate); playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate); - } + playlistItem.TranscodeReasons = transcodeReasons; return playlistItem; } @@ -308,8 +407,10 @@ namespace MediaBrowser.Model.Dlna return options.GetMaxBitrate(isAudio); } - private List GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options) + private Tuple, List> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options) { + List transcodeReasons = new List(); + DirectPlayProfile directPlayProfile = null; foreach (DirectPlayProfile i in options.Profile.DirectPlayProfiles) { @@ -325,27 +426,134 @@ namespace MediaBrowser.Model.Dlna if (directPlayProfile != null) { // While options takes the network and other factors into account. Only applies to direct stream - if (item.SupportsDirectStream && IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate(true), PlayMethod.DirectStream) && options.EnableDirectStream) + if (item.SupportsDirectStream) { - playMethods.Add(PlayMethod.DirectStream); + if (IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate(true), PlayMethod.DirectStream)) + { + if (options.EnableDirectStream) + { + playMethods.Add(PlayMethod.DirectStream); + } + } + else + { + transcodeReasons.Add(TranscodeReason.ContainerBitrateExceedsLimit); + } } // The profile describes what the device supports // If device requirements are satisfied then allow both direct stream and direct play - if (item.SupportsDirectPlay && - IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), PlayMethod.DirectPlay) && options.EnableDirectPlay) + if (item.SupportsDirectPlay) { - playMethods.Add(PlayMethod.DirectPlay); + if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true), PlayMethod.DirectPlay)) + { + if (options.EnableDirectPlay) + { + playMethods.Add(PlayMethod.DirectPlay); + } + } + else + { + transcodeReasons.Add(TranscodeReason.ContainerBitrateExceedsLimit); + } } } else { + transcodeReasons.InsertRange(0, GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles)); + _logger.Info("Profile: {0}, No direct play profiles found for Path: {1}", options.Profile.Name ?? "Unknown Profile", item.Path ?? "Unknown path"); } - return playMethods; + if (playMethods.Count > 0) + { + transcodeReasons.Clear(); + } + else + { + transcodeReasons = transcodeReasons.Distinct().ToList(); + } + + return new Tuple, List>(playMethods, transcodeReasons); + } + + private List GetTranscodeReasonsFromDirectPlayProfile(MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable directPlayProfiles) + { + var list = new List(); + var containerSupported = false; + var audioSupported = false; + var videoSupported = false; + + foreach (var profile in directPlayProfiles) + { + if (profile.Container.Length > 0) + { + // Check container type + string mediaContainer = item.Container ?? string.Empty; + foreach (string i in profile.GetContainers()) + { + if (StringHelper.EqualsIgnoreCase(i, mediaContainer)) + { + containerSupported = true; + + if (videoStream != null) + { + // Check video codec + List videoCodecs = profile.GetVideoCodecs(); + if (videoCodecs.Count > 0) + { + string videoCodec = videoStream.Codec; + if (!string.IsNullOrEmpty(videoCodec) && ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec)) + { + videoSupported = true; + } + } + else + { + videoSupported = true; + } + } + + if (audioStream != null) + { + // Check audio codec + List audioCodecs = profile.GetAudioCodecs(); + if (audioCodecs.Count > 0) + { + string audioCodec = audioStream.Codec; + if (!string.IsNullOrEmpty(audioCodec) && ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec)) + { + audioSupported = true; + } + } + else + { + audioSupported = true; + } + } + } + } + } + } + + if (!containerSupported) + { + list.Add(TranscodeReason.ContainerNotSupported); + } + + if (videoStream != null && !videoSupported) + { + list.Add(TranscodeReason.VideoCodecNotSupported); + } + + if (audioStream != null && !audioSupported) + { + list.Add(TranscodeReason.VideoCodecNotSupported); + } + + return list; } private int? GetDefaultSubtitleStreamIndex(MediaSourceInfo item, SubtitleProfile[] subtitleProfiles) @@ -393,6 +601,8 @@ namespace MediaBrowser.Model.Dlna private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { + List transcodeReasons = new List(); + StreamInfo playlistItem = new StreamInfo { ItemId = options.ItemId, @@ -428,7 +638,8 @@ namespace MediaBrowser.Model.Dlna if (isEligibleForDirectPlay || isEligibleForDirectStream) { // See if it can be direct played - PlayMethod? directPlay = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream); + var directPlayInfo = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream); + var directPlay = directPlayInfo.Item1; if (directPlay != null) { @@ -445,6 +656,8 @@ namespace MediaBrowser.Model.Dlna return playlistItem; } + + transcodeReasons.AddRange(directPlayInfo.Item2); } // Can't direct play, find the transcoding profile @@ -618,6 +831,8 @@ namespace MediaBrowser.Model.Dlna } } + playlistItem.TranscodeReasons = transcodeReasons; + return playlistItem; } @@ -677,7 +892,7 @@ namespace MediaBrowser.Model.Dlna return Math.Min(defaultBitrate, encoderAudioBitrateLimit); } - private PlayMethod? GetVideoDirectPlayProfile(VideoOptions options, + private Tuple> GetVideoDirectPlayProfile(VideoOptions options, MediaSourceInfo mediaSource, MediaStream videoStream, MediaStream audioStream, @@ -688,11 +903,11 @@ namespace MediaBrowser.Model.Dlna if (options.ForceDirectPlay) { - return PlayMethod.DirectPlay; + return new Tuple>(PlayMethod.DirectPlay, new List()); } if (options.ForceDirectStream) { - return PlayMethod.DirectStream; + return new Tuple>(PlayMethod.DirectStream, new List()); } if (videoStream == null) @@ -701,7 +916,7 @@ namespace MediaBrowser.Model.Dlna profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path"); - return null; + return new Tuple>(null, new List { TranscodeReason.UnknownVideoStreamInfo }); } // See if it can be direct played @@ -721,7 +936,7 @@ namespace MediaBrowser.Model.Dlna profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path"); - return null; + return new Tuple>(null, GetTranscodeReasonsFromDirectPlayProfile(mediaSource, videoStream, audioStream, profile.DirectPlayProfiles)); } string container = mediaSource.Container; @@ -784,7 +999,7 @@ namespace MediaBrowser.Model.Dlna profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path"); - return null; + return new Tuple>(null, new List { TranscodeReason.UnknownVideoStreamInfo }); } conditions = new List(); @@ -819,7 +1034,12 @@ namespace MediaBrowser.Model.Dlna { LogConditionFailure(profile, "VideoCodecProfile", i, mediaSource); - return null; + var transcodeReason = GetTranscodeReasonForFailedCondition(i); + var transcodeReasons = transcodeReason.HasValue + ? new List { transcodeReason.Value } + : new List { }; + + return new Tuple>(null, transcodeReasons); } } @@ -833,7 +1053,7 @@ namespace MediaBrowser.Model.Dlna profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path"); - return null; + return new Tuple>(null, new List { TranscodeReason.UnknownAudioStreamInfo }); } conditions = new List(); @@ -870,17 +1090,22 @@ namespace MediaBrowser.Model.Dlna { LogConditionFailure(profile, "VideoAudioCodecProfile", i, mediaSource); - return null; + var transcodeReason = GetTranscodeReasonForFailedCondition(i); + var transcodeReasons = transcodeReason.HasValue + ? new List { transcodeReason.Value } + : new List { }; + + return new Tuple>(null, transcodeReasons); } } } if (isEligibleForDirectStream && mediaSource.SupportsDirectStream) { - return PlayMethod.DirectStream; + return new Tuple>(PlayMethod.DirectStream, new List()); } - return null; + return new Tuple>(null, new List { TranscodeReason.ContainerBitrateExceedsLimit }); } private void LogConditionFailure(DeviceProfile profile, string type, ProfileCondition condition, MediaSourceInfo mediaSource) @@ -1047,12 +1272,12 @@ namespace MediaBrowser.Model.Dlna // Don't restrict by bitrate if coming from an external domain if (item.IsRemote) { - return true; + return true; } if (!maxBitrate.HasValue) { - _logger.Info("Cannot "+ playMethod + " due to unknown supported bitrate"); + _logger.Info("Cannot " + playMethod + " due to unknown supported bitrate"); return false; } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index ba5251e8c9..d70d89cf75 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -21,6 +21,7 @@ namespace MediaBrowser.Model.Dlna AudioCodecs = new string[] { }; VideoCodecs = new string[] { }; SubtitleCodecs = new string[] { }; + TranscodeReasons = new List(); } public string ItemId { get; set; } @@ -89,6 +90,7 @@ namespace MediaBrowser.Model.Dlna public string PlaySessionId { get; set; } public List AllMediaSources { get; set; } + public List TranscodeReasons { get; set; } public string MediaSourceId { @@ -231,22 +233,11 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty)); list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty)); - var forceStartPosition = false; long startPositionTicks = item.StartPositionTicks; - //if (item.MediaSource.DateLiveStreamOpened.HasValue && startPositionTicks == 0) - //{ - // var elapsed = DateTime.UtcNow - item.MediaSource.DateLiveStreamOpened.Value; - // elapsed -= TimeSpan.FromSeconds(20); - // if (elapsed.TotalSeconds >= 0) - // { - // startPositionTicks = elapsed.Ticks + startPositionTicks; - // forceStartPosition = true; - // } - //} var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"); - if (isHls && !forceStartPosition) + if (isHls) { list.Add(new NameValuePair("StartTimeTicks", string.Empty)); } @@ -310,6 +301,11 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString())); } + if (isDlna || !item.IsDirectStream) + { + list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()).ToArray()))); + } + return list; } diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index c93eca0e79..08824913f8 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -4,6 +4,7 @@ using MediaBrowser.Model.MediaInfo; using System.Collections.Generic; using System.Linq; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Session; namespace MediaBrowser.Model.Dto { @@ -110,6 +111,9 @@ namespace MediaBrowser.Model.Dto } } + [IgnoreDataMember] + public List TranscodeReasons { get; set; } + public int? DefaultAudioStreamIndex { get; set; } public int? DefaultSubtitleStreamIndex { get; set; } diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs index e646d80d35..67eac6fd57 100644 --- a/MediaBrowser.Model/Session/TranscodingInfo.cs +++ b/MediaBrowser.Model/Session/TranscodingInfo.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace MediaBrowser.Model.Session { public class TranscodingInfo @@ -15,5 +17,36 @@ namespace MediaBrowser.Model.Session public int? Width { get; set; } public int? Height { get; set; } public int? AudioChannels { get; set; } + + public List TranscodeReasons { get; set; } + + public TranscodingInfo() + { + TranscodeReasons = new List(); + } + } + + public enum TranscodeReason + { + ContainerNotSupported = 0, + VideoCodecNotSupported = 1, + AudioCodecNotSupported = 2, + ContainerBitrateExceedsLimit = 3, + AudioBitrateNotSupported = 4, + AudioChannelsNotSupported = 5, + VideoResolutionNotSupported = 6, + UnknownVideoStreamInfo = 7, + UnknownAudioStreamInfo = 8, + AudioProfileNotSupported = 9, + AudioSampleRateNotSupported = 10, + AnamorphicVideoNotSupported = 11, + InterlacedVideoNotSupported = 12, + SecondaryAudioNotSupported = 13, + RefFramesNotSupported = 14, + VideoBitDepthNotSupported = 15, + VideoBitrateNotSupported = 16, + VideoFramerateNotSupported = 17, + VideoLevelNotSupported = 18, + VideoProfileNotSupported = 19 } } \ No newline at end of file From 31a3e0f5c49804e9acceb7718542d1c04445425b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 24 Jun 2017 14:37:17 -0400 Subject: [PATCH 39/56] 3.2.20.12 --- SharedVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index ec801ba7fe..6aaf3af830 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.11")] +[assembly: AssemblyVersion("3.2.20.12")] From f2fb52761f527211dd738b453ce61df5886efa33 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 25 Jun 2017 13:50:17 -0400 Subject: [PATCH 40/56] update stream building --- .../LiveTv/LiveTvAudioRecording.cs | 15 ++++++++++++++- .../LiveTv/LiveTvVideoRecording.cs | 18 ++++++++++++++++++ MediaBrowser.Model/Dlna/StreamBuilder.cs | 2 +- .../Session/GeneralCommandType.cs | 3 ++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index 45786e7959..9f82344029 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs @@ -92,7 +92,20 @@ namespace MediaBrowser.Controller.LiveTv public override double? GetDefaultPrimaryImageAspectRatio() { - return null; + if (IsMovie) + { + double value = 2; + value /= 3; + + return value; + } + else + { + double value = 2; + value /= 3; + + return value; + } } public override string GetClientTypeName() diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index 2b527c4b09..c29d3dc478 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -108,6 +108,24 @@ namespace MediaBrowser.Controller.LiveTv return false; } + public override double? GetDefaultPrimaryImageAspectRatio() + { + if (IsMovie) + { + double value = 2; + value /= 3; + + return value; + } + else + { + double value = 2; + value /= 3; + + return value; + } + } + [IgnoreDataMember] public override bool SupportsLocalMetadata { diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index b758d5ed53..ebfeb358e7 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -550,7 +550,7 @@ namespace MediaBrowser.Model.Dlna if (audioStream != null && !audioSupported) { - list.Add(TranscodeReason.VideoCodecNotSupported); + list.Add(TranscodeReason.AudioCodecNotSupported); } return list; diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs index 1a96515599..8dd0c29e43 100644 --- a/MediaBrowser.Model/Session/GeneralCommandType.cs +++ b/MediaBrowser.Model/Session/GeneralCommandType.cs @@ -38,6 +38,7 @@ ChannelUp = 30, ChannelDown = 31, SetMaxStreamingBitrate = 31, - Guide = 32 + Guide = 32, + ToggleStats = 33 } } \ No newline at end of file From 31698e44246b5cae711fda529b13772cdf65bb8b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 25 Jun 2017 13:50:51 -0400 Subject: [PATCH 41/56] 3.2.20.13 --- SharedVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index 6aaf3af830..d6bc167e61 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.20.12")] +[assembly: AssemblyVersion("3.2.20.13")] From c2c1451bebd38f6af76dd01bb2c5ec78fabd88f5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 25 Jun 2017 19:04:15 -0400 Subject: [PATCH 42/56] update dlna profiles --- Emby.Dlna/DlnaManager.cs | 3 +- Emby.Dlna/Emby.Dlna.csproj | 4 ++ Emby.Dlna/Profiles/DenonAvrProfile.cs | 2 + Emby.Dlna/Profiles/MarantzProfile.cs | 42 ++++++++++++++++ Emby.Dlna/Profiles/MediaMonkeyProfile.cs | 2 +- Emby.Dlna/Profiles/Xml/Denon AVR.xml | 2 +- Emby.Dlna/Profiles/Xml/Marantz.xml | 62 ++++++++++++++++++++++++ 7 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 Emby.Dlna/Profiles/MarantzProfile.cs create mode 100644 Emby.Dlna/Profiles/Xml/Marantz.xml diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index b09dba70c3..82975ce22b 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -587,7 +587,8 @@ namespace Emby.Dlna new DirectTvProfile(), new DishHopperJoeyProfile(), new DefaultProfile(), - new PopcornHourProfile() + new PopcornHourProfile(), + new MarantzProfile() }; foreach (var item in list) diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index cae5a96362..365774b0d9 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -86,6 +86,7 @@ + @@ -177,6 +178,9 @@ + + +