diff options
Diffstat (limited to 'MediaBrowser.Api/LiveTv')
| -rw-r--r-- | MediaBrowser.Api/LiveTv/LiveTvService.cs | 42 | ||||
| -rw-r--r-- | MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs | 164 |
2 files changed, 187 insertions, 19 deletions
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 837a0f6a6..36bcee913 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -15,14 +15,14 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Api.UserLibrary; using MediaBrowser.Model.IO; -using MediaBrowser.Api.Playback.Progressive; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.IO; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; +using MediaBrowser.Model.Extensions; namespace MediaBrowser.Api.LiveTv { @@ -374,7 +374,7 @@ namespace MediaBrowser.Api.LiveTv public string SortBy { get; set; } [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public SortOrder? SortOrder { get; set; } + public string SortOrder { get; set; } [ApiMember(Name = "Genres", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string Genres { get; set; } @@ -649,7 +649,7 @@ namespace MediaBrowser.Api.LiveTv { public List<TunerChannelMapping> TunerChannels { get; set; } public List<NameIdPair> ProviderChannels { get; set; } - public List<NameValuePair> Mappings { get; set; } + public NameValuePair[] Mappings { get; set; } public string ProviderName { get; set; } } @@ -734,7 +734,7 @@ namespace MediaBrowser.Api.LiveTv outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path); - return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, _environment, CancellationToken.None) + return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, Logger, _environment, CancellationToken.None) { AllowEndOfFile = false }; @@ -753,7 +753,7 @@ namespace MediaBrowser.Api.LiveTv outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container); - return new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, _environment, CancellationToken.None) + return new ProgressiveFileCopier(directStreamProvider, outputHeaders, Logger, _environment, CancellationToken.None) { AllowEndOfFile = false }; @@ -790,7 +790,7 @@ namespace MediaBrowser.Api.LiveTv var providerChannels = await _liveTvManager.GetChannelsFromListingsProviderData(request.ProviderId, CancellationToken.None) .ConfigureAwait(false); - var mappings = listingsProviderInfo.ChannelMappings.ToList(); + var mappings = listingsProviderInfo.ChannelMappings; var result = new ChannelMappingOptions { @@ -853,6 +853,8 @@ namespace MediaBrowser.Api.LiveTv public async Task<object> Post(AddTunerHost request) { + request.EnableNewHdhrChannelIds = true; + var result = await _liveTvManager.SaveTunerHost(request).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -861,7 +863,7 @@ namespace MediaBrowser.Api.LiveTv { var config = GetConfiguration(); - config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(request.Id, i.Id, StringComparison.OrdinalIgnoreCase)).ToList(); + config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(request.Id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray(); _config.SaveConfiguration("livetv", config); } @@ -921,7 +923,7 @@ namespace MediaBrowser.Api.LiveTv options.AddCurrentProgram = request.AddCurrentProgram; - var returnArray = (await _dtoService.GetBaseItemDtos(channelResult.Items, options, user).ConfigureAwait(false)).ToArray(); + var returnArray = _dtoService.GetBaseItemDtos(channelResult.Items, options, user); var result = new QueryResult<BaseItemDto> { @@ -934,10 +936,13 @@ namespace MediaBrowser.Api.LiveTv private void RemoveFields(DtoOptions options) { - options.Fields.Remove(ItemFields.CanDelete); - options.Fields.Remove(ItemFields.CanDownload); - options.Fields.Remove(ItemFields.DisplayPreferencesId); - options.Fields.Remove(ItemFields.Etag); + var fields = options.Fields.ToList(); + + fields.Remove(ItemFields.CanDelete); + fields.Remove(ItemFields.CanDownload); + fields.Remove(ItemFields.DisplayPreferencesId); + fields.Remove(ItemFields.Etag); + options.Fields = fields.ToArray(fields.Count); } public object Get(GetChannel request) @@ -962,7 +967,7 @@ namespace MediaBrowser.Api.LiveTv { var query = new ProgramQuery { - ChannelIds = (request.ChannelIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToArray(), + ChannelIds = ApiEntryPoint.Split(request.ChannelIds, ',', true), UserId = request.UserId, HasAired = request.HasAired, EnableTotalRecordCount = request.EnableTotalRecordCount @@ -990,8 +995,7 @@ namespace MediaBrowser.Api.LiveTv query.StartIndex = request.StartIndex; query.Limit = request.Limit; - query.SortBy = (request.SortBy ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - query.SortOrder = request.SortOrder; + query.OrderBy = BaseItemsRequest.GetOrderBy(request.SortBy, request.SortOrder); query.IsNews = request.IsNews; query.IsMovie = request.IsMovie; query.IsSeries = request.IsSeries; @@ -1070,12 +1074,12 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(result); } - public async Task<object> Get(GetRecordingSeries request) + public object Get(GetRecordingSeries request) { var options = GetDtoOptions(_authContext, request); options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; - var result = await _liveTvManager.GetRecordingSeries(new RecordingQuery + var result = _liveTvManager.GetRecordingSeries(new RecordingQuery { ChannelId = request.ChannelId, UserId = request.UserId, @@ -1087,7 +1091,7 @@ namespace MediaBrowser.Api.LiveTv IsInProgress = request.IsInProgress, EnableTotalRecordCount = request.EnableTotalRecordCount - }, options, CancellationToken.None).ConfigureAwait(false); + }, options, CancellationToken.None); return ToOptimizedResult(result); } diff --git a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs new file mode 100644 index 000000000..9ce109fc4 --- /dev/null +++ b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; + +namespace MediaBrowser.Api.LiveTv +{ + public class ProgressiveFileCopier : IAsyncStreamWriter, IHasHeaders + { + private readonly IFileSystem _fileSystem; + private readonly ILogger _logger; + private readonly string _path; + private readonly CancellationToken _cancellationToken; + private readonly Dictionary<string, string> _outputHeaders; + + 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<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken) + { + _fileSystem = fileSystem; + _path = path; + _outputHeaders = outputHeaders; + _logger = logger; + _cancellationToken = cancellationToken; + _environment = environment; + } + + public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment, CancellationToken cancellationToken) + { + _directStreamProvider = directStreamProvider; + _outputHeaders = outputHeaders; + _logger = logger; + _cancellationToken = cancellationToken; + _environment = environment; + } + + public IDictionary<string, string> Headers + { + get + { + return _outputHeaders; + } + } + + private Stream GetInputStream(bool allowAsyncFileRead) + { + var fileOpenOptions = 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; + + if (_directStreamProvider != null) + { + await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false); + return; + } + + var eofCount = 0; + + // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 + var allowAsyncFileRead = _environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows; + + using (var inputStream = GetInputStream(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<int> 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); + + _bytesWritten += bytesRead; + totalBytesRead += bytesRead; + } + } + + return totalBytesRead; + } + + private async Task<int> 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); + + _bytesWritten += bytesRead; + totalBytesRead += bytesRead; + } + } + + return totalBytesRead; + } + } +} |
