implement modular media sources

This commit is contained in:
Luke Pulverenti 2015-03-28 16:22:27 -04:00
parent 3add1872c8
commit bd2ea703e3
42 changed files with 702 additions and 371 deletions

View File

@ -1,6 +1,5 @@
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
@ -65,7 +64,6 @@ namespace MediaBrowser.Api.Playback
protected IFileSystem FileSystem { get; private set; } protected IFileSystem FileSystem { get; private set; }
protected ILiveTvManager LiveTvManager { get; private set; }
protected IDlnaManager DlnaManager { get; private set; } protected IDlnaManager DlnaManager { get; private set; }
protected IDeviceManager DeviceManager { get; private set; } protected IDeviceManager DeviceManager { get; private set; }
protected ISubtitleEncoder SubtitleEncoder { get; private set; } protected ISubtitleEncoder SubtitleEncoder { get; private set; }
@ -75,14 +73,13 @@ namespace MediaBrowser.Api.Playback
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class. /// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
/// </summary> /// </summary>
protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient)
{ {
ZipClient = zipClient; ZipClient = zipClient;
MediaSourceManager = mediaSourceManager; MediaSourceManager = mediaSourceManager;
DeviceManager = deviceManager; DeviceManager = deviceManager;
SubtitleEncoder = subtitleEncoder; SubtitleEncoder = subtitleEncoder;
DlnaManager = dlnaManager; DlnaManager = dlnaManager;
LiveTvManager = liveTvManager;
FileSystem = fileSystem; FileSystem = fileSystem;
ServerConfigurationManager = serverConfig; ServerConfigurationManager = serverConfig;
UserManager = userManager; UserManager = userManager;
@ -95,11 +92,10 @@ namespace MediaBrowser.Api.Playback
/// Gets the command line arguments. /// Gets the command line arguments.
/// </summary> /// </summary>
/// <param name="outputPath">The output path.</param> /// <param name="outputPath">The output path.</param>
/// <param name="transcodingJobId">The transcoding job identifier.</param>
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param> /// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
protected abstract string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding); protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding);
/// <summary> /// <summary>
/// Gets the type of the transcoding job. /// Gets the type of the transcoding job.
@ -128,7 +124,7 @@ namespace MediaBrowser.Api.Playback
var outputFileExtension = GetOutputFileExtension(state); var outputFileExtension = GetOutputFileExtension(state);
var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false); var data = GetCommandLineArguments("dummy\\dummy", state, false);
data += "-" + (state.Request.DeviceId ?? string.Empty); data += "-" + (state.Request.DeviceId ?? string.Empty);
data += "-" + (state.Request.StreamId ?? string.Empty); data += "-" + (state.Request.StreamId ?? string.Empty);
@ -719,8 +715,10 @@ namespace MediaBrowser.Api.Playback
seconds.ToString(UsCulture)); seconds.ToString(UsCulture));
} }
var mediaPath = state.MediaPath ?? string.Empty;
return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB", return string.Format("subtitles='{0}:si={1}',setpts=PTS -{2}/TB",
state.MediaPath.Replace('\\', '/').Replace(":/", "\\:/"), mediaPath.Replace('\\', '/').Replace(":/", "\\:/"),
state.InternalSubtitleStreamOffset.ToString(UsCulture), state.InternalSubtitleStreamOffset.ToString(UsCulture),
seconds.ToString(UsCulture)); seconds.ToString(UsCulture));
} }
@ -895,12 +893,11 @@ namespace MediaBrowser.Api.Playback
/// <summary> /// <summary>
/// Gets the input argument. /// Gets the input argument.
/// </summary> /// </summary>
/// <param name="transcodingJobId">The transcoding job identifier.</param>
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
protected string GetInputArgument(string transcodingJobId, StreamState state) protected string GetInputArgument(StreamState state)
{ {
var arg = "-i " + GetInputPathArgument(transcodingJobId, state); var arg = "-i " + GetInputPathArgument(state);
if (state.SubtitleStream != null) if (state.SubtitleStream != null)
{ {
@ -913,27 +910,18 @@ namespace MediaBrowser.Api.Playback
return arg; return arg;
} }
private string GetInputPathArgument(string transcodingJobId, StreamState state) private string GetInputPathArgument(StreamState state)
{ {
//if (state.InputProtocol == MediaProtocol.File &&
// state.RunTimeTicks.HasValue &&
// state.VideoType == VideoType.VideoFile &&
// !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
//{
// if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo)
// {
// }
//}
var protocol = state.InputProtocol; var protocol = state.InputProtocol;
var mediaPath = state.MediaPath ?? string.Empty;
var inputPath = new[] { state.MediaPath }; var inputPath = new[] { mediaPath };
if (state.IsInputVideo) if (state.IsInputVideo)
{ {
if (!(state.VideoType == VideoType.Iso && state.IsoMount == null)) if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
{ {
inputPath = MediaEncoderHelpers.GetInputArgument(state.MediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames); inputPath = MediaEncoderHelpers.GetInputArgument(mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
} }
} }
@ -947,55 +935,20 @@ namespace MediaBrowser.Api.Playback
state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false); state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
} }
if (string.IsNullOrEmpty(state.MediaPath)) if (state.MediaSource.RequiresOpening)
{ {
var checkCodecs = false; var mediaSource = await MediaSourceManager.OpenMediaSource(state.MediaSource.OpenKey, cancellationTokenSource.Token)
.ConfigureAwait(false);
if (string.Equals(state.ItemType, typeof(LiveTvChannel).Name)) AttachMediaSourceInfo(state, mediaSource, state.VideoRequest, state.RequestedUrl);
if (state.VideoRequest != null)
{ {
var streamInfo = await LiveTvManager.GetChannelStream(state.Request.Id, cancellationTokenSource.Token).ConfigureAwait(false); TryStreamCopy(state, state.VideoRequest);
state.LiveTvStreamId = streamInfo.Id;
state.MediaPath = streamInfo.Path;
state.InputProtocol = streamInfo.Protocol;
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl);
checkCodecs = true;
} }
else if (string.Equals(state.ItemType, typeof(LiveTvVideoRecording).Name) || // TODO: This is only needed for live tv
string.Equals(state.ItemType, typeof(LiveTvAudioRecording).Name)) await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
{
var streamInfo = await LiveTvManager.GetRecordingStream(state.Request.Id, cancellationTokenSource.Token).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
state.MediaPath = streamInfo.Path;
state.InputProtocol = streamInfo.Protocol;
await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl);
checkCodecs = true;
}
var videoRequest = state.VideoRequest;
if (videoRequest != null && checkCodecs)
{
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
}
} }
} }
@ -1017,7 +970,7 @@ namespace MediaBrowser.Api.Playback
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false); await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
var transcodingId = Guid.NewGuid().ToString("N"); var transcodingId = Guid.NewGuid().ToString("N");
var commandLineArgs = GetCommandLineArguments(outputPath, transcodingId, state, true); var commandLineArgs = GetCommandLineArguments(outputPath, state, true);
if (ApiEntryPoint.Instance.GetEncodingOptions().EnableDebugLogging) if (ApiEntryPoint.Instance.GetEncodingOptions().EnableDebugLogging)
{ {
@ -1644,7 +1597,7 @@ namespace MediaBrowser.Api.Playback
request.AudioCodec = InferAudioCodec(url); request.AudioCodec = InferAudioCodec(url);
} }
var state = new StreamState(LiveTvManager, Logger) var state = new StreamState(MediaSourceManager, Logger)
{ {
Request = request, Request = request,
RequestedUrl = url RequestedUrl = url
@ -1658,109 +1611,20 @@ namespace MediaBrowser.Api.Playback
var item = LibraryManager.GetItemById(request.Id); var item = LibraryManager.GetItemById(request.Id);
List<MediaStream> mediaStreams = null; state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
state.ItemType = item.GetType().Name;
state.ItemId = item.Id.ToString("N");
var archivable = item as IArchivable; var archivable = item as IArchivable;
state.IsInputArchive = archivable != null && archivable.IsArchive; state.IsInputArchive = archivable != null && archivable.IsArchive;
if (item is ILiveTvRecording) var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false);
{
var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false);
state.VideoType = VideoType.VideoFile;
state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
var path = recording.RecordingInfo.Path;
var mediaUrl = recording.RecordingInfo.Url;
var source = string.IsNullOrEmpty(request.MediaSourceId)
? recording.GetMediaSources(false).First()
: MediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false);
mediaStreams = source.MediaStreams;
// Just to prevent this from being null and causing other methods to fail
state.MediaPath = string.Empty;
if (!string.IsNullOrEmpty(path))
{
state.MediaPath = path;
state.InputProtocol = MediaProtocol.File;
}
else if (!string.IsNullOrEmpty(mediaUrl))
{
state.MediaPath = mediaUrl;
state.InputProtocol = MediaProtocol.Http;
}
state.RunTimeTicks = recording.RunTimeTicks;
state.DeInterlace = true;
state.OutputAudioSync = "1000";
state.InputVideoSync = "-1";
state.InputAudioSync = "1";
state.InputContainer = recording.Container;
state.ReadInputAtNativeFramerate = source.ReadAtNativeFramerate;
}
else if (item is LiveTvChannel)
{
var channel = LiveTvManager.GetInternalChannel(request.Id);
state.VideoType = VideoType.VideoFile;
state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
mediaStreams = new List<MediaStream>();
state.DeInterlace = true;
// Just to prevent this from being null and causing other methods to fail
state.MediaPath = string.Empty;
}
else
{
var mediaSources = await MediaSourceManager.GetPlayackMediaSources(request.Id, false, cancellationToken).ConfigureAwait(false);
var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources.First()
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
mediaStreams = mediaSource.MediaStreams;
state.MediaPath = mediaSource.Path;
state.InputProtocol = mediaSource.Protocol;
state.InputContainer = mediaSource.Container;
state.InputFileSize = mediaSource.Size;
state.InputBitrate = mediaSource.Bitrate;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
state.RunTimeTicks = mediaSource.RunTimeTicks;
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
var video = item as Video;
if (video != null)
{
state.IsInputVideo = true;
if (mediaSource.VideoType.HasValue)
{
state.VideoType = mediaSource.VideoType.Value;
}
state.IsoType = mediaSource.IsoType;
state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
if (mediaSource.Timestamp.HasValue)
{
state.InputTimestamp = mediaSource.Timestamp.Value;
}
}
}
var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
? mediaSources.First()
: mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
var videoRequest = request as VideoStreamRequest; var videoRequest = request as VideoStreamRequest;
AttachMediaStreamInfo(state, mediaStreams, videoRequest, url); AttachMediaSourceInfo(state, mediaSource, videoRequest, url);
var container = Path.GetExtension(state.RequestedUrl); var container = Path.GetExtension(state.RequestedUrl);
@ -1801,15 +1665,7 @@ namespace MediaBrowser.Api.Playback
if (videoRequest != null) if (videoRequest != null)
{ {
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) TryStreamCopy(state, videoRequest);
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
} }
state.OutputFilePath = GetOutputFilePath(state); state.OutputFilePath = GetOutputFilePath(state);
@ -1817,11 +1673,47 @@ namespace MediaBrowser.Api.Playback
return state; return state;
} }
private void AttachMediaStreamInfo(StreamState state, private void TryStreamCopy(StreamState state, VideoStreamRequest videoRequest)
{
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
{
state.OutputAudioCodec = "copy";
}
}
private void AttachMediaSourceInfo(StreamState state,
MediaSourceInfo mediaSource, MediaSourceInfo mediaSource,
VideoStreamRequest videoRequest, VideoStreamRequest videoRequest,
string requestedUrl) string requestedUrl)
{ {
state.MediaPath = mediaSource.Path;
state.InputProtocol = mediaSource.Protocol;
state.InputContainer = mediaSource.Container;
state.InputFileSize = mediaSource.Size;
state.InputBitrate = mediaSource.Bitrate;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
state.RunTimeTicks = mediaSource.RunTimeTicks;
state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
if (mediaSource.VideoType.HasValue)
{
state.VideoType = mediaSource.VideoType.Value;
}
state.IsoType = mediaSource.IsoType;
state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList();
if (mediaSource.Timestamp.HasValue)
{
state.InputTimestamp = mediaSource.Timestamp.Value;
}
state.InputProtocol = mediaSource.Protocol; state.InputProtocol = mediaSource.Protocol;
state.MediaPath = mediaSource.Path; state.MediaPath = mediaSource.Path;
state.RunTimeTicks = mediaSource.RunTimeTicks; state.RunTimeTicks = mediaSource.RunTimeTicks;
@ -1830,21 +1722,16 @@ namespace MediaBrowser.Api.Playback
state.InputFileSize = mediaSource.Size; state.InputFileSize = mediaSource.Size;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
if (state.ReadInputAtNativeFramerate) if (state.ReadInputAtNativeFramerate ||
mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))
{ {
state.OutputAudioSync = "1000"; state.OutputAudioSync = "1000";
state.InputVideoSync = "-1"; state.InputVideoSync = "-1";
state.InputAudioSync = "1"; state.InputAudioSync = "1";
} }
AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest, requestedUrl); var mediaStreams = mediaSource.MediaStreams;
}
private void AttachMediaStreamInfo(StreamState state,
List<MediaStream> mediaStreams,
VideoStreamRequest videoRequest,
string requestedUrl)
{
if (videoRequest != null) if (videoRequest != null)
{ {
if (string.IsNullOrEmpty(videoRequest.VideoCodec)) if (string.IsNullOrEmpty(videoRequest.VideoCodec))
@ -1873,7 +1760,7 @@ namespace MediaBrowser.Api.Playback
state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
} }
state.AllMediaStreams = mediaStreams; state.MediaSource = mediaSource;
} }
private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)

