diff options
Diffstat (limited to 'MediaBrowser.Model')
35 files changed, 680 insertions, 184 deletions
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 14cdbdc9ab..9675de38b0 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -248,10 +248,9 @@ namespace MediaBrowser.Model.ApiClient /// <summary> /// Gets the playback information. /// </summary> - /// <param name="itemId">The item identifier.</param> - /// <param name="userId">The user identifier.</param> + /// <param name="request">The request.</param> /// <returns>Task<LiveMediaInfoResult>.</returns> - Task<LiveMediaInfoResult> GetPlaybackInfo(string itemId, string userId); + Task<PlaybackInfoResponse> GetPlaybackInfo(PlaybackInfoRequest request); /// <summary> /// Gets the users async. @@ -1486,5 +1485,12 @@ namespace MediaBrowser.Model.ApiClient /// <param name="query">The query.</param> /// <returns>Task<List<RecommendationDto>>.</returns> Task<List<RecommendationDto>> GetMovieRecommendations(MovieRecommendationQuery query); + /// <summary> + /// Opens the live stream. + /// </summary> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task<LiveStreamResponse>.</returns> + Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken); } }
\ No newline at end of file diff --git a/MediaBrowser.Model/ApiClient/IConnectionManager.cs b/MediaBrowser.Model/ApiClient/IConnectionManager.cs index 84a815dfc0..f8837f15de 100644 --- a/MediaBrowser.Model/ApiClient/IConnectionManager.cs +++ b/MediaBrowser.Model/ApiClient/IConnectionManager.cs @@ -172,5 +172,11 @@ namespace MediaBrowser.Model.ApiClient /// <param name="rememberCredentials">if set to <c>true</c> [remember credentials].</param> /// <returns>Task.</returns> Task AuthenticateOffline(UserDto user, string password, bool rememberCredentials); + + /// <summary> + /// Gets the offline users. + /// </summary> + /// <returns>Task<List<UserDto>>.</returns> + Task<List<UserDto>> GetOfflineUsers(); } } diff --git a/MediaBrowser.Model/ApiClient/ServerCredentials.cs b/MediaBrowser.Model/ApiClient/ServerCredentials.cs index d911f81211..6ba2a80c87 100644 --- a/MediaBrowser.Model/ApiClient/ServerCredentials.cs +++ b/MediaBrowser.Model/ApiClient/ServerCredentials.cs @@ -1,7 +1,6 @@ using MediaBrowser.Model.Extensions; using System; using System.Collections.Generic; -using System.Linq; namespace MediaBrowser.Model.ApiClient { @@ -24,7 +23,12 @@ namespace MediaBrowser.Model.ApiClient throw new ArgumentNullException("server"); } - var list = Servers.ToList(); + // Clone the existing list of servers + var list = new List<ServerInfo>(); + foreach (ServerInfo serverInfo in Servers) + { + list.Add(serverInfo); + } var index = FindIndex(list, server.Id); @@ -32,8 +36,11 @@ namespace MediaBrowser.Model.ApiClient { var existing = list[index]; - // Merge the data - existing.DateLastAccessed = new[] { existing.DateLastAccessed, server.DateLastAccessed }.Max(); + // Take the most recent DateLastAccessed + if (server.DateLastAccessed > existing.DateLastAccessed) + { + existing.DateLastAccessed = server.DateLastAccessed; + } existing.UserLinkType = server.UserLinkType; @@ -64,7 +71,11 @@ namespace MediaBrowser.Model.ApiClient } if (server.WakeOnLanInfos != null && server.WakeOnLanInfos.Count > 0) { - existing.WakeOnLanInfos = server.WakeOnLanInfos.ToList(); + existing.WakeOnLanInfos = new List<WakeOnLanInfo>(); + foreach (WakeOnLanInfo info in server.WakeOnLanInfos) + { + existing.WakeOnLanInfos.Add(info); + } } if (server.LastConnectionMode.HasValue) { diff --git a/MediaBrowser.Model/ApiClient/ServerInfo.cs b/MediaBrowser.Model/ApiClient/ServerInfo.cs index cc062f2f60..e1fa581d7b 100644 --- a/MediaBrowser.Model/ApiClient/ServerInfo.cs +++ b/MediaBrowser.Model/ApiClient/ServerInfo.cs @@ -3,7 +3,6 @@ using MediaBrowser.Model.Extensions; using MediaBrowser.Model.System; using System; using System.Collections.Generic; -using System.Linq; namespace MediaBrowser.Model.ApiClient { @@ -83,7 +82,12 @@ namespace MediaBrowser.Model.ApiClient throw new ArgumentNullException("user"); } - var list = Users.ToList(); + // Clone the existing list of users + var list = new List<ServerUserInfo>(); + foreach (ServerUserInfo serverUserInfo in Users) + { + list.Add(serverUserInfo); + } var index = FindIndex(list, user.Id); diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index f24367298e..afd67eb153 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -8,12 +8,16 @@ namespace MediaBrowser.Model.Configuration public double DownMixAudioBoost { get; set; } public string H264Encoder { get; set; } public bool EnableDebugLogging { get; set; } + public bool EnableThrottling { get; set; } + public int ThrottleThresholdSeconds { get; set; } public EncodingOptions() { H264Encoder = "libx264"; DownMixAudioBoost = 2; EncodingQuality = EncodingQuality.Auto; + EnableThrottling = true; + ThrottleThresholdSeconds = 120; } } } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index c06aedb500..bc167333ae 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -200,7 +200,7 @@ namespace MediaBrowser.Model.Configuration public PeopleMetadataOptions PeopleMetadataOptions { get; set; } public bool FindInternetTrailers { get; set; } - public string[] InsecureApps8 { get; set; } + public string[] InsecureApps9 { get; set; } public bool SaveMetadataHidden { get; set; } @@ -257,7 +257,7 @@ namespace MediaBrowser.Model.Configuration PeopleMetadataOptions = new PeopleMetadataOptions(); - InsecureApps8 = new[] + InsecureApps9 = new[] { "Chromecast", "iOS", @@ -266,7 +266,6 @@ namespace MediaBrowser.Model.Configuration "Media Portal", "iPad", "iPhone", - "Roku", "Windows Phone" }; diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index a78161140f..9cd8c1067f 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -7,12 +7,6 @@ namespace MediaBrowser.Model.Configuration public class UserConfiguration { /// <summary> - /// Gets or sets a value indicating whether this instance is administrator. - /// </summary> - /// <value><c>true</c> if this instance is administrator; otherwise, <c>false</c>.</value> - public bool IsAdministrator { get; set; } - - /// <summary> /// Gets or sets the audio language preference. /// </summary> /// <value>The audio language preference.</value> @@ -53,13 +47,14 @@ namespace MediaBrowser.Model.Configuration public string[] LatestItemsExcludes { get; set; } - public bool HasMigratedToPolicy { get; set; } + public bool HidePlayedInLatest { get; set; } /// <summary> /// Initializes a new instance of the <see cref="UserConfiguration" /> class. /// </summary> public UserConfiguration() { + HidePlayedInLatest = true; PlayDefaultAudioTrack = true; LatestItemsExcludes = new string[] { }; diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index e0a8e239e1..3769634440 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -20,7 +20,9 @@ namespace MediaBrowser.Model.Dlna TransportStreamTimestamp? timestamp, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { switch (condition.Property) { @@ -56,6 +58,10 @@ namespace MediaBrowser.Model.Dlna return IsConditionSatisfied(condition, width); case ProfileConditionValue.RefFrames: return IsConditionSatisfied(condition, refFrames); + case ProfileConditionValue.NumAudioStreams: + return IsConditionSatisfied(condition, numAudioStreams); + case ProfileConditionValue.NumVideoStreams: + return IsConditionSatisfied(condition, numVideoStreams); case ProfileConditionValue.VideoTimestamp: return IsConditionSatisfied(condition, timestamp); default: @@ -92,7 +98,8 @@ namespace MediaBrowser.Model.Dlna public bool IsVideoAudioConditionSatisfied(ProfileCondition condition, int? audioChannels, int? audioBitrate, - string audioProfile) + string audioProfile, + bool? isSecondaryTrack) { switch (condition.Property) { @@ -102,6 +109,8 @@ namespace MediaBrowser.Model.Dlna return IsConditionSatisfied(condition, audioBitrate); case ProfileConditionValue.AudioChannels: return IsConditionSatisfied(condition, audioChannels); + case ProfileConditionValue.IsSecondaryAudio: + return IsConditionSatisfied(condition, isSecondaryTrack); default: throw new ArgumentException("Unexpected condition on audio file: " + condition.Property); } diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index a3eeecff2f..8161f1c268 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -117,7 +117,9 @@ namespace MediaBrowser.Model.Dlna TranscodeSeekInfo transcodeSeekInfo, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo); @@ -158,7 +160,9 @@ namespace MediaBrowser.Model.Dlna timestamp, isAnamorphic, isCabac, - refFrames); + refFrames, + numVideoStreams, + numAudioStreams); List<string> orgPnValues = new List<string>(); diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 4b137a268c..8b9b2edf36 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -281,7 +281,9 @@ namespace MediaBrowser.Model.Dlna TransportStreamTimestamp timestamp, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { container = StringHelper.TrimStart((container ?? string.Empty), '.'); @@ -315,7 +317,7 @@ namespace MediaBrowser.Model.Dlna var anyOff = false; foreach (ProfileCondition c in i.Conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { anyOff = true; break; diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs index ae6dc74c8c..7563ffb5af 100644 --- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs +++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs @@ -17,6 +17,9 @@ VideoTimestamp = 12, IsAnamorphic = 13, RefFrames = 14, - IsCabac = 15 + IsCabac = 15, + NumAudioStreams = 16, + NumVideoStreams = 17, + IsSecondaryAudio } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 3f88f9ac06..bc9f07d04a 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -89,38 +89,14 @@ namespace MediaBrowser.Model.Dlna private StreamInfo GetOptimalStream(List<StreamInfo> streams) { - // Grab the first one that can be direct streamed - // If that doesn't produce anything, just take the first - foreach (StreamInfo i in streams) - { - if (i.PlayMethod == PlayMethod.DirectPlay && i.MediaSource.Protocol == MediaProtocol.File) - { - return i; - } - } - foreach (StreamInfo i in streams) - { - if (i.PlayMethod == PlayMethod.DirectPlay) - { - return i; - } - } - foreach (StreamInfo i in streams) - { - if (i.PlayMethod == PlayMethod.DirectStream) - { - return i; - } - } + streams = StreamInfoSorter.SortMediaSources(streams); foreach (StreamInfo stream in streams) { return stream; } - PlaybackException error = new PlaybackException(); - error.ErrorCode = PlaybackErrorCode.NoCompatibleStream; - throw error; + return null; } private StreamInfo BuildAudioItem(MediaSourceInfo item, AudioOptions options) @@ -221,7 +197,7 @@ namespace MediaBrowser.Model.Dlna playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; playlistItem.Container = transcodingProfile.Container; playlistItem.AudioCodec = transcodingProfile.AudioCodec; - playlistItem.Protocol = transcodingProfile.Protocol; + playlistItem.SubProtocol = transcodingProfile.Protocol; List<CodecProfile> audioCodecProfiles = new List<CodecProfile>(); foreach (CodecProfile i in options.Profile.CodecProfiles) @@ -263,6 +239,16 @@ namespace MediaBrowser.Model.Dlna return playlistItem; } + private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options) + { + if (item.Protocol == MediaProtocol.File) + { + return options.Profile.MaxStaticBitrate; + } + + return options.GetMaxBitrate(); + } + private List<PlayMethod> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options) { DirectPlayProfile directPlayProfile = null; @@ -287,7 +273,7 @@ namespace MediaBrowser.Model.Dlna // The profile describes what the device supports // If device requirements are satisfied then allow both direct stream and direct play - if (IsAudioEligibleForDirectPlay(item, options.Profile.MaxStaticBitrate)) + if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options))) { playMethods.Add(PlayMethod.DirectPlay); } @@ -296,6 +282,49 @@ namespace MediaBrowser.Model.Dlna return playMethods; } + private int? GetDefaultSubtitleStreamIndex(MediaSourceInfo item, SubtitleProfile[] subtitleProfiles) + { + int highestScore = -1; + + foreach (MediaStream stream in item.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue) + { + if (stream.Score.Value > highestScore) + { + highestScore = stream.Score.Value; + } + } + } + + List<MediaStream> topStreams = new List<MediaStream>(); + foreach (MediaStream stream in item.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue && stream.Score.Value == highestScore) + { + topStreams.Add(stream); + } + } + + // If multiple streams have an equal score, try to pick the most efficient one + if (topStreams.Count > 1) + { + foreach (MediaStream stream in topStreams) + { + foreach (SubtitleProfile profile in subtitleProfiles) + { + if (profile.Method == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.Format, stream.Codec)) + { + return stream.Index; + } + } + } + } + + // If no optimization panned out, just use the original default + return item.DefaultSubtitleStreamIndex; + } + private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { StreamInfo playlistItem = new StreamInfo @@ -308,16 +337,20 @@ namespace MediaBrowser.Model.Dlna DeviceProfile = options.Profile }; - playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? item.DefaultSubtitleStreamIndex; + playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles); MediaStream subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null; MediaStream audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex); - int? audioStreamIndex = audioStream == null ? (int?)null : audioStream.Index; + int? audioStreamIndex = null; + if (audioStream != null) + { + audioStreamIndex = audioStream.Index; + } MediaStream videoStream = item.VideoStream; // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough - bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, options.Profile.MaxStaticBitrate, subtitleStream, options); + bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options); bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options); if (isEligibleForDirectPlay || isEligibleForDirectStream) @@ -374,7 +407,7 @@ namespace MediaBrowser.Model.Dlna playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',')[0]; playlistItem.VideoCodec = transcodingProfile.VideoCodec; - playlistItem.Protocol = transcodingProfile.Protocol; + playlistItem.SubProtocol = transcodingProfile.Protocol; playlistItem.AudioStreamIndex = audioStreamIndex; List<ProfileCondition> videoTranscodingConditions = new List<ProfileCondition>(); @@ -509,10 +542,13 @@ namespace MediaBrowser.Model.Dlna int? packetLength = videoStream == null ? null : videoStream.PacketLength; int? refFrames = videoStream == null ? null : videoStream.RefFrames; + int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio); + int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video); + // Check container conditions foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { return null; } @@ -539,7 +575,7 @@ namespace MediaBrowser.Model.Dlna foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { return null; } @@ -568,7 +604,8 @@ namespace MediaBrowser.Model.Dlna foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile)) + bool? isSecondaryAudio = audioStream == null ? null : mediaSource.IsSecondaryAudio(audioStream); + if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile, isSecondaryAudio)) { return null; } @@ -628,8 +665,20 @@ namespace MediaBrowser.Model.Dlna // Look for an external profile that matches the stream type (text/graphical) foreach (SubtitleProfile profile in subtitleProfiles) { + bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); + + if (!profile.SupportsLanguage(subtitleStream.Language)) + { + continue; + } + if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { + if (!requiresConversion) + { + return profile; + } + if (subtitleStream.SupportsExternalStream) { return profile; @@ -645,8 +694,20 @@ namespace MediaBrowser.Model.Dlna foreach (SubtitleProfile profile in subtitleProfiles) { + bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); + + if (!profile.SupportsLanguage(subtitleStream.Language)) + { + continue; + } + if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { + if (!requiresConversion) + { + return profile; + } + return profile; } } @@ -756,6 +817,9 @@ namespace MediaBrowser.Model.Dlna case ProfileConditionValue.AudioProfile: case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.PacketLength: + case ProfileConditionValue.NumAudioStreams: + case ProfileConditionValue.NumVideoStreams: + case ProfileConditionValue.IsSecondaryAudio: case ProfileConditionValue.VideoTimestamp: { // Not supported yet diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 4eb1c0a8ef..a908c78506 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Model.Dlna public string Container { get; set; } - public string Protocol { get; set; } + public string SubProtocol { get; set; } public long StartPositionTicks { get; set; } @@ -51,7 +51,7 @@ namespace MediaBrowser.Model.Dlna public int? MaxVideoBitDepth { get; set; } public int? MaxRefFrames { get; set; } - + public float? MaxFramerate { get; set; } public DeviceProfile DeviceProfile { get; set; } @@ -69,7 +69,7 @@ namespace MediaBrowser.Model.Dlna public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; } public string SubtitleFormat { get; set; } - public LiveMediaInfoResult PlaybackInfo { get; set; } + public string PlaySessionId { get; set; } public string MediaSourceId { @@ -81,7 +81,8 @@ namespace MediaBrowser.Model.Dlna public bool IsDirectStream { - get { + get + { return PlayMethod == PlayMethod.DirectStream || PlayMethod == PlayMethod.DirectPlay; } @@ -89,7 +90,47 @@ namespace MediaBrowser.Model.Dlna public string ToUrl(string baseUrl, string accessToken) { - return ToDlnaUrl(baseUrl, accessToken); + if (PlayMethod == PlayMethod.DirectPlay) + { + return MediaSource.Path; + } + + if (string.IsNullOrEmpty(baseUrl)) + { + throw new ArgumentNullException(baseUrl); + } + + List<string> list = new List<string>(); + foreach (NameValuePair pair in BuildParams(this, accessToken)) + { + if (string.IsNullOrEmpty(pair.Value)) + { + continue; + } + + // Try to keep the url clean by omitting defaults + if (StringHelper.EqualsIgnoreCase(pair.Name, "StartTimeTicks") && + StringHelper.EqualsIgnoreCase(pair.Value, "0")) + { + continue; + } + if (StringHelper.EqualsIgnoreCase(pair.Name, "SubtitleStreamIndex") && + StringHelper.EqualsIgnoreCase(pair.Value, "-1")) + { + continue; + } + if (StringHelper.EqualsIgnoreCase(pair.Name, "Static") && + StringHelper.EqualsIgnoreCase(pair.Value, "false")) + { + continue; + } + + list.Add(string.Format("{0}={1}", pair.Name, pair.Value)); + } + + string queryString = string.Join("&", list.ToArray()); + + return GetUrl(baseUrl, queryString); } public string ToDlnaUrl(string baseUrl, string accessToken) @@ -99,115 +140,124 @@ namespace MediaBrowser.Model.Dlna return MediaSource.Path; } + string dlnaCommand = BuildDlnaParam(this, accessToken); + return GetUrl(baseUrl, dlnaCommand); + } + + private string GetUrl(string baseUrl, string queryString) + { if (string.IsNullOrEmpty(baseUrl)) { throw new ArgumentNullException(baseUrl); } - string dlnaCommand = BuildDlnaParam(this); - string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container; baseUrl = baseUrl.TrimEnd('/'); if (MediaType == DlnaProfileType.Audio) { - return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, dlnaCommand); + return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } - if (StringHelper.EqualsIgnoreCase(Protocol, "hls")) + if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls")) { - return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, dlnaCommand); + return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); } - return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, dlnaCommand); + return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } - private static string BuildDlnaParam(StreamInfo item) + private static string BuildDlnaParam(StreamInfo item, string accessToken) { - List<string> list = new List<string> - { - item.DeviceProfileId ?? string.Empty, - item.DeviceId ?? string.Empty, - item.MediaSourceId ?? string.Empty, - (item.IsDirectStream).ToString().ToLower(), - item.VideoCodec ?? string.Empty, - item.AudioCodec ?? string.Empty, - item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty, - item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty, - item.VideoBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoBitrate.Value) : string.Empty, - item.AudioBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioBitrate.Value) : string.Empty, - item.MaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxAudioChannels.Value) : string.Empty, - item.MaxFramerate.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxFramerate.Value) : string.Empty, - item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty, - item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty, - StringHelper.ToStringCultureInvariant(item.StartPositionTicks), - item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty - }; + List<string> list = new List<string>(); - list.Add(item.IsDirectStream ? string.Empty : DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture)); - list.Add(item.MaxRefFrames.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxRefFrames.Value) : string.Empty); - list.Add(item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty); - list.Add(item.VideoProfile ?? string.Empty); - list.Add(item.Cabac.HasValue ? item.Cabac.Value.ToString() : string.Empty); + foreach (NameValuePair pair in BuildParams(item, accessToken)) + { + list.Add(pair.Value); + } - string streamId = item.PlaybackInfo == null ? null : item.PlaybackInfo.StreamId; - list.Add(streamId ?? string.Empty); - return string.Format("Params={0}", string.Join(";", list.ToArray())); } - public List<SubtitleStreamInfo> GetExternalSubtitles(bool includeSelectedTrackOnly) + private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken) { - List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>(); + List<NameValuePair> list = new List<NameValuePair>(); - // First add the selected track - if (SubtitleStreamIndex.HasValue) - { - foreach (MediaStream stream in MediaSource.MediaStreams) - { - if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) - { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); + list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty)); + list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty)); + list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty)); + list.Add(new NameValuePair("Static", (item.IsDirectStream).ToString().ToLower())); + list.Add(new NameValuePair("VideoCodec", item.VideoCodec ?? string.Empty)); + list.Add(new NameValuePair("AudioCodec", item.AudioCodec ?? string.Empty)); + list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty)); + list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty)); + list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoBitrate.Value) : string.Empty)); + list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioBitrate.Value) : string.Empty)); + list.Add(new NameValuePair("MaxAudioChannels", item.MaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxAudioChannels.Value) : string.Empty)); + list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxFramerate.Value) : string.Empty)); + list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty)); + list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty)); - if (info != null) - { - list.Add(info); - } - } - } + if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls")) + { + list.Add(new NameValuePair("StartTimeTicks", string.Empty)); } - - if (!includeSelectedTrackOnly) + else { - foreach (MediaStream stream in MediaSource.MediaStreams) - { - if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) - { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); - - if (info != null) - { - list.Add(info); - } - } - } + list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(item.StartPositionTicks))); } + list.Add(new NameValuePair("Level", item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty)); + + list.Add(new NameValuePair("ClientTime", item.IsDirectStream ? string.Empty : DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture))); + list.Add(new NameValuePair("MaxRefFrames", item.MaxRefFrames.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxRefFrames.Value) : string.Empty)); + list.Add(new NameValuePair("MaxVideoBitDepth", item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty)); + list.Add(new NameValuePair("Profile", item.VideoProfile ?? string.Empty)); + list.Add(new NameValuePair("Cabac", item.Cabac.HasValue ? item.Cabac.Value.ToString() : string.Empty)); + + list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); + list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); + + string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId; + list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); + return list; } - public List<SubtitleStreamInfo> GetExternalSubtitles(string baseUrl, string accessToken, bool includeSelectedTrackOnly) + public List<SubtitleStreamInfo> GetExternalSubtitles(bool includeSelectedTrackOnly, string baseUrl, string accessToken) { - if (string.IsNullOrEmpty(baseUrl)) + return GetExternalSubtitles(includeSelectedTrackOnly, false, baseUrl, accessToken); + } + + public List<SubtitleStreamInfo> GetExternalSubtitles(bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) + { + List<SubtitleStreamInfo> list = GetSubtitleProfiles(includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken); + List<SubtitleStreamInfo> newList = new List<SubtitleStreamInfo>(); + + // First add the selected track + foreach (SubtitleStreamInfo stream in list) { - throw new ArgumentNullException(baseUrl); + if (stream.DeliveryMethod == SubtitleDeliveryMethod.External) + { + newList.Add(stream); + } } + return newList; + } + + public List<SubtitleStreamInfo> GetSubtitleProfiles(bool includeSelectedTrackOnly, string baseUrl, string accessToken) + { + return GetSubtitleProfiles(includeSelectedTrackOnly, false, baseUrl, accessToken); + } + + public List<SubtitleStreamInfo> GetSubtitleProfiles(bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) + { List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>(); // HLS will preserve timestamps so we can just grab the full subtitle stream - long startPositionTicks = StringHelper.EqualsIgnoreCase(Protocol, "hls") + long startPositionTicks = StringHelper.EqualsIgnoreCase(SubProtocol, "hls") ? 0 : StartPositionTicks; @@ -218,12 +268,7 @@ namespace MediaBrowser.Model.Dlna { if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks); - - if (info != null) - { - list.Add(info); - } + AddSubtitleProfiles(list, stream, enableAllProfiles, baseUrl, accessToken, startPositionTicks); } } } @@ -234,54 +279,68 @@ namespace MediaBrowser.Model.Dlna { if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks); - - if (info != null) - { - list.Add(info); - } + AddSubtitleProfiles(list, stream, enableAllProfiles, baseUrl, accessToken, startPositionTicks); } } } - + return list; } - private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks) + private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks) { - SubtitleStreamInfo info = GetSubtitleStreamInfo(stream); - - if (info != null) + if (enableAllProfiles) { - info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", - baseUrl, - ItemId, - MediaSourceId, - StringHelper.ToStringCultureInvariant(stream.Index), - StringHelper.ToStringCultureInvariant(startPositionTicks), - SubtitleFormat); + foreach (SubtitleProfile profile in DeviceProfile.SubtitleProfiles) + { + SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }); + + list.Add(info); + } } + else + { + SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles); - return info; + list.Add(info); + } } - private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream) + private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles) { - SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile.SubtitleProfiles, Context); - - if (subtitleProfile.Method != SubtitleDeliveryMethod.External) - { - return null; - } - - return new SubtitleStreamInfo + SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, Context); + SubtitleStreamInfo info = new SubtitleStreamInfo { IsForced = stream.IsForced, Language = stream.Language, Name = stream.Language ?? "Unknown", - Format = SubtitleFormat, - Index = stream.Index + Format = subtitleProfile.Format, + Index = stream.Index, + DeliveryMethod = subtitleProfile.Method }; + + if (info.DeliveryMethod == SubtitleDeliveryMethod.External) + { + if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format)) + { + info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", + baseUrl, + ItemId, + MediaSourceId, + StringHelper.ToStringCultureInvariant(stream.Index), + StringHelper.ToStringCultureInvariant(startPositionTicks), + subtitleProfile.Format); + + info.IsExternalUrl = false; + } + else + { + info.Url = stream.Path; + info.IsExternalUrl = true; + } + } + + return info; } /// <summary> @@ -613,6 +672,42 @@ namespace MediaBrowser.Model.Dlna } } + public int? TargetVideoStreamCount + { + get + { + if (IsDirectStream) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (IsDirectStream) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + public List<MediaStream> GetSelectableAudioStreams() { return GetSelectableStreams(MediaStreamType.Audio); diff --git a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs new file mode 100644 index 0000000000..0cccd80804 --- /dev/null +++ b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs @@ -0,0 +1,47 @@ +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Session; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Model.Dlna +{ + public class StreamInfoSorter + { + public static List<StreamInfo> SortMediaSources(List<StreamInfo> streams) + { + return streams.OrderBy(i => + { + // Nothing beats direct playing a file + if (i.PlayMethod == PlayMethod.DirectPlay && i.MediaSource.Protocol == MediaProtocol.File) + { + return 0; + } + + return 1; + + }).ThenBy(i => + { + switch (i.PlayMethod) + { + // Let's assume direct streaming a file is just as desirable as direct playing a remote url + case PlayMethod.DirectStream: + case PlayMethod.DirectPlay: + return 0; + default: + return 1; + } + + }).ThenBy(i => + { + switch (i.MediaSource.Protocol) + { + case MediaProtocol.File: + return 0; + default: + return 1; + } + + }).ToList(); + } + } +} diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs index d3989829ca..1795c374a4 100644 --- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs +++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs @@ -1,4 +1,6 @@ -using System.Xml.Serialization; +using MediaBrowser.Model.Extensions; +using System.Collections.Generic; +using System.Xml.Serialization; namespace MediaBrowser.Model.Dlna { @@ -13,5 +15,28 @@ namespace MediaBrowser.Model.Dlna [XmlAttribute("didlMode")] public string DidlMode { get; set; } + [XmlAttribute("language")] + public string Language { get; set; } + + public List<string> GetLanguages() + { + List<string> list = new List<string>(); + foreach (string i in (Language ?? string.Empty).Split(',')) + { + if (!string.IsNullOrEmpty(i)) list.Add(i); + } + return list; + } + + public bool SupportsLanguage(string language) + { + if (string.IsNullOrEmpty(language)) + { + language = "und"; + } + + List<string> languages = GetLanguages(); + return languages.Count == 0 || ListHelper.ContainsIgnoreCase(languages, language); + } } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs index a7a8da3ba2..61b2895fc1 100644 --- a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs +++ b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs @@ -8,5 +8,7 @@ namespace MediaBrowser.Model.Dlna public bool IsForced { get; set; } public string Format { get; set; } public int Index { get; set; } + public SubtitleDeliveryMethod DeliveryMethod { get; set; } + public bool IsExternalUrl { get; set; } } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 5495a1f692..8897edcbdc 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -24,6 +24,13 @@ namespace MediaBrowser.Model.Dto public bool ReadAtNativeFramerate { get; set; } public bool SupportsTranscoding { get; set; } public bool SupportsDirectStream { get; set; } + public bool SupportsDirectPlay { get; set; } + + public bool RequiresOpening { get; set; } + public string OpenToken { get; set; } + public bool RequiresClosing { get; set; } + public string LiveStreamId { get; set; } + public int? BufferMs { get; set; } public VideoType? VideoType { get; set; } @@ -41,6 +48,10 @@ namespace MediaBrowser.Model.Dto public TransportStreamTimestamp? Timestamp { get; set; } public Dictionary<string, string> RequiredHttpHeaders { get; set; } + public string TranscodingUrl { get; set; } + public string TranscodingSubProtocol { get; set; } + public string TranscodingContainer { get; set; } + public MediaSourceInfo() { Formats = new List<string>(); @@ -49,6 +60,7 @@ namespace MediaBrowser.Model.Dto PlayableStreamFileNames = new List<string>(); SupportsTranscoding = true; SupportsDirectStream = true; + SupportsDirectPlay = true; } public int? DefaultAudioStreamIndex { get; set; } @@ -123,5 +135,40 @@ namespace MediaBrowser.Model.Dto return null; } + + public int? GetStreamCount(MediaStreamType type) + { + int numMatches = 0; + int numStreams = 0; + + foreach (MediaStream i in MediaStreams) + { + numStreams++; + if (i.Type == type) + { + numMatches++; + } + } + + if (numStreams == 0) + { + return null; + } + + return numMatches; + } + + public bool? IsSecondaryAudio(MediaStream stream) + { + foreach (MediaStream currentStream in MediaStreams) + { + if (currentStream.Type == MediaStreamType.Audio) + { + return currentStream.Index != stream.Index; + } + } + + return null; + } } } diff --git a/MediaBrowser.Model/Dto/MediaSourceType.cs b/MediaBrowser.Model/Dto/MediaSourceType.cs index a9cd71df56..e049785025 100644 --- a/MediaBrowser.Model/Dto/MediaSourceType.cs +++ b/MediaBrowser.Model/Dto/MediaSourceType.cs @@ -4,6 +4,6 @@ namespace MediaBrowser.Model.Dto { Default = 0, Grouping = 1, - Cache = 2 + Placeholder = 2 } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Dto/NameValuePair.cs b/MediaBrowser.Model/Dto/NameValuePair.cs index 2d55e8f2a1..a6e6879491 100644 --- a/MediaBrowser.Model/Dto/NameValuePair.cs +++ b/MediaBrowser.Model/Dto/NameValuePair.cs @@ -3,6 +3,17 @@ namespace MediaBrowser.Model.Dto { public class NameValuePair { + public NameValuePair() + { + + } + + public NameValuePair(string name, string value) + { + Name = name; + Value = value; + } + /// <summary> /// Gets or sets the name. /// </summary> diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 4af32bb500..0f3435174c 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Extensions; using System.Diagnostics; namespace MediaBrowser.Model.Entities @@ -130,17 +131,44 @@ namespace MediaBrowser.Model.Entities public int Index { get; set; } /// <summary> + /// Gets or sets the score. + /// </summary> + /// <value>The score.</value> + public int? Score { get; set; } + + /// <summary> /// Gets or sets a value indicating whether this instance is external. /// </summary> /// <value><c>true</c> if this instance is external; otherwise, <c>false</c>.</value> public bool IsExternal { get; set; } + /// <summary> + /// Gets or sets the method. + /// </summary> + /// <value>The method.</value> + public SubtitleDeliveryMethod? DeliveryMethod { get; set; } + /// <summary> + /// Gets or sets the delivery URL. + /// </summary> + /// <value>The delivery URL.</value> + public string DeliveryUrl { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is external URL. + /// </summary> + /// <value><c>null</c> if [is external URL] contains no value, <c>true</c> if [is external URL]; otherwise, <c>false</c>.</value> + public bool? IsExternalUrl { get; set; } + public bool IsTextSubtitleStream { get { if (Type != MediaStreamType.Subtitle) return false; + if (string.IsNullOrEmpty(Codec) && !IsExternal) + { + return false; + } + return IsTextFormat(Codec); } } @@ -169,6 +197,12 @@ namespace MediaBrowser.Model.Entities public string Path { get; set; } /// <summary> + /// Gets or sets the external identifier. + /// </summary> + /// <value>The external identifier.</value> + public string ExternalId { get; set; } + + /// <summary> /// Gets or sets the pixel format. /// </summary> /// <value>The pixel format.</value> diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index bbd396c33f..c19ba54bd1 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -54,6 +54,12 @@ namespace MediaBrowser.Model.LiveTv public bool? IsMovie { get; set; } /// <summary> + /// Gets or sets a value indicating whether this instance is sports. + /// </summary> + /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value> + public bool? IsSports { get; set; } + + /// <summary> /// Skips over a given number of items within the results. Use for paging. /// </summary> public int? StartIndex { get; set; } diff --git a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs index 9ba8e0e5fc..4a8ae2365b 100644 --- a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs @@ -31,5 +31,10 @@ /// </summary> /// <value><c>null</c> if [is movie] contains no value, <c>true</c> if [is movie]; otherwise, <c>false</c>.</value> public bool? IsMovie { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is sports. + /// </summary> + /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value> + public bool? IsSports { get; set; } } }
\ No newline at end of file diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 3a6eda6207..0673095125 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -126,6 +126,7 @@ <Compile Include="Devices\DevicesOptions.cs" /> <Compile Include="Dlna\EncodingContext.cs" /> <Compile Include="Dlna\ILocalPlayer.cs" /> + <Compile Include="Dlna\StreamInfoSorter.cs" /> <Compile Include="Dlna\NullLocalPlayer.cs" /> <Compile Include="Dlna\PlaybackErrorCode.cs" /> <Compile Include="Dlna\PlaybackException.cs" /> @@ -140,7 +141,10 @@ <Compile Include="Dto\MetadataEditorInfo.cs" /> <Compile Include="Dto\NameIdPair.cs" /> <Compile Include="Dto\NameValuePair.cs" /> - <Compile Include="MediaInfo\LiveMediaInfoResult.cs" /> + <Compile Include="MediaInfo\LiveStreamRequest.cs" /> + <Compile Include="MediaInfo\LiveStreamResponse.cs" /> + <Compile Include="MediaInfo\PlaybackInfoRequest.cs" /> + <Compile Include="MediaInfo\PlaybackInfoResponse.cs" /> <Compile Include="Dto\MediaSourceType.cs" /> <Compile Include="Configuration\DynamicDayOfWeek.cs" /> <Compile Include="Entities\ExtraType.cs" /> diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs new file mode 100644 index 0000000000..3affbbcc34 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -0,0 +1,36 @@ +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.MediaInfo +{ + public class LiveStreamRequest + { + public string OpenToken { get; set; } + public string UserId { get; set; } + public string PlaySessionId { get; set; } + public int? MaxStreamingBitrate { get; set; } + public long? StartTimeTicks { get; set; } + public int? AudioStreamIndex { get; set; } + public int? SubtitleStreamIndex { get; set; } + public string ItemId { get; set; } + public DeviceProfile DeviceProfile { get; set; } + + public LiveStreamRequest() + { + + } + + public LiveStreamRequest(AudioOptions options) + { + MaxStreamingBitrate = options.MaxBitrate; + ItemId = options.ItemId; + DeviceProfile = options.Profile; + + VideoOptions videoOptions = options as VideoOptions; + if (videoOptions != null) + { + AudioStreamIndex = videoOptions.AudioStreamIndex; + SubtitleStreamIndex = videoOptions.SubtitleStreamIndex; + } + } + } +} diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs new file mode 100644 index 0000000000..e79e37a717 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs @@ -0,0 +1,9 @@ +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Model.MediaInfo +{ + public class LiveStreamResponse + { + public MediaSourceInfo MediaSource { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs new file mode 100644 index 0000000000..124739073a --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs @@ -0,0 +1,25 @@ +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.MediaInfo +{ + public class PlaybackInfoRequest + { + public string Id { get; set; } + + public string UserId { get; set; } + + public int? MaxStreamingBitrate { get; set; } + + public long? StartTimeTicks { get; set; } + + public int? AudioStreamIndex { get; set; } + + public int? SubtitleStreamIndex { get; set; } + + public string MediaSourceId { get; set; } + + public string LiveStreamId { get; set; } + + public DeviceProfile DeviceProfile { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaInfo/LiveMediaInfoResult.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs index 16f4f76ee4..1f8936d018 100644 --- a/MediaBrowser.Model/MediaInfo/LiveMediaInfoResult.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace MediaBrowser.Model.MediaInfo { - public class LiveMediaInfoResult + public class PlaybackInfoResponse { /// <summary> /// Gets or sets the media sources. @@ -13,10 +13,10 @@ namespace MediaBrowser.Model.MediaInfo public List<MediaSourceInfo> MediaSources { get; set; } /// <summary> - /// Gets or sets the stream identifier. + /// Gets or sets the play session identifier. /// </summary> - /// <value>The stream identifier.</value> - public string StreamId { get; set; } + /// <value>The play session identifier.</value> + public string PlaySessionId { get; set; } /// <summary> /// Gets or sets the error code. @@ -24,7 +24,7 @@ namespace MediaBrowser.Model.MediaInfo /// <value>The error code.</value> public PlaybackErrorCode? ErrorCode { get; set; } - public LiveMediaInfoResult() + public PlaybackInfoResponse() { MediaSources = new List<MediaSourceInfo>(); } diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs index e57955c9e2..683f1a76c8 100644 --- a/MediaBrowser.Model/Notifications/NotificationOptions.cs +++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs @@ -1,5 +1,6 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Users; namespace MediaBrowser.Model.Notifications { @@ -106,7 +107,7 @@ namespace MediaBrowser.Model.Notifications !ListHelper.ContainsIgnoreCase(opt.DisabledMonitorUsers, userId); } - public bool IsEnabledToSendToUser(string type, string userId, UserConfiguration userConfig) + public bool IsEnabledToSendToUser(string type, string userId, UserPolicy userPolicy) { NotificationOption opt = GetOptions(type); @@ -117,7 +118,7 @@ namespace MediaBrowser.Model.Notifications return true; } - if (opt.SendToUserMode == SendToUserType.Admins && userConfig.IsAdministrator) + if (opt.SendToUserMode == SendToUserType.Admins && userPolicy.IsAdministrator) { return true; } diff --git a/MediaBrowser.Model/Querying/ItemQuery.cs b/MediaBrowser.Model/Querying/ItemQuery.cs index 0cdf5ca7ab..5a88c0d43e 100644 --- a/MediaBrowser.Model/Querying/ItemQuery.cs +++ b/MediaBrowser.Model/Querying/ItemQuery.cs @@ -281,6 +281,13 @@ namespace MediaBrowser.Model.Querying public int? ImageTypeLimit { get; set; } public ImageType[] EnableImageTypes { get; set; } + [Obsolete] + public string[] Artists { get; set; } + [Obsolete] + public string[] Studios { get; set; } + [Obsolete] + public string Person { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="ItemQuery" /> class. /// </summary> @@ -299,6 +306,9 @@ namespace MediaBrowser.Model.Querying VideoTypes = new VideoType[] { }; + Artists = new string[] { }; + Studios = new string[] { }; + Genres = new string[] { }; StudioIds = new string[] { }; IncludeItemTypes = new string[] { }; diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs index fcc7e39a19..9c2926b542 100644 --- a/MediaBrowser.Model/Querying/ItemSortBy.cs +++ b/MediaBrowser.Model/Querying/ItemSortBy.cs @@ -43,6 +43,7 @@ namespace MediaBrowser.Model.Querying /// The premiere date /// </summary> public const string PremiereDate = "PremiereDate"; + public const string StartDate = "StartDate"; /// <summary> /// The sort name /// </summary> diff --git a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs index f04dea1eac..a7ca09c15a 100644 --- a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs +++ b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs @@ -78,5 +78,15 @@ namespace MediaBrowser.Model.Session /// </summary> /// <value>The play method.</value> public PlayMethod PlayMethod { get; set; } + /// <summary> + /// Gets or sets the live stream identifier. + /// </summary> + /// <value>The live stream identifier.</value> + public string LiveStreamId { get; set; } + /// <summary> + /// Gets or sets the play session identifier. + /// </summary> + /// <value>The play session identifier.</value> + public string PlaySessionId { get; set; } } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/PlaybackStopInfo.cs b/MediaBrowser.Model/Session/PlaybackStopInfo.cs index 38025f1832..80d719f037 100644 --- a/MediaBrowser.Model/Session/PlaybackStopInfo.cs +++ b/MediaBrowser.Model/Session/PlaybackStopInfo.cs @@ -36,5 +36,15 @@ namespace MediaBrowser.Model.Session /// </summary> /// <value>The position ticks.</value> public long? PositionTicks { get; set; } + /// <summary> + /// Gets or sets the live stream identifier. + /// </summary> + /// <value>The live stream identifier.</value> + public string LiveStreamId { get; set; } + /// <summary> + /// Gets or sets the play session identifier. + /// </summary> + /// <value>The play session identifier.</value> + public string PlaySessionId { get; set; } } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Sync/LocalItem.cs b/MediaBrowser.Model/Sync/LocalItem.cs index c6a10298fe..dbbecaf057 100644 --- a/MediaBrowser.Model/Sync/LocalItem.cs +++ b/MediaBrowser.Model/Sync/LocalItem.cs @@ -31,13 +31,24 @@ namespace MediaBrowser.Model.Sync /// <value>The item identifier.</value> public string ItemId { get; set; } /// <summary> + /// Gets or sets the synchronize job item identifier. + /// </summary> + /// <value>The synchronize job item identifier.</value> + public string SyncJobItemId { get; set; } + /// <summary> /// Gets or sets the user ids with access. /// </summary> /// <value>The user ids with access.</value> public List<string> UserIdsWithAccess { get; set; } + /// <summary> + /// Gets or sets the additional files. + /// </summary> + /// <value>The additional files.</value> + public List<string> AdditionalFiles { get; set; } public LocalItem() { + AdditionalFiles = new List<string>(); UserIdsWithAccess = new List<string>(); } } diff --git a/MediaBrowser.Model/Sync/SyncDataRequest.cs b/MediaBrowser.Model/Sync/SyncDataRequest.cs index dc33239a04..0df4de86d1 100644 --- a/MediaBrowser.Model/Sync/SyncDataRequest.cs +++ b/MediaBrowser.Model/Sync/SyncDataRequest.cs @@ -6,6 +6,7 @@ namespace MediaBrowser.Model.Sync { public List<string> LocalItemIds { get; set; } public List<string> OfflineUserIds { get; set; } + public List<string> SyncJobItemIds { get; set; } public string TargetId { get; set; } diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 7efc2cf6f6..640f03e2a0 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -32,8 +32,6 @@ namespace MediaBrowser.Model.Users public bool EnableUserPreferenceAccess { get; set; } public AccessSchedule[] AccessSchedules { get; set; } public UnratedItem[] BlockUnratedItems { get; set; } - public string[] BlockedMediaFolders { get; set; } - public string[] BlockedChannels { get; set; } public bool EnableRemoteControlOfOtherUsers { get; set; } public bool EnableSharedDeviceControl { get; set; } @@ -63,6 +61,7 @@ namespace MediaBrowser.Model.Users public UserPolicy() { + EnableSync = true; EnableLiveTvManagement = true; EnableMediaPlayback = true; EnableLiveTvAccess = true; |
