From 3ccecd3ca3a1640f15ffae70914a8ad0f5a1cb99 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 18 May 2014 15:58:42 -0400 Subject: [PATCH] channel fixes --- .../Playback/BaseStreamingService.cs | 51 ++- .../Playback/Hls/BaseHlsService.cs | 4 +- .../Playback/Hls/DynamicHlsService.cs | 3 +- .../Playback/Hls/VideoHlsService.cs | 3 +- .../Playback/Progressive/AudioService.cs | 4 +- .../BaseProgressiveStreamingService.cs | 6 +- .../Playback/Progressive/VideoService.cs | 5 +- MediaBrowser.Api/Playback/StreamState.cs | 7 +- MediaBrowser.Api/SessionsService.cs | 7 +- .../Channels/ChannelAudioItem.cs | 26 +- .../Channels/ChannelCategoryItem.cs | 9 +- .../Channels/ChannelItemInfo.cs | 2 + .../Channels/ChannelMediaInfo.cs | 3 + .../Channels/ChannelVideoItem.cs | 21 ++ MediaBrowser.Controller/Channels/IChannel.cs | 11 + .../Channels/IChannelItem.cs | 2 +- .../Channels/IChannelManager.cs | 8 + .../Channels/IChannelMediaItem.cs | 6 +- .../Entities/Audio/Audio.cs | 4 +- .../Session/ISessionController.cs | 4 +- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 3 +- .../Encoder/AudioEncoder.cs | 91 ----- .../Encoder/EncodingUtils.cs | 114 ------- .../Encoder/FFMpegProcess.cs | 168 ---------- .../Encoder/ImageEncoder.cs | 235 ------------- .../Encoder/InternalEncodingTask.cs | 95 ------ .../Encoder/InternalEncodingTaskFactory.cs | 311 ------------------ .../Encoder/MediaEncoder.cs | 2 +- .../MediaBrowser.MediaEncoding.csproj | 5 - .../Session/SessionCapabilities.cs | 2 + .../Channels/ChannelManager.cs | 45 ++- .../Session/HttpSessionController.cs | 85 ++--- .../Session/SessionManager.cs | 32 +- .../Session/SessionWebSocketListener.cs | 8 +- .../Session/WebSocketController.cs | 10 +- .../ApplicationHost.cs | 2 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 39 files changed, 285 insertions(+), 1119 deletions(-) delete mode 100644 MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs delete mode 100644 MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs delete mode 100644 MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs delete mode 100644 MediaBrowser.MediaEncoding/Encoder/InternalEncodingTask.cs delete mode 100644 MediaBrowser.MediaEncoding/Encoder/InternalEncodingTaskFactory.cs diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index a6171ff2c3..8d07b10ce5 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; @@ -25,7 +26,6 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Api.Playback { @@ -71,6 +71,7 @@ namespace MediaBrowser.Api.Playback protected IItemRepository ItemRepository { get; private set; } protected ILiveTvManager LiveTvManager { get; private set; } protected IDlnaManager DlnaManager { get; private set; } + protected IChannelManager ChannelManager { get; private set; } /// /// Initializes a new instance of the class. @@ -83,8 +84,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, IDlnaManager dlnaManager) + protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) { + ChannelManager = channelManager; DlnaManager = dlnaManager; EncodingManager = encodingManager; LiveTvManager = liveTvManager; @@ -169,13 +171,28 @@ namespace MediaBrowser.Api.Playback /// System.String. protected virtual string GetMapArgs(StreamState state) { - var args = string.Empty; + // If we don't have known media info + // If input is video, use -sn to drop subtitles + // Otherwise just return empty + if (state.VideoStream == null && state.AudioStream == null) + { + return state.IsInputVideo ? "-sn" : string.Empty; + } - if (!state.HasMediaStreams) + // We have media info, but we don't know the stream indexes + if (state.VideoStream != null && state.VideoStream.Index == -1) + { + return "-sn"; + } + + // We have media info, but we don't know the stream indexes + if (state.AudioStream != null && state.AudioStream.Index == -1) { return state.IsInputVideo ? "-sn" : string.Empty; } + var args = string.Empty; + if (state.VideoStream != null) { args += string.Format("-map 0:{0}", state.VideoStream.Index); @@ -1329,13 +1346,14 @@ namespace MediaBrowser.Api.Playback throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name)); } + List mediaStreams = null; + if (item is ILiveTvRecording) { var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false); state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - state.PlayableStreamFileNames = new List(); var path = recording.RecordingInfo.Path; var mediaUrl = recording.RecordingInfo.Url; @@ -1345,6 +1363,7 @@ namespace MediaBrowser.Api.Playback var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false); state.LiveTvStreamId = streamInfo.Id; + mediaStreams = streamInfo.MediaStreams; path = streamInfo.Path; mediaUrl = streamInfo.Url; @@ -1381,11 +1400,11 @@ namespace MediaBrowser.Api.Playback state.VideoType = VideoType.VideoFile; state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - state.PlayableStreamFileNames = new List(); var streamInfo = await LiveTvManager.GetChannelStream(request.Id, cancellationToken).ConfigureAwait(false); state.LiveTvStreamId = streamInfo.Id; + mediaStreams = streamInfo.MediaStreams; if (!string.IsNullOrEmpty(streamInfo.Path)) { @@ -1406,6 +1425,16 @@ namespace MediaBrowser.Api.Playback state.InputVideoSync = "-1"; state.InputAudioSync = "1"; } + else if (item is IChannelMediaItem) + { + var channelMediaSources = await ChannelManager.GetChannelItemMediaSources(request.Id, CancellationToken.None).ConfigureAwait(false); + + var source = channelMediaSources.First(); + state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); + state.IsRemote = source.IsRemote; + state.MediaPath = source.Path; + state.RunTimeTicks = item.RunTimeTicks; + } else { state.MediaPath = item.Path; @@ -1424,7 +1453,11 @@ namespace MediaBrowser.Api.Playback : video.PlayableStreamFileNames.ToList(); state.DeInterlace = string.Equals(video.Container, "wtv", StringComparison.OrdinalIgnoreCase); - state.InputTimestamp = video.Timestamp ?? TransportStreamTimestamp.None; + + if (video.Timestamp.HasValue) + { + state.InputTimestamp = video.Timestamp.Value; + } state.InputContainer = video.Container; } @@ -1440,7 +1473,7 @@ namespace MediaBrowser.Api.Playback var videoRequest = request as VideoStreamRequest; - var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery + mediaStreams = mediaStreams ?? ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id @@ -1469,8 +1502,6 @@ namespace MediaBrowser.Api.Playback state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } - state.HasMediaStreams = mediaStreams.Count > 0; - state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10; state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440; diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 2852d2489f..553f023684 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; @@ -24,8 +25,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, IDlnaManager dlnaManager) - : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) + protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager) { } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index fd93ef6852..c5405a3b5d 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; @@ -59,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, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) + public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager) { } diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 77ac95815d..162ab741aa 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dto; @@ -53,7 +54,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, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) + public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager) { } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index d746b265df..2e002cc9be 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; @@ -43,8 +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, 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, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, imageProcessor, httpClient) { } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index f39169dbd8..06e8f7b2c9 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; @@ -26,11 +27,10 @@ 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, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) - : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager) + protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager) { - HttpClient = httpClient; ImageProcessor = imageProcessor; + HttpClient = httpClient; } /// diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index d7061ae754..29bc3f8978 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; @@ -59,7 +60,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, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, 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, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, imageProcessor, httpClient) { } @@ -183,7 +184,7 @@ namespace MediaBrowser.Api.Playback.Progressive private string GetAudioArguments(StreamState state) { // If the video doesn't have an audio stream, return a default. - if (state.AudioStream == null && state.HasMediaStreams) + if (state.AudioStream == null && state.VideoStream != null) { return string.Empty; } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index cc733dfaf3..f9e861e8d4 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -28,6 +28,11 @@ namespace MediaBrowser.Api.Playback get { return Request as VideoStreamRequest; } } + public StreamState() + { + PlayableStreamFileNames = new List(); + } + /// /// Gets or sets the log file stream. /// @@ -57,8 +62,6 @@ namespace MediaBrowser.Api.Playback public List PlayableStreamFileNames { get; set; } - public bool HasMediaStreams { get; set; } - public string LiveTvStreamId { get; set; } public int SegmentLength = 10; diff --git a/MediaBrowser.Api/SessionsService.cs b/MediaBrowser.Api/SessionsService.cs index 0e69c96d19..4cb48e9fe8 100644 --- a/MediaBrowser.Api/SessionsService.cs +++ b/MediaBrowser.Api/SessionsService.cs @@ -218,6 +218,9 @@ namespace MediaBrowser.Api [ApiMember(Name = "SupportedCommands", Description = "A list of supported remote control commands, comma delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public string SupportedCommands { get; set; } + [ApiMember(Name = "MessageCallbackUrl", Description = "A url to post messages to, including remote control commands.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string MessageCallbackUrl { get; set; } + [ApiMember(Name = "SupportsMediaControl", Description = "Determines whether media can be played remotely.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool SupportsMediaControl { get; set; } } @@ -414,7 +417,9 @@ namespace MediaBrowser.Api SupportedCommands = request.SupportedCommands.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(), - SupportsMediaControl = request.SupportsMediaControl + SupportsMediaControl = request.SupportsMediaControl, + + MessageCallbackUrl = request.MessageCallbackUrl }); } diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs index 72a996b193..6d32f7d356 100644 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs @@ -1,6 +1,8 @@ -using System.Linq; -using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using System.Collections.Generic; +using System.Linq; namespace MediaBrowser.Controller.Channels { @@ -18,6 +20,8 @@ namespace MediaBrowser.Controller.Channels public string OriginalImageUrl { get; set; } + public List ChannelMediaSources { get; set; } + protected override bool GetBlockUnratedValue(UserConfiguration config) { return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); @@ -30,5 +34,23 @@ namespace MediaBrowser.Controller.Channels return false; } } + + public ChannelAudioItem() + { + ChannelMediaSources = new List(); + } + + public override LocationType LocationType + { + get + { + if (string.IsNullOrEmpty(Path)) + { + return LocationType.Remote; + } + + return base.LocationType; + } + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelCategoryItem.cs b/MediaBrowser.Controller/Channels/ChannelCategoryItem.cs index b20dcf6204..c18f748563 100644 --- a/MediaBrowser.Controller/Channels/ChannelCategoryItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelCategoryItem.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; +using System.Collections.Generic; namespace MediaBrowser.Controller.Channels { @@ -8,10 +9,11 @@ namespace MediaBrowser.Controller.Channels public string ExternalId { get; set; } public string ChannelId { get; set; } - + public ChannelItemType ChannelItemType { get; set; } public string OriginalImageUrl { get; set; } + public List Tags { get; set; } protected override bool GetBlockUnratedValue(UserConfiguration config) { @@ -26,5 +28,10 @@ namespace MediaBrowser.Controller.Channels return false; } } + + public ChannelCategoryItem() + { + Tags = new List(); + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs index 7bb8d15fc3..948754e49e 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs @@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.Channels public List Genres { get; set; } public List Studios { get; set; } + public List Tags { get; set; } public List People { get; set; } @@ -49,6 +50,7 @@ namespace MediaBrowser.Controller.Channels Genres = new List(); Studios = new List(); People = new List(); + Tags = new List(); ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); } } diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs index 8105bf43cd..3630342679 100644 --- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs @@ -18,9 +18,12 @@ namespace MediaBrowser.Controller.Channels public int? Height { get; set; } public int? AudioChannels { get; set; } + public bool IsRemote { get; set; } + public ChannelMediaInfo() { RequiredHttpHeaders = new Dictionary(); + IsRemote = true; } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index 0d2bd933be..01438bfad6 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; +using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -20,6 +21,8 @@ namespace MediaBrowser.Controller.Channels public string OriginalImageUrl { get; set; } + public List ChannelMediaSources { get; set; } + public override string GetUserDataKey() { if (ContentType == ChannelMediaContentType.Trailer) @@ -55,5 +58,23 @@ namespace MediaBrowser.Controller.Channels return false; } } + + public ChannelVideoItem() + { + ChannelMediaSources = new List(); + } + + public override LocationType LocationType + { + get + { + if (string.IsNullOrEmpty(Path)) + { + return LocationType.Remote; + } + + return base.LocationType; + } + } } } diff --git a/MediaBrowser.Controller/Channels/IChannel.cs b/MediaBrowser.Controller/Channels/IChannel.cs index bd0bd64ea6..8a21179b11 100644 --- a/MediaBrowser.Controller/Channels/IChannel.cs +++ b/MediaBrowser.Controller/Channels/IChannel.cs @@ -59,4 +59,15 @@ namespace MediaBrowser.Controller.Channels /// IEnumerable{ImageType}. IEnumerable GetSupportedChannelImages(); } + + public interface IRequiresMediaInfoCallback + { + /// + /// Gets the channel item media information. + /// + /// The identifier. + /// The cancellation token. + /// Task{IEnumerable{ChannelMediaInfo}}. + Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken); + } } diff --git a/MediaBrowser.Controller/Channels/IChannelItem.cs b/MediaBrowser.Controller/Channels/IChannelItem.cs index fc088b8886..8b97701138 100644 --- a/MediaBrowser.Controller/Channels/IChannelItem.cs +++ b/MediaBrowser.Controller/Channels/IChannelItem.cs @@ -2,7 +2,7 @@ namespace MediaBrowser.Controller.Channels { - public interface IChannelItem : IHasImages + public interface IChannelItem : IHasImages, IHasTags { string ChannelId { get; set; } diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 05f9afcf00..a47f6e6aee 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -31,5 +31,13 @@ namespace MediaBrowser.Controller.Channels /// The cancellation token. /// Task{QueryResult{BaseItemDto}}. Task> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken); + + /// + /// Gets the channel item media sources. + /// + /// The identifier. + /// The cancellation token. + /// Task{IEnumerable{ChannelMediaInfo}}. + Task> GetChannelItemMediaSources(string id, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Channels/IChannelMediaItem.cs b/MediaBrowser.Controller/Channels/IChannelMediaItem.cs index 3a2c076e0a..1e634027f0 100644 --- a/MediaBrowser.Controller/Channels/IChannelMediaItem.cs +++ b/MediaBrowser.Controller/Channels/IChannelMediaItem.cs @@ -1,9 +1,13 @@ -namespace MediaBrowser.Controller.Channels +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Channels { public interface IChannelMediaItem : IChannelItem { bool IsInfiniteStream { get; set; } ChannelMediaContentType ContentType { get; set; } + + List ChannelMediaSources { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 40b52b8861..dca645a750 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -10,16 +10,18 @@ namespace MediaBrowser.Controller.Entities.Audio /// /// Class Audio /// - public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo + public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo, IHasTags { public string FormatName { get; set; } public long? Size { get; set; } public string Container { get; set; } public int? TotalBitrate { get; set; } + public List Tags { get; set; } public Audio() { Artists = new List(); + Tags = new List(); } /// diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index 5f07b9ff80..d6dd7698e6 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -1,5 +1,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Session; +using MediaBrowser.Model.System; using System.Threading; using System.Threading.Tasks; @@ -54,9 +55,10 @@ namespace MediaBrowser.Controller.Session /// /// Sends the restart required message. /// + /// The information. /// The cancellation token. /// Task. - Task SendRestartRequiredNotification(CancellationToken cancellationToken); + Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken); /// /// Sends the user data change info. diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index 9312b84421..bcbd2a9c7c 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -19,6 +19,7 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.System; namespace MediaBrowser.Dlna.PlayTo { @@ -320,7 +321,7 @@ namespace MediaBrowser.Dlna.PlayTo return Task.FromResult(true); } - public Task SendRestartRequiredNotification(CancellationToken cancellationToken) + public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken) { return Task.FromResult(true); } diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs deleted file mode 100644 index 08b7fbe492..0000000000 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ /dev/null @@ -1,91 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using System.Collections.Generic; -using System.Globalization; -using System.Threading.Tasks; - -namespace MediaBrowser.MediaEncoding.Encoder -{ - public class AudioEncoder - { - private readonly string _ffmpegPath; - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - private readonly IApplicationPaths _appPaths; - private readonly IIsoManager _isoManager; - private readonly ILiveTvManager _liveTvManager; - - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - public AudioEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths, IIsoManager isoManager, ILiveTvManager liveTvManager) - { - _ffmpegPath = ffmpegPath; - _logger = logger; - _fileSystem = fileSystem; - _appPaths = appPaths; - _isoManager = isoManager; - _liveTvManager = liveTvManager; - } - - public Task BeginEncoding(InternalEncodingTask task) - { - return new FFMpegProcess(_ffmpegPath, _logger, _fileSystem, _appPaths, _isoManager, _liveTvManager).Start(task, GetArguments); - } - - private string GetArguments(InternalEncodingTask task, string mountedPath) - { - var options = task.Request; - - return string.Format("{0} -i {1} {2} -id3v2_version 3 -write_id3v1 1 \"{3}\"", - GetInputModifier(task), - GetInputArgument(task), - GetOutputModifier(task), - options.OutputPath).Trim(); - } - - private string GetInputModifier(InternalEncodingTask task) - { - return EncodingUtils.GetInputModifier(task); - } - - private string GetInputArgument(InternalEncodingTask task) - { - return EncodingUtils.GetInputArgument(new List { task.MediaPath }, task.IsInputRemote); - } - - private string GetOutputModifier(InternalEncodingTask task) - { - var options = task.Request; - - var audioTranscodeParams = new List - { - "-threads " + EncodingUtils.GetNumberOfThreads(task, false).ToString(_usCulture), - "-vn" - }; - - var bitrate = EncodingUtils.GetAudioBitrateParam(task); - - if (bitrate.HasValue) - { - audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(_usCulture)); - } - - var channels = EncodingUtils.GetNumAudioChannelsParam(options, task.AudioStream); - - if (channels.HasValue) - { - audioTranscodeParams.Add("-ac " + channels.Value); - } - - if (options.AudioSampleRate.HasValue) - { - audioTranscodeParams.Add("-ar " + options.AudioSampleRate.Value); - } - - return string.Join(" ", audioTranscodeParams.ToArray()); - } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index 2316518566..9bd2e92709 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -64,77 +64,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return string.Format("\"{0}\"", url); } - public static string GetAudioInputModifier(InternalEncodingTask options) - { - return GetCommonInputModifier(options); - } - - public static string GetInputModifier(InternalEncodingTask options) - { - var inputModifier = GetCommonInputModifier(options); - - //if (state.VideoRequest != null) - //{ - // inputModifier += " -fflags genpts"; - //} - - //if (!string.IsNullOrEmpty(state.InputVideoCodec)) - //{ - // inputModifier += " -vcodec " + state.InputVideoCodec; - //} - - //if (!string.IsNullOrEmpty(state.InputVideoSync)) - //{ - // inputModifier += " -vsync " + state.InputVideoSync; - //} - - return inputModifier; - } - - private static string GetCommonInputModifier(InternalEncodingTask options) - { - var inputModifier = string.Empty; - - if (options.EnableDebugLogging) - { - inputModifier += "-loglevel debug"; - } - - var probeSize = GetProbeSizeArgument(options.InputVideoType.HasValue && options.InputVideoType.Value == VideoType.Dvd); - inputModifier += " " + probeSize; - inputModifier = inputModifier.Trim(); - - if (!string.IsNullOrWhiteSpace(options.UserAgent)) - { - inputModifier += " -user-agent \"" + options.UserAgent + "\""; - } - - inputModifier += " " + GetFastSeekValue(options.Request); - inputModifier = inputModifier.Trim(); - - if (!string.IsNullOrEmpty(options.InputFormat)) - { - inputModifier += " -f " + options.InputFormat; - } - - if (!string.IsNullOrEmpty(options.InputAudioCodec)) - { - inputModifier += " -acodec " + options.InputAudioCodec; - } - - if (!string.IsNullOrEmpty(options.InputAudioSync)) - { - inputModifier += " -async " + options.InputAudioSync; - } - - if (options.ReadInputAtNativeFramerate) - { - inputModifier += " -re"; - } - - return inputModifier; - } - private static string GetFastSeekValue(EncodingOptions options) { var time = options.StartTimeTicks; @@ -157,19 +86,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return isDvd ? "-probesize 1G -analyzeduration 200M" : string.Empty; } - public static int? GetAudioBitrateParam(InternalEncodingTask task) - { - if (task.Request.AudioBitRate.HasValue) - { - // Make sure we don't request a bitrate higher than the source - var currentBitrate = task.AudioStream == null ? task.Request.AudioBitRate.Value : task.AudioStream.BitRate ?? task.Request.AudioBitRate.Value; - - return Math.Min(currentBitrate, task.Request.AudioBitRate.Value); - } - - return null; - } - /// /// Gets the number of audio channels to specify on the command line /// @@ -201,35 +117,5 @@ namespace MediaBrowser.MediaEncoding.Encoder return request.AudioChannels; } - - public static int GetNumberOfThreads(InternalEncodingTask state, bool isWebm) - { - // Use more when this is true. -re will keep cpu usage under control - if (state.ReadInputAtNativeFramerate) - { - if (isWebm) - { - return Math.Max(Environment.ProcessorCount - 1, 2); - } - - return 0; - } - - // Webm: http://www.webmproject.org/docs/encoder-parameters/ - // The decoder will usually automatically use an appropriate number of threads according to how many cores are available but it can only use multiple threads - // for the coefficient data if the encoder selected --token-parts > 0 at encode time. - - switch (state.QualitySetting) - { - case EncodingQuality.HighSpeed: - return 2; - case EncodingQuality.HighQuality: - return 2; - case EncodingQuality.MaxQuality: - return isWebm ? Math.Max(Environment.ProcessorCount - 1, 2) : 0; - default: - throw new Exception("Unrecognized MediaEncodingQuality value."); - } - } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs b/MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs deleted file mode 100644 index 05733aef03..0000000000 --- a/MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs +++ /dev/null @@ -1,168 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using System; -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.MediaEncoding.Encoder -{ - public class FFMpegProcess : IDisposable - { - private readonly string _ffmpegPath; - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - private readonly IApplicationPaths _appPaths; - private readonly IIsoManager _isoManager; - private readonly ILiveTvManager _liveTvManager; - - private Stream _logFileStream; - private InternalEncodingTask _task; - private IIsoMount _isoMount; - - public FFMpegProcess(string ffmpegPath, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths, IIsoManager isoManager, ILiveTvManager liveTvManager) - { - _ffmpegPath = ffmpegPath; - _logger = logger; - _fileSystem = fileSystem; - _appPaths = appPaths; - _isoManager = isoManager; - _liveTvManager = liveTvManager; - } - - public async Task Start(InternalEncodingTask task, Func argumentsFactory) - { - _task = task; - if (!File.Exists(_ffmpegPath)) - { - throw new InvalidOperationException("ffmpeg was not found at " + _ffmpegPath); - } - - Directory.CreateDirectory(Path.GetDirectoryName(task.Request.OutputPath)); - - string mountedPath = null; - if (task.InputVideoType.HasValue && task.InputVideoType == VideoType.Iso && task.IsoType.HasValue) - { - if (_isoManager.CanMount(task.MediaPath)) - { - _isoMount = await _isoManager.Mount(task.MediaPath, CancellationToken.None).ConfigureAwait(false); - mountedPath = _isoMount.MountedPath; - } - } - - var process = new Process - { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - - // Must consume both stdout and stderr or deadlocks may occur - RedirectStandardOutput = true, - RedirectStandardError = true, - - FileName = _ffmpegPath, - WorkingDirectory = Path.GetDirectoryName(_ffmpegPath), - Arguments = argumentsFactory(task, mountedPath), - - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }, - - EnableRaisingEvents = true - }; - - _logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments); - - var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-" + task.Id + ".txt"); - Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); - - // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - _logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true); - - process.Exited += process_Exited; - - try - { - process.Start(); - } - catch (Exception ex) - { - _logger.ErrorException("Error starting ffmpeg", ex); - - task.OnError(); - - DisposeLogFileStream(); - - process.Dispose(); - - throw; - } - - task.OnBegin(); - - // MUST read both stdout and stderr asynchronously or a deadlock may occurr - process.BeginOutputReadLine(); - -#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.StandardError.BaseStream.CopyToAsync(_logFileStream); -#pragma warning restore 4014 - } - - async void process_Exited(object sender, EventArgs e) - { - var process = (Process)sender; - - if (_isoMount != null) - { - _isoMount.Dispose(); - _isoMount = null; - } - - DisposeLogFileStream(); - - try - { - _logger.Info("FFMpeg exited with code {0} for {1}", process.ExitCode, _task.Request.OutputPath); - } - catch - { - _logger.Info("FFMpeg exited with an error for {0}", _task.Request.OutputPath); - } - - _task.OnCompleted(); - - if (!string.IsNullOrEmpty(_task.LiveTvStreamId)) - { - try - { - await _liveTvManager.CloseLiveStream(_task.LiveTvStreamId, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error closing live tv stream", ex); - } - } - } - - public void Dispose() - { - DisposeLogFileStream(); - } - - private void DisposeLogFileStream() - { - if (_logFileStream != null) - { - _logFileStream.Dispose(); - _logFileStream = null; - } - } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs deleted file mode 100644 index e0ca86c41a..0000000000 --- a/MediaBrowser.MediaEncoding/Encoder/ImageEncoder.cs +++ /dev/null @@ -1,235 +0,0 @@ -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; - -namespace MediaBrowser.MediaEncoding.Encoder -{ - public class ImageEncoder - { - 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, IApplicationPaths appPaths) - { - _ffmpegPath = ffmpegPath; - _logger = logger; - _fileSystem = fileSystem; - _appPaths = appPaths; - } - - 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 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 - { - CreateNoWindow = true, - UseShellExecute = false, - FileName = _ffmpegPath, - Arguments = GetArguments(options, filename), - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - WorkingDirectory = Path.GetDirectoryName(inputPath) - } - }; - - _logger.Debug("ffmpeg " + process.StartInfo.Arguments); - - 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); - } - } - - 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 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, string inputFilename) - { - var vfScale = GetFilterGraph(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("-f image2 -i file:\"{3}\" -q:v {0} {1} -f image2pipe -vcodec {2} -", - qualityValue.ToString(_usCulture), - vfScale, - outputFormat, - inputFilename); - } - - 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}\"", - widthScale, - heightScale); - } - - private string GetOutputFormat(string format) - { - if (string.Equals(format, "jpeg", StringComparison.OrdinalIgnoreCase) || - string.Equals(format, "jpg", StringComparison.OrdinalIgnoreCase)) - { - return "mjpeg"; - } - return format; - } - - private void ValidateInput(ImageEncodingOptions options) - { - - } - - /// - /// 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/InternalEncodingTask.cs b/MediaBrowser.MediaEncoding/Encoder/InternalEncodingTask.cs deleted file mode 100644 index 826525aef2..0000000000 --- a/MediaBrowser.MediaEncoding/Encoder/InternalEncodingTask.cs +++ /dev/null @@ -1,95 +0,0 @@ -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Entities; -using System; -using System.Collections.Generic; -using System.Threading; - -namespace MediaBrowser.MediaEncoding.Encoder -{ - public class InternalEncodingTask - { - public string Id { get; set; } - - public CancellationTokenSource CancellationTokenSource { get; set; } - - public double ProgressPercentage { get; set; } - - public EncodingOptions Request { get; set; } - - public VideoEncodingOptions VideoRequest - { - get { return Request as VideoEncodingOptions; } - } - - public string MediaPath { get; set; } - public List StreamFileNames { get; set; } - public bool IsInputRemote { get; set; } - - public VideoType? InputVideoType { get; set; } - public IsoType? IsoType { get; set; } - public long? InputRunTimeTicks; - - public string AudioSync = "1"; - public string VideoSync = "vfr"; - - public string InputAudioSync { get; set; } - public string InputVideoSync { get; set; } - - public bool DeInterlace { get; set; } - - public bool ReadInputAtNativeFramerate { get; set; } - - public string InputFormat { get; set; } - - public string InputVideoCodec { get; set; } - - public string InputAudioCodec { get; set; } - - public string LiveTvStreamId { get; set; } - - public MediaStream AudioStream { get; set; } - public MediaStream VideoStream { get; set; } - public MediaStream SubtitleStream { get; set; } - public bool HasMediaStreams { get; set; } - - public int SegmentLength = 10; - public int HlsListSize; - - public string MimeType { get; set; } - public string OrgPn { get; set; } - public bool EnableMpegtsM2TsMode { get; set; } - - /// - /// Gets or sets the user agent. - /// - /// The user agent. - public string UserAgent { get; set; } - - public EncodingQuality QualitySetting { get; set; } - - public InternalEncodingTask() - { - Id = Guid.NewGuid().ToString("N"); - CancellationTokenSource = new CancellationTokenSource(); - StreamFileNames = new List(); - } - - public bool EnableDebugLogging { get; set; } - - internal void OnBegin() - { - - } - - internal void OnCompleted() - { - - } - - internal void OnError() - { - - } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/InternalEncodingTaskFactory.cs b/MediaBrowser.MediaEncoding/Encoder/InternalEncodingTaskFactory.cs deleted file mode 100644 index f52f072dff..0000000000 --- a/MediaBrowser.MediaEncoding/Encoder/InternalEncodingTaskFactory.cs +++ /dev/null @@ -1,311 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.LiveTv; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.MediaEncoding.Encoder -{ - public class InternalEncodingTaskFactory - { - private readonly ILibraryManager _libraryManager; - private readonly ILiveTvManager _liveTvManager; - private readonly IItemRepository _itemRepo; - private readonly IServerConfigurationManager _config; - - public InternalEncodingTaskFactory(ILibraryManager libraryManager, ILiveTvManager liveTvManager, IItemRepository itemRepo, IServerConfigurationManager config) - { - _libraryManager = libraryManager; - _liveTvManager = liveTvManager; - _itemRepo = itemRepo; - _config = config; - } - - public async Task Create(EncodingOptions request, CancellationToken cancellationToken) - { - ValidateInput(request); - - var state = new InternalEncodingTask - { - Request = request - }; - - var item = string.IsNullOrEmpty(request.MediaSourceId) ? - _libraryManager.GetItemById(new Guid(request.ItemId)) : - _libraryManager.GetItemById(new Guid(request.MediaSourceId)); - - if (item is ILiveTvRecording) - { - var recording = await _liveTvManager.GetInternalRecording(request.ItemId, cancellationToken).ConfigureAwait(false); - - if (string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) - { - state.InputVideoType = VideoType.VideoFile; - } - - var path = recording.RecordingInfo.Path; - var mediaUrl = recording.RecordingInfo.Url; - - if (string.IsNullOrWhiteSpace(path) && string.IsNullOrWhiteSpace(mediaUrl)) - { - var streamInfo = await _liveTvManager.GetRecordingStream(request.ItemId, cancellationToken).ConfigureAwait(false); - - state.LiveTvStreamId = streamInfo.Id; - - path = streamInfo.Path; - mediaUrl = streamInfo.Url; - } - - if (!string.IsNullOrEmpty(path) && File.Exists(path)) - { - state.MediaPath = path; - state.IsInputRemote = false; - } - else if (!string.IsNullOrEmpty(mediaUrl)) - { - state.MediaPath = mediaUrl; - state.IsInputRemote = true; - } - - state.InputRunTimeTicks = recording.RunTimeTicks; - if (recording.RecordingInfo.Status == RecordingStatus.InProgress && !state.IsInputRemote) - { - await Task.Delay(1000, cancellationToken).ConfigureAwait(false); - } - - state.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress; - state.AudioSync = "1000"; - state.DeInterlace = true; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; - } - else if (item is LiveTvChannel) - { - var channel = _liveTvManager.GetInternalChannel(request.ItemId); - - if (string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) - { - state.InputVideoType = VideoType.VideoFile; - } - - var streamInfo = await _liveTvManager.GetChannelStream(request.ItemId, cancellationToken).ConfigureAwait(false); - - state.LiveTvStreamId = streamInfo.Id; - - if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path)) - { - state.MediaPath = streamInfo.Path; - state.IsInputRemote = false; - - await Task.Delay(1000, cancellationToken).ConfigureAwait(false); - } - else if (!string.IsNullOrEmpty(streamInfo.Url)) - { - state.MediaPath = streamInfo.Url; - state.IsInputRemote = true; - } - - state.ReadInputAtNativeFramerate = true; - state.AudioSync = "1000"; - state.DeInterlace = true; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; - } - else - { - state.MediaPath = item.Path; - state.IsInputRemote = item.LocationType == LocationType.Remote; - - var video = item as Video; - - if (video != null) - { - state.InputVideoType = video.VideoType; - state.IsoType = video.IsoType; - - state.StreamFileNames = video.PlayableStreamFileNames.ToList(); - } - - state.InputRunTimeTicks = item.RunTimeTicks; - } - - var videoRequest = request as VideoEncodingOptions; - - var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery - { - ItemId = item.Id - - }).ToList(); - - if (videoRequest != null) - { - state.VideoStream = GetMediaStream(mediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video); - state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false); - state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio); - - if (state.VideoStream != null && state.VideoStream.IsInterlaced) - { - state.DeInterlace = true; - } - } - else - { - state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); - } - - state.HasMediaStreams = mediaStreams.Count > 0; - - state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10; - state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440; - - state.QualitySetting = GetQualitySetting(); - - ApplyDeviceProfileSettings(state); - - return state; - } - - private void ValidateInput(EncodingOptions request) - { - if (string.IsNullOrWhiteSpace(request.ItemId)) - { - throw new ArgumentException("ItemId is required."); - } - if (string.IsNullOrWhiteSpace(request.OutputPath)) - { - throw new ArgumentException("OutputPath is required."); - } - if (string.IsNullOrWhiteSpace(request.Container)) - { - throw new ArgumentException("Container is required."); - } - if (string.IsNullOrWhiteSpace(request.AudioCodec)) - { - throw new ArgumentException("AudioCodec is required."); - } - - var videoRequest = request as VideoEncodingOptions; - - if (videoRequest == null) - { - return; - } - } - - /// - /// Determines which stream will be used for playback - /// - /// All stream. - /// Index of the desired. - /// The type. - /// if set to true [return first if no index]. - /// MediaStream. - private MediaStream GetMediaStream(IEnumerable allStream, int? desiredIndex, MediaStreamType type, bool returnFirstIfNoIndex = true) - { - var streams = allStream.Where(s => s.Type == type).OrderBy(i => i.Index).ToList(); - - if (desiredIndex.HasValue) - { - var stream = streams.FirstOrDefault(s => s.Index == desiredIndex.Value); - - if (stream != null) - { - return stream; - } - } - - if (returnFirstIfNoIndex && type == MediaStreamType.Audio) - { - return streams.FirstOrDefault(i => i.Channels.HasValue && i.Channels.Value > 0) ?? - streams.FirstOrDefault(); - } - - // Just return the first one - return returnFirstIfNoIndex ? streams.FirstOrDefault() : null; - } - - private void ApplyDeviceProfileSettings(InternalEncodingTask state) - { - var profile = state.Request.DeviceProfile; - - if (profile == null) - { - // Don't use settings from the default profile. - // Only use a specific profile if it was requested. - return; - } - - var container = state.Request.Container; - - 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) : - // 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; - - // if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.VideoProfile)) - // { - // state.VideoRequest.VideoProfile = transcodingProfile.VideoProfile; - // } - //} - } - - private EncodingQuality GetQualitySetting() - { - var quality = _config.Configuration.MediaEncodingQuality; - - if (quality == EncodingQuality.Auto) - { - var cpuCount = Environment.ProcessorCount; - - if (cpuCount >= 4) - { - //return EncodingQuality.HighQuality; - } - - return EncodingQuality.HighSpeed; - } - - return quality; - } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 389046c9e2..9ff28dda21 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -853,7 +853,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public Task EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) { - return new ImageEncoder(FFMpegPath, _logger, _fileSystem, _appPaths).EncodeImage(options, cancellationToken); + throw new NotImplementedException(); } /// diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 2343d97185..5abc509d0c 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -53,12 +53,7 @@ - - - - - diff --git a/MediaBrowser.Model/Session/SessionCapabilities.cs b/MediaBrowser.Model/Session/SessionCapabilities.cs index 5eeede1261..767df8f1c1 100644 --- a/MediaBrowser.Model/Session/SessionCapabilities.cs +++ b/MediaBrowser.Model/Session/SessionCapabilities.cs @@ -10,6 +10,8 @@ namespace MediaBrowser.Model.Session public bool SupportsMediaControl { get; set; } + public string MessageCallbackUrl { get; set; } + public SessionCapabilities() { PlayableMediaTypes = new List(); diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index cfa642add2..538e6a0fdf 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Server.Implementations.Channels { all = all.Take(query.Limit.Value).ToList(); } - + // Get everything var fields = Enum.GetNames(typeof(ItemFields)) .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) @@ -156,6 +156,23 @@ namespace MediaBrowser.Server.Implementations.Channels progress.Report(100); } + public Task> GetChannelItemMediaSources(string id, CancellationToken cancellationToken) + { + var item = (IChannelMediaItem)_libraryManager.GetItemById(id); + + var channelGuid = new Guid(item.ChannelId); + var channel = _channelEntities.First(i => i.Id == channelGuid); + + var requiresCallback = channel as IRequiresMediaInfoCallback; + + if (requiresCallback != null) + { + return requiresCallback.GetChannelItemMediaInfo(item.ExternalId, cancellationToken); + } + + return Task.FromResult>(item.ChannelMediaSources); + } + private async Task GetChannel(IChannel channelInfo, CancellationToken cancellationToken) { var path = Path.Combine(_config.ApplicationPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(channelInfo.Name)); @@ -303,10 +320,16 @@ namespace MediaBrowser.Server.Implementations.Channels var query = new InternalChannelItemQuery { - User = user, - CategoryId = categoryId + User = user }; + if (!string.IsNullOrWhiteSpace(categoryId)) + { + var categoryItem = (IChannelItem)_libraryManager.GetItemById(new Guid(categoryId)); + + query.CategoryId = categoryItem.ExternalId; + } + var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false); CacheResponse(result, cachePath); @@ -380,7 +403,7 @@ namespace MediaBrowser.Server.Implementations.Channels private string GetIdToHash(string externalId) { // Increment this as needed to force new downloads - return externalId + "4"; + return externalId + "7"; } private async Task GetChannelItemEntity(ChannelItemInfo info, string internalChannnelId, CancellationToken cancellationToken) @@ -434,10 +457,6 @@ namespace MediaBrowser.Server.Implementations.Channels item.Id = id; item.RunTimeTicks = info.RunTimeTicks; - var mediaSource = info.MediaSources.FirstOrDefault(); - - item.Path = mediaSource == null ? null : mediaSource.Path; - if (isNew) { item.Name = info.Name; @@ -464,12 +483,22 @@ namespace MediaBrowser.Server.Implementations.Channels channelItem.ChannelId = internalChannnelId; channelItem.ChannelItemType = info.Type; + if (isNew) + { + channelItem.Tags = info.Tags; + } + var channelMediaItem = item as IChannelMediaItem; if (channelMediaItem != null) { channelMediaItem.IsInfiniteStream = info.IsInfiniteStream; channelMediaItem.ContentType = info.ContentType; + channelMediaItem.ChannelMediaSources = info.MediaSources; + + var mediaSource = info.MediaSources.FirstOrDefault(); + + item.Path = mediaSource == null ? null : mediaSource.Path; } if (isNew) diff --git a/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs b/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs index 236963b9b8..0b0f21e2c9 100644 --- a/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs +++ b/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs @@ -1,5 +1,8 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Net; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Net; @@ -16,23 +19,18 @@ namespace MediaBrowser.Server.Implementations.Session { private readonly IHttpClient _httpClient; private readonly IJsonSerializer _json; - private readonly IServerApplicationHost _appHost; public SessionInfo Session { get; private set; } - //var postUrl = string.Format("http://{0}/mediabrowser/message", session.RemoteEndPoint); - private readonly string _postUrl; - public HttpSessionController(IHttpClient httpClient, - IJsonSerializer json, - IServerApplicationHost appHost, - SessionInfo session, + public HttpSessionController(IHttpClient httpClient, + IJsonSerializer json, + SessionInfo session, string postUrl) { _httpClient = httpClient; _json = json; - _appHost = appHost; Session = session; _postUrl = postUrl; } @@ -63,6 +61,21 @@ namespace MediaBrowser.Server.Implementations.Session }); } + private Task SendMessage(string name, CancellationToken cancellationToken) + { + return SendMessage(name, new NameValueCollection(), cancellationToken); + } + + private Task SendMessage(string name, NameValueCollection args, CancellationToken cancellationToken) + { + return SendMessage(new WebSocketMessage + { + MessageType = name, + Data = string.Empty + + }, cancellationToken); + } + public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken) { return Task.FromResult(true); @@ -80,22 +93,25 @@ namespace MediaBrowser.Server.Implementations.Session public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken) { - return SendMessage(new WebSocketMessage - { - MessageType = "Play", - Data = command + return Task.FromResult(true); + //return SendMessage(new WebSocketMessage + //{ + // MessageType = "Play", + // Data = command - }, cancellationToken); + //}, cancellationToken); } public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken) { - return SendMessage(new WebSocketMessage + var args = new Dictionary(); + + if (command.Command == PlaystateCommand.Seek) { - MessageType = "Playstate", - Data = command - }, cancellationToken); + } + + return SendMessage(command.Command.ToString(), cancellationToken); } public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken) @@ -103,14 +119,9 @@ namespace MediaBrowser.Server.Implementations.Session return Task.FromResult(true); } - public Task SendRestartRequiredNotification(CancellationToken cancellationToken) + public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken) { - return SendMessage(new WebSocketMessage - { - MessageType = "RestartRequired", - Data = _appHost.GetSystemInfo() - - }, cancellationToken); + return SendMessage("RestartRequired", cancellationToken); } public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken) @@ -120,22 +131,12 @@ namespace MediaBrowser.Server.Implementations.Session public Task SendServerShutdownNotification(CancellationToken cancellationToken) { - return SendMessage(new WebSocketMessage - { - MessageType = "ServerShuttingDown", - Data = string.Empty - - }, cancellationToken); + return SendMessage("ServerShuttingDown", cancellationToken); } public Task SendServerRestartNotification(CancellationToken cancellationToken) { - return SendMessage(new WebSocketMessage - { - MessageType = "ServerRestarting", - Data = string.Empty - - }, cancellationToken); + return SendMessage("ServerRestarting", cancellationToken); } public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken) @@ -147,5 +148,13 @@ namespace MediaBrowser.Server.Implementations.Session }, cancellationToken); } + + private string ToQueryString(Dictionary nvc) + { + var array = (from item in nvc + select string.Format("{0}={1}", WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value))) + .ToArray(); + return "?" + string.Join("&", array); + } } } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index cfa7cef671..d78fae87b2 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -1,6 +1,7 @@ -using System.Globalization; -using MediaBrowser.Common.Events; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -14,10 +15,12 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Library; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -51,6 +54,10 @@ namespace MediaBrowser.Server.Implementations.Session private readonly IImageProcessor _imageProcessor; private readonly IItemRepository _itemRepo; + private readonly IHttpClient _httpClient; + private readonly IJsonSerializer _jsonSerializer; + private readonly IServerApplicationHost _appHost; + /// /// Gets or sets the configuration manager. /// @@ -93,7 +100,7 @@ namespace MediaBrowser.Server.Implementations.Session /// The logger. /// The user repository. /// The library manager. - public SessionManager(IUserDataManager userDataRepository, IServerConfigurationManager configurationManager, ILogger logger, IUserRepository userRepository, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IItemRepository itemRepo) + public SessionManager(IUserDataManager userDataRepository, IServerConfigurationManager configurationManager, ILogger logger, IUserRepository userRepository, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IItemRepository itemRepo, IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IHttpClient httpClient) { _userDataRepository = userDataRepository; _configurationManager = configurationManager; @@ -105,6 +112,9 @@ namespace MediaBrowser.Server.Implementations.Session _dtoService = dtoService; _imageProcessor = imageProcessor; _itemRepo = itemRepo; + _jsonSerializer = jsonSerializer; + _appHost = appHost; + _httpClient = httpClient; } /// @@ -908,11 +918,13 @@ namespace MediaBrowser.Server.Implementations.Session { var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList(); + var info = _appHost.GetSystemInfo(); + var tasks = sessions.Select(session => Task.Run(async () => { try { - await session.SessionController.SendRestartRequiredNotification(cancellationToken).ConfigureAwait(false); + await session.SessionController.SendRestartRequiredNotification(info, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -1135,6 +1147,18 @@ namespace MediaBrowser.Server.Implementations.Session session.PlayableMediaTypes = capabilities.PlayableMediaTypes; session.SupportedCommands = capabilities.SupportedCommands; + if (!string.IsNullOrWhiteSpace(capabilities.MessageCallbackUrl)) + { + var postUrl = string.Format("http://{0}{1}", session.RemoteEndPoint, capabilities.MessageCallbackUrl); + + var controller = session.SessionController as HttpSessionController; + + if (controller == null) + { + session.SessionController = new HttpSessionController(_httpClient, _jsonSerializer, session, postUrl); + } + } + EventHelper.FireEventIfNotNull(CapabilitiesChanged, this, new SessionEventArgs { SessionInfo = session diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs index 1a042ede69..365845f416 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs @@ -35,19 +35,17 @@ namespace MediaBrowser.Server.Implementations.Session /// The _dto service /// private readonly IJsonSerializer _json; - private readonly IServerApplicationHost _appHost; /// /// Initializes a new instance of the class. /// /// The session manager. /// The log manager. - /// The application host. - public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IServerApplicationHost appHost, IJsonSerializer json) + /// The json. + public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IJsonSerializer json) { _sessionManager = sessionManager; _logger = logManager.GetLogger(GetType().Name); - _appHost = appHost; _json = json; } @@ -138,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.Session if (controller == null) { - controller = new WebSocketController(session, _appHost, _logger, _sessionManager); + controller = new WebSocketController(session, _logger, _sessionManager); } controller.AddWebSocket(message.Connection); diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs index 05eef611f5..5fc28e81b0 100644 --- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs +++ b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Net; -using MediaBrowser.Controller; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -19,15 +18,13 @@ namespace MediaBrowser.Server.Implementations.Session public SessionInfo Session { get; private set; } public IReadOnlyList Sockets { get; private set; } - private readonly IServerApplicationHost _appHost; private readonly ILogger _logger; private readonly ISessionManager _sessionManager; - public WebSocketController(SessionInfo session, IServerApplicationHost appHost, ILogger logger, ISessionManager sessionManager) + public WebSocketController(SessionInfo session, ILogger logger, ISessionManager sessionManager) { Session = session; - _appHost = appHost; _logger = logger; _sessionManager = sessionManager; Sockets = new List(); @@ -121,14 +118,15 @@ namespace MediaBrowser.Server.Implementations.Session /// /// Sends the restart required message. /// + /// The information. /// The cancellation token. /// Task. - public Task SendRestartRequiredNotification(CancellationToken cancellationToken) + public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken) { return SendMessages(new WebSocketMessage { MessageType = "RestartRequired", - Data = _appHost.GetSystemInfo() + Data = info }, cancellationToken); } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 831067a9ef..1cfc80f1c5 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -496,7 +496,7 @@ namespace MediaBrowser.ServerApplication DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager); RegisterSingleInstance(DtoService); - SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, ItemRepository); + SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, ItemRepository, JsonSerializer, this, HttpClient); RegisterSingleInstance(SessionManager); var newsService = new Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer); diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 6dd8fb458c..798d7169de 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.367 + 3.0.369 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 2a98209256..bc1fb3896f 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.367 + 3.0.369 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 7df46236c2..ba28896035 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.367 + 3.0.369 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 - +