aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2016-10-05 11:20:48 -0400
committerGitHub <noreply@github.com>2016-10-05 11:20:48 -0400
commita2fa7c475404347aaf675bdd708a69c451f5af2e (patch)
treee9aa273f5d6d77473d3d1f345e2890c864a0e481 /MediaBrowser.Server.Implementations
parent0a87046736843dfa983213ae1e661d499a6d1df9 (diff)
parent83606d82d57604f9796455640d2e93367783f69e (diff)
Merge pull request #2210 from MediaBrowser/dev
Dev
Diffstat (limited to 'MediaBrowser.Server.Implementations')
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs19
-rw-r--r--MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs19
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs11
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs46
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs11
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs79
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs4
8 files changed, 165 insertions, 26 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
index 6cba1b441..fae78b9bc 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
@@ -30,7 +30,7 @@ namespace MediaBrowser.Server.Implementations.Channels
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
}
- public Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken)
+ public Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 93ee91c21..b2bddc70d 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -43,6 +43,7 @@ using MediaBrowser.Server.Implementations.Library.Resolvers;
using MoreLinq;
using SortOrder = MediaBrowser.Model.Entities.SortOrder;
using VideoResolver = MediaBrowser.Naming.Video.VideoResolver;
+using MediaBrowser.Common.Configuration;
namespace MediaBrowser.Server.Implementations.Library
{
@@ -1900,6 +1901,24 @@ namespace MediaBrowser.Server.Implementations.Library
options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders;
}
+ if (options.SchemaVersion < 2)
+ {
+ var chapterOptions = ConfigurationManager.GetConfiguration<ChapterOptions>("chapters");
+ options.ExtractChapterImagesDuringLibraryScan = chapterOptions.ExtractDuringLibraryScan;
+
+ if (collectionFolder != null)
+ {
+ if (string.Equals(collectionFolder.CollectionType, "movies", StringComparison.OrdinalIgnoreCase))
+ {
+ options.EnableChapterImageExtraction = chapterOptions.EnableMovieChapterImageExtraction;
+ }
+ else if (string.Equals(collectionFolder.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+ {
+ options.EnableChapterImageExtraction = chapterOptions.EnableEpisodeChapterImageExtraction;
+ }
+ }
+ }
+
return options;
}
diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
index ae32bdaf7..e7bfe56f2 100644
--- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
@@ -367,7 +367,9 @@ namespace MediaBrowser.Server.Implementations.Library
var tuple = GetProvider(request.OpenToken);
var provider = tuple.Item1;
- var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
+ var mediaSourceTuple = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
+
+ var mediaSource = mediaSourceTuple.Item1;
if (string.IsNullOrWhiteSpace(mediaSource.LiveStreamId))
{
@@ -381,8 +383,10 @@ namespace MediaBrowser.Server.Implementations.Library
Date = DateTime.UtcNow,
EnableCloseTimer = enableAutoClose,
Id = mediaSource.LiveStreamId,
- MediaSource = mediaSource
+ MediaSource = mediaSource,
+ DirectStreamProvider = mediaSourceTuple.Item2
};
+
_openStreams[mediaSource.LiveStreamId] = info;
if (enableAutoClose)
@@ -414,7 +418,7 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
- public async Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken)
+ public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(id))
{
@@ -430,7 +434,7 @@ namespace MediaBrowser.Server.Implementations.Library
LiveStreamInfo info;
if (_openStreams.TryGetValue(id, out info))
{
- return info.MediaSource;
+ return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info.DirectStreamProvider);
}
else
{
@@ -443,6 +447,12 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
+ public async Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken)
+ {
+ var result = await GetLiveStreamWithDirectStreamProvider(id, cancellationToken).ConfigureAwait(false);
+ return result.Item1;
+ }
+
public async Task PingLiveStream(string id, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -630,6 +640,7 @@ namespace MediaBrowser.Server.Implementations.Library
public string Id;
public bool Closed;
public MediaSourceInfo MediaSource;
+ public IDirectStreamProvider DirectStreamProvider;
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index a3ba2c6f8..5b99849cd 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -36,7 +36,7 @@ using Microsoft.Win32;
namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
- public class EmbyTV : ILiveTvService, ISupportsNewTimerIds, IDisposable
+ public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable
{
private readonly IApplicationHost _appHpst;
private readonly ILogger _logger;
@@ -829,9 +829,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
{
+ var result = await GetChannelStreamWithDirectStreamProvider(channelId, streamId, cancellationToken).ConfigureAwait(false);
+
+ return result.Item1;
+ }
+
+ public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, CancellationToken cancellationToken)
+ {
var result = await GetChannelStreamInternal(channelId, streamId, cancellationToken).ConfigureAwait(false);
- return result.Item2;
+ return new Tuple<MediaSourceInfo, IDirectStreamProvider>(result.Item2, result.Item1 as IDirectStreamProvider);
}
private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId, bool enableStreamSharing)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 84b162286..546525fd1 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -227,10 +227,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<MediaSourceInfo> GetRecordingStream(string id, CancellationToken cancellationToken)
{
- return await GetLiveStream(id, null, false, cancellationToken).ConfigureAwait(false);
+ var info = await GetLiveStream(id, null, false, cancellationToken).ConfigureAwait(false);
+
+ return info.Item1;
}
- public async Task<MediaSourceInfo> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken)
+ public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken)
{
return await GetLiveStream(id, mediaSourceId, true, cancellationToken).ConfigureAwait(false);
}
@@ -280,7 +282,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return _services.FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
}
- private async Task<MediaSourceInfo> GetLiveStream(string id, string mediaSourceId, bool isChannel, CancellationToken cancellationToken)
+ private async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStream(string id, string mediaSourceId, bool isChannel, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -294,6 +296,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
MediaSourceInfo info;
bool isVideo;
ILiveTvService service;
+ IDirectStreamProvider directStreamProvider = null;
if (isChannel)
{
@@ -301,7 +304,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
isVideo = channel.ChannelType == ChannelType.TV;
service = GetService(channel);
_logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
- info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
+
+ var supportsManagedStream = service as ISupportsDirectStreamProvider;
+ if (supportsManagedStream != null)
+ {
+ var streamInfo = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
+ info = streamInfo.Item1;
+ directStreamProvider = streamInfo.Item2;
+ }
+ else
+ {
+ info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
+ }
info.RequiresClosing = true;
if (info.RequiresClosing)
@@ -332,7 +346,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info));
Normalize(info, service, isVideo);
- return info;
+ return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info, directStreamProvider);
}
catch (Exception ex)
{
@@ -544,11 +558,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return item;
}
- private async Task<LiveTvProgram> GetProgram(ProgramInfo info, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
+ private async Task<LiveTvProgram> GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
{
var id = _tvDtoService.GetInternalProgramId(serviceName, info.Id);
- var item = _libraryManager.GetItemById(id) as LiveTvProgram;
+ LiveTvProgram item = null;
+ allExistingPrograms.TryGetValue(id, out item);
+
var isNew = false;
var forceUpdate = false;
@@ -877,7 +893,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (!string.IsNullOrWhiteSpace(query.SeriesTimerId))
{
- var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery {}, cancellationToken).ConfigureAwait(false);
+ var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery { }, cancellationToken).ConfigureAwait(false);
var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.ServiceName, i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase));
if (seriesTimer != null)
{
@@ -1265,9 +1281,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var channelPrograms = await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false);
+ var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+
+ IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+ ChannelIds = new string[] { currentChannel.Id.ToString("N") }
+
+ }).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
+
foreach (var program in channelPrograms)
{
- var programItem = await GetProgram(program, currentChannel, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false);
+ var programItem = await GetProgram(program, existingPrograms, currentChannel, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false);
programs.Add(programItem.Id);
@@ -1881,11 +1905,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
if (query.IsScheduled.Value)
{
- timers = timers.Where(i => i.Item1.Status == RecordingStatus.New || i.Item1.Status == RecordingStatus.Scheduled);
+ timers = timers.Where(i => i.Item1.Status == RecordingStatus.New);
}
else
{
- timers = timers.Where(i => !(i.Item1.Status == RecordingStatus.New || i.Item1.Status == RecordingStatus.Scheduled));
+ timers = timers.Where(i => !(i.Item1.Status == RecordingStatus.New));
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index aacc0c22b..1861b79eb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -116,17 +116,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return list;
}
- public async Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken)
+ public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken)
{
- MediaSourceInfo stream;
+ MediaSourceInfo stream = null;
const bool isAudio = false;
var keys = openToken.Split(new[] { StreamIdDelimeter }, 3);
var mediaSourceId = keys.Length >= 3 ? keys[2] : null;
+ IDirectStreamProvider directStreamProvider = null;
if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase))
{
- stream = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, cancellationToken).ConfigureAwait(false);
+ var info = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, cancellationToken).ConfigureAwait(false);
+ stream = info.Item1;
+ directStreamProvider = info.Item2;
}
else
{
@@ -142,7 +145,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_logger.ErrorException("Error probing live tv stream", ex);
}
- return stream;
+ return new Tuple<MediaSourceInfo, IDirectStreamProvider>(stream, directStreamProvider);
}
private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
index d49e3f258..dd635bd55 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
@@ -6,14 +6,16 @@ using CommonIO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
+using System.Collections.Generic;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
- public class HdHomerunLiveStream : LiveStream
+ public class HdHomerunLiveStream : LiveStream, IDirectStreamProvider
{
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
@@ -106,7 +108,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
onStarted = () => ResolveWhenExists(openTaskCompletionSource, tempFilePath, cancellationToken);
}
- await DirectRecorder.CopyUntilCancelled(response.Content, outputStream, onStarted, cancellationToken).ConfigureAwait(false);
+ await CopyUntilCancelled(response.Content, outputStream, onStarted, cancellationToken).ConfigureAwait(false);
}
}
}
@@ -137,6 +139,79 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}).ConfigureAwait(false);
}
+ private List<Tuple<Stream, CancellationToken, TaskCompletionSource<bool>>> _additionalStreams = new List<Tuple<Stream, CancellationToken, TaskCompletionSource<bool>>>();
+
+ public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ var taskCompletionSource = new TaskCompletionSource<bool>();
+ _additionalStreams.Add(new Tuple<Stream, CancellationToken, TaskCompletionSource<bool>>(stream, cancellationToken, taskCompletionSource));
+
+ return taskCompletionSource.Task;
+ }
+
+ private void PopAdditionalStream(Tuple<Stream, CancellationToken, TaskCompletionSource<bool>> stream, Exception exception)
+ {
+ if (_additionalStreams.Remove(stream))
+ {
+ stream.Item3.TrySetException(exception);
+ }
+ }
+
+ private const int BufferSize = 81920;
+ private async Task CopyUntilCancelled(Stream source, Stream target, Action onStarted, CancellationToken cancellationToken)
+ {
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ var bytesRead = await CopyToAsyncInternal(source, target, BufferSize, onStarted, cancellationToken).ConfigureAwait(false);
+
+ onStarted = null;
+
+ //var position = fs.Position;
+ //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
+
+ if (bytesRead == 0)
+ {
+ await Task.Delay(100).ConfigureAwait(false);
+ }
+ }
+ }
+
+ private async Task<int> CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, Action onStarted, CancellationToken cancellationToken)
+ {
+ byte[] buffer = new byte[bufferSize];
+ int bytesRead;
+ int totalBytesRead = 0;
+
+ while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ {
+ await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
+
+ foreach (var additionalStream in _additionalStreams)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ try
+ {
+ await additionalStream.Item1.WriteAsync(buffer, 0, bytesRead, additionalStream.Item2).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ PopAdditionalStream(additionalStream, ex);
+ }
+ }
+
+ totalBytesRead += bytesRead;
+
+ if (onStarted != null)
+ {
+ onStarted();
+ }
+ onStarted = null;
+ }
+
+ return totalBytesRead;
+ }
+
private async void ResolveWhenExists(TaskCompletionSource<bool> taskCompletionSource, string file, CancellationToken cancellationToken)
{
while (!File.Exists(file) && !cancellationToken.IsCancellationRequested)
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
index cb666b6ac..e0553b1b1 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
@@ -99,7 +99,7 @@ namespace MediaBrowser.Server.Implementations.Sync
// Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
private const string StreamIdDelimeterString = "_";
- public async Task<MediaSourceInfo> OpenMediaSource(string openToken, CancellationToken cancellationToken)
+ public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken)
{
var openKeys = openToken.Split(new[] { StreamIdDelimeterString[0] }, 3);
@@ -137,7 +137,7 @@ namespace MediaBrowser.Server.Implementations.Sync
mediaSource.Protocol = dynamicInfo.Protocol;
mediaSource.RequiredHttpHeaders = dynamicInfo.RequiredHttpHeaders;
- return mediaSource;
+ return new Tuple<MediaSourceInfo, IDirectStreamProvider>(mediaSource, null);
}
private void SetStaticMediaSourceInfo(LocalItem item, MediaSourceInfo mediaSource)