View File

@ -54,7 +54,7 @@ namespace MediaBrowser.Api.Playback.Dash
public class MpegDashService : BaseHlsService public class MpegDashService : BaseHlsService
{ {
public MpegDashService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) public MpegDashService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{ {
NetworkManager = networkManager; NetworkManager = networkManager;
} }
@ -447,7 +447,7 @@ namespace MediaBrowser.Api.Playback.Dash
return args; return args;
} }
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{ {
// test url http://192.168.1.2:8096/videos/233e8905d559a8f230db9bffd2ac9d6d/master.mpd?mediasourceid=233e8905d559a8f230db9bffd2ac9d6d&videocodec=h264&audiocodec=aac&maxwidth=1280&videobitrate=500000&audiobitrate=128000&profile=baseline&level=3 // test url http://192.168.1.2:8096/videos/233e8905d559a8f230db9bffd2ac9d6d/master.mpd?mediasourceid=233e8905d559a8f230db9bffd2ac9d6d&videocodec=h264&audiocodec=aac&maxwidth=1280&videobitrate=500000&audiobitrate=128000&profile=baseline&level=3
// Good info on i-frames http://blog.streamroot.io/encode-multi-bitrate-videos-mpeg-dash-mse-based-media-players/ // Good info on i-frames http://blog.streamroot.io/encode-multi-bitrate-videos-mpeg-dash-mse-based-media-players/
@ -461,7 +461,7 @@ namespace MediaBrowser.Api.Playback.Dash
var args = string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts {5} -f dash -init_seg_name \"{6}\" -media_seg_name \"{7}\" -use_template 0 -use_timeline 1 -min_seg_duration {8} -y \"{9}\"", var args = string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts {5} -f dash -init_seg_name \"{6}\" -media_seg_name \"{7}\" -use_template 0 -use_timeline 1 -min_seg_duration {8} -y \"{9}\"",
inputModifier, inputModifier,
GetInputArgument(transcodingJobId, state), GetInputArgument(state),
threads, threads,
GetMapArgs(state), GetMapArgs(state),
GetVideoArguments(state), GetVideoArguments(state),

View File

@ -22,7 +22,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary> /// </summary>
public abstract class BaseHlsService : BaseStreamingService public abstract class BaseHlsService : BaseStreamingService
{ {
protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{ {
} }
@ -212,7 +212,7 @@ namespace MediaBrowser.Api.Playback.Hls
} }
} }
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{ {
var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream; var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
@ -240,7 +240,7 @@ namespace MediaBrowser.Api.Playback.Hls
var args = string.Format("{0} {1} {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"", var args = string.Format("{0} {1} {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
itsOffset, itsOffset,
inputModifier, inputModifier,
GetInputArgument(transcodingJobId, state), GetInputArgument(state),
threads, threads,
GetMapArgs(state), GetMapArgs(state),
GetVideoArguments(state), GetVideoArguments(state),

View File

@ -62,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Hls
public class DynamicHlsService : BaseHlsService public class DynamicHlsService : BaseHlsService
{ {
public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{ {
NetworkManager = networkManager; NetworkManager = networkManager;
} }
@ -414,7 +414,8 @@ namespace MediaBrowser.Api.Playback.Hls
var request = (GetMasterHlsVideoStream)state.Request; var request = (GetMasterHlsVideoStream)state.Request;
var subtitleStreams = state.AllMediaStreams var subtitleStreams = state.MediaSource
.MediaStreams
.Where(i => i.IsTextSubtitleStream) .Where(i => i.IsTextSubtitleStream)
.ToList(); .ToList();
@ -684,7 +685,7 @@ namespace MediaBrowser.Api.Playback.Hls
return args; return args;
} }
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{ {
var threads = GetNumberOfThreads(state, false); var threads = GetNumberOfThreads(state, false);
@ -699,7 +700,7 @@ namespace MediaBrowser.Api.Playback.Hls
return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -f segment -segment_time {6} -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
inputModifier, inputModifier,
GetInputArgument(transcodingJobId, state), GetInputArgument(state),
threads, threads,
GetMapArgs(state), GetMapArgs(state),
GetVideoArguments(state), GetVideoArguments(state),
@ -713,7 +714,7 @@ namespace MediaBrowser.Api.Playback.Hls
return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", return string.Format("{0} {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"",
inputModifier, inputModifier,
GetInputArgument(transcodingJobId, state), GetInputArgument(state),
threads, threads,
GetMapArgs(state), GetMapArgs(state),
GetVideoArguments(state), GetVideoArguments(state),

View File

@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary> /// </summary>
public class VideoHlsService : BaseHlsService public class VideoHlsService : BaseHlsService
{ {
public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{ {
} }

View File

@ -4,6 +4,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using ServiceStack; using ServiceStack;
@ -38,20 +39,23 @@ namespace MediaBrowser.Api.Playback
[Route("/Items/{Id}/PlaybackInfo", "POST", Summary = "Gets live playback media info for an item")] [Route("/Items/{Id}/PlaybackInfo", "POST", Summary = "Gets live playback media info for an item")]
public class GetPostedPlaybackInfo : PlaybackInfoRequest, IReturn<PlaybackInfoResponse> public class GetPostedPlaybackInfo : PlaybackInfoRequest, IReturn<PlaybackInfoResponse>
{ {
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; } public string Id { get; set; }
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string UserId { get; set; } public string UserId { get; set; }
[ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
public long? StartTimeTicks { get; set; } public long? StartTimeTicks { get; set; }
[ApiMember(Name = "AudioStreamIndex", Description = "Optional. The index of the audio stream to use. If omitted the first audio stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "AudioStreamIndex", Description = "Optional. The index of the audio stream to use. If omitted the first audio stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
public int? AudioStreamIndex { get; set; } public int? AudioStreamIndex { get; set; }
[ApiMember(Name = "SubtitleStreamIndex", Description = "Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "SubtitleStreamIndex", Description = "Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
public int? SubtitleStreamIndex { get; set; } public int? SubtitleStreamIndex { get; set; }
[ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string MediaSourceId { get; set; }
} }
[Authenticated] [Authenticated]
@ -82,7 +86,7 @@ namespace MediaBrowser.Api.Playback
public async Task<object> Post(GetPostedPlaybackInfo request) public async Task<object> Post(GetPostedPlaybackInfo request)
{ {
var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSource).ConfigureAwait(false); var info = await GetPlaybackInfo(request.Id, request.UserId, request.MediaSourceId).ConfigureAwait(false);
var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
var profile = request.DeviceProfile; var profile = request.DeviceProfile;
@ -97,36 +101,36 @@ namespace MediaBrowser.Api.Playback
if (profile != null) if (profile != null)
{ {
var mediaSourceId = request.MediaSource == null ? null : request.MediaSource.Id; var mediaSourceId = request.MediaSourceId;
SetDeviceSpecificData(request.Id, info, profile, authInfo, null, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex); SetDeviceSpecificData(request.Id, info, profile, authInfo, null, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex);
} }
return ToOptimizedResult(info); return ToOptimizedResult(info);
} }
private async Task<PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, MediaSourceInfo mediaSource = null) private async Task<PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, string mediaSourceId = null)
{ {
var result = new PlaybackInfoResponse(); var result = new PlaybackInfoResponse();
if (mediaSource == null) IEnumerable<MediaSourceInfo> mediaSources;
try
{ {
IEnumerable<MediaSourceInfo> mediaSources; mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false);
try
{
mediaSources = await _mediaSourceManager.GetPlayackMediaSources(id, userId, true, CancellationToken.None).ConfigureAwait(false);
}
catch (PlaybackException ex)
{
mediaSources = new List<MediaSourceInfo>();
result.ErrorCode = ex.ErrorCode;
}
result.MediaSources = mediaSources.ToList();
} }
else catch (PlaybackException ex)
{ {
result.MediaSources = new List<MediaSourceInfo> { mediaSource }; mediaSources = new List<MediaSourceInfo>();
result.ErrorCode = ex.ErrorCode;
}
result.MediaSources = mediaSources.ToList();
if (!string.IsNullOrWhiteSpace(mediaSourceId))
{
result.MediaSources = result.MediaSources
.Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
.ToList();
} }
if (result.MediaSources.Count == 0) if (result.MediaSources.Count == 0)
@ -185,9 +189,9 @@ namespace MediaBrowser.Api.Playback
mediaSource.SupportsDirectStream = true; mediaSource.SupportsDirectStream = true;
// The MediaSource supports direct stream, now test to see if the client supports it // The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = item is Video ? var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
streamBuilder.BuildVideoItem(options) : streamBuilder.BuildAudioItem(options) :
streamBuilder.BuildAudioItem(options); streamBuilder.BuildVideoItem(options);
if (streamInfo == null || !streamInfo.IsDirectStream) if (streamInfo == null || !streamInfo.IsDirectStream)
{ {
@ -201,9 +205,9 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsDirectStream) if (mediaSource.SupportsDirectStream)
{ {
// The MediaSource supports direct stream, now test to see if the client supports it // The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = item is Video ? var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
streamBuilder.BuildVideoItem(options) : streamBuilder.BuildAudioItem(options) :
streamBuilder.BuildAudioItem(options); streamBuilder.BuildVideoItem(options);
if (streamInfo == null || !streamInfo.IsDirectStream) if (streamInfo == null || !streamInfo.IsDirectStream)
{ {
@ -214,9 +218,9 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsTranscoding) if (mediaSource.SupportsTranscoding)
{ {
// The MediaSource supports direct stream, now test to see if the client supports it // The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = item is Video ? var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
streamBuilder.BuildVideoItem(options) : streamBuilder.BuildAudioItem(options) :
streamBuilder.BuildAudioItem(options); streamBuilder.BuildVideoItem(options);
if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode) if (streamInfo != null && streamInfo.PlayMethod == PlayMethod.Transcode)
{ {
@ -227,6 +231,46 @@ namespace MediaBrowser.Api.Playback
} }
} }
} }
SortMediaSources(result);
}
private void SortMediaSources(PlaybackInfoResponse result)
{
var originalList = result.MediaSources.ToList();
result.MediaSources = result.MediaSources.OrderBy(i =>
{
// Nothing beats direct playing a file
if (i.SupportsDirectPlay && i.Protocol == MediaProtocol.File)
{
return 0;
}
return 1;
}).ThenBy(i =>
{
// Let's assume direct streaming a file is just as desirable as direct playing a remote url
if (i.SupportsDirectPlay || i.SupportsDirectStream)
{
return 0;
}
return 1;
}).ThenBy(i =>
{
switch (i.Protocol)
{
case MediaProtocol.File:
return 0;
default:
return 1;
}
}).ThenBy(originalList.IndexOf)
.ToList();
} }
} }
} }

View File

@ -31,7 +31,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary> /// </summary>
public class AudioService : BaseProgressiveStreamingService public class AudioService : BaseProgressiveStreamingService
{ {
public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient) public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient)
{ {
} }
@ -55,7 +55,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true); return ProcessRequest(request, true);
} }
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{ {
var audioTranscodeParams = new List<string>(); var audioTranscodeParams = new List<string>();
@ -84,7 +84,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"", return string.Format("{0} {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
inputModifier, inputModifier,
GetInputArgument(transcodingJobId, state), GetInputArgument(state),
threads, threads,
vn, vn,
string.Join(" ", audioTranscodeParams.ToArray()), string.Join(" ", audioTranscodeParams.ToArray()),

View File

@ -27,7 +27,7 @@ namespace MediaBrowser.Api.Playback.Progressive
protected readonly IImageProcessor ImageProcessor; protected readonly IImageProcessor ImageProcessor;
protected readonly IHttpClient HttpClient; protected readonly IHttpClient HttpClient;
protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient) protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient)
{ {
ImageProcessor = imageProcessor; ImageProcessor = imageProcessor;
HttpClient = httpClient; HttpClient = httpClient;

View File

@ -62,7 +62,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary> /// </summary>
public class VideoService : BaseProgressiveStreamingService public class VideoService : BaseProgressiveStreamingService
{ {
public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient) public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, imageProcessor, httpClient)
{ {
} }
@ -86,7 +86,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true); return ProcessRequest(request, true);
} }
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding) protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
{ {
// Get the output codec name // Get the output codec name
var videoCodec = state.OutputVideoCodec; var videoCodec = state.OutputVideoCodec;
@ -106,7 +106,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"", return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"",
inputModifier, inputModifier,
GetInputArgument(transcodingJobId, state), GetInputArgument(state),
keyFrame, keyFrame,
GetMapArgs(state), GetMapArgs(state),
GetVideoArguments(state, videoCodec), GetVideoArguments(state, videoCodec),

View File

@ -1,6 +1,7 @@
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -17,7 +18,7 @@ namespace MediaBrowser.Api.Playback
public class StreamState : IDisposable public class StreamState : IDisposable
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ILiveTvManager _liveTvManager; private readonly IMediaSourceManager _mediaSourceManager;
public string RequestedUrl { get; set; } public string RequestedUrl { get; set; }
@ -39,7 +40,7 @@ namespace MediaBrowser.Api.Playback
public string InputContainer { get; set; } public string InputContainer { get; set; }
public List<MediaStream> AllMediaStreams { get; set; } public MediaSourceInfo MediaSource { get; set; }
public MediaStream AudioStream { get; set; } public MediaStream AudioStream { get; set; }
public MediaStream VideoStream { get; set; } public MediaStream VideoStream { get; set; }
@ -64,8 +65,6 @@ namespace MediaBrowser.Api.Playback
public List<string> PlayableStreamFileNames { get; set; } public List<string> PlayableStreamFileNames { get; set; }
public string LiveTvStreamId { get; set; }
public int SegmentLength = 3; public int SegmentLength = 3;
public bool EnableGenericHlsSegmenter = false; public bool EnableGenericHlsSegmenter = false;
public int HlsListSize public int HlsListSize
@ -86,14 +85,13 @@ namespace MediaBrowser.Api.Playback
public List<string> SupportedAudioCodecs { get; set; } public List<string> SupportedAudioCodecs { get; set; }
public StreamState(ILiveTvManager liveTvManager, ILogger logger) public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger)
{ {
_liveTvManager = liveTvManager; _mediaSourceManager = mediaSourceManager;
_logger = logger; _logger = logger;
SupportedAudioCodecs = new List<string>(); SupportedAudioCodecs = new List<string>();
PlayableStreamFileNames = new List<string>(); PlayableStreamFileNames = new List<string>();
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
AllMediaStreams = new List<MediaStream>();
} }
public string InputAudioSync { get; set; } public string InputAudioSync { get; set; }
@ -113,9 +111,6 @@ namespace MediaBrowser.Api.Playback
public long? EncodingDurationTicks { get; set; } public long? EncodingDurationTicks { get; set; }
public string ItemType { get; set; }
public string ItemId { get; set; }
public string GetMimeType(string outputPath) public string GetMimeType(string outputPath)
{ {
if (!string.IsNullOrEmpty(MimeType)) if (!string.IsNullOrEmpty(MimeType))
@ -187,15 +182,15 @@ namespace MediaBrowser.Api.Playback
private async void DisposeLiveStream() private async void DisposeLiveStream()
{ {
if (!string.IsNullOrEmpty(LiveTvStreamId)) if (MediaSource.RequiresClosing)
{ {
try try
{ {
await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false); await _mediaSourceManager.CloseMediaSource(MediaSource.CloseKey, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException("Error closing live tv stream", ex); _logger.ErrorException("Error closing media source", ex);
} }
} }
} }

View File

@ -192,7 +192,7 @@ namespace MediaBrowser.Api.Subtitles
{ {
var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
var mediaSource = item.GetMediaSources(false) var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null)
.First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id)); .First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id));
var subtitleStream = mediaSource.MediaStreams var subtitleStream = mediaSource.MediaStreams

View File

@ -75,17 +75,23 @@ namespace MediaBrowser.Controller.Channels
public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution) public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{ {
var list = base.GetMediaSources(enablePathSubstitution).ToList(); var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None)
.Result.ToList();
var sources = ChannelManager.GetChannelItemMediaSources(Id.ToString("N"), false, CancellationToken.None)
.Result.ToList();
if (sources.Count > 0) if (sources.Count > 0)
{ {
return sources; return sources;
} }
list.InsertRange(0, sources); var list = base.GetMediaSources(enablePathSubstitution).ToList();
foreach (var mediaSource in list)
{
if (string.IsNullOrWhiteSpace(mediaSource.Path))
{
mediaSource.Type = MediaSourceType.Placeholder;
}
}
return list; return list;
} }

View File

@ -90,17 +90,23 @@ namespace MediaBrowser.Controller.Channels
public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution) public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{ {
var list = base.GetMediaSources(enablePathSubstitution).ToList(); var sources = ChannelManager.GetStaticMediaSources(this, false, CancellationToken.None)
.Result.ToList();
var sources = ChannelManager.GetChannelItemMediaSources(Id.ToString("N"), false, CancellationToken.None)
.Result.ToList();
if (sources.Count > 0) if (sources.Count > 0)
{ {
return sources; return sources;
} }
list.InsertRange(0, sources); var list = base.GetMediaSources(enablePathSubstitution).ToList();
foreach (var mediaSource in list)
{
if (string.IsNullOrWhiteSpace(mediaSource.Path))
{
mediaSource.Type = MediaSourceType.Placeholder;
}
}
return list; return list;
} }

