unify didl generation

pull/702/head
Luke Pulverenti 11 years ago
parent 73b771f9c5
commit 0ab3a1bf8e

@ -1,167 +1,691 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml;
namespace MediaBrowser.Dlna.Didl
{
public class DidlBuilder
{
const string CRLF = "\r\n";
const string UNKNOWN = "Unknown";
const string DIDL_START = @"<item id=""{0}"" parentID=""{1}"" restricted=""1"" xmlns=""urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"">" + CRLF;
const string DIDL_TITLE = @" <dc:title xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:title>" + CRLF;
const string DIDL_ARTIST = @"<upnp:artist xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:artist>" + CRLF;
const string DIDL_ALBUM = @"<upnp:album xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:album>" + CRLF;
const string DIDL_TRACKNUM = @"<upnp:originalTrackNumber xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:originalTrackNumber>" + CRLF;
const string DIDL_VIDEOCLASS = @" <upnp:class xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">object.item.videoItem</upnp:class>" + CRLF;
const string DIDL_AUDIOCLASS = @" <upnp:class xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">object.item.audioItem.musicTrack</upnp:class>" + CRLF;
const string DIDL_IMAGE = @" <upnp:albumArtURI dlna:profileID=""JPEG_TN"" xmlns:dlna=""urn:schemas-dlna-org:metadata-1-0/"" xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:albumArtURI>" + CRLF +
@" <upnp:icon xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:icon>" + CRLF;
const string DIDL_RELEASEDATE = @" <dc:date xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:date>" + CRLF;
const string DIDL_GENRE = @" <upnp:genre xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:genre>" + CRLF;
const string DESCRIPTION = @" <dc:description xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:description>" + CRLF;
const string DIDL_VIDEO_RES = @" <res bitrate=""{0}"" duration=""{1}"" protocolInfo=""http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"" resolution=""{2}x{3}"">{4}</res>" + CRLF;
const string DIDL_AUDIO_RES = @" <res bitrate=""{0}"" duration=""{1}"" nrAudioChannels=""2"" protocolInfo=""http-get:*:audio/mp3:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"" sampleFrequency=""{2}"">{3}</res>" + CRLF;
const string DIDL_IMAGE_RES = @" <res protocolInfo=""http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_CI=1;DLNA.ORG_FLAGS=00D00000000000000000000000000000"" resolution=""212x320"">{0}</res>" + CRLF;
const string DIDL_ALBUMIMAGE_RES = @" <res protocolInfo=""http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_CI=1;DLNA.ORG_FLAGS=00D00000000000000000000000000000"" resolution=""320x320"">{0}</res>" + CRLF;
const string DIDL_RATING = @" <upnp:rating xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:rating>" + CRLF;
const string DIDL_END = "</item>";
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
private const string NS_DC = "http://purl.org/dc/elements/1.1/";
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
private readonly DeviceProfile _profile;
private readonly IImageProcessor _imageProcessor;
private readonly string _serverAddress;
private readonly IDtoService _dtoService;
public DidlBuilder(DeviceProfile profile, IImageProcessor imageProcessor, string serverAddress, IDtoService dtoService)
{
_profile = profile;
_imageProcessor = imageProcessor;
_serverAddress = serverAddress;
_dtoService = dtoService;
}
public string GetItemDidl(BaseItem item, string deviceId, Filter filter)
{
var result = new XmlDocument();
var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
didl.SetAttribute("xmlns:dc", NS_DC);
didl.SetAttribute("xmlns:dlna", NS_DLNA);
didl.SetAttribute("xmlns:upnp", NS_UPNP);
//didl.SetAttribute("xmlns:sec", NS_SEC);
result.AppendChild(didl);
result.DocumentElement.AppendChild(GetItemElement(result, item, deviceId, filter));
return result.DocumentElement.OuterXml;
}
public XmlElement GetItemElement(XmlDocument doc, BaseItem item, string deviceId, Filter filter)
{
var element = doc.CreateElement(string.Empty, "item", NS_DIDL);
element.SetAttribute("restricted", "1");
element.SetAttribute("id", item.Id.ToString("N"));
if (item.Parent != null)
{
element.SetAttribute("parentID", item.Parent.Id.ToString("N"));
}
//AddBookmarkInfo(item, user, element);
AddGeneralProperties(item, element, filter);
// refID?
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
var audio = item as Audio;
if (audio != null)
{
AddAudioResource(element, audio, deviceId, filter);
}
var video = item as Video;
if (video != null)
{
AddVideoResource(element, video, deviceId, filter);
}
AddCover(item, element);
return element;
}
private void AddVideoResource(XmlElement container, Video video, string deviceId, Filter filter)
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
var sources = _dtoService.GetMediaSources(video);
int? maxBitrateSetting = null;
var streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
{
ItemId = video.Id.ToString("N"),
MediaSources = sources,
Profile = _profile,
DeviceId = deviceId,
MaxBitrate = maxBitrateSetting
});
var url = streamInfo.ToDlnaUrl(_serverAddress);
//res.AppendChild(container.OwnerDocument.CreateCDataSection(url));
res.InnerText = url;
var mediaSource = sources.First(i => string.Equals(i.Id, streamInfo.MediaSourceId));
if (mediaSource.RunTimeTicks.HasValue)
{
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
}
if (filter.Contains("res@size"))
{
if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
{
var size = streamInfo.TargetSize;
if (size.HasValue)
{
res.SetAttribute("size", size.Value.ToString(_usCulture));
}
}
}
var totalBitrate = streamInfo.TotalOutputBitrate;
var targetSampleRate = streamInfo.TargetAudioSampleRate;
var targetChannels = streamInfo.TargetAudioChannels;
var targetWidth = streamInfo.TargetWidth;
var targetHeight = streamInfo.TargetHeight;
if (targetChannels.HasValue)
{
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
}
if (filter.Contains("res@resolution"))
{
if (targetWidth.HasValue && targetHeight.HasValue)
{
res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
}
}
if (targetSampleRate.HasValue)
{
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
}
if (totalBitrate.HasValue)
{
res.SetAttribute("bitrate", totalBitrate.Value.ToString(_usCulture));
}
var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
streamInfo.AudioCodec,
streamInfo.VideoCodec);
var filename = url.Substring(0, url.IndexOf('?'));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
var contentFeatures = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container,
streamInfo.VideoCodec,
streamInfo.AudioCodec,
targetWidth,
targetHeight,
totalBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.TranscodeSeekInfo);
res.SetAttribute("protocolInfo", String.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
container.AppendChild(res);
}
private void AddAudioResource(XmlElement container, Audio audio, string deviceId, Filter filter)
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
var sources = _dtoService.GetMediaSources(audio);
var streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions
{
ItemId = audio.Id.ToString("N"),
MediaSources = sources,
Profile = _profile,
DeviceId = deviceId
});
var url = streamInfo.ToDlnaUrl(_serverAddress);
//res.AppendChild(container.OwnerDocument.CreateCDataSection(url));
res.InnerText = url;
var mediaSource = sources.First(i => string.Equals(i.Id, streamInfo.MediaSourceId));
if (mediaSource.RunTimeTicks.HasValue)
{
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
}
if (filter.Contains("res@size"))
{
if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
{
var size = streamInfo.TargetSize;
if (size.HasValue)
{
res.SetAttribute("size", size.Value.ToString(_usCulture));
}
}
}
var targetAudioBitrate = streamInfo.TargetAudioBitrate;
var targetSampleRate = streamInfo.TargetAudioSampleRate;
var targetChannels = streamInfo.TargetAudioChannels;
if (targetChannels.HasValue)
{
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
}
if (targetSampleRate.HasValue)
{
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
}
if (targetAudioBitrate.HasValue)
{
res.SetAttribute("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
}
var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
streamInfo.AudioCodec);
var filename = url.Substring(0, url.IndexOf('?'));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
streamInfo.TargetAudioCodec,
targetAudioBitrate,
targetSampleRate,
targetChannels,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.TranscodeSeekInfo);
res.SetAttribute("protocolInfo", String.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
container.AppendChild(res);
}
public XmlElement GetFolderElement(XmlDocument doc, Folder folder, int childCount, Filter filter)
{
var container = doc.CreateElement(string.Empty, "container", NS_DIDL);
container.SetAttribute("restricted", "0");
container.SetAttribute("searchable", "1");
container.SetAttribute("childCount", childCount.ToString(_usCulture));
container.SetAttribute("id", folder.Id.ToString("N"));
var parent = folder.Parent;
if (parent == null)
{
container.SetAttribute("parentID", "0");
}
else
{
container.SetAttribute("parentID", parent.Id.ToString("N"));
}
AddCommonFields(folder, container, filter);
AddCover(folder, container);
return container;
}
//private void AddBookmarkInfo(BaseItem item, User user, XmlElement element)
//{
// var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());
// if (userdata.PlaybackPositionTicks > 0)
// {
// var dcmInfo = element.OwnerDocument.CreateElement("sec", "dcmInfo", NS_SEC);
// dcmInfo.InnerText = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds).ToString(_usCulture));
// element.AppendChild(dcmInfo);
// }
//}
/// <summary>
/// Builds a Didl MetaData object for the specified dto.
/// Adds fields used by both items and folders
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="userId">The user identifier.</param>
/// <param name="serverAddress">The server address.</param>
/// <param name="streamUrl">The stream URL.</param>
/// <param name="streams">The streams.</param>
/// <param name="includeImageRes">if set to <c>true</c> [include image resource].</param>
/// <returns>System.String.</returns>
public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable<MediaStream> streams, bool includeImageRes)
{
string response = string.Format(DIDL_START, dto.Id, userId);
response += string.Format(DIDL_TITLE, dto.Name.Replace("&", "and"));
if (IsVideo(dto))
response += DIDL_VIDEOCLASS;
else
response += DIDL_AUDIOCLASS;
/// <param name="item">The item.</param>
/// <param name="element">The element.</param>
/// <param name="filter">The filter.</param>
private void AddCommonFields(BaseItem item, XmlElement element, Filter filter)
{
if (filter.Contains("dc:title"))
{
AddValue(element, "dc", "title", item.Name, NS_DC);
}
var imageUrl = GetImageUrl(dto, serverAddress);
element.AppendChild(CreateObjectClass(element.OwnerDocument, item));
if (!string.IsNullOrWhiteSpace(imageUrl))
if (filter.Contains("dc:date"))
{
response += string.Format(DIDL_IMAGE, imageUrl);
if (item.PremiereDate.HasValue)
{
AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
}
}
response += string.Format(DIDL_RELEASEDATE, GetDateString(dto.PremiereDate));
//TODO Add genres to didl;
response += string.Format(DIDL_GENRE, UNKNOWN);
foreach (var genre in item.Genres)
{
AddValue(element, "upnp", "genre", genre, NS_UPNP);
}
if (IsVideo(dto))
foreach (var studio in item.Studios)
{
response += string.Format(DESCRIPTION, UNKNOWN);
response += GetVideoDIDL(dto, streamUrl, streams);
AddValue(element, "upnp", "publisher", studio, NS_UPNP);
}
if (includeImageRes && !string.IsNullOrWhiteSpace(imageUrl))
if (filter.Contains("dc:description"))
{
if (!string.IsNullOrWhiteSpace(item.Overview))
{
AddValue(element, "dc", "description", item.Overview, NS_DC);
}
}
if (filter.Contains("upnp:longDescription"))
{
if (!string.IsNullOrWhiteSpace(item.Overview))
{
AddValue(element, "upnp", "longDescription", item.Overview, NS_UPNP);
}
}
if (!string.IsNullOrEmpty(item.OfficialRating))
{
if (filter.Contains("dc:rating"))
{
AddValue(element, "dc", "rating", item.OfficialRating, NS_DC);
}
if (filter.Contains("upnp:rating"))
{
response += string.Format(DIDL_IMAGE_RES, imageUrl);
AddValue(element, "upnp", "rating", item.OfficialRating, NS_UPNP);
}
}
AddPeople(item, element);
}
private XmlElement CreateObjectClass(XmlDocument result, BaseItem item)
{
var objectClass = result.CreateElement("upnp", "class", NS_UPNP);
if (item.IsFolder)
{
string classType = null;
if (!_profile.RequiresPlainFolders)
{
if (item is MusicAlbum)
{
classType = "object.container.album.musicAlbum";
}
if (item is MusicArtist)
{
classType = "object.container.person.musicArtist";
}
}
objectClass.InnerText = classType ?? "object.container.storageFolder";
}
else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
objectClass.InnerText = "object.item.audioItem.musicTrack";
}
else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
{
objectClass.InnerText = "object.item.imageItem.photo";
}
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
if (!_profile.RequiresPlainVideoItems && item is Movie)
{
objectClass.InnerText = "object.item.videoItem.movie";
}
else
{
objectClass.InnerText = "object.item.videoItem";
}
}
else
{
var audio = dto as Audio;
throw new NotSupportedException();
}
return objectClass;
}
private void AddPeople(BaseItem item, XmlElement element)
{
foreach (var actor in item.People)
{
AddValue(element, "upnp", (actor.Type ?? PersonType.Actor).ToLower(), actor.Name, NS_UPNP);
}
}
private void AddGeneralProperties(BaseItem item, XmlElement element, Filter filter)
{
AddCommonFields(item, element, filter);
var audio = item as Audio;
if (audio != null)
{
foreach (var artist in audio.Artists)
{
AddValue(element, "upnp", "artist", artist, NS_UPNP);
}
if (audio != null)
if (!string.IsNullOrEmpty(audio.Album))
{
response += string.Format(DIDL_ARTIST, audio.Artists.FirstOrDefault() ?? UNKNOWN);
response += string.Format(DIDL_ALBUM, audio.Album);
AddValue(element, "upnp", "album", audio.Album, NS_UPNP);
}
response += string.Format(DIDL_TRACKNUM, audio.IndexNumber ?? 0);
if (!string.IsNullOrEmpty(audio.AlbumArtist))
{
AddValue(element, "upnp", "albumArtist", audio.AlbumArtist, NS_UPNP);
}
}
response += GetAudioDIDL(dto, streamUrl, streams);
var album = item as MusicAlbum;
if (includeImageRes && !string.IsNullOrWhiteSpace(imageUrl))
if (album != null)
{
if (!string.IsNullOrEmpty(album.AlbumArtist))
{
response += string.Format(DIDL_ALBUMIMAGE_RES, imageUrl);
AddValue(element, "upnp", "artist", album.AlbumArtist, NS_UPNP);
AddValue(element, "upnp", "albumArtist", album.AlbumArtist, NS_UPNP);
}
}
response += DIDL_END;
var musicVideo = item as MusicVideo;
return response;
if (musicVideo != null)
{
if (!string.IsNullOrEmpty(musicVideo.Artist))
{
AddValue(element, "upnp", "artist", musicVideo.Artist, NS_UPNP);
}
if (!string.IsNullOrEmpty(musicVideo.Album))
{
AddValue(element, "upnp", "album", musicVideo.Album, NS_UPNP);
}
}
if (item.IndexNumber.HasValue)
{
AddValue(element, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
}
}
private void AddValue(XmlElement elem, string prefix, string name, string value, string namespaceUri)
{
try
{
var date = elem.OwnerDocument.CreateElement(prefix, name, namespaceUri);
date.InnerText = value;
elem.AppendChild(date);
}
catch (XmlException)
{
//_logger.Error("Error adding xml value: " + value);
}
}
private static string GetVideoDIDL(BaseItem dto, string streamUrl, IEnumerable<MediaStream> streams)
private void AddCover(BaseItem item, XmlElement element)
{
var videostream = streams.Where(stream => stream.Type == MediaStreamType.Video).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault();
var imageInfo = GetImageInfo(item);
if (imageInfo == null)
{
return;
}
var result = element.OwnerDocument;
var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight);
var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = _profile.AlbumArtPn;
icon.SetAttributeNode(profile);
icon.InnerText = albumartUrlInfo.Url;
element.AppendChild(icon);
var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth, _profile.MaxIconHeight);
icon = result.CreateElement("upnp", "icon", NS_UPNP);
profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = _profile.AlbumArtPn;
icon.SetAttributeNode(profile);
icon.InnerText = iconUrlInfo.Url;
element.AppendChild(icon);
if (videostream == null)
if (!_profile.EnableAlbumArtInDidl)
{
// TOOD: ???
return string.Empty;
return;
}
return string.Format(DIDL_VIDEO_RES,
videostream.BitRate.HasValue ? videostream.BitRate.Value / 10 : 0,
GetDurationString(dto),
videostream.Width ?? 0,
videostream.Height ?? 0,
streamUrl);
var res = result.CreateElement(string.Empty, "res", NS_DIDL);
res.InnerText = albumartUrlInfo.Url;
var width = albumartUrlInfo.Width;
var height = albumartUrlInfo.Height;
var mediaProfile = new MediaFormatProfileResolver().ResolveImageFormat("jpg", width, height);
var orgPn = mediaProfile.HasValue ? "DLNA.ORG_PN=:" + mediaProfile.Value + ";" : string.Empty;
res.SetAttribute("protocolInfo", string.Format(
"http-get:*:{1}:{0}DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
orgPn,
"image/jpeg",
DlnaMaps.DefaultStreaming
));
if (width.HasValue && height.HasValue)
{
res.SetAttribute("resolution", string.Format("{0}x{1}", width.Value, height.Value));
}
element.AppendChild(res);
}
private static string GetAudioDIDL(BaseItem dto, string streamUrl, IEnumerable<MediaStream> streams)
private ImageDownloadInfo GetImageInfo(BaseItem item)
{
var audiostream = streams.Where(stream => stream.Type == MediaStreamType.Audio).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault();
if (item.HasImage(ImageType.Primary))
{
return GetImageInfo(item, ImageType.Primary);
}
if (item.HasImage(ImageType.Thumb))
{
return GetImageInfo(item, ImageType.Thumb);
}
if (audiostream == null)
if (item is Audio || item is Episode)
{
// TOOD: ???
return string.Empty;
item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
if (item != null)
{
return GetImageInfo(item, ImageType.Primary);
}
}
return string.Format(DIDL_AUDIO_RES,
audiostream.BitRate.HasValue ? audiostream.BitRate.Value / 10 : 16000,
GetDurationString(dto),
audiostream.SampleRate ?? 0,
streamUrl);
return null;
}
private static string GetImageUrl(BaseItem dto, string serverAddress)
private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type)
{
const ImageType imageType = ImageType.Primary;
var imageInfo = item.GetImageInfo(type, 0);
string tag = null;
try
{
var guid = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
if (!dto.HasImage(imageType))
tag = guid.HasValue ? guid.Value.ToString("N") : null;
}
catch
{
dto = dto.Parents.FirstOrDefault(i => i.HasImage(imageType));
}
return dto == null ? null : string.Format("{0}/Items/{1}/Images/{2}", serverAddress, dto.Id, imageType);
int? width = null;
int? height = null;
try
{
var size = _imageProcessor.GetImageSize(imageInfo.Path, imageInfo.DateModified);
width = Convert.ToInt32(size.Width);
height = Convert.ToInt32(size.Height);
}
catch
{
}
return new ImageDownloadInfo
{
ItemId = item.Id.ToString("N"),
Type = ImageType.Primary,
ImageTag = tag,
Width = width,
Height = height
};
}
private static string GetDurationString(BaseItem dto)
class ImageDownloadInfo
{
var duration = TimeSpan.FromTicks(dto.RunTimeTicks.HasValue ? dto.RunTimeTicks.Value : 0);
internal string ItemId;
internal string ImageTag;
internal ImageType Type;
// TODO: Bad format string?
return string.Format("{0}:{1:00}:2{00}.000", duration.Hours, duration.Minutes, duration.Seconds);
internal int? Width;
internal int? Height;
}
private static string GetDateString(DateTime? date)
class ImageUrlInfo
{
if (!date.HasValue)
return UNKNOWN;
internal string Url;
return string.Format("{0}-{1:00}-{2:00}", date.Value.Year, date.Value.Month, date.Value.Day);
internal int? Width;
internal int? Height;
}
private static bool IsVideo(BaseItem item)
private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int? maxWidth, int? maxHeight)
{
return string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
var url = string.Format("{0}/Items/{1}/Images/{2}?tag={3}&format=jpg",
_serverAddress,
info.ItemId,
info.Type,
info.ImageTag);
if (maxWidth.HasValue)
{
url += "&maxWidth=" + maxWidth.Value.ToString(_usCulture);
}
if (maxHeight.HasValue)
{
url += "&maxHeight=" + maxHeight.Value.ToString(_usCulture);
}
var width = info.Width;
var height = info.Height;
if (width.HasValue && height.HasValue)
{
if (maxWidth.HasValue || maxHeight.HasValue)
{
var newSize = DrawingUtils.Resize(new ImageSize
{
Height = height.Value,
Width = width.Value
}, maxWidth: maxWidth, maxHeight: maxHeight);
width = Convert.ToInt32(newSize.Width);
height = Convert.ToInt32(newSize.Height);
}
}
return new ImageUrlInfo
{
Url = url,
Width = width,
Height = height
};
}
}
}

