aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Model')
-rw-r--r--MediaBrowser.Model/ApiClient/IApiClient.cs12
-rw-r--r--MediaBrowser.Model/ApiClient/IConnectionManager.cs6
-rw-r--r--MediaBrowser.Model/ApiClient/ServerCredentials.cs21
-rw-r--r--MediaBrowser.Model/ApiClient/ServerInfo.cs8
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs4
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs5
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs9
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs13
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs8
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs6
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionValue.cs5
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs134
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs313
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfoSorter.cs47
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleProfile.cs27
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs2
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs47
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceType.cs2
-rw-r--r--MediaBrowser.Model/Dto/NameValuePair.cs11
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs36
-rw-r--r--MediaBrowser.Model/LiveTv/ProgramQuery.cs6
-rw-r--r--MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs5
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj6
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs36
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs9
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs25
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs (renamed from MediaBrowser.Model/MediaInfo/LiveMediaInfoResult.cs)10
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOptions.cs5
-rw-r--r--MediaBrowser.Model/Querying/ItemQuery.cs10
-rw-r--r--MediaBrowser.Model/Querying/ItemSortBy.cs1
-rw-r--r--MediaBrowser.Model/Session/PlaybackProgressInfo.cs10
-rw-r--r--MediaBrowser.Model/Session/PlaybackStopInfo.cs10
-rw-r--r--MediaBrowser.Model/Sync/LocalItem.cs11
-rw-r--r--MediaBrowser.Model/Sync/SyncDataRequest.cs1
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs3
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&lt;LiveMediaInfoResult&gt;.</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&lt;List&lt;RecommendationDto&gt;&gt;.</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&lt;LiveStreamResponse&gt;.</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&lt;List&lt;UserDto&gt;&gt;.</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;