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.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Xml;
namespace MediaBrowser.Dlna.Didl namespace MediaBrowser.Dlna.Didl
{ {
public class DidlBuilder public class DidlBuilder
{ {
const string CRLF = "\r\n"; private readonly CultureInfo _usCulture = new CultureInfo("en-US");
const string UNKNOWN = "Unknown";
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
const string DIDL_START = @"<item id=""{0}"" parentID=""{1}"" restricted=""1"" xmlns=""urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"">" + CRLF; private const string NS_DC = "http://purl.org/dc/elements/1.1/";
const string DIDL_TITLE = @" <dc:title xmlns:dc=""http://purl.org/dc/elements/1.1/"">{0}</dc:title>" + CRLF; private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
const string DIDL_ARTIST = @"<upnp:artist xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">{0}</upnp:artist>" + CRLF; private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
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; private readonly DeviceProfile _profile;
const string DIDL_VIDEOCLASS = @" <upnp:class xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">object.item.videoItem</upnp:class>" + CRLF; private readonly IImageProcessor _imageProcessor;
const string DIDL_AUDIOCLASS = @" <upnp:class xmlns:upnp=""urn:schemas-upnp-org:metadata-1-0/upnp/"">object.item.audioItem.musicTrack</upnp:class>" + CRLF; private readonly string _serverAddress;
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 + private readonly IDtoService _dtoService;
@" <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; public DidlBuilder(DeviceProfile profile, IImageProcessor imageProcessor, string serverAddress, IDtoService dtoService)
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; _profile = profile;
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; _imageProcessor = imageProcessor;
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; _serverAddress = serverAddress;
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; _dtoService = dtoService;
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>"; 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> /// <summary>
/// Builds a Didl MetaData object for the specified dto. /// Adds fields used by both items and folders
/// </summary> /// </summary>
/// <param name="dto">The dto.</param> /// <param name="item">The item.</param>
/// <param name="userId">The user identifier.</param> /// <param name="element">The element.</param>
/// <param name="serverAddress">The server address.</param> /// <param name="filter">The filter.</param>
/// <param name="streamUrl">The stream URL.</param> private void AddCommonFields(BaseItem item, XmlElement element, Filter filter)
/// <param name="streams">The streams.</param> {
/// <param name="includeImageRes">if set to <c>true</c> [include image resource].</param> if (filter.Contains("dc:title"))
/// <returns>System.String.</returns> {
public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable<MediaStream> streams, bool includeImageRes) AddValue(element, "dc", "title", item.Name, NS_DC);
{ }
string response = string.Format(DIDL_START, dto.Id, userId);
response += string.Format(DIDL_TITLE, dto.Name.Replace("&", "and")); element.AppendChild(CreateObjectClass(element.OwnerDocument, item));
if (IsVideo(dto))
response += DIDL_VIDEOCLASS; if (filter.Contains("dc:date"))
else {
response += DIDL_AUDIOCLASS; 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);
}
var imageUrl = GetImageUrl(dto, serverAddress); 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.IsNullOrWhiteSpace(imageUrl)) if (!string.IsNullOrEmpty(item.OfficialRating))
{ {
response += string.Format(DIDL_IMAGE, imageUrl); 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);
}
} }
response += string.Format(DIDL_RELEASEDATE, GetDateString(dto.PremiereDate));
//TODO Add genres to didl; AddPeople(item, element);
response += string.Format(DIDL_GENRE, UNKNOWN); }
private XmlElement CreateObjectClass(XmlDocument result, BaseItem item)
{
var objectClass = result.CreateElement("upnp", "class", NS_UPNP);
if (item.IsFolder)
{
string classType = null;
if (IsVideo(dto)) if (!_profile.RequiresPlainFolders)
{
if (item is MusicAlbum)
{
classType = "object.container.album.musicAlbum";
}
if (item is MusicArtist)
{ {
response += string.Format(DESCRIPTION, UNKNOWN); classType = "object.container.person.musicArtist";
response += GetVideoDIDL(dto, streamUrl, streams); }
}
if (includeImageRes && !string.IsNullOrWhiteSpace(imageUrl)) 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)
{ {
response += string.Format(DIDL_IMAGE_RES, imageUrl); objectClass.InnerText = "object.item.videoItem.movie";
}
else
{
objectClass.InnerText = "object.item.videoItem";
} }
} }
else 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) if (audio != null)
{ {
response += string.Format(DIDL_ARTIST, audio.Artists.FirstOrDefault() ?? UNKNOWN); foreach (var artist in audio.Artists)
response += string.Format(DIDL_ALBUM, audio.Album); {
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 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 AddCover(BaseItem item, XmlElement element)
{
var imageInfo = GetImageInfo(item);
response += string.Format(DIDL_TRACKNUM, audio.IndexNumber ?? 0); if (imageInfo == null)
{
return;
} }
response += GetAudioDIDL(dto, streamUrl, streams); var result = element.OwnerDocument;
if (includeImageRes && !string.IsNullOrWhiteSpace(imageUrl)) 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)
{ {
response += string.Format(DIDL_ALBUMIMAGE_RES, imageUrl); 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));
} }
response += DIDL_END; element.AppendChild(res);
}
return response; 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);
} }
private static string GetVideoDIDL(BaseItem dto, string streamUrl, IEnumerable<MediaStream> streams) if (item is Audio || item is Episode)
{ {
var videostream = streams.Where(stream => stream.Type == MediaStreamType.Video).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault(); item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
if (videostream == null) if (item != null)
{ {
// TOOD: ??? return GetImageInfo(item, ImageType.Primary);
return string.Empty; }
} }
return string.Format(DIDL_VIDEO_RES, return null;
videostream.BitRate.HasValue ? videostream.BitRate.Value / 10 : 0,
GetDurationString(dto),
videostream.Width ?? 0,
videostream.Height ?? 0,
streamUrl);
} }
private static string GetAudioDIDL(BaseItem dto, string streamUrl, IEnumerable<MediaStream> streams) 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
{ {
var audiostream = streams.Where(stream => stream.Type == MediaStreamType.Audio).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault();
if (audiostream == null) }
int? width = null;
int? height = null;
try
{ {
// TOOD: ??? var size = _imageProcessor.GetImageSize(imageInfo.Path, imageInfo.DateModified);
return string.Empty;
width = Convert.ToInt32(size.Width);
height = Convert.ToInt32(size.Height);
} }
catch
{
return string.Format(DIDL_AUDIO_RES,
audiostream.BitRate.HasValue ? audiostream.BitRate.Value / 10 : 16000,
GetDurationString(dto),
audiostream.SampleRate ?? 0,
streamUrl);
} }
private static string GetImageUrl(BaseItem dto, string serverAddress) return new ImageDownloadInfo
{ {
const ImageType imageType = ImageType.Primary; ItemId = item.Id.ToString("N"),
Type = ImageType.Primary,
ImageTag = tag,
Width = width,
Height = height
};
}
if (!dto.HasImage(imageType)) class ImageDownloadInfo
{ {
dto = dto.Parents.FirstOrDefault(i => i.HasImage(imageType)); internal string ItemId;
internal string ImageTag;
internal ImageType Type;
internal int? Width;
internal int? Height;
} }
return dto == null ? null : string.Format("{0}/Items/{1}/Images/{2}", serverAddress, dto.Id, imageType); class ImageUrlInfo
{
internal string Url;
internal int? Width;
internal int? Height;
} }
private static string GetDurationString(BaseItem dto) 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)
{ {
var duration = TimeSpan.FromTicks(dto.RunTimeTicks.HasValue ? dto.RunTimeTicks.Value : 0); url += "&maxWidth=" + maxWidth.Value.ToString(_usCulture);
}
// TODO: Bad format string? if (maxHeight.HasValue)
return string.Format("{0}:{1:00}:2{00}.000", duration.Hours, duration.Minutes, duration.Seconds); {
url += "&maxHeight=" + maxHeight.Value.ToString(_usCulture);
} }
private static string GetDateString(DateTime? date) var width = info.Width;
var height = info.Height;
if (width.HasValue && height.HasValue)
{ {
if (!date.HasValue) if (maxWidth.HasValue || maxHeight.HasValue)
return UNKNOWN; {
var newSize = DrawingUtils.Resize(new ImageSize
{
Height = height.Value,
Width = width.Value
return string.Format("{0}-{1:00}-{2:00}", date.Value.Year, date.Value.Month, date.Value.Day); }, maxWidth: maxWidth, maxHeight: maxHeight);
width = Convert.ToInt32(newSize.Width);
height = Convert.ToInt32(newSize.Height);
}
} }
private static bool IsVideo(BaseItem item) return new ImageUrlInfo
{ {
return string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); 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.Controller.Configuration;
using MediaBrowser.Dlna.Common; using MediaBrowser.Dlna.Common;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -299,16 +300,12 @@ namespace MediaBrowser.Dlna.PlayTo
private string CreateDidlMeta(string value) private string CreateDidlMeta(string value)
{ {
if (value == null) if (string.IsNullOrEmpty(value))
return String.Empty; return String.Empty;
var escapedData = value.Replace("<", "&lt;").Replace(">", "&gt;"); return SecurityElement.Escape(value);
return String.Format(BaseDidl, escapedData.Replace("\r\n", ""));
} }
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) public async Task SetNextAvTransport(string value, string header, string metaData)
{ {
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetNextAVTransportURI"); 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)) 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; XElement uPnpResponse;
@ -624,9 +621,9 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
uPnpResponse = XElement.Parse(trackString); 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); return new Tuple<bool, uBaseObject>(true, null);
} }

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