View File

@ -112,11 +112,11 @@ namespace MediaBrowser.Controller.Channels
/// <summary> /// <summary>
/// Gets the channel item media sources. /// Gets the channel item media sources.
/// </summary> /// </summary>
/// <param name="id">The identifier.</param> /// <param name="item">The item.</param>
/// <param name="includeDynamicSources">if set to <c>true</c> [include dynamic sources].</param> /// <param name="includeCachedVersions">if set to <c>true</c> [include cached versions].</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns> /// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken); Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(IChannelMediaItem item, bool includeCachedVersions, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the channel folder. /// Gets the channel folder.

View File

@ -64,6 +64,14 @@ namespace MediaBrowser.Controller.Library
/// <returns>IEnumerable&lt;MediaSourceInfo&gt;.</returns> /// <returns>IEnumerable&lt;MediaSourceInfo&gt;.</returns>
IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user); IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user);
/// <summary>
/// Gets the static media sources.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
/// <returns>IEnumerable&lt;MediaSourceInfo&gt;.</returns>
IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution);
/// <summary> /// <summary>
/// Gets the static media source. /// Gets the static media source.
/// </summary> /// </summary>
@ -72,5 +80,21 @@ namespace MediaBrowser.Controller.Library
/// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param> /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param>
/// <returns>MediaSourceInfo.</returns> /// <returns>MediaSourceInfo.</returns>
MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); MediaSourceInfo GetStaticMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution);
/// <summary>
/// Opens the media source.
/// </summary>
/// <param name="openKey">The open key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken);
/// <summary>
/// Closes the media source.
/// </summary>
/// <param name="closeKey">The close key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CloseMediaSource(string closeKey, CancellationToken cancellationToken);
} }
} }