@ -1,4 +1,5 @@
using MediaBrowser.Common.Net;
using System.Security;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Dlna.Common;
using MediaBrowser.Model.Logging;
@ -299,16 +300,12 @@ namespace MediaBrowser.Dlna.PlayTo
private string CreateDidlMeta(string value)
{
if (value == null)
if (string.IsNullOrEmpty(value))
return String.Empty;
var escapedData = value.Replace("<", "&lt;").Replace(">", "&gt;");
return String.Format(BaseDidl, escapedData.Replace("\r\n", ""));
return SecurityElement.Escape(value);
}
private const string BaseDidl = "&lt;DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"&gt;{0}&lt;/DIDL-Lite&gt;";
public async Task SetNextAvTransport(string value, string header, string metaData)
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetNextAVTransportURI");
@ -615,7 +612,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
{
return new Tuple<bool, uBaseObject>(true, null);
return new Tuple<bool, uBaseObject>(false, null);
}
XElement uPnpResponse;
@ -624,9 +621,9 @@ namespace MediaBrowser.Dlna.PlayTo
{
uPnpResponse = XElement.Parse(trackString);
}
catch
catch (Exception ex)
{
_logger.Error("Unable to parse xml {0}", trackString);
_logger.ErrorException("Unable to parse xml {0}", ex, trackString);
return new Tuple<bool, uBaseObject>(true, null);
}

