|
@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Entities.TV;
|
|
|
using MediaBrowser.Controller.Library;
|
|
|
using MediaBrowser.Controller.Localization;
|
|
|
using MediaBrowser.Controller.Playlists;
|
|
|
+using MediaBrowser.Dlna.ContentDirectory;
|
|
|
using MediaBrowser.Model.Dlna;
|
|
|
using MediaBrowser.Model.Drawing;
|
|
|
using MediaBrowser.Model.Entities;
|
|
@@ -63,25 +64,35 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
|
|
|
result.AppendChild(didl);
|
|
|
|
|
|
- result.DocumentElement.AppendChild(GetItemElement(result, item, context, deviceId, filter, streamInfo));
|
|
|
+ result.DocumentElement.AppendChild(GetItemElement(result, item, context, null, deviceId, filter, streamInfo));
|
|
|
|
|
|
return result.DocumentElement.OuterXml;
|
|
|
}
|
|
|
|
|
|
- public XmlElement GetItemElement(XmlDocument doc, BaseItem item, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo = null)
|
|
|
+ public XmlElement GetItemElement(XmlDocument doc, BaseItem item, BaseItem context, StubType? contextStubType, string deviceId, Filter filter, StreamInfo streamInfo = null)
|
|
|
{
|
|
|
+ var clientId = GetClientId(item, null);
|
|
|
+
|
|
|
var element = doc.CreateElement(string.Empty, "item", NS_DIDL);
|
|
|
element.SetAttribute("restricted", "1");
|
|
|
- element.SetAttribute("id", item.Id.ToString("N"));
|
|
|
+ element.SetAttribute("id", clientId);
|
|
|
|
|
|
- if (item.Parent != null)
|
|
|
+ if (context != null)
|
|
|
+ {
|
|
|
+ element.SetAttribute("parentID", GetClientId(context, contextStubType));
|
|
|
+ }
|
|
|
+ else
|
|
|
{
|
|
|
- element.SetAttribute("parentID", item.Parent.Id.ToString("N"));
|
|
|
+ var parent = item.DisplayParent;
|
|
|
+ if (parent != null)
|
|
|
+ {
|
|
|
+ element.SetAttribute("parentID", GetClientId(parent, null));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//AddBookmarkInfo(item, user, element);
|
|
|
|
|
|
- AddGeneralProperties(item, context, element, filter);
|
|
|
+ AddGeneralProperties(item, null, context, element, filter);
|
|
|
|
|
|
// refID?
|
|
|
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
|
|
@@ -111,14 +122,14 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
{
|
|
|
var sources = _user == null ? video.GetMediaSources(true).ToList() : video.GetMediaSources(true, _user).ToList();
|
|
|
|
|
|
- streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
|
|
|
- {
|
|
|
- ItemId = video.Id.ToString("N"),
|
|
|
- MediaSources = sources,
|
|
|
- Profile = _profile,
|
|
|
- DeviceId = deviceId,
|
|
|
- MaxBitrate = _profile.MaxStreamingBitrate
|
|
|
- });
|
|
|
+ streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
|
|
|
+ {
|
|
|
+ ItemId = GetClientId(video),
|
|
|
+ MediaSources = sources,
|
|
|
+ Profile = _profile,
|
|
|
+ DeviceId = deviceId,
|
|
|
+ MaxBitrate = _profile.MaxStreamingBitrate
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
var targetWidth = streamInfo.TargetWidth;
|
|
@@ -142,6 +153,7 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
streamInfo.TargetPacketLength,
|
|
|
streamInfo.TranscodeSeekInfo,
|
|
|
streamInfo.IsTargetAnamorphic,
|
|
|
+ streamInfo.IsTargetCabac,
|
|
|
streamInfo.TargetRefFrames);
|
|
|
|
|
|
foreach (var contentFeature in contentFeatureList)
|
|
@@ -263,6 +275,7 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
streamInfo.TargetPacketLength,
|
|
|
streamInfo.TargetTimestamp,
|
|
|
streamInfo.IsTargetAnamorphic,
|
|
|
+ streamInfo.IsTargetCabac,
|
|
|
streamInfo.TargetRefFrames);
|
|
|
|
|
|
var filename = url.Substring(0, url.IndexOf('?'));
|
|
@@ -311,7 +324,7 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
|
|
|
return item.Name;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private void AddAudioResource(XmlElement container, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
|
|
|
{
|
|
|
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
|
|
@@ -322,7 +335,7 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
|
|
|
streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions
|
|
|
{
|
|
|
- ItemId = audio.Id.ToString("N"),
|
|
|
+ ItemId = GetClientId(audio),
|
|
|
MediaSources = sources,
|
|
|
Profile = _profile,
|
|
|
DeviceId = deviceId
|
|
@@ -403,8 +416,8 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
|
|
|
public static bool IsIdRoot(string id)
|
|
|
{
|
|
|
- if (string.IsNullOrWhiteSpace(id) ||
|
|
|
-
|
|
|
+ if (string.IsNullOrWhiteSpace(id) ||
|
|
|
+
|
|
|
string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
|
|
|
|
|
|
// Samsung sometimes uses 1 as root
|
|
@@ -416,13 +429,15 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- public XmlElement GetFolderElement(XmlDocument doc, BaseItem folder, int childCount, Filter filter, string requestedId = null)
|
|
|
+ public XmlElement GetFolderElement(XmlDocument doc, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
|
|
|
{
|
|
|
var container = doc.CreateElement(string.Empty, "container", NS_DIDL);
|
|
|
container.SetAttribute("restricted", "0");
|
|
|
container.SetAttribute("searchable", "1");
|
|
|
container.SetAttribute("childCount", childCount.ToString(_usCulture));
|
|
|
|
|
|
+ var clientId = GetClientId(folder, stubType);
|
|
|
+
|
|
|
if (string.Equals(requestedId, "0"))
|
|
|
{
|
|
|
container.SetAttribute("id", "0");
|
|
@@ -430,20 +445,20 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- container.SetAttribute("id", folder.Id.ToString("N"));
|
|
|
+ container.SetAttribute("id", clientId);
|
|
|
|
|
|
- var parent = folder.Parent;
|
|
|
+ var parent = context ?? folder.DisplayParent;
|
|
|
if (parent == null)
|
|
|
{
|
|
|
container.SetAttribute("parentID", "0");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- container.SetAttribute("parentID", parent.Id.ToString("N"));
|
|
|
+ container.SetAttribute("parentID", GetClientId(parent, null));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- AddCommonFields(folder, null, container, filter);
|
|
|
+ AddCommonFields(folder, stubType, null, container, filter);
|
|
|
|
|
|
AddCover(folder, container);
|
|
|
|
|
@@ -466,10 +481,11 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
/// Adds fields used by both items and folders
|
|
|
/// </summary>
|
|
|
/// <param name="item">The item.</param>
|
|
|
+ /// <param name="itemStubType">Type of the item stub.</param>
|
|
|
/// <param name="context">The context.</param>
|
|
|
/// <param name="element">The element.</param>
|
|
|
/// <param name="filter">The filter.</param>
|
|
|
- private void AddCommonFields(BaseItem item, BaseItem context, XmlElement element, Filter filter)
|
|
|
+ private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter)
|
|
|
{
|
|
|
// Don't filter on dc:title because not all devices will include it in the filter
|
|
|
// MediaMonkey for example won't display content without a title
|
|
@@ -478,7 +494,7 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
AddValue(element, "dc", "title", GetDisplayName(item, context), NS_DC);
|
|
|
}
|
|
|
|
|
|
- element.AppendChild(CreateObjectClass(element.OwnerDocument, item));
|
|
|
+ element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType));
|
|
|
|
|
|
if (filter.Contains("dc:date"))
|
|
|
{
|
|
@@ -539,14 +555,14 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
AddPeople(item, element);
|
|
|
}
|
|
|
|
|
|
- private XmlElement CreateObjectClass(XmlDocument result, BaseItem item)
|
|
|
+ private XmlElement CreateObjectClass(XmlDocument result, BaseItem item, StubType? stubType)
|
|
|
{
|
|
|
// More types here
|
|
|
// http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs
|
|
|
|
|
|
var objectClass = result.CreateElement("upnp", "class", NS_UPNP);
|
|
|
|
|
|
- if (item.IsFolder)
|
|
|
+ if (item.IsFolder || stubType.HasValue)
|
|
|
{
|
|
|
string classType = null;
|
|
|
|
|
@@ -560,7 +576,7 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
{
|
|
|
classType = "object.container.person.musicArtist";
|
|
|
}
|
|
|
- else if (item is Series || item is Season || item is BoxSet)
|
|
|
+ else if (item is Series || item is Season || item is BoxSet || item is Video)
|
|
|
{
|
|
|
classType = "object.container.album.videoAlbum";
|
|
|
}
|
|
@@ -628,9 +644,9 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void AddGeneralProperties(BaseItem item, BaseItem context, XmlElement element, Filter filter)
|
|
|
+ private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter)
|
|
|
{
|
|
|
- AddCommonFields(item, context, element, filter);
|
|
|
+ AddCommonFields(item, itemStubType, context, element, filter);
|
|
|
|
|
|
var audio = item as Audio;
|
|
|
|
|
@@ -671,10 +687,10 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
|
|
|
if (musicVideo != null)
|
|
|
{
|
|
|
- if (!string.IsNullOrEmpty(musicVideo.Artist))
|
|
|
+ foreach (var artist in musicVideo.Artists)
|
|
|
{
|
|
|
- AddValue(element, "upnp", "artist", musicVideo.Artist, NS_UPNP);
|
|
|
- AddAlbumArtist(element, musicVideo.Artist);
|
|
|
+ AddValue(element, "upnp", "artist", artist, NS_UPNP);
|
|
|
+ AddAlbumArtist(element, artist);
|
|
|
}
|
|
|
|
|
|
if (!string.IsNullOrEmpty(musicVideo.Album))
|
|
@@ -773,20 +789,26 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- AddImageResElement(item, element, 4096, 4096, playbackPercentage, "jpg", "JPEG_LRG");
|
|
|
- AddImageResElement(item, element, 4096, 4096, playbackPercentage, "png", "PNG_LRG");
|
|
|
- AddImageResElement(item, element, 1024, 768, playbackPercentage, "jpg", "JPEG_MED");
|
|
|
- AddImageResElement(item, element, 640, 480, playbackPercentage, "jpg", "JPEG_SM");
|
|
|
+ var imageLimit = _profile.DidlAlbumArtLimit ?? 100;
|
|
|
+
|
|
|
AddImageResElement(item, element, 160, 160, playbackPercentage, "jpg", "JPEG_TN");
|
|
|
- AddImageResElement(item, element, 160, 160, playbackPercentage, "png", "PNG_TN");
|
|
|
+
|
|
|
+ if (imageLimit > 1)
|
|
|
+ {
|
|
|
+ AddImageResElement(item, element, 4096, 4096, playbackPercentage, "jpg", "JPEG_LRG");
|
|
|
+ AddImageResElement(item, element, 1024, 768, playbackPercentage, "jpg", "JPEG_MED");
|
|
|
+ AddImageResElement(item, element, 640, 480, playbackPercentage, "jpg", "JPEG_SM");
|
|
|
+ AddImageResElement(item, element, 4096, 4096, playbackPercentage, "png", "PNG_LRG");
|
|
|
+ AddImageResElement(item, element, 160, 160, playbackPercentage, "png", "PNG_TN");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- private void AddImageResElement(BaseItem item,
|
|
|
- XmlElement element,
|
|
|
- int maxWidth,
|
|
|
- int maxHeight,
|
|
|
+ private void AddImageResElement(BaseItem item,
|
|
|
+ XmlElement element,
|
|
|
+ int maxWidth,
|
|
|
+ int maxHeight,
|
|
|
int playbackPercentage,
|
|
|
- string format,
|
|
|
+ string format,
|
|
|
string org_Pn)
|
|
|
{
|
|
|
var imageInfo = GetImageInfo(item);
|
|
@@ -920,6 +942,25 @@ namespace MediaBrowser.Dlna.Didl
|
|
|
internal int? Height;
|
|
|
}
|
|
|
|
|
|
+ public static string GetClientId(BaseItem item, StubType? stubType)
|
|
|
+ {
|
|
|
+ var id = item.Id.ToString("N");
|
|
|
+
|
|
|
+ if (stubType.HasValue)
|
|
|
+ {
|
|
|
+ id = stubType.Value.ToString().ToLower() + "_" + id;
|
|
|
+ }
|
|
|
+
|
|
|
+ return id;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string GetClientId(IHasMediaSources item)
|
|
|
+ {
|
|
|
+ var id = item.Id.ToString("N");
|
|
|
+
|
|
|
+ return id;
|
|
|
+ }
|
|
|
+
|
|
|
private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, int playbackPercentage, string format)
|
|
|
{
|
|
|
var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/{7}",
|