aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api')
-rw-r--r--MediaBrowser.Api/Dlna/DlnaServerService.cs89
-rw-r--r--MediaBrowser.Api/Dlna/DlnaService.cs (renamed from MediaBrowser.Api/DlnaService.cs)2
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs2
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj3
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs44
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs51
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs8
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs8
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs3
-rw-r--r--MediaBrowser.Api/SessionsService.cs18
10 files changed, 164 insertions, 64 deletions
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs
new file mode 100644
index 0000000000..922c67aa2d
--- /dev/null
+++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs
@@ -0,0 +1,89 @@
+using MediaBrowser.Controller.Dlna;
+using ServiceStack;
+using ServiceStack.Web;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Dlna
+{
+ [Route("/Dlna/{UuId}/description.xml", "GET", Summary = "Gets dlna server info")]
+ [Route("/Dlna/{UuId}/description", "GET", Summary = "Gets dlna server info")]
+ public class GetDescriptionXml
+ {
+ [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string UuId { get; set; }
+ }
+
+ [Route("/Dlna/{UuId}/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")]
+ [Route("/Dlna/{UuId}/contentdirectory", "GET", Summary = "Gets the content directory xml")]
+ public class GetContentDirectory
+ {
+ [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string UuId { get; set; }
+ }
+
+ [Route("/Dlna/{UuId}/control", "POST", Summary = "Processes a control request")]
+ public class ProcessControlRequest : IRequiresRequestStream
+ {
+ [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string UuId { get; set; }
+
+ public Stream RequestStream { get; set; }
+ }
+
+ public class DlnaServerService : BaseApiService
+ {
+ private readonly IDlnaManager _dlnaManager;
+
+ public DlnaServerService(IDlnaManager dlnaManager)
+ {
+ _dlnaManager = dlnaManager;
+ }
+
+ public object Get(GetDescriptionXml request)
+ {
+ var xml = _dlnaManager.GetServerDescriptionXml(GetRequestHeaders(), request.UuId);
+
+ return ResultFactory.GetResult(xml, "text/xml");
+ }
+
+ public object Get(GetContentDirectory request)
+ {
+ var xml = _dlnaManager.GetContentDirectoryXml(GetRequestHeaders());
+
+ return ResultFactory.GetResult(xml, "text/xml");
+ }
+
+ public object Post(ProcessControlRequest request)
+ {
+ var response = PostAsync(request).Result;
+
+ return ResultFactory.GetResult(response.Xml, "text/xml");
+ }
+
+ private async Task<ControlResponse> PostAsync(ProcessControlRequest request)
+ {
+ using (var reader = new StreamReader(request.RequestStream))
+ {
+ return _dlnaManager.ProcessControlRequest(new ControlRequest
+ {
+ Headers = GetRequestHeaders(),
+ InputXml = await reader.ReadToEndAsync().ConfigureAwait(false)
+ });
+ }
+ }
+
+ private IDictionary<string, string> GetRequestHeaders()
+ {
+ var headers = new Dictionary<string, string>();
+
+ foreach (var key in Request.Headers.AllKeys)
+ {
+ headers[key] = Request.Headers[key];
+ }
+
+ return headers;
+ }
+ }
+}
diff --git a/MediaBrowser.Api/DlnaService.cs b/MediaBrowser.Api/Dlna/DlnaService.cs
index 792a7ff43f..9e6ca3aea9 100644
--- a/MediaBrowser.Api/DlnaService.cs
+++ b/MediaBrowser.Api/Dlna/DlnaService.cs
@@ -4,7 +4,7 @@ using ServiceStack;
using System.Collections.Generic;
using System.Linq;
-namespace MediaBrowser.Api
+namespace MediaBrowser.Api.Dlna
{
[Route("/Dlna/ProfileInfos", "GET", Summary = "Gets a list of profiles")]
public class GetProfileInfos : IReturn<List<DeviceProfileInfo>>
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 6667ba25c6..967bc1fbac 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.Api.Library
public string Id { get; set; }
}
- [Route("/Videos/{Id}/Subtitle/{Index}", "GET")]
+ [Route("/Videos/{Id}/Subtitles/{Index}", "GET")]
[Api(Description = "Gets an external subtitle file")]
public class GetSubtitle
{
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index c03eddf99e..edbae3903e 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -66,7 +66,8 @@
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="ChannelService.cs" />
- <Compile Include="DlnaService.cs" />
+ <Compile Include="Dlna\DlnaServerService.cs" />
+ <Compile Include="Dlna\DlnaService.cs" />
<Compile Include="Movies\CollectionService.cs" />
<Compile Include="Music\AlbumsService.cs" />
<Compile Include="AppThemeService.cs" />
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index bb55b893a2..e588068d0d 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -185,9 +185,9 @@ namespace MediaBrowser.Api.Playback
{
var args = string.Empty;
- if (state.IsRemote || !state.HasMediaStreams)
+ if (!state.HasMediaStreams)
{
- return string.Empty;
+ return state.IsInputVideo ? "-sn" : string.Empty;
}
if (state.VideoStream != null)
@@ -1341,6 +1341,12 @@ namespace MediaBrowser.Api.Playback
RequestedUrl = url
};
+ if (!string.IsNullOrWhiteSpace(request.AudioCodec))
+ {
+ state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault();
+ }
+
var item = string.IsNullOrEmpty(request.MediaSourceId) ?
DtoService.GetItemByDtoId(request.Id) :
DtoService.GetItemByDtoId(request.MediaSourceId);
@@ -1487,33 +1493,27 @@ namespace MediaBrowser.Api.Playback
if (videoRequest != null)
{
- if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream, state.VideoType))
+ if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
videoRequest.VideoCodec = "copy";
}
- //if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream))
- //{
- // request.AudioCodec = "copy";
- //}
+ if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs))
+ {
+ request.AudioCodec = "copy";
+ }
}
return state;
}
- private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream, VideoType videoType)
+ private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream)
{
if (videoStream.IsInterlaced)
{
return false;
}
- // Not going to attempt this with folder rips
- if (videoType != VideoType.VideoFile)
- {
- return false;
- }
-
// Source and target codecs must match
if (!string.Equals(request.VideoCodec, videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{
@@ -1584,13 +1584,13 @@ namespace MediaBrowser.Api.Playback
}
}
- return SupportsAutomaticVideoStreamCopy;
+ return request.EnableAutoStreamCopy;
}
- private bool CanStreamCopyAudio(StreamRequest request, MediaStream audioStream)
+ private bool CanStreamCopyAudio(StreamRequest request, MediaStream audioStream, List<string> supportedAudioCodecs)
{
// Source and target codecs must match
- if (string.IsNullOrEmpty(request.AudioCodec) || !string.Equals(request.AudioCodec, audioStream.Codec, StringComparison.OrdinalIgnoreCase))
+ if (string.IsNullOrEmpty(audioStream.Codec) || !supportedAudioCodecs.Contains(audioStream.Codec, StringComparer.OrdinalIgnoreCase))
{
return false;
}
@@ -1623,15 +1623,7 @@ namespace MediaBrowser.Api.Playback
}
}
- return SupportsAutomaticVideoStreamCopy;
- }
-
- protected virtual bool SupportsAutomaticVideoStreamCopy
- {
- get
- {
- return false;
- }
+ return true;
}
private void ApplyDeviceProfileSettings(StreamState state)
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 6e71e503f3..b5cd1bd409 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -24,7 +24,8 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public abstract class BaseHlsService : BaseStreamingService
{
- protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager)
+ protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager)
+ : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager)
{
}
@@ -77,6 +78,7 @@ namespace MediaBrowser.Api.Playback.Hls
return ProcessRequestAsync(request).Result;
}
+ private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1);
/// <summary>
/// Processes the request async.
/// </summary>
@@ -103,32 +105,41 @@ namespace MediaBrowser.Api.Playback.Hls
}
var playlist = GetOutputFilePath(state);
- var isPlaylistNewlyCreated = false;
- // If the playlist doesn't already exist, startup ffmpeg
- if (!File.Exists(playlist))
+ if (File.Exists(playlist))
{
- isPlaylistNewlyCreated = true;
-
+ ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
+ }
+ else
+ {
+ await FfmpegStartLock.WaitAsync().ConfigureAwait(false);
try
{
- await StartFfMpeg(state, playlist).ConfigureAwait(false);
+ if (File.Exists(playlist))
+ {
+ ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
+ }
+ else
+ {
+ // If the playlist doesn't already exist, startup ffmpeg
+ try
+ {
+ await StartFfMpeg(state, playlist).ConfigureAwait(false);
+ }
+ catch
+ {
+ state.Dispose();
+ throw;
+ }
+ }
+
+ await WaitForMinimumSegmentCount(playlist, GetSegmentWait()).ConfigureAwait(false);
}
- catch
+ finally
{
- state.Dispose();
- throw;
+ FfmpegStartLock.Release();
}
}
- else
- {
- ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
- }
-
- if (isPlaylistNewlyCreated)
- {
- await WaitForMinimumSegmentCount(playlist, GetSegmentWait()).ConfigureAwait(false);
- }
int audioBitrate;
int videoBitrate;
@@ -295,7 +306,7 @@ namespace MediaBrowser.Api.Playback.Hls
// If performSubtitleConversions is true we're actually starting ffmpeg
var startNumberParam = performSubtitleConversions ? GetStartNumber(state).ToString(UsCulture) : "0";
-
+
var args = string.Format("{0} {1} -i {2}{3} -map_metadata -1 -threads {4} {5} {6} -sc_threshold 0 {7} -hls_time {8} -start_number {9} -hls_list_size {10} \"{11}\"",
itsOffset,
inputModifier,
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index bedf742c2c..1bca4cae9a 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -98,14 +98,6 @@ namespace MediaBrowser.Api.Playback.Hls
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
}
- protected override bool SupportsAutomaticVideoStreamCopy
- {
- get
- {
- return true;
- }
- }
-
/// <summary>
/// Gets the specified request.
/// </summary>
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index add517b5da..d5355783e8 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -171,5 +171,13 @@ namespace MediaBrowser.Api.Playback
return Width.HasValue || Height.HasValue;
}
}
+
+ [ApiMember(Name = "EnableAutoStreamCopy", Description = "Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool EnableAutoStreamCopy { get; set; }
+
+ public VideoStreamRequest()
+ {
+ EnableAutoStreamCopy = true;
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index ce7d79917a..e41f296638 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -67,10 +67,13 @@ namespace MediaBrowser.Api.Playback
public string AudioSync = "1";
public string VideoSync = "vfr";
+ public List<string> SupportedAudioCodecs { get; set; }
+
public StreamState(ILiveTvManager liveTvManager, ILogger logger)
{
_liveTvManager = liveTvManager;
_logger = logger;
+ SupportedAudioCodecs = new List<string>();
}
public string InputAudioSync { get; set; }
diff --git a/MediaBrowser.Api/SessionsService.cs b/MediaBrowser.Api/SessionsService.cs
index 5c1c716417..b71cf67b66 100644
--- a/MediaBrowser.Api/SessionsService.cs
+++ b/MediaBrowser.Api/SessionsService.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Session;
using ServiceStack;
@@ -32,10 +31,10 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class BrowseTo
+ /// Class DisplayContent
/// </summary>
[Route("/Sessions/{Id}/Viewing", "POST", Summary = "Instructs a session to browse to an item or view")]
- public class BrowseTo : IReturnVoid
+ public class DisplayContent : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
@@ -218,6 +217,7 @@ namespace MediaBrowser.Api
public Guid UserId { get; set; }
}
+ [Route("/Sessions/Capabilities", "POST", Summary = "Updates capabilities for a device")]
[Route("/Sessions/{Id}/Capabilities", "POST", Summary = "Updates capabilities for a device")]
public class PostCapabilities : IReturnVoid
{
@@ -226,7 +226,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public Guid Id { get; set; }
+ public string Id { get; set; }
[ApiMember(Name = "PlayableMediaTypes", Description = "A list of playable media types, comma delimited. Audio, Video, Book, Game, Photo.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string PlayableMediaTypes { get; set; }
@@ -307,7 +307,7 @@ namespace MediaBrowser.Api
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(BrowseTo request)
+ public void Post(DisplayContent request)
{
var command = new BrowseRequest
{
@@ -421,7 +421,11 @@ namespace MediaBrowser.Api
public void Post(PostCapabilities request)
{
- _sessionManager.ReportCapabilities(request.Id, new SessionCapabilities
+ if (string.IsNullOrWhiteSpace(request.Id))
+ {
+ request.Id = GetSession().Id.ToString("N");
+ }
+ _sessionManager.ReportCapabilities(new Guid(request.Id), new SessionCapabilities
{
PlayableMediaTypes = request.PlayableMediaTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),