View File

@ -15,5 +15,21 @@ namespace MediaBrowser.Controller.Library
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns> /// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken); Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
/// <summary>
/// Opens the media source.
/// </summary>
/// <param name="openKey">The open key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken);
/// <summary>
/// Closes the media source.
/// </summary>
/// <param name="closeKey">The close key.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task CloseMediaSource(string closeKey, CancellationToken cancellationToken);
} }
} }

View File

@ -1,8 +1,10 @@
 using System;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
public interface ILiveTvItem public interface ILiveTvItem
{ {
Guid Id { get; }
string ServiceName { get; set; } string ServiceName { get; set; }
} }
} }

View File

@ -1,10 +1,12 @@
using System.Runtime.Serialization; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
@ -99,5 +101,20 @@ namespace MediaBrowser.Controller.LiveTv
{ {
return user.Policy.EnableLiveTvManagement; return user.Policy.EnableLiveTvManagement;
} }
public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
var list = base.GetMediaSources(enablePathSubstitution).ToList();
foreach (var mediaSource in list)
{
if (string.IsNullOrWhiteSpace(mediaSource.Path))
{
mediaSource.Type = MediaSourceType.Placeholder;
}
}
return list;
}
} }
} }

View File

@ -127,7 +127,7 @@ namespace MediaBrowser.Controller.LiveTv
Name = Name, Name = Name,
Path = Path, Path = Path,
RunTimeTicks = RunTimeTicks, RunTimeTicks = RunTimeTicks,
Type = MediaSourceType.Default Type = MediaSourceType.Placeholder
}; };
list.Add(info); list.Add(info);