@ -1,6 +1,7 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@ -35,6 +36,7 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IUserManager _userManager;
private readonly IServerApplicationHost _appHost;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
public bool SupportsMediaRemoteControl
{
@ -52,7 +54,7 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager, IDlnaManager dlnaManager, IUserManager userManager, IServerApplicationHost appHost, IDtoService dtoService)
public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, INetworkManager networkManager, IDlnaManager dlnaManager, IUserManager userManager, IServerApplicationHost appHost, IDtoService dtoService, IImageProcessor imageProcessor)
{
_session = session;
_itemRepository = itemRepository;
@ -63,6 +65,7 @@ namespace MediaBrowser.Dlna.PlayTo
_userManager = userManager;
_appHost = appHost;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_logger = logger;
}
@ -390,12 +393,11 @@ namespace MediaBrowser.Dlna.PlayTo
playlistItem.StreamUrl = playlistItem.StreamInfo.ToUrl(serverAddress);
var mediaStreams = mediaSources
.Where(i => string.Equals(i.Id, playlistItem.StreamInfo.MediaSourceId))
.SelectMany(i => i.MediaStreams)
.ToList();
var itemXml =
new DidlBuilder(profile, _imageProcessor, serverAddress, _dtoService).GetItemDidl(item, _session.DeviceId,
new Filter());
playlistItem.Didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, mediaStreams, profile.EnableAlbumArtInDidl);
playlistItem.Didl = itemXml;
return playlistItem;
}