@ -2,6 +2,7 @@
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
@ -38,8 +39,9 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IDtoService _dtoService; 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>(); _locations = new ConcurrentDictionary<string, DateTime>();
_tokenSource = new CancellationTokenSource(); _tokenSource = new CancellationTokenSource();
@ -54,6 +56,7 @@ namespace MediaBrowser.Dlna.PlayTo
_dlnaManager = dlnaManager; _dlnaManager = dlnaManager;
_appHost = appHost; _appHost = appHost;
_dtoService = dtoService; _dtoService = dtoService;
_imageProcessor = imageProcessor;
_config = config; _config = config;
} }
@ -238,7 +241,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (controller == null) 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); controller.Init(device);

@ -2,6 +2,7 @@
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
@ -26,8 +27,9 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IDlnaManager _dlnaManager; private readonly IDlnaManager _dlnaManager;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IDtoService _dtoService; 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; _config = config;
_sessionManager = sessionManager; _sessionManager = sessionManager;
@ -39,6 +41,7 @@ namespace MediaBrowser.Dlna.PlayTo
_dlnaManager = dlnaManager; _dlnaManager = dlnaManager;
_appHost = appHost; _appHost = appHost;
_dtoService = dtoService; _dtoService = dtoService;
_imageProcessor = imageProcessor;
_logger = logManager.GetLogger("PlayTo"); _logger = logManager.GetLogger("PlayTo");
} }
@ -85,7 +88,8 @@ namespace MediaBrowser.Dlna.PlayTo
_userManager, _userManager,
_dlnaManager, _dlnaManager,
_appHost, _appHost,
_dtoService); _dtoService,
_imageProcessor);
_manager.Start(); _manager.Start();
} }