View File

@ -1,9 +1,11 @@
using System.Runtime.Serialization; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System.Linq;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
@ -97,5 +99,20 @@ namespace MediaBrowser.Controller.LiveTv
{ {
return user.Policy.EnableLiveTvManagement; return user.Policy.EnableLiveTvManagement;
} }
public override IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
var list = base.GetMediaSources(enablePathSubstitution).ToList();
foreach (var mediaSource in list)
{
if (string.IsNullOrWhiteSpace(mediaSource.Path))
{
mediaSource.Type = MediaSourceType.Placeholder;
}
}
return list;
}
} }
} }

View File

@ -476,7 +476,7 @@ namespace MediaBrowser.Dlna.PlayTo
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex); var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
playlistItem.StreamInfo.StartPositionTicks = startPostionTicks; playlistItem.StreamInfo.StartPositionTicks = startPostionTicks;
playlistItem.StreamUrl = playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken); playlistItem.StreamUrl = playlistItem.StreamInfo.ToDlnaUrl(_serverAddress, _accessToken);
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager) var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager)
.GetItemDidl(item, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo); .GetItemDidl(item, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);

View File

@ -239,6 +239,16 @@ namespace MediaBrowser.Model.Dlna
return playlistItem; return playlistItem;
} }
private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options)
{
if (item.Protocol == MediaProtocol.File)
{
return options.Profile.MaxStaticBitrate;
}
return options.GetMaxBitrate();
}
private List<PlayMethod> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options) private List<PlayMethod> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
{ {
DirectPlayProfile directPlayProfile = null; DirectPlayProfile directPlayProfile = null;
@ -263,7 +273,7 @@ namespace MediaBrowser.Model.Dlna
// The profile describes what the device supports // The profile describes what the device supports
// If device requirements are satisfied then allow both direct stream and direct play // If device requirements are satisfied then allow both direct stream and direct play
if (IsAudioEligibleForDirectPlay(item, options.Profile.MaxStaticBitrate)) if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)))
{ {
playMethods.Add(PlayMethod.DirectPlay); playMethods.Add(PlayMethod.DirectPlay);
} }
@ -293,7 +303,7 @@ namespace MediaBrowser.Model.Dlna
MediaStream videoStream = item.VideoStream; MediaStream videoStream = item.VideoStream;
// TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough
bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, options.Profile.MaxStaticBitrate, subtitleStream, options); bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options);
bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options); bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options);
if (isEligibleForDirectPlay || isEligibleForDirectStream) if (isEligibleForDirectPlay || isEligibleForDirectStream)
@ -604,6 +614,11 @@ namespace MediaBrowser.Model.Dlna
// Look for an external profile that matches the stream type (text/graphical) // Look for an external profile that matches the stream type (text/graphical)
foreach (SubtitleProfile profile in subtitleProfiles) foreach (SubtitleProfile profile in subtitleProfiles)
{ {
if (!profile.SupportsLanguage(subtitleStream.Language))
{
continue;
}
if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
{ {
if (subtitleStream.SupportsExternalStream) if (subtitleStream.SupportsExternalStream)
@ -621,6 +636,11 @@ namespace MediaBrowser.Model.Dlna
foreach (SubtitleProfile profile in subtitleProfiles) foreach (SubtitleProfile profile in subtitleProfiles)
{ {
if (!profile.SupportsLanguage(subtitleStream.Language))
{
continue;
}
if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
{ {
return profile; return profile;

View File

@ -1,4 +1,6 @@
using System.Xml.Serialization; using MediaBrowser.Model.Extensions;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace MediaBrowser.Model.Dlna namespace MediaBrowser.Model.Dlna
{ {
@ -13,5 +15,28 @@ namespace MediaBrowser.Model.Dlna
[XmlAttribute("didlMode")] [XmlAttribute("didlMode")]
public string DidlMode { get; set; } public string DidlMode { get; set; }
[XmlAttribute("language")]
public string Language { get; set; }
public List<string> GetLanguages()
{
List<string> list = new List<string>();
foreach (string i in (Language ?? string.Empty).Split(','))
{
if (!string.IsNullOrEmpty(i)) list.Add(i);
}
return list;
}
public bool SupportsLanguage(string language)
{
if (string.IsNullOrEmpty(language))
{
language = "und";
}
List<string> languages = GetLanguages();
return languages.Count == 0 || ListHelper.ContainsIgnoreCase(languages, language);
}
} }
} }

View File

@ -26,6 +26,11 @@ namespace MediaBrowser.Model.Dto
public bool SupportsDirectStream { get; set; } public bool SupportsDirectStream { get; set; }
public bool SupportsDirectPlay { get; set; } public bool SupportsDirectPlay { get; set; }
public bool RequiresOpening { get; set; }
public string OpenKey { get; set; }
public bool RequiresClosing { get; set; }
public string CloseKey { get; set; }
public VideoType? VideoType { get; set; } public VideoType? VideoType { get; set; }
public IsoType? IsoType { get; set; } public IsoType? IsoType { get; set; }

View File

@ -4,6 +4,6 @@ namespace MediaBrowser.Model.Dto
{ {
Default = 0, Default = 0,
Grouping = 1, Grouping = 1,
Cache = 2 Placeholder = 2
} }
} }

View File

@ -141,6 +141,11 @@ namespace MediaBrowser.Model.Entities
{ {
if (Type != MediaStreamType.Subtitle) return false; if (Type != MediaStreamType.Subtitle) return false;
if (string.IsNullOrEmpty(Codec) && !IsExternal)
{
return false;
}
return IsTextFormat(Codec); return IsTextFormat(Codec);
} }
} }

View File

@ -1,11 +1,9 @@
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.MediaInfo namespace MediaBrowser.Model.MediaInfo
{ {
public class PlaybackInfoRequest public class PlaybackInfoRequest
{ {
public DeviceProfile DeviceProfile { get; set; } public DeviceProfile DeviceProfile { get; set; }
public MediaSourceInfo MediaSource { get; set; }
} }
} }

View File

@ -96,7 +96,7 @@ namespace MediaBrowser.Providers.MediaInfo
var album = item.Parent as MusicAlbum; var album = item.Parent as MusicAlbum;
var filename = item.Album ?? string.Empty; var filename = item.Album ?? string.Empty;
filename += item.Artists.FirstOrDefault() ?? string.Empty; filename += string.Join(",", item.Artists.ToArray());
filename += album == null ? item.Id.ToString("N") + "_primary" + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary"; filename += album == null ? item.Id.ToString("N") + "_primary" + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary";
filename = filename.GetMD5() + ".jpg"; filename = filename.GetMD5() + ".jpg";

View File

@ -169,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Channels
foreach (var item in result.Items) foreach (var item in result.Items)
{ {
var channelItem = (IChannelItem)item; var channelItem = (IChannelMediaItem)item;
var channelFeatures = _manager.GetChannelFeatures(channelItem.ChannelId); var channelFeatures = _manager.GetChannelFeatures(channelItem.ChannelId);
@ -179,7 +179,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{ {
try try
{ {
await DownloadChannelItem(item, options, cancellationToken, path); await DownloadChannelItem(channelItem, options, cancellationToken, path);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -210,13 +210,13 @@ namespace MediaBrowser.Server.Implementations.Channels
return channelOptions.DownloadSizeLimit; return channelOptions.DownloadSizeLimit;
} }
private async Task DownloadChannelItem(BaseItem item, private async Task DownloadChannelItem(IChannelMediaItem item,
ChannelOptions channelOptions, ChannelOptions channelOptions,
CancellationToken cancellationToken, CancellationToken cancellationToken,
string path) string path)
{ {
var itemId = item.Id.ToString("N"); var itemId = item.Id.ToString("N");
var sources = await _manager.GetChannelItemMediaSources(itemId, false, cancellationToken) var sources = await _manager.GetStaticMediaSources(item, true, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
var cachedVersions = sources.Where(i => i.Protocol == MediaProtocol.File).ToList(); var cachedVersions = sources.Where(i => i.Protocol == MediaProtocol.File).ToList();
@ -237,11 +237,9 @@ namespace MediaBrowser.Server.Implementations.Channels
} }
} }
var channelItem = (IChannelMediaItem)item; var destination = Path.Combine(path, item.ChannelId, itemId);
var destination = Path.Combine(path, channelItem.ChannelId, itemId); await _manager.DownloadChannelItem(item, destination, new Progress<double>(), cancellationToken)
await _manager.DownloadChannelItem(channelItem, destination, new Progress<double>(), cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
await RefreshMediaSourceItem(destination, cancellationToken).ConfigureAwait(false); await RefreshMediaSourceItem(destination, cancellationToken).ConfigureAwait(false);

View File

@ -0,0 +1,43 @@
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Channels
{
public class ChannelDynamicMediaSourceProvider : IMediaSourceProvider
{
private readonly ChannelManager _channelManager;
public ChannelDynamicMediaSourceProvider(IChannelManager channelManager)
{
_channelManager = (ChannelManager)channelManager;
}
public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
{
var channelItem = item as IChannelMediaItem;
if (channelItem != null)
{
return _channelManager.GetDynamicMediaSources(channelItem, cancellationToken);
}
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
}
public Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}

View File

@ -241,10 +241,25 @@ namespace MediaBrowser.Server.Implementations.Channels
return item; return item;
} }
public async Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaSources(string id, bool includeDynamicSources, CancellationToken cancellationToken) public async Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(IChannelMediaItem item, bool includeCachedVersions, CancellationToken cancellationToken)
{ {
var item = (IChannelMediaItem)_libraryManager.GetItemById(id); IEnumerable<ChannelMediaInfo> results = item.ChannelMediaSources;
var sources = SortMediaInfoResults(results)
.Select(i => GetMediaSource(item, i))
.ToList();
if (includeCachedVersions)
{
var cachedVersions = GetCachedChannelItemMediaSources(item);
sources.InsertRange(0, cachedVersions);
}
return sources.Where(IsValidMediaSource);
}
public async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IChannelMediaItem item, CancellationToken cancellationToken)
{
var channel = GetChannel(item.ChannelId); var channel = GetChannel(item.ChannelId);
var channelPlugin = GetChannelProvider(channel); var channelPlugin = GetChannelProvider(channel);
@ -252,24 +267,25 @@ namespace MediaBrowser.Server.Implementations.Channels
IEnumerable<ChannelMediaInfo> results; IEnumerable<ChannelMediaInfo> results;
if (requiresCallback != null && includeDynamicSources) if (requiresCallback != null)
{ {
results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken) results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
else else
{ {
results = item.ChannelMediaSources; results = new List<ChannelMediaInfo>();
} }
var sources = SortMediaInfoResults(results).Select(i => GetMediaSource(item, i)) var list = SortMediaInfoResults(results)
.Select(i => GetMediaSource(item, i))
.Where(IsValidMediaSource)
.ToList(); .ToList();
var cachedVersions = GetCachedChannelItemMediaSources(item); var cachedVersions = GetCachedChannelItemMediaSources(item);
list.InsertRange(0, cachedVersions);
sources.InsertRange(0, cachedVersions); return list;
return sources.Where(IsValidMediaSource);
} }
private readonly ConcurrentDictionary<string, Tuple<DateTime, List<ChannelMediaInfo>>> _channelItemMediaInfo = private readonly ConcurrentDictionary<string, Tuple<DateTime, List<ChannelMediaInfo>>> _channelItemMediaInfo =
@ -297,14 +313,7 @@ namespace MediaBrowser.Server.Implementations.Channels
return list; return list;
} }
public IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(string id) private IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(IChannelMediaItem item)
{
var item = (IChannelMediaItem)_libraryManager.GetItemById(id);
return GetCachedChannelItemMediaSources(item);
}
public IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(IChannelMediaItem item)
{ {
var filenamePrefix = item.Id.ToString("N"); var filenamePrefix = item.Id.ToString("N");
var parentPath = Path.Combine(ChannelDownloadPath, item.ChannelId); var parentPath = Path.Combine(ChannelDownloadPath, item.ChannelId);
@ -339,7 +348,6 @@ namespace MediaBrowser.Server.Implementations.Channels
if (source != null) if (source != null)
{ {
source.Type = MediaSourceType.Cache;
return new[] { source }; return new[] { source };
} }
} }
@ -1408,8 +1416,7 @@ namespace MediaBrowser.Server.Implementations.Channels
public async Task DownloadChannelItem(IChannelMediaItem item, string destination, public async Task DownloadChannelItem(IChannelMediaItem item, string destination,
IProgress<double> progress, CancellationToken cancellationToken) IProgress<double> progress, CancellationToken cancellationToken)
{ {
var itemId = item.Id.ToString("N"); var sources = await GetDynamicMediaSources(item, cancellationToken)
var sources = await GetChannelItemMediaSources(itemId, true, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
var list = sources.Where(i => i.Protocol == MediaProtocol.Http).ToList(); var list = sources.Where(i => i.Protocol == MediaProtocol.Http).ToList();

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Channels; using System.Collections.Concurrent;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
@ -13,25 +14,24 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Server.Implementations.LiveTv;
namespace MediaBrowser.Server.Implementations.Library namespace MediaBrowser.Server.Implementations.Library
{ {
public class MediaSourceManager : IMediaSourceManager public class MediaSourceManager : IMediaSourceManager, IDisposable
{ {
private readonly IItemRepository _itemRepo; private readonly IItemRepository _itemRepo;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IChannelManager _channelManager;
private IMediaSourceProvider[] _providers; private IMediaSourceProvider[] _providers;
private readonly ILogger _logger; private readonly ILogger _logger;
public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, IChannelManager channelManager, ILogger logger) public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger)
{ {
_itemRepo = itemRepo; _itemRepo = itemRepo;
_userManager = userManager; _userManager = userManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_channelManager = channelManager;
_logger = logger; _logger = logger;
} }
@ -133,24 +133,15 @@ namespace MediaBrowser.Server.Implementations.Library
IEnumerable<MediaSourceInfo> mediaSources; IEnumerable<MediaSourceInfo> mediaSources;
var hasMediaSources = (IHasMediaSources)item; var hasMediaSources = (IHasMediaSources)item;
var channelItem = item as IChannelMediaItem;
if (channelItem != null) if (string.IsNullOrWhiteSpace(userId))
{ {
mediaSources = await _channelManager.GetChannelItemMediaSources(id, true, cancellationToken) mediaSources = hasMediaSources.GetMediaSources(enablePathSubstitution);
.ConfigureAwait(false);
} }
else else
{ {
if (string.IsNullOrWhiteSpace(userId)) var user = _userManager.GetUserById(userId);
{ mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user);
mediaSources = hasMediaSources.GetMediaSources(enablePathSubstitution);
}
else
{
var user = _userManager.GetUserById(userId);
mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user);
}
} }
var dynamicMediaSources = await GetDynamicMediaSources(hasMediaSources, cancellationToken).ConfigureAwait(false); var dynamicMediaSources = await GetDynamicMediaSources(hasMediaSources, cancellationToken).ConfigureAwait(false);
@ -161,11 +152,16 @@ namespace MediaBrowser.Server.Implementations.Library
foreach (var source in dynamicMediaSources) foreach (var source in dynamicMediaSources)
{ {
source.SupportsTranscoding = false;
if (source.Protocol == MediaProtocol.File) if (source.Protocol == MediaProtocol.File)
{ {
source.SupportsDirectStream = File.Exists(source.Path); source.SupportsDirectStream = File.Exists(source.Path);
// TODO: Path substitution
}
else if (source.Protocol == MediaProtocol.Http)
{
// TODO: Allow this when the source is plain http, e.g. not HLS or Mpeg Dash
source.SupportsDirectStream = false;
} }
else else
{ {
@ -175,7 +171,7 @@ namespace MediaBrowser.Server.Implementations.Library
list.Add(source); list.Add(source);
} }
return SortMediaSources(list); return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder);
} }
private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, CancellationToken cancellationToken) private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
@ -190,7 +186,15 @@ namespace MediaBrowser.Server.Implementations.Library
{ {
try try
{ {
return await provider.GetMediaSources(item, cancellationToken).ConfigureAwait(false); var sources = await provider.GetMediaSources(item, cancellationToken).ConfigureAwait(false);
var list = sources.ToList();
foreach (var mediaSource in list)
{
SetKeyProperties(provider, mediaSource);
}
return list;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -199,6 +203,21 @@ namespace MediaBrowser.Server.Implementations.Library
} }
} }
private void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource)
{
var prefix = provider.GetType().FullName.GetMD5().ToString("N") + "|";
if (!string.IsNullOrWhiteSpace(mediaSource.OpenKey) && !mediaSource.OpenKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
mediaSource.OpenKey = prefix + mediaSource.OpenKey;
}
if (!string.IsNullOrWhiteSpace(mediaSource.CloseKey) && !mediaSource.CloseKey.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
mediaSource.CloseKey = prefix + mediaSource.CloseKey;
}
}
public Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken) public Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, bool enablePathSubstitution, CancellationToken cancellationToken)
{ {
return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken); return GetPlayackMediaSources(id, null, enablePathSubstitution, cancellationToken);
@ -294,5 +313,90 @@ namespace MediaBrowser.Server.Implementations.Library
{ {
return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); return GetStaticMediaSources(item, enablePathSubstitution).FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
} }
private readonly ConcurrentDictionary<string, string> _openStreams =
new ConcurrentDictionary<string, string>();
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var tuple = GetProvider(openKey);
var provider = tuple.Item1;
var mediaSource = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
SetKeyProperties(provider, mediaSource);
_openStreams.AddOrUpdate(mediaSource.CloseKey, mediaSource.CloseKey, (key, i) => mediaSource.CloseKey);
return mediaSource;
}
finally
{
_liveStreamSemaphore.Release();
}
}
public async Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var tuple = GetProvider(closeKey);
await tuple.Item1.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
string removedKey;
_openStreams.TryRemove(closeKey, out removedKey);
}
finally
{
_liveStreamSemaphore.Release();
}
}
private Tuple<IMediaSourceProvider, string> GetProvider(string key)
{
var keys = key.Split(new[] { '|' }, 2);
var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase));
return new Tuple<IMediaSourceProvider, string>(provider, keys[1]);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
}
private readonly object _disposeLock = new object();
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool dispose)
{
if (dispose)
{
lock (_disposeLock)
{
foreach (var key in _openStreams.Keys.ToList())
{
var task = CloseMediaSource(key, CancellationToken.None);
Task.WaitAll(task);
}
_openStreams.Clear();
}
}
}
} }
} }