@ -2,6 +2,7 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
@ -38,8 +39,9 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
public PlayToManager(ILogger logger, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepository, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService)
public PlayToManager(ILogger logger, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepository, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService, IImageProcessor imageProcessor)
{
_locations = new ConcurrentDictionary<string, DateTime>();
_tokenSource = new CancellationTokenSource();
@ -54,6 +56,7 @@ namespace MediaBrowser.Dlna.PlayTo
_dlnaManager = dlnaManager;
_appHost = appHost;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_config = config;
}
@ -238,7 +241,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (controller == null)
{
sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager, _appHost, _dtoService);
sessionInfo.SessionController = controller = new PlayToController(sessionInfo, _sessionManager, _itemRepository, _libraryManager, _logger, _networkManager, _dlnaManager, _userManager, _appHost, _dtoService, _imageProcessor);
controller.Init(device);

@ -2,6 +2,7 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
@ -26,8 +27,9 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IDlnaManager _dlnaManager;
private readonly IServerApplicationHost _appHost;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
public PlayToServerEntryPoint(ILogManager logManager, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService)
public PlayToServerEntryPoint(ILogManager logManager, IServerConfigurationManager config, ISessionManager sessionManager, IHttpClient httpClient, IItemRepository itemRepo, ILibraryManager libraryManager, INetworkManager networkManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IDtoService dtoService, IImageProcessor imageProcessor)
{
_config = config;
_sessionManager = sessionManager;
@ -39,6 +41,7 @@ namespace MediaBrowser.Dlna.PlayTo
_dlnaManager = dlnaManager;
_appHost = appHost;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_logger = logManager.GetLogger("PlayTo");
}
@ -85,7 +88,8 @@ namespace MediaBrowser.Dlna.PlayTo
_userManager,
_dlnaManager,
_appHost,
_dtoService);
_dtoService,
_imageProcessor);
_manager.Start();
}

