aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api')
-rw-r--r--MediaBrowser.Api/ConnectService.cs47
-rw-r--r--MediaBrowser.Api/Devices/DeviceService.cs45
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs18
-rw-r--r--MediaBrowser.Api/ItemLookupService.cs19
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs82
-rw-r--r--MediaBrowser.Api/Playback/Hls/MpegDashService.cs120
-rw-r--r--MediaBrowser.Api/Session/SessionsService.cs4
-rw-r--r--MediaBrowser.Api/UserService.cs1
8 files changed, 283 insertions, 53 deletions
diff --git a/MediaBrowser.Api/ConnectService.cs b/MediaBrowser.Api/ConnectService.cs
index 9ea75d4ac..5a2c04ab6 100644
--- a/MediaBrowser.Api/ConnectService.cs
+++ b/MediaBrowser.Api/ConnectService.cs
@@ -1,6 +1,8 @@
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Connect;
using ServiceStack;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace MediaBrowser.Api
@@ -22,6 +24,30 @@ namespace MediaBrowser.Api
public string Id { get; set; }
}
+ [Route("/Connect/Invite", "POST", Summary = "Creates a Connect link for a user")]
+ public class CreateConnectInvite : IReturn<UserLinkResult>
+ {
+ [ApiMember(Name = "ConnectUsername", Description = "Connect username", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string ConnectUsername { get; set; }
+
+ [ApiMember(Name = "SendingUserId", Description = "Sending User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string SendingUserId { get; set; }
+ }
+
+
+ [Route("/Connect/Pending", "GET", Summary = "Creates a Connect link for a user")]
+ public class GetPendingGuests : IReturn<List<ConnectAuthorization>>
+ {
+ }
+
+
+ [Route("/Connect/Pending", "DELETE", Summary = "Deletes a Connect link for a user")]
+ public class DeleteAuthorization : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Authorization Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
[Authenticated(Roles = "Admin")]
public class ConnectService : BaseApiService
{
@@ -37,9 +63,28 @@ namespace MediaBrowser.Api
return _connectManager.LinkUser(request.Id, request.ConnectUsername);
}
+ public object Post(CreateConnectInvite request)
+ {
+ return _connectManager.InviteUser(request.SendingUserId, request.ConnectUsername);
+ }
+
public void Delete(DeleteConnectLink request)
{
- var task = _connectManager.RemoveLink(request.Id);
+ var task = _connectManager.RemoveConnect(request.Id);
+
+ Task.WaitAll(task);
+ }
+
+ public async Task<object> Get(GetPendingGuests request)
+ {
+ var result = await _connectManager.GetPendingGuests().ConfigureAwait(false);
+
+ return ToOptimizedResult(result);
+ }
+
+ public void Delete(DeleteAuthorization request)
+ {
+ var task = _connectManager.CancelAuthorization(request.Id);
Task.WaitAll(task);
}
diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs
index 87419e440..bb8d8eda3 100644
--- a/MediaBrowser.Api/Devices/DeviceService.cs
+++ b/MediaBrowser.Api/Devices/DeviceService.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.Session;
using ServiceStack;
using ServiceStack.Web;
using System.Collections.Generic;
@@ -49,6 +50,27 @@ namespace MediaBrowser.Api.Devices
public Stream RequestStream { get; set; }
}
+ [Route("/Devices/Info", "GET", Summary = "Gets device info")]
+ public class GetDeviceInfo : IReturn<DeviceInfo>
+ {
+ [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Devices/Capabilities", "GET", Summary = "Gets device capabilities")]
+ public class GetDeviceCapabilities : IReturn<ClientCapabilities>
+ {
+ [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Devices/Options", "POST", Summary = "Updates device options")]
+ public class PostDeviceOptions : DeviceOptions, IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string Id { get; set; }
+ }
+
[Authenticated]
public class DeviceService : BaseApiService
{
@@ -59,6 +81,27 @@ namespace MediaBrowser.Api.Devices
_deviceManager = deviceManager;
}
+ public void Post(PostDeviceOptions request)
+ {
+ var task = _deviceManager.UpdateDeviceInfo(request.Id, new DeviceOptions
+ {
+ CustomName = request.CustomName,
+ CameraUploadPath = request.CameraUploadPath
+ });
+
+ Task.WaitAll(task);
+ }
+
+ public object Get(GetDeviceInfo request)
+ {
+ return ToOptimizedResult(_deviceManager.GetDevice(request.Id));
+ }
+
+ public object Get(GetDeviceCapabilities request)
+ {
+ return ToOptimizedResult(_deviceManager.GetCapabilities(request.Id));
+ }
+
public object Get(GetDevices request)
{
var devices = _deviceManager.GetDevices();
@@ -67,7 +110,7 @@ namespace MediaBrowser.Api.Devices
{
var val = request.SupportsContentUploading.Value;
- devices = devices.Where(i => i.Capabilities.SupportsContentUploading == val);
+ devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsContentUploading == val);
}
return ToOptimizedResult(devices.ToList());
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index d455290be..ca54249b3 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -321,6 +321,20 @@ namespace MediaBrowser.Api.Images
{
var fileInfo = new FileInfo(info.Path);
+ int? width = null;
+ int? height = null;
+
+ try
+ {
+ var size = _imageProcessor.GetImageSize(info.Path, info.DateModified);
+
+ width = Convert.ToInt32(size.Width);
+ height = Convert.ToInt32(size.Height);
+ }
+ catch
+ {
+
+ }
return new ImageInfo
{
Path = info.Path,
@@ -328,8 +342,8 @@ namespace MediaBrowser.Api.Images
ImageType = info.Type,
ImageTag = _imageProcessor.GetImageCacheTag(item, info),
Size = fileInfo.Length,
- Width = info.Width ?? 0,
- Height = info.Height ?? 0
+ Width = width,
+ Height = height
};
}
catch (Exception ex)
diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs
index 67167da99..846d71802 100644
--- a/MediaBrowser.Api/ItemLookupService.cs
+++ b/MediaBrowser.Api/ItemLookupService.cs
@@ -207,15 +207,16 @@ namespace MediaBrowser.Api
{
var item = _libraryManager.GetItemById(new Guid(request.Id));
- foreach (var key in request.ProviderIds)
- {
- var value = key.Value;
-
- if (!string.IsNullOrWhiteSpace(value))
- {
- item.SetProviderId(key.Key, value);
- }
- }
+ //foreach (var key in request.ProviderIds)
+ //{
+ // var value = key.Value;
+
+ // if (!string.IsNullOrWhiteSpace(value))
+ // {
+ // item.SetProviderId(key.Key, value);
+ // }
+ //}
+ item.ProviderIds = request.ProviderIds;
var service = new ItemRefreshService(_libraryManager)
{
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 8b5395488..919e51ecc 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -825,6 +825,23 @@ namespace MediaBrowser.Api.Playback
return MediaEncoder.GetInputArgument(inputPath, protocol);
}
+ private MediaProtocol GetProtocol(string path)
+ {
+ if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Http;
+ }
+ if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Rtsp;
+ }
+ if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
+ {
+ return MediaProtocol.Rtmp;
+ }
+ return MediaProtocol.File;
+ }
+
private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
{
if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
@@ -842,20 +859,12 @@ namespace MediaBrowser.Api.Playback
state.LiveTvStreamId = streamInfo.Id;
- if (!string.IsNullOrEmpty(streamInfo.Path))
- {
- state.MediaPath = streamInfo.Path;
- state.InputProtocol = MediaProtocol.File;
+ state.MediaPath = streamInfo.Path;
+ state.InputProtocol = streamInfo.Protocol;
- await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
- }
- else if (!string.IsNullOrEmpty(streamInfo.Url))
- {
- state.MediaPath = streamInfo.Url;
- state.InputProtocol = MediaProtocol.Http;
- }
+ await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
- AttachMediaStreamInfo(state, streamInfo.MediaStreams, state.VideoRequest, state.RequestedUrl);
+ AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl);
checkCodecs = true;
}
@@ -866,20 +875,12 @@ namespace MediaBrowser.Api.Playback
state.LiveTvStreamId = streamInfo.Id;
- if (!string.IsNullOrEmpty(streamInfo.Path))
- {
- state.MediaPath = streamInfo.Path;
- state.InputProtocol = MediaProtocol.File;
-
- await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
- }
- else if (!string.IsNullOrEmpty(streamInfo.Url))
- {
- state.MediaPath = streamInfo.Url;
- state.InputProtocol = MediaProtocol.Http;
- }
+ state.MediaPath = streamInfo.Path;
+ state.InputProtocol = streamInfo.Protocol;
- AttachMediaStreamInfo(state, streamInfo.MediaStreams, state.VideoRequest, state.RequestedUrl);
+ await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
+
+ AttachMediaStreamInfo(state, streamInfo, state.VideoRequest, state.RequestedUrl);
checkCodecs = true;
}
@@ -991,6 +992,16 @@ namespace MediaBrowser.Api.Playback
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
}
+ if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive)
+ {
+ await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);
+
+ if (state.ReadInputAtNativeFramerate)
+ {
+ await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);
+ }
+ }
+
return transcodingJob;
}
@@ -1591,7 +1602,9 @@ namespace MediaBrowser.Api.Playback
state.InputFileSize = mediaSource.Size;
state.InputBitrate = mediaSource.Bitrate;
- if (item is Video)
+ var video = item as Video;
+
+ if (video != null)
{
state.IsInputVideo = true;
@@ -1686,6 +1699,23 @@ namespace MediaBrowser.Api.Playback
}
private void AttachMediaStreamInfo(StreamState state,
+ ChannelMediaInfo mediaInfo,
+ VideoStreamRequest videoRequest,
+ string requestedUrl)
+ {
+ var mediaSource = mediaInfo.ToMediaSource();
+
+ state.InputProtocol = mediaSource.Protocol;
+ state.MediaPath = mediaSource.Path;
+ state.RunTimeTicks = mediaSource.RunTimeTicks;
+ state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders;
+ state.InputBitrate = mediaSource.Bitrate;
+ state.InputFileSize = mediaSource.Size;
+
+ AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest, requestedUrl);
+ }
+
+ private void AttachMediaStreamInfo(StreamState state,
List<MediaStream> mediaStreams,
VideoStreamRequest videoRequest,
string requestedUrl)
diff --git a/MediaBrowser.Api/Playback/Hls/MpegDashService.cs b/MediaBrowser.Api/Playback/Hls/MpegDashService.cs
index caddbd9a1..baa623380 100644
--- a/MediaBrowser.Api/Playback/Hls/MpegDashService.cs
+++ b/MediaBrowser.Api/Playback/Hls/MpegDashService.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.IO;
+using System.Security;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@@ -41,7 +42,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <value>The segment id.</value>
public string SegmentId { get; set; }
}
-
+
public class MpegDashService : BaseHlsService
{
protected INetworkManager NetworkManager { get; private set; }
@@ -102,10 +103,13 @@ namespace MediaBrowser.Api.Playback.Hls
var builder = new StringBuilder();
- var duration = "PT0H02M11.00S";
+ var time = TimeSpan.FromTicks(state.RunTimeTicks.Value);
+
+ var duration = "PT" + time.Hours.ToString("00", UsCulture) + "H" + time.Minutes.ToString("00", UsCulture) + "M" + time.Seconds.ToString("00", UsCulture) + ".00S";
+ builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
builder.AppendFormat(
- "<MPD type=\"static\" minBufferTime=\"PT2S\" mediaPresentationDuration=\"{0}\" profiles=\"urn:mpeg:dash:profile:mp2t-simple:2011\" xmlns=\"urn:mpeg:DASH:schema:MPD:2011\" maxSegmentDuration=\"PT{1}S\">",
+ "<MPD xmlns=\"urn:mpeg:dash:schema:mpd:2011\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\" minBufferTime=\"PT2.00S\" mediaPresentationDuration=\"{0}\" maxSegmentDuration=\"PT{1}S\" type=\"static\" profiles=\"urn:mpeg:dash:profile:mp2t-simple:2011\">",
duration,
state.SegmentLength.ToString(CultureInfo.InvariantCulture));
@@ -116,9 +120,13 @@ namespace MediaBrowser.Api.Playback.Hls
builder.Append("<AdaptationSet segmentAlignment=\"true\">");
builder.Append("<ContentComponent id=\"1\" contentType=\"video\"/>");
- builder.Append("<ContentComponent id=\"2\" contentType=\"audio\" lang=\"eng\"/>");
- builder.Append(GetRepresentationOpenElement(state));
+ var lang = state.AudioStream != null ? state.AudioStream.Language : null;
+ if (string.IsNullOrWhiteSpace(lang)) lang = "und";
+
+ builder.AppendFormat("<ContentComponent id=\"2\" contentType=\"audio\" lang=\"{0}\"/>", lang);
+
+ builder.Append(GetRepresentationOpenElement(state, lang));
AppendSegmentList(state, builder);
@@ -131,10 +139,97 @@ namespace MediaBrowser.Api.Playback.Hls
return builder.ToString();
}
- private string GetRepresentationOpenElement(StreamState state)
+ private string GetRepresentationOpenElement(StreamState state, string language)
+ {
+ var codecs = GetVideoCodecDescriptor(state) + "," + GetAudioCodecDescriptor(state);
+
+ var xml = "<Representation id=\"1\" mimeType=\"video/mp2t\" startWithSAP=\"1\" codecs=\"" + codecs + "\"";
+
+ if (state.OutputWidth.HasValue)
+ {
+ xml += " width=\"" + state.OutputWidth.Value.ToString(UsCulture) + "\"";
+ }
+ if (state.OutputHeight.HasValue)
+ {
+ xml += " height=\"" + state.OutputHeight.Value.ToString(UsCulture) + "\"";
+ }
+ if (state.OutputAudioSampleRate.HasValue)
+ {
+ xml += " sampleRate=\"" + state.OutputAudioSampleRate.Value.ToString(UsCulture) + "\"";
+ }
+
+ if (state.TotalOutputBitrate.HasValue)
+ {
+ xml += " bandwidth=\"" + state.TotalOutputBitrate.Value.ToString(UsCulture) + "\"";
+ }
+
+ xml += ">";
+
+ return xml;
+ }
+
+ private string GetVideoCodecDescriptor(StreamState state)
+ {
+ // https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
+
+ var level = state.TargetVideoLevel ?? 0;
+ var profile = state.TargetVideoProfile ?? string.Empty;
+
+ if (profile.IndexOf("high", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ if (level >= 4.1)
+ {
+ return "avc1.640028";
+ }
+
+ if (level >= 4)
+ {
+ return "avc1.640028";
+ }
+
+ return "avc1.64001f";
+ }
+
+ if (profile.IndexOf("main", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ if (level >= 4)
+ {
+ return "avc1.4d0028";
+ }
+
+ if (level >= 3.1)
+ {
+ return "avc1.4d001f";
+ }
+
+ return "avc1.4d001e";
+ }
+
+ if (level >= 3.1)
+ {
+ return "avc1.42001f";
+ }
+
+ return "avc1.42001e";
+ }
+
+ private string GetAudioCodecDescriptor(StreamState state)
{
- return
- "<Representation id=\"1\" mimeType=\"video/mp2t\" codecs=\"avc1.640028,mp4a.40.02\" width=\"1280\" height=\"1024\" sampleRate=\"44100\" numChannels=\"2\" lang=\"und\" startWithSAP=\"1\" bandwidth=\"317599\">";
+ // https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
+
+ if (string.Equals(state.OutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
+ {
+ return "mp4a.40.34";
+ }
+
+ // AAC 5ch
+ if (state.OutputAudioChannels.HasValue && state.OutputAudioChannels.Value >= 5)
+ {
+ return "mp4a.40.5";
+ }
+
+ // AAC 2ch
+ return "mp4a.40.2";
}
public object Get(GetDashSegment request)
@@ -146,16 +241,17 @@ namespace MediaBrowser.Api.Playback.Hls
{
var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
- builder.Append("<SegmentList timescale=\"1000\" duration=\"10000\">");
-
var queryStringIndex = Request.RawUrl.IndexOf('?');
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
var index = 0;
+ builder.Append("<SegmentList timescale=\"1000\" duration=\"10000\">");
while (seconds > 0)
{
- builder.AppendFormat("<SegmentURL media=\"{0}.ts{1}\"/>", index.ToString(UsCulture), queryString);
+ var segmentUrl = string.Format("{0}.ts{1}", index.ToString(UsCulture), SecurityElement.Escape(queryString));
+
+ builder.AppendFormat("<SegmentURL media=\"{0}\"/>", segmentUrl);
seconds -= state.SegmentLength;
index++;
diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs
index 014bedbd9..74dccb7af 100644
--- a/MediaBrowser.Api/Session/SessionsService.cs
+++ b/MediaBrowser.Api/Session/SessionsService.cs
@@ -499,9 +499,9 @@ namespace MediaBrowser.Api.Session
}
_sessionManager.ReportCapabilities(request.Id, new ClientCapabilities
{
- PlayableMediaTypes = request.PlayableMediaTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
+ PlayableMediaTypes = (request.PlayableMediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
- SupportedCommands = request.SupportedCommands.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
+ SupportedCommands = (request.SupportedCommands ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
SupportsMediaControl = request.SupportsMediaControl,
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 10595d926..a30ecf7d6 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -290,6 +290,7 @@ namespace MediaBrowser.Api
}
await _sessionMananger.RevokeUserTokens(user.Id.ToString("N")).ConfigureAwait(false);
+
await _userManager.DeleteUser(user).ConfigureAwait(false);
}