@ -1,5 +1,4 @@
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
@ -8,8 +7,8 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Dlna.Didl;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
@ -27,35 +26,29 @@ namespace MediaBrowser.Dlna.Server
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly DeviceProfile _profile;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly User _user; private readonly User _user;
private readonly string _serverAddress;
private const string NS_DC = "http://purl.org/dc/elements/1.1/"; 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_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_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_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/"; private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private readonly int _systemUpdateId; private readonly int _systemUpdateId;
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); 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) public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId)
{ {
_logger = logger; _logger = logger;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_profile = profile;
_serverAddress = serverAddress;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_user = user; _user = user;
_systemUpdateId = systemUpdateId; _systemUpdateId = systemUpdateId;
_didlBuilder = new DidlBuilder(profile, imageProcessor, serverAddress, dtoService);
} }
public ControlResponse ProcessControlRequest(ControlRequest request) public ControlResponse ProcessControlRequest(ControlRequest request)
@ -91,7 +84,7 @@ namespace MediaBrowser.Dlna.Server
sparams.Add(e.LocalName, e.InnerText.Trim()); sparams.Add(e.LocalName, e.InnerText.Trim());
} }
var deviceId = "fgd"; var deviceId = "test";
IEnumerable<KeyValuePair<string, string>> result; IEnumerable<KeyValuePair<string, string>> result;
@ -270,7 +263,7 @@ namespace MediaBrowser.Dlna.Server
if (string.Equals(flag, "BrowseMetadata")) if (string.Equals(flag, "BrowseMetadata"))
{ {
Browse_AddFolder(result, folder, children.Count, filter); result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, children.Count, filter));
provided++; provided++;
} }
else else
@ -293,11 +286,11 @@ namespace MediaBrowser.Dlna.Server
var f = (Folder)i; var f = (Folder)i;
var childCount = GetChildrenSorted(f, user, sortCriteria).Count(); var childCount = GetChildrenSorted(f, user, sortCriteria).Count();
Browse_AddFolder(result, f, childCount, filter); result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter));
} }
else 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 f = (Folder)i;
var childCount = GetChildrenSorted(f, user, searchCriteria, sortCriteria).Count(); var childCount = GetChildrenSorted(f, user, searchCriteria, sortCriteria).Count();
Browse_AddFolder(result, f, childCount, filter); result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter));
} }
else 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 ? user.RootFolder
: _libraryManager.GetItemById(new Guid(id)); : _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