ControlHandler.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. using MediaBrowser.Common.Extensions;
  2. using MediaBrowser.Controller.Channels;
  3. using MediaBrowser.Controller.Configuration;
  4. using MediaBrowser.Controller.Drawing;
  5. using MediaBrowser.Controller.Entities;
  6. using MediaBrowser.Controller.Entities.Movies;
  7. using MediaBrowser.Controller.Entities.TV;
  8. using MediaBrowser.Controller.Library;
  9. using Emby.Dlna.Didl;
  10. using Emby.Dlna.Server;
  11. using Emby.Dlna.Service;
  12. using MediaBrowser.Model.Configuration;
  13. using MediaBrowser.Model.Dlna;
  14. using MediaBrowser.Model.Entities;
  15. using MediaBrowser.Model.Logging;
  16. using MediaBrowser.Model.Querying;
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Globalization;
  20. using System.IO;
  21. using System.Linq;
  22. using System.Text;
  23. using System.Threading;
  24. using System.Threading.Tasks;
  25. using System.Xml;
  26. using MediaBrowser.Controller.Entities.Audio;
  27. using MediaBrowser.Controller.MediaEncoding;
  28. using MediaBrowser.Model.Globalization;
  29. using MediaBrowser.Model.Xml;
  30. namespace Emby.Dlna.ContentDirectory
  31. {
  32. public class ControlHandler : BaseControlHandler
  33. {
  34. private readonly ILibraryManager _libraryManager;
  35. private readonly IChannelManager _channelManager;
  36. private readonly IUserDataManager _userDataManager;
  37. private readonly IServerConfigurationManager _config;
  38. private readonly User _user;
  39. private readonly IUserViewManager _userViewManager;
  40. private const string NS_DC = "http://purl.org/dc/elements/1.1/";
  41. private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
  42. private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
  43. private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
  44. private readonly int _systemUpdateId;
  45. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  46. private readonly DidlBuilder _didlBuilder;
  47. private readonly DeviceProfile _profile;
  48. public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, IMediaEncoder mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
  49. : base(config, logger, xmlReaderSettingsFactory)
  50. {
  51. _libraryManager = libraryManager;
  52. _userDataManager = userDataManager;
  53. _user = user;
  54. _systemUpdateId = systemUpdateId;
  55. _channelManager = channelManager;
  56. _userViewManager = userViewManager;
  57. _profile = profile;
  58. _config = config;
  59. _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager, mediaEncoder);
  60. }
  61. protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
  62. {
  63. var deviceId = "test";
  64. var user = _user;
  65. if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
  66. return HandleGetSearchCapabilities();
  67. if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
  68. return HandleGetSortCapabilities();
  69. if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase))
  70. return HandleGetSortExtensionCapabilities();
  71. if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
  72. return HandleGetSystemUpdateID();
  73. if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
  74. return HandleBrowse(methodParams, user, deviceId).Result;
  75. if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
  76. return HandleXGetFeatureList();
  77. if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase))
  78. return HandleGetFeatureList();
  79. if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
  80. return HandleXSetBookmark(methodParams, user);
  81. if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
  82. return HandleSearch(methodParams, user, deviceId).Result;
  83. if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase))
  84. return HandleX_BrowseByLetter(methodParams, user, deviceId).Result;
  85. throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
  86. }
  87. private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user)
  88. {
  89. var id = sparams["ObjectID"];
  90. var serverItem = GetItemFromObjectId(id, user);
  91. var item = serverItem.Item;
  92. var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
  93. var userdata = _userDataManager.GetUserData(user, item);
  94. userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
  95. _userDataManager.SaveUserData(user.Id, item, userdata, UserDataSaveReason.TogglePlayed,
  96. CancellationToken.None);
  97. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  98. }
  99. private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities()
  100. {
  101. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  102. {
  103. { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" }
  104. };
  105. }
  106. private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities()
  107. {
  108. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  109. {
  110. { "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
  111. };
  112. }
  113. private IEnumerable<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities()
  114. {
  115. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  116. {
  117. { "SortExtensionCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
  118. };
  119. }
  120. private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
  121. {
  122. var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  123. headers.Add("Id", _systemUpdateId.ToString(_usCulture));
  124. return headers;
  125. }
  126. private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList()
  127. {
  128. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  129. {
  130. { "FeatureList", GetFeatureListXml() }
  131. };
  132. }
  133. private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
  134. {
  135. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  136. {
  137. { "FeatureList", GetFeatureListXml() }
  138. };
  139. }
  140. private string GetFeatureListXml()
  141. {
  142. var builder = new StringBuilder();
  143. builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  144. builder.Append("<Features xmlns=\"urn:schemas-upnp-org:av:avs\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upnp.org/schemas/av/avs.xsd\">");
  145. builder.Append("<Feature name=\"samsung.com_BASICVIEW\" version=\"1\">");
  146. builder.Append("<container id=\"I\" type=\"object.item.imageItem\"/>");
  147. builder.Append("<container id=\"A\" type=\"object.item.audioItem\"/>");
  148. builder.Append("<container id=\"V\" type=\"object.item.videoItem\"/>");
  149. builder.Append("</Feature>");
  150. builder.Append("</Features>");
  151. return builder.ToString();
  152. }
  153. public string GetValueOrDefault(IDictionary<string, string> sparams, string key, string defaultValue)
  154. {
  155. string val;
  156. if (sparams.TryGetValue(key, out val))
  157. {
  158. return val;
  159. }
  160. return defaultValue;
  161. }
  162. private async Task<IEnumerable<KeyValuePair<string, string>>> HandleBrowse(IDictionary<string, string> sparams, User user, string deviceId)
  163. {
  164. var id = sparams["ObjectID"];
  165. var flag = sparams["BrowseFlag"];
  166. var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
  167. var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
  168. var provided = 0;
  169. // Default to null instead of 0
  170. // Upnp inspector sends 0 as requestedCount when it wants everything
  171. int? requestedCount = null;
  172. int? start = 0;
  173. int requestedVal;
  174. if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
  175. {
  176. requestedCount = requestedVal;
  177. }
  178. int startVal;
  179. if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0)
  180. {
  181. start = startVal;
  182. }
  183. var settings = new XmlWriterSettings
  184. {
  185. Encoding = Encoding.UTF8,
  186. CloseOutput = false,
  187. OmitXmlDeclaration = true,
  188. ConformanceLevel = ConformanceLevel.Fragment
  189. };
  190. StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
  191. int totalCount;
  192. using (XmlWriter writer = XmlWriter.Create(builder, settings))
  193. {
  194. //writer.WriteStartDocument();
  195. writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
  196. writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
  197. writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
  198. writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
  199. //didl.SetAttribute("xmlns:sec", NS_SEC);
  200. DidlBuilder.WriteXmlRootAttributes(_profile, writer);
  201. var serverItem = GetItemFromObjectId(id, user);
  202. var item = serverItem.Item;
  203. if (string.Equals(flag, "BrowseMetadata"))
  204. {
  205. totalCount = 1;
  206. if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
  207. {
  208. var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
  209. _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
  210. }
  211. else
  212. {
  213. _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, item, user, null, null, deviceId, filter);
  214. }
  215. provided++;
  216. }
  217. else
  218. {
  219. var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
  220. totalCount = childrenResult.TotalRecordCount;
  221. provided = childrenResult.Items.Length;
  222. foreach (var i in childrenResult.Items)
  223. {
  224. var childItem = i.Item;
  225. var displayStubType = i.StubType;
  226. if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
  227. {
  228. var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
  229. .TotalRecordCount;
  230. _didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
  231. }
  232. else
  233. {
  234. _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, childItem, user, item, serverItem.StubType, deviceId, filter);
  235. }
  236. }
  237. }
  238. writer.WriteFullEndElement();
  239. //writer.WriteEndDocument();
  240. }
  241. var resXML = builder.ToString();
  242. return new List<KeyValuePair<string, string>>
  243. {
  244. new KeyValuePair<string,string>("Result", resXML),
  245. new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
  246. new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
  247. new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
  248. };
  249. }
  250. private Task<IEnumerable<KeyValuePair<string, string>>> HandleX_BrowseByLetter(IDictionary<string, string> sparams, User user, string deviceId)
  251. {
  252. // TODO: Implement this method
  253. return HandleSearch(sparams, user, deviceId);
  254. }
  255. private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(IDictionary<string, string> sparams, User user, string deviceId)
  256. {
  257. var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", ""));
  258. var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
  259. var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
  260. // sort example: dc:title, dc:date
  261. // Default to null instead of 0
  262. // Upnp inspector sends 0 as requestedCount when it wants everything
  263. int? requestedCount = null;
  264. int? start = 0;
  265. int requestedVal;
  266. if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
  267. {
  268. requestedCount = requestedVal;
  269. }
  270. int startVal;
  271. if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0)
  272. {
  273. start = startVal;
  274. }
  275. var settings = new XmlWriterSettings
  276. {
  277. Encoding = Encoding.UTF8,
  278. CloseOutput = false,
  279. OmitXmlDeclaration = true,
  280. ConformanceLevel = ConformanceLevel.Fragment
  281. };
  282. StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
  283. int totalCount = 0;
  284. int provided = 0;
  285. using (XmlWriter writer = XmlWriter.Create(builder, settings))
  286. {
  287. //writer.WriteStartDocument();
  288. writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
  289. writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
  290. writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
  291. writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
  292. //didl.SetAttribute("xmlns:sec", NS_SEC);
  293. DidlBuilder.WriteXmlRootAttributes(_profile, writer);
  294. var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
  295. var item = serverItem.Item;
  296. var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount).ConfigureAwait(false));
  297. totalCount = childrenResult.TotalRecordCount;
  298. provided = childrenResult.Items.Length;
  299. foreach (var i in childrenResult.Items)
  300. {
  301. if (i.IsDisplayedAsFolder)
  302. {
  303. var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
  304. .TotalRecordCount;
  305. _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
  306. }
  307. else
  308. {
  309. _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, i, user, item, serverItem.StubType, deviceId, filter);
  310. }
  311. }
  312. writer.WriteFullEndElement();
  313. //writer.WriteEndDocument();
  314. }
  315. var resXML = builder.ToString();
  316. return new List<KeyValuePair<string, string>>
  317. {
  318. new KeyValuePair<string,string>("Result", resXML),
  319. new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
  320. new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
  321. new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
  322. };
  323. }
  324. private Task<QueryResult<BaseItem>> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
  325. {
  326. var folder = (Folder)item;
  327. var sortOrders = new List<string>();
  328. if (!folder.IsPreSorted)
  329. {
  330. sortOrders.Add(ItemSortBy.SortName);
  331. }
  332. var mediaTypes = new List<string>();
  333. bool? isFolder = null;
  334. if (search.SearchType == SearchType.Audio)
  335. {
  336. mediaTypes.Add(MediaType.Audio);
  337. isFolder = false;
  338. }
  339. else if (search.SearchType == SearchType.Video)
  340. {
  341. mediaTypes.Add(MediaType.Video);
  342. isFolder = false;
  343. }
  344. else if (search.SearchType == SearchType.Image)
  345. {
  346. mediaTypes.Add(MediaType.Photo);
  347. isFolder = false;
  348. }
  349. else if (search.SearchType == SearchType.Playlist)
  350. {
  351. //items = items.OfType<Playlist>();
  352. isFolder = true;
  353. }
  354. else if (search.SearchType == SearchType.MusicAlbum)
  355. {
  356. //items = items.OfType<MusicAlbum>();
  357. isFolder = true;
  358. }
  359. return folder.GetItems(new InternalItemsQuery
  360. {
  361. Limit = limit,
  362. StartIndex = startIndex,
  363. SortBy = sortOrders.ToArray(),
  364. SortOrder = sort.SortOrder,
  365. User = user,
  366. Recursive = true,
  367. IsMissing = false,
  368. ExcludeItemTypes = new[] { typeof(Game).Name, typeof(Book).Name },
  369. IsFolder = isFolder,
  370. MediaTypes = mediaTypes.ToArray()
  371. });
  372. }
  373. private async Task<QueryResult<ServerItem>> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
  374. {
  375. if (item is MusicGenre)
  376. {
  377. return GetMusicGenreItems(item, null, user, sort, startIndex, limit);
  378. }
  379. if (item is MusicArtist)
  380. {
  381. return GetMusicArtistItems(item, null, user, sort, startIndex, limit);
  382. }
  383. if (stubType.HasValue)
  384. {
  385. if (stubType.Value == StubType.People)
  386. {
  387. var items = _libraryManager.GetPeopleItems(new InternalPeopleQuery
  388. {
  389. ItemId = item.Id
  390. }).ToArray();
  391. var result = new QueryResult<ServerItem>
  392. {
  393. Items = items.Select(i => new ServerItem(i)).ToArray(),
  394. TotalRecordCount = items.Length
  395. };
  396. return ApplyPaging(result, startIndex, limit);
  397. }
  398. var person = item as Person;
  399. if (person != null)
  400. {
  401. return GetItemsFromPerson(person, user, startIndex, limit);
  402. }
  403. return ApplyPaging(new QueryResult<ServerItem>(), startIndex, limit);
  404. }
  405. var folder = (Folder)item;
  406. var query = new InternalItemsQuery
  407. {
  408. Limit = limit,
  409. StartIndex = startIndex,
  410. User = user,
  411. IsMissing = false,
  412. PresetViews = new[] {CollectionType.Movies, CollectionType.TvShows, CollectionType.Music},
  413. ExcludeItemTypes = new[] {typeof (Game).Name, typeof (Book).Name},
  414. IsPlaceHolder = false
  415. };
  416. SetSorting(query, sort, folder.IsPreSorted);
  417. var queryResult = await folder.GetItems(query).ConfigureAwait(false);
  418. return ToResult(queryResult);
  419. }
  420. private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
  421. {
  422. var query = new InternalItemsQuery(user)
  423. {
  424. Recursive = true,
  425. ParentId = parentId,
  426. ArtistIds = new[] { item.Id.ToString("N") },
  427. IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
  428. Limit = limit,
  429. StartIndex = startIndex
  430. };
  431. SetSorting(query, sort, false);
  432. var result = _libraryManager.GetItemsResult(query);
  433. return ToResult(result);
  434. }
  435. private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
  436. {
  437. var query = new InternalItemsQuery(user)
  438. {
  439. Recursive = true,
  440. ParentId = parentId,
  441. GenreIds = new[] {item.Id.ToString("N")},
  442. IncludeItemTypes = new[] {typeof (MusicAlbum).Name},
  443. Limit = limit,
  444. StartIndex = startIndex
  445. };
  446. SetSorting(query, sort, false);
  447. var result = _libraryManager.GetItemsResult(query);
  448. return ToResult(result);
  449. }
  450. private QueryResult<ServerItem> ToResult(QueryResult<BaseItem> result)
  451. {
  452. var serverItems = result
  453. .Items
  454. .Select(i => new ServerItem(i))
  455. .ToArray();
  456. return new QueryResult<ServerItem>
  457. {
  458. TotalRecordCount = result.TotalRecordCount,
  459. Items = serverItems
  460. };
  461. }
  462. private void SetSorting(InternalItemsQuery query, SortCriteria sort, bool isPreSorted)
  463. {
  464. var sortOrders = new List<string>();
  465. if (!isPreSorted)
  466. {
  467. sortOrders.Add(ItemSortBy.SortName);
  468. }
  469. query.SortBy = sortOrders.ToArray();
  470. query.SortOrder = sort.SortOrder;
  471. }
  472. private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
  473. {
  474. var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
  475. {
  476. PersonIds = new[] { person.Id.ToString("N") },
  477. IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(Trailer).Name },
  478. SortBy = new[] { ItemSortBy.SortName },
  479. Limit = limit,
  480. StartIndex = startIndex
  481. });
  482. var serverItems = itemsResult.Items.Select(i => new ServerItem(i))
  483. .ToArray();
  484. return new QueryResult<ServerItem>
  485. {
  486. TotalRecordCount = itemsResult.TotalRecordCount,
  487. Items = serverItems
  488. };
  489. }
  490. private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit)
  491. {
  492. result.Items = result.Items.Skip(startIndex ?? 0).Take(limit ?? int.MaxValue).ToArray();
  493. return result;
  494. }
  495. private ServerItem GetItemFromObjectId(string id, User user)
  496. {
  497. return DidlBuilder.IsIdRoot(id)
  498. ? new ServerItem(user.RootFolder)
  499. : ParseItemId(id, user);
  500. }
  501. private ServerItem ParseItemId(string id, User user)
  502. {
  503. Guid itemId;
  504. StubType? stubType = null;
  505. // After using PlayTo, MediaMonkey sends a request to the server trying to get item info
  506. const string paramsSrch = "Params=";
  507. var paramsIndex = id.IndexOf(paramsSrch, StringComparison.OrdinalIgnoreCase);
  508. if (paramsIndex != -1)
  509. {
  510. id = id.Substring(paramsIndex + paramsSrch.Length);
  511. var parts = id.Split(';');
  512. id = parts[23];
  513. }
  514. if (id.StartsWith("folder_", StringComparison.OrdinalIgnoreCase))
  515. {
  516. stubType = StubType.Folder;
  517. id = id.Split(new[] { '_' }, 2)[1];
  518. }
  519. else if (id.StartsWith("people_", StringComparison.OrdinalIgnoreCase))
  520. {
  521. stubType = StubType.People;
  522. id = id.Split(new[] { '_' }, 2)[1];
  523. }
  524. if (Guid.TryParse(id, out itemId))
  525. {
  526. var item = _libraryManager.GetItemById(itemId);
  527. return new ServerItem(item)
  528. {
  529. StubType = stubType
  530. };
  531. }
  532. Logger.Error("Error parsing item Id: {0}. Returning user root folder.", id);
  533. return new ServerItem(user.RootFolder);
  534. }
  535. }
  536. internal class ServerItem
  537. {
  538. public BaseItem Item { get; set; }
  539. public StubType? StubType { get; set; }
  540. public ServerItem(BaseItem item)
  541. {
  542. Item = item;
  543. if (item is IItemByName && !(item is Folder))
  544. {
  545. StubType = Dlna.ContentDirectory.StubType.Folder;
  546. }
  547. }
  548. }
  549. public enum StubType
  550. {
  551. Folder = 0,
  552. People = 1
  553. }
  554. }