aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs2
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs4
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs3
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs18
-rw-r--r--MediaBrowser.Api/UserLibrary/YearsService.cs6
-rw-r--r--MediaBrowser.Api/VideosService.cs36
-rw-r--r--MediaBrowser.Controller/Dlna/CodecProfile.cs56
-rw-r--r--MediaBrowser.Controller/Dlna/DeviceIdentification.cs7
-rw-r--r--MediaBrowser.Controller/Dlna/DeviceProfile.cs7
-rw-r--r--MediaBrowser.Controller/Dlna/DirectPlayProfile.cs90
-rw-r--r--MediaBrowser.Controller/Dlna/MediaProfile.cs26
-rw-r--r--MediaBrowser.Controller/Dlna/TranscodingProfile.cs5
-rw-r--r--MediaBrowser.Controller/Dto/IDtoService.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs4
-rw-r--r--MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs1
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj2
-rw-r--r--MediaBrowser.Controller/Session/PlaybackInfo.cs6
-rw-r--r--MediaBrowser.Controller/Session/PlaybackProgressInfo.cs6
-rw-r--r--MediaBrowser.Controller/Session/PlaybackStopInfo.cs6
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs26
-rw-r--r--MediaBrowser.Dlna/DlnaManager.cs358
-rw-r--r--MediaBrowser.Dlna/MediaBrowser.Dlna.csproj1
-rw-r--r--MediaBrowser.Dlna/PlayTo/DlnaController.cs45
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlaylistItem.cs80
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs375
-rw-r--r--MediaBrowser.Dlna/PlayTo/StreamHelper.cs26
-rw-r--r--MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj3
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj3
-rw-r--r--MediaBrowser.Model/ApiClient/IApiClient.cs21
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs4
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs4
-rw-r--r--MediaBrowser.Model/Dto/MediaVersionInfo.cs8
-rw-r--r--MediaBrowser.Model/Entities/BaseItemInfo.cs6
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj1
-rw-r--r--MediaBrowser.Model/Querying/ItemFields.cs2
-rw-r--r--MediaBrowser.Model/Session/PlaybackReports.cs56
-rw-r--r--MediaBrowser.Model/Session/SessionCapabilities.cs4
-rw-r--r--MediaBrowser.Model/Session/SessionInfoDto.cs16
-rw-r--r--MediaBrowser.Providers/Savers/XmlSaverHelpers.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs138
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs26
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs1
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs4
-rw-r--r--MediaBrowser.Server.Implementations/Library/ResolverHelper.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs8
-rw-r--r--MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs5
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs56
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs3
-rw-r--r--MediaBrowser.WebDashboard/ApiClient.js59
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj4
-rw-r--r--MediaBrowser.WebDashboard/packages.config2
52 files changed, 1254 insertions, 402 deletions
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index b15e67ffa..1e9f2f7a2 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -94,7 +94,7 @@ namespace MediaBrowser.Api
var item = _dtoService.GetItemByDtoId(request.ItemId);
var newLockData = request.LockData ?? false;
- var dontFetchMetaChanged = item.DontFetchMeta != newLockData;
+ var dontFetchMetaChanged = item.IsLocked != newLockData;
UpdateItem(request, item);
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index e1f4799f1..1001980ec 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -1308,7 +1308,9 @@ namespace MediaBrowser.Api.Playback
RequestedUrl = url
};
- var item = DtoService.GetItemByDtoId(request.Id);
+ var item = string.IsNullOrEmpty(request.MediaSourceId) ?
+ DtoService.GetItemByDtoId(request.Id) :
+ DtoService.GetItemByDtoId(request.MediaSourceId);
if (user != null && item.GetPlayAccess(user) != PlayAccess.Full)
{
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 6b0375e2d..df52e5e3e 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -15,6 +15,9 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
+ [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string MediaSourceId { get; set; }
+
[ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceId { get; set; }
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index e026aec03..c6051c02c 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -241,6 +241,9 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
+ [ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string MediaSourceId { get; set; }
+
/// <summary>
/// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
/// </summary>
@@ -277,6 +280,9 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
+ [ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string MediaSourceId { get; set; }
+
/// <summary>
/// Gets or sets the position ticks.
/// </summary>
@@ -312,6 +318,9 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
+ [ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string MediaSourceId { get; set; }
+
/// <summary>
/// Gets or sets the position ticks.
/// </summary>
@@ -736,7 +745,8 @@ namespace MediaBrowser.Api.UserLibrary
CanSeek = request.CanSeek,
Item = item,
SessionId = GetSession().Id,
- QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
+ QueueableMediaTypes = queueableMediaTypes.Split(',').ToList(),
+ MediaSourceId = request.MediaSourceId
};
_sessionManager.OnPlaybackStart(info);
@@ -758,7 +768,8 @@ namespace MediaBrowser.Api.UserLibrary
PositionTicks = request.PositionTicks,
IsMuted = request.IsMuted,
IsPaused = request.IsPaused,
- SessionId = GetSession().Id
+ SessionId = GetSession().Id,
+ MediaSourceId = request.MediaSourceId
};
var task = _sessionManager.OnPlaybackProgress(info);
@@ -782,7 +793,8 @@ namespace MediaBrowser.Api.UserLibrary
{
Item = item,
PositionTicks = request.PositionTicks,
- SessionId = session.Id
+ SessionId = session.Id,
+ MediaSourceId = request.MediaSourceId
};
var task = _sessionManager.OnPlaybackStopped(info);
diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs
index b8b0aa9e9..8a3bc12b2 100644
--- a/MediaBrowser.Api/UserLibrary/YearsService.cs
+++ b/MediaBrowser.Api/UserLibrary/YearsService.cs
@@ -14,8 +14,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <summary>
/// Class GetYears
/// </summary>
- [Route("/Years", "GET")]
- [Api(Description = "Gets all years from a given item, folder, or the entire library")]
+ [Route("/Years", "GET", Summary = "Gets all years from a given item, folder, or the entire library")]
public class GetYears : GetItemsByName
{
}
@@ -23,8 +22,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <summary>
/// Class GetYear
/// </summary>
- [Route("/Years/{Year}", "GET")]
- [Api(Description = "Gets a year")]
+ [Route("/Years/{Year}", "GET", Summary = "Gets a year")]
public class GetYear : IReturn<BaseItemDto>
{
/// <summary>
diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs
index fa4b22cea..94432871c 100644
--- a/MediaBrowser.Api/VideosService.cs
+++ b/MediaBrowser.Api/VideosService.cs
@@ -13,8 +13,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Api
{
- [Route("/Videos/{Id}/AdditionalParts", "GET")]
- [Api(Description = "Gets additional parts for a video.")]
+ [Route("/Videos/{Id}/AdditionalParts", "GET", Summary = "Gets additional parts for a video.")]
public class GetAdditionalParts : IReturn<ItemsResult>
{
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
@@ -28,16 +27,14 @@ namespace MediaBrowser.Api
public string Id { get; set; }
}
- [Route("/Videos/{Id}/AlternateVersions", "DELETE")]
- [Api(Description = "Assigns videos as alternates of antoher.")]
- public class DeleteAlternateVersions : IReturnVoid
+ [Route("/Videos/{Id}/AlternateSources", "DELETE", Summary = "Removes alternate video sources.")]
+ public class DeleteAlternateSources : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
- [Route("/Videos/MergeVersions", "POST")]
- [Api(Description = "Merges videos into a single record")]
+ [Route("/Videos/MergeVersions", "POST", Summary = "Merges videos into a single record")]
public class MergeVersions : IReturnVoid
{
[ApiMember(Name = "Ids", Description = "Item id list. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
@@ -98,14 +95,14 @@ namespace MediaBrowser.Api
return ToOptimizedSerializedResultUsingCache(result);
}
- public void Delete(DeleteAlternateVersions request)
+ public void Delete(DeleteAlternateSources request)
{
var task = RemoveAlternateVersions(request);
Task.WaitAll(task);
}
- private async Task RemoveAlternateVersions(DeleteAlternateVersions request)
+ private async Task RemoveAlternateVersions(DeleteAlternateSources request)
{
var video = (Video)_dtoService.GetItemByDtoId(request.Id);
@@ -146,7 +143,7 @@ namespace MediaBrowser.Api
var videos = items.Cast<Video>().ToList();
- var videosWithVersions = videos.Where(i => i.AlternateVersionCount > 0)
+ var videosWithVersions = videos.Where(i => i.MediaSourceCount > 1)
.ToList();
if (videosWithVersions.Count > 1)
@@ -158,14 +155,27 @@ namespace MediaBrowser.Api
if (primaryVersion == null)
{
- primaryVersion = videos.OrderByDescending(i =>
+ primaryVersion = videos.OrderBy(i =>
+ {
+ if (i.Video3DFormat.HasValue)
+ {
+ return 1;
+ }
+
+ if (i.VideoType != Model.Entities.VideoType.VideoFile)
+ {
+ return 1;
+ }
+
+ return 0;
+ })
+ .ThenByDescending(i =>
{
var stream = i.GetDefaultVideoStream();
return stream == null || stream.Width == null ? 0 : stream.Width.Value;
- }).ThenBy(i => i.Name.Length)
- .First();
+ }).First();
}
foreach (var item in videos.Where(i => i.Id != primaryVersion.Id))
diff --git a/MediaBrowser.Controller/Dlna/CodecProfile.cs b/MediaBrowser.Controller/Dlna/CodecProfile.cs
new file mode 100644
index 000000000..bff374298
--- /dev/null
+++ b/MediaBrowser.Controller/Dlna/CodecProfile.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Dlna
+{
+ public class CodecProfile
+ {
+ public CodecType Type { get; set; }
+ public List<ProfileCondition> Conditions { get; set; }
+ public string Codec { get; set; }
+
+ public CodecProfile()
+ {
+ Conditions = new List<ProfileCondition>();
+ }
+
+ public List<string> GetCodecs()
+ {
+ return (Codec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ }
+ }
+
+ public enum CodecType
+ {
+ VideoCodec = 0,
+ VideoAudioCodec = 1,
+ AudioCodec = 2
+ }
+
+ public class ProfileCondition
+ {
+ public ProfileConditionType Condition { get; set; }
+ public ProfileConditionValue Property { get; set; }
+ public string Value { get; set; }
+ }
+
+ public enum ProfileConditionType
+ {
+ Equals = 0,
+ NotEquals = 1,
+ LessThanEqual = 2,
+ GreaterThanEqual = 3
+ }
+
+ public enum ProfileConditionValue
+ {
+ AudioChannels,
+ AudioBitrate,
+ Filesize,
+ Width,
+ Height,
+ VideoBitrate,
+ VideoFramerate,
+ VideoLevel
+ }
+}
diff --git a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs
index 4ccba1106..20c94ad50 100644
--- a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs
+++ b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs
@@ -67,5 +67,12 @@ namespace MediaBrowser.Controller.Dlna
{
public string Name { get; set; }
public string Value { get; set; }
+ public HeaderMatchType Match { get; set; }
+ }
+
+ public enum HeaderMatchType
+ {
+ Equals = 0,
+ Substring = 1
}
}
diff --git a/MediaBrowser.Controller/Dlna/DeviceProfile.cs b/MediaBrowser.Controller/Dlna/DeviceProfile.cs
index ca5929d13..91be73bba 100644
--- a/MediaBrowser.Controller/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Controller/Dlna/DeviceProfile.cs
@@ -55,10 +55,17 @@ namespace MediaBrowser.Controller.Dlna
public string ProtocolInfo { get; set; }
+ public MediaProfile[] MediaProfiles { get; set; }
+ public CodecProfile[] CodecProfiles { get; set; }
+
+ public int TimelineOffsetSeconds { get; set; }
+
public DeviceProfile()
{
DirectPlayProfiles = new DirectPlayProfile[] { };
TranscodingProfiles = new TranscodingProfile[] { };
+ MediaProfiles = new MediaProfile[] { };
+ CodecProfiles = new CodecProfile[] { };
}
}
}
diff --git a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
index 68e25e2f7..53d32a2f8 100644
--- a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
@@ -1,60 +1,14 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.Serialization;
-using System.Xml.Serialization;
+using System.Collections.Generic;
+using System.Linq;
namespace MediaBrowser.Controller.Dlna
{
public class DirectPlayProfile
{
- public string Container { get; set; }
+ public string[] Containers { get; set; }
public string AudioCodec { get; set; }
public string VideoCodec { get; set; }
- [IgnoreDataMember]
- [XmlIgnore]
- public string[] Containers
- {
- get
- {
- return (Container ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
- set
- {
- Container = value == null ? null : string.Join(",", value);
- }
- }
-
- [IgnoreDataMember]
- [XmlIgnore]
- public string[] AudioCodecs
- {
- get
- {
- return (AudioCodec ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
- set
- {
- AudioCodec = value == null ? null : string.Join(",", value);
- }
- }
-
- [IgnoreDataMember]
- [XmlIgnore]
- public string[] VideoCodecs
- {
- get
- {
- return (VideoCodec ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- }
- set
- {
- VideoCodec = value == null ? null : string.Join(",", value);
- }
- }
-
- public string OrgPn { get; set; }
- public string MimeType { get; set; }
public DlnaProfileType Type { get; set; }
public List<ProfileCondition> Conditions { get; set; }
@@ -62,37 +16,25 @@ namespace MediaBrowser.Controller.Dlna
public DirectPlayProfile()
{
Conditions = new List<ProfileCondition>();
+
+ Containers = new string[] { };
}
- }
- public class ProfileCondition
- {
- public ProfileConditionType Condition { get; set; }
- public ProfileConditionValue Value { get; set; }
+ public List<string> GetAudioCodecs()
+ {
+ return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ }
+
+ public List<string> GetVideoCodecs()
+ {
+ return (VideoCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ }
}
public enum DlnaProfileType
{
Audio = 0,
- Video = 1
- }
-
- public enum ProfileConditionType
- {
- Equals = 0,
- NotEquals = 1,
- LessThanEqual = 2,
- GreaterThanEqual = 3
- }
-
- public enum ProfileConditionValue
- {
- AudioChannels,
- AudioBitrate,
- Filesize,
- VideoWidth,
- VideoHeight,
- VideoBitrate,
- VideoFramerate
+ Video = 1,
+ Photo = 2
}
}
diff --git a/MediaBrowser.Controller/Dlna/MediaProfile.cs b/MediaBrowser.Controller/Dlna/MediaProfile.cs
new file mode 100644
index 000000000..5fa41b18a
--- /dev/null
+++ b/MediaBrowser.Controller/Dlna/MediaProfile.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Dlna
+{
+ public class MediaProfile
+ {
+ public string Container { get; set; }
+ public string AudioCodec { get; set; }
+ public string VideoCodec { get; set; }
+
+ public DlnaProfileType Type { get; set; }
+ public string OrgPn { get; set; }
+ public string MimeType { get; set; }
+
+ public List<string> GetAudioCodecs()
+ {
+ return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ }
+
+ public List<string> GetVideoCodecs()
+ {
+ return (VideoCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
index 3b0a513d1..530a44b8c 100644
--- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
+++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs
@@ -8,12 +8,7 @@ namespace MediaBrowser.Controller.Dlna
public DlnaProfileType Type { get; set; }
- public string MimeType { get; set; }
-
- public string OrgPn { get; set; }
-
public string VideoCodec { get; set; }
-
public string AudioCodec { get; set; }
public List<TranscodingSetting> Settings { get; set; }
diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs
index 03039dc83..a02851a9f 100644
--- a/MediaBrowser.Controller/Dto/IDtoService.cs
+++ b/MediaBrowser.Controller/Dto/IDtoService.cs
@@ -29,13 +29,6 @@ namespace MediaBrowser.Controller.Dto
SessionInfoDto GetSessionInfoDto(SessionInfo session);
/// <summary>
- /// Gets the base item info.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>BaseItemInfo.</returns>
- BaseItemInfo GetBaseItemInfo(BaseItem item);
-
- /// <summary>
/// Gets the dto id.
/// </summary>
/// <param name="item">The item.</param>
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 76e0e1fc5..a6f501689 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -37,11 +37,11 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
- public int AlternateVersionCount
+ public int MediaSourceCount
{
get
{
- return LinkedAlternateVersions.Count + LocalAlternateVersionIds.Count;
+ return LinkedAlternateVersions.Count + LocalAlternateVersionIds.Count + 1;
}
}
diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
index 2ec3d308e..61984c795 100644
--- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
+++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs
@@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Library
public List<User> Users { get; set; }
public long? PlaybackPositionTicks { get; set; }
public BaseItem Item { get; set; }
+ public string MediaSourceId { get; set; }
public PlaybackProgressEventArgs()
{
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index ac178ff53..b51824bdb 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -78,10 +78,12 @@
<Compile Include="Channels\Channel.cs" />
<Compile Include="Collections\CollectionCreationOptions.cs" />
<Compile Include="Collections\ICollectionManager.cs" />
+ <Compile Include="Dlna\CodecProfile.cs" />
<Compile Include="Dlna\DeviceIdentification.cs" />
<Compile Include="Dlna\DirectPlayProfile.cs" />
<Compile Include="Dlna\IDlnaManager.cs" />
<Compile Include="Dlna\DeviceProfile.cs" />
+ <Compile Include="Dlna\MediaProfile.cs" />
<Compile Include="Dlna\TranscodingProfile.cs" />
<Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageFormat.cs" />
diff --git a/MediaBrowser.Controller/Session/PlaybackInfo.cs b/MediaBrowser.Controller/Session/PlaybackInfo.cs
index ab3111e76..a97f9e0d0 100644
--- a/MediaBrowser.Controller/Session/PlaybackInfo.cs
+++ b/MediaBrowser.Controller/Session/PlaybackInfo.cs
@@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session
/// </summary>
/// <value>The session id.</value>
public Guid SessionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the media version identifier.
+ /// </summary>
+ /// <value>The media version identifier.</value>
+ public string MediaSourceId { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Session/PlaybackProgressInfo.cs b/MediaBrowser.Controller/Session/PlaybackProgressInfo.cs
index a07543260..3d402aa6f 100644
--- a/MediaBrowser.Controller/Session/PlaybackProgressInfo.cs
+++ b/MediaBrowser.Controller/Session/PlaybackProgressInfo.cs
@@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session
/// </summary>
/// <value>The position ticks.</value>
public long? PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the media version identifier.
+ /// </summary>
+ /// <value>The media version identifier.</value>
+ public string MediaSourceId { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Session/PlaybackStopInfo.cs b/MediaBrowser.Controller/Session/PlaybackStopInfo.cs
index 5d1ce0131..063abf78c 100644
--- a/MediaBrowser.Controller/Session/PlaybackStopInfo.cs
+++ b/MediaBrowser.Controller/Session/PlaybackStopInfo.cs
@@ -22,5 +22,11 @@ namespace MediaBrowser.Controller.Session
/// </summary>
/// <value>The position ticks.</value>
public long? PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the media version identifier.
+ /// </summary>
+ /// <value>The media version identifier.</value>
+ public string MediaSourceId { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index 73e33d78c..9dd57d12a 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -120,11 +120,23 @@ namespace MediaBrowser.Controller.Session
public BaseItem NowPlayingItem { get; set; }
/// <summary>
+ /// Gets or sets the now playing media version identifier.
+ /// </summary>
+ /// <value>The now playing media version identifier.</value>
+ public string NowPlayingMediaSourceId { get; set; }
+
+
+ /// <summary>
+ /// Gets or sets the now playing run time ticks.
+ /// </summary>
+ /// <value>The now playing run time ticks.</value>
+ public long? NowPlayingRunTimeTicks { get; set; }
+
+ /// <summary>
/// Gets or sets the now playing position ticks.
/// </summary>
/// <value>The now playing position ticks.</value>
public long? NowPlayingPositionTicks { get; set; }
-
/// <summary>
/// Gets or sets a value indicating whether this instance is paused.
/// </summary>
@@ -162,6 +174,18 @@ namespace MediaBrowser.Controller.Session
public bool SupportsFullscreenToggle { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether [supports osd toggle].
+ /// </summary>
+ /// <value><c>true</c> if [supports osd toggle]; otherwise, <c>false</c>.</value>
+ public bool SupportsOsdToggle { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [supports navigation commands].
+ /// </summary>
+ /// <value><c>true</c> if [supports navigation commands]; otherwise, <c>false</c>.</value>
+ public bool SupportsNavigationControl { get; set; }
+
+ /// <summary>
/// Gets a value indicating whether this instance is active.
/// </summary>
/// <value><c>true</c> if this instance is active; otherwise, <c>false</c>.</value>
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 1ec193204..be7295e12 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -62,13 +62,11 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"mkv"},
- MimeType = "x-mkv",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Containers = new[]{"avi"},
- MimeType = "x-msvideo",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
@@ -76,6 +74,23 @@ namespace MediaBrowser.Dlna
Containers = new[]{"mp4"},
Type = DlnaProfileType.Video
}
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/x-msvideo",
+ Type = DlnaProfileType.Video
+ },
+
+ new MediaProfile
+ {
+ Container ="mkv",
+ MimeType = "video/x-mkv",
+ Type = DlnaProfileType.Video
+ }
}
});
@@ -114,13 +129,11 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"mkv"},
- MimeType = "x-mkv",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Containers = new[]{"avi"},
- MimeType = "x-msvideo",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
@@ -128,6 +141,23 @@ namespace MediaBrowser.Dlna
Containers = new[]{"mp4"},
Type = DlnaProfileType.Video
}
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/x-msvideo",
+ Type = DlnaProfileType.Video
+ },
+
+ new MediaProfile
+ {
+ Container ="mkv",
+ MimeType = "video/x-mkv",
+ Type = DlnaProfileType.Video
+ }
}
});
@@ -166,13 +196,11 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"mkv"},
- MimeType = "x-mkv",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Containers = new[]{"avi"},
- MimeType = "x-msvideo",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
@@ -180,6 +208,23 @@ namespace MediaBrowser.Dlna
Containers = new[]{"mp4"},
Type = DlnaProfileType.Video
}
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/x-msvideo",
+ Type = DlnaProfileType.Video
+ },
+
+ new MediaProfile
+ {
+ Container ="mkv",
+ MimeType = "video/x-mkv",
+ Type = DlnaProfileType.Video
+ }
}
});
@@ -217,7 +262,6 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"avi"},
- MimeType = "avi",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
@@ -225,6 +269,16 @@ namespace MediaBrowser.Dlna
Containers = new[]{"mp4"},
Type = DlnaProfileType.Video
}
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/avi",
+ Type = DlnaProfileType.Video
+ }
}
});
@@ -263,7 +317,16 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"avi"},
- MimeType = "x-msvideo",
+ Type = DlnaProfileType.Video
+ }
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/x-msvideo",
Type = DlnaProfileType.Video
}
}
@@ -303,14 +366,29 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"avi"},
- Type = DlnaProfileType.Video,
- MimeType = "avi"
+ Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Containers = new[]{"asf"},
- Type = DlnaProfileType.Audio,
- MimeType = "x-ms-wmv"
+ Type = DlnaProfileType.Audio
+ }
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/avi",
+ Type = DlnaProfileType.Video
+ },
+
+ new MediaProfile
+ {
+ Container ="asf",
+ MimeType = "video/x-ms-wmv",
+ Type = DlnaProfileType.Audio
}
}
});
@@ -335,8 +413,7 @@ namespace MediaBrowser.Dlna
new TranscodingProfile
{
Container = "ts",
- Type = DlnaProfileType.Video,
- MimeType = "mpeg"
+ Type = DlnaProfileType.Video
}
},
@@ -350,20 +427,48 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"wma"},
- Type = DlnaProfileType.Audio,
- MimeType = "x-ms-wma"
+ Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Containers = new[]{"avi"},
- Type = DlnaProfileType.Video,
- MimeType = "avi"
+ Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Containers = new[]{"mp4"},
- Type = DlnaProfileType.Video,
- MimeType = "mp4"
+ Type = DlnaProfileType.Video
+ }
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/avi",
+ Type = DlnaProfileType.Video
+ },
+
+ new MediaProfile
+ {
+ Container ="mp4",
+ MimeType = "video/mp4",
+ Type = DlnaProfileType.Video
+ },
+
+ new MediaProfile
+ {
+ Container ="ts",
+ MimeType = "video/mpeg",
+ Type = DlnaProfileType.Video
+ },
+
+ new MediaProfile
+ {
+ Container ="wma",
+ MimeType = "video/x-ms-wma",
+ Type = DlnaProfileType.Audio
}
}
});
@@ -450,13 +555,21 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"avi"},
- Type = DlnaProfileType.Video ,
- MimeType="divx"
+ Type = DlnaProfileType.Video
+ }
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/divx",
+ Type = DlnaProfileType.Video
}
}
});
- //WDTV does not need any transcoding of the formats we support statically
list.Add(new DeviceProfile
{
Name = "Philips (2010-)",
@@ -479,49 +592,222 @@ namespace MediaBrowser.Dlna
new DirectPlayProfile
{
Containers = new[]{"avi"},
- Type = DlnaProfileType.Video,
- MimeType = "avi"
+ Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Containers = new[]{"mkv"},
- Type = DlnaProfileType.Video,
- MimeType = "x-matroska"
+ Type = DlnaProfileType.Video
+ }
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="avi",
+ MimeType = "video/avi",
+ Type = DlnaProfileType.Video
+ },
+
+ new MediaProfile
+ {
+ Container ="mkv",
+ MimeType = "video/x-matroska",
+ Type = DlnaProfileType.Video
}
}
});
- //WDTV does not need any transcoding of the formats we support statically
list.Add(new DeviceProfile
{
Name = "WDTV Live",
ClientType = "DLNA",
+ TimelineOffsetSeconds = 5,
+
Identification = new DeviceIdentification
{
- ModelName = "WD TV HD Live"
+ ModelName = "WD TV HD Live",
+
+ Headers = new List<HttpHeaderInfo>
+ {
+ new HttpHeaderInfo{ Name="User-Agent", Value="alphanetworks", Match= HeaderMatchType.Substring},
+ new HttpHeaderInfo{ Name="User-Agent", Value="ALPHA Networks", Match= HeaderMatchType.Substring}
+ }
+ },
+
+ TranscodingProfiles = new[]
+ {
+ new TranscodingProfile
+ {
+ Container = "mp3",
+ Type = DlnaProfileType.Audio,
+ AudioCodec = "mp3"
+ },
+ new TranscodingProfile
+ {
+ Container = "ts",
+ Type = DlnaProfileType.Video,
+ VideoCodec = "h264",
+ AudioCodec = "aac"
+ },
+ new TranscodingProfile
+ {
+ Container = "jpeg",
+ Type = DlnaProfileType.Photo
+ }
},
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
- Containers = new[]{"mp3", "flac", "m4a", "wma"},
+ Containers = new[]{"avi"},
+ Type = DlnaProfileType.Video,
+ VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
+ AudioCodec = "ac3,dca,mp2,mp3,pcm"
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"mpeg"},
+ Type = DlnaProfileType.Video,
+ VideoCodec = "mpeg1video,mpeg2video",
+ AudioCodec = "ac3,dca,mp2,mp3,pcm"
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"mkv"},
+ Type = DlnaProfileType.Video,
+ VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
+ AudioCodec = "ac3,dca,aac,mp2,mp3,pcm"
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"ts"},
+ Type = DlnaProfileType.Video,
+ VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
+ AudioCodec = "ac3,dca,mp2,mp3"
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"mp4", "mov"},
+ Type = DlnaProfileType.Video,
+ VideoCodec = "h264,mpeg4",
+ AudioCodec = "ac3,aac,mp2,mp3"
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"asf"},
+ Type = DlnaProfileType.Video,
+ VideoCodec = "vc1",
+ AudioCodec = "wmav2,wmapro"
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"asf"},
+ Type = DlnaProfileType.Video,
+ VideoCodec = "mpeg2video",
+ AudioCodec = "mp2,ac3"
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"mp3"},
+ AudioCodec = "mp2,mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
- Containers = new[]{"avi", "mp4", "mkv", "ts"},
+ Containers = new[]{"mp4"},
+ AudioCodec = "mp4",
+ Type = DlnaProfileType.Audio
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"flac"},
+ AudioCodec = "flac",
+ Type = DlnaProfileType.Audio
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"asf"},
+ AudioCodec = "wmav2,wmapro,wmavoice",
+ Type = DlnaProfileType.Audio
+ },
+
+ new DirectPlayProfile
+ {
+ Containers = new[]{"ogg"},
+ AudioCodec = "vorbis",
+ Type = DlnaProfileType.Audio
+ },
+
+ new DirectPlayProfile
+ {
+ Type = DlnaProfileType.Photo,
+
+ Containers = new[]{"jpeg", "png", "gif", "bmp", "tiff"},
+
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Width, Value = "1920"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080"}
+ }
+ }
+ },
+
+ MediaProfiles = new[]
+ {
+ new MediaProfile
+ {
+ Container ="ts",
+ OrgPn = "MPEG_TS_SD_NA",
Type = DlnaProfileType.Video
}
+ },
+
+ CodecProfiles = new[]
+ {
+ new CodecProfile
+ {
+ Type = CodecType.VideoCodec,
+ Codec= "h264",
+
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Width, Value = "1920"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080"},
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.VideoLevel, Value = "41"}
+ }
+ },
+
+ new CodecProfile
+ {
+ Type = CodecType.VideoAudioCodec,
+ Codec= "aac",
+
+ Conditions = new List<ProfileCondition>
+ {
+ new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.AudioChannels, Value = "2"}
+ }
+ }
}
});
list.Add(new DeviceProfile
{
- //Linksys DMA2100us does not need any transcoding of the formats we support statically
+ // Linksys DMA2100us does not need any transcoding of the formats we support statically
Name = "Linksys DMA2100",
ClientType = "DLNA",
@@ -547,10 +833,10 @@ namespace MediaBrowser.Dlna
});
list.Add(new DeviceProfile
- {
+ {
Name = "Denon AVR",
- ClientType = "DLNA",
-
+ ClientType = "DLNA",
+
Identification = new DeviceIdentification
{
FriendlyName = @"Denon:\[AVR:.*",
@@ -612,7 +898,7 @@ namespace MediaBrowser.Dlna
public DeviceProfile GetProfile(DeviceIdentification deviceInfo)
{
- return GetProfiles().FirstOrDefault(i => IsMatch(deviceInfo, i.Identification)) ??
+ return GetProfiles().FirstOrDefault(i => IsMatch(deviceInfo, i.Identification)) ??
GetDefaultProfile();
}
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index a7ee05cf3..800fb1b23 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -66,6 +66,7 @@
<Compile Include="PlayTo\PlaylistItem.cs">
<SubType>Code</SubType>
</Compile>
+ <Compile Include="PlayTo\PlaylistItemFactory.cs" />
<Compile Include="PlayTo\PlayToManager.cs" />
<Compile Include="PlayTo\PlayToServerEntryPoint.cs" />
<Compile Include="PlayTo\ServiceAction.cs" />
diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
index cb4bda127..e94663802 100644
--- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs
+++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session;
@@ -69,12 +70,12 @@ namespace MediaBrowser.Dlna.PlayTo
_device.CurrentIdChanged += Device_CurrentIdChanged;
_device.Start();
- _updateTimer = new System.Threading.Timer(updateTimer_Elapsed, null, UpdateTimerIntervalMs, UpdateTimerIntervalMs);
+ _updateTimer = new Timer(updateTimer_Elapsed, null, UpdateTimerIntervalMs, UpdateTimerIntervalMs);
}
#region Device EventHandlers & Update Timer
- System.Threading.Timer _updateTimer;
+ Timer _updateTimer;
async void Device_PlaybackChanged(object sender, TransportStateEventArgs e)
{
@@ -88,7 +89,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
_playbackStarted = false;
- await _sessionManager.OnPlaybackStopped(new PlaybackStopInfo
+ await _sessionManager.OnPlaybackStopped(new Controller.Session.PlaybackStopInfo
{
Item = _currentItem,
SessionId = _session.Id,
@@ -164,7 +165,7 @@ namespace MediaBrowser.Dlna.PlayTo
var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
if (playlistItem != null && playlistItem.Transcode)
{
- await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
+ await _sessionManager.OnPlaybackProgress(new Controller.Session.PlaybackProgressInfo
{
Item = _currentItem,
SessionId = _session.Id,
@@ -176,7 +177,7 @@ namespace MediaBrowser.Dlna.PlayTo
}
else if (_currentItem != null)
{
- await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
+ await _sessionManager.OnPlaybackProgress(new Controller.Session.PlaybackProgressInfo
{
Item = _currentItem,
SessionId = _session.Id,
@@ -263,7 +264,7 @@ namespace MediaBrowser.Dlna.PlayTo
case PlaystateCommand.Seek:
var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
- if (playlistItem != null && playlistItem.Transcode && playlistItem.IsVideo && _currentItem != null)
+ if (playlistItem != null && playlistItem.Transcode && playlistItem.MediaType == DlnaProfileType.Video && _currentItem != null)
{
var newItem = CreatePlaylistItem(_currentItem, command.SeekPositionTicks ?? 0, GetServerAddress());
playlistItem.StartPositionTicks = newItem.StartPositionTicks;
@@ -394,11 +395,13 @@ namespace MediaBrowser.Dlna.PlayTo
var deviceInfo = _device.Properties;
- var playlistItem = PlaylistItem.Create(item, _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()));
+ var playlistItem = GetPlaylistItem(item, _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()));
playlistItem.StartPositionTicks = startPostionTicks;
- if (playlistItem.IsAudio)
+ if (playlistItem.MediaType == DlnaProfileType.Audio)
+ {
playlistItem.StreamUrl = StreamHelper.GetAudioUrl(playlistItem, serverAddress);
+ }
else
{
playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress);
@@ -412,6 +415,32 @@ namespace MediaBrowser.Dlna.PlayTo
return playlistItem;
}
+ private PlaylistItem GetPlaylistItem(BaseItem item, DeviceProfile profile)
+ {
+ var video = item as Video;
+
+ if (video != null)
+ {
+ return new PlaylistItemFactory(_itemRepository).Create(video, profile);
+ }
+
+ var audio = item as Audio;
+
+ if (audio != null)
+ {
+ return new PlaylistItemFactory(_itemRepository).Create(audio, profile);
+ }
+
+ var photo = item as Photo;
+
+ if (photo != null)
+ {
+ return new PlaylistItemFactory(_itemRepository).Create(photo, profile);
+ }
+
+ throw new ArgumentException("Unrecognized item type.");
+ }
+
/// <summary>
/// Plays the items.
/// </summary>
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
index 4f776807e..1304f61b1 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
@@ -1,9 +1,4 @@
using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-using System;
-using System.IO;
-using System.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
@@ -11,13 +6,13 @@ namespace MediaBrowser.Dlna.PlayTo
{
public string ItemId { get; set; }
+ public string MediaSourceId { get; set; }
+
public bool Transcode { get; set; }
- public bool IsVideo { get; set; }
+ public DlnaProfileType MediaType { get; set; }
- public bool IsAudio { get; set; }
-
- public string FileFormat { get; set; }
+ public string Container { get; set; }
public string MimeType { get; set; }
@@ -30,72 +25,5 @@ namespace MediaBrowser.Dlna.PlayTo
public string Didl { get; set; }
public long StartPositionTicks { get; set; }
-
- public static PlaylistItem Create(BaseItem item, DeviceProfile profile)
- {
- var playlistItem = new PlaylistItem
- {
- ItemId = item.Id.ToString()
- };
-
- DlnaProfileType profileType;
- if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- playlistItem.IsVideo = true;
- profileType = DlnaProfileType.Video;
- }
- else
- {
- playlistItem.IsAudio = true;
- profileType = DlnaProfileType.Audio;
- }
-
- var path = item.Path;
-
- var directPlay = profile.DirectPlayProfiles.FirstOrDefault(i => i.Type == profileType && IsSupported(i, path));
-
- if (directPlay != null)
- {
- playlistItem.Transcode = false;
- playlistItem.FileFormat = Path.GetExtension(path);
- playlistItem.MimeType = directPlay.MimeType;
- return playlistItem;
- }
-
- var transcodingProfile = profile.TranscodingProfiles.FirstOrDefault(i => i.Type == profileType && IsSupported(profile, i, path));
-
- if (transcodingProfile != null)
- {
- playlistItem.Transcode = true;
- //Just to make sure we have a "." for the url, remove it in case a user adds it or not
- playlistItem.FileFormat = "." + transcodingProfile.Container.TrimStart('.');
-
- playlistItem.MimeType = transcodingProfile.MimeType;
- }
-
- return playlistItem;
- }
-
- private static bool IsSupported(DirectPlayProfile profile, string path)
- {
- var mediaContainer = Path.GetExtension(path);
-
- if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
- {
- return false;
- }
-
- // Placeholder for future conditions
-
- // TODO: Support codec list as additional restriction
-
- return true;
- }
-
- private static bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, string path)
- {
- // Placeholder for future conditions
- return true;
- }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
new file mode 100644
index 000000000..6817c4eaa
--- /dev/null
+++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -0,0 +1,375 @@
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Entities;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+
+namespace MediaBrowser.Dlna.PlayTo
+{
+ public class PlaylistItemFactory
+ {
+ private readonly IItemRepository _itemRepo;
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public PlaylistItemFactory(IItemRepository itemRepo)
+ {
+ _itemRepo = itemRepo;
+ }
+
+ public PlaylistItem Create(Audio item, DeviceProfile profile)
+ {
+ var playlistItem = new PlaylistItem
+ {
+ ItemId = item.Id.ToString("N"),
+ MediaType = DlnaProfileType.Audio
+ };
+
+ var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
+ {
+ ItemId = item.Id,
+ Type = MediaStreamType.Audio
+ });
+
+ var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+
+ var directPlay = profile.DirectPlayProfiles
+ .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
+
+ if (directPlay != null)
+ {
+ playlistItem.Transcode = false;
+ playlistItem.Container = Path.GetExtension(item.Path);
+
+ return playlistItem;
+ }
+
+ var transcodingProfile = profile.TranscodingProfiles
+ .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
+
+ if (transcodingProfile != null)
+ {
+ playlistItem.Transcode = true;
+
+ playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
+ }
+
+ AttachMediaProfile(playlistItem, profile);
+
+ return playlistItem;
+ }
+
+ public PlaylistItem Create(Photo item, DeviceProfile profile)
+ {
+ var playlistItem = new PlaylistItem
+ {
+ ItemId = item.Id.ToString("N"),
+ MediaType = DlnaProfileType.Photo
+ };
+
+ var directPlay = profile.DirectPlayProfiles
+ .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item));
+
+ if (directPlay != null)
+ {
+ playlistItem.Transcode = false;
+ playlistItem.Container = Path.GetExtension(item.Path);
+
+ return playlistItem;
+ }
+
+ var transcodingProfile = profile.TranscodingProfiles
+ .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
+
+ if (transcodingProfile != null)
+ {
+ playlistItem.Transcode = true;
+
+ playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
+ }
+
+ AttachMediaProfile(playlistItem, profile);
+
+ return playlistItem;
+ }
+
+ public PlaylistItem Create(Video item, DeviceProfile profile)
+ {
+ var playlistItem = new PlaylistItem
+ {
+ ItemId = item.Id.ToString("N"),
+ MediaType = DlnaProfileType.Video
+ };
+
+ var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
+ {
+ ItemId = item.Id
+
+ }).ToList();
+
+ var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+ var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+
+ var directPlay = profile.DirectPlayProfiles
+ .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream));
+
+ if (directPlay != null)
+ {
+ playlistItem.Transcode = false;
+ playlistItem.Container = Path.GetExtension(item.Path);
+
+ return playlistItem;
+ }
+
+ var transcodingProfile = profile.TranscodingProfiles
+ .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
+
+ if (transcodingProfile != null)
+ {
+ playlistItem.Transcode = true;
+ playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
+ }
+
+ AttachMediaProfile(playlistItem, profile);
+
+ return playlistItem;
+ }
+
+ private void AttachMediaProfile(PlaylistItem item, DeviceProfile profile)
+ {
+ var mediaProfile = GetMediaProfile(item, profile);
+
+ if (mediaProfile != null)
+ {
+ item.MimeType = (mediaProfile.MimeType ?? string.Empty).Split('/').LastOrDefault();
+
+ // TODO: Org_pn?
+ }
+ }
+
+ private MediaProfile GetMediaProfile(PlaylistItem item, DeviceProfile profile)
+ {
+ return profile.MediaProfiles.FirstOrDefault(i =>
+ {
+ if (i.Type == item.MediaType)
+ {
+ if (string.Equals(item.Container.TrimStart('.'), i.Container.TrimStart('.'), StringComparison.OrdinalIgnoreCase))
+ {
+ // TODO: Enforce codecs
+ return true;
+ }
+ }
+
+ return false;
+ });
+ }
+
+ private bool IsSupported(DirectPlayProfile profile, Photo item)
+ {
+ var mediaPath = item.Path;
+
+ if (profile.Containers.Length > 0)
+ {
+ // Check container type
+ var mediaContainer = Path.GetExtension(mediaPath);
+ if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
+ {
+ return false;
+ }
+ }
+
+ // Check additional conditions
+ if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, null, null)))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool IsSupported(DirectPlayProfile profile, Audio item, MediaStream audioStream)
+ {
+ var mediaPath = item.Path;
+
+ if (profile.Containers.Length > 0)
+ {
+ // Check container type
+ var mediaContainer = Path.GetExtension(mediaPath);
+ if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
+ {
+ return false;
+ }
+ }
+
+ // Check additional conditions
+ if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, null, audioStream)))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool IsSupported(DirectPlayProfile profile, Video item, MediaStream videoStream, MediaStream audioStream)
+ {
+ if (item.VideoType != VideoType.VideoFile)
+ {
+ return false;
+ }
+
+ var mediaPath = item.Path;
+
+ if (profile.Containers.Length > 0)
+ {
+ // Check container type
+ var mediaContainer = Path.GetExtension(mediaPath);
+ if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
+ {
+ return false;
+ }
+ }
+
+ // Check video codec
+ var videoCodecs = profile.GetVideoCodecs();
+ if (videoCodecs.Count > 0)
+ {
+ var videoCodec = videoStream == null ? null : videoStream.Codec;
+ if (string.IsNullOrWhiteSpace(videoCodec) || !videoCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+ var audioCodecs = profile.GetAudioCodecs();
+ if (audioCodecs.Count > 0)
+ {
+ // Check audio codecs
+ var audioCodec = audioStream == null ? null : audioStream.Codec;
+ if (string.IsNullOrWhiteSpace(audioCodec) || !audioCodecs.Contains(audioCodec, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+ // Check additional conditions
+ if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream)))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Audio item)
+ {
+ // Placeholder for future conditions
+ return true;
+ }
+
+ private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Photo item)
+ {
+ // Placeholder for future conditions
+ return true;
+ }
+
+ private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Video item)
+ {
+ // Placeholder for future conditions
+ return true;
+ }
+
+ /// <summary>
+ /// Determines whether [is condition satisfied] [the specified condition].
+ /// </summary>
+ /// <param name="condition">The condition.</param>
+ /// <param name="mediaPath">The media path.</param>
+ /// <param name="videoStream">The video stream.</param>
+ /// <param name="audioStream">The audio stream.</param>
+ /// <returns><c>true</c> if [is condition satisfied] [the specified condition]; otherwise, <c>false</c>.</returns>
+ /// <exception cref="System.InvalidOperationException">Unexpected ProfileConditionType</exception>
+ private bool IsConditionSatisfied(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
+ {
+ var actualValue = GetConditionValue(condition, mediaPath, videoStream, audioStream);
+
+ if (actualValue.HasValue)
+ {
+ long expected;
+ if (long.TryParse(condition.Value, NumberStyles.Any, _usCulture, out expected))
+ {
+ switch (condition.Condition)
+ {
+ case ProfileConditionType.Equals:
+ return actualValue.Value == expected;
+ case ProfileConditionType.GreaterThanEqual:
+ return actualValue.Value >= expected;
+ case ProfileConditionType.LessThanEqual:
+ return actualValue.Value <= expected;
+ case ProfileConditionType.NotEquals:
+ return actualValue.Value != expected;
+ default:
+ throw new InvalidOperationException("Unexpected ProfileConditionType");
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Gets the condition value.
+ /// </summary>
+ /// <param name="condition">The condition.</param>
+ /// <param name="mediaPath">The media path.</param>
+ /// <param name="videoStream">The video stream.</param>
+ /// <param name="audioStream">The audio stream.</param>
+ /// <returns>System.Nullable{System.Int64}.</returns>
+ /// <exception cref="System.InvalidOperationException">Unexpected Property</exception>
+ private long? GetConditionValue(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
+ {
+ switch (condition.Property)
+ {
+ case ProfileConditionValue.AudioBitrate:
+ return audioStream == null ? null : audioStream.BitRate;
+ case ProfileConditionValue.AudioChannels:
+ return audioStream == null ? null : audioStream.Channels;
+ case ProfileConditionValue.Filesize:
+ return new FileInfo(mediaPath).Length;
+ case ProfileConditionValue.VideoBitrate:
+ return videoStream == null ? null : videoStream.BitRate;
+ case ProfileConditionValue.VideoFramerate:
+ return videoStream == null ? null : (ConvertToLong(videoStream.AverageFrameRate ?? videoStream.RealFrameRate));
+ case ProfileConditionValue.Height:
+ return videoStream == null ? null : videoStream.Height;
+ case ProfileConditionValue.Width:
+ return videoStream == null ? null : videoStream.Width;
+ case ProfileConditionValue.VideoLevel:
+ return videoStream == null ? null : ConvertToLong(videoStream.Level);
+ default:
+ throw new InvalidOperationException("Unexpected Property");
+ }
+ }
+
+ /// <summary>
+ /// Converts to long.
+ /// </summary>
+ /// <param name="val">The value.</param>
+ /// <returns>System.Nullable{System.Int64}.</returns>
+ private long? ConvertToLong(float? val)
+ {
+ return val.HasValue ? Convert.ToInt64(val.Value) : (long?)null;
+ }
+
+ /// <summary>
+ /// Converts to long.
+ /// </summary>
+ /// <param name="val">The value.</param>
+ /// <returns>System.Nullable{System.Int64}.</returns>
+ private long? ConvertToLong(double? val)
+ {
+ return val.HasValue ? Convert.ToInt64(val.Value) : (long?)null;
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs
index 97d208aa3..cb2b72a03 100644
--- a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs
+++ b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs
@@ -24,43 +24,43 @@ namespace MediaBrowser.Dlna.PlayTo
var contentFeatures = string.Empty;
- if (string.Equals(item.FileFormat, "mp3", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(item.Container, "mp3", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MP3";
}
- else if (string.Equals(item.FileFormat, "wma", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(item.Container, "wma", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMABASE";
}
- else if (string.Equals(item.FileFormat, "wmw", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(item.Container, "wmw", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
}
- else if (string.Equals(item.FileFormat, "asf", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(item.Container, "asf", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
}
- else if (string.Equals(item.FileFormat, "avi", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(item.Container, "avi", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVI";
}
- else if (string.Equals(item.FileFormat, "mkv", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(item.Container, "mkv", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MATROSKA";
}
- else if (string.Equals(item.FileFormat, "mp4", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(item.Container, "mp4", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC";
}
- else if (string.Equals(item.FileFormat, "mpeg", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(item.Container, "mpeg", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
- else if (string.Equals(item.FileFormat, "ts", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(item.Container, "ts", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
- else if (item.IsVideo)
+ else if (item.MediaType == Controller.Dlna.DlnaProfileType.Video)
{
//Default to AVI for video
contentFeatures = "DLNA.ORG_PN=AVI";
@@ -85,7 +85,7 @@ namespace MediaBrowser.Dlna.PlayTo
internal static string GetAudioUrl(PlaylistItem item, string serverAddress)
{
if (!item.Transcode)
- return string.Format("{0}/audio/{1}/stream{2}?Static=True", serverAddress, item.ItemId, item.FileFormat);
+ return string.Format("{0}/audio/{1}/stream{2}?Static=True", serverAddress, item.ItemId, item.Container);
return string.Format("{0}/audio/{1}/stream.mp3?AudioCodec=Mp3", serverAddress, item.ItemId);
}
@@ -108,7 +108,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (!item.Transcode)
{
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, null, null, null, null, null, null, null, null, null, null, item.MimeType);
- return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.FileFormat, dlnaCommand);
+ return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand);
}
var videostream = streams.Where(m => m.Type == MediaStreamType.Video).OrderBy(m => m.IsDefault).FirstOrDefault();
var audiostream = streams.Where(m => m.Type == MediaStreamType.Audio).OrderBy(m => m.IsDefault).FirstOrDefault();
@@ -129,7 +129,7 @@ namespace MediaBrowser.Dlna.PlayTo
}
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, videoCodec, audioCodec, null, null, videoBitrate, audioChannels, audioBitrate, item.StartPositionTicks, "baseline", "3", item.MimeType);
- return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.FileFormat, dlnaCommand);
+ return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand);
}
/// <summary>
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index c46e355df..960b0f635 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -428,6 +428,9 @@
<Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
<Link>Session\MessageCommand.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Session\PlaybackReports.cs">
+ <Link>Session\PlaybackReports.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
<Link>Session\PlayRequest.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index 7ef891209..b010ad9c9 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -415,6 +415,9 @@
<Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
<Link>Session\MessageCommand.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Session\PlaybackReports.cs">
+ <Link>Session\PlaybackReports.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
<Link>Session\PlayRequest.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index 46d3dcc3f..91ac67a1f 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -538,35 +538,26 @@ namespace MediaBrowser.Model.ApiClient
/// <summary>
/// Reports to the server that the user has begun playing an item
/// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="isSeekable">if set to <c>true</c> [is seekable].</param>
- /// <param name="queueableMediaTypes">The list of media types that the client is capable of queuing onto the playlist. See MediaType class.</param>
+ /// <param name="info">The information.</param>
/// <returns>Task{UserItemDataDto}.</returns>
/// <exception cref="ArgumentNullException">itemId</exception>
- Task ReportPlaybackStartAsync(string itemId, string userId, bool isSeekable, List<string> queueableMediaTypes);
+ Task ReportPlaybackStartAsync(PlaybackStartInfo info);
/// <summary>
/// Reports playback progress to the server
/// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="positionTicks">The position ticks.</param>
- /// <param name="isPaused">if set to <c>true</c> [is paused].</param>
- /// <param name="isMuted">if set to <c>true</c> [is muted].</param>
+ /// <param name="info">The information.</param>
/// <returns>Task{UserItemDataDto}.</returns>
/// <exception cref="ArgumentNullException">itemId</exception>
- Task ReportPlaybackProgressAsync(string itemId, string userId, long? positionTicks, bool isPaused, bool isMuted);
+ Task ReportPlaybackProgressAsync(PlaybackProgressInfo info);
/// <summary>
/// Reports to the server that the user has stopped playing an item
/// </summary>
- /// <param name="itemId">The item id.</param>
- /// <param name="userId">The user id.</param>
- /// <param name="positionTicks">The position ticks.</param>
+ /// <param name="info">The information.</param>
/// <returns>Task{UserItemDataDto}.</returns>
/// <exception cref="ArgumentNullException">itemId</exception>
- Task ReportPlaybackStoppedAsync(string itemId, string userId, long? positionTicks);
+ Task ReportPlaybackStoppedAsync(PlaybackStopInfo info);
/// <summary>
/// Instructs antoher client to browse to a library item.
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index c2765754e..1f304112f 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -147,7 +147,7 @@ namespace MediaBrowser.Model.Configuration
/// different directories and files.
/// </summary>
/// <value>The file watcher delay.</value>
- public int RealtimeWatcherDelay { get; set; }
+ public int RealtimeMonitorDelay { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable dashboard response caching].
@@ -239,7 +239,7 @@ namespace MediaBrowser.Model.Configuration
MaxResumePct = 90;
MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds);
- RealtimeWatcherDelay = 20;
+ RealtimeMonitorDelay = 30;
RecentItemDays = 10;
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 379e757bb..780f936be 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -92,7 +92,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the media versions.
/// </summary>
/// <value>The media versions.</value>
- public List<MediaVersionInfo> MediaVersions { get; set; }
+ public List<MediaSourceInfo> MediaSources { get; set; }
/// <summary>
/// Gets or sets the critic rating.
@@ -500,7 +500,7 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The part count.</value>
public int? PartCount { get; set; }
- public int? MediaVersionCount { get; set; }
+ public int? MediaSourceCount { get; set; }
/// <summary>
/// Determines whether the specified type is type.
diff --git a/MediaBrowser.Model/Dto/MediaVersionInfo.cs b/MediaBrowser.Model/Dto/MediaVersionInfo.cs
index 3da5e2b8d..ddb340787 100644
--- a/MediaBrowser.Model/Dto/MediaVersionInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaVersionInfo.cs
@@ -3,9 +3,9 @@ using System.Collections.Generic;
namespace MediaBrowser.Model.Dto
{
- public class MediaVersionInfo
+ public class MediaSourceInfo
{
- public string ItemId { get; set; }
+ public string Id { get; set; }
public string Path { get; set; }
@@ -22,9 +22,5 @@ namespace MediaBrowser.Model.Dto
public Video3DFormat? Video3DFormat { get; set; }
public List<MediaStream> MediaStreams { get; set; }
-
- public List<ChapterInfoDto> Chapters { get; set; }
-
- public bool IsPrimaryVersion { get; set; }
}
}
diff --git a/MediaBrowser.Model/Entities/BaseItemInfo.cs b/MediaBrowser.Model/Entities/BaseItemInfo.cs
index b704bdb57..16a08d693 100644
--- a/MediaBrowser.Model/Entities/BaseItemInfo.cs
+++ b/MediaBrowser.Model/Entities/BaseItemInfo.cs
@@ -69,6 +69,12 @@ namespace MediaBrowser.Model.Entities
/// </summary>
/// <value>The thumb item identifier.</value>
public string BackdropItemId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the media version identifier.
+ /// </summary>
+ /// <value>The media version identifier.</value>
+ public string MediaSourceId { get; set; }
/// <summary>
/// Gets a value indicating whether this instance has primary image.
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 6a7e723a7..5e9b97939 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -132,6 +132,7 @@
<Compile Include="Search\SearchQuery.cs" />
<Compile Include="Session\BrowseRequest.cs" />
<Compile Include="Session\MessageCommand.cs" />
+ <Compile Include="Session\PlaybackReports.cs" />
<Compile Include="Session\PlayRequest.cs" />
<Compile Include="Session\PlaystateCommand.cs" />
<Compile Include="Logging\ILogManager.cs" />
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index 65509fd6d..398be3c7b 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -84,7 +84,7 @@ namespace MediaBrowser.Model.Querying
/// <summary>
/// The media versions
/// </summary>
- MediaVersions,
+ MediaSources,
/// <summary>
/// The metadata settings
diff --git a/MediaBrowser.Model/Session/PlaybackReports.cs b/MediaBrowser.Model/Session/PlaybackReports.cs
new file mode 100644
index 000000000..75dd3346c
--- /dev/null
+++ b/MediaBrowser.Model/Session/PlaybackReports.cs
@@ -0,0 +1,56 @@
+
+namespace MediaBrowser.Model.Session
+{
+ /// <summary>
+ /// Class PlaybackStartInfo.
+ /// </summary>
+ public class PlaybackStartInfo
+ {
+ public string UserId { get; set; }
+
+ public string ItemId { get; set; }
+
+ public string MediaSourceId { get; set; }
+
+ public bool IsSeekable { get; set; }
+
+ public string[] QueueableMediaTypes { get; set; }
+
+ public PlaybackStartInfo()
+ {
+ QueueableMediaTypes = new string[] { };
+ }
+ }
+
+ /// <summary>
+ /// Class PlaybackProgressInfo.
+ /// </summary>
+ public class PlaybackProgressInfo
+ {
+ public string UserId { get; set; }
+
+ public string ItemId { get; set; }
+
+ public string MediaSourceId { get; set; }
+
+ public long? PositionTicks { get; set; }
+
+ public bool IsPaused { get; set; }
+
+ public bool IsMuted { get; set; }
+ }
+
+ /// <summary>
+ /// Class PlaybackStopInfo.
+ /// </summary>
+ public class PlaybackStopInfo
+ {
+ public string UserId { get; set; }
+
+ public string ItemId { get; set; }
+
+ public string MediaSourceId { get; set; }
+
+ public long? PositionTicks { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Session/SessionCapabilities.cs b/MediaBrowser.Model/Session/SessionCapabilities.cs
index 731ebaccc..7b3b04ce9 100644
--- a/MediaBrowser.Model/Session/SessionCapabilities.cs
+++ b/MediaBrowser.Model/Session/SessionCapabilities.cs
@@ -7,6 +7,10 @@ namespace MediaBrowser.Model.Session
public bool SupportsFullscreenToggle { get; set; }
+ public bool SupportsOsdToggle { get; set; }
+
+ public bool SupportsNavigationControl { get; set; }
+
public SessionCapabilities()
{
PlayableMediaTypes = new string[] {};
diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs
index d44bbeaeb..09a8375d8 100644
--- a/MediaBrowser.Model/Session/SessionInfoDto.cs
+++ b/MediaBrowser.Model/Session/SessionInfoDto.cs
@@ -1,8 +1,8 @@
-using System.Diagnostics;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics;
namespace MediaBrowser.Model.Session
{
@@ -147,6 +147,18 @@ namespace MediaBrowser.Model.Session
/// <value><c>true</c> if [supports remote control]; otherwise, <c>false</c>.</value>
public bool SupportsRemoteControl { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether [supports osd toggle].
+ /// </summary>
+ /// <value><c>true</c> if [supports osd toggle]; otherwise, <c>false</c>.</value>
+ public bool SupportsOsdToggle { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [supports navigation commands].
+ /// </summary>
+ /// <value><c>true</c> if [supports navigation commands]; otherwise, <c>false</c>.</value>
+ public bool SupportsNavigationControl { get; set; }
+
public event PropertyChangedEventHandler PropertyChanged;
public SessionInfoDto()
diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
index 03fe5c802..391ab7cfd 100644
--- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
+++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
@@ -213,7 +213,7 @@ namespace MediaBrowser.Providers.Savers
builder.Append("<Added>" + SecurityElement.Escape(item.DateCreated.ToLocalTime().ToString("G")) + "</Added>");
- builder.Append("<LockData>" + item.DontFetchMeta.ToString().ToLower() + "</LockData>");
+ builder.Append("<LockData>" + item.IsLocked.ToString().ToLower() + "</LockData>");
if (item.LockedFields.Count > 0)
{
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index c94cdda84..66a9284d7 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -127,7 +127,7 @@ namespace MediaBrowser.Server.Implementations.Dto
public BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, User user = null)
where T : BaseItem, IItemByName
{
- var libraryItems = user != null ? user.RootFolder.GetRecursiveChildren(user) :
+ var libraryItems = user != null ? user.RootFolder.GetRecursiveChildren(user) :
_libraryManager.RootFolder.RecursiveChildren;
return GetItemByNameDto(item, fields, item.GetTaggedItems(libraryItems).ToList(), user);
@@ -267,12 +267,14 @@ namespace MediaBrowser.Server.Implementations.Dto
PlayableMediaTypes = session.PlayableMediaTypes,
RemoteEndPoint = session.RemoteEndPoint,
AdditionalUsers = session.AdditionalUsers,
- SupportsFullscreenToggle = session.SupportsFullscreenToggle
+ SupportsFullscreenToggle = session.SupportsFullscreenToggle,
+ SupportsNavigationControl = session.SupportsNavigationControl,
+ SupportsOsdToggle = session.SupportsOsdToggle
};
if (session.NowPlayingItem != null)
{
- dto.NowPlayingItem = GetBaseItemInfo(session.NowPlayingItem);
+ dto.NowPlayingItem = GetNowPlayingInfo(session.NowPlayingItem, session.NowPlayingMediaSourceId, session.NowPlayingRunTimeTicks);
}
if (session.UserId.HasValue)
@@ -288,9 +290,11 @@ namespace MediaBrowser.Server.Implementations.Dto
/// Converts a BaseItem to a BaseItemInfo
/// </summary>
/// <param name="item">The item.</param>
+ /// <param name="mediaSourceId">The media version identifier.</param>
+ /// <param name="nowPlayingRuntimeTicks">The now playing runtime ticks.</param>
/// <returns>BaseItemInfo.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
- public BaseItemInfo GetBaseItemInfo(BaseItem item)
+ private BaseItemInfo GetNowPlayingInfo(BaseItem item, string mediaSourceId, long? nowPlayingRuntimeTicks)
{
if (item == null)
{
@@ -303,7 +307,8 @@ namespace MediaBrowser.Server.Implementations.Dto
Name = item.Name,
MediaType = item.MediaType,
Type = item.GetClientTypeName(),
- RunTimeTicks = item.RunTimeTicks
+ RunTimeTicks = nowPlayingRuntimeTicks,
+ MediaSourceId = mediaSourceId
};
info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary);
@@ -735,7 +740,7 @@ namespace MediaBrowser.Server.Implementations.Dto
if (fields.Contains(ItemFields.Settings))
{
dto.LockedFields = item.LockedFields;
- dto.LockData = item.DontFetchMeta;
+ dto.LockData = item.IsLocked;
}
var hasBudget = item as IHasBudget;
@@ -1041,7 +1046,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{
dto.IsPlaceHolder = supportsPlaceHolders.IsPlaceHolder;
}
-
+
// Add audio info
var audio = item as Audio;
if (audio != null)
@@ -1058,8 +1063,8 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary);
}
- dto.MediaVersions = GetMediaVersions(audio);
- dto.MediaVersionCount = 1;
+ dto.MediaSources = GetMediaSources(audio);
+ dto.MediaSourceCount = 1;
}
var album = item as MusicAlbum;
@@ -1090,22 +1095,20 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.IsHD = video.IsHD;
dto.PartCount = video.AdditionalPartIds.Count + 1;
- dto.MediaVersionCount = video.AlternateVersionCount + 1;
+ dto.MediaSourceCount = video.MediaSourceCount;
- if (fields.Contains(ItemFields.MediaVersions))
+ if (fields.Contains(ItemFields.MediaSources))
{
- dto.MediaVersions = GetMediaVersions(video);
+ dto.MediaSources = GetMediaSources(video);
}
if (fields.Contains(ItemFields.Chapters))
{
List<ChapterInfoDto> chapters;
- if (dto.MediaVersions != null && dto.MediaVersions.Count > 0)
+ if (dto.MediaSources != null && dto.MediaSources.Count > 0)
{
- chapters = dto.MediaVersions.Where(i => i.IsPrimaryVersion)
- .SelectMany(i => i.Chapters)
- .ToList();
+ chapters = _itemRepo.GetChapters(item.Id).Select(c => GetChapterInfoDto(c, item)).ToList();
}
else
{
@@ -1127,9 +1130,9 @@ namespace MediaBrowser.Server.Implementations.Dto
{
List<MediaStream> mediaStreams;
- if (dto.MediaVersions != null && dto.MediaVersions.Count > 0)
+ if (dto.MediaSources != null && dto.MediaSources.Count > 0)
{
- mediaStreams = dto.MediaVersions.Where(i => i.IsPrimaryVersion)
+ mediaStreams = dto.MediaSources.Where(i => new Guid(i.Id) == item.Id)
.SelectMany(i => i.MediaStreams)
.ToList();
}
@@ -1264,11 +1267,11 @@ namespace MediaBrowser.Server.Implementations.Dto
}
}
- private List<MediaVersionInfo> GetMediaVersions(Video item)
+ private List<MediaSourceInfo> GetMediaSources(Video item)
{
- var result = item.GetAlternateVersions().Select(i => GetVersionInfo(i, false)).ToList();
+ var result = item.GetAlternateVersions().Select(GetVersionInfo).ToList();
- result.Add(GetVersionInfo(item, true));
+ result.Add(GetVersionInfo(item));
return result.OrderBy(i =>
{
@@ -1286,49 +1289,47 @@ namespace MediaBrowser.Server.Implementations.Dto
return stream == null || stream.Width == null ? 0 : stream.Width.Value;
})
- .ThenBy(i => i.IsPrimaryVersion ? 0 : 1)
.ToList();
}
- private List<MediaVersionInfo> GetMediaVersions(Audio item)
+ private List<MediaSourceInfo> GetMediaSources(Audio item)
{
- var result = new List<MediaVersionInfo>();
-
- result.Add(GetVersionInfo(item, true));
+ var result = new List<MediaSourceInfo>
+ {
+ GetVersionInfo(item, true)
+ };
return result;
}
- private MediaVersionInfo GetVersionInfo(Video i, bool isPrimary)
+ private MediaSourceInfo GetVersionInfo(Video i)
{
- return new MediaVersionInfo
- {
- Chapters = _itemRepo.GetChapters(i.Id).Select(c => GetChapterInfoDto(c, i)).ToList(),
+ var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList();
- ItemId = i.Id.ToString("N"),
+ return new MediaSourceInfo
+ {
+ Id = i.Id.ToString("N"),
IsoType = i.IsoType,
LocationType = i.LocationType,
- MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
- Name = GetAlternateVersionName(i),
+ MediaStreams = mediaStreams,
+ Name = GetMediaSourceName(i, mediaStreams),
Path = GetMappedPath(i),
RunTimeTicks = i.RunTimeTicks,
Video3DFormat = i.Video3DFormat,
- VideoType = i.VideoType,
- IsPrimaryVersion = isPrimary
+ VideoType = i.VideoType
};
}
- private MediaVersionInfo GetVersionInfo(Audio i, bool isPrimary)
+ private MediaSourceInfo GetVersionInfo(Audio i, bool isPrimary)
{
- return new MediaVersionInfo
+ return new MediaSourceInfo
{
- ItemId = i.Id.ToString("N"),
+ Id = i.Id.ToString("N"),
LocationType = i.LocationType,
MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
Name = i.Name,
Path = GetMappedPath(i),
- RunTimeTicks = i.RunTimeTicks,
- IsPrimaryVersion = isPrimary
+ RunTimeTicks = i.RunTimeTicks
};
}
@@ -1351,32 +1352,29 @@ namespace MediaBrowser.Server.Implementations.Dto
return path;
}
- private string GetAlternateVersionName(Video video)
+ private string GetMediaSourceName(Video video, List<MediaStream> mediaStreams)
{
- var name = "";
+ var terms = new List<string>();
- var videoStream = video.GetDefaultVideoStream();
+ var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+ var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
if (video.Video3DFormat.HasValue)
{
- name = "3D " + name;
- name = name.Trim();
+ terms.Add("3D");
}
if (video.VideoType == VideoType.BluRay)
{
- name = name + " " + "Bluray";
- name = name.Trim();
+ terms.Add("Bluray");
}
else if (video.VideoType == VideoType.Dvd)
{
- name = name + " " + "DVD";
- name = name.Trim();
+ terms.Add("DVD");
}
else if (video.VideoType == VideoType.HdDvd)
{
- name = name + " " + "HD-DVD";
- name = name.Trim();
+ terms.Add("HD-DVD");
}
else if (video.VideoType == VideoType.Iso)
{
@@ -1384,18 +1382,17 @@ namespace MediaBrowser.Server.Implementations.Dto
{
if (video.IsoType.Value == IsoType.BluRay)
{
- name = name + " " + "Bluray";
+ terms.Add("Bluray");
}
else if (video.IsoType.Value == IsoType.Dvd)
{
- name = name + " " + "DVD";
+ terms.Add("DVD");
}
}
else
{
- name = name + " " + "ISO";
+ terms.Add("ISO");
}
- name = name.Trim();
}
if (videoStream != null)
@@ -1404,44 +1401,45 @@ namespace MediaBrowser.Server.Implementations.Dto
{
if (videoStream.Width.Value >= 3800)
{
- name = name + " " + "4K";
- name = name.Trim();
+ terms.Add("4K");
}
else if (videoStream.Width.Value >= 1900)
{
- name = name + " " + "1080P";
- name = name.Trim();
+ terms.Add("1080P");
}
else if (videoStream.Width.Value >= 1270)
{
- name = name + " " + "720P";
- name = name.Trim();
+ terms.Add("720P");
}
else if (videoStream.Width.Value >= 700)
{
- name = name + " " + "480p";
- name = name.Trim();
+ terms.Add("480P");
}
else
{
- name = name + " " + "SD";
- name = name.Trim();
+ terms.Add("SD");
}
}
}
if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
{
- name = name + " " + videoStream.Codec.ToUpper();
- name = name.Trim();
+ terms.Add(videoStream.Codec.ToUpper());
}
- if (string.IsNullOrWhiteSpace(name))
+ if (audioStream != null)
{
- return video.Name;
+ var audioCodec = string.Equals(audioStream.Codec, "dca", StringComparison.OrdinalIgnoreCase)
+ ? audioStream.Profile
+ : audioStream.Codec;
+
+ if (!string.IsNullOrEmpty(audioCodec))
+ {
+ terms.Add(audioCodec.ToUpper());
+ }
}
- return name;
+ return string.Join("/", terms.ToArray());
}
private string GetMappedPath(string path)
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index 5d326f1ca..d44811886 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -180,7 +180,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
result.StatusMessage = string.Empty;
return;
}
-
+
if (fileExists || otherDuplicatePaths.Count > 0)
{
result.Status = FileSortingStatus.SkippedExisting;
@@ -453,24 +453,22 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private bool IsSameEpisode(string sourcePath, string newPath)
{
+ var sourceFileInfo = new FileInfo(sourcePath);
+ var destinationFileInfo = new FileInfo(newPath);
- FileInfo sourceFileInfo = new FileInfo(sourcePath);
- FileInfo destinationFileInfo = new FileInfo(newPath);
-
- try
- {
- if (sourceFileInfo.Length == destinationFileInfo.Length)
- {
- return true;
- }
- }
- catch (FileNotFoundException)
+ try
+ {
+ if (sourceFileInfo.Length == destinationFileInfo.Length)
{
- return false;
+ return true;
}
-
+ }
+ catch (FileNotFoundException)
+ {
return false;
+ }
+ return false;
}
}
}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs b/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
index 23d0363cb..7f936791a 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
@@ -67,6 +67,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
.Replace("!", " ")
.Replace("(", " ")
.Replace(")", " ")
+ .Replace(":", " ")
.Replace(",", " ")
.Replace("-", " ")
.Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase)
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index 65bbb35ba..0a0b3f4bc 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -427,11 +427,11 @@ namespace MediaBrowser.Server.Implementations.IO
{
if (_updateTimer == null)
{
- _updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
+ _updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
else
{
- _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
+ _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
index ad2db5abb..3097495e3 100644
--- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
+++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
@@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.Library
EnsureName(item, args);
item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
- item.Parents.Any(i => i.DontFetchMeta);
+ item.Parents.Any(i => i.IsLocked);
// Make sure DateCreated and DateModified have values
EntityResolutionHelper.EnsureDates(fileSystem, item, args, true);
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index d7b2b1321..13a1e8f16 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -190,7 +190,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
/// <param name="directoryService">The directory service.</param>
/// <param name="supportMultiFileItems">if set to <c>true</c> [support multi file items].</param>
/// <returns>Movie.</returns>
- private T FindMovie<T>(string path, Folder parent, IEnumerable<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService, bool supportMultiFileItems, bool supportsAlternateVersions)
+ private T FindMovie<T>(string path, Folder parent, IEnumerable<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService, bool supportMultiFileItems, bool supportsMultipleSources)
where T : Video, new()
{
var movies = new List<T>();
@@ -262,9 +262,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
return result;
}
}
- if (supportsAlternateVersions)
+ if (supportsMultipleSources)
{
- var result = GetMovieWithAlternateVersions(movies);
+ var result = GetMovieWithMultipleSources(movies);
if (result != null)
{
@@ -393,7 +393,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
return null;
}
- private T GetMovieWithAlternateVersions<T>(IEnumerable<T> movies)
+ private T GetMovieWithMultipleSources<T>(IEnumerable<T> movies)
where T : Video, new()
{
var sortedMovies = movies.OrderBy(i => i.Path).ToList();
diff --git a/MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs b/MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs
index 71f70421a..53f28e7ae 100644
--- a/MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs
+++ b/MediaBrowser.Server.Implementations/Roku/RokuControllerFactory.cs
@@ -1,8 +1,10 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization;
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Server.Implementations.Roku
{
@@ -23,6 +25,9 @@ namespace MediaBrowser.Server.Implementations.Roku
{
if (string.Equals(session.Client, "roku", StringComparison.OrdinalIgnoreCase))
{
+ session.PlayableMediaTypes = new List<string> { MediaType.Video, MediaType.Audio };
+ session.SupportsFullscreenToggle = false;
+
return new RokuSessionController(_httpClient, _json, _appHost, session);
}
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index 1fb5af127..9d405a175 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -218,15 +218,29 @@ namespace MediaBrowser.Server.Implementations.Session
/// </summary>
/// <param name="session">The session.</param>
/// <param name="item">The item.</param>
+ /// <param name="mediaSourceId">The media version identifier.</param>
/// <param name="isPaused">if set to <c>true</c> [is paused].</param>
+ /// <param name="isMuted">if set to <c>true</c> [is muted].</param>
/// <param name="currentPositionTicks">The current position ticks.</param>
- private void UpdateNowPlayingItem(SessionInfo session, BaseItem item, bool isPaused, bool isMuted, long? currentPositionTicks = null)
+ private void UpdateNowPlayingItem(SessionInfo session, BaseItem item, string mediaSourceId, bool isPaused, bool isMuted, long? currentPositionTicks = null)
{
session.IsMuted = isMuted;
session.IsPaused = isPaused;
session.NowPlayingPositionTicks = currentPositionTicks;
session.NowPlayingItem = item;
session.LastActivityDate = DateTime.UtcNow;
+ session.NowPlayingMediaSourceId = mediaSourceId;
+
+ if (string.IsNullOrWhiteSpace(mediaSourceId))
+ {
+ session.NowPlayingRunTimeTicks = item.RunTimeTicks;
+ }
+ else
+ {
+ var version = _libraryManager.GetItemById(new Guid(mediaSourceId));
+
+ session.NowPlayingRunTimeTicks = version.RunTimeTicks;
+ }
}
/// <summary>
@@ -246,6 +260,8 @@ namespace MediaBrowser.Server.Implementations.Session
session.NowPlayingItem = null;
session.NowPlayingPositionTicks = null;
session.IsPaused = false;
+ session.NowPlayingRunTimeTicks = null;
+ session.NowPlayingMediaSourceId = null;
}
}
@@ -352,7 +368,9 @@ namespace MediaBrowser.Server.Implementations.Session
var item = info.Item;
- UpdateNowPlayingItem(session, item, false, false);
+ var mediaSourceId = GetMediaSourceId(item, info.MediaSourceId);
+
+ UpdateNowPlayingItem(session, item, mediaSourceId, false, false);
session.CanSeek = info.CanSeek;
session.QueueableMediaTypes = info.QueueableMediaTypes;
@@ -371,7 +389,8 @@ namespace MediaBrowser.Server.Implementations.Session
EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
{
Item = item,
- Users = users
+ Users = users,
+ MediaSourceId = info.MediaSourceId
}, _logger);
}
@@ -405,7 +424,7 @@ namespace MediaBrowser.Server.Implementations.Session
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
/// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
- public async Task OnPlaybackProgress(PlaybackProgressInfo info)
+ public async Task OnPlaybackProgress(Controller.Session.PlaybackProgressInfo info)
{
if (info == null)
{
@@ -419,7 +438,9 @@ namespace MediaBrowser.Server.Implementations.Session
var session = Sessions.First(i => i.Id.Equals(info.SessionId));
- UpdateNowPlayingItem(session, info.Item, info.IsPaused, info.IsMuted, info.PositionTicks);
+ var mediaSourceId = GetMediaSourceId(info.Item, info.MediaSourceId);
+
+ UpdateNowPlayingItem(session, info.Item, mediaSourceId, info.IsPaused, info.IsMuted, info.PositionTicks);
var key = info.Item.GetUserDataKey();
@@ -434,7 +455,8 @@ namespace MediaBrowser.Server.Implementations.Session
{
Item = info.Item,
Users = users,
- PlaybackPositionTicks = info.PositionTicks
+ PlaybackPositionTicks = info.PositionTicks,
+ MediaSourceId = mediaSourceId
}, _logger);
}
@@ -458,7 +480,7 @@ namespace MediaBrowser.Server.Implementations.Session
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">info</exception>
/// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
- public async Task OnPlaybackStopped(PlaybackStopInfo info)
+ public async Task OnPlaybackStopped(Controller.Session.PlaybackStopInfo info)
{
if (info == null)
{
@@ -494,16 +516,32 @@ namespace MediaBrowser.Server.Implementations.Session
playedToCompletion = await OnPlaybackStopped(user.Id, key, info.Item, info.PositionTicks).ConfigureAwait(false);
}
+ var mediaSourceId = GetMediaSourceId(info.Item, info.MediaSourceId);
+
EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs
{
Item = info.Item,
Users = users,
PlaybackPositionTicks = info.PositionTicks,
- PlayedToCompletion = playedToCompletion
+ PlayedToCompletion = playedToCompletion,
+ MediaSourceId = mediaSourceId
}, _logger);
}
+ private string GetMediaSourceId(BaseItem item, string reportedMediaSourceId)
+ {
+ if (string.IsNullOrWhiteSpace(reportedMediaSourceId))
+ {
+ if (item is Video || item is Audio)
+ {
+ reportedMediaSourceId = item.Id.ToString("N");
+ }
+ }
+
+ return reportedMediaSourceId;
+ }
+
private async Task<bool> OnPlaybackStopped(Guid userId, string userDataKey, BaseItem item, long? positionTicks)
{
var data = _userDataRepository.GetUserData(userId, userDataKey);
@@ -899,6 +937,8 @@ namespace MediaBrowser.Server.Implementations.Session
session.PlayableMediaTypes = capabilities.PlayableMediaTypes.ToList();
session.SupportsFullscreenToggle = capabilities.SupportsFullscreenToggle;
+ session.SupportsOsdToggle = capabilities.SupportsOsdToggle;
+ session.SupportsNavigationControl = capabilities.SupportsNavigationControl;
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
index e5fbc7d9f..fe32e2328 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -223,6 +223,11 @@ namespace MediaBrowser.Server.Implementations.Session
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
};
+ if (vals.Length > 3)
+ {
+ info.MediaSourceId = vals[3];
+ }
+
_sessionManager.OnPlaybackStart(info);
}
}
@@ -265,6 +270,11 @@ namespace MediaBrowser.Server.Implementations.Session
SessionId = session.Id
};
+ if (vals.Length > 4)
+ {
+ info.MediaSourceId = vals[4];
+ }
+
_sessionManager.OnPlaybackProgress(info);
}
}
@@ -304,6 +314,11 @@ namespace MediaBrowser.Server.Implementations.Session
SessionId = session.Id
};
+ if (vals.Length > 2)
+ {
+ info.MediaSourceId = vals[2];
+ }
+
_sessionManager.OnPlaybackStopped(info);
}
}
diff --git a/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs b/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs
index 39a68b3f6..b76bf0a9c 100644
--- a/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs
+++ b/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Text;
namespace MediaBrowser.Server.Implementations.Sorting
diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js
index 97c26ec78..765cae612 100644
--- a/MediaBrowser.WebDashboard/ApiClient.js
+++ b/MediaBrowser.WebDashboard/ApiClient.js
@@ -2192,20 +2192,6 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
};
/**
- * Gets a list of all available conrete BaseItem types from the server
- */
- self.getItemTypes = function (options) {
-
- var url = self.getUrl("Library/ItemTypes", options);
-
- return self.ajax({
- type: "GET",
- url: url,
- dataType: "json"
- });
- };
-
- /**
* Constructs a url for a user image
* @param {String} userId
* @param {Object} options
@@ -3805,7 +3791,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
* @param {String} userId
* @param {String} itemId
*/
- self.reportPlaybackStart = function (userId, itemId, canSeek, queueableMediaTypes) {
+ self.reportPlaybackStart = function (userId, itemId, mediaSourceId, canSeek, queueableMediaTypes) {
if (!userId) {
throw new Error("null userId");
@@ -3823,6 +3809,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
var deferred = $.Deferred();
var msg = [itemId, canSeek, queueableMediaTypes];
+
+ if (mediaSourceId) {
+ msg.push(mediaSourceId);
+ }
self.sendWebSocketMessage("PlaybackStart", msg.join('|'));
@@ -3830,10 +3820,16 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
return deferred.promise();
}
- var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, {
+ var params = {
CanSeek: canSeek,
QueueableMediaTypes: queueableMediaTypes
- });
+ };
+
+ if (mediaSourceId) {
+ params.mediaSourceId = mediaSourceId;
+ }
+
+ var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
return self.ajax({
type: "POST",
@@ -3846,7 +3842,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
* @param {String} userId
* @param {String} itemId
*/
- self.reportPlaybackProgress = function (userId, itemId, positionTicks, isPaused, isMuted) {
+ self.reportPlaybackProgress = function (userId, itemId, mediaSourceId, positionTicks, isPaused, isMuted) {
if (!userId) {
throw new Error("null userId");
@@ -3860,7 +3856,12 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
var deferred = $.Deferred();
- var msgData = itemId + "|" + (positionTicks == null ? "" : positionTicks) + "|" + (isPaused == null ? "" : isPaused) + "|" + (isMuted == null ? "" : isMuted);
+ var msgData = itemId;
+
+ msgData += "|" + (positionTicks == null ? "" : positionTicks);
+ msgData += "|" + (isPaused == null ? "" : isPaused);
+ msgData += "|" + (isMuted == null ? "" : isMuted);
+ msgData += "|" + (mediaSourceId == null ? "" : mediaSourceId);
self.sendWebSocketMessage("PlaybackProgress", msgData);
@@ -3877,6 +3878,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
params.positionTicks = positionTicks;
}
+ if (mediaSourceId) {
+ params.mediaSourceId = mediaSourceId;
+ }
+
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId + "/Progress", params);
return self.ajax({
@@ -3890,7 +3895,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
* @param {String} userId
* @param {String} itemId
*/
- self.reportPlaybackStopped = function (userId, itemId, positionTicks) {
+ self.reportPlaybackStopped = function (userId, itemId, mediaSourceId, positionTicks) {
if (!userId) {
throw new Error("null userId");
@@ -3904,20 +3909,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
var deferred = $.Deferred();
- self.sendWebSocketMessage("PlaybackStopped", itemId + "|" + (positionTicks == null ? "" : positionTicks));
+ var msg = itemId;
+ msg += "|" + (positionTicks == null ? "" : positionTicks);
+ msg += "|" + (mediaSourceId == null ? "" : mediaSourceId);
+
+ self.sendWebSocketMessage("PlaybackStopped", msg);
deferred.resolveWith(null, []);
return deferred.promise();
}
- var params = {
-
- };
+ var params = {};
if (positionTicks) {
params.positionTicks = positionTicks;
}
+ if (mediaSourceId) {
+ params.mediaSourceId = mediaSourceId;
+ }
+
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
return self.ajax({
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 16eec2640..a0dbae3d4 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -84,9 +84,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
- <EmbeddedResource Include="ApiClient.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </EmbeddedResource>
+ <EmbeddedResource Include="ApiClient.js" />
<Content Include="dashboard-ui\advancedserversettings.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index 0bdea523e..7396527bb 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="MediaBrowser.ApiClient.Javascript" version="3.0.245" targetFramework="net45" />
+ <package id="MediaBrowser.ApiClient.Javascript" version="3.0.247" targetFramework="net45" />
</packages> \ No newline at end of file