View File

@ -1,10 +1,8 @@
using System.Globalization; using MediaBrowser.Common;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
@ -15,7 +13,6 @@ using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
@ -342,6 +339,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var service = GetService(channel); var service = GetService(channel);
_logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId); _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
info = await service.GetChannelStream(channel.ExternalId, null, cancellationToken).ConfigureAwait(false); info = await service.GetChannelStream(channel.ExternalId, null, cancellationToken).ConfigureAwait(false);
info.RequiresClosing = true;
info.CloseKey = info.Id;
} }
else else
{ {
@ -351,6 +350,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.RecordingInfo.Id); _logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.RecordingInfo.Id);
info = await service.GetRecordingStream(recording.RecordingInfo.Id, null, cancellationToken).ConfigureAwait(false); info = await service.GetRecordingStream(recording.RecordingInfo.Id, null, cancellationToken).ConfigureAwait(false);
info.RequiresClosing = true;
info.CloseKey = info.Id;
} }
_logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info)); _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info));

View File

@ -0,0 +1,77 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv
{
public class LiveTvMediaSourceProvider : IMediaSourceProvider
{
private readonly ILiveTvManager _liveTvManager;
public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager)
{
_liveTvManager = liveTvManager;
}
public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
{
var channelItem = item as ILiveTvItem;
if (channelItem != null)
{
var hasMetadata = (IHasMetadata)channelItem;
if (string.IsNullOrWhiteSpace(hasMetadata.Path))
{
return GetMediaSourcesInternal(channelItem, cancellationToken);
}
}
return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
}
private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(ILiveTvItem item, CancellationToken cancellationToken)
{
var hasMediaSources = (IHasMediaSources)item;
var sources = hasMediaSources.GetMediaSources(false)
.ToList();
foreach (var source in sources)
{
source.Type = MediaSourceType.Default;
source.RequiresOpening = true;
var openKeys = new List<string>();
openKeys.Add(item.GetType().Name);
openKeys.Add(item.Id.ToString("N"));
source.OpenKey = string.Join("|", openKeys.ToArray());
}
return sources;
}
public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
{
var keys = openKey.Split(new[] { '|' }, 2);
if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase))
{
return await _liveTvManager.GetChannelStream(keys[1], cancellationToken).ConfigureAwait(false);
}
return await _liveTvManager.GetRecordingStream(keys[1], cancellationToken).ConfigureAwait(false);
}
public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
{
return _liveTvManager.CloseLiveStream(closeKey, cancellationToken);
}
}
}

