diff options
Diffstat (limited to 'Emby.Server.Implementations')
15 files changed, 295 insertions, 223 deletions
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 49bf9e39c..fbd97b6a2 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 bb46e6006..6f2e437cf 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 5e96eda94..79209d438 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 /// <summary> /// Overridable method that can be used to implement a custom hnandler /// </summary> - /// <param name="httpReq">The HTTP req.</param> - /// <param name="url">The URL.</param> - /// <returns>Task.</returns> - 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 18df5682d..82175dbed 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. /// </summary> /// <value>The request handler.</value> - Func<IHttpRequest, Uri, Task> RequestHandler { get; set; } + Func<IHttpRequest, Uri, CancellationToken, Task> RequestHandler { get; set; } /// <summary> /// 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 682fa7a0b..f085ff71e 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<Exception, IRequest, bool> ErrorHandler { get; set; } - public Func<IHttpRequest, Uri, Task> RequestHandler { get; set; } + public Func<IHttpRequest, Uri, CancellationToken, Task> RequestHandler { get; set; } public Action<WebSocketConnectingEventArgs> 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 a423db9d6..8907c911a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -885,7 +885,7 @@ namespace Emby.Server.Implementations.Library /// <returns>Task{Person}.</returns> public Person GetPerson(string name) { - return CreateItemByName<Person>(Person.GetPath, name); + return CreateItemByName<Person>(Person.GetPath, name, new DtoOptions(true)); } /// <summary> @@ -895,7 +895,7 @@ namespace Emby.Server.Implementations.Library /// <returns>Task{Studio}.</returns> public Studio GetStudio(string name) { - return CreateItemByName<Studio>(Studio.GetPath, name); + return CreateItemByName<Studio>(Studio.GetPath, name, new DtoOptions(true)); } public Guid GetStudioId(string name) @@ -925,7 +925,7 @@ namespace Emby.Server.Implementations.Library /// <returns>Task{Genre}.</returns> public Genre GetGenre(string name) { - return CreateItemByName<Genre>(Genre.GetPath, name); + return CreateItemByName<Genre>(Genre.GetPath, name, new DtoOptions(true)); } /// <summary> @@ -935,7 +935,7 @@ namespace Emby.Server.Implementations.Library /// <returns>Task{MusicGenre}.</returns> public MusicGenre GetMusicGenre(string name) { - return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name); + return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name, new DtoOptions(true)); } /// <summary> @@ -945,7 +945,7 @@ namespace Emby.Server.Implementations.Library /// <returns>Task{GameGenre}.</returns> public GameGenre GetGameGenre(string name) { - return CreateItemByName<GameGenre>(GameGenre.GetPath, name); + return CreateItemByName<GameGenre>(GameGenre.GetPath, name, new DtoOptions(true)); } /// <summary> @@ -963,7 +963,7 @@ namespace Emby.Server.Implementations.Library var name = value.ToString(CultureInfo.InvariantCulture); - return CreateItemByName<Year>(Year.GetPath, name); + return CreateItemByName<Year>(Year.GetPath, name, new DtoOptions(true)); } /// <summary> @@ -973,10 +973,15 @@ namespace Emby.Server.Implementations.Library /// <returns>Task{Genre}.</returns> public MusicArtist GetArtist(string name) { - return CreateItemByName<MusicArtist>(MusicArtist.GetPath, name); + return GetArtist(name, new DtoOptions(true)); } - private T CreateItemByName<T>(Func<string, string> getPathFn, string name) + public MusicArtist GetArtist(string name, DtoOptions options) + { + return CreateItemByName<MusicArtist>(MusicArtist.GetPath, name, options); + } + + private T CreateItemByName<T>(Func<string, string> 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<MusicArtist>() .OrderBy(i => i.IsAccessedByName ? 1 : 0) @@ -1029,54 +1034,6 @@ namespace Emby.Server.Implementations.Library return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId); } - public IEnumerable<MusicArtist> GetAlbumArtists(IEnumerable<IHasAlbumArtist> 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<MusicArtist> GetArtists(IEnumerable<IHasArtist> 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; - } - /// <summary> /// 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 89b772731..83db58668 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 fa505d3fb..48ad7cf12 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 5d489985f..c1e1bf2b6 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 dd63bbb72..03c21b120 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<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>(); - 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<bool> openTaskCompletionSource, CancellationToken cancellationToken) + private Task StartStreaming(string url, TaskCompletionSource<bool> 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<bool> 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 2c678d9f8..3202b7313 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<Tuple<string, string>> GetCommands() @@ -57,7 +59,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var commands = new List<Tuple<string, string>>(); 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 c84aebce7..ff6c0fb2c 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<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>(); - 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<bool> openTaskCompletionSource, CancellationToken cancellationToken) + private Task StartStreaming(string remoteIp, int localPort, TaskCompletionSource<bool> 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<bool> 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 8cf1106f0..2c6641ee1 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 3c2af60db..22d2b95f7 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); } /// <summary> /// Writes to response. /// Response headers are customizable by implementing IHasHeaders an returning Dictionary of Http headers. /// </summary> - /// <param name="response">The response.</param> - /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param> - /// <param name="request">The serialization context.</param> - /// <returns></returns> - 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 8b59b4843..95a9dd682 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) |