@ -1,5 +1,4 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
@ -8,8 +7,8 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Dlna.Didl;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
@ -27,35 +26,29 @@ namespace MediaBrowser.Dlna.Server
{
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
private readonly DeviceProfile _profile;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IUserDataManager _userDataManager;
private readonly User _user;
private readonly string _serverAddress;
private const string NS_DC = "http://purl.org/dc/elements/1.1/";
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
private const string NS_SEC = "http://www.sec.co.kr/";
private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private readonly int _systemUpdateId;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly DidlBuilder _didlBuilder;
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId)
{
_logger = logger;
_libraryManager = libraryManager;
_profile = profile;
_serverAddress = serverAddress;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_userDataManager = userDataManager;
_user = user;
_systemUpdateId = systemUpdateId;
_didlBuilder = new DidlBuilder(profile, imageProcessor, serverAddress, dtoService);
}
public ControlResponse ProcessControlRequest(ControlRequest request)
@ -91,7 +84,7 @@ namespace MediaBrowser.Dlna.Server
sparams.Add(e.LocalName, e.InnerText.Trim());
}
var deviceId = "fgd";
var deviceId = "test";
IEnumerable<KeyValuePair<string, string>> result;
@ -270,7 +263,7 @@ namespace MediaBrowser.Dlna.Server
if (string.Equals(flag, "BrowseMetadata"))
{
Browse_AddFolder(result, folder, children.Count, filter);
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, children.Count, filter));
provided++;
}
else
@ -293,11 +286,11 @@ namespace MediaBrowser.Dlna.Server
var f = (Folder)i;
var childCount = GetChildrenSorted(f, user, sortCriteria).Count();
Browse_AddFolder(result, f, childCount, filter);
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter));
}
else
{
Browse_AddItem(result, i, deviceId, filter);
result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, deviceId, filter));
}
}
}
@ -368,11 +361,11 @@ namespace MediaBrowser.Dlna.Server
var f = (Folder)i;
var childCount = GetChildrenSorted(f, user, searchCriteria, sortCriteria).Count();
Browse_AddFolder(result, f, childCount, filter);
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter));
}
else
{
Browse_AddItem(result, i, deviceId, filter);
result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, deviceId, filter));
}
}
@ -475,637 +468,5 @@ namespace MediaBrowser.Dlna.Server
? user.RootFolder
: _libraryManager.GetItemById(new Guid(id));
}
private void Browse_AddFolder(XmlDocument result, Folder f, int childCount, Filter filter)
{
var container = result.CreateElement(string.Empty, "container", NS_DIDL);
container.SetAttribute("restricted", "0");
container.SetAttribute("searchable", "1");
container.SetAttribute("childCount", childCount.ToString(_usCulture));
container.SetAttribute("id", f.Id.ToString("N"));
var parent = f.Parent;
if (parent == null)
{
container.SetAttribute("parentID", "0");
}
else
{
container.SetAttribute("parentID", parent.Id.ToString("N"));
}
AddCommonFields(f, container, filter);
AddCover(f, container);
result.DocumentElement.AppendChild(container);
}
private void AddValue(XmlElement elem, string prefix, string name, string value, string namespaceUri)
{
try
{
var date = elem.OwnerDocument.CreateElement(prefix, name, namespaceUri);
date.InnerText = value;
elem.AppendChild(date);
}
catch (XmlException)
{
//_logger.Error("Error adding xml value: " + value);
}
}
private void Browse_AddItem(XmlDocument result, BaseItem item, string deviceId, Filter filter)
{
var element = result.CreateElement(string.Empty, "item", NS_DIDL);
element.SetAttribute("restricted", "1");
element.SetAttribute("id", item.Id.ToString("N"));
if (item.Parent != null)
{
element.SetAttribute("parentID", item.Parent.Id.ToString("N"));
}
//AddBookmarkInfo(item, user, element);
AddGeneralProperties(item, element, filter);
// refID?
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
var audio = item as Audio;
if (audio != null)
{
AddAudioResource(element, audio, deviceId, filter);
}
var video = item as Video;
if (video != null)
{
AddVideoResource(element, video, deviceId, filter);
}
AddCover(item, element);
result.DocumentElement.AppendChild(element);
}
private void AddVideoResource(XmlElement container, Video video, string deviceId, Filter filter)
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
var sources = _dtoService.GetMediaSources(video);
int? maxBitrateSetting = null;
var streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
{
ItemId = video.Id.ToString("N"),
MediaSources = sources,
Profile = _profile,
DeviceId = deviceId,
MaxBitrate = maxBitrateSetting
});
var url = streamInfo.ToDlnaUrl(_serverAddress);
res.InnerText = url;
var mediaSource = sources.First(i => string.Equals(i.Id, streamInfo.MediaSourceId));
if (mediaSource.RunTimeTicks.HasValue)
{
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
}
if (filter.Contains("res@size"))
{
if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
{
var size = streamInfo.TargetSize;
if (size.HasValue)
{
res.SetAttribute("size", size.Value.ToString(_usCulture));
}
}
}
var totalBitrate = streamInfo.TotalOutputBitrate;
var targetSampleRate = streamInfo.TargetAudioSampleRate;
var targetChannels = streamInfo.TargetAudioChannels;
var targetWidth = streamInfo.TargetWidth;
var targetHeight = streamInfo.TargetHeight;
if (targetChannels.HasValue)
{
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
}
if (filter.Contains("res@resolution"))
{
if (targetWidth.HasValue && targetHeight.HasValue)
{
res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
}
}
if (targetSampleRate.HasValue)
{
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
}
if (totalBitrate.HasValue)
{
res.SetAttribute("bitrate", totalBitrate.Value.ToString(_usCulture));
}
var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
streamInfo.AudioCodec,
streamInfo.VideoCodec);
var filename = url.Substring(0, url.IndexOf('?'));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
var contentFeatures = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container,
streamInfo.VideoCodec,
streamInfo.AudioCodec,
targetWidth,
targetHeight,
totalBitrate,
streamInfo.TargetTimestamp,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.TranscodeSeekInfo);
res.SetAttribute("protocolInfo", String.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
container.AppendChild(res);
}
private void AddAudioResource(XmlElement container, Audio audio, string deviceId, Filter filter)
{
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
var sources = _dtoService.GetMediaSources(audio);
var streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions
{
ItemId = audio.Id.ToString("N"),
MediaSources = sources,
Profile = _profile,
DeviceId = deviceId
});
var url = streamInfo.ToDlnaUrl(_serverAddress);
res.InnerText = url;
var mediaSource = sources.First(i => string.Equals(i.Id, streamInfo.MediaSourceId));
if (mediaSource.RunTimeTicks.HasValue)
{
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
}
if (filter.Contains("res@size"))
{
if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
{
var size = streamInfo.TargetSize;
if (size.HasValue)
{
res.SetAttribute("size", size.Value.ToString(_usCulture));
}
}
}
var targetAudioBitrate = streamInfo.TargetAudioBitrate;
var targetSampleRate = streamInfo.TargetAudioSampleRate;
var targetChannels = streamInfo.TargetAudioChannels;
if (targetChannels.HasValue)
{
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
}
if (targetSampleRate.HasValue)
{
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
}
if (targetAudioBitrate.HasValue)
{
res.SetAttribute("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
}
var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
streamInfo.AudioCodec);
var filename = url.Substring(0, url.IndexOf('?'));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
streamInfo.TargetAudioCodec,
targetAudioBitrate,
targetSampleRate,
targetChannels,
streamInfo.IsDirectStream,
streamInfo.RunTimeTicks,
streamInfo.TranscodeSeekInfo);
res.SetAttribute("protocolInfo", String.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
container.AppendChild(res);
}
private XmlElement CreateObjectClass(XmlDocument result, BaseItem item)
{
var objectClass = result.CreateElement("upnp", "class", NS_UPNP);
if (item.IsFolder)
{
string classType = null;
if (!_profile.RequiresPlainFolders)
{
if (item is MusicAlbum)
{
classType = "object.container.album.musicAlbum";
}
if (item is MusicArtist)
{
classType = "object.container.person.musicArtist";
}
}
objectClass.InnerText = classType ?? "object.container.storageFolder";
}
else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
objectClass.InnerText = "object.item.audioItem.musicTrack";
}
else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
{
objectClass.InnerText = "object.item.imageItem.photo";
}
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
if (!_profile.RequiresPlainVideoItems && item is Movie)
{
objectClass.InnerText = "object.item.videoItem.movie";
}
else
{
objectClass.InnerText = "object.item.videoItem";
}
}
else
{
throw new NotSupportedException();
}
return objectClass;
}
private void AddPeople(BaseItem item, XmlElement element)
{
foreach (var actor in item.People)
{
AddValue(element, "upnp", (actor.Type ?? PersonType.Actor).ToLower(), actor.Name, NS_UPNP);
}
}
private void AddBookmarkInfo(BaseItem item, User user, XmlElement element)
{
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());
if (userdata.PlaybackPositionTicks > 0)
{
var dcmInfo = element.OwnerDocument.CreateElement("sec", "dcmInfo", NS_SEC);
dcmInfo.InnerText = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds).ToString(_usCulture));
element.AppendChild(dcmInfo);
}
}
/// <summary>
/// Adds fields used by both items and folders
/// </summary>
/// <param name="item">The item.</param>
/// <param name="element">The element.</param>
/// <param name="filter">The filter.</param>
private void AddCommonFields(BaseItem item, XmlElement element, Filter filter)
{
if (filter.Contains("dc:title"))
{
AddValue(element, "dc", "title", item.Name, NS_DC);
}
element.AppendChild(CreateObjectClass(element.OwnerDocument, item));
if (filter.Contains("dc:date"))
{
if (item.PremiereDate.HasValue)
{
AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
}
}
foreach (var genre in item.Genres)
{
AddValue(element, "upnp", "genre", genre, NS_UPNP);
}
foreach (var studio in item.Studios)
{
AddValue(element, "upnp", "publisher", studio, NS_UPNP);
}
if (filter.Contains("dc:description"))
{
if (!string.IsNullOrWhiteSpace(item.Overview))
{
AddValue(element, "dc", "description", item.Overview, NS_DC);
}
}
if (filter.Contains("upnp:longDescription"))
{
if (!string.IsNullOrWhiteSpace(item.Overview))
{
AddValue(element, "upnp", "longDescription", item.Overview, NS_UPNP);
}
}
if (!string.IsNullOrEmpty(item.OfficialRating))
{
if (filter.Contains("dc:rating"))
{
AddValue(element, "dc", "rating", item.OfficialRating, NS_DC);
}
if (filter.Contains("upnp:rating"))
{
AddValue(element, "upnp", "rating", item.OfficialRating, NS_UPNP);
}
}
AddPeople(item, element);
}
private void AddGeneralProperties(BaseItem item, XmlElement element, Filter filter)
{
AddCommonFields(item, element, filter);
var audio = item as Audio;
if (audio != null)
{
foreach (var artist in audio.Artists)
{
AddValue(element, "upnp", "artist", artist, NS_UPNP);
}
if (!string.IsNullOrEmpty(audio.Album))
{
AddValue(element, "upnp", "album", audio.Album, NS_UPNP);
}
if (!string.IsNullOrEmpty(audio.AlbumArtist))
{
AddValue(element, "upnp", "albumArtist", audio.AlbumArtist, NS_UPNP);
}
}
var album = item as MusicAlbum;
if (album != null)
{
if (!string.IsNullOrEmpty(album.AlbumArtist))
{
AddValue(element, "upnp", "artist", album.AlbumArtist, NS_UPNP);
AddValue(element, "upnp", "albumArtist", album.AlbumArtist, NS_UPNP);
}
}
var musicVideo = item as MusicVideo;
if (musicVideo != null)
{
if (!string.IsNullOrEmpty(musicVideo.Artist))
{
AddValue(element, "upnp", "artist", musicVideo.Artist, NS_UPNP);
}
if (!string.IsNullOrEmpty(musicVideo.Album))
{
AddValue(element, "upnp", "album", musicVideo.Album, NS_UPNP);
}
}
if (item.IndexNumber.HasValue)
{
AddValue(element, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
}
}
private void AddCover(BaseItem item, XmlElement element)
{
var imageInfo = GetImageInfo(item);
if (imageInfo == null)
{
return;
}
var result = element.OwnerDocument;
var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight);
var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = _profile.AlbumArtPn;
icon.SetAttributeNode(profile);
icon.InnerText = albumartUrlInfo.Url;
element.AppendChild(icon);
var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth, _profile.MaxIconHeight);
icon = result.CreateElement("upnp", "icon", NS_UPNP);
profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
profile.InnerText = _profile.AlbumArtPn;
icon.SetAttributeNode(profile);
icon.InnerText = iconUrlInfo.Url;
element.AppendChild(icon);
if (!_profile.EnableAlbumArtInDidl)
{
return;
}
var res = result.CreateElement(string.Empty, "res", NS_DIDL);
res.InnerText = albumartUrlInfo.Url;
var width = albumartUrlInfo.Width;
var height = albumartUrlInfo.Height;
var mediaProfile = new MediaFormatProfileResolver().ResolveImageFormat("jpg", width, height);
var orgPn = mediaProfile.HasValue ? "DLNA.ORG_PN=:" + mediaProfile.Value + ";" : string.Empty;
res.SetAttribute("protocolInfo", string.Format(
"http-get:*:{1}:{0}DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS={2}",
orgPn,
"image/jpeg",
DlnaMaps.DefaultStreaming
));
if (width.HasValue && height.HasValue)
{
res.SetAttribute("resolution", string.Format("{0}x{1}", width.Value, height.Value));
}
element.AppendChild(res);
}
private ImageDownloadInfo GetImageInfo(BaseItem item)
{
if (item.HasImage(ImageType.Primary))
{
return GetImageInfo(item, ImageType.Primary);
}
if (item.HasImage(ImageType.Thumb))
{
return GetImageInfo(item, ImageType.Thumb);
}
if (item is Audio || item is Episode)
{
item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
if (item != null)
{
return GetImageInfo(item, ImageType.Primary);
}
}
return null;
}
private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type)
{
var imageInfo = item.GetImageInfo(type, 0);
string tag = null;
try
{
var guid = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
tag = guid.HasValue ? guid.Value.ToString("N") : null;
}
catch
{
}
int? width = null;
int? height = null;
try
{
var size = _imageProcessor.GetImageSize(imageInfo.Path, imageInfo.DateModified);
width = Convert.ToInt32(size.Width);
height = Convert.ToInt32(size.Height);
}
catch
{
}
return new ImageDownloadInfo
{
ItemId = item.Id.ToString("N"),
Type = ImageType.Primary,
ImageTag = tag,
Width = width,
Height = height
};
}
class ImageDownloadInfo
{
internal string ItemId;
internal string ImageTag;
internal ImageType Type;
internal int? Width;
internal int? Height;
}
class ImageUrlInfo
{
internal string Url;
internal int? Width;
internal int? Height;
}
private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int? maxWidth, int? maxHeight)
{
var url = string.Format("{0}/Items/{1}/Images/{2}?tag={3}&format=jpg",
_serverAddress,
info.ItemId,
info.Type,
info.ImageTag);
if (maxWidth.HasValue)
{
url += "&maxWidth=" + maxWidth.Value.ToString(_usCulture);
}
if (maxHeight.HasValue)
{
url += "&maxHeight=" + maxHeight.Value.ToString(_usCulture);
}
var width = info.Width;
var height = info.Height;
if (width.HasValue && height.HasValue)
{
if (maxWidth.HasValue || maxHeight.HasValue)
{
var newSize = DrawingUtils.Resize(new ImageSize
{
Height = height.Value,
Width = width.Value
}, maxWidth: maxWidth, maxHeight: maxHeight);
width = Convert.ToInt32(newSize.Width);
height = Convert.ToInt32(newSize.Height);
}
}
return new ImageUrlInfo
{
Url = url,
Width = width,
Height = height
};
}
}
}

Loading…
Cancel
Save