From 3632d679fb15189a2ab14983c7b12e15efef22e4 Mon Sep 17 00:00:00 2001 From: Tim Hobbs Date: Mon, 24 Mar 2014 13:02:58 -0700 Subject: [PATCH 01/24] Image language fix The language only needs to be added if it is not already part of the list. It is not necessarily a problem, it just winds up with duplicates. --- MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs | 2 +- MediaBrowser.Providers/Movies/MovieDbProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs index 29032ca298..cf7904e5cb 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs +++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs @@ -172,7 +172,7 @@ namespace MediaBrowser.Providers.BoxSets if (!string.IsNullOrEmpty(language)) { // If preferred language isn't english, get those images too - if (imageLanguages.Contains(language, StringComparer.OrdinalIgnoreCase)) + if (!imageLanguages.Contains(language, StringComparer.OrdinalIgnoreCase)) { imageLanguages.Add(language); } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 05f2d485e6..7a63713f19 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -295,7 +295,7 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(language)) { // If preferred language isn't english, get those images too - if (imageLanguages.Contains(language, StringComparer.OrdinalIgnoreCase)) + if (!imageLanguages.Contains(language, StringComparer.OrdinalIgnoreCase)) { imageLanguages.Add(language); } From f245fffad10bde5bf559e67ab5342df8dd2d0aa9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 25 Mar 2014 01:25:03 -0400 Subject: [PATCH 02/24] implement dlna headers --- .../Playback/BaseStreamingService.cs | 244 ++++++++++++++---- .../Playback/Hls/BaseHlsService.cs | 4 +- .../Playback/Hls/DynamicHlsService.cs | 4 +- .../Playback/Hls/VideoHlsService.cs | 3 +- .../Playback/Progressive/AudioService.cs | 3 +- .../BaseProgressiveStreamingService.cs | 91 +------ .../Playback/Progressive/VideoService.cs | 3 +- MediaBrowser.Api/Playback/StreamState.cs | 14 + .../Dlna/DeviceIdentification.cs | 4 +- MediaBrowser.Controller/Dlna/DeviceProfile.cs | 147 ++++++++++- MediaBrowser.Controller/Dlna/IDlnaManager.cs | 7 + MediaBrowser.Controller/Dlna/MediaProfile.cs | 5 + .../Dlna/TranscodingProfile.cs | 12 +- .../Providers/BaseItemXmlParser.cs | 88 ------- MediaBrowser.Dlna/DlnaManager.cs | 37 ++- MediaBrowser.Dlna/MediaBrowser.Dlna.csproj | 5 +- MediaBrowser.Dlna/PlayTo/Device.cs | 17 +- MediaBrowser.Dlna/PlayTo/DeviceInfo.cs | 25 +- MediaBrowser.Dlna/PlayTo/DidlBuilder.cs | 85 +++--- MediaBrowser.Dlna/PlayTo/DlnaController.cs | 25 +- .../PlayTo/DlnaControllerFactory.cs | 31 --- MediaBrowser.Dlna/PlayTo/PlayToManager.cs | 58 ++--- MediaBrowser.Dlna/PlayTo/PlaylistItem.cs | 2 - .../PlayTo/PlaylistItemFactory.cs | 17 +- MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs | 20 +- MediaBrowser.Dlna/PlayTo/StreamHelper.cs | 6 - MediaBrowser.Dlna/Profiles/DefaultProfile.cs | 3 +- .../Profiles/Foobar2000Profile.cs | 27 ++ .../Profiles/SamsungSmartTvProfile.cs | 7 + .../Profiles/SonyBlurayPlayer2013Profile.cs | 2 + .../Profiles/SonyBlurayPlayerProfile.cs | 2 + MediaBrowser.Dlna/Server/Headers.cs | 2 +- MediaBrowser.Dlna/Server/RawHeaders.cs | 16 -- MediaBrowser.Dlna/Server/SsdpHandler.cs | 6 +- .../{PlayTo => Ssdp}/SsdpHelper.cs | 44 ++-- .../Savers/MovieXmlSaver.cs | 15 +- .../Savers/SeriesXmlSaver.cs | 10 +- .../Savers/XmlSaverHelpers.cs | 28 +- MediaBrowser.Providers/TV/SeriesXmlParser.cs | 2 + .../MediaEncoder/MediaEncoder.cs | 2 +- 40 files changed, 645 insertions(+), 478 deletions(-) delete mode 100644 MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs create mode 100644 MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs delete mode 100644 MediaBrowser.Dlna/Server/RawHeaders.cs rename MediaBrowser.Dlna/{PlayTo => Ssdp}/SsdpHelper.cs (57%) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 7dcb06f7b3..f65949ac7d 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -65,6 +66,7 @@ namespace MediaBrowser.Api.Playback protected IItemRepository ItemRepository { get; private set; } protected ILiveTvManager LiveTvManager { get; private set; } + protected IDlnaManager DlnaManager { get; private set; } /// /// Initializes a new instance of the class. @@ -77,8 +79,9 @@ namespace MediaBrowser.Api.Playback /// The dto service. /// The file system. /// The item repository. - protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager) + protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) { + DlnaManager = dlnaManager; EncodingManager = encodingManager; LiveTvManager = liveTvManager; ItemRepository = itemRepository; @@ -774,29 +777,24 @@ namespace MediaBrowser.Api.Playback { var codec = request.AudioCodec; - if (!string.IsNullOrEmpty(codec)) + if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) - { - return "aac -strict experimental"; - } - if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) - { - return "libmp3lame"; - } - if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase)) - { - return "libvorbis"; - } - if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase)) - { - return "wmav2"; - } - - return codec.ToLower(); + return "aac -strict experimental"; + } + if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase)) + { + return "libmp3lame"; + } + if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase)) + { + return "libvorbis"; + } + if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase)) + { + return "wmav2"; } - return "copy"; + return codec.ToLower(); } /// @@ -1211,97 +1209,86 @@ namespace MediaBrowser.Api.Playback } if (i == 0) - { - // Device profile name - } - else if (i == 1) { request.DeviceId = val; } - else if (i == 2) + else if (i == 1) { request.MediaSourceId = val; } - else if (i == 3) + else if (i == 2) { request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); } - else if (i == 4) + else if (i == 3) { if (videoRequest != null) { videoRequest.VideoCodec = val; } } - else if (i == 5) + else if (i == 4) { request.AudioCodec = val; } - else if (i == 6) + else if (i == 5) { if (videoRequest != null) { videoRequest.AudioStreamIndex = int.Parse(val, UsCulture); } } - else if (i == 7) + else if (i == 6) { if (videoRequest != null) { videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture); } } - else if (i == 8) + else if (i == 7) { if (videoRequest != null) { videoRequest.VideoBitRate = int.Parse(val, UsCulture); } } - else if (i == 9) + else if (i == 8) { request.AudioBitRate = int.Parse(val, UsCulture); } - else if (i == 10) + else if (i == 9) { request.MaxAudioChannels = int.Parse(val, UsCulture); } - else if (i == 11) + else if (i == 10) { if (videoRequest != null) { videoRequest.MaxWidth = int.Parse(val, UsCulture); } } - else if (i == 12) + else if (i == 11) { if (videoRequest != null) { videoRequest.MaxHeight = int.Parse(val, UsCulture); } } - else if (i == 13) + else if (i == 12) { if (videoRequest != null) { videoRequest.Framerate = int.Parse(val, UsCulture); } } - else if (i == 14) + else if (i == 13) { if (videoRequest != null) { request.StartTimeTicks = long.Parse(val, UsCulture); } } - else if (i == 15) - { - if (videoRequest != null) - { - videoRequest.Profile = val; - } - } - else if (i == 16) + else if (i == 14) { if (videoRequest != null) { @@ -1487,9 +1474,172 @@ namespace MediaBrowser.Api.Playback state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10; state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440; + ApplyDeviceProfileSettings(state); + return state; } + private void ApplyDeviceProfileSettings(StreamState state) + { + var headers = new Dictionary(); + + foreach (var key in Request.Headers.AllKeys) + { + headers[key] = Request.Headers[key]; + } + + var profile = DlnaManager.GetProfile(headers); + + var container = Path.GetExtension(state.RequestedUrl); + + if (string.IsNullOrEmpty(container)) + { + container = Path.GetExtension(GetOutputFilePath(state)); + } + + var audioCodec = state.Request.AudioCodec; + + if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null) + { + audioCodec = state.AudioStream.Codec; + } + + var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec; + + if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null) + { + videoCodec = state.VideoStream.Codec; + } + + var mediaProfile = state.VideoRequest == null ? + profile.GetAudioMediaProfile(container, audioCodec, state.AudioStream) : + profile.GetVideoMediaProfile(container, audioCodec, videoCodec, state.AudioStream, state.VideoStream); + + if (mediaProfile != null) + { + state.MimeType = mediaProfile.MimeType; + state.OrgPn = mediaProfile.OrgPn; + } + + var transcodingProfile = state.VideoRequest == null ? + profile.GetAudioTranscodingProfile(container, audioCodec) : + profile.GetVideoTranscodingProfile(container, audioCodec, videoCodec); + + if (transcodingProfile != null) + { + state.EstimateContentLength = transcodingProfile.EstimateContentLength; + state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; + state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; + + foreach (var setting in transcodingProfile.Settings) + { + switch (setting.Name) + { + case TranscodingSettingType.VideoProfile: + { + if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile)) + { + state.VideoRequest.Profile = setting.Value; + } + break; + } + default: + throw new ArgumentException("Unrecognized TranscodingSettingType"); + } + } + } + } + + + /// + /// Adds the dlna headers. + /// + /// The state. + /// The response headers. + /// if set to true [is statically streamed]. + /// true if XXXX, false otherwise + protected void AddDlnaHeaders(StreamState state, IDictionary responseHeaders, bool isStaticallyStreamed) + { + var timeSeek = GetHeader("TimeSeekRange.dlna.org"); + + if (!string.IsNullOrEmpty(timeSeek)) + { + ResultFactory.ThrowError(406, "Time seek not supported during encoding.", responseHeaders); + return; + } + + var transferMode = GetHeader("transferMode.dlna.org"); + responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode; + responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*"; + + var contentFeatures = string.Empty; + var extension = GetOutputFileExtension(state); + + // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none + var orgOp = isStaticallyStreamed || state.TranscodeSeekInfo == TranscodeSeekInfo.Bytes ? ";DLNA.ORG_OP=01" : ";DLNA.ORG_OP=00"; + + // 0 = native, 1 = transcoded + var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1"; + + const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000"; + + if (!string.IsNullOrWhiteSpace(state.OrgPn)) + { + contentFeatures = "DLNA.ORG_PN=" + state.OrgPn; + } + else if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MP3"; + } + else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=AAC_ISO"; + } + else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=WMABASE"; + } + else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=AVI"; + } + else if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MATROSKA"; + } + else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC"; + } + else if (string.Equals(extension, ".mpeg", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; + } + else if (string.Equals(extension, ".ts", StringComparison.OrdinalIgnoreCase)) + { + contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; + } + //else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase)) + //{ + // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; + //} + //else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase)) + //{ + // // ?? + // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; + //} + + if (!string.IsNullOrEmpty(contentFeatures)) + { + responseHeaders["contentFeatures.dlna.org"] = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); + } + + foreach (var item in responseHeaders) + { + Request.Response.AddHeader(item.Key, item.Value); + } + } + /// /// Enforces the resolution limit. /// @@ -1605,7 +1755,7 @@ namespace MediaBrowser.Api.Playback return "vorbis"; } - return null; + return "copy"; } /// diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index eb8f415e0d..198376d6a1 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -2,13 +2,13 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using System; using System.Collections.Generic; @@ -24,7 +24,7 @@ namespace MediaBrowser.Api.Playback.Hls /// 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) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager) + 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) { } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 66e8b0d149..ab0cd8871f 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -1,12 +1,12 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using ServiceStack; using System; @@ -60,7 +60,7 @@ namespace MediaBrowser.Api.Playback.Hls public class DynamicHlsService : BaseHlsService { - public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager) + public DynamicHlsService(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) { } diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index a2080995d0..1bca4cae9a 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -52,7 +53,7 @@ namespace MediaBrowser.Api.Playback.Hls /// public class VideoHlsService : BaseHlsService { - public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager) + public VideoHlsService(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) { } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 4d8d3a5816..ca206c0125 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; @@ -43,7 +44,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class AudioService : BaseProgressiveStreamingService { - public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, httpClient, imageProcessor) + public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor) { } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 9cb989fc21..dad8a51bd7 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -1,13 +1,13 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using ServiceStack.Web; using System; @@ -26,8 +26,7 @@ namespace MediaBrowser.Api.Playback.Progressive protected readonly IImageProcessor ImageProcessor; protected readonly IHttpClient HttpClient; - protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IHttpClient httpClient, IImageProcessor imageProcessor) - : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager) + protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) { HttpClient = httpClient; ImageProcessor = imageProcessor; @@ -100,92 +99,6 @@ namespace MediaBrowser.Api.Playback.Progressive return null; } - /// - /// Adds the dlna headers. - /// - /// The state. - /// The response headers. - /// if set to true [is statically streamed]. - /// true if XXXX, false otherwise - private void AddDlnaHeaders(StreamState state, IDictionary responseHeaders, bool isStaticallyStreamed) - { - var timeSeek = GetHeader("TimeSeekRange.dlna.org"); - - if (!string.IsNullOrEmpty(timeSeek)) - { - ResultFactory.ThrowError(406, "Time seek not supported during encoding.", responseHeaders); - return; - } - - var transferMode = GetHeader("transferMode.dlna.org"); - responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode; - responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*"; - - var contentFeatures = string.Empty; - var extension = GetOutputFileExtension(state); - - // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none - var orgOp = isStaticallyStreamed ? ";DLNA.ORG_OP=01" : ";DLNA.ORG_OP=00"; - - // 0 = native, 1 = transcoded - var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1"; - - const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000"; - - if (string.Equals(extension, ".mp3", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MP3"; - } - else if (string.Equals(extension, ".aac", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=AAC_ISO"; - } - else if (string.Equals(extension, ".wma", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=WMABASE"; - } - else if (string.Equals(extension, ".avi", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=AVI"; - } - else if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MATROSKA"; - } - else if (string.Equals(extension, ".mp4", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC"; - } - else if (string.Equals(extension, ".mpeg", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; - } - else if (string.Equals(extension, ".ts", StringComparison.OrdinalIgnoreCase)) - { - contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL"; - } - //else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase)) - //{ - // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; - //} - //else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase)) - //{ - // // ?? - // contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE"; - //} - - - if (!string.IsNullOrEmpty(contentFeatures)) - { - responseHeaders["contentFeatures.dlna.org"] = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); - } - - foreach (var item in responseHeaders) - { - Request.Response.AddHeader(item.Key, item.Value); - } - } - /// /// Gets the type of the transcoding job. /// diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 43dc6f0d49..0fc78f0e3d 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; @@ -58,7 +59,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class VideoService : BaseProgressiveStreamingService { - public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, httpClient, imageProcessor) + public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor) { } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index ecc5c93ef2..504d7d921b 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using System.Collections.Generic; @@ -77,8 +78,21 @@ namespace MediaBrowser.Api.Playback public string InputAudioCodec { get; set; } + public string MimeType { get; set; } + public string OrgPn { get; set; } + + // DLNA Settings + public bool EstimateContentLength { get; set; } + public bool EnableMpegtsM2TsMode { get; set; } + public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + public string GetMimeType(string outputPath) { + if (!string.IsNullOrEmpty(MimeType)) + { + return MimeType; + } + return MimeTypes.GetMimeType(outputPath); } } diff --git a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs index 461c77537c..7b8e3a1e72 100644 --- a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs +++ b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs @@ -41,9 +41,7 @@ namespace MediaBrowser.Controller.Dlna /// /// Gets or sets the manufacturer. /// - /// - /// The manufacturer. - /// + /// The manufacturer. public string Manufacturer { get; set; } /// /// Gets or sets the manufacturer URL. diff --git a/MediaBrowser.Controller/Dlna/DeviceProfile.cs b/MediaBrowser.Controller/Dlna/DeviceProfile.cs index f3de1bc34a..f34c4bf645 100644 --- a/MediaBrowser.Controller/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Controller/Dlna/DeviceProfile.cs @@ -1,4 +1,7 @@ - +using MediaBrowser.Model.Entities; +using System; +using System.Linq; + namespace MediaBrowser.Controller.Dlna { public class DeviceProfile @@ -9,12 +12,6 @@ namespace MediaBrowser.Controller.Dlna /// The name. public string Name { get; set; } - /// - /// Gets or sets the type of the client. - /// - /// The type of the client. - public string ClientType { get; set; } - /// /// Gets or sets the transcoding profiles. /// @@ -76,5 +73,141 @@ namespace MediaBrowser.Controller.Dlna CodecProfiles = new CodecProfile[] { }; ContainerProfiles = new ContainerProfile[] { }; } + + public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec) + { + container = (container ?? string.Empty).TrimStart('.'); + + return TranscodingProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Audio) + { + return false; + } + + if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty)) + { + return false; + } + + return true; + }); + } + + public TranscodingProfile GetVideoTranscodingProfile(string container, string audioCodec, string videoCodec) + { + container = (container ?? string.Empty).TrimStart('.'); + + return TranscodingProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Video) + { + return false; + } + + if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty)) + { + return false; + } + + if (!string.Equals(videoCodec, i.VideoCodec, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return true; + }); + } + + public MediaProfile GetAudioMediaProfile(string container, string audioCodec, MediaStream audioStream) + { + container = (container ?? string.Empty).TrimStart('.'); + + return MediaProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Audio) + { + return false; + } + + var containers = i.GetContainers().ToList(); + if (containers.Count > 0 && !containers.Contains(container)) + { + return false; + } + + var audioCodecs = i.GetAudioCodecs().ToList(); + if (audioCodecs.Count > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty)) + { + return false; + } + + return true; + }); + } + + public MediaProfile GetVideoMediaProfile(string container, string audioCodec, string videoCodec, MediaStream audioStream, MediaStream videoStream) + { + container = (container ?? string.Empty).TrimStart('.'); + + return MediaProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Video) + { + return false; + } + + var containers = i.GetContainers().ToList(); + if (containers.Count > 0 && !containers.Contains(container)) + { + return false; + } + + var audioCodecs = i.GetAudioCodecs().ToList(); + if (audioCodecs.Count > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty)) + { + return false; + } + + var videoCodecs = i.GetVideoCodecs().ToList(); + if (videoCodecs.Count > 0 && !videoCodecs.Contains(videoCodec ?? string.Empty)) + { + return false; + } + + return true; + }); + } + + public MediaProfile GetPhotoMediaProfile(string container) + { + container = (container ?? string.Empty).TrimStart('.'); + + return MediaProfiles.FirstOrDefault(i => + { + if (i.Type != DlnaProfileType.Photo) + { + return false; + } + + var containers = i.GetContainers().ToList(); + if (containers.Count > 0 && !containers.Contains(container)) + { + return false; + } + + return true; + }); + } } } diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs index 6de17e5511..22d13fc3ad 100644 --- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs +++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs @@ -16,6 +16,13 @@ namespace MediaBrowser.Controller.Dlna /// DlnaProfile. DeviceProfile GetDefaultProfile(); + /// + /// Gets the profile. + /// + /// The headers. + /// DeviceProfile. + DeviceProfile GetProfile(IDictionary headers); + /// /// Gets the profile. /// diff --git a/MediaBrowser.Controller/Dlna/MediaProfile.cs b/MediaBrowser.Controller/Dlna/MediaProfile.cs index 1d2613face..9a9b56ddd5 100644 --- a/MediaBrowser.Controller/Dlna/MediaProfile.cs +++ b/MediaBrowser.Controller/Dlna/MediaProfile.cs @@ -19,6 +19,11 @@ namespace MediaBrowser.Controller.Dlna { Conditions = new ProfileCondition[] {}; } + + public List GetContainers() + { + return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); + } public List GetAudioCodecs() { diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs index 007cb632e4..d4cfae9893 100644 --- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs @@ -1,4 +1,6 @@ - +using System.Collections.Generic; +using System.Linq; + namespace MediaBrowser.Controller.Dlna { public class TranscodingProfile @@ -11,7 +13,7 @@ namespace MediaBrowser.Controller.Dlna public string AudioCodec { get; set; } public bool EstimateContentLength { get; set; } - + public bool EnableMpegtsM2TsMode { get; set; } public TranscodeSeekInfo TranscodeSeekInfo { get; set; } public TranscodingSetting[] Settings { get; set; } @@ -21,7 +23,11 @@ namespace MediaBrowser.Controller.Dlna Settings = new TranscodingSetting[] { }; } - public bool EnableMpegtsM2TsMode { get; set; } + + public List GetAudioCodecs() + { + return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); + } } public class TranscodingSetting diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index d71c7af323..70b49efece 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -1,5 +1,4 @@ using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -284,22 +283,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "TagLine": - { - var tagline = reader.ReadElementContentAsString(); - - var hasTaglines = item as IHasTaglines; - if (hasTaglines != null) - { - if (!string.IsNullOrWhiteSpace(tagline)) - { - hasTaglines.AddTagline(tagline); - } - } - - break; - } - case "Language": { var val = reader.ReadElementContentAsString(); @@ -380,9 +363,7 @@ namespace MediaBrowser.Controller.Providers } case "ContentRating": - case "certification": case "MPAARating": - case "ESRBRating": { var rating = reader.ReadElementContentAsString(); @@ -415,7 +396,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "Runtime": case "RunningTime": { var text = reader.ReadElementContentAsString(); @@ -431,19 +411,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "Genre": - { - foreach (var name in SplitNames(reader.ReadElementContentAsString())) - { - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - item.AddGenre(name); - } - break; - } - case "AspectRatio": { var val = reader.ReadElementContentAsString(); @@ -587,7 +554,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "ReleaseYear": case "ProductionYear": { var val = reader.ReadElementContentAsString(); @@ -606,7 +572,6 @@ namespace MediaBrowser.Controller.Providers case "Rating": case "IMDBrating": - case "TGDBRating": { var rating = reader.ReadElementContentAsString(); @@ -683,22 +648,6 @@ namespace MediaBrowser.Controller.Providers } break; } - case "MusicbrainzId": - { - var mbz = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(mbz)) - { - if (item is MusicAlbum) - { - item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz); - } - else if (item is MusicArtist) - { - item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz); - } - } - break; - } case "MusicBrainzAlbumId": { var mbz = reader.ReadElementContentAsString(); @@ -802,9 +751,7 @@ namespace MediaBrowser.Controller.Providers } break; - case "IMDB_ID": case "IMDB": - case "IMDbId": var imDbId = reader.ReadElementContentAsString(); if (!string.IsNullOrWhiteSpace(imDbId)) { @@ -856,15 +803,6 @@ namespace MediaBrowser.Controller.Providers break; } - case "ParentalRating": - { - using (var subtree = reader.ReadSubtree()) - { - FetchFromParentalRatingNode(subtree, item); - } - break; - } - case "Studios": { using (var subtree = reader.ReadSubtree()) @@ -1227,32 +1165,6 @@ namespace MediaBrowser.Controller.Providers } } - /// - /// Fetches from parental rating node. - /// - /// The reader. - /// The item. - private void FetchFromParentalRatingNode(XmlReader reader, T item) - { - reader.MoveToContent(); - - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - // Removed support for "Value" tag as it conflicted with MPAA rating but leaving this function for possible - // future support of "Description" -ebr - - default: - reader.Skip(); - break; - } - } - } - } - /// /// Gets the persons from XML node. /// diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index c6da865cd7..78876d239c 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Dlna; using MediaBrowser.Dlna.Profiles; using MediaBrowser.Model.Serialization; +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; @@ -43,7 +44,8 @@ namespace MediaBrowser.Dlna new WdtvLiveProfile(), new DenonAvrProfile(), new LinksysDMA2100Profile(), - new LgTvProfile() + new LgTvProfile(), + new Foobar2000Profile() }; foreach (var item in list) @@ -124,5 +126,38 @@ namespace MediaBrowser.Dlna return true; } + + public DeviceProfile GetProfile(IDictionary headers) + { + return GetProfiles().FirstOrDefault(i => IsMatch(headers, i.Identification)) ?? + GetDefaultProfile(); + } + + private bool IsMatch(IDictionary headers, DeviceIdentification profileInfo) + { + return profileInfo.Headers.Any(i => IsMatch(headers, i)); + } + + private bool IsMatch(IDictionary headers, HttpHeaderInfo header) + { + string value; + + if (headers.TryGetValue(header.Name, out value)) + { + switch (header.Match) + { + case HeaderMatchType.Equals: + return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase); + case HeaderMatchType.Substring: + return value.IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1; + case HeaderMatchType.Regex: + return Regex.IsMatch(value, header.Value, RegexOptions.IgnoreCase); + default: + throw new ArgumentException("Unrecognized HeaderMatchType"); + } + } + + return false; + } } } \ No newline at end of file diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index bea281b614..bdfcae39b6 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -61,7 +61,6 @@ - Code @@ -70,7 +69,8 @@ - + + @@ -100,7 +100,6 @@ - diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index 2b43c019c0..a677cf5dd7 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -607,7 +607,7 @@ namespace MediaBrowser.Dlna.PlayTo url = "/" + url; var httpClient = new SsdpHttpClient(_httpClient, _config); - var document = await httpClient.GetDataAsync(new Uri(Properties.BaseUrl + url)); + var document = await httpClient.GetDataAsync(Properties.BaseUrl + url); AvCommands = TransportCommands.Create(document); } @@ -625,7 +625,7 @@ namespace MediaBrowser.Dlna.PlayTo url = "/" + url; var httpClient = new SsdpHttpClient(_httpClient, _config); - var document = await httpClient.GetDataAsync(new Uri(Properties.BaseUrl + url)); + var document = await httpClient.GetDataAsync(Properties.BaseUrl + url); RendererCommands = TransportCommands.Create(document); } @@ -646,7 +646,7 @@ namespace MediaBrowser.Dlna.PlayTo { var ssdpHttpClient = new SsdpHttpClient(httpClient, config); - var document = await ssdpHttpClient.GetDataAsync(url).ConfigureAwait(false); + var document = await ssdpHttpClient.GetDataAsync(url.ToString()).ConfigureAwait(false); var deviceProperties = new DeviceInfo(); @@ -681,10 +681,18 @@ namespace MediaBrowser.Dlna.PlayTo var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault(); if (presentationUrl != null) deviceProperties.PresentationUrl = presentationUrl.Value; + var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault(); if (modelUrl != null) deviceProperties.ModelUrl = modelUrl.Value; - + + var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault(); + if (serialNumber != null) + deviceProperties.SerialNumber = serialNumber.Value; + + var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault(); + if (modelDescription != null) + deviceProperties.ModelDescription = modelDescription.Value; deviceProperties.BaseUrl = String.Format("http://{0}:{1}", url.Host, url.Port); @@ -724,7 +732,6 @@ namespace MediaBrowser.Dlna.PlayTo if (isRenderer) { - var device = new Device(deviceProperties, httpClient, logger, config); await device.GetRenderingProtocolAsync().ConfigureAwait(false); diff --git a/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs b/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs index c57e95c194..122549c7d3 100644 --- a/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs +++ b/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Dlna; +using System.Collections.Generic; namespace MediaBrowser.Dlna.PlayTo { @@ -17,27 +17,18 @@ namespace MediaBrowser.Dlna.PlayTo public string ClientType { get; set; } - private string _displayName = string.Empty; - public string DisplayName - { - get - { - return string.IsNullOrEmpty(_displayName) ? Name : _displayName; - } - set - { - _displayName = value; - } - } - public string ModelName { get; set; } public string ModelNumber { get; set; } + public string ModelDescription { get; set; } + public string ModelUrl { get; set; } public string Manufacturer { get; set; } + public string SerialNumber { get; set; } + public string ManufacturerUrl { get; set; } public string PresentationUrl { get; set; } @@ -75,7 +66,9 @@ namespace MediaBrowser.Dlna.PlayTo ModelNumber = ModelNumber, FriendlyName = Name, ManufacturerUrl = ManufacturerUrl, - ModelUrl = ModelUrl + ModelUrl = ModelUrl, + ModelDescription = ModelDescription, + SerialNumber = SerialNumber }; } } diff --git a/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs b/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs index 04f9a4644c..1327da1480 100644 --- a/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs +++ b/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs @@ -9,31 +9,27 @@ namespace MediaBrowser.Dlna.PlayTo { internal class DidlBuilder { - #region Constants - - internal const string CRLF = "\r\n"; - internal const string UNKNOWN = "Unknown"; - - internal const string DIDL_START = @"" + CRLF; - internal const string DIDL_TITLE = @" {0}" + CRLF; - internal const string DIDL_ARTIST = @"{0}" + CRLF; - internal const string DIDL_ALBUM = @"{0}" + CRLF; - internal const string DIDL_TRACKNUM = @"0" + CRLF; - internal const string DIDL_VIDEOCLASS = @" object.item.videoItem" + CRLF; - internal const string DIDL_AUDIOCLASS = @" object.item.audioItem.musicTrack" + CRLF; - internal const string DIDL_IMAGE = @" {0}" + CRLF + - @" {0}" + CRLF; - internal const string DIDL_RELEASEDATE = @" {0}" + CRLF; - internal const string DIDL_GENRE = @" {0}" + CRLF; - internal const string DESCRIPTION = @" {0}" + CRLF; - internal const string DIDL_VIDEO_RES = @" {4}" + CRLF; - internal const string DIDL_AUDIO_RES = @" {3}" + CRLF; - internal const string DIDL_IMAGE_RES = @" {0}" + CRLF; - internal const string DIDL_ALBUMIMAGE_RES = @" {0}" + CRLF; - internal const string DIDL_RATING = @" {0}" + CRLF; - internal const string DIDL_END = ""; - - #endregion + const string CRLF = "\r\n"; + const string UNKNOWN = "Unknown"; + + const string DIDL_START = @"" + CRLF; + const string DIDL_TITLE = @" {0}" + CRLF; + const string DIDL_ARTIST = @"{0}" + CRLF; + const string DIDL_ALBUM = @"{0}" + CRLF; + const string DIDL_TRACKNUM = @"{0}" + CRLF; + const string DIDL_VIDEOCLASS = @" object.item.videoItem" + CRLF; + const string DIDL_AUDIOCLASS = @" object.item.audioItem.musicTrack" + CRLF; + const string DIDL_IMAGE = @" {0}" + CRLF + + @" {0}" + CRLF; + const string DIDL_RELEASEDATE = @" {0}" + CRLF; + const string DIDL_GENRE = @" {0}" + CRLF; + const string DESCRIPTION = @" {0}" + CRLF; + const string DIDL_VIDEO_RES = @" {4}" + CRLF; + const string DIDL_AUDIO_RES = @" {3}" + CRLF; + const string DIDL_IMAGE_RES = @" {0}" + CRLF; + const string DIDL_ALBUMIMAGE_RES = @" {0}" + CRLF; + const string DIDL_RATING = @" {0}" + CRLF; + const string DIDL_END = ""; /// /// Builds a Didl MetaData object for the specified dto. @@ -44,7 +40,7 @@ namespace MediaBrowser.Dlna.PlayTo /// The stream URL. /// The streams. /// System.String. - internal static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable streams) + public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable streams) { string response = string.Format(DIDL_START, dto.Id, userId); response += string.Format(DIDL_TITLE, dto.Name.Replace("&", "and")); @@ -53,7 +49,12 @@ namespace MediaBrowser.Dlna.PlayTo else response += DIDL_AUDIOCLASS; - response += string.Format(DIDL_IMAGE, GetImageUrl(dto, serverAddress)); + var imageUrl = GetImageUrl(dto, serverAddress); + + if (!string.IsNullOrEmpty(imageUrl)) + { + response += string.Format(DIDL_IMAGE, imageUrl); + } response += string.Format(DIDL_RELEASEDATE, GetDateString(dto.PremiereDate)); //TODO Add genres to didl; @@ -63,7 +64,11 @@ namespace MediaBrowser.Dlna.PlayTo { response += string.Format(DESCRIPTION, UNKNOWN); response += GetVideoDIDL(dto, streamUrl, streams); - response += string.Format(DIDL_IMAGE_RES, GetImageUrl(dto, serverAddress)); + + if (!string.IsNullOrEmpty(imageUrl)) + { + response += string.Format(DIDL_IMAGE_RES, imageUrl); + } } else { @@ -74,25 +79,25 @@ namespace MediaBrowser.Dlna.PlayTo response += string.Format(DIDL_ARTIST, audio.Artists.FirstOrDefault() ?? UNKNOWN); response += string.Format(DIDL_ALBUM, audio.Album); - // TODO: Bad format string? response += string.Format(DIDL_TRACKNUM, audio.IndexNumber ?? 0); } response += GetAudioDIDL(dto, streamUrl, streams); - response += string.Format(DIDL_ALBUMIMAGE_RES, GetImageUrl(dto, serverAddress)); + + if (!string.IsNullOrEmpty(imageUrl)) + { + response += string.Format(DIDL_ALBUMIMAGE_RES, imageUrl); + } } response += DIDL_END; return response; - } - #region Private methods - private static string GetVideoDIDL(BaseItem dto, string streamUrl, IEnumerable streams) { - var videostream = streams.Where(stream => stream.Type == Model.Entities.MediaStreamType.Video).OrderBy(s => s.IsDefault).FirstOrDefault(); + var videostream = streams.Where(stream => stream.Type == MediaStreamType.Video).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault(); if (videostream == null) { @@ -105,7 +110,7 @@ namespace MediaBrowser.Dlna.PlayTo private static string GetAudioDIDL(BaseItem dto, string streamUrl, IEnumerable streams) { - var audiostream = streams.Where(stream => stream.Type == MediaStreamType.Audio).OrderBy(s => s.IsDefault).FirstOrDefault(); + var audiostream = streams.Where(stream => stream.Type == MediaStreamType.Audio).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault(); if (audiostream == null) { @@ -118,14 +123,14 @@ namespace MediaBrowser.Dlna.PlayTo private static string GetImageUrl(BaseItem dto, string serverAddress) { - var imageType = ImageType.Primary; + const ImageType imageType = ImageType.Primary; - if (!dto.HasImage(ImageType.Primary)) + if (!dto.HasImage(imageType)) { - dto = dto.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary)); + dto = dto.Parents.FirstOrDefault(i => i.HasImage(imageType)); } - return string.Format("{0}/Items/{1}/Images/{2}", serverAddress, dto.Id, imageType); + return dto == null ? null : string.Format("{0}/Items/{1}/Images/{2}", serverAddress, dto.Id, imageType); } private static string GetDurationString(BaseItem dto) @@ -148,7 +153,5 @@ namespace MediaBrowser.Dlna.PlayTo { return string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); } - - #endregion } } diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs index ecda07f0bc..0b0c03fcd4 100644 --- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs +++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs @@ -140,8 +140,15 @@ namespace MediaBrowser.Dlna.PlayTo { _updateTimer.Change(Timeout.Infinite, Timeout.Infinite); - //Session is inactive, mark it for Disposal and don't start the elapsed timer. - await _sessionManager.ReportSessionEnded(_session.Id); + try + { + // Session is inactive, mark it for Disposal and don't start the elapsed timer. + await _sessionManager.ReportSessionEnded(_session.Id); + } + catch (Exception ex) + { + _logger.ErrorException("Error in ReportSessionEnded", ex); + } } } @@ -156,7 +163,15 @@ namespace MediaBrowser.Dlna.PlayTo if (!_playbackStarted) { - await _sessionManager.OnPlaybackStart(new PlaybackInfo { Item = _currentItem, SessionId = _session.Id, CanSeek = true, QueueableMediaTypes = new List { "Audio", "Video" } }).ConfigureAwait(false); + await _sessionManager.OnPlaybackStart(new PlaybackInfo + { + Item = _currentItem, + SessionId = _session.Id, + CanSeek = true, + QueueableMediaTypes = new List { "Audio", "Video" } + + }).ConfigureAwait(false); + _playbackStarted = true; } @@ -403,7 +418,6 @@ namespace MediaBrowser.Dlna.PlayTo var playlistItem = GetPlaylistItem(item, streams, profile); playlistItem.StartPositionTicks = startPostionTicks; - playlistItem.DeviceProfileName = profile.Name; if (playlistItem.MediaType == DlnaProfileType.Audio) { @@ -414,8 +428,7 @@ namespace MediaBrowser.Dlna.PlayTo playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress); } - var didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams); - playlistItem.Didl = didl; + playlistItem.Didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams); return playlistItem; } diff --git a/MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs b/MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs deleted file mode 100644 index 720dc200b4..0000000000 --- a/MediaBrowser.Dlna/PlayTo/DlnaControllerFactory.cs +++ /dev/null @@ -1,31 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Dlna.PlayTo -{ - public class PlayToControllerFactory : ISessionControllerFactory - { - private readonly ISessionManager _sessionManager; - private readonly IItemRepository _itemRepository; - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly INetworkManager _networkManager; - - public PlayToControllerFactory(ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogManager logManager, INetworkManager networkManager) - { - _itemRepository = itemRepository; - _sessionManager = sessionManager; - _libraryManager = libraryManager; - _networkManager = networkManager; - _logger = logManager.GetLogger("PlayTo"); - } - - public ISessionController GetSessionController(SessionInfo session) - { - return null; - } - } -} diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs index ca76116feb..297f7a696b 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs @@ -5,16 +5,17 @@ using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Session; +using MediaBrowser.Dlna.Ssdp; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Session; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -126,10 +127,9 @@ namespace MediaBrowser.Dlna.PlayTo if (receivedBytes > 0) { - var rawData = Encoding.UTF8.GetString(receiveBuffer, 0, receivedBytes); - var uri = SsdpHelper.ParseSsdpResponse(rawData); + var headers = SsdpHelper.ParseSsdpResponse(receiveBuffer); - TryCreateController(uri); + TryCreateController(headers); } } @@ -146,13 +146,20 @@ namespace MediaBrowser.Dlna.PlayTo }, _tokenSource.Token, TaskCreationOptions.LongRunning); } - private void TryCreateController(Uri uri) + private void TryCreateController(IDictionary headers) { + string location; + + if (!headers.TryGetValue("Location", out location)) + { + return; + } + Task.Run(async () => { try { - await CreateController(uri).ConfigureAwait(false); + await CreateController(new Uri(location)).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -221,46 +228,25 @@ namespace MediaBrowser.Dlna.PlayTo if (device != null && device.RendererCommands != null && !_sessionManager.Sessions.Any(s => string.Equals(s.DeviceId, device.Properties.UUID) && s.IsActive)) { - GetProfileSettings(device.Properties); - - var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, device.Properties.Name, device.Properties.UUID, device.Properties.DisplayName, uri.OriginalString, null) + var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null) .ConfigureAwait(false); - _sessionManager.ReportCapabilities(sessionInfo.Id, new SessionCapabilities - { - PlayableMediaTypes = new[] { MediaType.Audio, MediaType.Video, MediaType.Photo }, - SupportsFullscreenToggle = false - }); - var controller = sessionInfo.SessionController as PlayToController; if (controller == null) { sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager, _appHost); - } - - controller.Init(device); - _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName); - } - } + controller.Init(device); - /// - /// Gets the profile settings. - /// - /// The device properties. - /// The TranscodeSettings for the device - private void GetProfileSettings(DeviceInfo deviceProperties) - { - var profile = _dlnaManager.GetProfile(deviceProperties.ToDeviceIdentification()); + _sessionManager.ReportCapabilities(sessionInfo.Id, new SessionCapabilities + { + PlayableMediaTypes = new[] { MediaType.Audio, MediaType.Video, MediaType.Photo }, + SupportsFullscreenToggle = false + }); - if (!string.IsNullOrWhiteSpace(profile.Name)) - { - deviceProperties.DisplayName = profile.Name; - } - if (!string.IsNullOrWhiteSpace(profile.ClientType)) - { - deviceProperties.ClientType = profile.ClientType; + _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName); + } } } diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs index 20f31cf9da..e1eea08240 100644 --- a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs +++ b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs @@ -33,8 +33,6 @@ namespace MediaBrowser.Dlna.PlayTo public int? SubtitleStreamIndex { get; set; } - public string DeviceProfileName { get; set; } - public int? MaxAudioChannels { get; set; } public int? AudioBitrate { get; set; } diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs index 1b2d791136..0dec9bbf3f 100644 --- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs +++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs @@ -162,7 +162,8 @@ namespace MediaBrowser.Dlna.PlayTo private void ApplyTranscodingConditions(PlaylistItem item, IEnumerable conditions) { - foreach (var condition in conditions.Where(i => !string.IsNullOrEmpty(i.Value))) + foreach (var condition in conditions + .Where(i => !string.IsNullOrEmpty(i.Value))) { var value = condition.Value; @@ -170,7 +171,7 @@ namespace MediaBrowser.Dlna.PlayTo { case ProfileConditionValue.AudioBitrate: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.AudioBitrate = num; @@ -179,7 +180,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.AudioChannels: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.MaxAudioChannels = num; @@ -199,7 +200,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.Height: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.MaxHeight = num; @@ -208,7 +209,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.VideoBitrate: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.VideoBitrate = num; @@ -217,7 +218,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.VideoFramerate: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.MaxFramerate = num; @@ -226,7 +227,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.VideoLevel: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.VideoLevel = num; @@ -235,7 +236,7 @@ namespace MediaBrowser.Dlna.PlayTo } case ProfileConditionValue.Width: { - var num = 0; + int num; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num)) { item.MaxWidth = num; diff --git a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs index f540a80041..b1ae21a437 100644 --- a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs +++ b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs @@ -2,7 +2,6 @@ using MediaBrowser.Controller.Configuration; using System; using System.IO; -using System.Net; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; @@ -14,8 +13,6 @@ namespace MediaBrowser.Dlna.PlayTo private const string USERAGENT = "Microsoft-Windows/6.2 UPnP/1.0 Microsoft-DLNA DLNADOC/1.50"; private const string FriendlyName = "MediaBrowser"; - private static readonly CookieContainer Container = new CookieContainer(); - private readonly IHttpClient _httpClient; private readonly IServerConfigurationManager _config; @@ -31,7 +28,7 @@ namespace MediaBrowser.Dlna.PlayTo if (!serviceUrl.StartsWith("/")) serviceUrl = "/" + serviceUrl; - var response = await PostSoapDataAsync(new Uri(baseUrl + serviceUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header) + var response = await PostSoapDataAsync(baseUrl + serviceUrl, "\"" + service.ServiceType + "#" + command + "\"", postData, header) .ConfigureAwait(false); using (var stream = response.Content) @@ -43,11 +40,11 @@ namespace MediaBrowser.Dlna.PlayTo } } - public async Task SubscribeAsync(Uri url, string ip, int port, string localIp, int eventport, int timeOut = 3600) + public async Task SubscribeAsync(string url, string ip, int port, string localIp, int eventport, int timeOut = 3600) { var options = new HttpRequestOptions { - Url = url.ToString(), + Url = url, UserAgent = USERAGENT, LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging }; @@ -56,7 +53,6 @@ namespace MediaBrowser.Dlna.PlayTo options.RequestHeaders["CALLBACK"] = "<" + localIp + ":" + eventport + ">"; options.RequestHeaders["NT"] = "upnp:event"; options.RequestHeaders["TIMEOUT"] = "Second - " + timeOut; - //request.CookieContainer = Container; using (await _httpClient.Get(options).ConfigureAwait(false)) { @@ -75,24 +71,22 @@ namespace MediaBrowser.Dlna.PlayTo options.RequestHeaders["CALLBACK"] = "<" + localIp + ":" + eventport + ">"; options.RequestHeaders["NT"] = "upnp:event"; options.RequestHeaders["TIMEOUT"] = "Second - 3600"; - //request.CookieContainer = Container; using (await _httpClient.Get(options).ConfigureAwait(false)) { } } - public async Task GetDataAsync(Uri url) + public async Task GetDataAsync(string url) { var options = new HttpRequestOptions { - Url = url.ToString(), + Url = url, UserAgent = USERAGENT, LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging }; options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName; - //request.CookieContainer = Container; using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) { @@ -103,14 +97,14 @@ namespace MediaBrowser.Dlna.PlayTo } } - private Task PostSoapDataAsync(Uri url, string soapAction, string postData, string header = null, int timeOut = 20000) + private Task PostSoapDataAsync(string url, string soapAction, string postData, string header = null) { if (!soapAction.StartsWith("\"")) soapAction = "\"" + soapAction + "\""; var options = new HttpRequestOptions { - Url = url.ToString(), + Url = url, UserAgent = USERAGENT, LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging }; diff --git a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs index 61c3bdd730..a4855c94f2 100644 --- a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs +++ b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs @@ -43,15 +43,10 @@ namespace MediaBrowser.Dlna.PlayTo /// private static string BuildDlnaUrl(DeviceInfo deviceProperties, PlaylistItem item) { - var profile = item.TranscodingSettings.Where(i => i.Name == TranscodingSettingType.VideoProfile) - .Select(i => i.Value) - .FirstOrDefault(); - var usCulture = new CultureInfo("en-US"); var list = new List { - item.DeviceProfileName ?? string.Empty, deviceProperties.UUID ?? string.Empty, item.MediaSourceId ?? string.Empty, (!item.Transcode).ToString().ToLower(), @@ -66,7 +61,6 @@ namespace MediaBrowser.Dlna.PlayTo item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(usCulture) : string.Empty, item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(usCulture) : string.Empty, item.StartPositionTicks.ToString(usCulture), - profile ?? string.Empty, item.VideoLevel.HasValue ? item.VideoLevel.Value.ToString(usCulture) : string.Empty }; diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs index 710f02df2e..214b6f814d 100644 --- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs +++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs @@ -6,9 +6,10 @@ namespace MediaBrowser.Dlna.Profiles { public DefaultProfile() { + Name = "Generic Device"; + ProtocolInfo = "DLNA"; - ClientType = "DLNA"; Manufacturer = "Media Browser"; ModelDescription = "Media Browser"; ModelName = "Media Browser"; diff --git a/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs b/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs new file mode 100644 index 0000000000..877f1a6665 --- /dev/null +++ b/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs @@ -0,0 +1,27 @@ +using MediaBrowser.Controller.Dlna; + +namespace MediaBrowser.Dlna.Profiles +{ + public class Foobar2000Profile : DefaultProfile + { + public Foobar2000Profile() + { + Name = "foobar2000"; + + Identification = new DeviceIdentification + { + FriendlyName = @"foobar", + + Headers = new[] + { + new HttpHeaderInfo + { + Name = "User-Agent", + Value = "foobar", + Match = HeaderMatchType.Substring + } + } + }; + } + } +} diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs index fa6b1201ab..fbbb7a594a 100644 --- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs @@ -302,6 +302,13 @@ namespace MediaBrowser.Dlna.Profiles MediaProfiles = new[] { + new MediaProfile + { + Container = "avi", + MimeType = "video/x-msvideo", + Type = DlnaProfileType.Video + }, + new MediaProfile { Container = "mkv", diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs index 49aa47027a..cec29b4182 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs @@ -6,6 +6,8 @@ namespace MediaBrowser.Dlna.Profiles { public SonyBlurayPlayer2013Profile() { + Name = "Sony Blu-ray Player 2013"; + Identification = new DeviceIdentification { FriendlyName = @"Blu-ray Disc Player", diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs index 5121726706..2c678b11f0 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs @@ -6,6 +6,8 @@ namespace MediaBrowser.Dlna.Profiles { public SonyBlurayPlayerProfile() { + Name = "Sony Blu-ray Player"; + Identification = new DeviceIdentification { FriendlyName = @"Blu-ray Disc Player", diff --git a/MediaBrowser.Dlna/Server/Headers.cs b/MediaBrowser.Dlna/Server/Headers.cs index 859ae7fbf2..2bd1a72f7b 100644 --- a/MediaBrowser.Dlna/Server/Headers.cs +++ b/MediaBrowser.Dlna/Server/Headers.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Dlna.Server private readonly Dictionary _dict = new Dictionary(); private readonly static Regex Validator = new Regex(@"^[a-z\d][a-z\d_.-]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase); - protected Headers(bool asIs) + public Headers(bool asIs) { _asIs = asIs; } diff --git a/MediaBrowser.Dlna/Server/RawHeaders.cs b/MediaBrowser.Dlna/Server/RawHeaders.cs deleted file mode 100644 index f57e6b9f3d..0000000000 --- a/MediaBrowser.Dlna/Server/RawHeaders.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MediaBrowser.Dlna.Server -{ - public class RawHeaders : Headers - { - public RawHeaders() - : base(true) - { - } - } -} diff --git a/MediaBrowser.Dlna/Server/SsdpHandler.cs b/MediaBrowser.Dlna/Server/SsdpHandler.cs index 63c2abbec4..64c1118196 100644 --- a/MediaBrowser.Dlna/Server/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Server/SsdpHandler.cs @@ -96,7 +96,7 @@ namespace MediaBrowser.Dlna.Server { break; } - var parts = line.Split(new char[] { ':' }, 2); + var parts = line.Split(new[] { ':' }, 2); headers[parts[0]] = parts[1].Trim(); } @@ -148,7 +148,7 @@ namespace MediaBrowser.Dlna.Server private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev) { - var headers = new RawHeaders(); + var headers = new Headers(true); headers.Add("CACHE-CONTROL", "max-age = 600"); headers.Add("DATE", DateTime.Now.ToString("R")); headers.Add("EXT", ""); @@ -188,7 +188,7 @@ namespace MediaBrowser.Dlna.Server private void NotifyDevice(UpnpDevice dev, string type, bool sticky) { _logger.Debug("NotifyDevice"); - var headers = new RawHeaders(); + var headers = new Headers(true); headers.Add("HOST", "239.255.255.250:1900"); headers.Add("CACHE-CONTROL", "max-age = 600"); headers.Add("LOCATION", dev.Descriptor.ToString()); diff --git a/MediaBrowser.Dlna/PlayTo/SsdpHelper.cs b/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs similarity index 57% rename from MediaBrowser.Dlna/PlayTo/SsdpHelper.cs rename to MediaBrowser.Dlna/Ssdp/SsdpHelper.cs index d07a7679f0..b22db781ad 100644 --- a/MediaBrowser.Dlna/PlayTo/SsdpHelper.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHelper.cs @@ -1,8 +1,9 @@ using System; -using System.Linq; +using System.Collections.Generic; +using System.IO; using System.Text; -namespace MediaBrowser.Dlna.PlayTo +namespace MediaBrowser.Dlna.Ssdp { public class SsdpHelper { @@ -29,28 +30,29 @@ namespace MediaBrowser.Dlna.PlayTo /// /// The data. /// - public static Uri ParseSsdpResponse(string data) + public static Dictionary ParseSsdpResponse(byte[] data) { - var res = (from line in data.Split(new[] { '\r', '\n' }) - where line.ToLowerInvariant().StartsWith("location:") - select line).FirstOrDefault(); + var headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - return !string.IsNullOrEmpty(res) ? new Uri(res.Substring(9).Trim()) : null; - } - - /// - /// Parses data into SSDP event. - /// - /// The data. - /// - [Obsolete("Not yet used", true)] - public static string ParseSsdpEvent(string data) - { - var sid = (from line in data.Split(new[] { '\r', '\n' }) - where line.ToLowerInvariant().StartsWith("sid:") - select line).FirstOrDefault(); + using (var reader = new StreamReader(new MemoryStream(data), Encoding.ASCII)) + { + for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) + { + line = line.Trim(); + if (string.IsNullOrEmpty(line)) + { + break; + } + var parts = line.Split(new[] { ':' }, 2); - return data; + if (parts.Length == 2) + { + headers[parts[0]] = parts[1].Trim(); + } + } + } + + return headers; } } } diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs index 5957938543..9a31fd0912 100644 --- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs @@ -3,7 +3,6 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -75,16 +74,6 @@ namespace MediaBrowser.Providers.Savers XmlSaverHelpers.AddCommonNodes(video, builder); - if (video.CommunityRating.HasValue) - { - builder.Append("" + SecurityElement.Escape(video.CommunityRating.Value.ToString(UsCulture)) + ""); - } - - if (!string.IsNullOrEmpty(video.Overview)) - { - builder.Append(""); - } - var musicVideo = item as MusicVideo; if (musicVideo != null) @@ -117,8 +106,12 @@ namespace MediaBrowser.Providers.Savers XmlSaverHelpers.Save(builder, xmlFilePath, new List { + // Deprecated. No longer saving in this field. "IMDBrating", + + // Deprecated. No longer saving in this field. "Description", + "Artist", "Album", "TmdbCollectionName" diff --git a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs index e69f2e0850..a7ed55e5dd 100644 --- a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs @@ -60,11 +60,6 @@ namespace MediaBrowser.Providers.Savers builder.Append("" + SecurityElement.Escape(tvdb) + ""); } - if (!string.IsNullOrEmpty(item.Name)) - { - builder.Append("" + SecurityElement.Escape(item.Name) + ""); - } - if (series.Status.HasValue) { builder.Append("" + SecurityElement.Escape(series.Status.Value.ToString()) + ""); @@ -111,7 +106,6 @@ namespace MediaBrowser.Providers.Savers XmlSaverHelpers.Save(builder, xmlFilePath, new List { "id", - "SeriesName", "Status", "Network", "Airs_Time", @@ -120,6 +114,10 @@ namespace MediaBrowser.Providers.Savers // Don't preserve old series node "Series", + + "SeriesName", + + // Deprecated. No longer saving in this field. "AnimeSeriesIndex" }); } diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index 391ab7cfd9..6d681197e5 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -28,7 +28,10 @@ namespace MediaBrowser.Providers.Savers "AwardSummary", "BirthDate", "Budget", + + // Deprecated. No longer saving in this field. "certification", + "Chapters", "ContentRating", "CustomRating", @@ -40,22 +43,31 @@ namespace MediaBrowser.Providers.Savers "Genres", "Genre", "GamesDbId", + + // Deprecated. No longer saving in this field. "IMDB_ID", + "IMDB", + + // Deprecated. No longer saving in this field. "IMDbId", + "Language", "LocalTitle", "LockData", "LockedFields", "Format3D", "Metascore", + + // Deprecated. No longer saving in this field. "MPAARating", + "MusicBrainzArtistId", "MusicBrainzAlbumArtistId", "MusicBrainzAlbumId", "MusicBrainzReleaseGroupId", - // Old - not used anymore + // Deprecated. No longer saving in this field. "MusicbrainzId", "Overview", @@ -67,15 +79,24 @@ namespace MediaBrowser.Providers.Savers "Revenue", "RottenTomatoesId", "RunningTime", + + // Deprecated. No longer saving in this field. "Runtime", + "SortTitle", "Studios", "Tags", + + // Deprecated. No longer saving in this field. "TagLine", + "Taglines", "TMDbCollectionId", "TMDbId", + + // Deprecated. No longer saving in this field. "Trailer", + "Trailers", "TVcomId", "TvDbId", @@ -207,8 +228,6 @@ namespace MediaBrowser.Providers.Savers if (!string.IsNullOrEmpty(item.OfficialRating)) { builder.Append("" + SecurityElement.Escape(item.OfficialRating) + ""); - builder.Append("" + SecurityElement.Escape(item.OfficialRating) + ""); - builder.Append("" + SecurityElement.Escape(item.OfficialRating) + ""); } builder.Append("" + SecurityElement.Escape(item.DateCreated.ToLocalTime().ToString("G")) + ""); @@ -376,16 +395,13 @@ namespace MediaBrowser.Providers.Savers var timespan = TimeSpan.FromTicks(runTimeTicks.Value); builder.Append("" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + ""); - builder.Append("" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + ""); } var imdb = item.GetProviderId(MetadataProviders.Imdb); if (!string.IsNullOrEmpty(imdb)) { - builder.Append("" + SecurityElement.Escape(imdb) + ""); builder.Append("" + SecurityElement.Escape(imdb) + ""); - builder.Append("" + SecurityElement.Escape(imdb) + ""); } var tmdb = item.GetProviderId(MetadataProviders.Tmdb); diff --git a/MediaBrowser.Providers/TV/SeriesXmlParser.cs b/MediaBrowser.Providers/TV/SeriesXmlParser.cs index 0c220031c1..9f68ad7a4e 100644 --- a/MediaBrowser.Providers/TV/SeriesXmlParser.cs +++ b/MediaBrowser.Providers/TV/SeriesXmlParser.cs @@ -90,6 +90,8 @@ namespace MediaBrowser.Providers.TV break; } case "SeriesName": + // TODO: Deprecate in mid-2014 + // No longer saving this tag but will still read it for a while item.Name = reader.ReadElementContentAsString(); break; diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index 02c629906c..fcfe98a46e 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -851,7 +851,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. - var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=20\" -f image2 \"{1}\"", inputPath, "-", vf) : + var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"thumbnail,{2}\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); var probeSize = GetProbeSizeArgument(type); From b5a3cbfb74e96348ff50fb9fa987d316501d2174 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 25 Mar 2014 01:42:58 -0400 Subject: [PATCH 03/24] restore thumbnail change --- .../MediaEncoder/MediaEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs index fcfe98a46e..fddba76626 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs @@ -851,7 +851,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. - var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"thumbnail,{2}\" -f image2 \"{1}\"", inputPath, "-", vf) : + var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); var probeSize = GetProbeSizeArgument(type); From 31e8288393bead548e815a081c34d7e688fa0643 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 25 Mar 2014 17:13:55 -0400 Subject: [PATCH 04/24] make metadata path configurable --- MediaBrowser.Api/AppThemeService.cs | 14 ++-- MediaBrowser.Api/BaseApiService.cs | 15 ++++ MediaBrowser.Api/ConfigurationService.cs | 15 ++-- MediaBrowser.Api/Library/LibraryService.cs | 12 +++- MediaBrowser.Api/Movies/MoviesService.cs | 6 +- MediaBrowser.Api/Movies/TrailersService.cs | 3 +- MediaBrowser.Api/Music/InstantMixService.cs | 12 ++-- .../ScheduledTasks/ScheduledTaskService.cs | 17 ++--- .../UserLibrary/StudiosService.cs | 7 +- .../UserLibrary/UserLibraryService.cs | 19 ++--- .../WebSocket/SessionInfoWebSocketListener.cs | 2 +- MediaBrowser.Controller/Dlna/CodecProfile.cs | 4 +- MediaBrowser.Controller/Entities/User.cs | 5 +- MediaBrowser.Dlna/PlayTo/PlayToManager.cs | 8 ++- .../PlayTo/PlaylistItemFactory.cs | 8 --- MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs | 24 +++++-- MediaBrowser.Dlna/PlayTo/StreamHelper.cs | 4 +- MediaBrowser.Dlna/Profiles/DefaultProfile.cs | 2 +- .../Profiles/SonyBravia2010Profile.cs | 15 +--- .../Profiles/SonyBravia2011Profile.cs | 15 +--- .../Profiles/SonyBravia2012Profile.cs | 15 +--- .../Profiles/SonyBravia2013Profile.cs | 15 +--- .../Configuration/ServerConfiguration.cs | 6 ++ .../Configuration/UserConfiguration.cs | 4 +- .../MediaInfo/FFProbeAudioInfo.cs | 13 ++-- .../ServerConfigurationManager.cs | 32 +++++++++ .../Library/UserManager.cs | 58 ++++++++++----- .../ServerApplicationPaths.cs | 12 +++- .../ApplicationHost.cs | 2 +- .../Api/DashboardInfo.cs | 39 ----------- .../Api/DashboardInfoWebSocketListener.cs | 62 ---------------- .../Api/DashboardService.cs | 70 ++----------------- .../MediaBrowser.WebDashboard.csproj | 8 ++- 33 files changed, 204 insertions(+), 339 deletions(-) delete mode 100644 MediaBrowser.WebDashboard/Api/DashboardInfo.cs delete mode 100644 MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs diff --git a/MediaBrowser.Api/AppThemeService.cs b/MediaBrowser.Api/AppThemeService.cs index 54141b3e22..4d8eed7ddb 100644 --- a/MediaBrowser.Api/AppThemeService.cs +++ b/MediaBrowser.Api/AppThemeService.cs @@ -9,16 +9,14 @@ using System.Linq; namespace MediaBrowser.Api { - [Route("/Themes", "GET")] - [Api(Description = "Gets a list of available themes for an app")] + [Route("/Themes", "GET", Summary = "Gets a list of available themes for an app")] public class GetAppThemes : IReturn> { [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] public string App { get; set; } } - [Route("/Themes/Info", "GET")] - [Api(Description = "Gets an app theme")] + [Route("/Themes/Info", "GET", Summary = "Gets an app theme")] public class GetAppTheme : IReturn { [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -28,8 +26,7 @@ namespace MediaBrowser.Api public string Name { get; set; } } - [Route("/Themes/Images", "GET")] - [Api(Description = "Gets an app theme")] + [Route("/Themes/Images", "GET", Summary = "Gets an app theme")] public class GetAppThemeImage { [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -45,12 +42,11 @@ namespace MediaBrowser.Api public string CacheTag { get; set; } } - [Route("/Themes", "POST")] - [Api(Description = "Saves a theme")] + [Route("/Themes", "POST", Summary = "Saves a theme")] public class SaveTheme : AppTheme, IReturnVoid { } - + public class AppThemeService : BaseApiService { private readonly IAppThemeManager _themeManager; diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 08686b43a8..707cfb4572 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Session; using MediaBrowser.Model.Logging; using ServiceStack.Web; using System; @@ -78,6 +79,20 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } + /// + /// Gets the session. + /// + /// The session manager. + /// SessionInfo. + protected SessionInfo GetSession(ISessionManager sessionManager) + { + var auth = AuthorizationRequestFilterAttribute.GetAuthorization(Request); + + return sessionManager.Sessions.First(i => string.Equals(i.DeviceId, auth.DeviceId) && + string.Equals(i.Client, auth.Client) && + string.Equals(i.ApplicationVersion, auth.Version)); + } + /// /// To the cached result. /// diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index 6007043503..b3191cd4b9 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -17,8 +17,7 @@ namespace MediaBrowser.Api /// /// Class GetConfiguration /// - [Route("/System/Configuration", "GET")] - [Api(("Gets application configuration"))] + [Route("/System/Configuration", "GET", Summary = "Gets application configuration")] public class GetConfiguration : IReturn { @@ -27,28 +26,24 @@ namespace MediaBrowser.Api /// /// Class UpdateConfiguration /// - [Route("/System/Configuration", "POST")] - [Api(("Updates application configuration"))] + [Route("/System/Configuration", "POST", Summary = "Updates application configuration")] public class UpdateConfiguration : ServerConfiguration, IReturnVoid { } - [Route("/System/Configuration/MetadataOptions/Default", "GET")] - [Api(("Gets a default MetadataOptions object"))] + [Route("/System/Configuration/MetadataOptions/Default", "GET", Summary = "Gets a default MetadataOptions object")] public class GetDefaultMetadataOptions : IReturn { } - [Route("/System/Configuration/MetadataPlugins", "GET")] - [Api(("Gets all available metadata plugins"))] + [Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")] public class GetMetadataPlugins : IReturn> { } - [Route("/System/Configuration/VideoImageExtraction", "POST")] - [Api(("Updates image extraction for all types"))] + [Route("/System/Configuration/VideoImageExtraction", "POST", Summary = "Updates image extraction for all types")] public class UpdateVideoImageExtraction : IReturnVoid { public bool Enabled { get; set; } diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index dba256418e..533a92fba8 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -7,6 +7,7 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Session; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -245,12 +246,13 @@ namespace MediaBrowser.Api.Library private readonly IDtoService _dtoService; private readonly IChannelManager _channelManager; + private readonly ISessionManager _sessionManager; /// /// Initializes a new instance of the class. /// public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, - IDtoService dtoService, IUserDataManager userDataManager, IChannelManager channelManager) + IDtoService dtoService, IUserDataManager userDataManager, IChannelManager channelManager, ISessionManager sessionManager) { _itemRepo = itemRepo; _libraryManager = libraryManager; @@ -258,6 +260,7 @@ namespace MediaBrowser.Api.Library _dtoService = dtoService; _userDataManager = userDataManager; _channelManager = channelManager; + _sessionManager = sessionManager; } public object Get(GetMediaFolders request) @@ -504,6 +507,13 @@ namespace MediaBrowser.Api.Library { var item = _dtoService.GetItemByDtoId(request.Id); + var session = GetSession(_sessionManager); + + if (!session.UserId.HasValue || !_userManager.GetUserById(session.UserId.Value).Configuration.EnableContentDeletion) + { + throw new UnauthorizedAccessException("This operation requires a logged in user with delete access."); + } + return _libraryManager.DeleteItem(item); } diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 204a7aab45..228dc378b5 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -17,8 +17,7 @@ namespace MediaBrowser.Api.Movies /// /// Class GetSimilarMovies /// - [Route("/Movies/{Id}/Similar", "GET")] - [Api(Description = "Finds movies and trailers similar to a given movie.")] + [Route("/Movies/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given movie.")] public class GetSimilarMovies : BaseGetSimilarItemsFromItem { [ApiMember(Name = "IncludeTrailers", Description = "Whether or not to include trailers within the results. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] @@ -30,8 +29,7 @@ namespace MediaBrowser.Api.Movies } } - [Route("/Movies/Recommendations", "GET")] - [Api(Description = "Gets movie recommendations")] + [Route("/Movies/Recommendations", "GET", Summary = "Gets movie recommendations")] public class GetMovieRecommendations : IReturn, IHasItemFields { [ApiMember(Name = "CategoryLimit", Description = "The max number of categories to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs index 0575066350..05e6a95774 100644 --- a/MediaBrowser.Api/Movies/TrailersService.cs +++ b/MediaBrowser.Api/Movies/TrailersService.cs @@ -10,8 +10,7 @@ namespace MediaBrowser.Api.Movies /// /// Class GetSimilarTrailers /// - [Route("/Trailers/{Id}/Similar", "GET")] - [Api(Description = "Finds movies and trailers similar to a given trailer.")] + [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")] public class GetSimilarTrailers : BaseGetSimilarItemsFromItem { } diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index a8446a7ef2..9b9df3a92c 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -9,28 +9,24 @@ using System.Linq; namespace MediaBrowser.Api.Music { - [Route("/Songs/{Id}/InstantMix", "GET")] - [Api(Description = "Creates an instant playlist based on a given song")] + [Route("/Songs/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given song")] public class GetInstantMixFromSong : BaseGetSimilarItemsFromItem { } - [Route("/Albums/{Id}/InstantMix", "GET")] - [Api(Description = "Creates an instant playlist based on a given album")] + [Route("/Albums/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given album")] public class GetInstantMixFromAlbum : BaseGetSimilarItemsFromItem { } - [Route("/Artists/{Name}/InstantMix", "GET")] - [Api(Description = "Creates an instant playlist based on a given artist")] + [Route("/Artists/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")] public class GetInstantMixFromArtist : BaseGetSimilarItems { [ApiMember(Name = "Name", Description = "The artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Name { get; set; } } - [Route("/MusicGenres/{Name}/InstantMix", "GET")] - [Api(Description = "Creates an instant playlist based on a music genre")] + [Route("/MusicGenres/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a music genre")] public class GetInstantMixFromMusicGenre : BaseGetSimilarItems { [ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs index daa735fe53..80080fbb31 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs @@ -2,18 +2,17 @@ using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Model.Tasks; using ServiceStack; +using ServiceStack.Text.Controller; using System; using System.Collections.Generic; using System.Linq; -using ServiceStack.Text.Controller; namespace MediaBrowser.Api.ScheduledTasks { /// /// Class GetScheduledTask /// - [Route("/ScheduledTasks/{Id}", "GET")] - [Api(Description = "Gets a scheduled task, by Id")] + [Route("/ScheduledTasks/{Id}", "GET", Summary = "Gets a scheduled task, by Id")] public class GetScheduledTask : IReturn { /// @@ -27,8 +26,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// Class GetScheduledTasks /// - [Route("/ScheduledTasks", "GET")] - [Api(Description = "Gets scheduled tasks")] + [Route("/ScheduledTasks", "GET", Summary = "Gets scheduled tasks")] public class GetScheduledTasks : IReturn> { [ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] @@ -38,8 +36,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// Class StartScheduledTask /// - [Route("/ScheduledTasks/Running/{Id}", "POST")] - [Api(Description = "Starts a scheduled task")] + [Route("/ScheduledTasks/Running/{Id}", "POST", Summary = "Starts a scheduled task")] public class StartScheduledTask : IReturnVoid { /// @@ -53,8 +50,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// Class StopScheduledTask /// - [Route("/ScheduledTasks/Running/{Id}", "DELETE")] - [Api(Description = "Stops a scheduled task")] + [Route("/ScheduledTasks/Running/{Id}", "DELETE", Summary = "Stops a scheduled task")] public class StopScheduledTask : IReturnVoid { /// @@ -68,8 +64,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// Class UpdateScheduledTaskTriggers /// - [Route("/ScheduledTasks/{Id}/Triggers", "POST")] - [Api(Description = "Updates the triggers for a scheduled task")] + [Route("/ScheduledTasks/{Id}/Triggers", "POST", Summary = "Updates the triggers for a scheduled task")] public class UpdateScheduledTaskTriggers : List, IReturnVoid { /// diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index fc7fdd1600..7eff5054be 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -8,15 +8,13 @@ using ServiceStack; using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace MediaBrowser.Api.UserLibrary { /// /// Class GetStudios /// - [Route("/Studios", "GET")] - [Api(Description = "Gets all studios from a given item, folder, or the entire library")] + [Route("/Studios", "GET", Summary = "Gets all studios from a given item, folder, or the entire library")] public class GetStudios : GetItemsByName { } @@ -24,8 +22,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetStudio /// - [Route("/Studios/{Name}", "GET")] - [Api(Description = "Gets a studio, by name")] + [Route("/Studios/{Name}", "GET", Summary = "Gets a studio, by name")] public class GetStudio : IReturn { /// diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index c6051c02cc..a49f957f60 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -705,7 +705,7 @@ namespace MediaBrowser.Api.UserLibrary datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); } - var session = GetSession(); + var session = GetSession(_sessionManager); var dto = await UpdatePlayedStatus(user, request.Id, true, datePlayed).ConfigureAwait(false); @@ -719,15 +719,6 @@ namespace MediaBrowser.Api.UserLibrary return dto; } - private SessionInfo GetSession() - { - var auth = AuthorizationRequestFilterAttribute.GetAuthorization(Request); - - return _sessionManager.Sessions.First(i => string.Equals(i.DeviceId, auth.DeviceId) && - string.Equals(i.Client, auth.Client) && - string.Equals(i.ApplicationVersion, auth.Version)); - } - /// /// Posts the specified request. /// @@ -744,7 +735,7 @@ namespace MediaBrowser.Api.UserLibrary { CanSeek = request.CanSeek, Item = item, - SessionId = GetSession().Id, + SessionId = GetSession(_sessionManager).Id, QueueableMediaTypes = queueableMediaTypes.Split(',').ToList(), MediaSourceId = request.MediaSourceId }; @@ -768,7 +759,7 @@ namespace MediaBrowser.Api.UserLibrary PositionTicks = request.PositionTicks, IsMuted = request.IsMuted, IsPaused = request.IsPaused, - SessionId = GetSession().Id, + SessionId = GetSession(_sessionManager).Id, MediaSourceId = request.MediaSourceId }; @@ -787,7 +778,7 @@ namespace MediaBrowser.Api.UserLibrary var item = _dtoService.GetItemByDtoId(request.Id, user.Id); - var session = GetSession(); + var session = GetSession(_sessionManager); var info = new PlaybackStopInfo { @@ -817,7 +808,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var session = GetSession(); + var session = GetSession(_sessionManager); var dto = await UpdatePlayedStatus(user, request.Id, false, null).ConfigureAwait(false); diff --git a/MediaBrowser.Api/WebSocket/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/WebSocket/SessionInfoWebSocketListener.cs index 38139645eb..25acd613ca 100644 --- a/MediaBrowser.Api/WebSocket/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/WebSocket/SessionInfoWebSocketListener.cs @@ -49,7 +49,7 @@ namespace MediaBrowser.Api.WebSocket /// Task{SystemInfo}. protected override Task> GetDataToSend(object state) { - return Task.FromResult(_sessionManager.Sessions.Select(_dtoService.GetSessionInfoDto)); + return Task.FromResult(_sessionManager.Sessions.Where(i => i.IsActive).Select(_dtoService.GetSessionInfoDto)); } } } diff --git a/MediaBrowser.Controller/Dlna/CodecProfile.cs b/MediaBrowser.Controller/Dlna/CodecProfile.cs index 2b9a40ea06..0f61cad98d 100644 --- a/MediaBrowser.Controller/Dlna/CodecProfile.cs +++ b/MediaBrowser.Controller/Dlna/CodecProfile.cs @@ -69,8 +69,6 @@ namespace MediaBrowser.Controller.Dlna VideoBitrate, VideoFramerate, VideoLevel, - VideoPacketLength, - VideoProfile, - VideoTimestamp + VideoProfile } } diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index e6a62c1812..0a34b80168 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -121,10 +121,7 @@ namespace MediaBrowser.Controller.Entities { _configuration = value; - if (value == null) - { - _configurationInitialized = false; - } + _configurationInitialized = value != null; } } diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs index 297f7a696b..2f4f09ad66 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs @@ -241,7 +241,13 @@ namespace MediaBrowser.Dlna.PlayTo _sessionManager.ReportCapabilities(sessionInfo.Id, new SessionCapabilities { - PlayableMediaTypes = new[] { MediaType.Audio, MediaType.Video, MediaType.Photo }, + PlayableMediaTypes = new[] + { + MediaType.Audio, + MediaType.Video, + MediaType.Photo + }, + SupportsFullscreenToggle = false }); diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs index 0dec9bbf3f..f79dc1e5fc 100644 --- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs +++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs @@ -191,9 +191,7 @@ namespace MediaBrowser.Dlna.PlayTo case ProfileConditionValue.AudioProfile: case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.VideoBitDepth: - case ProfileConditionValue.VideoPacketLength: case ProfileConditionValue.VideoProfile: - case ProfileConditionValue.VideoTimestamp: { // Not supported yet break; @@ -461,12 +459,6 @@ namespace MediaBrowser.Dlna.PlayTo return videoStream == null ? null : videoStream.Width; case ProfileConditionValue.VideoLevel: return videoStream == null ? null : ConvertToLong(videoStream.Level); - case ProfileConditionValue.VideoPacketLength: - // TODO: Determine how to get this - return null; - case ProfileConditionValue.VideoTimestamp: - // TODO: Determine how to get this - return null; default: throw new InvalidOperationException("Unexpected Property"); } diff --git a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs index b1ae21a437..42c788d381 100644 --- a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs +++ b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs @@ -22,7 +22,11 @@ namespace MediaBrowser.Dlna.PlayTo _config = config; } - public async Task SendCommandAsync(string baseUrl, DeviceService service, string command, string postData, string header = null) + public async Task SendCommandAsync(string baseUrl, + DeviceService service, + string command, + string postData, + string header = null) { var serviceUrl = service.ControlUrl; if (!serviceUrl.StartsWith("/")) @@ -40,7 +44,12 @@ namespace MediaBrowser.Dlna.PlayTo } } - public async Task SubscribeAsync(string url, string ip, int port, string localIp, int eventport, int timeOut = 3600) + public async Task SubscribeAsync(string url, + string ip, + int port, + string localIp, + int eventport, + int timeOut = 3600) { var options = new HttpRequestOptions { @@ -59,7 +68,11 @@ namespace MediaBrowser.Dlna.PlayTo } } - public async Task RespondAsync(Uri url, string ip, int port, string localIp, int eventport, int timeOut = 20000) + public async Task RespondAsync(Uri url, + string ip, + int port, + string localIp, + int eventport) { var options = new HttpRequestOptions { @@ -97,7 +110,10 @@ namespace MediaBrowser.Dlna.PlayTo } } - private Task PostSoapDataAsync(string url, string soapAction, string postData, string header = null) + private Task PostSoapDataAsync(string url, + string soapAction, + string postData, + string header = null) { if (!soapAction.StartsWith("\"")) soapAction = "\"" + soapAction + "\""; diff --git a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs index a4855c94f2..30d787cf8f 100644 --- a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs +++ b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs @@ -1,8 +1,6 @@ -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Entities; using System.Collections.Generic; using System.Globalization; -using System.Linq; namespace MediaBrowser.Dlna.PlayTo { diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs index 214b6f814d..5eb364080f 100644 --- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs +++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Dlna.Profiles { public DefaultProfile() { - Name = "Generic Device"; + Name = "Media Browser"; ProtocolInfo = "DLNA"; diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs index 042cc0a965..4cf108172d 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs @@ -96,13 +96,7 @@ namespace MediaBrowser.Dlna.Profiles AudioCodec="ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", - Type = DlnaProfileType.Video, - - Conditions = new [] - { - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"}, - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"} - } + Type = DlnaProfileType.Video }, new MediaProfile @@ -112,12 +106,7 @@ namespace MediaBrowser.Dlna.Profiles AudioCodec="ac3,aac,mp3", MimeType = "video/mpeg", OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", - Type = DlnaProfileType.Video, - - Conditions = new [] - { - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"} - } + Type = DlnaProfileType.Video }, new MediaProfile diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs index 401c40c362..ec10f71aa3 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs @@ -138,13 +138,7 @@ namespace MediaBrowser.Dlna.Profiles AudioCodec="ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", - Type = DlnaProfileType.Video, - - Conditions = new [] - { - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"}, - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"} - } + Type = DlnaProfileType.Video }, new MediaProfile @@ -154,12 +148,7 @@ namespace MediaBrowser.Dlna.Profiles AudioCodec="ac3,aac,mp3", MimeType = "video/mpeg", OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", - Type = DlnaProfileType.Video, - - Conditions = new [] - { - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"} - } + Type = DlnaProfileType.Video }, new MediaProfile diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs index 2d24c406e3..21efc3b457 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs @@ -126,13 +126,7 @@ namespace MediaBrowser.Dlna.Profiles AudioCodec="ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", - Type = DlnaProfileType.Video, - - Conditions = new [] - { - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"}, - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"} - } + Type = DlnaProfileType.Video }, new MediaProfile @@ -142,12 +136,7 @@ namespace MediaBrowser.Dlna.Profiles AudioCodec="ac3,aac,mp3", MimeType = "video/mpeg", OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", - Type = DlnaProfileType.Video, - - Conditions = new [] - { - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"} - } + Type = DlnaProfileType.Video }, new MediaProfile diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs index 10f7129581..b247c39ac9 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs @@ -182,13 +182,7 @@ namespace MediaBrowser.Dlna.Profiles AudioCodec="ac3,aac,mp3", MimeType = "video/vnd.dlna.mpeg-tts", OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T", - Type = DlnaProfileType.Video, - - Conditions = new [] - { - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"}, - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"} - } + Type = DlnaProfileType.Video }, new MediaProfile @@ -198,12 +192,7 @@ namespace MediaBrowser.Dlna.Profiles AudioCodec="ac3,aac,mp3", MimeType = "video/mpeg", OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO", - Type = DlnaProfileType.Video, - - Conditions = new [] - { - new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"} - } + Type = DlnaProfileType.Video }, new MediaProfile diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 1f304112f5..d0caa3ad20 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -57,6 +57,12 @@ namespace MediaBrowser.Model.Configuration /// The item by name path. public string ItemsByNamePath { get; set; } + /// + /// Gets or sets the metadata path. + /// + /// The metadata path. + public string MetadataPath { get; set; } + /// /// Gets or sets the display name of the season zero. /// diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 2145860c73..10f984f88e 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -52,6 +52,7 @@ namespace MediaBrowser.Model.Configuration public bool EnableLiveTvAccess { get; set; } public bool EnableMediaPlayback { get; set; } + public bool EnableContentDeletion { get; set; } public string[] BlockedMediaFolders { get; set; } @@ -63,8 +64,9 @@ namespace MediaBrowser.Model.Configuration public UserConfiguration() { IsAdministrator = true; - EnableRemoteControlOfOtherUsers = true; + EnableRemoteControlOfOtherUsers = true; + EnableContentDeletion = true; EnableLiveTvManagement = true; EnableMediaPlayback = true; EnableLiveTvAccess = true; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 044973064e..a27fa057ca 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -148,7 +148,7 @@ namespace MediaBrowser.Providers.MediaInfo if (!string.IsNullOrWhiteSpace(composer)) { - foreach (var person in Split(composer)) + foreach (var person in Split(composer, false)) { audio.AddPerson(new PersonInfo { Name = person, Type = PersonType.Composer }); } @@ -221,12 +221,15 @@ namespace MediaBrowser.Providers.MediaInfo /// Splits the specified val. /// /// The val. + /// if set to true [allow comma delimiter]. /// System.String[][]. - private IEnumerable Split(string val) + private IEnumerable Split(string val, bool allowCommaDelimiter) { // Only use the comma as a delimeter if there are no slashes or pipes. // We want to be careful not to split names that have commas in them - var delimeter = _nameDelimiters.Any(i => val.IndexOf(i) != -1) ? _nameDelimiters : new[] { ',' }; + var delimeter = !allowCommaDelimiter || _nameDelimiters.Any(i => val.IndexOf(i) != -1) ? + _nameDelimiters : + new[] { ',' }; return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)) @@ -312,7 +315,7 @@ namespace MediaBrowser.Providers.MediaInfo if (!string.IsNullOrEmpty(val)) { // Sometimes the artist name is listed here, account for that - var studios = Split(val).Where(i => !audio.HasArtist(i)); + var studios = Split(val, true).Where(i => !audio.HasArtist(i)); foreach (var studio in studios) { @@ -334,7 +337,7 @@ namespace MediaBrowser.Providers.MediaInfo { audio.Genres.Clear(); - foreach (var genre in Split(val)) + foreach (var genre in Split(val, true)) { audio.AddGenre(genre); } diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index 415205cb11..cb3621fd17 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -26,6 +26,7 @@ namespace MediaBrowser.Server.Implementations.Configuration { UpdateItemsByNamePath(); UpdateTranscodingTempPath(); + UpdateMetadataPath(); } /// @@ -76,6 +77,16 @@ namespace MediaBrowser.Server.Implementations.Configuration Configuration.ItemsByNamePath; } + /// + /// Updates the metadata path. + /// + private void UpdateMetadataPath() + { + ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = string.IsNullOrEmpty(Configuration.MetadataPath) ? + null : + Configuration.MetadataPath; + } + /// /// Updates the transcoding temporary path. /// @@ -98,6 +109,7 @@ namespace MediaBrowser.Server.Implementations.Configuration ValidateItemByNamePath(newConfig); ValidateTranscodingTempPath(newConfig); ValidatePathSubstitutions(newConfig); + ValidateMetadataPath(newConfig); base.ReplaceConfiguration(newConfiguration); } @@ -166,5 +178,25 @@ namespace MediaBrowser.Server.Implementations.Configuration } } } + + /// + /// Validates the metadata path. + /// + /// The new configuration. + /// + private void ValidateMetadataPath(ServerConfiguration newConfig) + { + var newPath = newConfig.MetadataPath; + + if (!string.IsNullOrWhiteSpace(newPath) + && !string.Equals(Configuration.MetadataPath ?? string.Empty, newPath)) + { + // Validate + if (!Directory.Exists(newPath)) + { + throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath)); + } + } + } } } diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index 06028d37ec..2ee843f09b 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -260,6 +260,8 @@ namespace MediaBrowser.Server.Implementations.Library public event EventHandler> UserCreated; + private readonly SemaphoreSlim _userListLock = new SemaphoreSlim(1, 1); + /// /// Creates the user. /// @@ -279,19 +281,28 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name)); } - var user = InstantiateNewUser(name); + await _userListLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); - var list = Users.ToList(); - list.Add(user); - Users = list; + try + { + var user = InstantiateNewUser(name); - user.DateLastSaved = DateTime.UtcNow; + var list = Users.ToList(); + list.Add(user); + Users = list; - await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false); + user.DateLastSaved = DateTime.UtcNow; - EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs { Argument = user }, _logger); + await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false); - return user; + EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs { Argument = user }, _logger); + + return user; + } + finally + { + _userListLock.Release(); + } } /// @@ -325,23 +336,32 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one admin user in the system.", user.Name)); } - await UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false); - - var path = user.ConfigurationFilePath; + await _userListLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); try { - File.Delete(path); + await UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false); + + var path = user.ConfigurationFilePath; + + try + { + File.Delete(path); + } + catch (IOException ex) + { + _logger.ErrorException("Error deleting file {0}", ex, path); + } + + // Force this to be lazy loaded again + Users = await LoadUsers().ConfigureAwait(false); + + OnUserDeleted(user); } - catch (IOException ex) + finally { - _logger.ErrorException("Error deleting file {0}", ex, path); + _userListLock.Release(); } - - // Force this to be lazy loaded again - Users = await LoadUsers().ConfigureAwait(false); - - OnUserDeleted(user); } /// diff --git a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs index c36c49df0e..df2a5f83c2 100644 --- a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs +++ b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs @@ -1,6 +1,6 @@ -using System; -using MediaBrowser.Common.Implementations; +using MediaBrowser.Common.Implementations; using MediaBrowser.Controller; +using System; using System.IO; namespace MediaBrowser.Server.Implementations @@ -239,14 +239,20 @@ namespace MediaBrowser.Server.Implementations } } + private string _internalMetadataPath; public string InternalMetadataPath { get { - return Path.Combine(DataPath, "metadata"); + return _internalMetadataPath ?? (_internalMetadataPath = Path.Combine(DataPath, "metadata")); + } + set + { + _internalMetadataPath = value; } } + public string GetInternalMetadataPath(Guid id) { var idString = id.ToString("N"); diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index e49244edf0..351cbcfc86 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -795,7 +795,7 @@ namespace MediaBrowser.ServerApplication list.Add(typeof(ApiEntryPoint).Assembly); // Include composable parts in the Dashboard assembly - list.Add(typeof(DashboardInfo).Assembly); + list.Add(typeof(DashboardService).Assembly); // Include composable parts in the Model assembly list.Add(typeof(SystemInfo).Assembly); diff --git a/MediaBrowser.WebDashboard/Api/DashboardInfo.cs b/MediaBrowser.WebDashboard/Api/DashboardInfo.cs deleted file mode 100644 index 78cc5a758c..0000000000 --- a/MediaBrowser.WebDashboard/Api/DashboardInfo.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MediaBrowser.Model.Session; -using MediaBrowser.Model.System; -using MediaBrowser.Model.Tasks; -using System; -using System.Collections.Generic; - -namespace MediaBrowser.WebDashboard.Api -{ - /// - /// Class DashboardInfo - /// - public class DashboardInfo - { - /// - /// Gets or sets the system info. - /// - /// The system info. - public SystemInfo SystemInfo { get; set; } - - /// - /// Gets or sets the running tasks. - /// - /// The running tasks. - public List RunningTasks { get; set; } - - /// - /// Gets or sets the application update task id. - /// - /// The application update task id. - public Guid ApplicationUpdateTaskId { get; set; } - - /// - /// Gets or sets the active connections. - /// - /// The active connections. - public List ActiveConnections { get; set; } - } - -} diff --git a/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs b/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs deleted file mode 100644 index af0f9e3a05..0000000000 --- a/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs +++ /dev/null @@ -1,62 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Logging; -using System.Threading.Tasks; - -namespace MediaBrowser.WebDashboard.Api -{ - /// - /// Class DashboardInfoWebSocketListener - /// - class DashboardInfoWebSocketListener : BasePeriodicWebSocketListener - { - /// - /// Gets the name. - /// - /// The name. - protected override string Name - { - get { return "DashboardInfo"; } - } - - private readonly IServerApplicationHost _appHost; - - /// - /// Gets or sets the task manager. - /// - /// The task manager. - private readonly ITaskManager _taskManager; - - private readonly ISessionManager _sessionManager; - private readonly IDtoService _dtoService; - - /// - /// Initializes a new instance of the class. - /// - /// The app host. - /// The logger. - /// The task manager. - /// The session manager. - public DashboardInfoWebSocketListener(IServerApplicationHost appHost, ILogger logger, ITaskManager taskManager, ISessionManager sessionManager, IDtoService dtoService) - : base(logger) - { - _appHost = appHost; - _taskManager = taskManager; - _sessionManager = sessionManager; - _dtoService = dtoService; - } - - /// - /// Gets the data to send. - /// - /// The state. - /// Task{IEnumerable{TaskInfo}}. - protected override Task GetDataToSend(object state) - { - return Task.FromResult(DashboardService.GetDashboardInfo(_appHost, _taskManager, _sessionManager, _dtoService)); - } - } -} diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 88f86632b7..2c42cced24 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -1,16 +1,13 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; -using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Plugins; -using MediaBrowser.Controller.Session; using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Tasks; using ServiceStack; +using ServiceStack.Web; using System; using System.Collections.Generic; using System.IO; @@ -18,7 +15,6 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; -using ServiceStack.Web; namespace MediaBrowser.WebDashboard.Api { @@ -66,14 +62,6 @@ namespace MediaBrowser.WebDashboard.Api public string V { get; set; } } - /// - /// Class GetDashboardInfo - /// - [Route("/dashboard/dashboardInfo", "GET")] - public class GetDashboardInfo : IReturn - { - } - /// /// Class DashboardService /// @@ -97,12 +85,6 @@ namespace MediaBrowser.WebDashboard.Api /// The request context. public IRequest Request { get; set; } - /// - /// Gets or sets the task manager. - /// - /// The task manager. - private readonly ITaskManager _taskManager; - /// /// The _app host /// @@ -113,24 +95,18 @@ namespace MediaBrowser.WebDashboard.Api /// private readonly IServerConfigurationManager _serverConfigurationManager; - private readonly ISessionManager _sessionManager; - private readonly IDtoService _dtoService; private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. /// - /// The task manager. /// The app host. /// The server configuration manager. - /// The session manager. - public DashboardService(ITaskManager taskManager, IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, ISessionManager sessionManager, IDtoService dtoService, IFileSystem fileSystem) + /// The file system. + public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem) { - _taskManager = taskManager; _appHost = appHost; _serverConfigurationManager = serverConfigurationManager; - _sessionManager = sessionManager; - _dtoService = dtoService; _fileSystem = fileSystem; } @@ -163,45 +139,6 @@ namespace MediaBrowser.WebDashboard.Api return Path.Combine(DashboardUIPath, virtualPath.Replace('/', Path.DirectorySeparatorChar)); } - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetDashboardInfo request) - { - var result = GetDashboardInfo(_appHost, _taskManager, _sessionManager, _dtoService); - - return ResultFactory.GetOptimizedResult(Request, result); - } - - /// - /// Gets the dashboard info. - /// - /// The app host. - /// The task manager. - /// The connection manager. - /// DashboardInfo. - public static DashboardInfo GetDashboardInfo(IServerApplicationHost appHost, - ITaskManager taskManager, - ISessionManager connectionManager, IDtoService dtoService) - { - var connections = connectionManager.Sessions.Where(i => i.IsActive).ToList(); - - return new DashboardInfo - { - SystemInfo = appHost.GetSystemInfo(), - - RunningTasks = taskManager.ScheduledTasks.Where(i => i.State == TaskState.Running || i.State == TaskState.Cancelling) - .Select(ScheduledTaskHelpers.GetTaskInfo) - .ToList(), - - ApplicationUpdateTaskId = taskManager.ScheduledTasks.First(t => t.ScheduledTask.GetType().Name.Equals("SystemUpdateTask", StringComparison.OrdinalIgnoreCase)).Id, - - ActiveConnections = connections.Select(dtoService.GetSessionInfoDto).ToList() - }; - } - /// /// Gets the specified request. /// @@ -473,6 +410,7 @@ namespace MediaBrowser.WebDashboard.Api "alphapicker.js", "addpluginpage.js", "advancedconfigurationpage.js", + "advancedpaths.js", "advancedserversettings.js", "metadataadvanced.js", "appsplayback.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index a0dbae3d4c..8351afbae4 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -63,9 +63,7 @@ Properties\SharedVersion.cs - - @@ -85,6 +83,9 @@ + + PreserveNewest + PreserveNewest @@ -483,6 +484,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest From 4e2764e516ba62506aa85765401769d0bc7192aa Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 26 Mar 2014 11:06:48 -0400 Subject: [PATCH 05/24] Create profile xml structure --- .../Playback/BaseStreamingService.cs | 32 +-- MediaBrowser.Api/Playback/StreamRequest.cs | 3 + MediaBrowser.Controller/Dlna/CodecProfile.cs | 12 + .../Dlna/ContainerProfile.cs | 4 + .../Dlna/DeviceIdentification.cs | 7 + MediaBrowser.Controller/Dlna/DeviceProfile.cs | 40 ++-- .../Dlna/DirectPlayProfile.cs | 7 + MediaBrowser.Controller/Dlna/IDlnaManager.cs | 22 +- MediaBrowser.Controller/Dlna/MediaProfile.cs | 11 + .../Dlna/TranscodingProfile.cs | 14 ++ MediaBrowser.Dlna/DlnaManager.cs | 207 +++++++++++++++++- MediaBrowser.Dlna/MediaBrowser.Dlna.csproj | 22 +- .../PlayTo/CurrentIdEventArgs.cs | 14 +- MediaBrowser.Dlna/PlayTo/Device.cs | 7 +- MediaBrowser.Dlna/PlayTo/DlnaController.cs | 32 +-- MediaBrowser.Dlna/PlayTo/PlaylistItem.cs | 2 + MediaBrowser.Dlna/PlayTo/StreamHelper.cs | 1 + MediaBrowser.Dlna/Profiles/DefaultProfile.cs | 2 + MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs | 4 +- .../Profiles/Foobar2000Profile.cs | 4 +- MediaBrowser.Dlna/Profiles/LgTvProfile.cs | 4 +- .../Profiles/LinksysDMA2100Profile.cs | 4 +- .../Profiles/PanasonicVieraProfile.cs | 36 +-- .../Profiles/SamsungSmartTvProfile.cs | 4 +- .../Profiles/SonyBlurayPlayer2013Profile.cs | 4 +- .../Profiles/SonyBlurayPlayerProfile.cs | 4 +- .../Profiles/SonyBravia2010Profile.cs | 4 +- .../Profiles/SonyBravia2011Profile.cs | 4 +- .../Profiles/SonyBravia2012Profile.cs | 4 +- .../Profiles/SonyBravia2013Profile.cs | 4 +- MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs | 4 +- MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs | 4 +- MediaBrowser.Dlna/Profiles/Xbox360Profile.cs | 4 +- MediaBrowser.Dlna/Profiles/XboxOneProfile.cs | 4 +- MediaBrowser.Dlna/Profiles/Xml/Default.xml | 39 ++++ MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml | 43 ++++ .../Profiles/Xml/LG Smart TV.xml | 71 ++++++ .../Profiles/Xml/Linksys DMA2100.xml | 43 ++++ .../Profiles/Xml/Panasonic Viera.xml | 64 ++++++ .../Profiles/Xml/Samsung Smart TV.xml | 100 +++++++++ .../Profiles/Xml/Sony Blu-ray Player 2013.xml | 67 ++++++ .../Profiles/Xml/Sony Blu-ray Player.xml | 95 ++++++++ .../Profiles/Xml/Sony Bravia (2010).xml | 98 +++++++++ .../Profiles/Xml/Sony Bravia (2011).xml | 101 +++++++++ .../Profiles/Xml/Sony Bravia (2012).xml | 84 +++++++ .../Profiles/Xml/Sony Bravia (2013).xml | 84 +++++++ .../Profiles/Xml/Sony PlayStation 3.xml | 92 ++++++++ MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml | 78 +++++++ MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml | 103 +++++++++ MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml | 45 ++++ MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml | 45 ++++ .../MediaBrowser.Model.Portable.csproj | 3 + .../MediaBrowser.Model.net35.csproj | 3 + MediaBrowser.Model/Dlna/DeviceProfileInfo.cs | 30 +++ MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + .../LiveTv/LiveTvManager.cs | 7 +- .../ApplicationHost.cs | 10 +- .../MediaBrowser.WebDashboard.csproj | 8 +- 58 files changed, 1727 insertions(+), 122 deletions(-) create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Default.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml create mode 100644 MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml create mode 100644 MediaBrowser.Model/Dlna/DeviceProfileInfo.cs diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index f65949ac7d..c365fcd829 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1210,85 +1210,89 @@ namespace MediaBrowser.Api.Playback if (i == 0) { - request.DeviceId = val; + request.DeviceProfileId = val; } else if (i == 1) { - request.MediaSourceId = val; + request.DeviceId = val; } else if (i == 2) { - request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); + request.MediaSourceId = val; } else if (i == 3) + { + request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); + } + else if (i == 4) { if (videoRequest != null) { videoRequest.VideoCodec = val; } } - else if (i == 4) + else if (i == 5) { request.AudioCodec = val; } - else if (i == 5) + else if (i == 6) { if (videoRequest != null) { videoRequest.AudioStreamIndex = int.Parse(val, UsCulture); } } - else if (i == 6) + else if (i == 7) { if (videoRequest != null) { videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture); } } - else if (i == 7) + else if (i == 8) { if (videoRequest != null) { videoRequest.VideoBitRate = int.Parse(val, UsCulture); } } - else if (i == 8) + else if (i == 9) { request.AudioBitRate = int.Parse(val, UsCulture); } - else if (i == 9) + else if (i == 10) { request.MaxAudioChannels = int.Parse(val, UsCulture); } - else if (i == 10) + else if (i == 11) { if (videoRequest != null) { videoRequest.MaxWidth = int.Parse(val, UsCulture); } } - else if (i == 11) + else if (i == 12) { if (videoRequest != null) { videoRequest.MaxHeight = int.Parse(val, UsCulture); } } - else if (i == 12) + else if (i == 13) { if (videoRequest != null) { videoRequest.Framerate = int.Parse(val, UsCulture); } } - else if (i == 13) + else if (i == 14) { if (videoRequest != null) { request.StartTimeTicks = long.Parse(val, UsCulture); } } - else if (i == 14) + else if (i == 15) { if (videoRequest != null) { diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 8db5920f6b..0eb2984fb5 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -65,6 +65,9 @@ namespace MediaBrowser.Api.Playback [ApiMember(Name = "Static", Description = "Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool Static { get; set; } + [ApiMember(Name = "DeviceProfileId", Description = "Optional. The dlna device profile id to utilize.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string DeviceProfileId { get; set; } + /// /// For testing purposes /// diff --git a/MediaBrowser.Controller/Dlna/CodecProfile.cs b/MediaBrowser.Controller/Dlna/CodecProfile.cs index 0f61cad98d..75f80ed3b1 100644 --- a/MediaBrowser.Controller/Dlna/CodecProfile.cs +++ b/MediaBrowser.Controller/Dlna/CodecProfile.cs @@ -1,13 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Xml.Serialization; namespace MediaBrowser.Controller.Dlna { public class CodecProfile { + [XmlAttribute("type")] public CodecType Type { get; set; } + public ProfileCondition[] Conditions { get; set; } + + [XmlAttribute("codec")] public string Codec { get; set; } public CodecProfile() @@ -37,9 +42,16 @@ namespace MediaBrowser.Controller.Dlna public class ProfileCondition { + [XmlAttribute("condition")] public ProfileConditionType Condition { get; set; } + + [XmlAttribute("property")] public ProfileConditionValue Property { get; set; } + + [XmlAttribute("value")] public string Value { get; set; } + + [XmlAttribute("isRequired")] public bool IsRequired { get; set; } public ProfileCondition() diff --git a/MediaBrowser.Controller/Dlna/ContainerProfile.cs b/MediaBrowser.Controller/Dlna/ContainerProfile.cs index 3bd3c9eaf6..1029ba72cb 100644 --- a/MediaBrowser.Controller/Dlna/ContainerProfile.cs +++ b/MediaBrowser.Controller/Dlna/ContainerProfile.cs @@ -1,12 +1,16 @@ using System.Collections.Generic; using System.Linq; +using System.Xml.Serialization; namespace MediaBrowser.Controller.Dlna { public class ContainerProfile { + [XmlAttribute("type")] public DlnaProfileType Type { get; set; } public ProfileCondition[] Conditions { get; set; } + + [XmlAttribute("container")] public string Container { get; set; } public ContainerProfile() diff --git a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs index 7b8e3a1e72..c9cd4bc703 100644 --- a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs +++ b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs @@ -1,4 +1,6 @@  +using System.Xml.Serialization; + namespace MediaBrowser.Controller.Dlna { public class DeviceIdentification @@ -62,8 +64,13 @@ namespace MediaBrowser.Controller.Dlna public class HttpHeaderInfo { + [XmlAttribute("name")] public string Name { get; set; } + + [XmlAttribute("value")] public string Value { get; set; } + + [XmlAttribute("match")] public HeaderMatchType Match { get; set; } } diff --git a/MediaBrowser.Controller/Dlna/DeviceProfile.cs b/MediaBrowser.Controller/Dlna/DeviceProfile.cs index f34c4bf645..f5aff02629 100644 --- a/MediaBrowser.Controller/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Controller/Dlna/DeviceProfile.cs @@ -1,9 +1,12 @@ using MediaBrowser.Model.Entities; using System; using System.Linq; +using System.Runtime.Serialization; +using System.Xml.Serialization; namespace MediaBrowser.Controller.Dlna { + [XmlRoot("Profile")] public class DeviceProfile { /// @@ -12,19 +15,9 @@ namespace MediaBrowser.Controller.Dlna /// The name. public string Name { get; set; } - /// - /// Gets or sets the transcoding profiles. - /// - /// The transcoding profiles. - public TranscodingProfile[] TranscodingProfiles { get; set; } - - /// - /// Gets or sets the direct play profiles. - /// - /// The direct play profiles. - public DirectPlayProfile[] DirectPlayProfiles { get; set; } - - public ContainerProfile[] ContainerProfiles { get; set; } + [XmlIgnore] + [IgnoreDataMember] + public string Id { get; set; } /// /// Gets or sets the identification. @@ -57,14 +50,27 @@ namespace MediaBrowser.Controller.Dlna public string ProtocolInfo { get; set; } - public MediaProfile[] MediaProfiles { get; set; } - public CodecProfile[] CodecProfiles { get; set; } - public int TimelineOffsetSeconds { get; set; } - public bool RequiresPlainVideoItems { get; set; } public bool RequiresPlainFolders { get; set; } + /// + /// Gets or sets the direct play profiles. + /// + /// The direct play profiles. + public DirectPlayProfile[] DirectPlayProfiles { get; set; } + + /// + /// Gets or sets the transcoding profiles. + /// + /// The transcoding profiles. + public TranscodingProfile[] TranscodingProfiles { get; set; } + + public ContainerProfile[] ContainerProfiles { get; set; } + + public CodecProfile[] CodecProfiles { get; set; } + public MediaProfile[] MediaProfiles { get; set; } + public DeviceProfile() { DirectPlayProfiles = new DirectPlayProfile[] { }; diff --git a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs index 686b31287e..ad70640daa 100644 --- a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs +++ b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs @@ -1,14 +1,21 @@ using System.Collections.Generic; using System.Linq; +using System.Xml.Serialization; namespace MediaBrowser.Controller.Dlna { public class DirectPlayProfile { + [XmlAttribute("container")] public string Container { get; set; } + + [XmlAttribute("audioCodec")] public string AudioCodec { get; set; } + + [XmlAttribute("videoCodec")] public string VideoCodec { get; set; } + [XmlAttribute("type")] public DlnaProfileType Type { get; set; } public List GetContainers() diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs index 22d13fc3ad..dd9b0e8a84 100644 --- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs +++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs @@ -1,20 +1,15 @@ -using System.Collections.Generic; +using MediaBrowser.Model.Dlna; +using System.Collections.Generic; namespace MediaBrowser.Controller.Dlna { public interface IDlnaManager { /// - /// Gets the dlna profiles. + /// Gets the profile infos. /// - /// IEnumerable{DlnaProfile}. - IEnumerable GetProfiles(); - - /// - /// Gets the default profile. - /// - /// DlnaProfile. - DeviceProfile GetDefaultProfile(); + /// IEnumerable{DeviceProfileInfo}. + IEnumerable GetProfileInfos(); /// /// Gets the profile. @@ -23,6 +18,13 @@ namespace MediaBrowser.Controller.Dlna /// DeviceProfile. DeviceProfile GetProfile(IDictionary headers); + /// + /// Gets the profile. + /// + /// The identifier. + /// DeviceProfile. + DeviceProfile GetProfile(string id); + /// /// Gets the profile. /// diff --git a/MediaBrowser.Controller/Dlna/MediaProfile.cs b/MediaBrowser.Controller/Dlna/MediaProfile.cs index 9a9b56ddd5..bf3057294c 100644 --- a/MediaBrowser.Controller/Dlna/MediaProfile.cs +++ b/MediaBrowser.Controller/Dlna/MediaProfile.cs @@ -1,16 +1,27 @@ using System.Collections.Generic; using System.Linq; +using System.Xml.Serialization; namespace MediaBrowser.Controller.Dlna { public class MediaProfile { + [XmlAttribute("container")] public string Container { get; set; } + + [XmlAttribute("audioCodec")] public string AudioCodec { get; set; } + + [XmlAttribute("videoCodec")] public string VideoCodec { get; set; } + [XmlAttribute("type")] public DlnaProfileType Type { get; set; } + + [XmlAttribute("orgPn")] public string OrgPn { get; set; } + + [XmlAttribute("mimeType")] public string MimeType { get; set; } public ProfileCondition[] Conditions { get; set; } diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs index d4cfae9893..289333aa77 100644 --- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs @@ -1,19 +1,30 @@ using System.Collections.Generic; using System.Linq; +using System.Xml.Serialization; namespace MediaBrowser.Controller.Dlna { public class TranscodingProfile { + [XmlAttribute("container")] public string Container { get; set; } + [XmlAttribute("type")] public DlnaProfileType Type { get; set; } + [XmlAttribute("videoCodec")] public string VideoCodec { get; set; } + + [XmlAttribute("audioCodec")] public string AudioCodec { get; set; } + [XmlAttribute("estimateContentLength")] public bool EstimateContentLength { get; set; } + + [XmlAttribute("enableMpegtsM2TsMode")] public bool EnableMpegtsM2TsMode { get; set; } + + [XmlAttribute("transcodeSeekInfo")] public TranscodeSeekInfo TranscodeSeekInfo { get; set; } public TranscodingSetting[] Settings { get; set; } @@ -32,7 +43,10 @@ namespace MediaBrowser.Controller.Dlna public class TranscodingSetting { + [XmlAttribute("name")] public TranscodingSettingType Name { get; set; } + + [XmlAttribute("value")] public string Value { get; set; } } diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index 78876d239c..9d9df01e6d 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -1,10 +1,14 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Dlna; using MediaBrowser.Dlna.Profiles; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -12,21 +16,36 @@ namespace MediaBrowser.Dlna { public class DlnaManager : IDlnaManager { - private IApplicationPaths _appPaths; + private readonly IApplicationPaths _appPaths; private readonly IXmlSerializer _xmlSerializer; private readonly IFileSystem _fileSystem; - private readonly IJsonSerializer _jsonSerializer; + private readonly ILogger _logger; - public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IJsonSerializer jsonSerializer) + public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger) { _xmlSerializer = xmlSerializer; _fileSystem = fileSystem; - _jsonSerializer = jsonSerializer; + _appPaths = appPaths; + _logger = logger; - GetProfiles(); + //DumpProfiles(); } public IEnumerable GetProfiles() + { + ExtractProfilesIfNeeded(); + + var list = GetProfiles(UserProfilesPath) + .OrderBy(i => i.Name) + .ToList(); + + list.AddRange(GetProfiles(SystemProfilesPath) + .OrderBy(i => i.Name)); + + return list; + } + + private void DumpProfiles() { var list = new List { @@ -45,16 +64,40 @@ namespace MediaBrowser.Dlna new DenonAvrProfile(), new LinksysDMA2100Profile(), new LgTvProfile(), - new Foobar2000Profile() + new Foobar2000Profile(), + new DefaultProfile() }; foreach (var item in list) { - //_xmlSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name) + ".xml"); - //_jsonSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name) + ".json"); + _xmlSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name) + ".xml"); } + } - return list; + private bool _extracted; + private readonly object _syncLock = new object(); + private void ExtractProfilesIfNeeded() + { + if (!_extracted) + { + lock (_syncLock) + { + if (!_extracted) + { + try + { + ExtractSystemProfiles(); + } + catch (Exception ex) + { + _logger.ErrorException("Error extracting DLNA profiles.", ex); + } + + _extracted = true; + } + + } + } } public DeviceProfile GetDefaultProfile() @@ -64,8 +107,12 @@ namespace MediaBrowser.Dlna public DeviceProfile GetProfile(DeviceIdentification deviceInfo) { - return GetProfiles().FirstOrDefault(i => IsMatch(deviceInfo, i.Identification)) ?? + var profile = GetProfiles().FirstOrDefault(i => IsMatch(deviceInfo, i.Identification)) ?? GetDefaultProfile(); + + _logger.Debug("Found matching device profile: {0}", profile.Name); + + return profile; } private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo) @@ -159,5 +206,145 @@ namespace MediaBrowser.Dlna return false; } + + private string UserProfilesPath + { + get + { + return Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user"); + } + } + + private string SystemProfilesPath + { + get + { + return Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system"); + } + } + + private IEnumerable GetProfiles(string path) + { + try + { + return new DirectoryInfo(path) + .EnumerateFiles("*", SearchOption.TopDirectoryOnly) + .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase)) + .Select(i => ParseProfileXmlFile(i.FullName)) + .Where(i => i != null) + .ToList(); + } + catch (DirectoryNotFoundException) + { + return new List(); + } + } + + private DeviceProfile ParseProfileXmlFile(string path) + { + try + { + var profile = (DeviceProfile)_xmlSerializer.DeserializeFromFile(typeof(DeviceProfile), path); + + profile.Id = path.ToLower().GetMD5().ToString("N"); + + return profile; + } + catch (Exception ex) + { + _logger.ErrorException("Error parsing profile xml: {0}", ex, path); + + return null; + } + } + + public DeviceProfile GetProfile(string id) + { + var info = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, id)); + + return ParseProfileXmlFile(info.Path); + } + + private IEnumerable GetProfileInfosInternal() + { + ExtractProfilesIfNeeded(); + + return GetProfileInfos(UserProfilesPath, DeviceProfileType.User) + .Concat(GetProfileInfos(SystemProfilesPath, DeviceProfileType.System)) + .OrderBy(i => i.Info.Type == DeviceProfileType.User ? 0 : 1) + .ThenBy(i => i.Info.Name); + } + + public IEnumerable GetProfileInfos() + { + return GetProfileInfosInternal().Select(i => i.Info); + } + + private IEnumerable GetProfileInfos(string path, DeviceProfileType type) + { + try + { + return new DirectoryInfo(path) + .EnumerateFiles("*", SearchOption.TopDirectoryOnly) + .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase)) + .Select(i => new InternalProfileInfo + { + Path = i.FullName, + + Info = new DeviceProfileInfo + { + Id = i.FullName.ToLower().GetMD5().ToString("N"), + Name = Path.GetFileNameWithoutExtension(i.FullName), + Type = type + } + }) + .ToList(); + } + catch (DirectoryNotFoundException) + { + return new List(); + } + } + + private void ExtractSystemProfiles() + { + var assembly = GetType().Assembly; + var namespaceName = GetType().Namespace + ".Profiles.Xml."; + + var systemProfilesPath = SystemProfilesPath; + + foreach (var name in assembly.GetManifestResourceNames() + .Where(i => i.StartsWith(namespaceName)) + .ToList()) + { + var filename = Path.GetFileName(name).Substring(namespaceName.Length); + + var path = Path.Combine(systemProfilesPath, filename); + + using (var stream = assembly.GetManifestResourceStream(name)) + { + var fileInfo = new FileInfo(path); + + if (!fileInfo.Exists || fileInfo.Length != stream.Length) + { + Directory.CreateDirectory(systemProfilesPath); + + using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + stream.CopyTo(fileStream); + } + } + } + } + + // Not necessary, but just to make it easy to find + Directory.CreateDirectory(UserProfilesPath); + } + + class InternalProfileInfo + { + internal DeviceProfileInfo Info { get; set; } + internal string Path { get; set; } + } } } \ No newline at end of file diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index bdfcae39b6..df1fed12f0 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -117,7 +117,27 @@ MediaBrowser.Model - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6616e46acc --- /dev/null +++ b/MediaBrowser.MediaEncoding/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MediaBrowser.MediaEncoding")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.MediaEncoding")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("05f49ab9-2a90-4332-9d41-7817a9cccd90")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MediaBrowser.MediaEncoding/packages.config b/MediaBrowser.MediaEncoding/packages.config new file mode 100644 index 0000000000..6e52b72b8f --- /dev/null +++ b/MediaBrowser.MediaEncoding/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index b3c3f278eb..b2ca97f55c 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -94,7 +94,7 @@ namespace MediaBrowser.Providers.MediaInfo { Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var stream = await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, cancellationToken).ConfigureAwait(false)) + using (var stream = await _mediaEncoder.ExtractAudioImage(item.Path, cancellationToken).ConfigureAwait(false)) { using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 58fe7f66d4..cb326c5ad0 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -113,8 +113,6 @@ namespace MediaBrowser.Providers.MediaInfo { cancellationToken.ThrowIfCancellationRequested(); - cancellationToken.ThrowIfCancellationRequested(); - var idString = item.Id.ToString("N"); var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-video", diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 3547584979..70daa3f519 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.MediaInfo var inputPath = MediaEncoderHelpers.GetInputArgument(item.Path, item.LocationType == LocationType.Remote, item.VideoType, item.IsoType, isoMount, item.PlayableStreamFileNames, out type); - var stream = await _mediaEncoder.ExtractImage(inputPath, type, false, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false); + var stream = await _mediaEncoder.ExtractVideoImage(inputPath, type, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false); return new DynamicImageResponse { diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 0a0b3f4bcd..9279fd8d73 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -249,17 +249,24 @@ namespace MediaBrowser.Server.Implementations.IO // Creating a FileSystemWatcher over the LAN can take hundreds of milliseconds, so wrap it in a Task to do them all in parallel Task.Run(() => { - var newWatcher = new FileSystemWatcher(path, "*") { IncludeSubdirectories = true, InternalBufferSize = 32767 }; + try + { + var newWatcher = new FileSystemWatcher(path, "*") + { + IncludeSubdirectories = true, + InternalBufferSize = 32767 + }; - newWatcher.Created += watcher_Changed; - newWatcher.Deleted += watcher_Changed; - newWatcher.Renamed += watcher_Changed; - newWatcher.Changed += watcher_Changed; + newWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName | + NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size; - newWatcher.Error += watcher_Error; + newWatcher.Created += watcher_Changed; + newWatcher.Deleted += watcher_Changed; + newWatcher.Renamed += watcher_Changed; + newWatcher.Changed += watcher_Changed; + + newWatcher.Error += watcher_Error; - try - { if (_fileSystemWatchers.TryAdd(path, newWatcher)) { newWatcher.EnableRaisingEvents = true; @@ -272,11 +279,7 @@ namespace MediaBrowser.Server.Implementations.IO } } - catch (IOException ex) - { - Logger.ErrorException("Error watching path: {0}", ex, path); - } - catch (PlatformNotSupportedException ex) + catch (Exception ex) { Logger.ErrorException("Error watching path: {0}", ex, path); } @@ -346,7 +349,9 @@ namespace MediaBrowser.Server.Implementations.IO { try { - OnWatcherChanged(e); + Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); + + ReportFileSystemChanged(e.FullPath); } catch (Exception ex) { @@ -354,13 +359,6 @@ namespace MediaBrowser.Server.Implementations.IO } } - private void OnWatcherChanged(FileSystemEventArgs e) - { - Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); - - ReportFileSystemChanged(e.FullPath); - } - public void ReportFileSystemChanged(string path) { if (string.IsNullOrEmpty(path)) @@ -370,12 +368,9 @@ namespace MediaBrowser.Server.Implementations.IO var filename = Path.GetFileName(path); - // Ignore certain files - if (!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)) - { - return; - } + var monitorPath = !(!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase)); + // Ignore certain files var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList(); // If the parent of an ignored path has a change event, ignore that too @@ -416,12 +411,15 @@ namespace MediaBrowser.Server.Implementations.IO })) { - return; + monitorPath = false; } - // Avoid implicitly captured closure - var affectedPath = path; - _affectedPaths.AddOrUpdate(path, path, (key, oldValue) => affectedPath); + if (monitorPath) + { + // Avoid implicitly captured closure + var affectedPath = path; + _affectedPaths.AddOrUpdate(path, path, (key, oldValue) => affectedPath); + } lock (_timerLock) { diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 73a12caf2c..ea7ef2ed65 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -48,14 +48,6 @@ ..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll - - False - ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\BDInfo.dll - - - False - ..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll - False ..\packages\Mono.Nat.1.2.3\lib\Net40\Mono.Nat.dll @@ -105,7 +97,6 @@ Properties\SharedVersion.cs - @@ -191,7 +182,6 @@ - diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index f74865d2c7..7237ffee5c 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -178,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var stream = await _encoder.ExtractImage(inputPath, type, false, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false)) + using (var stream = await _encoder.ExtractVideoImage(inputPath, type, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false)) { using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs index f4e7fd0a6b..fde1e7f216 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs @@ -37,6 +37,8 @@ namespace MediaBrowser.Server.Implementations.Persistence var createTableCommand = "create table if not exists mediastreams "; + // Add PixelFormat column + createTableCommand += "(ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PRIMARY KEY (ItemId, StreamIndex))"; string[] queries = { diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index f04536190b..d82e880c98 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,7 +1,6 @@  - diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index fe7b17d1dd..dff6242d1a 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -33,13 +33,14 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Themes; using MediaBrowser.Dlna; using MediaBrowser.Dlna.PlayTo; +using MediaBrowser.MediaEncoding.BdInfo; +using MediaBrowser.MediaEncoding.Encoder; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; using MediaBrowser.Model.Updates; using MediaBrowser.Providers.Manager; using MediaBrowser.Server.Implementations; -using MediaBrowser.Server.Implementations.BdInfo; using MediaBrowser.Server.Implementations.Channels; using MediaBrowser.Server.Implementations.Collections; using MediaBrowser.Server.Implementations.Configuration; @@ -815,7 +816,10 @@ namespace MediaBrowser.ServerApplication // Server implementations list.Add(typeof(ServerApplicationPaths).Assembly); - // Dlna implementations + // MediaEncoding + list.Add(typeof(MediaEncoder).Assembly); + + // Dlna list.Add(typeof(PlayToServerEntryPoint).Assembly); list.AddRange(Assemblies.GetAssembliesWithParts()); diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 227d0dd1d3..d999841ca7 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -195,6 +195,10 @@ {734098eb-6dc1-4dd0-a1ca-3140dcd2737c} MediaBrowser.Dlna + + {0bd82fa6-eb8a-4452-8af5-74f9c3849451} + MediaBrowser.MediaEncoding + {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} MediaBrowser.Model diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 7ac1580659..7dc06fb0ce 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ServerApplicat EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Dlna", "MediaBrowser.Dlna\MediaBrowser.Dlna.csproj", "{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -247,6 +249,20 @@ Global {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Win32.ActiveCfg = Release|Any CPU {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x64.ActiveCfg = Release|Any CPU {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x86.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Win32.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x64.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Win32.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x64.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 9705594845756710a0bb2f6f6a879f9c86d8f417 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 27 Mar 2014 19:01:42 -0400 Subject: [PATCH 18/24] add image encoder based on ffmpeg --- .../Playback/BaseStreamingService.cs | 9 +- .../MediaBrowser.Controller.csproj | 1 + .../MediaEncoding/IMediaEncoder.cs | 8 + .../MediaEncoding/ImageEncodingOptions.cs | 18 ++ .../Encoder/ImageEncoder.cs | 158 ++++++++++++++++++ .../Encoder/MediaEncoder.cs | 9 +- .../MediaBrowser.MediaEncoding.csproj | 1 + 7 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs create mode 100644 MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index ceb96d226c..b510a640e4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -503,14 +503,13 @@ namespace MediaBrowser.Api.Playback return string.Format("{4} -vf \"{0}scale=trunc({1}/2)*2:trunc({2}/2)*2{3}\"", yadifParam, widthParam, heightParam, assSubtitleParam, copyTsParam); } - // If Max dimensions were supplied - //this makes my brain hurt. For width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size + // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size if (request.MaxWidth.HasValue && request.MaxHeight.HasValue) { - var MaxwidthParam = request.MaxWidth.Value.ToString(UsCulture); - var MaxheightParam = request.MaxHeight.Value.ToString(UsCulture); + var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture); + var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture); - return string.Format("{4} -vf \"{0}scale=trunc(min(iw\\,{1})/2)*2:trunc(min((iw/dar)\\,{2})/2)*2{3}\"", yadifParam, MaxwidthParam, MaxheightParam, assSubtitleParam, copyTsParam); + return string.Format("{4} -vf \"{0}scale=trunc(min(iw\\,{1})/2)*2:trunc(min((iw/dar)\\,{2})/2)*2{3}\"", yadifParam, maxWidthParam, maxHeightParam, assSubtitleParam, copyTsParam); } var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 5e6297d060..38955f728e 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -157,6 +157,7 @@ + diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 657e52e5a8..e9081fe8a3 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -88,6 +88,14 @@ namespace MediaBrowser.Controller.MediaEncoding /// The type. /// System.String. string GetInputArgument(string[] inputFiles, InputType type); + + /// + /// Encodes the image. + /// + /// The options. + /// The cancellation token. + /// Task{Stream}. + Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken); } /// diff --git a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs new file mode 100644 index 0000000000..c76977f29b --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs @@ -0,0 +1,18 @@ + +namespace MediaBrowser.Controller.MediaEncoding +{ + public class ImageEncodingOptions + { + public string InputPath { get; set; } + + public int? Width { get; set; } + + public int? Height { get; set; } + + public int? MaxWidth { get; set; } + + public int? MaxHeight { get; set; } + + public string Format { get; set; } + } +} diff --git a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs new file mode 100644 index 0000000000..5e4221b0f5 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs @@ -0,0 +1,158 @@ +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Logging; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.MediaEncoding.Encoder +{ + public class ImageEncoder + { + private readonly string _ffmpegPath; + private readonly ILogger _logger; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(5, 5); + + public ImageEncoder(string ffmpegPath, ILogger logger) + { + _ffmpegPath = ffmpegPath; + _logger = logger; + } + + public async Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) + { + ValidateInput(options); + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + FileName = _ffmpegPath, + Arguments = GetArguments(options), + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false, + RedirectStandardOutput = true, + RedirectStandardError = true + } + }; + + await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + process.Start(); + + var memoryStream = new MemoryStream(); + +#pragma warning disable 4014 + // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback + process.StandardOutput.BaseStream.CopyToAsync(memoryStream); +#pragma warning restore 4014 + + // MUST read both stdout and stderr asynchronously or a deadlock may occurr + process.BeginErrorReadLine(); + + var ranToCompletion = process.WaitForExit(5000); + + if (!ranToCompletion) + { + try + { + _logger.Info("Killing ffmpeg process"); + + process.Kill(); + + process.WaitForExit(1000); + } + catch (Exception ex) + { + _logger.ErrorException("Error killing process", ex); + } + } + + ResourcePool.Release(); + + var exitCode = ranToCompletion ? process.ExitCode : -1; + + process.Dispose(); + + if (exitCode == -1 || memoryStream.Length == 0) + { + memoryStream.Dispose(); + + var msg = string.Format("ffmpeg image encoding failed for {0}", options.InputPath); + + _logger.Error(msg); + + throw new ApplicationException(msg); + } + + memoryStream.Position = 0; + return memoryStream; + } + + private string GetArguments(ImageEncodingOptions options) + { + var vfScale = GetFilterGraph(options); + var outputFormat = GetOutputFormat(options); + + return string.Format("-i file:\"{0}\" {1} -f {2}", + options.InputPath, + vfScale, + outputFormat); + } + + private string GetFilterGraph(ImageEncodingOptions options) + { + if (!options.Width.HasValue && + !options.Height.HasValue && + !options.MaxHeight.HasValue && + !options.MaxWidth.HasValue) + { + return string.Empty; + } + + var widthScale = "-1"; + var heightScale = "-1"; + + if (options.MaxWidth.HasValue) + { + widthScale = "min(iw," + options.MaxWidth.Value.ToString(_usCulture) + ")"; + } + else if (options.Width.HasValue) + { + widthScale = options.Width.Value.ToString(_usCulture); + } + + if (options.MaxHeight.HasValue) + { + heightScale = "min(ih," + options.MaxHeight.Value.ToString(_usCulture) + ")"; + } + else if (options.Height.HasValue) + { + heightScale = options.Height.Value.ToString(_usCulture); + } + + var scaleMethod = "lanczos"; + + return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", + widthScale, + heightScale, + scaleMethod); + } + + private string GetOutputFormat(ImageEncodingOptions options) + { + return options.Format; + } + + private void ValidateInput(ImageEncodingOptions options) + { + + } + } +} diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7cabf23c48..55035a4caf 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -948,14 +948,9 @@ namespace MediaBrowser.MediaEncoding.Encoder return GetFileInputArgument(playableStreamFiles[0]); } - /// - /// Gets the bluray input argument. - /// - /// The bluray root. - /// System.String. - private string GetBlurayInputArgument(string blurayRoot) + public Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { - return string.Format("bluray:\"{0}\"", blurayRoot); + return new ImageEncoder(FFMpegPath, _logger).EncodeImage(options, cancellationToken); } /// diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index d92522bf01..fb1041f89e 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -48,6 +48,7 @@ + From 1664de62c0571dc24e495f29e59f92a29a8b9b95 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 27 Mar 2014 23:32:43 -0400 Subject: [PATCH 19/24] added image encoder methods --- .../MediaEncoding/ImageEncodingOptions.cs | 2 + .../Encoder/ImageEncoder.cs | 65 ++++++++++---- .../Encoder/MediaEncoder.cs | 41 +-------- .../Drawing/ImageProcessor.cs | 87 ++++++++++++------- .../ApplicationHost.cs | 14 +-- MediaBrowser.sln | 3 + 6 files changed, 117 insertions(+), 95 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs index c76977f29b..a8d1e5a0f1 100644 --- a/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/ImageEncodingOptions.cs @@ -13,6 +13,8 @@ namespace MediaBrowser.Controller.MediaEncoding public int? MaxHeight { get; set; } + public int? Quality { get; set; } + public string Format { get; set; } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs index 5e4221b0f5..d259c631d0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Logging; using System; using System.Diagnostics; @@ -13,20 +14,39 @@ namespace MediaBrowser.MediaEncoding.Encoder { private readonly string _ffmpegPath; private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(5, 5); + private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(10, 10); - public ImageEncoder(string ffmpegPath, ILogger logger) + public ImageEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem) { _ffmpegPath = ffmpegPath; _logger = logger; + _fileSystem = fileSystem; } public async Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { ValidateInput(options); + await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + return await EncodeImageInternal(options, cancellationToken).ConfigureAwait(false); + } + finally + { + ResourcePool.Release(); + } + } + + private async Task EncodeImageInternal(ImageEncodingOptions options, CancellationToken cancellationToken) + { + ValidateInput(options); + var process = new Process { StartInfo = new ProcessStartInfo @@ -38,11 +58,12 @@ namespace MediaBrowser.MediaEncoding.Encoder WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false, RedirectStandardOutput = true, - RedirectStandardError = true + RedirectStandardError = true, + WorkingDirectory = Path.GetDirectoryName(options.InputPath) } }; - await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + _logger.Debug("ffmpeg " + process.StartInfo.Arguments); process.Start(); @@ -74,8 +95,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - ResourcePool.Release(); - var exitCode = ranToCompletion ? process.ExitCode : -1; process.Dispose(); @@ -94,16 +113,21 @@ namespace MediaBrowser.MediaEncoding.Encoder memoryStream.Position = 0; return memoryStream; } - + private string GetArguments(ImageEncodingOptions options) { var vfScale = GetFilterGraph(options); - var outputFormat = GetOutputFormat(options); + var outputFormat = GetOutputFormat(options.Format); + + var quality = (options.Quality ?? 100) * .3; + quality = 31 - quality; + var qualityValue = Convert.ToInt32(Math.Max(quality, 1)); - return string.Format("-i file:\"{0}\" {1} -f {2}", - options.InputPath, + return string.Format("-f image2 -i file:\"{3}\" -q:v {0} {1} -f image2pipe -vcodec {2} -", + qualityValue.ToString(_usCulture), vfScale, - outputFormat); + outputFormat, + Path.GetFileName(options.InputPath)); } private string GetFilterGraph(ImageEncodingOptions options) @@ -121,7 +145,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (options.MaxWidth.HasValue) { - widthScale = "min(iw," + options.MaxWidth.Value.ToString(_usCulture) + ")"; + widthScale = "min(iw\\," + options.MaxWidth.Value.ToString(_usCulture) + ")"; } else if (options.Width.HasValue) { @@ -130,7 +154,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (options.MaxHeight.HasValue) { - heightScale = "min(ih," + options.MaxHeight.Value.ToString(_usCulture) + ")"; + heightScale = "min(ih\\," + options.MaxHeight.Value.ToString(_usCulture) + ")"; } else if (options.Height.HasValue) { @@ -139,15 +163,20 @@ namespace MediaBrowser.MediaEncoding.Encoder var scaleMethod = "lanczos"; - return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", - widthScale, + return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", + widthScale, heightScale, scaleMethod); } - private string GetOutputFormat(ImageEncodingOptions options) + private string GetOutputFormat(string format) { - return options.Format; + if (string.Equals(format, "jpeg", StringComparison.OrdinalIgnoreCase) || + string.Equals(format, "jpg", StringComparison.OrdinalIgnoreCase)) + { + return "mjpeg"; + } + return format; } private void ValidateInput(ImageEncodingOptions options) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 55035a4caf..8b41d2105f 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -879,45 +879,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return memoryStream; } - /// - /// Starts the and wait for process. - /// - /// The process. - /// The timeout. - /// true if XXXX, false otherwise - private bool StartAndWaitForProcess(Process process, int timeout = 10000) - { - process.Start(); - - var ranToCompletion = process.WaitForExit(timeout); - - if (!ranToCompletion) - { - try - { - _logger.Info("Killing ffmpeg process"); - - process.Kill(); - - process.WaitForExit(1000); - } - catch (Win32Exception ex) - { - _logger.ErrorException("Error killing process", ex); - } - catch (InvalidOperationException ex) - { - _logger.ErrorException("Error killing process", ex); - } - catch (NotSupportedException ex) - { - _logger.ErrorException("Error killing process", ex); - } - } - - return ranToCompletion; - } - /// /// Gets the file input argument. /// @@ -950,7 +911,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { - return new ImageEncoder(FFMpegPath, _logger).EncodeImage(options, cancellationToken); + return new ImageEncoder(FFMpegPath, _logger, _fileSystem).EncodeImage(options, cancellationToken); } /// diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 12d3eadfd3..408d8c9b1f 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -3,6 +3,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -52,12 +53,14 @@ namespace MediaBrowser.Server.Implementations.Drawing private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; private readonly IServerApplicationPaths _appPaths; + private readonly IMediaEncoder _mediaEncoder; - public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer) + public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder) { _logger = logger; _fileSystem = fileSystem; _jsonSerializer = jsonSerializer; + _mediaEncoder = mediaEncoder; _appPaths = appPaths; _saveImageSizeTimer = new Timer(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite); @@ -66,7 +69,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - sizeDictionary = jsonSerializer.DeserializeFromFile>(ImageSizeFile) ?? + sizeDictionary = jsonSerializer.DeserializeFromFile>(ImageSizeFile) ?? new Dictionary(); } catch (FileNotFoundException) @@ -213,6 +216,39 @@ namespace MediaBrowser.Server.Implementations.Drawing try { + var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed.HasValue; + + if (!hasPostProcessing) + { + using (var outputStream = await _mediaEncoder.EncodeImage(new ImageEncodingOptions + { + InputPath = originalImagePath, + MaxHeight = options.MaxHeight, + MaxWidth = options.MaxWidth, + Height = options.Height, + Width = options.Width, + Quality = options.Quality, + Format = options.OutputFormat == ImageOutputFormat.Original ? Path.GetExtension(originalImagePath).TrimStart('.') : options.OutputFormat.ToString().ToLower() + + }, CancellationToken.None).ConfigureAwait(false)) + { + using (var outputMemoryStream = new MemoryStream()) + { + // Save to the memory stream + await outputStream.CopyToAsync(outputMemoryStream).ConfigureAwait(false); + + var bytes = outputMemoryStream.ToArray(); + + await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + + // kick off a task to cache the result + await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false); + } + + return; + } + } + using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { // Copy to memory stream to avoid Image locking file @@ -241,8 +277,8 @@ namespace MediaBrowser.Server.Implementations.Drawing thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; - thumbnailGraph.CompositingMode = string.IsNullOrEmpty(options.BackgroundColor) && !options.UnplayedCount.HasValue && !options.AddPlayedIndicator && !options.PercentPlayed.HasValue ? - CompositingMode.SourceCopy : + thumbnailGraph.CompositingMode = !hasPostProcessing ? + CompositingMode.SourceCopy : CompositingMode.SourceOver; SetBackgroundColor(thumbnailGraph, options); @@ -263,7 +299,7 @@ namespace MediaBrowser.Server.Implementations.Drawing await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); // kick off a task to cache the result - CacheResizedImage(cacheFilePath, bytes, semaphore); + await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false); } } } @@ -272,11 +308,9 @@ namespace MediaBrowser.Server.Implementations.Drawing } } } - catch + finally { semaphore.Release(); - - throw; } } @@ -285,33 +319,26 @@ namespace MediaBrowser.Server.Implementations.Drawing /// /// The cache file path. /// The bytes. - /// The semaphore. - private void CacheResizedImage(string cacheFilePath, byte[] bytes, SemaphoreSlim semaphore) + /// Task. + private async Task CacheResizedImage(string cacheFilePath, byte[] bytes) { - Task.Run(async () => + try { - try - { - var parentPath = Path.GetDirectoryName(cacheFilePath); + var parentPath = Path.GetDirectoryName(cacheFilePath); - Directory.CreateDirectory(parentPath); + Directory.CreateDirectory(parentPath); - // Save to the cache location - using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) - { - // Save to the filestream - await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - } - } - catch (Exception ex) - { - _logger.ErrorException("Error writing to image cache file {0}", ex, cacheFilePath); - } - finally + // Save to the cache location + using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { - semaphore.Release(); + // Save to the filestream + await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); } - }); + } + catch (Exception ex) + { + _logger.ErrorException("Error writing to image cache file {0}", ex, cacheFilePath); + } } /// @@ -519,7 +546,7 @@ namespace MediaBrowser.Server.Implementations.Drawing { filename += "iv=" + IndicatorVersion; } - + if (!string.IsNullOrEmpty(backgroundColor)) { filename += "b=" + backgroundColor; diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 6083158bcc..b7e9017d6e 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -473,7 +473,13 @@ namespace MediaBrowser.ServerApplication LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(LocalizationManager); - ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer); + var innerProgress = new ActionableProgress(); + innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15)); + + await RegisterMediaEncoder(innerProgress).ConfigureAwait(false); + progress.Report(90); + + ImageProcessor = new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, MediaEncoder); RegisterSingleInstance(ImageProcessor); DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager); @@ -487,12 +493,6 @@ namespace MediaBrowser.ServerApplication progress.Report(15); - var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15)); - - await RegisterMediaEncoder(innerProgress).ConfigureAwait(false); - progress.Report(90); - EncodingManager = new EncodingManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, MediaEncoder); RegisterSingleInstance(EncodingManager); diff --git a/MediaBrowser.sln b/MediaBrowser.sln index c2f9dff59c..7dc06fb0ce 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -267,4 +267,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal From ec49a657525292b97c152027779b96274ed30685 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 28 Mar 2014 00:24:11 -0400 Subject: [PATCH 20/24] correct dlna param positions --- .../Playback/BaseStreamingService.cs | 17 +++-- .../Encoder/ImageEncoder.cs | 66 ++++++++++++++++--- .../Encoder/MediaEncoder.cs | 2 +- .../Drawing/ImageOutputFormat.cs | 3 +- .../Music/LastfmArtistProvider.cs | 24 ------- .../Drawing/ImageProcessor.cs | 60 ++++++++--------- 6 files changed, 101 insertions(+), 71 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index b510a640e4..519ff7947a 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -967,8 +967,6 @@ namespace MediaBrowser.Api.Playback private async void StreamToStandardInput(Process process, StreamState state) { - state.StandardInputCancellationTokenSource = new CancellationTokenSource(); - try { await StreamToStandardInputInternal(process, state).ConfigureAwait(false); @@ -1263,31 +1261,38 @@ namespace MediaBrowser.Api.Playback { if (videoRequest != null) { - videoRequest.MaxWidth = int.Parse(val, UsCulture); + videoRequest.MaxFramerate = double.Parse(val, UsCulture); } } else if (i == 12) { if (videoRequest != null) { - videoRequest.MaxHeight = int.Parse(val, UsCulture); + videoRequest.MaxWidth = int.Parse(val, UsCulture); } } else if (i == 13) { if (videoRequest != null) { - videoRequest.Framerate = int.Parse(val, UsCulture); + videoRequest.MaxHeight = int.Parse(val, UsCulture); } } else if (i == 14) { if (videoRequest != null) { - request.StartTimeTicks = long.Parse(val, UsCulture); + videoRequest.Framerate = int.Parse(val, UsCulture); } } else if (i == 15) + { + if (videoRequest != null) + { + request.StartTimeTicks = long.Parse(val, UsCulture); + } + } + else if (i == 16) { if (videoRequest != null) { diff --git a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs index d259c631d0..e0ca86c41a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs @@ -1,10 +1,13 @@ -using MediaBrowser.Common.IO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Logging; using System; using System.Diagnostics; using System.Globalization; using System.IO; +using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -15,16 +18,18 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly string _ffmpegPath; private readonly ILogger _logger; private readonly IFileSystem _fileSystem; + private readonly IApplicationPaths _appPaths; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(10, 10); - public ImageEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem) + public ImageEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths) { _ffmpegPath = ffmpegPath; _logger = logger; _fileSystem = fileSystem; + _appPaths = appPaths; } public async Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) @@ -47,6 +52,15 @@ namespace MediaBrowser.MediaEncoding.Encoder { ValidateInput(options); + var inputPath = options.InputPath; + var filename = Path.GetFileName(inputPath); + + if (HasDiacritics(filename)) + { + inputPath = GetTempFile(inputPath); + filename = Path.GetFileName(inputPath); + } + var process = new Process { StartInfo = new ProcessStartInfo @@ -54,12 +68,12 @@ namespace MediaBrowser.MediaEncoding.Encoder CreateNoWindow = true, UseShellExecute = false, FileName = _ffmpegPath, - Arguments = GetArguments(options), + Arguments = GetArguments(options, filename), WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false, RedirectStandardOutput = true, RedirectStandardError = true, - WorkingDirectory = Path.GetDirectoryName(options.InputPath) + WorkingDirectory = Path.GetDirectoryName(inputPath) } }; @@ -113,8 +127,19 @@ namespace MediaBrowser.MediaEncoding.Encoder memoryStream.Position = 0; return memoryStream; } + + private string GetTempFile(string path) + { + var extension = Path.GetExtension(path) ?? string.Empty; + + var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N") + extension); + + File.Copy(path, tempPath); + + return tempPath; + } - private string GetArguments(ImageEncodingOptions options) + private string GetArguments(ImageEncodingOptions options, string inputFilename) { var vfScale = GetFilterGraph(options); var outputFormat = GetOutputFormat(options.Format); @@ -127,7 +152,7 @@ namespace MediaBrowser.MediaEncoding.Encoder qualityValue.ToString(_usCulture), vfScale, outputFormat, - Path.GetFileName(options.InputPath)); + inputFilename); } private string GetFilterGraph(ImageEncodingOptions options) @@ -163,10 +188,9 @@ namespace MediaBrowser.MediaEncoding.Encoder var scaleMethod = "lanczos"; - return string.Format("-vf scale=\"{0}:{1}\" -sws_flags {2}", + return string.Format("-vf scale=\"{0}:{1}\"", widthScale, - heightScale, - scaleMethod); + heightScale); } private string GetOutputFormat(string format) @@ -183,5 +207,29 @@ namespace MediaBrowser.MediaEncoding.Encoder { } + + /// + /// Determines whether the specified text has diacritics. + /// + /// The text. + /// true if the specified text has diacritics; otherwise, false. + private bool HasDiacritics(string text) + { + return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal); + } + + /// + /// Removes the diacritics. + /// + /// The text. + /// System.String. + private string RemoveDiacritics(string text) + { + return String.Concat( + text.Normalize(NormalizationForm.FormD) + .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != + UnicodeCategory.NonSpacingMark) + ).Normalize(NormalizationForm.FormC); + } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 8b41d2105f..fac54ecfff 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -911,7 +911,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { - return new ImageEncoder(FFMpegPath, _logger, _fileSystem).EncodeImage(options, cancellationToken); + return new ImageEncoder(FFMpegPath, _logger, _fileSystem, _appPaths).EncodeImage(options, cancellationToken); } /// diff --git a/MediaBrowser.Model/Drawing/ImageOutputFormat.cs b/MediaBrowser.Model/Drawing/ImageOutputFormat.cs index 6cbe75a7a0..824970073a 100644 --- a/MediaBrowser.Model/Drawing/ImageOutputFormat.cs +++ b/MediaBrowser.Model/Drawing/ImageOutputFormat.cs @@ -25,6 +25,7 @@ namespace MediaBrowser.Model.Drawing /// /// The PNG /// - Png + Png, + Webp } } diff --git a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs index a50e5f9d53..95169aef0c 100644 --- a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs @@ -130,30 +130,6 @@ namespace MediaBrowser.Providers.Music } } - /// - /// Determines whether the specified text has diacritics. - /// - /// The text. - /// true if the specified text has diacritics; otherwise, false. - private bool HasDiacritics(string text) - { - return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal); - } - - /// - /// Removes the diacritics. - /// - /// The text. - /// System.String. - private string RemoveDiacritics(string text) - { - return String.Concat( - text.Normalize(NormalizationForm.FormD) - .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != - UnicodeCategory.NonSpacingMark) - ).Normalize(NormalizationForm.FormC); - } - /// /// Encodes an URL. /// diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 408d8c9b1f..c6c1ec050d 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -218,36 +218,36 @@ namespace MediaBrowser.Server.Implementations.Drawing { var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed.HasValue; - if (!hasPostProcessing) - { - using (var outputStream = await _mediaEncoder.EncodeImage(new ImageEncodingOptions - { - InputPath = originalImagePath, - MaxHeight = options.MaxHeight, - MaxWidth = options.MaxWidth, - Height = options.Height, - Width = options.Width, - Quality = options.Quality, - Format = options.OutputFormat == ImageOutputFormat.Original ? Path.GetExtension(originalImagePath).TrimStart('.') : options.OutputFormat.ToString().ToLower() - - }, CancellationToken.None).ConfigureAwait(false)) - { - using (var outputMemoryStream = new MemoryStream()) - { - // Save to the memory stream - await outputStream.CopyToAsync(outputMemoryStream).ConfigureAwait(false); - - var bytes = outputMemoryStream.ToArray(); - - await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - - // kick off a task to cache the result - await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false); - } - - return; - } - } + //if (!hasPostProcessing) + //{ + // using (var outputStream = await _mediaEncoder.EncodeImage(new ImageEncodingOptions + // { + // InputPath = originalImagePath, + // MaxHeight = options.MaxHeight, + // MaxWidth = options.MaxWidth, + // Height = options.Height, + // Width = options.Width, + // Quality = options.Quality, + // Format = options.OutputFormat == ImageOutputFormat.Original ? Path.GetExtension(originalImagePath).TrimStart('.') : options.OutputFormat.ToString().ToLower() + + // }, CancellationToken.None).ConfigureAwait(false)) + // { + // using (var outputMemoryStream = new MemoryStream()) + // { + // // Save to the memory stream + // await outputStream.CopyToAsync(outputMemoryStream).ConfigureAwait(false); + + // var bytes = outputMemoryStream.ToArray(); + + // await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + + // // kick off a task to cache the result + // await CacheResizedImage(cacheFilePath, bytes).ConfigureAwait(false); + // } + + // return; + // } + //} using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) { From c3577838762f390d930b4043118ead2c64bad1b6 Mon Sep 17 00:00:00 2001 From: 7illusions Date: Fri, 28 Mar 2014 17:53:45 +0100 Subject: [PATCH 21/24] DLNA Albumart exclusion --- MediaBrowser.Dlna/PlayTo/DidlBuilder.cs | 8 ++++---- MediaBrowser.Dlna/PlayTo/DlnaController.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs b/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs index 1327da1480..80235740fb 100644 --- a/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs +++ b/MediaBrowser.Dlna/PlayTo/DidlBuilder.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Dlna.PlayTo /// The stream URL. /// The streams. /// System.String. - public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable streams) + public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable streams, bool includeImageRes) { string response = string.Format(DIDL_START, dto.Id, userId); response += string.Format(DIDL_TITLE, dto.Name.Replace("&", "and")); @@ -51,7 +51,7 @@ namespace MediaBrowser.Dlna.PlayTo var imageUrl = GetImageUrl(dto, serverAddress); - if (!string.IsNullOrEmpty(imageUrl)) + if (!string.IsNullOrWhiteSpace(imageUrl)) { response += string.Format(DIDL_IMAGE, imageUrl); } @@ -65,7 +65,7 @@ namespace MediaBrowser.Dlna.PlayTo response += string.Format(DESCRIPTION, UNKNOWN); response += GetVideoDIDL(dto, streamUrl, streams); - if (!string.IsNullOrEmpty(imageUrl)) + if (includeImageRes && !string.IsNullOrWhiteSpace(imageUrl)) { response += string.Format(DIDL_IMAGE_RES, imageUrl); } @@ -84,7 +84,7 @@ namespace MediaBrowser.Dlna.PlayTo response += GetAudioDIDL(dto, streamUrl, streams); - if (!string.IsNullOrEmpty(imageUrl)) + if (includeImageRes && !string.IsNullOrWhiteSpace(imageUrl)) { response += string.Format(DIDL_ALBUMIMAGE_RES, imageUrl); } diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs index e4bd8819da..e99c7b50ed 100644 --- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs +++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs @@ -435,7 +435,7 @@ namespace MediaBrowser.Dlna.PlayTo playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress); } - playlistItem.Didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams); + playlistItem.Didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams, profile.EnableAlbumArtInDidl); return playlistItem; } From 934b3e668cdad0ad29f7d6a3f613fc4d687c6743 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 28 Mar 2014 14:17:18 -0400 Subject: [PATCH 22/24] add more content to dlna profile editing page --- .../MediaBrowser.Common.Implementations.csproj | 1 - MediaBrowser.Controller/Dlna/CodecProfile.cs | 6 +++--- MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs | 12 ++++++------ MediaBrowser.Dlna/Profiles/LgTvProfile.cs | 6 +++--- MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs | 2 +- MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs | 10 +++++----- .../Profiles/SonyBlurayPlayer2013Profile.cs | 4 ++-- .../Profiles/SonyBlurayPlayerProfile.cs | 6 +++--- MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs | 10 +++++----- MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs | 10 +++++----- MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs | 4 ++-- MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs | 2 +- MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs | 10 +++++----- MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs | 4 ++-- MediaBrowser.Dlna/Profiles/Xbox360Profile.cs | 10 +++++----- MediaBrowser.Dlna/Profiles/Xml/Default.xml | 1 + MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml | 1 + MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml | 6 +++--- MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml | 1 + MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml | 10 +++++----- .../Profiles/Xml/Sony Blu-ray Player 2013.xml | 4 ++-- .../Profiles/Xml/Sony Blu-ray Player.xml | 6 +++--- .../Profiles/Xml/Sony Bravia (2010).xml | 10 +++++----- .../Profiles/Xml/Sony Bravia (2011).xml | 10 +++++----- .../Profiles/Xml/Sony Bravia (2012).xml | 4 ++-- .../Profiles/Xml/Sony Bravia (2013).xml | 2 +- .../Profiles/Xml/Sony PlayStation 3.xml | 10 +++++----- MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml | 4 ++-- MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml | 10 +++++----- MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml | 1 + MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml | 1 + MediaBrowser.ServerApplication/MainStartup.cs | 5 ++--- .../MediaBrowser.ServerApplication.csproj | 1 + .../Updates/ApplicationUpdater.cs | 12 +++--------- 35 files changed, 98 insertions(+), 100 deletions(-) rename {MediaBrowser.Common.Implementations => MediaBrowser.ServerApplication}/Updates/ApplicationUpdater.cs (86%) diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index 04b8865c1d..f6f800f4af 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -102,7 +102,6 @@ - diff --git a/MediaBrowser.Controller/Dlna/CodecProfile.cs b/MediaBrowser.Controller/Dlna/CodecProfile.cs index 75f80ed3b1..e2eb60d9a1 100644 --- a/MediaBrowser.Controller/Dlna/CodecProfile.cs +++ b/MediaBrowser.Controller/Dlna/CodecProfile.cs @@ -35,9 +35,9 @@ namespace MediaBrowser.Controller.Dlna public enum CodecType { - VideoCodec = 0, - VideoAudioCodec = 1, - AudioCodec = 2 + Video = 0, + VideoAudio = 1, + Audio = 2 } public class ProfileCondition diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs index f79dc1e5fc..c14a851cab 100644 --- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs +++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs @@ -32,7 +32,7 @@ namespace MediaBrowser.Dlna.PlayTo var audioCodec = audioStream == null ? null : audioStream.Codec; // Make sure audio codec profiles are satisfied - if (!string.IsNullOrEmpty(audioCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.AudioCodec && i.ContainsCodec(audioCodec)) + if (!string.IsNullOrEmpty(audioCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.Audio && i.ContainsCodec(audioCodec)) .All(i => AreConditionsSatisfied(i.Conditions, item.Path, null, audioStream))) { playlistItem.Transcode = false; @@ -53,7 +53,7 @@ namespace MediaBrowser.Dlna.PlayTo playlistItem.AudioCodec = transcodingProfile.AudioCodec; var audioTranscodingConditions = profile.CodecProfiles - .Where(i => i.Type == CodecType.AudioCodec && i.ContainsCodec(transcodingProfile.AudioCodec)) + .Where(i => i.Type == CodecType.Audio && i.ContainsCodec(transcodingProfile.AudioCodec)) .Take(1) .SelectMany(i => i.Conditions); @@ -114,13 +114,13 @@ namespace MediaBrowser.Dlna.PlayTo var videoCodec = videoStream == null ? null : videoStream.Codec; // Make sure video codec profiles are satisfied - if (!string.IsNullOrEmpty(videoCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.VideoCodec && i.ContainsCodec(videoCodec)) + if (!string.IsNullOrEmpty(videoCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.Video && i.ContainsCodec(videoCodec)) .All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream))) { var audioCodec = audioStream == null ? null : audioStream.Codec; // Make sure audio codec profiles are satisfied - if (string.IsNullOrEmpty(audioCodec) || profile.CodecProfiles.Where(i => i.Type == CodecType.VideoAudioCodec && i.ContainsCodec(audioCodec)) + if (string.IsNullOrEmpty(audioCodec) || profile.CodecProfiles.Where(i => i.Type == CodecType.VideoAudio && i.ContainsCodec(audioCodec)) .All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream))) { playlistItem.Transcode = false; @@ -143,14 +143,14 @@ namespace MediaBrowser.Dlna.PlayTo playlistItem.VideoCodec = transcodingProfile.VideoCodec; var videoTranscodingConditions = profile.CodecProfiles - .Where(i => i.Type == CodecType.VideoCodec && i.ContainsCodec(transcodingProfile.VideoCodec)) + .Where(i => i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec)) .Take(1) .SelectMany(i => i.Conditions); ApplyTranscodingConditions(playlistItem, videoTranscodingConditions); var audioTranscodingConditions = profile.CodecProfiles - .Where(i => i.Type == CodecType.VideoAudioCodec && i.ContainsCodec(transcodingProfile.AudioCodec)) + .Where(i => i.Type == CodecType.VideoAudio && i.ContainsCodec(transcodingProfile.AudioCodec)) .Take(1) .SelectMany(i => i.Conditions); diff --git a/MediaBrowser.Dlna/Profiles/LgTvProfile.cs b/MediaBrowser.Dlna/Profiles/LgTvProfile.cs index 7ca8069dac..ccf36e8447 100644 --- a/MediaBrowser.Dlna/Profiles/LgTvProfile.cs +++ b/MediaBrowser.Dlna/Profiles/LgTvProfile.cs @@ -113,7 +113,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "mpeg4", Conditions = new[] @@ -141,7 +141,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new[] @@ -175,7 +175,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3,aac,mp3", Conditions = new[] diff --git a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs b/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs index 64747776c6..ced9a7c612 100644 --- a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs +++ b/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs @@ -157,7 +157,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Conditions = new[] { diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs index 259d8e9ffb..122bde875f 100644 --- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs @@ -145,7 +145,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "mpeg2video", Conditions = new[] @@ -179,7 +179,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "mpeg4", Conditions = new[] @@ -213,7 +213,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new[] @@ -253,7 +253,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "wmv2,wmv3,vc1", Conditions = new[] @@ -287,7 +287,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3,wmav2,dca,aac,mp3", Conditions = new[] diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs index 04309d55f7..b64d4f6caf 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs @@ -113,7 +113,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new [] { @@ -141,7 +141,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3", Conditions = new [] { diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs index d9dfc1caf8..c5025edbb6 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs @@ -109,7 +109,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new [] { @@ -151,7 +151,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3", Conditions = new [] { @@ -167,7 +167,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "aac", Conditions = new [] { diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs index cb91de4a09..8f29ad76e3 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs @@ -168,7 +168,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Conditions = new [] { new ProfileCondition @@ -188,7 +188,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new [] { @@ -215,7 +215,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "mpeg2video", Conditions = new [] { @@ -236,7 +236,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3", Conditions = new [] @@ -252,7 +252,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "aac", Conditions = new [] diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs index 626a91a73c..eaf6979a6b 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs @@ -186,7 +186,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Conditions = new [] { new ProfileCondition @@ -206,7 +206,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new [] { @@ -233,7 +233,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "mpeg2video", Conditions = new [] { @@ -254,7 +254,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3", Conditions = new [] @@ -270,7 +270,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "aac", Conditions = new[] diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs index 82b6b6d5f9..41d057ef8a 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs @@ -198,7 +198,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Conditions = new[] { new ProfileCondition @@ -218,7 +218,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3", Conditions = new[] diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs index aec65e98ec..386a36ae0c 100644 --- a/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs @@ -231,7 +231,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Conditions = new [] { diff --git a/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs b/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs index 7502201eac..351a13f001 100644 --- a/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs +++ b/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs @@ -85,7 +85,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new [] @@ -128,7 +128,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3", Conditions = new [] @@ -153,7 +153,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "wmapro", Conditions = new [] @@ -169,7 +169,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "aac", Conditions = new [] @@ -186,7 +186,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "aac", Conditions = new [] diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs index af5d9b2956..f0b95d4e88 100644 --- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs @@ -195,7 +195,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new [] @@ -223,7 +223,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "aac", Conditions = new [] diff --git a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs index ed9edeb27a..38d08adef7 100644 --- a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs +++ b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs @@ -165,7 +165,7 @@ namespace MediaBrowser.Dlna.Profiles { new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "mpeg4", Conditions = new [] { @@ -200,7 +200,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "h264", Conditions = new [] { @@ -235,7 +235,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoCodec, + Type = CodecType.Video, Codec = "wmv2,wmv3,vc1", Conditions = new [] { @@ -270,7 +270,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "ac3,wmav2,wmapro", Conditions = new [] { @@ -286,7 +286,7 @@ namespace MediaBrowser.Dlna.Profiles new CodecProfile { - Type = CodecType.VideoAudioCodec, + Type = CodecType.VideoAudio, Codec = "aac", Conditions = new [] { diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml index 60f22a64a9..895cb99d32 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml @@ -30,5 +30,6 @@ + \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml index 28bb3289e2..58c5cefbc5 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml @@ -34,5 +34,6 @@ + \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml index cc8b40430d..53781ad324 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml @@ -48,14 +48,14 @@ - + - + @@ -63,7 +63,7 @@ - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml index 477fee2dde..fc833b9186 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -34,5 +34,6 @@ + \ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml index 71e240313b..49fd05b1e0 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml @@ -54,7 +54,7 @@ - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml index 77648147f5..75c50aae36 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml @@ -52,7 +52,7 @@ - + @@ -60,7 +60,7 @@ - + @@ -68,7 +68,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -85,7 +85,7 @@ - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml index 9428122954..5bd27c7716 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml @@ -52,14 +52,14 @@ - + - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml index 5193045768..f5502ca14a 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml @@ -51,7 +51,7 @@ - + @@ -60,12 +60,12 @@ - + - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml index d240e1d348..1337b59361 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml @@ -49,31 +49,31 @@ - + - + - + - + - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml index 2372aa5ad9..b022c10a51 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml @@ -52,31 +52,31 @@ - + - + - + - + - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml index 7edf09134c..cbef70b378 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml @@ -54,13 +54,13 @@ - + - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml index ec624cd8ec..47db46ce11 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml @@ -59,7 +59,7 @@ - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml index eeaf2d8193..d9aa441bb4 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml @@ -48,7 +48,7 @@ - + @@ -57,23 +57,23 @@ - + - + - + - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index ffa39c26d1..0f4ad54a77 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -59,14 +59,14 @@ - + - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml index ea25391976..1e8d8164c3 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml @@ -59,7 +59,7 @@ - + @@ -67,7 +67,7 @@ - + @@ -75,7 +75,7 @@ - + @@ -83,12 +83,12 @@ - + - + diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml index ef4fec99ac..f6c338b2f5 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml @@ -32,6 +32,7 @@ + diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml index 3e6623ac48..f682e4c421 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml @@ -36,5 +36,6 @@ + \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 6afe9367e5..3d490a1f8c 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -1,12 +1,11 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Constants; using MediaBrowser.Common.Implementations.Logging; -using MediaBrowser.Common.Implementations.Updates; -using MediaBrowser.Controller; using MediaBrowser.Model.Logging; using MediaBrowser.Server.Implementations; using MediaBrowser.ServerApplication.Native; using MediaBrowser.ServerApplication.Splash; +using MediaBrowser.ServerApplication.Updates; using Microsoft.Win32; using System; using System.Configuration.Install; @@ -490,7 +489,7 @@ namespace MediaBrowser.ServerApplication try { var serviceName = _isRunningAsService ? BackgroundService.Name : string.Empty; - new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive, logger, serviceName); + new ApplicationUpdater().UpdateApplication(appPaths, updateArchive, logger, serviceName); // And just let the app exit so it can update return true; diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index d999841ca7..b49e100ab2 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -145,6 +145,7 @@ SplashForm.cs + diff --git a/MediaBrowser.Common.Implementations/Updates/ApplicationUpdater.cs b/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs similarity index 86% rename from MediaBrowser.Common.Implementations/Updates/ApplicationUpdater.cs rename to MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs index e24ff30645..9f3e44cb0f 100644 --- a/MediaBrowser.Common.Implementations/Updates/ApplicationUpdater.cs +++ b/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs @@ -3,14 +3,8 @@ using MediaBrowser.Model.Logging; using System.Diagnostics; using System.IO; -namespace MediaBrowser.Common.Implementations.Updates +namespace MediaBrowser.ServerApplication.Updates { - public enum MBApplication - { - MBServer, - MBTheater - } - /// /// Update the specified application using the specified archive /// @@ -18,7 +12,7 @@ namespace MediaBrowser.Common.Implementations.Updates { private const string UpdaterExe = "Mediabrowser.Updater.exe"; private const string UpdaterDll = "Mediabrowser.InstallUtil.dll"; - public void UpdateApplication(MBApplication app, IApplicationPaths appPaths, string archive, ILogger logger, string restartServiceName) + public void UpdateApplication(IApplicationPaths appPaths, string archive, ILogger logger, string restartServiceName) { // First see if there is a version file and read that in var version = "Unknown"; @@ -39,7 +33,7 @@ namespace MediaBrowser.Common.Implementations.Updates logger.Info("Copying updater dependencies to temporary location"); File.Copy(source, tempUpdaterDll, true); - var product = app == MBApplication.MBTheater ? "mbt" : "server"; + const string product = "server"; // Our updater needs SS and ionic source = Path.Combine(appPaths.ProgramSystemPath, "ServiceStack.Text.dll"); File.Copy(source, Path.Combine(Path.GetTempPath(), "ServiceStack.Text.dll"), true); From 57623886b25cd2104f326d5974ff8375ba4964f6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 28 Mar 2014 15:58:18 -0400 Subject: [PATCH 23/24] added generic remote control commands --- MediaBrowser.Api/DisplayPreferencesService.cs | 6 +-- MediaBrowser.Api/EnvironmentService.cs | 15 +++---- MediaBrowser.Api/GamesService.cs | 15 +++---- .../Playback/Progressive/AudioService.cs | 36 ++++++++-------- MediaBrowser.Api/UserLibrary/ItemsService.cs | 5 +-- MediaBrowser.Controller/Dlna/DeviceProfile.cs | 1 - .../Dlna/TranscodingProfile.cs | 3 ++ .../Session/ISessionController.cs | 8 ++++ MediaBrowser.Dlna/PlayTo/DlnaController.cs | 5 +++ .../MediaBrowser.Model.Portable.csproj | 3 ++ .../MediaBrowser.Model.net35.csproj | 3 ++ MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + MediaBrowser.Model/Session/GenericCommand.cs | 43 +++++++++++++++++++ .../Session/PlaystateCommand.cs | 10 ++++- .../Roku/RokuSessionController.cs | 11 +++++ .../Session/WebSocketController.cs | 12 ++++++ 16 files changed, 131 insertions(+), 46 deletions(-) create mode 100644 MediaBrowser.Model/Session/GenericCommand.cs diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs index f22dc9e39e..4060b42b49 100644 --- a/MediaBrowser.Api/DisplayPreferencesService.cs +++ b/MediaBrowser.Api/DisplayPreferencesService.cs @@ -12,8 +12,7 @@ namespace MediaBrowser.Api /// /// Class UpdateDisplayPreferences /// - [Route("/DisplayPreferences/{DisplayPreferencesId}", "POST")] - [Api(("Updates a user's display preferences for an item"))] + [Route("/DisplayPreferences/{DisplayPreferencesId}", "POST", Summary = "Updates a user's display preferences for an item")] public class UpdateDisplayPreferences : DisplayPreferences, IReturnVoid { /// @@ -30,8 +29,7 @@ namespace MediaBrowser.Api public string Client { get; set; } } - [Route("/DisplayPreferences/{Id}", "GET")] - [Api(("Gets a user's display preferences for an item"))] + [Route("/DisplayPreferences/{Id}", "GET", Summary = "Gets a user's display preferences for an item")] public class GetDisplayPreferences : IReturn { /// diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index cb104072bd..56f71fc00e 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -13,8 +13,7 @@ namespace MediaBrowser.Api /// /// Class GetDirectoryContents /// - [Route("/Environment/DirectoryContents", "GET")] - [Api(Description = "Gets the contents of a given directory in the file system")] + [Route("/Environment/DirectoryContents", "GET", Summary = "Gets the contents of a given directory in the file system")] public class GetDirectoryContents : IReturn> { /// @@ -46,8 +45,7 @@ namespace MediaBrowser.Api public bool IncludeHidden { get; set; } } - [Route("/Environment/NetworkShares", "GET")] - [Api(Description = "Gets shares from a network device")] + [Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")] public class GetNetworkShares : IReturn> { /// @@ -61,8 +59,7 @@ namespace MediaBrowser.Api /// /// Class GetDrives /// - [Route("/Environment/Drives", "GET")] - [Api(Description = "Gets available drives from the server's file system")] + [Route("/Environment/Drives", "GET", Summary = "Gets available drives from the server's file system")] public class GetDrives : IReturn> { } @@ -70,14 +67,12 @@ namespace MediaBrowser.Api /// /// Class GetNetworkComputers /// - [Route("/Environment/NetworkDevices", "GET")] - [Api(Description = "Gets a list of devices on the network")] + [Route("/Environment/NetworkDevices", "GET", Summary = "Gets a list of devices on the network")] public class GetNetworkDevices : IReturn> { } - [Route("/Environment/ParentPath", "GET")] - [Api(Description = "Gets the parent path of a given path")] + [Route("/Environment/ParentPath", "GET", Summary = "Gets the parent path of a given path")] public class GetParentPath : IReturn { /// diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs index e371791f81..ff2771ce1b 100644 --- a/MediaBrowser.Api/GamesService.cs +++ b/MediaBrowser.Api/GamesService.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; @@ -7,6 +6,7 @@ using MediaBrowser.Model.Dto; using ServiceStack; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; @@ -15,8 +15,7 @@ namespace MediaBrowser.Api /// /// Class GetSimilarGames /// - [Route("/Games/{Id}/Similar", "GET")] - [Api(Description = "Finds games similar to a given game.")] + [Route("/Games/{Id}/Similar", "GET", Summary = "Finds games similar to a given game.")] public class GetSimilarGames : BaseGetSimilarItemsFromItem { } @@ -24,8 +23,7 @@ namespace MediaBrowser.Api /// /// Class GetGameSystemSummaries /// - [Route("/Games/SystemSummaries", "GET")] - [Api(Description = "Finds games similar to a given game.")] + [Route("/Games/SystemSummaries", "GET", Summary = "Finds games similar to a given game.")] public class GetGameSystemSummaries : IReturn> { /// @@ -39,8 +37,7 @@ namespace MediaBrowser.Api /// /// Class GetGameSystemSummaries /// - [Route("/Games/PlayerIndex", "GET")] - [Api(Description = "Gets an index of players (1-x) and the number of games listed under each")] + [Route("/Games/PlayerIndex", "GET", Summary = "Gets an index of players (1-x) and the number of games listed under each")] public class GetPlayerIndex : IReturn> { /// @@ -117,7 +114,7 @@ namespace MediaBrowser.Api } private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - + public object Get(GetPlayerIndex request) { var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager) diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index ca206c0125..55b311f867 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -17,23 +17,22 @@ namespace MediaBrowser.Api.Playback.Progressive /// /// Class GetAudioStream /// - [Route("/Audio/{Id}/stream.mp3", "GET")] - [Route("/Audio/{Id}/stream.wma", "GET")] - [Route("/Audio/{Id}/stream.aac", "GET")] - [Route("/Audio/{Id}/stream.flac", "GET")] - [Route("/Audio/{Id}/stream.ogg", "GET")] - [Route("/Audio/{Id}/stream.oga", "GET")] - [Route("/Audio/{Id}/stream.webm", "GET")] - [Route("/Audio/{Id}/stream", "GET")] - [Route("/Audio/{Id}/stream.mp3", "HEAD")] - [Route("/Audio/{Id}/stream.wma", "HEAD")] - [Route("/Audio/{Id}/stream.aac", "HEAD")] - [Route("/Audio/{Id}/stream.flac", "HEAD")] - [Route("/Audio/{Id}/stream.ogg", "HEAD")] - [Route("/Audio/{Id}/stream.oga", "HEAD")] - [Route("/Audio/{Id}/stream.webm", "HEAD")] - [Route("/Audio/{Id}/stream", "HEAD")] - [Api(Description = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.mp3", "GET", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.wma", "GET", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.aac", "GET", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.flac", "GET", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.ogg", "GET", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.oga", "GET", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.webm", "GET", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream", "GET", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.mp3", "HEAD", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.wma", "HEAD", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.aac", "HEAD", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.flac", "HEAD", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.ogg", "HEAD", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.oga", "HEAD", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream.webm", "HEAD", Summary = "Gets an audio stream")] + [Route("/Audio/{Id}/stream", "HEAD", Summary = "Gets an audio stream")] public class GetAudioStream : StreamRequest { @@ -44,7 +43,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// public class AudioService : BaseProgressiveStreamingService { - public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor) + public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) + : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor) { } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index a787684bbf..c5d0a621d4 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -20,9 +20,8 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetItems /// - [Route("/Items", "GET")] - [Route("/Users/{UserId}/Items", "GET")] - [Api(Description = "Gets items based on a query.")] + [Route("/Items", "GET", Summary = "Gets items based on a query.")] + [Route("/Users/{UserId}/Items", "GET", Summary = "Gets items based on a query.")] public class GetItems : BaseItemsRequest, IReturn { /// diff --git a/MediaBrowser.Controller/Dlna/DeviceProfile.cs b/MediaBrowser.Controller/Dlna/DeviceProfile.cs index 5950698fb7..c1fc713e4a 100644 --- a/MediaBrowser.Controller/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Controller/Dlna/DeviceProfile.cs @@ -3,7 +3,6 @@ using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; using System.Xml.Serialization; namespace MediaBrowser.Controller.Dlna diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs index 289333aa77..707f0c5731 100644 --- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs @@ -18,6 +18,9 @@ namespace MediaBrowser.Controller.Dlna [XmlAttribute("audioCodec")] public string AudioCodec { get; set; } + [XmlAttribute("protocol")] + public string Protocol { get; set; } + [XmlAttribute("estimateContentLength")] public bool EstimateContentLength { get; set; } diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index 21206af757..02cc875bd0 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -59,6 +59,14 @@ namespace MediaBrowser.Controller.Session /// Task. Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken); + /// + /// Sends the generic command. + /// + /// The command. + /// The cancellation token. + /// Task. + Task SendGenericCommand(GenericCommand command, CancellationToken cancellationToken); + /// /// Sends the library update info. /// diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs index e99c7b50ed..0c9f292ad8 100644 --- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs +++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs @@ -619,5 +619,10 @@ namespace MediaBrowser.Dlna.PlayTo _logger.Log(LogSeverity.Debug, "Controller disposed"); } } + + public Task SendGenericCommand(GenericCommand command, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } } } diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 9aefb4f1c8..6c4d9d9e29 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -428,6 +428,9 @@ Session\BrowseRequest.cs + + Session\GenericCommand.cs + Session\MessageCommand.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index ce2a7600f0..b39cecc61d 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -415,6 +415,9 @@ Session\BrowseRequest.cs + + Session\GenericCommand.cs + Session\MessageCommand.cs diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index bf29b4bff0..207543fe88 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -132,6 +132,7 @@ + diff --git a/MediaBrowser.Model/Session/GenericCommand.cs b/MediaBrowser.Model/Session/GenericCommand.cs new file mode 100644 index 0000000000..3d5e503eff --- /dev/null +++ b/MediaBrowser.Model/Session/GenericCommand.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Session +{ + public class GenericCommand + { + public string Name { get; set; } + + public Dictionary Arguments { get; set; } + + public GenericCommand() + { + Arguments = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } + + public enum CoreGenericCommand + { + MoveUp = 0, + MoveDown = 1, + MoveLeft = 2, + MoveRight = 3, + PageUp = 4, + PageDown = 5, + PreviousLetter = 6, + NextLetter = 7, + ToggleOsd = 8, + ToggleContextMenu = 9, + Select = 10, + Back = 11, + TakeScreenshot = 12, + SendKey = 13, + SendString = 14, + GoHome = 15, + GoToSettings = 16, + VolumeUp = 17, + VolumeDown = 18, + Mute = 19, + Unmute = 20, + ToggleMute = 21 + } +} diff --git a/MediaBrowser.Model/Session/PlaystateCommand.cs b/MediaBrowser.Model/Session/PlaystateCommand.cs index d83c6dae54..91572ba623 100644 --- a/MediaBrowser.Model/Session/PlaystateCommand.cs +++ b/MediaBrowser.Model/Session/PlaystateCommand.cs @@ -33,7 +33,15 @@ namespace MediaBrowser.Model.Session /// /// The fullscreen /// - Fullscreen + Fullscreen, + /// + /// The rewind + /// + Rewind, + /// + /// The fast forward + /// + FastForward } public class PlaystateRequest diff --git a/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs b/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs index 0e2f9e1b57..d806db1e0b 100644 --- a/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs +++ b/MediaBrowser.Server.Implementations/Roku/RokuSessionController.cs @@ -146,5 +146,16 @@ namespace MediaBrowser.Server.Implementations.Roku RequestContentType = "application/json" }); } + + + public Task SendGenericCommand(GenericCommand command, CancellationToken cancellationToken) + { + return SendCommand(new WebSocketMessage + { + MessageType = "Command", + Data = command + + }, cancellationToken); + } } } diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs index 70d7ac071f..ddf4ec2ca7 100644 --- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs +++ b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs @@ -198,5 +198,17 @@ namespace MediaBrowser.Server.Implementations.Session }, cancellationToken); } + + public Task SendGenericCommand(GenericCommand command, CancellationToken cancellationToken) + { + var socket = GetActiveSocket(); + + return socket.SendAsync(new WebSocketMessage + { + MessageType = "Command", + Data = command + + }, cancellationToken); + } } } From 5e5b1f180c2a13152c8f318f045547c6508c5b3e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 28 Mar 2014 16:36:29 -0400 Subject: [PATCH 24/24] updated nuget --- MediaBrowser.Model/ApiClient/IApiClient.cs | 8 ++++++++ MediaBrowser.Model/Session/GenericCommand.cs | 5 +++++ Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 8de54f34a9..28c5822e9a 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -589,6 +589,14 @@ namespace MediaBrowser.Model.ApiClient /// request Task SendPlayCommandAsync(string sessionId, PlayRequest request); + /// + /// Sends the command asynchronous. + /// + /// The session identifier. + /// The request. + /// Task. + Task SendCommandAsync(string sessionId, GenericCommand request); + /// /// Sends a system command to the client /// diff --git a/MediaBrowser.Model/Session/GenericCommand.cs b/MediaBrowser.Model/Session/GenericCommand.cs index 3d5e503eff..f7ea0a84a5 100644 --- a/MediaBrowser.Model/Session/GenericCommand.cs +++ b/MediaBrowser.Model/Session/GenericCommand.cs @@ -7,6 +7,8 @@ namespace MediaBrowser.Model.Session { public string Name { get; set; } + public string ControllingUserId { get; set; } + public Dictionary Arguments { get; set; } public GenericCommand() @@ -15,6 +17,9 @@ namespace MediaBrowser.Model.Session } } + /// + /// This exists simply to identify a set of known commands. + /// public enum CoreGenericCommand { MoveUp = 0, diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index c550b350c6..a3b4533dd9 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.345 + 3.0.346 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index a6fa5c152c..b80f673e38 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.345 + 3.0.346 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index cc87b00300..eccfcccd2a 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.345 + 3.0.346 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - +