diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 917d7faa94..5999a2b552 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1427,13 +1427,12 @@ namespace MediaBrowser.Api.Playback } else if (item is IChannelMediaItem) { - var channelMediaSources = await ChannelManager.GetChannelItemMediaSources(request.Id, CancellationToken.None).ConfigureAwait(false); - - var source = channelMediaSources.First(); + var source = await GetChannelMediaInfo(request.Id, CancellationToken.None).ConfigureAwait(false); state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); state.IsRemote = source.IsRemote; state.MediaPath = source.Path; state.RunTimeTicks = item.RunTimeTicks; + mediaStreams = GetMediaStreams(source).ToList(); } else { @@ -1479,28 +1478,7 @@ namespace MediaBrowser.Api.Playback }).ToList(); - if (videoRequest != null) - { - if (string.IsNullOrEmpty(videoRequest.VideoCodec)) - { - videoRequest.VideoCodec = InferVideoCodec(url); - } - - 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; - } - - EnforceResolutionLimit(state, videoRequest); - } - else - { - state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); - } + AttachMediaStreamInfo(state, mediaStreams, videoRequest, url); state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10; state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440; @@ -1538,6 +1516,96 @@ namespace MediaBrowser.Api.Playback return state; } + private void AttachMediaStreamInfo(StreamState state, + List mediaStreams, + VideoStreamRequest videoRequest, + string requestedUrl) + { + if (videoRequest != null) + { + if (string.IsNullOrEmpty(videoRequest.VideoCodec)) + { + videoRequest.VideoCodec = InferVideoCodec(requestedUrl); + } + + 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; + } + + EnforceResolutionLimit(state, videoRequest); + } + else + { + state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); + } + } + + private IEnumerable GetMediaStreams(ChannelMediaInfo info) + { + var list = new List(); + + if (!string.IsNullOrWhiteSpace(info.VideoCodec) && + !string.IsNullOrWhiteSpace(info.AudioCodec)) + { + list.Add(new MediaStream + { + Type = MediaStreamType.Video, + Width = info.Width, + RealFrameRate = info.Framerate, + Profile = info.VideoProfile, + Level = info.VideoLevel, + Index = -1, + Height = info.Height, + Codec = info.VideoCodec, + BitRate = info.VideoBitrate, + AverageFrameRate = info.Framerate + }); + + list.Add(new MediaStream + { + Type = MediaStreamType.Audio, + Index = -1, + Codec = info.AudioCodec, + BitRate = info.AudioBitrate, + Channels = info.AudioChannels, + SampleRate = info.AudioSampleRate + }); + } + + return list; + } + + private async Task GetChannelMediaInfo(string id, CancellationToken cancellationToken) + { + var channelMediaSources = await ChannelManager.GetChannelItemMediaSources(id, cancellationToken) + .ConfigureAwait(false); + + var list = channelMediaSources.ToList(); + + var preferredWidth = ServerConfigurationManager.Configuration.ChannelOptions.PreferredStreamingWidth; + + if (preferredWidth.HasValue) + { + var val = preferredWidth.Value; + + return list + .OrderBy(i => Math.Abs(i.Width ?? 0 - val)) + .ThenByDescending(i => i.Width ?? 0) + .ThenBy(list.IndexOf) + .First(); + } + + return list + .OrderByDescending(i => i.Width ?? 0) + .ThenBy(list.IndexOf) + .First(); + } + private bool CanStreamCopyVideo(VideoStreamRequest request, MediaStream videoStream) { if (videoStream.IsInterlaced) diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs index e4ea186bdd..895c430767 100644 --- a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs +++ b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs @@ -170,7 +170,7 @@ namespace MediaBrowser.Common.Implementations.Updates // Let dev users get results more often for testing purposes var cacheLength = _config.CommonConfiguration.SystemUpdateLevel == PackageVersionClass.Dev ? TimeSpan.FromMinutes(3) - : TimeSpan.FromHours(4); + : TimeSpan.FromHours(6); if ((DateTime.UtcNow - _lastPackageListResult.Item2) < cacheLength) { diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs index 3630342679..b38ef363a8 100644 --- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs @@ -17,9 +17,14 @@ namespace MediaBrowser.Controller.Channels public int? Width { get; set; } public int? Height { get; set; } public int? AudioChannels { get; set; } + public int? AudioSampleRate { get; set; } public bool IsRemote { get; set; } + public string VideoProfile { get; set; } + public float? VideoLevel { get; set; } + public float? Framerate { get; set; } + public ChannelMediaInfo() { RequiredHttpHeaders = new Dictionary(); diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 3ffdf1a933..eac2e0d9b8 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -218,7 +218,7 @@ namespace MediaBrowser.Model.ApiClient /// /// The item identifier. /// Task{BaseItemDto[]}. - Task GetAdditionalParts(string itemId, string userId); + Task GetAdditionalParts(string itemId, string userId); /// /// Gets the users async. @@ -443,8 +443,9 @@ namespace MediaBrowser.Model.ApiClient /// /// Gets the system status async. /// + /// The cancellation token. /// Task{SystemInfo}. - Task GetSystemInfoAsync(); + Task GetSystemInfoAsync(CancellationToken cancellationToken); /// /// Gets a person diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 4fe5c8bcfc..17c7e8b557 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -226,6 +226,8 @@ namespace MediaBrowser.Model.Configuration [Obsolete] public string[] ManualLoginClients { get; set; } + public ChannelOptions ChannelOptions { get; set; } + /// /// Initializes a new instance of the class. /// @@ -293,6 +295,13 @@ namespace MediaBrowser.Model.Configuration NotificationOptions = new NotificationOptions(); SubtitleOptions = new SubtitleOptions(); + + ChannelOptions = new ChannelOptions(); } } + + public class ChannelOptions + { + public int? PreferredStreamingWidth { get; set; } + } } diff --git a/MediaBrowser.Model/Extensions/DoubleHelper.cs b/MediaBrowser.Model/Extensions/DoubleHelper.cs index 203f2b7df2..bcaf2d7800 100644 --- a/MediaBrowser.Model/Extensions/DoubleHelper.cs +++ b/MediaBrowser.Model/Extensions/DoubleHelper.cs @@ -2,6 +2,9 @@ namespace MediaBrowser.Model.Extensions { + /// + /// Isolating these helpers allow this entire project to be easily converted to Java + /// public static class DoubleHelper { /// diff --git a/MediaBrowser.Model/Extensions/IntHelper.cs b/MediaBrowser.Model/Extensions/IntHelper.cs index 4a96e19d49..6c5f26080a 100644 --- a/MediaBrowser.Model/Extensions/IntHelper.cs +++ b/MediaBrowser.Model/Extensions/IntHelper.cs @@ -2,6 +2,9 @@ namespace MediaBrowser.Model.Extensions { + /// + /// Isolating these helpers allow this entire project to be easily converted to Java + /// public static class IntHelper { /// diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs index b3ebc3f22c..1c086655ff 100644 --- a/MediaBrowser.Model/Extensions/StringHelper.cs +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -3,6 +3,9 @@ using System.Globalization; namespace MediaBrowser.Model.Extensions { + /// + /// Isolating these helpers allow this entire project to be easily converted to Java + /// public static class StringHelper { /// diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index 335366077c..b9b9b8327b 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; @@ -519,7 +518,7 @@ namespace MediaBrowser.Server.Implementations.Channels { // Increment this as needed to force new downloads // Incorporate Name because it's being used to convert channel entity to provider - return externalId + (channelProvider.DataVersion ?? string.Empty) + (channelProvider.Name ?? string.Empty) + "13"; + return externalId + (channelProvider.DataVersion ?? string.Empty) + (channelProvider.Name ?? string.Empty) + "14"; } private async Task GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Channel internalChannel, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index dae0470cf0..88bf902db9 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1171,7 +1171,12 @@ namespace MediaBrowser.Server.Implementations.Dto LocationType = item.LocationType, Name = item.Name, Path = GetMappedPath(item), - MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList(), + MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery + { + ItemId = item.Id + + }).ToList(), + RunTimeTicks = item.RunTimeTicks } }; diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index a31c53b50d..a920b8e572 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -563,6 +563,7 @@ "LabelDefaultUser": "Default user:", "LabelDefaultUserHelp": "Determines which user library should be displayed on connected devices. This can be overridden for each device using profiles.", "TitleDlna": "DLNA", + "TitleChannels": "Channels", "HeaderServerSettings": "Server Settings", "LabelWeatherDisplayLocation": "Weather display location:", "LabelWeatherDisplayLocationHelp": "US zip code / City, State, Country / City, Country", @@ -796,5 +797,8 @@ "HeaderWelcomeToMediaBrowserWebClient": "Welcome to the Media Browser Web Client", "ButtonDismiss": "Dismiss", "MessageLearnHowToCustomize": "Learn how to customize this page to your own personal tastes. Click your user icon in the top right corner of the screen to view and update your preferences.", - "ButtonEditOtherUserPreferences": "Edit this user's personal preferences." + "ButtonEditOtherUserPreferences": "Edit this user's personal preferences.", + "ChannelStreamOptionBestAvailable": "Best available", + "LabelChannelStreamOptionBestAvailable": "Preferred streaming quality:", + "LabelChannelStreamOptionBestAvailableHelp": "Determines the selected quality when channel content is available in multiple resolutions." } \ No newline at end of file diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 3f89c976cf..7946e73197 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -539,6 +539,7 @@ namespace MediaBrowser.WebDashboard.Api "autoorganizelog.js", "channels.js", "channelitems.js", + "channelsettings.js", "dashboardgeneral.js", "dashboardinfo.js", "dashboardpage.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index c4854ad119..d8fa2c9be4 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -103,6 +103,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -613,6 +616,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index e934b12522..265b00dfcd 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.388 + 3.0.391 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 3fe966d180..f2d4e4eecd 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.388 + 3.0.391 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 3e186e60cf..6a8991ae20 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.388 + 3.0.391 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 - +