From 54cf0da75826d641b28a34afece7a4cb0eaaaec2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 22 May 2017 00:54:02 -0400 Subject: [PATCH] update query fields --- .../Data/SqliteItemRepository.cs | 254 ++++++++++++------ Emby.Server.Implementations/Dto/DtoService.cs | 12 +- .../HttpServer/HttpListenerHost.cs | 8 +- .../HttpServer/IHttpListener.cs | 3 +- .../SocketSharp/WebSocketSharpListener.cs | 9 +- .../Library/LibraryManager.cs | 71 +---- .../LiveTv/EmbyTV/EmbyTV.cs | 3 +- .../LiveTv/LiveTvManager.cs | 2 +- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 16 +- .../HdHomerun/HdHomerunHttpStream.cs | 42 +-- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 15 +- .../HdHomerun/HdHomerunUdpStream.cs | 55 ++-- .../LiveTv/TunerHosts/M3UTunerHost.cs | 7 +- .../Services/ResponseHelper.cs | 16 +- .../Services/ServiceHandler.cs | 5 +- MediaBrowser.Api/BaseApiService.cs | 50 ++-- MediaBrowser.Api/Images/ImageService.cs | 5 +- MediaBrowser.Api/LiveTv/LiveTvService.cs | 9 +- MediaBrowser.Api/Music/InstantMixService.cs | 2 +- .../Playback/Progressive/AudioService.cs | 9 +- .../BaseProgressiveStreamingService.cs | 11 +- .../Progressive/ProgressiveStreamWriter.cs | 95 +++++-- .../Playback/Progressive/VideoService.cs | 3 +- .../Playback/UniversalAudioService.cs | 8 +- .../UserLibrary/ArtistsService.cs | 4 +- .../UserLibrary/BaseItemsByNameService.cs | 3 +- .../UserLibrary/GameGenresService.cs | 6 +- MediaBrowser.Api/UserLibrary/GenresService.cs | 6 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 2 +- .../UserLibrary/MusicGenresService.cs | 4 +- .../UserLibrary/PersonsService.cs | 4 +- .../UserLibrary/StudiosService.cs | 6 +- .../Entities/Audio/MusicAlbum.cs | 25 +- .../Entities/InternalItemsQuery.cs | 36 --- .../Library/ILibraryManager.cs | 14 +- MediaBrowser.Controller/LiveTv/LiveStream.cs | 141 +++++++++- MediaBrowser.Model/Querying/ItemFields.cs | 5 +- .../Api/DashboardService.cs | 10 +- .../Api/PackageCreator.cs | 8 +- SharedVersion.cs | 2 +- .../Net/ResponseStream.cs | 19 +- 41 files changed, 620 insertions(+), 385 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 49bf9e39c5..fbd97b6a23 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1415,66 +1415,69 @@ namespace Emby.Server.Implementations.Data var index = 5; - var hasProgramAttributes = item as IHasProgramAttributes; - if (hasProgramAttributes != null) + if (HasProgramAttributes(query)) { - if (!reader.IsDBNull(index)) + var hasProgramAttributes = item as IHasProgramAttributes; + if (hasProgramAttributes != null) { - hasProgramAttributes.IsMovie = reader.GetBoolean(index); - } - index++; + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.IsMovie = reader.GetBoolean(index); + } + index++; - if (!reader.IsDBNull(index)) - { - hasProgramAttributes.IsSports = reader.GetBoolean(index); - } - index++; + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.IsSports = reader.GetBoolean(index); + } + index++; - if (!reader.IsDBNull(index)) - { - hasProgramAttributes.IsKids = reader.GetBoolean(index); - } - index++; + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.IsKids = reader.GetBoolean(index); + } + index++; - if (!reader.IsDBNull(index)) - { - hasProgramAttributes.IsSeries = reader.GetBoolean(index); - } - index++; + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.IsSeries = reader.GetBoolean(index); + } + index++; - if (!reader.IsDBNull(index)) - { - hasProgramAttributes.IsLive = reader.GetBoolean(index); - } - index++; + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.IsLive = reader.GetBoolean(index); + } + index++; - if (!reader.IsDBNull(index)) - { - hasProgramAttributes.IsNews = reader.GetBoolean(index); - } - index++; + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.IsNews = reader.GetBoolean(index); + } + index++; - if (!reader.IsDBNull(index)) - { - hasProgramAttributes.IsPremiere = reader.GetBoolean(index); - } - index++; + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.IsPremiere = reader.GetBoolean(index); + } + index++; - if (!reader.IsDBNull(index)) - { - hasProgramAttributes.EpisodeTitle = reader.GetString(index); - } - index++; + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.EpisodeTitle = reader.GetString(index); + } + index++; - if (!reader.IsDBNull(index)) + if (!reader.IsDBNull(index)) + { + hasProgramAttributes.IsRepeat = reader.GetBoolean(index); + } + index++; + } + else { - hasProgramAttributes.IsRepeat = reader.GetBoolean(index); + index += 9; } - index++; - } - else - { - index += 9; } if (!reader.IsDBNull(index)) @@ -1483,7 +1486,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.CustomRating)) + if (HasField(query, ItemFields.CustomRating)) { if (!reader.IsDBNull(index)) { @@ -1498,7 +1501,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.Settings)) + if (HasField(query, ItemFields.Settings)) { if (!reader.IsDBNull(index)) { @@ -1525,7 +1528,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.ExternalEtag)) + if (HasField(query, ItemFields.ExternalEtag)) { if (!reader.IsDBNull(index)) { @@ -1534,11 +1537,14 @@ namespace Emby.Server.Implementations.Data index++; } - if (!reader.IsDBNull(index)) + if (HasField(query, ItemFields.DateLastRefreshed)) { - item.DateLastRefreshed = reader[index].ReadDateTime(); + if (!reader.IsDBNull(index)) + { + item.DateLastRefreshed = reader[index].ReadDateTime(); + } + index++; } - index++; if (!reader.IsDBNull(index)) { @@ -1558,7 +1564,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.Overview)) + if (HasField(query, ItemFields.Overview)) { if (!reader.IsDBNull(index)) { @@ -1585,7 +1591,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.HomePageUrl)) + if (HasField(query, ItemFields.HomePageUrl)) { if (!reader.IsDBNull(index)) { @@ -1594,7 +1600,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.DisplayMediaType)) + if (HasField(query, ItemFields.DisplayMediaType)) { if (!reader.IsDBNull(index)) { @@ -1603,7 +1609,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.SortName)) + if (HasField(query, ItemFields.SortName)) { if (!reader.IsDBNull(index)) { @@ -1618,7 +1624,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.VoteCount)) + if (HasField(query, ItemFields.VoteCount)) { if (!reader.IsDBNull(index)) { @@ -1627,7 +1633,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.DateCreated)) + if (HasField(query, ItemFields.DateCreated)) { if (!reader.IsDBNull(index)) { @@ -1645,7 +1651,7 @@ namespace Emby.Server.Implementations.Data item.Id = reader.GetGuid(index); index++; - if (query.HasField(ItemFields.Genres)) + if (HasField(query, ItemFields.Genres)) { if (!reader.IsDBNull(index)) { @@ -1680,13 +1686,16 @@ namespace Emby.Server.Implementations.Data } index++; - if (!reader.IsDBNull(index)) + if (HasField(query, ItemFields.DateLastSaved)) { - item.DateLastSaved = reader[index].ReadDateTime(); + if (!reader.IsDBNull(index)) + { + item.DateLastSaved = reader[index].ReadDateTime(); + } + index++; } - index++; - if (query.HasField(ItemFields.Settings)) + if (HasField(query, ItemFields.Settings)) { if (!reader.IsDBNull(index)) { @@ -1695,7 +1704,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.Studios)) + if (HasField(query, ItemFields.Studios)) { if (!reader.IsDBNull(index)) { @@ -1704,7 +1713,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.Tags)) + if (HasField(query, ItemFields.Tags)) { if (!reader.IsDBNull(index)) { @@ -1729,7 +1738,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.OriginalTitle)) + if (HasField(query, ItemFields.OriginalTitle)) { if (!reader.IsDBNull(index)) { @@ -1748,7 +1757,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.DateLastMediaAdded)) + if (HasField(query, ItemFields.DateLastMediaAdded)) { var folder = item as Folder; if (folder != null && !reader.IsDBNull(index)) @@ -1814,7 +1823,7 @@ namespace Emby.Server.Implementations.Data } index++; - if (query.HasField(ItemFields.PresentationUniqueKey)) + if (HasField(query, ItemFields.PresentationUniqueKey)) { if (!reader.IsDBNull(index)) { @@ -1823,7 +1832,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.InheritedParentalRatingValue)) + if (HasField(query, ItemFields.InheritedParentalRatingValue)) { if (!reader.IsDBNull(index)) { @@ -1832,7 +1841,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.Tags)) + if (HasField(query, ItemFields.Tags)) { if (!reader.IsDBNull(index)) { @@ -1841,7 +1850,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.ExternalSeriesId)) + if (HasField(query, ItemFields.ExternalSeriesId)) { if (!reader.IsDBNull(index)) { @@ -1850,7 +1859,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.Taglines)) + if (HasField(query, ItemFields.Taglines)) { if (!reader.IsDBNull(index)) { @@ -1859,7 +1868,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.Keywords)) + if (HasField(query, ItemFields.Keywords)) { if (!reader.IsDBNull(index)) { @@ -1883,7 +1892,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.ProductionLocations)) + if (HasField(query, ItemFields.ProductionLocations)) { if (!reader.IsDBNull(index)) { @@ -1892,7 +1901,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.ThemeSongIds)) + if (HasField(query, ItemFields.ThemeSongIds)) { if (!reader.IsDBNull(index)) { @@ -1901,7 +1910,7 @@ namespace Emby.Server.Implementations.Data index++; } - if (query.HasField(ItemFields.ThemeVideoIds)) + if (HasField(query, ItemFields.ThemeVideoIds)) { if (!reader.IsDBNull(index)) { @@ -1942,14 +1951,17 @@ namespace Emby.Server.Implementations.Data } index++; - if (hasSeries != null) + if (HasField(query, ItemFields.SeriesPresentationUniqueKey)) { - if (!reader.IsDBNull(index)) + if (hasSeries != null) { - hasSeries.SeriesPresentationUniqueKey = reader.GetString(index); + if (!reader.IsDBNull(index)) + { + hasSeries.SeriesPresentationUniqueKey = reader.GetString(index); + } } + index++; } - index++; return item; } @@ -2259,13 +2271,73 @@ namespace Emby.Server.Implementations.Data return new[] { field.ToString() }; } + private bool HasField(InternalItemsQuery query, ItemFields name) + { + var fields = query.DtoOptions.Fields; + + switch (name) + { + case ItemFields.HomePageUrl: + case ItemFields.Keywords: + case ItemFields.DisplayMediaType: + case ItemFields.VoteCount: + case ItemFields.CustomRating: + case ItemFields.ProductionLocations: + case ItemFields.Settings: + case ItemFields.OriginalTitle: + case ItemFields.Taglines: + case ItemFields.SortName: + case ItemFields.Studios: + case ItemFields.Tags: + case ItemFields.ThemeSongIds: + case ItemFields.ThemeVideoIds: + case ItemFields.DateCreated: + case ItemFields.Overview: + case ItemFields.Genres: + case ItemFields.DateLastMediaAdded: + case ItemFields.ExternalEtag: + case ItemFields.PresentationUniqueKey: + case ItemFields.InheritedParentalRatingValue: + case ItemFields.ExternalSeriesId: + case ItemFields.SeriesPresentationUniqueKey: + case ItemFields.DateLastRefreshed: + case ItemFields.DateLastSaved: + return fields.Contains(name); + case ItemFields.ServiceName: + return true; + default: + return true; + } + } + + private bool HasProgramAttributes(InternalItemsQuery query) + { + if (query.IncludeItemTypes.Length == 0) + { + return true; + } + + var types = new string[] + { + "Program", + "Recording", + "TvChannel", + "LiveTvAudioRecording", + "LiveTvVideoRecording", + "LiveTvProgram", + "LiveTvTvChannel" + }; + + return types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase)); + } + private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns) { var list = startColumns.ToList(); foreach (var field in allFields) { - if (!query.HasField(field)) + if (!HasField(query, field)) { foreach (var fieldToRemove in GetColumnNamesFromField(field).ToList()) { @@ -2274,6 +2346,19 @@ namespace Emby.Server.Implementations.Data } } + if (!HasProgramAttributes(query)) + { + list.Remove("IsKids"); + list.Remove("IsMovie"); + list.Remove("IsSports"); + list.Remove("IsSeries"); + list.Remove("IsLive"); + list.Remove("IsNews"); + list.Remove("IsPremiere"); + list.Remove("EpisodeTitle"); + list.Remove("IsRepeat"); + } + if (!query.DtoOptions.EnableImages) { list.Remove("Images"); @@ -2400,7 +2485,7 @@ namespace Emby.Server.Implementations.Data query.Limit = query.Limit.Value + 4; } - var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new [] { "count(distinct PresentationUniqueKey)" })) + GetFromText(); + var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" })) + GetFromText(); commandText += GetJoinUserDataText(query); var whereClauses = GetWhereClauses(query, null); @@ -3671,6 +3756,7 @@ namespace Emby.Server.Implementations.Data if (!string.IsNullOrWhiteSpace(query.SlugName)) { + Logger.Info("Searching by SlugName for {0}", query.SlugName); whereClauses.Add("CleanName=@SlugName"); if (statement != null) { diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index bb46e60063..6f2e437cf8 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1137,7 +1137,10 @@ namespace Emby.Server.Implementations.Dto return null; } - var artist = _libraryManager.GetArtist(i); + var artist = _libraryManager.GetArtist(i, new DtoOptions(false) + { + EnableImages = false + }); if (artist != null) { return new NameIdPair @@ -1186,7 +1189,10 @@ namespace Emby.Server.Implementations.Dto return null; } - var artist = _libraryManager.GetArtist(i); + var artist = _libraryManager.GetArtist(i, new DtoOptions(false) + { + EnableImages = false + }); if (artist != null) { return new NameIdPair @@ -1456,7 +1462,7 @@ namespace Emby.Server.Implementations.Dto var musicAlbum = item as MusicAlbum; if (musicAlbum != null) { - var artist = musicAlbum.MusicArtist; + var artist = musicAlbum.GetMusicArtist(new DtoOptions(false)); if (artist != null) { return artist; diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 5e96eda94a..79209d438b 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; +using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer.SocketSharp; @@ -445,10 +446,7 @@ namespace Emby.Server.Implementations.HttpServer /// /// Overridable method that can be used to implement a custom hnandler /// - /// The HTTP req. - /// The URL. - /// Task. - protected async Task RequestHandler(IHttpRequest httpReq, Uri url) + protected async Task RequestHandler(IHttpRequest httpReq, Uri url, CancellationToken cancellationToken) { var date = DateTime.Now; var httpRes = httpReq.Response; @@ -589,7 +587,7 @@ namespace Emby.Server.Implementations.HttpServer if (handler != null) { - await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName).ConfigureAwait(false); + await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName, cancellationToken).ConfigureAwait(false); } else { diff --git a/Emby.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs index 18df5682d7..82175dbed6 100644 --- a/Emby.Server.Implementations/HttpServer/IHttpListener.cs +++ b/Emby.Server.Implementations/HttpServer/IHttpListener.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Net; using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; @@ -18,7 +19,7 @@ namespace Emby.Server.Implementations.HttpServer /// Gets or sets the request handler. /// /// The request handler. - Func RequestHandler { get; set; } + Func RequestHandler { get; set; } /// /// Gets or sets the web socket handler. diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs index 682fa7a0b8..f085ff71e7 100644 --- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs @@ -4,6 +4,7 @@ using SocketHttpListener.Net; using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Model.Cryptography; @@ -50,7 +51,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp } public Action ErrorHandler { get; set; } - public Func RequestHandler { get; set; } + public Func RequestHandler { get; set; } public Action WebSocketConnecting { get; set; } @@ -82,10 +83,10 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp private void ProcessContext(HttpListenerContext context) { //Task.Factory.StartNew(() => InitTask(context), TaskCreationOptions.DenyChildAttach | TaskCreationOptions.PreferFairness); - Task.Run(() => InitTask(context)); + Task.Run(() => InitTask(context, CancellationToken.None)); } - private Task InitTask(HttpListenerContext context) + private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken) { IHttpRequest httpReq = null; var request = context.Request; @@ -111,7 +112,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp return Task.FromResult(true); } - return RequestHandler(httpReq, request.Url); + return RequestHandler(httpReq, request.Url, cancellationToken); } private void ProcessWebSocketRequest(HttpListenerContext ctx) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a423db9d6f..8907c911a1 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -885,7 +885,7 @@ namespace Emby.Server.Implementations.Library /// Task{Person}. public Person GetPerson(string name) { - return CreateItemByName(Person.GetPath, name); + return CreateItemByName(Person.GetPath, name, new DtoOptions(true)); } /// @@ -895,7 +895,7 @@ namespace Emby.Server.Implementations.Library /// Task{Studio}. public Studio GetStudio(string name) { - return CreateItemByName(Studio.GetPath, name); + return CreateItemByName(Studio.GetPath, name, new DtoOptions(true)); } public Guid GetStudioId(string name) @@ -925,7 +925,7 @@ namespace Emby.Server.Implementations.Library /// Task{Genre}. public Genre GetGenre(string name) { - return CreateItemByName(Genre.GetPath, name); + return CreateItemByName(Genre.GetPath, name, new DtoOptions(true)); } /// @@ -935,7 +935,7 @@ namespace Emby.Server.Implementations.Library /// Task{MusicGenre}. public MusicGenre GetMusicGenre(string name) { - return CreateItemByName(MusicGenre.GetPath, name); + return CreateItemByName(MusicGenre.GetPath, name, new DtoOptions(true)); } /// @@ -945,7 +945,7 @@ namespace Emby.Server.Implementations.Library /// Task{GameGenre}. public GameGenre GetGameGenre(string name) { - return CreateItemByName(GameGenre.GetPath, name); + return CreateItemByName(GameGenre.GetPath, name, new DtoOptions(true)); } /// @@ -963,7 +963,7 @@ namespace Emby.Server.Implementations.Library var name = value.ToString(CultureInfo.InvariantCulture); - return CreateItemByName(Year.GetPath, name); + return CreateItemByName(Year.GetPath, name, new DtoOptions(true)); } /// @@ -973,10 +973,15 @@ namespace Emby.Server.Implementations.Library /// Task{Genre}. public MusicArtist GetArtist(string name) { - return CreateItemByName(MusicArtist.GetPath, name); + return GetArtist(name, new DtoOptions(true)); } - private T CreateItemByName(Func getPathFn, string name) + public MusicArtist GetArtist(string name, DtoOptions options) + { + return CreateItemByName(MusicArtist.GetPath, name, options); + } + + private T CreateItemByName(Func getPathFn, string name, DtoOptions options) where T : BaseItem, new() { if (typeof(T) == typeof(MusicArtist)) @@ -985,7 +990,7 @@ namespace Emby.Server.Implementations.Library { IncludeItemTypes = new[] { typeof(T).Name }, Name = name, - DtoOptions = new DtoOptions(true) + DtoOptions = options }).Cast() .OrderBy(i => i.IsAccessedByName ? 1 : 0) @@ -1029,54 +1034,6 @@ namespace Emby.Server.Implementations.Library return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId); } - public IEnumerable GetAlbumArtists(IEnumerable items) - { - var names = items - .SelectMany(i => i.AlbumArtists) - .DistinctNames() - .Select(i => - { - try - { - var artist = GetArtist(i); - - return artist; - } - catch - { - // Already logged at lower levels - return null; - } - }) - .Where(i => i != null); - - return names; - } - - public IEnumerable GetArtists(IEnumerable items) - { - var names = items - .SelectMany(i => i.AllArtists) - .DistinctNames() - .Select(i => - { - try - { - var artist = GetArtist(i); - - return artist; - } - catch - { - // Already logged at lower levels - return null; - } - }) - .Where(i => i != null); - - return names; - } - /// /// Validate and refresh the People sub-set of the IBN. /// The items are stored in the db but not loaded into memory until actually requested by an operation. diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 89b772731c..83db58668d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1234,8 +1234,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http, BufferMs = 0, IgnoreDts = true, - IgnoreIndex = true, - GenPtsInput = true + IgnoreIndex = true }; var isAudio = false; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index fa505d3fb3..48ad7cf123 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -479,7 +479,7 @@ namespace Emby.Server.Implementations.LiveTv if (!(service is EmbyTV.EmbyTV)) { // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says - mediaSource.SupportsDirectPlay = false; + //mediaSource.SupportsDirectPlay = false; mediaSource.SupportsDirectStream = false; mediaSource.SupportsTranscoding = true; foreach (var stream in mediaSource.MediaStreams) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 5d489985f2..c1e1bf2b62 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -422,8 +422,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun SupportsTranscoding = true, IsInfiniteStream = true, IgnoreDts = true, - IgnoreIndex = true, - GenPtsInput = true + IgnoreIndex = true }; mediaSource.InferTotalBitrate(); @@ -507,12 +506,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner) { - return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); + return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment); } // The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet - var enableHttpStream = _environment.OperatingSystem == OperatingSystem.OSX || - _environment.OperatingSystem == OperatingSystem.BSD; + var enableHttpStream = _environment.OperatingSystem == OperatingSystem.OSX + || _environment.OperatingSystem == OperatingSystem.BSD; enableHttpStream = true; if (enableHttpStream) { @@ -521,17 +520,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var httpUrl = GetApiUrl(info, true) + "/auto/v" + hdhrId; // If raw was used, the tuner doesn't support params - if (!string.IsNullOrWhiteSpace(profile) - && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) { httpUrl += "?transcode=" + profile; } mediaSource.Path = httpUrl; - return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment); } - return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); + return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager, _environment); } public async Task Validate(TunerHostInfo info) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs index dd63bbb726..03c21b1202 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs @@ -10,6 +10,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.System; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { @@ -17,24 +18,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { private readonly ILogger _logger; private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - private readonly IServerApplicationPaths _appPaths; private readonly IServerApplicationHost _appHost; private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); - private readonly MulticastStream _multicastStream; - public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost) - : base(mediaSource) + private readonly string _tempFilePath; + + public HdHomerunHttpStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, IEnvironmentInfo environment) + : base(mediaSource, environment, fileSystem) { - _fileSystem = fileSystem; _httpClient = httpClient; _logger = logger; - _appPaths = appPaths; _appHost = appHost; OriginalStreamId = originalStreamId; - _multicastStream = new MulticastStream(_logger); + + _tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts"); } protected override async Task OpenInternal(CancellationToken openCancellationToken) @@ -74,9 +73,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return _liveStreamTaskCompletionSource.Task; } - private async Task StartStreaming(string url, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) + private Task StartStreaming(string url, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) { - await Task.Run(async () => + return Task.Run(async () => { var isFirstAttempt = true; @@ -101,13 +100,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { _logger.Info("Beginning multicastStream.CopyUntilCancelled"); - Action onStarted = null; - if (isFirstAttempt) + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath)); + using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous | FileOpenOptions.SequentialScan)) { - onStarted = () => openTaskCompletionSource.TrySetResult(true); - } + ResolveAfterDelay(2000, openTaskCompletionSource); - await _multicastStream.CopyUntilCancelled(response.Content, onStarted, cancellationToken).ConfigureAwait(false); + await response.Content.CopyToAsync(fileStream, 81920, cancellationToken).ConfigureAwait(false); + } } } } @@ -131,13 +130,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } _liveStreamTaskCompletionSource.TrySetResult(true); + await DeleteTempFile(_tempFilePath).ConfigureAwait(false); + }); + } - }).ConfigureAwait(false); + private void ResolveAfterDelay(int delayMs, TaskCompletionSource openTaskCompletionSource) + { + Task.Run(async () => + { + await Task.Delay(delayMs).ConfigureAwait(false); + openTaskCompletionSource.TrySetResult(true); + }); } public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) { - return _multicastStream.CopyToAsync(stream , cancellationToken); + return CopyFileTo(_tempFilePath, false, stream, cancellationToken); } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 2c678d9f8d..3202b7313c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -46,10 +46,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public class HdHomerunChannelCommands : IHdHomerunChannelCommands { private string _channel; + private string _profile; - public HdHomerunChannelCommands(string channel) + public HdHomerunChannelCommands(string channel, string profile) { _channel = channel; + _profile = profile; } public IEnumerable> GetCommands() @@ -57,7 +59,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var commands = new List>(); if (!String.IsNullOrEmpty(_channel)) - commands.Add(Tuple.Create("vchannel", _channel)); + { + if (!string.IsNullOrWhiteSpace(_profile) && !string.Equals(_profile, "native", StringComparison.OrdinalIgnoreCase)) + { + commands.Add(Tuple.Create("vchannel", String.Format("{0} transcode={1}", _channel, _profile))); + } + else + { + commands.Add(Tuple.Create("vchannel", _channel)); + } + } return commands; } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index c84aebce76..ff6c0fb2c9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -14,39 +14,35 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; +using MediaBrowser.Model.System; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider { private readonly ILogger _logger; - private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - private readonly IServerApplicationPaths _appPaths; private readonly IServerApplicationHost _appHost; private readonly ISocketFactory _socketFactory; private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); - private readonly MulticastStream _multicastStream; private readonly IHdHomerunChannelCommands _channelCommands; private readonly int _numTuners; private readonly INetworkManager _networkManager; - public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager) - : base(mediaSource) + private readonly string _tempFilePath; + + public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment) + : base(mediaSource, environment, fileSystem) { - _fileSystem = fileSystem; - _httpClient = httpClient; _logger = logger; - _appPaths = appPaths; _appHost = appHost; _socketFactory = socketFactory; _networkManager = networkManager; OriginalStreamId = originalStreamId; - _multicastStream = new MulticastStream(_logger); _channelCommands = channelCommands; _numTuners = numTuners; + _tempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts"); } protected override async Task OpenInternal(CancellationToken openCancellationToken) @@ -87,9 +83,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return _liveStreamTaskCompletionSource.Task; } - private async Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) + private Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) { - await Task.Run(async () => + return Task.Run(async () => { var isFirstAttempt = true; using (var udpClient = _socketFactory.CreateUdpSocket(localPort)) @@ -124,13 +120,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (!cancellationToken.IsCancellationRequested) { - Action onStarted = null; - if (isFirstAttempt) + FileSystem.CreateDirectory(FileSystem.GetDirectoryName(_tempFilePath)); + using (var fileStream = FileSystem.GetFileStream(_tempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous | FileOpenOptions.SequentialScan)) { - onStarted = () => openTaskCompletionSource.TrySetResult(true); - } + ResolveAfterDelay(2000, openTaskCompletionSource); - await _multicastStream.CopyUntilCancelled(new UdpClientStream(udpClient), onStarted, cancellationToken).ConfigureAwait(false); + await new UdpClientStream(udpClient).CopyToAsync(fileStream, 81920, cancellationToken).ConfigureAwait(false); + } } } catch (OperationCanceledException ex) @@ -159,12 +155,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - }).ConfigureAwait(false); + await DeleteTempFile(_tempFilePath).ConfigureAwait(false); + }); + } + + private void ResolveAfterDelay(int delayMs, TaskCompletionSource openTaskCompletionSource) + { + Task.Run(async () => + { + await Task.Delay(delayMs).ConfigureAwait(false); + openTaskCompletionSource.TrySetResult(true); + }); } public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) { - return _multicastStream.CopyToAsync(stream, cancellationToken); + return CopyFileTo(_tempFilePath, false, stream, cancellationToken); } } @@ -198,7 +204,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize) // The RTP header will be stripped so see how many reads we need to make to fill the buffer. - int numReads = count / PacketSize; + var numReads = count / PacketSize; + int totalBytesRead = 0; for (int i = 0; i < numReads; ++i) @@ -206,7 +213,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var data = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); var bytesRead = data.ReceivedBytes - RtpHeaderBytes; - + // remove rtp header Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead); offset += bytesRead; @@ -224,7 +231,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { get { - throw new NotImplementedException(); + return true; } } @@ -232,7 +239,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { get { - throw new NotImplementedException(); + return false; } } @@ -240,7 +247,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { get { - throw new NotImplementedException(); + return false; } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 8cf1106f0f..2c6641ee14 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -19,6 +19,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.System; namespace Emby.Server.Implementations.LiveTv.TunerHosts { @@ -27,13 +28,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private readonly IFileSystem _fileSystem; private readonly IHttpClient _httpClient; private readonly IServerApplicationHost _appHost; + private readonly IEnvironmentInfo _environment; - public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost) + public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder) { _fileSystem = fileSystem; _httpClient = httpClient; _appHost = appHost; + _environment = environment; } public override string Type @@ -73,7 +76,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false); - var liveStream = new LiveStream(sources.First()); + var liveStream = new LiveStream(sources.First(), _environment, _fileSystem); return liveStream; } diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs index 3c2af60db0..22d2b95f7f 100644 --- a/Emby.Server.Implementations/Services/ResponseHelper.cs +++ b/Emby.Server.Implementations/Services/ResponseHelper.cs @@ -12,7 +12,7 @@ namespace Emby.Server.Implementations.Services { public static class ResponseHelper { - public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result) + public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result, CancellationToken cancellationToken) { if (result == null) { @@ -30,21 +30,17 @@ namespace Emby.Server.Implementations.Services { httpResult.RequestContext = httpReq; httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType; - return WriteToResponseInternal(httpRes, httpResult, httpReq); + return WriteToResponseInternal(httpRes, httpResult, httpReq, cancellationToken); } - return WriteToResponseInternal(httpRes, result, httpReq); + return WriteToResponseInternal(httpRes, result, httpReq, cancellationToken); } /// /// Writes to response. /// Response headers are customizable by implementing IHasHeaders an returning Dictionary of Http headers. /// - /// The response. - /// Whether or not it was implicity handled by ServiceStack's built-in handlers. - /// The serialization context. - /// - private static async Task WriteToResponseInternal(IResponse response, object result, IRequest request) + private static async Task WriteToResponseInternal(IResponse response, object result, IRequest request, CancellationToken cancellationToken) { var defaultContentType = request.ResponseContentType; @@ -105,7 +101,7 @@ namespace Emby.Server.Implementations.Services var asyncStreamWriter = result as IAsyncStreamWriter; if (asyncStreamWriter != null) { - await asyncStreamWriter.WriteToAsync(response.OutputStream, CancellationToken.None).ConfigureAwait(false); + await asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken).ConfigureAwait(false); return; } @@ -119,7 +115,7 @@ namespace Emby.Server.Implementations.Services var fileWriter = result as FileWriter; if (fileWriter != null) { - await fileWriter.WriteToAsync(response, CancellationToken.None).ConfigureAwait(false); + await fileWriter.WriteToAsync(response, cancellationToken).ConfigureAwait(false); return; } diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs index 8b59b48430..95a9dd6829 100644 --- a/Emby.Server.Implementations/Services/ServiceHandler.cs +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.Logging; @@ -123,7 +124,7 @@ namespace Emby.Server.Implementations.Services // Set from SSHHF.GetHandlerForPathInfo() public string ResponseContentType { get; set; } - public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName) + public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName, CancellationToken cancellationToken) { var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo); if (restPath == null) @@ -150,7 +151,7 @@ namespace Emby.Server.Implementations.Services responseFilter(httpReq, httpRes, response); } - await ResponseHelper.WriteToResponse(httpRes, httpReq, response).ConfigureAwait(false); + await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false); } public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger) diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 1a57ff62dd..0f1d240d0c 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -174,14 +174,15 @@ namespace MediaBrowser.Api return options; } - protected MusicArtist GetArtist(string name, ILibraryManager libraryManager) + protected MusicArtist GetArtist(string name, ILibraryManager libraryManager, DtoOptions dtoOptions) { if (name.IndexOf(BaseItem.SlugChar) != -1) { var result = libraryManager.GetItemList(new InternalItemsQuery { SlugName = name, - IncludeItemTypes = new[] { typeof(MusicArtist).Name } + IncludeItemTypes = new[] { typeof(MusicArtist).Name }, + DtoOptions = dtoOptions }).OfType().FirstOrDefault(); @@ -191,17 +192,18 @@ namespace MediaBrowser.Api } } - return libraryManager.GetArtist(name); + return libraryManager.GetArtist(name, dtoOptions); } - protected Studio GetStudio(string name, ILibraryManager libraryManager) + protected Studio GetStudio(string name, ILibraryManager libraryManager, DtoOptions dtoOptions) { if (name.IndexOf(BaseItem.SlugChar) != -1) { var result = libraryManager.GetItemList(new InternalItemsQuery { SlugName = name, - IncludeItemTypes = new[] { typeof(Studio).Name } + IncludeItemTypes = new[] { typeof(Studio).Name }, + DtoOptions = dtoOptions }).OfType().FirstOrDefault(); @@ -214,14 +216,15 @@ namespace MediaBrowser.Api return libraryManager.GetStudio(name); } - protected Genre GetGenre(string name, ILibraryManager libraryManager) + protected Genre GetGenre(string name, ILibraryManager libraryManager, DtoOptions dtoOptions) { if (name.IndexOf(BaseItem.SlugChar) != -1) { var result = libraryManager.GetItemList(new InternalItemsQuery { SlugName = name, - IncludeItemTypes = new[] { typeof(Genre).Name } + IncludeItemTypes = new[] { typeof(Genre).Name }, + DtoOptions = dtoOptions }).OfType().FirstOrDefault(); @@ -234,14 +237,15 @@ namespace MediaBrowser.Api return libraryManager.GetGenre(name); } - protected MusicGenre GetMusicGenre(string name, ILibraryManager libraryManager) + protected MusicGenre GetMusicGenre(string name, ILibraryManager libraryManager, DtoOptions dtoOptions) { if (name.IndexOf(BaseItem.SlugChar) != -1) { var result = libraryManager.GetItemList(new InternalItemsQuery { SlugName = name, - IncludeItemTypes = new[] { typeof(MusicGenre).Name } + IncludeItemTypes = new[] { typeof(MusicGenre).Name }, + DtoOptions = dtoOptions }).OfType().FirstOrDefault(); @@ -254,14 +258,15 @@ namespace MediaBrowser.Api return libraryManager.GetMusicGenre(name); } - protected GameGenre GetGameGenre(string name, ILibraryManager libraryManager) + protected GameGenre GetGameGenre(string name, ILibraryManager libraryManager, DtoOptions dtoOptions) { if (name.IndexOf(BaseItem.SlugChar) != -1) { var result = libraryManager.GetItemList(new InternalItemsQuery { SlugName = name, - IncludeItemTypes = new[] { typeof(GameGenre).Name } + IncludeItemTypes = new[] { typeof(GameGenre).Name }, + DtoOptions = dtoOptions }).OfType().FirstOrDefault(); @@ -274,14 +279,15 @@ namespace MediaBrowser.Api return libraryManager.GetGameGenre(name); } - protected Person GetPerson(string name, ILibraryManager libraryManager) + protected Person GetPerson(string name, ILibraryManager libraryManager, DtoOptions dtoOptions) { if (name.IndexOf(BaseItem.SlugChar) != -1) { var result = libraryManager.GetItemList(new InternalItemsQuery { SlugName = name, - IncludeItemTypes = new[] { typeof(Person).Name } + IncludeItemTypes = new[] { typeof(Person).Name }, + DtoOptions = dtoOptions }).OfType().FirstOrDefault(); @@ -329,37 +335,33 @@ namespace MediaBrowser.Api /// /// Gets the name of the item by. /// - /// The name. - /// The type. - /// The library manager. - /// Task{BaseItem}. - protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager) + protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager, DtoOptions dtoOptions) { BaseItem item; if (type.IndexOf("Person", StringComparison.OrdinalIgnoreCase) == 0) { - item = GetPerson(name, libraryManager); + item = GetPerson(name, libraryManager, dtoOptions); } else if (type.IndexOf("Artist", StringComparison.OrdinalIgnoreCase) == 0) { - item = GetArtist(name, libraryManager); + item = GetArtist(name, libraryManager, dtoOptions); } else if (type.IndexOf("Genre", StringComparison.OrdinalIgnoreCase) == 0) { - item = GetGenre(name, libraryManager); + item = GetGenre(name, libraryManager, dtoOptions); } else if (type.IndexOf("MusicGenre", StringComparison.OrdinalIgnoreCase) == 0) { - item = GetMusicGenre(name, libraryManager); + item = GetMusicGenre(name, libraryManager, dtoOptions); } else if (type.IndexOf("GameGenre", StringComparison.OrdinalIgnoreCase) == 0) { - item = GetGameGenre(name, libraryManager); + item = GetGameGenre(name, libraryManager, dtoOptions); } else if (type.IndexOf("Studio", StringComparison.OrdinalIgnoreCase) == 0) { - item = GetStudio(name, libraryManager); + item = GetStudio(name, libraryManager, dtoOptions); } else if (type.IndexOf("Year", StringComparison.OrdinalIgnoreCase) == 0) { diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index cbbaa82b80..6c4cc22170 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; @@ -406,7 +407,7 @@ namespace MediaBrowser.Api.Images { var type = GetPathValue(0); - var item = GetItemByName(request.Name, type, _libraryManager); + var item = GetItemByName(request.Name, type, _libraryManager, new DtoOptions(false)); return GetImage(request, item, false); } @@ -415,7 +416,7 @@ namespace MediaBrowser.Api.Images { var type = GetPathValue(0); - var item = GetItemByName(request.Name, type, _libraryManager); + var item = GetItemByName(request.Name, type, _libraryManager, new DtoOptions(false)); return GetImage(request, item, true); } diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 3494754d0a..a90c8d9e78 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -22,6 +22,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; namespace MediaBrowser.Api.LiveTv { @@ -698,8 +699,9 @@ namespace MediaBrowser.Api.LiveTv private readonly IFileSystem _fileSystem; private readonly IAuthorizationContext _authContext; private readonly ISessionContext _sessionContext; + private readonly IEnvironmentInfo _environment; - public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext) + public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext, IEnvironmentInfo environment) { _liveTvManager = liveTvManager; _userManager = userManager; @@ -710,6 +712,7 @@ namespace MediaBrowser.Api.LiveTv _fileSystem = fileSystem; _authContext = authContext; _sessionContext = sessionContext; + _environment = environment; } public object Get(GetTunerHostTypes request) @@ -731,7 +734,7 @@ namespace MediaBrowser.Api.LiveTv outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path); - return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, CancellationToken.None) + return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, _environment, CancellationToken.None) { AllowEndOfFile = false }; @@ -750,7 +753,7 @@ namespace MediaBrowser.Api.LiveTv outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container); - return new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, CancellationToken.None) + return new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, _environment, CancellationToken.None) { AllowEndOfFile = false }; diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index 797edd9402..3cb29de072 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -171,7 +171,7 @@ namespace MediaBrowser.Api.Music public Task Get(GetInstantMixFromArtist request) { var user = _userManager.GetUserById(request.UserId); - var artist = _libraryManager.GetArtist(request.Name); + var artist = _libraryManager.GetArtist(request.Name, new DtoOptions(false)); var dtoOptions = GetDtoOptions(_authContext, request); diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index f0386d5ba1..c0257b0079 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; namespace MediaBrowser.Api.Playback.Progressive { @@ -35,6 +36,10 @@ namespace MediaBrowser.Api.Playback.Progressive //[Authenticated] public class AudioService : BaseProgressiveStreamingService { + public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor, environmentInfo) + { + } + /// /// Gets the specified request. /// @@ -61,9 +66,5 @@ namespace MediaBrowser.Api.Playback.Progressive return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath); } - - public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor) - { - } } } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 646a91c275..db5c78a2f2 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -16,6 +16,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; namespace MediaBrowser.Api.Playback.Progressive { @@ -25,10 +26,12 @@ namespace MediaBrowser.Api.Playback.Progressive public abstract class BaseProgressiveStreamingService : BaseStreamingService { protected readonly IImageProcessor ImageProcessor; + protected readonly IEnvironmentInfo EnvironmentInfo; - public BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext) + public BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext) { ImageProcessor = imageProcessor; + EnvironmentInfo = environmentInfo; } /// @@ -130,7 +133,7 @@ namespace MediaBrowser.Api.Playback.Progressive // TODO: Don't hardcode this outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts"); - return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None) + return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None) { AllowEndOfFile = false }; @@ -174,7 +177,7 @@ namespace MediaBrowser.Api.Playback.Progressive outputHeaders["Content-Type"] = contentType; - return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None) + return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, EnvironmentInfo, CancellationToken.None) { AllowEndOfFile = false }; @@ -398,7 +401,7 @@ namespace MediaBrowser.Api.Playback.Progressive outputHeaders[item.Key] = item.Value; } - return new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, CancellationToken.None); + return new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, EnvironmentInfo, CancellationToken.None); } finally { diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index a33fbcbcfd..d4d4689134 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -10,6 +10,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; namespace MediaBrowser.Api.Playback.Progressive { @@ -22,16 +23,16 @@ namespace MediaBrowser.Api.Playback.Progressive private readonly CancellationToken _cancellationToken; private readonly Dictionary _outputHeaders; - // 256k - private const int BufferSize = 81920; + const int StreamCopyToBufferSize = 81920; private long _bytesWritten = 0; public long StartPosition { get; set; } public bool AllowEndOfFile = true; private readonly IDirectStreamProvider _directStreamProvider; + private readonly IEnvironmentInfo _environment; - public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken) + public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, TranscodingJob job, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken) { _fileSystem = fileSystem; _path = path; @@ -39,15 +40,17 @@ namespace MediaBrowser.Api.Playback.Progressive _job = job; _logger = logger; _cancellationToken = cancellationToken; + _environment = environment; } - public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken) + public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary outputHeaders, TranscodingJob job, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken) { _directStreamProvider = directStreamProvider; _outputHeaders = outputHeaders; _job = job; _logger = logger; _cancellationToken = cancellationToken; + _environment = environment; } public IDictionary Headers @@ -58,33 +61,55 @@ namespace MediaBrowser.Api.Playback.Progressive } } - private Stream GetInputStream() + private Stream GetInputStream(bool allowAsyncFileRead) { - return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true); + var fileOpenOptions = StartPosition > 0 + ? FileOpenOptions.RandomAccess + : FileOpenOptions.SequentialScan; + + if (allowAsyncFileRead) + { + fileOpenOptions |= FileOpenOptions.Asynchronous; + } + + return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); } public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken) { + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationToken).Token; + try { if (_directStreamProvider != null) { - await _directStreamProvider.CopyToAsync(outputStream, _cancellationToken).ConfigureAwait(false); + await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false); return; } var eofCount = 0; - using (var inputStream = GetInputStream()) + // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 + var allowAsyncFileRead = _environment.OperatingSystem != OperatingSystem.Windows; + + using (var inputStream = GetInputStream(allowAsyncFileRead)) { if (StartPosition > 0) { inputStream.Position = StartPosition; } - while (eofCount < 15 || !AllowEndOfFile) + while (eofCount < 20 || !AllowEndOfFile) { - var bytesRead = await CopyToAsyncInternal(inputStream, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false); + int bytesRead; + if (allowAsyncFileRead) + { + bytesRead = await CopyToInternalAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false); + } + else + { + bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false); + } //var position = fs.Position; //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); @@ -95,7 +120,7 @@ namespace MediaBrowser.Api.Playback.Progressive { eofCount++; } - await Task.Delay(100, _cancellationToken).ConfigureAwait(false); + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } else { @@ -113,22 +138,54 @@ namespace MediaBrowser.Api.Playback.Progressive } } - private async Task CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken) + private async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken) { - byte[] buffer = new byte[bufferSize]; + var array = new byte[StreamCopyToBufferSize]; int bytesRead; int totalBytesRead = 0; - while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + while ((bytesRead = source.Read(array, 0, array.Length)) != 0) { - await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + var bytesToWrite = bytesRead; + + if (bytesToWrite > 0) + { + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); - _bytesWritten += bytesRead; - totalBytesRead += bytesRead; + _bytesWritten += bytesRead; + totalBytesRead += bytesRead; - if (_job != null) + if (_job != null) + { + _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten); + } + } + } + + return totalBytesRead; + } + + private async Task CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken) + { + var array = new byte[StreamCopyToBufferSize]; + int bytesRead; + int totalBytesRead = 0; + + while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + var bytesToWrite = bytesRead; + + if (bytesToWrite > 0) { - _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten); + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + + _bytesWritten += bytesRead; + totalBytesRead += bytesRead; + + if (_job != null) + { + _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten); + } } } diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index c36a27690a..5e21f6a841 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -9,6 +9,7 @@ using MediaBrowser.Model.Serialization; using System.Threading.Tasks; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; namespace MediaBrowser.Api.Playback.Progressive { @@ -66,7 +67,7 @@ namespace MediaBrowser.Api.Playback.Progressive //[Authenticated] public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor) + public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, IEnvironmentInfo environmentInfo) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor, environmentInfo) { } diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index ae64623df9..c28014f9bc 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -18,6 +18,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; namespace MediaBrowser.Api.Playback { @@ -63,7 +64,7 @@ namespace MediaBrowser.Api.Playback //[Authenticated] public class UniversalAudioService : BaseApiService { - public UniversalAudioService(IServerConfigurationManager serverConfigurationManager, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, IDeviceManager deviceManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, INetworkManager networkManager) + public UniversalAudioService(IServerConfigurationManager serverConfigurationManager, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, IDeviceManager deviceManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, INetworkManager networkManager, IEnvironmentInfo environmentInfo) { ServerConfigurationManager = serverConfigurationManager; UserManager = userManager; @@ -80,6 +81,7 @@ namespace MediaBrowser.Api.Playback AuthorizationContext = authorizationContext; ImageProcessor = imageProcessor; NetworkManager = networkManager; + EnvironmentInfo = environmentInfo; } protected IServerConfigurationManager ServerConfigurationManager { get; private set; } @@ -97,6 +99,7 @@ namespace MediaBrowser.Api.Playback protected IAuthorizationContext AuthorizationContext { get; private set; } protected IImageProcessor ImageProcessor { get; private set; } protected INetworkManager NetworkManager { get; private set; } + protected IEnvironmentInfo EnvironmentInfo { get; private set; } public Task Get(GetUniversalAudioStream request) { @@ -263,7 +266,8 @@ namespace MediaBrowser.Api.Playback ZipClient, JsonSerializer, AuthorizationContext, - ImageProcessor) + ImageProcessor, + EnvironmentInfo) { Request = Request }; diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index 5bbd96c7c3..7c1519e9fd 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -68,10 +68,10 @@ namespace MediaBrowser.Api.UserLibrary /// Task{BaseItemDto}. private BaseItemDto GetItem(GetArtist request) { - var item = GetArtist(request.Name, LibraryManager); - var dtoOptions = GetDtoOptions(AuthorizationContext, request); + var item = GetArtist(request.Name, LibraryManager, dtoOptions); + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = UserManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index daef97915a..d79fafd321 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -267,7 +267,8 @@ namespace MediaBrowser.Api.UserLibrary { ExcludeItemTypes = excludeItemTypes, IncludeItemTypes = includeItemTypes, - MediaTypes = mediaTypes + MediaTypes = mediaTypes, + DtoOptions = dtoOptions }; Func filter = i => FilterItem(request, i, excludeItemTypes, includeItemTypes, mediaTypes); diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs index 2eef1ab2fd..56730c1b20 100644 --- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs @@ -56,10 +56,10 @@ namespace MediaBrowser.Api.UserLibrary /// Task{BaseItemDto}. private BaseItemDto GetItem(GetGameGenre request) { - var item = GetGameGenre(request.Name, LibraryManager); - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - + + var item = GetGameGenre(request.Name, LibraryManager, dtoOptions); + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = UserManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index 664efac14c..fc387e5e39 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -66,10 +66,10 @@ namespace MediaBrowser.Api.UserLibrary /// Task{BaseItemDto}. private BaseItemDto GetItem(GetGenre request) { - var item = GetGenre(request.Name, LibraryManager); - - var dtoOptions = GetDtoOptions(AuthorizationContext ,request); + var dtoOptions = GetDtoOptions(AuthorizationContext, request); + var item = GetGenre(request.Name, LibraryManager, dtoOptions); + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = UserManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index da5c5c763c..b7bb758c3f 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -358,7 +358,7 @@ namespace MediaBrowser.Api.UserLibrary { try { - return _libraryManager.GetArtist(i); + return _libraryManager.GetArtist(i, new DtoOptions(false)); } catch { diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs index 305c136dfb..d1d4aa634b 100644 --- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs @@ -57,10 +57,10 @@ namespace MediaBrowser.Api.UserLibrary /// Task{BaseItemDto}. private BaseItemDto GetItem(GetMusicGenre request) { - var item = GetMusicGenre(request.Name, LibraryManager); - var dtoOptions = GetDtoOptions(AuthorizationContext, request); + var item = GetMusicGenre(request.Name, LibraryManager, dtoOptions); + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = UserManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index dbce22578b..21f416025e 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -64,10 +64,10 @@ namespace MediaBrowser.Api.UserLibrary /// Task{BaseItemDto}. private BaseItemDto GetItem(GetPerson request) { - var item = GetPerson(request.Name, LibraryManager); - var dtoOptions = GetDtoOptions(AuthorizationContext, request); + var item = GetPerson(request.Name, LibraryManager, dtoOptions); + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = UserManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index f4debcf48a..7ac1264e8f 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -66,10 +66,10 @@ namespace MediaBrowser.Api.UserLibrary /// Task{BaseItemDto}. private BaseItemDto GetItem(GetStudio request) { - var item = GetStudio(request.Name, LibraryManager); - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - + + var item = GetStudio(request.Name, LibraryManager, dtoOptions); + if (!string.IsNullOrWhiteSpace(request.UserId)) { var user = UserManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index f40ab3cdee..e9eca19cf7 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -8,6 +8,7 @@ using System.Linq; using MediaBrowser.Model.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; @@ -42,20 +43,22 @@ namespace MediaBrowser.Controller.Entities.Audio [IgnoreDataMember] public MusicArtist MusicArtist { - get - { - var artist = GetParents().OfType().FirstOrDefault(); + get { return GetMusicArtist(new DtoOptions(true)); } + } - if (artist == null) + public MusicArtist GetMusicArtist(DtoOptions options) + { + var artist = GetParents().OfType().FirstOrDefault(); + + if (artist == null) + { + var name = AlbumArtist; + if (!string.IsNullOrWhiteSpace(name)) { - var name = AlbumArtist; - if (!string.IsNullOrWhiteSpace(name)) - { - artist = LibraryManager.GetArtist(name); - } + artist = LibraryManager.GetArtist(name, options); } - return artist; } + return artist; } [IgnoreDataMember] @@ -171,7 +174,7 @@ namespace MediaBrowser.Controller.Entities.Audio id.AlbumArtists = AlbumArtists; - var artist = MusicArtist; + var artist = GetMusicArtist(new DtoOptions(false)); if (artist != null) { diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index f029d36fd7..d3220f6c1b 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -160,42 +160,6 @@ namespace MediaBrowser.Controller.Entities public DtoOptions DtoOptions { get; set; } public int MinSimilarityScore { get; set; } - public bool HasField(ItemFields name) - { - var fields = DtoOptions.Fields; - - switch (name) - { - case ItemFields.HomePageUrl: - case ItemFields.Keywords: - case ItemFields.DisplayMediaType: - case ItemFields.VoteCount: - case ItemFields.CustomRating: - case ItemFields.ProductionLocations: - case ItemFields.Settings: - case ItemFields.OriginalTitle: - case ItemFields.Taglines: - case ItemFields.SortName: - case ItemFields.Studios: - case ItemFields.Tags: - case ItemFields.ThemeSongIds: - case ItemFields.ThemeVideoIds: - case ItemFields.DateCreated: - case ItemFields.Overview: - case ItemFields.Genres: - case ItemFields.DateLastMediaAdded: - case ItemFields.ExternalEtag: - case ItemFields.PresentationUniqueKey: - case ItemFields.InheritedParentalRatingValue: - case ItemFields.ExternalSeriesId: - return fields.Contains(name); - case ItemFields.ServiceName: - return true; - default: - return true; - } - } - public InternalItemsQuery() { MinSimilarityScore = 20; diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 70a635872a..7dcc207db5 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -12,6 +12,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.IO; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; @@ -68,18 +69,7 @@ namespace MediaBrowser.Controller.Library /// The name. /// Task{Artist}. MusicArtist GetArtist(string name); - /// - /// Gets the album artists. - /// - /// The items. - /// IEnumerable<MusicArtist>. - IEnumerable GetAlbumArtists(IEnumerable items); - /// - /// Gets the artists. - /// - /// The items. - /// IEnumerable<MusicArtist>. - IEnumerable GetArtists(IEnumerable items); + MusicArtist GetArtist(string name, DtoOptions options); /// /// Gets a Studio /// diff --git a/MediaBrowser.Controller/LiveTv/LiveStream.cs b/MediaBrowser.Controller/LiveTv/LiveStream.cs index 0908c3eccb..48468d1a0e 100644 --- a/MediaBrowser.Controller/LiveTv/LiveStream.cs +++ b/MediaBrowser.Controller/LiveTv/LiveStream.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.System; namespace MediaBrowser.Controller.LiveTv { @@ -10,7 +13,8 @@ namespace MediaBrowser.Controller.LiveTv { public MediaSourceInfo OriginalMediaSource { get; set; } public MediaSourceInfo OpenedMediaSource { get; set; } - public int ConsumerCount { + public int ConsumerCount + { get { return SharedStreamIds.Count; } } public ITunerHost TunerHost { get; set; } @@ -18,11 +22,16 @@ namespace MediaBrowser.Controller.LiveTv public bool EnableStreamSharing { get; set; } public string UniqueId = Guid.NewGuid().ToString("N"); - public List SharedStreamIds = new List(); + public List SharedStreamIds = new List(); + protected readonly IEnvironmentInfo Environment; + protected readonly IFileSystem FileSystem; + const int StreamCopyToBufferSize = 81920; - public LiveStream(MediaSourceInfo mediaSource) + public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem) { OriginalMediaSource = mediaSource; + Environment = environment; + FileSystem = fileSystem; OpenedMediaSource = mediaSource; EnableStreamSharing = true; } @@ -41,5 +50,131 @@ namespace MediaBrowser.Controller.LiveTv { return Task.FromResult(true); } + + private Stream GetInputStream(string path, long startPosition, bool allowAsyncFileRead) + { + var fileOpenOptions = startPosition > 0 + ? FileOpenOptions.RandomAccess + : FileOpenOptions.SequentialScan; + + if (allowAsyncFileRead) + { + fileOpenOptions |= FileOpenOptions.Asynchronous; + } + + return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); + } + + protected async Task DeleteTempFile(string path, int retryCount = 0) + { + try + { + FileSystem.DeleteFile(path); + return; + } + catch + { + + } + + if (retryCount > 20) + { + return; + } + + await Task.Delay(500).ConfigureAwait(false); + await DeleteTempFile(path, retryCount + 1).ConfigureAwait(false); + } + + protected async Task CopyFileTo(string path, bool allowEndOfFile, Stream outputStream, CancellationToken cancellationToken) + { + var eofCount = 0; + + long startPosition = -25000; + if (startPosition < 0) + { + var length = FileSystem.GetFileInfo(path).Length; + startPosition = Math.Max(length - startPosition, 0); + } + + // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 + var allowAsyncFileRead = Environment.OperatingSystem != OperatingSystem.Windows; + + using (var inputStream = GetInputStream(path, startPosition, allowAsyncFileRead)) + { + if (startPosition > 0) + { + inputStream.Position = startPosition; + } + + while (eofCount < 20 || !allowEndOfFile) + { + int bytesRead; + if (allowAsyncFileRead) + { + bytesRead = await CopyToInternalAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false); + } + else + { + bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false); + } + + //var position = fs.Position; + //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); + + if (bytesRead == 0) + { + eofCount++; + await Task.Delay(100, cancellationToken).ConfigureAwait(false); + } + else + { + eofCount = 0; + } + } + } + } + + private async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken) + { + var array = new byte[StreamCopyToBufferSize]; + int bytesRead; + int totalBytesRead = 0; + + while ((bytesRead = source.Read(array, 0, array.Length)) != 0) + { + var bytesToWrite = bytesRead; + + if (bytesToWrite > 0) + { + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + + totalBytesRead += bytesRead; + } + } + + return totalBytesRead; + } + + private async Task CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken) + { + var array = new byte[StreamCopyToBufferSize]; + int bytesRead; + int totalBytesRead = 0; + + while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + var bytesToWrite = bytesRead; + + if (bytesToWrite > 0) + { + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); + + totalBytesRead += bytesRead; + } + } + + return totalBytesRead; + } } } diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index db35090b9b..9a70520895 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -235,6 +235,9 @@ ExternalEtag, PresentationUniqueKey, InheritedParentalRatingValue, - ExternalSeriesId + ExternalSeriesId, + SeriesPresentationUniqueKey, + DateLastRefreshed, + DateLastSaved } } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index c6cdeb3542..cf3e221b33 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -305,8 +305,6 @@ namespace MediaBrowser.WebDashboard.Api } } - path = path.Replace("scripts/jquery.mobile-1.4.5.min.map", "thirdparty/jquerymobile-1.4.5/jquery.mobile-1.4.5.min.map", StringComparison.OrdinalIgnoreCase); - var localizationCulture = GetLocalizationCulture(); // Don't cache if not configured to do so @@ -330,7 +328,13 @@ namespace MediaBrowser.WebDashboard.Api var cacheKey = (_appHost.ApplicationVersion + (localizationCulture ?? string.Empty) + path).GetMD5(); - return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false); + // html gets modified on the fly + if (contentType.StartsWith("text/html", StringComparison.OrdinalIgnoreCase)) + { + return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false); + } + + return await _resultFactory.GetStaticFileResult(Request, GetPackageCreator(basePath).GetResourcePath(path)); } private string GetLocalizationCulture() diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 72389044bd..ee911f0e98 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -68,7 +68,7 @@ namespace MediaBrowser.WebDashboard.Api /// Gets the dashboard resource path. /// /// System.String. - private string GetDashboardResourcePath(string virtualPath) + public string GetResourcePath(string virtualPath) { var fullPath = Path.Combine(_basePath, virtualPath.Replace('/', _fileSystem.DirectorySeparatorChar)); @@ -97,7 +97,7 @@ namespace MediaBrowser.WebDashboard.Api return false; } - path = GetDashboardResourcePath(path); + path = GetResourcePath(path); var parent = _fileSystem.GetDirectoryName(path); return string.Equals(_basePath, parent, StringComparison.OrdinalIgnoreCase) || @@ -140,7 +140,7 @@ namespace MediaBrowser.WebDashboard.Api html = html.Substring(0, index); } } - var mainFile = _fileSystem.ReadAllText(GetDashboardResourcePath("index.html")); + var mainFile = _fileSystem.ReadAllText(GetResourcePath("index.html")); html = ReplaceFirst(mainFile, "
", "
" + html + "
"); } @@ -299,7 +299,7 @@ namespace MediaBrowser.WebDashboard.Api /// private Stream GetRawResourceStream(string virtualPath) { - return _fileSystem.GetFileStream(GetDashboardResourcePath(virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true); + return _fileSystem.GetFileStream(GetResourcePath(virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true); } } diff --git a/SharedVersion.cs b/SharedVersion.cs index 140f3c2869..5a7ec0dba4 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.17.9")] +[assembly: AssemblyVersion("3.2.17.10")] diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs index 149b24028d..b2d0d4e9c2 100644 --- a/SocketHttpListener.Portable/Net/ResponseStream.cs +++ b/SocketHttpListener.Portable/Net/ResponseStream.cs @@ -148,6 +148,7 @@ namespace SocketHttpListener.Net } const int MsCopyBufferSize = 81920; + const int StreamCopyToBufferSize = 81920; public override void Write(byte[] buffer, int offset, int count) { if (disposed) @@ -340,11 +341,11 @@ namespace SocketHttpListener.Net { if (allowAsync) { - await fs.CopyToAsync(targetStream, 81920, cancellationToken).ConfigureAwait(false); + await fs.CopyToAsync(targetStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false); } else { - fs.CopyTo(targetStream, 81920); + fs.CopyTo(targetStream, StreamCopyToBufferSize); } } } @@ -352,16 +353,11 @@ namespace SocketHttpListener.Net private static async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) { - var array = new byte[81920]; + var array = new byte[StreamCopyToBufferSize]; int bytesRead; while ((bytesRead = source.Read(array, 0, array.Length)) != 0) { - if (bytesRead == 0) - { - break; - } - var bytesToWrite = Math.Min(bytesRead, copyLength); if (bytesToWrite > 0) @@ -380,16 +376,11 @@ namespace SocketHttpListener.Net private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) { - var array = new byte[81920]; + var array = new byte[StreamCopyToBufferSize]; int bytesRead; while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) { - if (bytesRead == 0) - { - break; - } - var bytesToWrite = Math.Min(bytesRead, copyLength); if (bytesToWrite > 0)