Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
543ce24c10
|
@ -94,7 +94,7 @@ namespace MediaBrowser.Api
|
||||||
var item = _dtoService.GetItemByDtoId(request.ItemId);
|
var item = _dtoService.GetItemByDtoId(request.ItemId);
|
||||||
|
|
||||||
var newLockData = request.LockData ?? false;
|
var newLockData = request.LockData ?? false;
|
||||||
var dontFetchMetaChanged = item.DontFetchMeta != newLockData;
|
var dontFetchMetaChanged = item.IsLocked != newLockData;
|
||||||
|
|
||||||
UpdateItem(request, item);
|
UpdateItem(request, item);
|
||||||
|
|
||||||
|
|
|
@ -1308,7 +1308,9 @@ namespace MediaBrowser.Api.Playback
|
||||||
RequestedUrl = url
|
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)
|
if (user != null && item.GetPlayAccess(user) != PlayAccess.Full)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,9 @@ namespace MediaBrowser.Api.Playback
|
||||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||||
public string Id { get; set; }
|
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")]
|
[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; }
|
public string DeviceId { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -241,6 +241,9 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||||
public string Id { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
|
/// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -277,6 +280,9 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||||
public string Id { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the position ticks.
|
/// Gets or sets the position ticks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -312,6 +318,9 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||||
public string Id { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the position ticks.
|
/// Gets or sets the position ticks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -736,7 +745,8 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
CanSeek = request.CanSeek,
|
CanSeek = request.CanSeek,
|
||||||
Item = item,
|
Item = item,
|
||||||
SessionId = GetSession().Id,
|
SessionId = GetSession().Id,
|
||||||
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
|
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList(),
|
||||||
|
MediaSourceId = request.MediaSourceId
|
||||||
};
|
};
|
||||||
|
|
||||||
_sessionManager.OnPlaybackStart(info);
|
_sessionManager.OnPlaybackStart(info);
|
||||||
|
@ -758,7 +768,8 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
PositionTicks = request.PositionTicks,
|
PositionTicks = request.PositionTicks,
|
||||||
IsMuted = request.IsMuted,
|
IsMuted = request.IsMuted,
|
||||||
IsPaused = request.IsPaused,
|
IsPaused = request.IsPaused,
|
||||||
SessionId = GetSession().Id
|
SessionId = GetSession().Id,
|
||||||
|
MediaSourceId = request.MediaSourceId
|
||||||
};
|
};
|
||||||
|
|
||||||
var task = _sessionManager.OnPlaybackProgress(info);
|
var task = _sessionManager.OnPlaybackProgress(info);
|
||||||
|
@ -782,7 +793,8 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
{
|
{
|
||||||
Item = item,
|
Item = item,
|
||||||
PositionTicks = request.PositionTicks,
|
PositionTicks = request.PositionTicks,
|
||||||
SessionId = session.Id
|
SessionId = session.Id,
|
||||||
|
MediaSourceId = request.MediaSourceId
|
||||||
};
|
};
|
||||||
|
|
||||||
var task = _sessionManager.OnPlaybackStopped(info);
|
var task = _sessionManager.OnPlaybackStopped(info);
|
||||||
|
|
|
@ -14,8 +14,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class GetYears
|
/// Class GetYears
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Route("/Years", "GET")]
|
[Route("/Years", "GET", Summary = "Gets all years from a given item, folder, or the entire library")]
|
||||||
[Api(Description = "Gets all years from a given item, folder, or the entire library")]
|
|
||||||
public class GetYears : GetItemsByName
|
public class GetYears : GetItemsByName
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -23,8 +22,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class GetYear
|
/// Class GetYear
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Route("/Years/{Year}", "GET")]
|
[Route("/Years/{Year}", "GET", Summary = "Gets a year")]
|
||||||
[Api(Description = "Gets a year")]
|
|
||||||
public class GetYear : IReturn<BaseItemDto>
|
public class GetYear : IReturn<BaseItemDto>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -13,8 +13,7 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
[Route("/Videos/{Id}/AdditionalParts", "GET")]
|
[Route("/Videos/{Id}/AdditionalParts", "GET", Summary = "Gets additional parts for a video.")]
|
||||||
[Api(Description = "Gets additional parts for a video.")]
|
|
||||||
public class GetAdditionalParts : IReturn<ItemsResult>
|
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")]
|
[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; }
|
public string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Videos/{Id}/AlternateVersions", "DELETE")]
|
[Route("/Videos/{Id}/AlternateSources", "DELETE", Summary = "Removes alternate video sources.")]
|
||||||
[Api(Description = "Assigns videos as alternates of antoher.")]
|
public class DeleteAlternateSources : IReturnVoid
|
||||||
public class DeleteAlternateVersions : IReturnVoid
|
|
||||||
{
|
{
|
||||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Videos/MergeVersions", "POST")]
|
[Route("/Videos/MergeVersions", "POST", Summary = "Merges videos into a single record")]
|
||||||
[Api(Description = "Merges videos into a single record")]
|
|
||||||
public class MergeVersions : IReturnVoid
|
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)]
|
[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);
|
return ToOptimizedSerializedResultUsingCache(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(DeleteAlternateVersions request)
|
public void Delete(DeleteAlternateSources request)
|
||||||
{
|
{
|
||||||
var task = RemoveAlternateVersions(request);
|
var task = RemoveAlternateVersions(request);
|
||||||
|
|
||||||
Task.WaitAll(task);
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemoveAlternateVersions(DeleteAlternateVersions request)
|
private async Task RemoveAlternateVersions(DeleteAlternateSources request)
|
||||||
{
|
{
|
||||||
var video = (Video)_dtoService.GetItemByDtoId(request.Id);
|
var video = (Video)_dtoService.GetItemByDtoId(request.Id);
|
||||||
|
|
||||||
|
@ -146,7 +143,7 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
var videos = items.Cast<Video>().ToList();
|
var videos = items.Cast<Video>().ToList();
|
||||||
|
|
||||||
var videosWithVersions = videos.Where(i => i.AlternateVersionCount > 0)
|
var videosWithVersions = videos.Where(i => i.MediaSourceCount > 1)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (videosWithVersions.Count > 1)
|
if (videosWithVersions.Count > 1)
|
||||||
|
@ -158,14 +155,27 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
if (primaryVersion == null)
|
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();
|
var stream = i.GetDefaultVideoStream();
|
||||||
|
|
||||||
return stream == null || stream.Width == null ? 0 : stream.Width.Value;
|
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))
|
foreach (var item in videos.Where(i => i.Id != primaryVersion.Id))
|
||||||
|
|
56
MediaBrowser.Controller/Dlna/CodecProfile.cs
Normal file
56
MediaBrowser.Controller/Dlna/CodecProfile.cs
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,5 +67,12 @@ namespace MediaBrowser.Controller.Dlna
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
|
public HeaderMatchType Match { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum HeaderMatchType
|
||||||
|
{
|
||||||
|
Equals = 0,
|
||||||
|
Substring = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,10 +55,17 @@ namespace MediaBrowser.Controller.Dlna
|
||||||
|
|
||||||
public string ProtocolInfo { get; set; }
|
public string ProtocolInfo { get; set; }
|
||||||
|
|
||||||
|
public MediaProfile[] MediaProfiles { get; set; }
|
||||||
|
public CodecProfile[] CodecProfiles { get; set; }
|
||||||
|
|
||||||
|
public int TimelineOffsetSeconds { get; set; }
|
||||||
|
|
||||||
public DeviceProfile()
|
public DeviceProfile()
|
||||||
{
|
{
|
||||||
DirectPlayProfiles = new DirectPlayProfile[] { };
|
DirectPlayProfiles = new DirectPlayProfile[] { };
|
||||||
TranscodingProfiles = new TranscodingProfile[] { };
|
TranscodingProfiles = new TranscodingProfile[] { };
|
||||||
|
MediaProfiles = new MediaProfile[] { };
|
||||||
|
CodecProfiles = new CodecProfile[] { };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +1,14 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Dlna
|
namespace MediaBrowser.Controller.Dlna
|
||||||
{
|
{
|
||||||
public class DirectPlayProfile
|
public class DirectPlayProfile
|
||||||
{
|
{
|
||||||
public string Container { get; set; }
|
public string[] Containers { get; set; }
|
||||||
public string AudioCodec { get; set; }
|
public string AudioCodec { get; set; }
|
||||||
public string VideoCodec { 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 DlnaProfileType Type { get; set; }
|
||||||
|
|
||||||
public List<ProfileCondition> Conditions { get; set; }
|
public List<ProfileCondition> Conditions { get; set; }
|
||||||
|
@ -62,37 +16,25 @@ namespace MediaBrowser.Controller.Dlna
|
||||||
public DirectPlayProfile()
|
public DirectPlayProfile()
|
||||||
{
|
{
|
||||||
Conditions = new List<ProfileCondition>();
|
Conditions = new List<ProfileCondition>();
|
||||||
}
|
|
||||||
|
Containers = new string[] { };
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ProfileCondition
|
public List<string> GetAudioCodecs()
|
||||||
{
|
{
|
||||||
public ProfileConditionType Condition { get; set; }
|
return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
|
||||||
public ProfileConditionValue Value { get; set; }
|
}
|
||||||
|
|
||||||
|
public List<string> GetVideoCodecs()
|
||||||
|
{
|
||||||
|
return (VideoCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DlnaProfileType
|
public enum DlnaProfileType
|
||||||
{
|
{
|
||||||
Audio = 0,
|
Audio = 0,
|
||||||
Video = 1
|
Video = 1,
|
||||||
}
|
Photo = 2
|
||||||
|
|
||||||
public enum ProfileConditionType
|
|
||||||
{
|
|
||||||
Equals = 0,
|
|
||||||
NotEquals = 1,
|
|
||||||
LessThanEqual = 2,
|
|
||||||
GreaterThanEqual = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ProfileConditionValue
|
|
||||||
{
|
|
||||||
AudioChannels,
|
|
||||||
AudioBitrate,
|
|
||||||
Filesize,
|
|
||||||
VideoWidth,
|
|
||||||
VideoHeight,
|
|
||||||
VideoBitrate,
|
|
||||||
VideoFramerate
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
26
MediaBrowser.Controller/Dlna/MediaProfile.cs
Normal file
26
MediaBrowser.Controller/Dlna/MediaProfile.cs
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,12 +8,7 @@ namespace MediaBrowser.Controller.Dlna
|
||||||
|
|
||||||
public DlnaProfileType Type { get; set; }
|
public DlnaProfileType Type { get; set; }
|
||||||
|
|
||||||
public string MimeType { get; set; }
|
|
||||||
|
|
||||||
public string OrgPn { get; set; }
|
|
||||||
|
|
||||||
public string VideoCodec { get; set; }
|
public string VideoCodec { get; set; }
|
||||||
|
|
||||||
public string AudioCodec { get; set; }
|
public string AudioCodec { get; set; }
|
||||||
|
|
||||||
public List<TranscodingSetting> Settings { get; set; }
|
public List<TranscodingSetting> Settings { get; set; }
|
||||||
|
|
|
@ -28,13 +28,6 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// <returns>SessionInfoDto.</returns>
|
/// <returns>SessionInfoDto.</returns>
|
||||||
SessionInfoDto GetSessionInfoDto(SessionInfo session);
|
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>
|
/// <summary>
|
||||||
/// Gets the dto id.
|
/// Gets the dto id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -37,11 +37,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
public int AlternateVersionCount
|
public int MediaSourceCount
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return LinkedAlternateVersions.Count + LocalAlternateVersionIds.Count;
|
return LinkedAlternateVersions.Count + LocalAlternateVersionIds.Count + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
public List<User> Users { get; set; }
|
public List<User> Users { get; set; }
|
||||||
public long? PlaybackPositionTicks { get; set; }
|
public long? PlaybackPositionTicks { get; set; }
|
||||||
public BaseItem Item { get; set; }
|
public BaseItem Item { get; set; }
|
||||||
|
public string MediaSourceId { get; set; }
|
||||||
|
|
||||||
public PlaybackProgressEventArgs()
|
public PlaybackProgressEventArgs()
|
||||||
{
|
{
|
||||||
|
|
|
@ -78,10 +78,12 @@
|
||||||
<Compile Include="Channels\Channel.cs" />
|
<Compile Include="Channels\Channel.cs" />
|
||||||
<Compile Include="Collections\CollectionCreationOptions.cs" />
|
<Compile Include="Collections\CollectionCreationOptions.cs" />
|
||||||
<Compile Include="Collections\ICollectionManager.cs" />
|
<Compile Include="Collections\ICollectionManager.cs" />
|
||||||
|
<Compile Include="Dlna\CodecProfile.cs" />
|
||||||
<Compile Include="Dlna\DeviceIdentification.cs" />
|
<Compile Include="Dlna\DeviceIdentification.cs" />
|
||||||
<Compile Include="Dlna\DirectPlayProfile.cs" />
|
<Compile Include="Dlna\DirectPlayProfile.cs" />
|
||||||
<Compile Include="Dlna\IDlnaManager.cs" />
|
<Compile Include="Dlna\IDlnaManager.cs" />
|
||||||
<Compile Include="Dlna\DeviceProfile.cs" />
|
<Compile Include="Dlna\DeviceProfile.cs" />
|
||||||
|
<Compile Include="Dlna\MediaProfile.cs" />
|
||||||
<Compile Include="Dlna\TranscodingProfile.cs" />
|
<Compile Include="Dlna\TranscodingProfile.cs" />
|
||||||
<Compile Include="Drawing\IImageProcessor.cs" />
|
<Compile Include="Drawing\IImageProcessor.cs" />
|
||||||
<Compile Include="Drawing\ImageFormat.cs" />
|
<Compile Include="Drawing\ImageFormat.cs" />
|
||||||
|
|
|
@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The session id.</value>
|
/// <value>The session id.</value>
|
||||||
public Guid SessionId { get; set; }
|
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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,5 +34,11 @@ namespace MediaBrowser.Controller.Session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The position ticks.</value>
|
/// <value>The position ticks.</value>
|
||||||
public long? PositionTicks { get; set; }
|
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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,11 @@ namespace MediaBrowser.Controller.Session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The position ticks.</value>
|
/// <value>The position ticks.</value>
|
||||||
public long? PositionTicks { get; set; }
|
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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,12 +119,24 @@ namespace MediaBrowser.Controller.Session
|
||||||
/// <value>The now playing item.</value>
|
/// <value>The now playing item.</value>
|
||||||
public BaseItem NowPlayingItem { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the now playing position ticks.
|
/// Gets or sets the now playing position ticks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The now playing position ticks.</value>
|
/// <value>The now playing position ticks.</value>
|
||||||
public long? NowPlayingPositionTicks { get; set; }
|
public long? NowPlayingPositionTicks { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this instance is paused.
|
/// Gets or sets a value indicating whether this instance is paused.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -161,6 +173,18 @@ namespace MediaBrowser.Controller.Session
|
||||||
/// <value><c>true</c> if [supports fullscreen toggle]; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if [supports fullscreen toggle]; otherwise, <c>false</c>.</value>
|
||||||
public bool SupportsFullscreenToggle { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance is active.
|
/// Gets a value indicating whether this instance is active.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -62,13 +62,11 @@ namespace MediaBrowser.Dlna
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"mkv"},
|
Containers = new[]{"mkv"},
|
||||||
MimeType = "x-mkv",
|
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
MimeType = "x-msvideo",
|
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
|
@ -76,6 +74,23 @@ namespace MediaBrowser.Dlna
|
||||||
Containers = new[]{"mp4"},
|
Containers = new[]{"mp4"},
|
||||||
Type = DlnaProfileType.Video
|
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
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"mkv"},
|
Containers = new[]{"mkv"},
|
||||||
MimeType = "x-mkv",
|
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
MimeType = "x-msvideo",
|
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
|
@ -128,6 +141,23 @@ namespace MediaBrowser.Dlna
|
||||||
Containers = new[]{"mp4"},
|
Containers = new[]{"mp4"},
|
||||||
Type = DlnaProfileType.Video
|
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
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"mkv"},
|
Containers = new[]{"mkv"},
|
||||||
MimeType = "x-mkv",
|
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
MimeType = "x-msvideo",
|
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
|
@ -180,6 +208,23 @@ namespace MediaBrowser.Dlna
|
||||||
Containers = new[]{"mp4"},
|
Containers = new[]{"mp4"},
|
||||||
Type = DlnaProfileType.Video
|
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
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
MimeType = "avi",
|
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
|
@ -225,6 +269,16 @@ namespace MediaBrowser.Dlna
|
||||||
Containers = new[]{"mp4"},
|
Containers = new[]{"mp4"},
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MediaProfiles = new[]
|
||||||
|
{
|
||||||
|
new MediaProfile
|
||||||
|
{
|
||||||
|
Container ="avi",
|
||||||
|
MimeType = "video/avi",
|
||||||
|
Type = DlnaProfileType.Video
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -263,7 +317,16 @@ namespace MediaBrowser.Dlna
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
MimeType = "x-msvideo",
|
Type = DlnaProfileType.Video
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MediaProfiles = new[]
|
||||||
|
{
|
||||||
|
new MediaProfile
|
||||||
|
{
|
||||||
|
Container ="avi",
|
||||||
|
MimeType = "video/x-msvideo",
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,14 +366,29 @@ namespace MediaBrowser.Dlna
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
Type = DlnaProfileType.Video,
|
Type = DlnaProfileType.Video
|
||||||
MimeType = "avi"
|
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"asf"},
|
Containers = new[]{"asf"},
|
||||||
Type = DlnaProfileType.Audio,
|
Type = DlnaProfileType.Audio
|
||||||
MimeType = "x-ms-wmv"
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
new TranscodingProfile
|
||||||
{
|
{
|
||||||
Container = "ts",
|
Container = "ts",
|
||||||
Type = DlnaProfileType.Video,
|
Type = DlnaProfileType.Video
|
||||||
MimeType = "mpeg"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -350,20 +427,48 @@ namespace MediaBrowser.Dlna
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"wma"},
|
Containers = new[]{"wma"},
|
||||||
Type = DlnaProfileType.Audio,
|
Type = DlnaProfileType.Audio
|
||||||
MimeType = "x-ms-wma"
|
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
Type = DlnaProfileType.Video,
|
Type = DlnaProfileType.Video
|
||||||
MimeType = "avi"
|
|
||||||
},
|
},
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"mp4"},
|
Containers = new[]{"mp4"},
|
||||||
Type = DlnaProfileType.Video,
|
Type = DlnaProfileType.Video
|
||||||
MimeType = "mp4"
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
Type = DlnaProfileType.Video ,
|
Type = DlnaProfileType.Video
|
||||||
MimeType="divx"
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
list.Add(new DeviceProfile
|
||||||
{
|
{
|
||||||
Name = "Philips (2010-)",
|
Name = "Philips (2010-)",
|
||||||
|
@ -479,43 +592,216 @@ namespace MediaBrowser.Dlna
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"avi"},
|
Containers = new[]{"avi"},
|
||||||
Type = DlnaProfileType.Video,
|
Type = DlnaProfileType.Video
|
||||||
MimeType = "avi"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Containers = new[]{"mkv"},
|
Containers = new[]{"mkv"},
|
||||||
Type = DlnaProfileType.Video,
|
Type = DlnaProfileType.Video
|
||||||
MimeType = "x-matroska"
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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
|
list.Add(new DeviceProfile
|
||||||
{
|
{
|
||||||
Name = "WDTV Live",
|
Name = "WDTV Live",
|
||||||
ClientType = "DLNA",
|
ClientType = "DLNA",
|
||||||
|
|
||||||
|
TimelineOffsetSeconds = 5,
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
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[]
|
DirectPlayProfiles = new[]
|
||||||
{
|
{
|
||||||
new DirectPlayProfile
|
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
|
Type = DlnaProfileType.Audio
|
||||||
},
|
},
|
||||||
|
|
||||||
new DirectPlayProfile
|
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
|
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"}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@
|
||||||
<Compile Include="PlayTo\PlaylistItem.cs">
|
<Compile Include="PlayTo\PlaylistItem.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="PlayTo\PlaylistItemFactory.cs" />
|
||||||
<Compile Include="PlayTo\PlayToManager.cs" />
|
<Compile Include="PlayTo\PlayToManager.cs" />
|
||||||
<Compile Include="PlayTo\PlayToServerEntryPoint.cs" />
|
<Compile Include="PlayTo\PlayToServerEntryPoint.cs" />
|
||||||
<Compile Include="PlayTo\ServiceAction.cs" />
|
<Compile Include="PlayTo\ServiceAction.cs" />
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
|
@ -69,12 +70,12 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
_device.CurrentIdChanged += Device_CurrentIdChanged;
|
_device.CurrentIdChanged += Device_CurrentIdChanged;
|
||||||
_device.Start();
|
_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
|
#region Device EventHandlers & Update Timer
|
||||||
|
|
||||||
System.Threading.Timer _updateTimer;
|
Timer _updateTimer;
|
||||||
|
|
||||||
async void Device_PlaybackChanged(object sender, TransportStateEventArgs e)
|
async void Device_PlaybackChanged(object sender, TransportStateEventArgs e)
|
||||||
{
|
{
|
||||||
|
@ -88,7 +89,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
_playbackStarted = false;
|
_playbackStarted = false;
|
||||||
|
|
||||||
await _sessionManager.OnPlaybackStopped(new PlaybackStopInfo
|
await _sessionManager.OnPlaybackStopped(new Controller.Session.PlaybackStopInfo
|
||||||
{
|
{
|
||||||
Item = _currentItem,
|
Item = _currentItem,
|
||||||
SessionId = _session.Id,
|
SessionId = _session.Id,
|
||||||
|
@ -164,7 +165,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
|
var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
|
||||||
if (playlistItem != null && playlistItem.Transcode)
|
if (playlistItem != null && playlistItem.Transcode)
|
||||||
{
|
{
|
||||||
await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
|
await _sessionManager.OnPlaybackProgress(new Controller.Session.PlaybackProgressInfo
|
||||||
{
|
{
|
||||||
Item = _currentItem,
|
Item = _currentItem,
|
||||||
SessionId = _session.Id,
|
SessionId = _session.Id,
|
||||||
|
@ -176,7 +177,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
}
|
}
|
||||||
else if (_currentItem != null)
|
else if (_currentItem != null)
|
||||||
{
|
{
|
||||||
await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
|
await _sessionManager.OnPlaybackProgress(new Controller.Session.PlaybackProgressInfo
|
||||||
{
|
{
|
||||||
Item = _currentItem,
|
Item = _currentItem,
|
||||||
SessionId = _session.Id,
|
SessionId = _session.Id,
|
||||||
|
@ -263,7 +264,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
|
|
||||||
case PlaystateCommand.Seek:
|
case PlaystateCommand.Seek:
|
||||||
var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
|
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());
|
var newItem = CreatePlaylistItem(_currentItem, command.SeekPositionTicks ?? 0, GetServerAddress());
|
||||||
playlistItem.StartPositionTicks = newItem.StartPositionTicks;
|
playlistItem.StartPositionTicks = newItem.StartPositionTicks;
|
||||||
|
@ -394,11 +395,13 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
|
|
||||||
var deviceInfo = _device.Properties;
|
var deviceInfo = _device.Properties;
|
||||||
|
|
||||||
var playlistItem = PlaylistItem.Create(item, _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()));
|
var playlistItem = GetPlaylistItem(item, _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()));
|
||||||
playlistItem.StartPositionTicks = startPostionTicks;
|
playlistItem.StartPositionTicks = startPostionTicks;
|
||||||
|
|
||||||
if (playlistItem.IsAudio)
|
if (playlistItem.MediaType == DlnaProfileType.Audio)
|
||||||
|
{
|
||||||
playlistItem.StreamUrl = StreamHelper.GetAudioUrl(playlistItem, serverAddress);
|
playlistItem.StreamUrl = StreamHelper.GetAudioUrl(playlistItem, serverAddress);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress);
|
playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress);
|
||||||
|
@ -412,6 +415,32 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
return playlistItem;
|
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>
|
/// <summary>
|
||||||
/// Plays the items.
|
/// Plays the items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
using MediaBrowser.Controller.Entities;
|
|
||||||
using MediaBrowser.Model.Entities;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Dlna.PlayTo
|
namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
|
@ -11,13 +6,13 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
{
|
{
|
||||||
public string ItemId { get; set; }
|
public string ItemId { get; set; }
|
||||||
|
|
||||||
|
public string MediaSourceId { get; set; }
|
||||||
|
|
||||||
public bool Transcode { get; set; }
|
public bool Transcode { get; set; }
|
||||||
|
|
||||||
public bool IsVideo { get; set; }
|
public DlnaProfileType MediaType { get; set; }
|
||||||
|
|
||||||
public bool IsAudio { get; set; }
|
public string Container { get; set; }
|
||||||
|
|
||||||
public string FileFormat { get; set; }
|
|
||||||
|
|
||||||
public string MimeType { get; set; }
|
public string MimeType { get; set; }
|
||||||
|
|
||||||
|
@ -30,72 +25,5 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
public string Didl { get; set; }
|
public string Didl { get; set; }
|
||||||
|
|
||||||
public long StartPositionTicks { 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
375
MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
Normal file
375
MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,43 +24,43 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
|
|
||||||
var contentFeatures = string.Empty;
|
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";
|
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";
|
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";
|
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";
|
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";
|
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";
|
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";
|
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";
|
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";
|
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
|
||||||
}
|
}
|
||||||
else if (item.IsVideo)
|
else if (item.MediaType == Controller.Dlna.DlnaProfileType.Video)
|
||||||
{
|
{
|
||||||
//Default to AVI for video
|
//Default to AVI for video
|
||||||
contentFeatures = "DLNA.ORG_PN=AVI";
|
contentFeatures = "DLNA.ORG_PN=AVI";
|
||||||
|
@ -85,7 +85,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
internal static string GetAudioUrl(PlaylistItem item, string serverAddress)
|
internal static string GetAudioUrl(PlaylistItem item, string serverAddress)
|
||||||
{
|
{
|
||||||
if (!item.Transcode)
|
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);
|
return string.Format("{0}/audio/{1}/stream.mp3?AudioCodec=Mp3", serverAddress, item.ItemId);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
||||||
if (!item.Transcode)
|
if (!item.Transcode)
|
||||||
{
|
{
|
||||||
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, null, null, null, null, null, null, null, null, null, null, item.MimeType);
|
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 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();
|
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);
|
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>
|
/// <summary>
|
||||||
|
|
|
@ -428,6 +428,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
|
<Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
|
||||||
<Link>Session\MessageCommand.cs</Link>
|
<Link>Session\MessageCommand.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Session\PlaybackReports.cs">
|
||||||
|
<Link>Session\PlaybackReports.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
|
<Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
|
||||||
<Link>Session\PlayRequest.cs</Link>
|
<Link>Session\PlayRequest.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -415,6 +415,9 @@
|
||||||
<Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
|
<Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
|
||||||
<Link>Session\MessageCommand.cs</Link>
|
<Link>Session\MessageCommand.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\MediaBrowser.Model\Session\PlaybackReports.cs">
|
||||||
|
<Link>Session\PlaybackReports.cs</Link>
|
||||||
|
</Compile>
|
||||||
<Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
|
<Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
|
||||||
<Link>Session\PlayRequest.cs</Link>
|
<Link>Session\PlayRequest.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -538,35 +538,26 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reports to the server that the user has begun playing an item
|
/// Reports to the server that the user has begun playing an item
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="itemId">The item id.</param>
|
/// <param name="info">The information.</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>
|
|
||||||
/// <returns>Task{UserItemDataDto}.</returns>
|
/// <returns>Task{UserItemDataDto}.</returns>
|
||||||
/// <exception cref="ArgumentNullException">itemId</exception>
|
/// <exception cref="ArgumentNullException">itemId</exception>
|
||||||
Task ReportPlaybackStartAsync(string itemId, string userId, bool isSeekable, List<string> queueableMediaTypes);
|
Task ReportPlaybackStartAsync(PlaybackStartInfo info);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reports playback progress to the server
|
/// Reports playback progress to the server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="itemId">The item id.</param>
|
/// <param name="info">The information.</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>
|
|
||||||
/// <returns>Task{UserItemDataDto}.</returns>
|
/// <returns>Task{UserItemDataDto}.</returns>
|
||||||
/// <exception cref="ArgumentNullException">itemId</exception>
|
/// <exception cref="ArgumentNullException">itemId</exception>
|
||||||
Task ReportPlaybackProgressAsync(string itemId, string userId, long? positionTicks, bool isPaused, bool isMuted);
|
Task ReportPlaybackProgressAsync(PlaybackProgressInfo info);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reports to the server that the user has stopped playing an item
|
/// Reports to the server that the user has stopped playing an item
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="itemId">The item id.</param>
|
/// <param name="info">The information.</param>
|
||||||
/// <param name="userId">The user id.</param>
|
|
||||||
/// <param name="positionTicks">The position ticks.</param>
|
|
||||||
/// <returns>Task{UserItemDataDto}.</returns>
|
/// <returns>Task{UserItemDataDto}.</returns>
|
||||||
/// <exception cref="ArgumentNullException">itemId</exception>
|
/// <exception cref="ArgumentNullException">itemId</exception>
|
||||||
Task ReportPlaybackStoppedAsync(string itemId, string userId, long? positionTicks);
|
Task ReportPlaybackStoppedAsync(PlaybackStopInfo info);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instructs antoher client to browse to a library item.
|
/// Instructs antoher client to browse to a library item.
|
||||||
|
|
|
@ -147,7 +147,7 @@ namespace MediaBrowser.Model.Configuration
|
||||||
/// different directories and files.
|
/// different directories and files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The file watcher delay.</value>
|
/// <value>The file watcher delay.</value>
|
||||||
public int RealtimeWatcherDelay { get; set; }
|
public int RealtimeMonitorDelay { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [enable dashboard response caching].
|
/// Gets or sets a value indicating whether [enable dashboard response caching].
|
||||||
|
@ -239,7 +239,7 @@ namespace MediaBrowser.Model.Configuration
|
||||||
MaxResumePct = 90;
|
MaxResumePct = 90;
|
||||||
MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds);
|
MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds);
|
||||||
|
|
||||||
RealtimeWatcherDelay = 20;
|
RealtimeMonitorDelay = 30;
|
||||||
|
|
||||||
RecentItemDays = 10;
|
RecentItemDays = 10;
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// Gets or sets the media versions.
|
/// Gets or sets the media versions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The media versions.</value>
|
/// <value>The media versions.</value>
|
||||||
public List<MediaVersionInfo> MediaVersions { get; set; }
|
public List<MediaSourceInfo> MediaSources { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the critic rating.
|
/// Gets or sets the critic rating.
|
||||||
|
@ -500,7 +500,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The part count.</value>
|
/// <value>The part count.</value>
|
||||||
public int? PartCount { get; set; }
|
public int? PartCount { get; set; }
|
||||||
public int? MediaVersionCount { get; set; }
|
public int? MediaSourceCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified type is type.
|
/// Determines whether the specified type is type.
|
||||||
|
|
|
@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Dto
|
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; }
|
public string Path { get; set; }
|
||||||
|
|
||||||
|
@ -22,9 +22,5 @@ namespace MediaBrowser.Model.Dto
|
||||||
public Video3DFormat? Video3DFormat { get; set; }
|
public Video3DFormat? Video3DFormat { get; set; }
|
||||||
|
|
||||||
public List<MediaStream> MediaStreams { get; set; }
|
public List<MediaStream> MediaStreams { get; set; }
|
||||||
|
|
||||||
public List<ChapterInfoDto> Chapters { get; set; }
|
|
||||||
|
|
||||||
public bool IsPrimaryVersion { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,12 @@ namespace MediaBrowser.Model.Entities
|
||||||
/// <value>The thumb item identifier.</value>
|
/// <value>The thumb item identifier.</value>
|
||||||
public string BackdropItemId { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance has primary image.
|
/// Gets a value indicating whether this instance has primary image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -132,6 +132,7 @@
|
||||||
<Compile Include="Search\SearchQuery.cs" />
|
<Compile Include="Search\SearchQuery.cs" />
|
||||||
<Compile Include="Session\BrowseRequest.cs" />
|
<Compile Include="Session\BrowseRequest.cs" />
|
||||||
<Compile Include="Session\MessageCommand.cs" />
|
<Compile Include="Session\MessageCommand.cs" />
|
||||||
|
<Compile Include="Session\PlaybackReports.cs" />
|
||||||
<Compile Include="Session\PlayRequest.cs" />
|
<Compile Include="Session\PlayRequest.cs" />
|
||||||
<Compile Include="Session\PlaystateCommand.cs" />
|
<Compile Include="Session\PlaystateCommand.cs" />
|
||||||
<Compile Include="Logging\ILogManager.cs" />
|
<Compile Include="Logging\ILogManager.cs" />
|
||||||
|
|
|
@ -84,7 +84,7 @@ namespace MediaBrowser.Model.Querying
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The media versions
|
/// The media versions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
MediaVersions,
|
MediaSources,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The metadata settings
|
/// The metadata settings
|
||||||
|
|
56
MediaBrowser.Model/Session/PlaybackReports.cs
Normal file
56
MediaBrowser.Model/Session/PlaybackReports.cs
Normal file
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,10 @@ namespace MediaBrowser.Model.Session
|
||||||
|
|
||||||
public bool SupportsFullscreenToggle { get; set; }
|
public bool SupportsFullscreenToggle { get; set; }
|
||||||
|
|
||||||
|
public bool SupportsOsdToggle { get; set; }
|
||||||
|
|
||||||
|
public bool SupportsNavigationControl { get; set; }
|
||||||
|
|
||||||
public SessionCapabilities()
|
public SessionCapabilities()
|
||||||
{
|
{
|
||||||
PlayableMediaTypes = new string[] {};
|
PlayableMediaTypes = new string[] {};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using System.Diagnostics;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Entities;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Session
|
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>
|
/// <value><c>true</c> if [supports remote control]; otherwise, <c>false</c>.</value>
|
||||||
public bool SupportsRemoteControl { get; set; }
|
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 event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
public SessionInfoDto()
|
public SessionInfoDto()
|
||||||
|
|
|
@ -213,7 +213,7 @@ namespace MediaBrowser.Providers.Savers
|
||||||
|
|
||||||
builder.Append("<Added>" + SecurityElement.Escape(item.DateCreated.ToLocalTime().ToString("G")) + "</Added>");
|
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)
|
if (item.LockedFields.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -267,12 +267,14 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
PlayableMediaTypes = session.PlayableMediaTypes,
|
PlayableMediaTypes = session.PlayableMediaTypes,
|
||||||
RemoteEndPoint = session.RemoteEndPoint,
|
RemoteEndPoint = session.RemoteEndPoint,
|
||||||
AdditionalUsers = session.AdditionalUsers,
|
AdditionalUsers = session.AdditionalUsers,
|
||||||
SupportsFullscreenToggle = session.SupportsFullscreenToggle
|
SupportsFullscreenToggle = session.SupportsFullscreenToggle,
|
||||||
|
SupportsNavigationControl = session.SupportsNavigationControl,
|
||||||
|
SupportsOsdToggle = session.SupportsOsdToggle
|
||||||
};
|
};
|
||||||
|
|
||||||
if (session.NowPlayingItem != null)
|
if (session.NowPlayingItem != null)
|
||||||
{
|
{
|
||||||
dto.NowPlayingItem = GetBaseItemInfo(session.NowPlayingItem);
|
dto.NowPlayingItem = GetNowPlayingInfo(session.NowPlayingItem, session.NowPlayingMediaSourceId, session.NowPlayingRunTimeTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.UserId.HasValue)
|
if (session.UserId.HasValue)
|
||||||
|
@ -288,9 +290,11 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
/// Converts a BaseItem to a BaseItemInfo
|
/// Converts a BaseItem to a BaseItemInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <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>
|
/// <returns>BaseItemInfo.</returns>
|
||||||
/// <exception cref="System.ArgumentNullException">item</exception>
|
/// <exception cref="System.ArgumentNullException">item</exception>
|
||||||
public BaseItemInfo GetBaseItemInfo(BaseItem item)
|
private BaseItemInfo GetNowPlayingInfo(BaseItem item, string mediaSourceId, long? nowPlayingRuntimeTicks)
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
|
@ -303,7 +307,8 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
Name = item.Name,
|
Name = item.Name,
|
||||||
MediaType = item.MediaType,
|
MediaType = item.MediaType,
|
||||||
Type = item.GetClientTypeName(),
|
Type = item.GetClientTypeName(),
|
||||||
RunTimeTicks = item.RunTimeTicks
|
RunTimeTicks = nowPlayingRuntimeTicks,
|
||||||
|
MediaSourceId = mediaSourceId
|
||||||
};
|
};
|
||||||
|
|
||||||
info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary);
|
info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary);
|
||||||
|
@ -735,7 +740,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
if (fields.Contains(ItemFields.Settings))
|
if (fields.Contains(ItemFields.Settings))
|
||||||
{
|
{
|
||||||
dto.LockedFields = item.LockedFields;
|
dto.LockedFields = item.LockedFields;
|
||||||
dto.LockData = item.DontFetchMeta;
|
dto.LockData = item.IsLocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasBudget = item as IHasBudget;
|
var hasBudget = item as IHasBudget;
|
||||||
|
@ -1058,8 +1063,8 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary);
|
dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
dto.MediaVersions = GetMediaVersions(audio);
|
dto.MediaSources = GetMediaSources(audio);
|
||||||
dto.MediaVersionCount = 1;
|
dto.MediaSourceCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var album = item as MusicAlbum;
|
var album = item as MusicAlbum;
|
||||||
|
@ -1090,22 +1095,20 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
dto.IsHD = video.IsHD;
|
dto.IsHD = video.IsHD;
|
||||||
|
|
||||||
dto.PartCount = video.AdditionalPartIds.Count + 1;
|
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))
|
if (fields.Contains(ItemFields.Chapters))
|
||||||
{
|
{
|
||||||
List<ChapterInfoDto> 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)
|
chapters = _itemRepo.GetChapters(item.Id).Select(c => GetChapterInfoDto(c, item)).ToList();
|
||||||
.SelectMany(i => i.Chapters)
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1127,9 +1130,9 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
{
|
{
|
||||||
List<MediaStream> mediaStreams;
|
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)
|
.SelectMany(i => i.MediaStreams)
|
||||||
.ToList();
|
.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 =>
|
return result.OrderBy(i =>
|
||||||
{
|
{
|
||||||
|
@ -1286,49 +1289,47 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
|
|
||||||
return stream == null || stream.Width == null ? 0 : stream.Width.Value;
|
return stream == null || stream.Width == null ? 0 : stream.Width.Value;
|
||||||
})
|
})
|
||||||
.ThenBy(i => i.IsPrimaryVersion ? 0 : 1)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MediaVersionInfo> GetMediaVersions(Audio item)
|
private List<MediaSourceInfo> GetMediaSources(Audio item)
|
||||||
{
|
{
|
||||||
var result = new List<MediaVersionInfo>();
|
var result = new List<MediaSourceInfo>
|
||||||
|
{
|
||||||
result.Add(GetVersionInfo(item, true));
|
GetVersionInfo(item, true)
|
||||||
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaVersionInfo GetVersionInfo(Video i, bool isPrimary)
|
private MediaSourceInfo GetVersionInfo(Video i)
|
||||||
{
|
{
|
||||||
return new MediaVersionInfo
|
var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList();
|
||||||
{
|
|
||||||
Chapters = _itemRepo.GetChapters(i.Id).Select(c => GetChapterInfoDto(c, i)).ToList(),
|
|
||||||
|
|
||||||
ItemId = i.Id.ToString("N"),
|
return new MediaSourceInfo
|
||||||
|
{
|
||||||
|
Id = i.Id.ToString("N"),
|
||||||
IsoType = i.IsoType,
|
IsoType = i.IsoType,
|
||||||
LocationType = i.LocationType,
|
LocationType = i.LocationType,
|
||||||
MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
|
MediaStreams = mediaStreams,
|
||||||
Name = GetAlternateVersionName(i),
|
Name = GetMediaSourceName(i, mediaStreams),
|
||||||
Path = GetMappedPath(i),
|
Path = GetMappedPath(i),
|
||||||
RunTimeTicks = i.RunTimeTicks,
|
RunTimeTicks = i.RunTimeTicks,
|
||||||
Video3DFormat = i.Video3DFormat,
|
Video3DFormat = i.Video3DFormat,
|
||||||
VideoType = i.VideoType,
|
VideoType = i.VideoType
|
||||||
IsPrimaryVersion = isPrimary
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
LocationType = i.LocationType,
|
||||||
MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
|
MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
|
||||||
Name = i.Name,
|
Name = i.Name,
|
||||||
Path = GetMappedPath(i),
|
Path = GetMappedPath(i),
|
||||||
RunTimeTicks = i.RunTimeTicks,
|
RunTimeTicks = i.RunTimeTicks
|
||||||
IsPrimaryVersion = isPrimary
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1351,32 +1352,29 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
return path;
|
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)
|
if (video.Video3DFormat.HasValue)
|
||||||
{
|
{
|
||||||
name = "3D " + name;
|
terms.Add("3D");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video.VideoType == VideoType.BluRay)
|
if (video.VideoType == VideoType.BluRay)
|
||||||
{
|
{
|
||||||
name = name + " " + "Bluray";
|
terms.Add("Bluray");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
else if (video.VideoType == VideoType.Dvd)
|
else if (video.VideoType == VideoType.Dvd)
|
||||||
{
|
{
|
||||||
name = name + " " + "DVD";
|
terms.Add("DVD");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
else if (video.VideoType == VideoType.HdDvd)
|
else if (video.VideoType == VideoType.HdDvd)
|
||||||
{
|
{
|
||||||
name = name + " " + "HD-DVD";
|
terms.Add("HD-DVD");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
else if (video.VideoType == VideoType.Iso)
|
else if (video.VideoType == VideoType.Iso)
|
||||||
{
|
{
|
||||||
|
@ -1384,18 +1382,17 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
{
|
{
|
||||||
if (video.IsoType.Value == IsoType.BluRay)
|
if (video.IsoType.Value == IsoType.BluRay)
|
||||||
{
|
{
|
||||||
name = name + " " + "Bluray";
|
terms.Add("Bluray");
|
||||||
}
|
}
|
||||||
else if (video.IsoType.Value == IsoType.Dvd)
|
else if (video.IsoType.Value == IsoType.Dvd)
|
||||||
{
|
{
|
||||||
name = name + " " + "DVD";
|
terms.Add("DVD");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
name = name + " " + "ISO";
|
terms.Add("ISO");
|
||||||
}
|
}
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoStream != null)
|
if (videoStream != null)
|
||||||
|
@ -1404,44 +1401,45 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||||
{
|
{
|
||||||
if (videoStream.Width.Value >= 3800)
|
if (videoStream.Width.Value >= 3800)
|
||||||
{
|
{
|
||||||
name = name + " " + "4K";
|
terms.Add("4K");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
else if (videoStream.Width.Value >= 1900)
|
else if (videoStream.Width.Value >= 1900)
|
||||||
{
|
{
|
||||||
name = name + " " + "1080P";
|
terms.Add("1080P");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
else if (videoStream.Width.Value >= 1270)
|
else if (videoStream.Width.Value >= 1270)
|
||||||
{
|
{
|
||||||
name = name + " " + "720P";
|
terms.Add("720P");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
else if (videoStream.Width.Value >= 700)
|
else if (videoStream.Width.Value >= 700)
|
||||||
{
|
{
|
||||||
name = name + " " + "480p";
|
terms.Add("480P");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
name = name + " " + "SD";
|
terms.Add("SD");
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
|
if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
|
||||||
{
|
{
|
||||||
name = name + " " + videoStream.Codec.ToUpper();
|
terms.Add(videoStream.Codec.ToUpper());
|
||||||
name = name.Trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
private string GetMappedPath(string path)
|
||||||
|
|
|
@ -453,9 +453,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
|
|
||||||
private bool IsSameEpisode(string sourcePath, string newPath)
|
private bool IsSameEpisode(string sourcePath, string newPath)
|
||||||
{
|
{
|
||||||
|
var sourceFileInfo = new FileInfo(sourcePath);
|
||||||
FileInfo sourceFileInfo = new FileInfo(sourcePath);
|
var destinationFileInfo = new FileInfo(newPath);
|
||||||
FileInfo destinationFileInfo = new FileInfo(newPath);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -470,7 +469,6 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||||
.Replace("!", " ")
|
.Replace("!", " ")
|
||||||
.Replace("(", " ")
|
.Replace("(", " ")
|
||||||
.Replace(")", " ")
|
.Replace(")", " ")
|
||||||
|
.Replace(":", " ")
|
||||||
.Replace(",", " ")
|
.Replace(",", " ")
|
||||||
.Replace("-", " ")
|
.Replace("-", " ")
|
||||||
.Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase)
|
.Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|
|
@ -427,11 +427,11 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||||
{
|
{
|
||||||
if (_updateTimer == null)
|
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
|
else
|
||||||
{
|
{
|
||||||
_updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
|
_updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeMonitorDelay), TimeSpan.FromMilliseconds(-1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
EnsureName(item, args);
|
EnsureName(item, args);
|
||||||
|
|
||||||
item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
|
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
|
// Make sure DateCreated and DateModified have values
|
||||||
EntityResolutionHelper.EnsureDates(fileSystem, item, args, true);
|
EntityResolutionHelper.EnsureDates(fileSystem, item, args, true);
|
||||||
|
|
|
@ -190,7 +190,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
/// <param name="directoryService">The directory service.</param>
|
/// <param name="directoryService">The directory service.</param>
|
||||||
/// <param name="supportMultiFileItems">if set to <c>true</c> [support multi file items].</param>
|
/// <param name="supportMultiFileItems">if set to <c>true</c> [support multi file items].</param>
|
||||||
/// <returns>Movie.</returns>
|
/// <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()
|
where T : Video, new()
|
||||||
{
|
{
|
||||||
var movies = new List<T>();
|
var movies = new List<T>();
|
||||||
|
@ -262,9 +262,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (supportsAlternateVersions)
|
if (supportsMultipleSources)
|
||||||
{
|
{
|
||||||
var result = GetMovieWithAlternateVersions(movies);
|
var result = GetMovieWithMultipleSources(movies);
|
||||||
|
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
|
@ -393,7 +393,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private T GetMovieWithAlternateVersions<T>(IEnumerable<T> movies)
|
private T GetMovieWithMultipleSources<T>(IEnumerable<T> movies)
|
||||||
where T : Video, new()
|
where T : Video, new()
|
||||||
{
|
{
|
||||||
var sortedMovies = movies.OrderBy(i => i.Path).ToList();
|
var sortedMovies = movies.OrderBy(i => i.Path).ToList();
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MediaBrowser.Server.Implementations.Roku
|
namespace MediaBrowser.Server.Implementations.Roku
|
||||||
{
|
{
|
||||||
|
@ -23,6 +25,9 @@ namespace MediaBrowser.Server.Implementations.Roku
|
||||||
{
|
{
|
||||||
if (string.Equals(session.Client, "roku", StringComparison.OrdinalIgnoreCase))
|
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);
|
return new RokuSessionController(_httpClient, _json, _appHost, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,15 +218,29 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="session">The session.</param>
|
/// <param name="session">The session.</param>
|
||||||
/// <param name="item">The item.</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="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>
|
/// <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.IsMuted = isMuted;
|
||||||
session.IsPaused = isPaused;
|
session.IsPaused = isPaused;
|
||||||
session.NowPlayingPositionTicks = currentPositionTicks;
|
session.NowPlayingPositionTicks = currentPositionTicks;
|
||||||
session.NowPlayingItem = item;
|
session.NowPlayingItem = item;
|
||||||
session.LastActivityDate = DateTime.UtcNow;
|
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>
|
/// <summary>
|
||||||
|
@ -246,6 +260,8 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
session.NowPlayingItem = null;
|
session.NowPlayingItem = null;
|
||||||
session.NowPlayingPositionTicks = null;
|
session.NowPlayingPositionTicks = null;
|
||||||
session.IsPaused = false;
|
session.IsPaused = false;
|
||||||
|
session.NowPlayingRunTimeTicks = null;
|
||||||
|
session.NowPlayingMediaSourceId = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,7 +368,9 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
var item = info.Item;
|
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.CanSeek = info.CanSeek;
|
||||||
session.QueueableMediaTypes = info.QueueableMediaTypes;
|
session.QueueableMediaTypes = info.QueueableMediaTypes;
|
||||||
|
@ -371,7 +389,8 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
|
EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
|
||||||
{
|
{
|
||||||
Item = item,
|
Item = item,
|
||||||
Users = users
|
Users = users,
|
||||||
|
MediaSourceId = info.MediaSourceId
|
||||||
|
|
||||||
}, _logger);
|
}, _logger);
|
||||||
}
|
}
|
||||||
|
@ -405,7 +424,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
/// <exception cref="System.ArgumentNullException"></exception>
|
/// <exception cref="System.ArgumentNullException"></exception>
|
||||||
/// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
|
/// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
|
||||||
public async Task OnPlaybackProgress(PlaybackProgressInfo info)
|
public async Task OnPlaybackProgress(Controller.Session.PlaybackProgressInfo info)
|
||||||
{
|
{
|
||||||
if (info == null)
|
if (info == null)
|
||||||
{
|
{
|
||||||
|
@ -419,7 +438,9 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
var session = Sessions.First(i => i.Id.Equals(info.SessionId));
|
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();
|
var key = info.Item.GetUserDataKey();
|
||||||
|
|
||||||
|
@ -434,7 +455,8 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
{
|
{
|
||||||
Item = info.Item,
|
Item = info.Item,
|
||||||
Users = users,
|
Users = users,
|
||||||
PlaybackPositionTicks = info.PositionTicks
|
PlaybackPositionTicks = info.PositionTicks,
|
||||||
|
MediaSourceId = mediaSourceId
|
||||||
|
|
||||||
}, _logger);
|
}, _logger);
|
||||||
}
|
}
|
||||||
|
@ -458,7 +480,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
/// <exception cref="System.ArgumentNullException">info</exception>
|
/// <exception cref="System.ArgumentNullException">info</exception>
|
||||||
/// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
|
/// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
|
||||||
public async Task OnPlaybackStopped(PlaybackStopInfo info)
|
public async Task OnPlaybackStopped(Controller.Session.PlaybackStopInfo info)
|
||||||
{
|
{
|
||||||
if (info == null)
|
if (info == null)
|
||||||
{
|
{
|
||||||
|
@ -494,16 +516,32 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
playedToCompletion = await OnPlaybackStopped(user.Id, key, info.Item, info.PositionTicks).ConfigureAwait(false);
|
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
|
EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs
|
||||||
{
|
{
|
||||||
Item = info.Item,
|
Item = info.Item,
|
||||||
Users = users,
|
Users = users,
|
||||||
PlaybackPositionTicks = info.PositionTicks,
|
PlaybackPositionTicks = info.PositionTicks,
|
||||||
PlayedToCompletion = playedToCompletion
|
PlayedToCompletion = playedToCompletion,
|
||||||
|
MediaSourceId = mediaSourceId
|
||||||
|
|
||||||
}, _logger);
|
}, _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)
|
private async Task<bool> OnPlaybackStopped(Guid userId, string userDataKey, BaseItem item, long? positionTicks)
|
||||||
{
|
{
|
||||||
var data = _userDataRepository.GetUserData(userId, userDataKey);
|
var data = _userDataRepository.GetUserData(userId, userDataKey);
|
||||||
|
@ -899,6 +937,8 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
session.PlayableMediaTypes = capabilities.PlayableMediaTypes.ToList();
|
session.PlayableMediaTypes = capabilities.PlayableMediaTypes.ToList();
|
||||||
session.SupportsFullscreenToggle = capabilities.SupportsFullscreenToggle;
|
session.SupportsFullscreenToggle = capabilities.SupportsFullscreenToggle;
|
||||||
|
session.SupportsOsdToggle = capabilities.SupportsOsdToggle;
|
||||||
|
session.SupportsNavigationControl = capabilities.SupportsNavigationControl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -223,6 +223,11 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
|
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (vals.Length > 3)
|
||||||
|
{
|
||||||
|
info.MediaSourceId = vals[3];
|
||||||
|
}
|
||||||
|
|
||||||
_sessionManager.OnPlaybackStart(info);
|
_sessionManager.OnPlaybackStart(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,6 +270,11 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
SessionId = session.Id
|
SessionId = session.Id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (vals.Length > 4)
|
||||||
|
{
|
||||||
|
info.MediaSourceId = vals[4];
|
||||||
|
}
|
||||||
|
|
||||||
_sessionManager.OnPlaybackProgress(info);
|
_sessionManager.OnPlaybackProgress(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,6 +314,11 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
SessionId = session.Id
|
SessionId = session.Id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (vals.Length > 2)
|
||||||
|
{
|
||||||
|
info.MediaSourceId = vals[2];
|
||||||
|
}
|
||||||
|
|
||||||
_sessionManager.OnPlaybackStopped(info);
|
_sessionManager.OnPlaybackStopped(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace MediaBrowser.Server.Implementations.Sorting
|
namespace MediaBrowser.Server.Implementations.Sorting
|
||||||
|
|
|
@ -2191,20 +2191,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
|
* Constructs a url for a user image
|
||||||
* @param {String} userId
|
* @param {String} userId
|
||||||
|
@ -3805,7 +3791,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
||||||
* @param {String} userId
|
* @param {String} userId
|
||||||
* @param {String} itemId
|
* @param {String} itemId
|
||||||
*/
|
*/
|
||||||
self.reportPlaybackStart = function (userId, itemId, canSeek, queueableMediaTypes) {
|
self.reportPlaybackStart = function (userId, itemId, mediaSourceId, canSeek, queueableMediaTypes) {
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Error("null userId");
|
throw new Error("null userId");
|
||||||
|
@ -3824,16 +3810,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
||||||
|
|
||||||
var msg = [itemId, canSeek, queueableMediaTypes];
|
var msg = [itemId, canSeek, queueableMediaTypes];
|
||||||
|
|
||||||
|
if (mediaSourceId) {
|
||||||
|
msg.push(mediaSourceId);
|
||||||
|
}
|
||||||
|
|
||||||
self.sendWebSocketMessage("PlaybackStart", msg.join('|'));
|
self.sendWebSocketMessage("PlaybackStart", msg.join('|'));
|
||||||
|
|
||||||
deferred.resolveWith(null, []);
|
deferred.resolveWith(null, []);
|
||||||
return deferred.promise();
|
return deferred.promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, {
|
var params = {
|
||||||
CanSeek: canSeek,
|
CanSeek: canSeek,
|
||||||
QueueableMediaTypes: queueableMediaTypes
|
QueueableMediaTypes: queueableMediaTypes
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (mediaSourceId) {
|
||||||
|
params.mediaSourceId = mediaSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
|
||||||
|
|
||||||
return self.ajax({
|
return self.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
|
@ -3846,7 +3842,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
||||||
* @param {String} userId
|
* @param {String} userId
|
||||||
* @param {String} itemId
|
* @param {String} itemId
|
||||||
*/
|
*/
|
||||||
self.reportPlaybackProgress = function (userId, itemId, positionTicks, isPaused, isMuted) {
|
self.reportPlaybackProgress = function (userId, itemId, mediaSourceId, positionTicks, isPaused, isMuted) {
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Error("null userId");
|
throw new Error("null userId");
|
||||||
|
@ -3860,7 +3856,12 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
||||||
|
|
||||||
var deferred = $.Deferred();
|
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);
|
self.sendWebSocketMessage("PlaybackProgress", msgData);
|
||||||
|
|
||||||
|
@ -3877,6 +3878,10 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
||||||
params.positionTicks = positionTicks;
|
params.positionTicks = positionTicks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mediaSourceId) {
|
||||||
|
params.mediaSourceId = mediaSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId + "/Progress", params);
|
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId + "/Progress", params);
|
||||||
|
|
||||||
return self.ajax({
|
return self.ajax({
|
||||||
|
@ -3890,7 +3895,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
||||||
* @param {String} userId
|
* @param {String} userId
|
||||||
* @param {String} itemId
|
* @param {String} itemId
|
||||||
*/
|
*/
|
||||||
self.reportPlaybackStopped = function (userId, itemId, positionTicks) {
|
self.reportPlaybackStopped = function (userId, itemId, mediaSourceId, positionTicks) {
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Error("null userId");
|
throw new Error("null userId");
|
||||||
|
@ -3904,20 +3909,26 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
||||||
|
|
||||||
var deferred = $.Deferred();
|
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, []);
|
deferred.resolveWith(null, []);
|
||||||
return deferred.promise();
|
return deferred.promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
var params = {
|
var params = {};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
if (positionTicks) {
|
if (positionTicks) {
|
||||||
params.positionTicks = positionTicks;
|
params.positionTicks = positionTicks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mediaSourceId) {
|
||||||
|
params.mediaSourceId = mediaSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
|
var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, params);
|
||||||
|
|
||||||
return self.ajax({
|
return self.ajax({
|
||||||
|
|
|
@ -84,9 +84,7 @@
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="ApiClient.js">
|
<EmbeddedResource Include="ApiClient.js" />
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<Content Include="dashboard-ui\advancedserversettings.html">
|
<Content Include="dashboard-ui\advancedserversettings.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.245" targetFramework="net45" />
|
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.247" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
Loading…
Reference in New Issue
Block a user