diff options
Diffstat (limited to 'MediaBrowser.Api')
| -rw-r--r-- | MediaBrowser.Api/ConnectService.cs | 47 | ||||
| -rw-r--r-- | MediaBrowser.Api/Devices/DeviceService.cs | 45 | ||||
| -rw-r--r-- | MediaBrowser.Api/Images/ImageService.cs | 18 | ||||
| -rw-r--r-- | MediaBrowser.Api/ItemLookupService.cs | 19 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/BaseStreamingService.cs | 82 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/MpegDashService.cs | 120 | ||||
| -rw-r--r-- | MediaBrowser.Api/Session/SessionsService.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Api/UserService.cs | 1 |
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); } |