View File

@ -111,6 +111,7 @@
<Compile Include="Branding\BrandingConfigurationFactory.cs" /> <Compile Include="Branding\BrandingConfigurationFactory.cs" />
<Compile Include="Channels\ChannelConfigurations.cs" /> <Compile Include="Channels\ChannelConfigurations.cs" />
<Compile Include="Channels\ChannelDownloadScheduledTask.cs" /> <Compile Include="Channels\ChannelDownloadScheduledTask.cs" />
<Compile Include="Channels\ChannelDynamicMediaSourceProvider.cs" />
<Compile Include="Channels\ChannelImageProvider.cs" /> <Compile Include="Channels\ChannelImageProvider.cs" />
<Compile Include="Channels\ChannelItemImageProvider.cs" /> <Compile Include="Channels\ChannelItemImageProvider.cs" />
<Compile Include="Channels\ChannelManager.cs" /> <Compile Include="Channels\ChannelManager.cs" />
@ -225,6 +226,7 @@
<Compile Include="LiveTv\LiveTvConfigurationFactory.cs" /> <Compile Include="LiveTv\LiveTvConfigurationFactory.cs" />
<Compile Include="LiveTv\LiveTvDtoService.cs" /> <Compile Include="LiveTv\LiveTvDtoService.cs" />
<Compile Include="LiveTv\LiveTvManager.cs" /> <Compile Include="LiveTv\LiveTvManager.cs" />
<Compile Include="LiveTv\LiveTvMediaSourceProvider.cs" />
<Compile Include="LiveTv\ProgramImageProvider.cs" /> <Compile Include="LiveTv\ProgramImageProvider.cs" />
<Compile Include="LiveTv\RecordingImageProvider.cs" /> <Compile Include="LiveTv\RecordingImageProvider.cs" />
<Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" /> <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />

View File

@ -161,6 +161,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{ {
mediaSource.Path = sendFileResult.Path; mediaSource.Path = sendFileResult.Path;
mediaSource.Protocol = sendFileResult.Protocol; mediaSource.Protocol = sendFileResult.Protocol;
mediaSource.RequiredHttpHeaders = sendFileResult.RequiredHttpHeaders;
mediaSource.SupportsTranscoding = false; mediaSource.SupportsTranscoding = false;
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sync; using MediaBrowser.Controller.Sync;
@ -61,7 +62,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{ {
foreach (var mediaSource in localItem.Item.MediaSources) foreach (var mediaSource in localItem.Item.MediaSources)
{ {
await TryAddMediaSource(list, localItem, mediaSource, syncProvider, syncTarget, cancellationToken).ConfigureAwait(false); AddMediaSource(list, localItem, mediaSource, syncProvider, syncTarget);
} }
} }
} }
@ -71,41 +72,70 @@ namespace MediaBrowser.Server.Implementations.Sync
return list; return list;
} }
private async Task TryAddMediaSource(List<MediaSourceInfo> list, private void AddMediaSource(List<MediaSourceInfo> list,
LocalItem item, LocalItem item,
MediaSourceInfo mediaSource, MediaSourceInfo mediaSource,
IServerSyncProvider provider, IServerSyncProvider provider,
SyncTarget target, SyncTarget target)
CancellationToken cancellationToken)
{ {
SetStaticMediaSourceInfo(item, mediaSource);
var requiresDynamicAccess = provider as IHasDynamicAccess; var requiresDynamicAccess = provider as IHasDynamicAccess;
if (requiresDynamicAccess == null) if (requiresDynamicAccess != null)
{ {
list.Add(mediaSource); mediaSource.RequiresOpening = true;
return;
var keyList = new List<string>();
keyList.Add(provider.GetType().FullName.GetMD5().ToString("N"));
keyList.Add(target.Id.GetMD5().ToString("N"));
keyList.Add(item.Id);
mediaSource.OpenKey = string.Join("|", keyList.ToArray());
}
}
public async Task<MediaSourceInfo> OpenMediaSource(string openKey, CancellationToken cancellationToken)
{
var openKeys = openKey.Split(new[] { '|' }, 3);
var provider = _syncManager.ServerSyncProviders
.FirstOrDefault(i => string.Equals(openKeys[0], i.GetType().FullName.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase));
var target = provider.GetAllSyncTargets()
.FirstOrDefault(i => string.Equals(openKeys[1], i.Id.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase));
var dataProvider = _syncManager.GetDataProvider(provider, target);
var localItem = await dataProvider.Get(target, openKeys[2]).ConfigureAwait(false);
var requiresDynamicAccess = (IHasDynamicAccess)provider;
var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(localItem.LocalPath, target, cancellationToken).ConfigureAwait(false);
var mediaSource = localItem.Item.MediaSources.First();
SetStaticMediaSourceInfo(localItem, mediaSource);
foreach (var stream in mediaSource.MediaStreams)
{
var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false);
stream.Path = dynamicStreamInfo.Path;
} }
try mediaSource.Path = dynamicInfo.Path;
{ mediaSource.Protocol = dynamicInfo.Protocol;
var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(item.LocalPath, target, cancellationToken).ConfigureAwait(false); mediaSource.RequiredHttpHeaders = dynamicInfo.RequiredHttpHeaders;
foreach (var stream in mediaSource.MediaStreams) return mediaSource;
{ }
var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false);
stream.Path = dynamicStreamInfo.Path; private void SetStaticMediaSourceInfo(LocalItem item, MediaSourceInfo mediaSource)
} {
mediaSource.Id = item.Id;
mediaSource.SupportsTranscoding = false;
}
mediaSource.Path = dynamicInfo.Path; public Task CloseMediaSource(string closeKey, CancellationToken cancellationToken)
mediaSource.Protocol = dynamicInfo.Protocol; {
throw new NotImplementedException();
list.Add(mediaSource);
}
catch (Exception ex)
{
_logger.ErrorException("Error getting dynamic media source info", ex);
}
} }
} }
} }

View File

@ -472,7 +472,7 @@ namespace MediaBrowser.Server.Startup.Common
ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient); ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient);
RegisterSingleInstance(ChannelManager); RegisterSingleInstance(ChannelManager);
MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, ChannelManager, LogManager.GetLogger("MediaSourceManager")); MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"));
RegisterSingleInstance(MediaSourceManager); RegisterSingleInstance(MediaSourceManager);
SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager); SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager);

View File

@ -9,8 +9,8 @@
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl> <projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl> <iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description> <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright> <copyright>Copyright © Emby 2013</copyright>
<dependencies> <dependencies>
<dependency id="MediaBrowser.Common" version="3.0.603" /> <dependency id="MediaBrowser.Common" version="3.0.603" />
<dependency id="NLog" version="3.2.0.0" /> <dependency id="NLog" version="3.2.0.0" />

View File

@ -4,13 +4,13 @@
<id>MediaBrowser.Common</id> <id>MediaBrowser.Common</id>
<version>3.0.603</version> <version>3.0.603</version>
<title>MediaBrowser.Common</title> <title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors> <authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl> <projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl> <iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common model objects and interfaces used by all Media Browser solutions.</description> <description>Contains common model objects and interfaces used by all Emby solutions.</description>
<copyright>Copyright © Media Browser 2013</copyright> <copyright>Copyright © Emby 2013</copyright>
</metadata> </metadata>
<files> <files>
<file src="dlls\net35\MediaBrowser.Model.dll" target="lib\net35\MediaBrowser.Model.dll" /> <file src="dlls\net35\MediaBrowser.Model.dll" target="lib\net35\MediaBrowser.Model.dll" />

View File

@ -4,13 +4,13 @@
<id>MediaBrowser.Model.Signed</id> <id>MediaBrowser.Model.Signed</id>
<version>3.0.603</version> <version>3.0.603</version>
<title>MediaBrowser.Model - Signed Edition</title> <title>MediaBrowser.Model - Signed Edition</title>
<authors>Media Browser Team</authors> <authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl> <projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl> <iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common model objects and interfaces used by all Media Browser solutions.</description> <description>Contains common model objects and interfaces used by all Emby solutions.</description>
<copyright>Copyright © Media Browser 2013</copyright> <copyright>Copyright © Emby 2013</copyright>
<dependencies> <dependencies>
</dependencies> </dependencies>
</metadata> </metadata>

View File

@ -4,13 +4,13 @@
<id>MediaBrowser.Server.Core</id> <id>MediaBrowser.Server.Core</id>
<version>3.0.603</version> <version>3.0.603</version>
<title>Media Browser.Server.Core</title> <title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors> <authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners> <owners>ebr,Luke,scottisafool</owners>
<projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl> <projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl> <iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains core components required to build plugins for Media Browser Server.</description> <description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Media Browser 2013</copyright> <copyright>Copyright © Emby 2013</copyright>
<dependencies> <dependencies>
<dependency id="MediaBrowser.Common" version="3.0.603" /> <dependency id="MediaBrowser.Common" version="3.0.603" />
</dependencies> </dependencies>