ControlHandler.cs 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading;
  8. using System.Xml;
  9. using Emby.Dlna.Didl;
  10. using Emby.Dlna.Service;
  11. using MediaBrowser.Common.Extensions;
  12. using MediaBrowser.Controller.Configuration;
  13. using MediaBrowser.Controller.Drawing;
  14. using MediaBrowser.Controller.Dto;
  15. using MediaBrowser.Controller.Entities;
  16. using MediaBrowser.Controller.Entities.Audio;
  17. using MediaBrowser.Controller.Entities.Movies;
  18. using MediaBrowser.Controller.Entities.TV;
  19. using MediaBrowser.Controller.Library;
  20. using MediaBrowser.Controller.LiveTv;
  21. using MediaBrowser.Controller.MediaEncoding;
  22. using MediaBrowser.Controller.Playlists;
  23. using MediaBrowser.Controller.TV;
  24. using MediaBrowser.Model.Dlna;
  25. using MediaBrowser.Model.Entities;
  26. using MediaBrowser.Model.Globalization;
  27. using MediaBrowser.Model.Querying;
  28. using Microsoft.Extensions.Logging;
  29. namespace Emby.Dlna.ContentDirectory
  30. {
  31. public class ControlHandler : BaseControlHandler
  32. {
  33. private readonly ILibraryManager _libraryManager;
  34. private readonly IUserDataManager _userDataManager;
  35. private readonly IServerConfigurationManager _config;
  36. private readonly User _user;
  37. private readonly IUserViewManager _userViewManager;
  38. private readonly ITVSeriesManager _tvSeriesManager;
  39. private const string NS_DC = "http://purl.org/dc/elements/1.1/";
  40. private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
  41. private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
  42. private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
  43. private readonly int _systemUpdateId;
  44. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  45. private readonly DidlBuilder _didlBuilder;
  46. private readonly DeviceProfile _profile;
  47. public ControlHandler(
  48. ILogger logger,
  49. ILibraryManager libraryManager,
  50. DeviceProfile profile,
  51. string serverAddress,
  52. string accessToken,
  53. IImageProcessor imageProcessor,
  54. IUserDataManager userDataManager,
  55. User user, int systemUpdateId,
  56. IServerConfigurationManager config,
  57. ILocalizationManager localization,
  58. IMediaSourceManager mediaSourceManager,
  59. IUserViewManager userViewManager,
  60. IMediaEncoder mediaEncoder,
  61. ITVSeriesManager tvSeriesManager)
  62. : base(config, logger)
  63. {
  64. _libraryManager = libraryManager;
  65. _userDataManager = userDataManager;
  66. _user = user;
  67. _systemUpdateId = systemUpdateId;
  68. _userViewManager = userViewManager;
  69. _tvSeriesManager = tvSeriesManager;
  70. _profile = profile;
  71. _config = config;
  72. _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, mediaEncoder);
  73. }
  74. protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
  75. {
  76. var deviceId = "test";
  77. var user = _user;
  78. if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
  79. return HandleGetSearchCapabilities();
  80. if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
  81. return HandleGetSortCapabilities();
  82. if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase))
  83. return HandleGetSortExtensionCapabilities();
  84. if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
  85. return HandleGetSystemUpdateID();
  86. if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
  87. return HandleBrowse(methodParams, user, deviceId);
  88. if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
  89. return HandleXGetFeatureList();
  90. if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase))
  91. return HandleGetFeatureList();
  92. if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
  93. return HandleXSetBookmark(methodParams, user);
  94. if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
  95. return HandleSearch(methodParams, user, deviceId);
  96. if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase))
  97. return HandleX_BrowseByLetter(methodParams, user, deviceId);
  98. throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
  99. }
  100. private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user)
  101. {
  102. var id = sparams["ObjectID"];
  103. var serverItem = GetItemFromObjectId(id, user);
  104. var item = serverItem.Item;
  105. var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
  106. var userdata = _userDataManager.GetUserData(user, item);
  107. userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
  108. _userDataManager.SaveUserData(user, item, userdata, UserDataSaveReason.TogglePlayed,
  109. CancellationToken.None);
  110. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  111. }
  112. private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities()
  113. {
  114. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  115. {
  116. { "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" }
  117. };
  118. }
  119. private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities()
  120. {
  121. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  122. {
  123. { "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" }
  124. };
  125. }
  126. private IEnumerable<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities()
  127. {
  128. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  129. {
  130. { "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" }
  131. };
  132. }
  133. private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
  134. {
  135. var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  136. headers.Add("Id", _systemUpdateId.ToString(_usCulture));
  137. return headers;
  138. }
  139. private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList()
  140. {
  141. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  142. {
  143. { "FeatureList", GetFeatureListXml() }
  144. };
  145. }
  146. private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
  147. {
  148. return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
  149. {
  150. { "FeatureList", GetFeatureListXml() }
  151. };
  152. }
  153. private string GetFeatureListXml()
  154. {
  155. var builder = new StringBuilder();
  156. builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  157. 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\">");
  158. builder.Append("<Feature name=\"samsung.com_BASICVIEW\" version=\"1\">");
  159. builder.Append("<container id=\"I\" type=\"object.item.imageItem\"/>");
  160. builder.Append("<container id=\"A\" type=\"object.item.audioItem\"/>");
  161. builder.Append("<container id=\"V\" type=\"object.item.videoItem\"/>");
  162. builder.Append("</Feature>");
  163. builder.Append("</Features>");
  164. return builder.ToString();
  165. }
  166. public string GetValueOrDefault(IDictionary<string, string> sparams, string key, string defaultValue)
  167. {
  168. if (sparams.TryGetValue(key, out string val))
  169. {
  170. return val;
  171. }
  172. return defaultValue;
  173. }
  174. private IEnumerable<KeyValuePair<string, string>> HandleBrowse(IDictionary<string, string> sparams, User user, string deviceId)
  175. {
  176. var id = sparams["ObjectID"];
  177. var flag = sparams["BrowseFlag"];
  178. var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
  179. var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
  180. var provided = 0;
  181. // Default to null instead of 0
  182. // Upnp inspector sends 0 as requestedCount when it wants everything
  183. int? requestedCount = null;
  184. int? start = 0;
  185. if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out var requestedVal) && requestedVal > 0)
  186. {
  187. requestedCount = requestedVal;
  188. }
  189. if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out var startVal) && startVal > 0)
  190. {
  191. start = startVal;
  192. }
  193. var settings = new XmlWriterSettings
  194. {
  195. Encoding = Encoding.UTF8,
  196. CloseOutput = false,
  197. OmitXmlDeclaration = true,
  198. ConformanceLevel = ConformanceLevel.Fragment
  199. };
  200. StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
  201. int totalCount;
  202. var dlnaOptions = _config.GetDlnaConfiguration();
  203. using (var writer = XmlWriter.Create(builder, settings))
  204. {
  205. //writer.WriteStartDocument();
  206. writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
  207. writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
  208. writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
  209. writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
  210. //didl.SetAttribute("xmlns:sec", NS_SEC);
  211. DidlBuilder.WriteXmlRootAttributes(_profile, writer);
  212. var serverItem = GetItemFromObjectId(id, user);
  213. var item = serverItem.Item;
  214. if (string.Equals(flag, "BrowseMetadata"))
  215. {
  216. totalCount = 1;
  217. if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
  218. {
  219. var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
  220. _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
  221. }
  222. else
  223. {
  224. _didlBuilder.WriteItemElement(dlnaOptions, writer, item, user, null, null, deviceId, filter);
  225. }
  226. provided++;
  227. }
  228. else
  229. {
  230. var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
  231. totalCount = childrenResult.TotalRecordCount;
  232. provided = childrenResult.Items.Count;
  233. foreach (var i in childrenResult.Items)
  234. {
  235. var childItem = i.Item;
  236. var displayStubType = i.StubType;
  237. if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
  238. {
  239. var childCount = (GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0))
  240. .TotalRecordCount;
  241. _didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
  242. }
  243. else
  244. {
  245. _didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, user, item, serverItem.StubType, deviceId, filter);
  246. }
  247. }
  248. }
  249. writer.WriteFullEndElement();
  250. //writer.WriteEndDocument();
  251. }
  252. var resXML = builder.ToString();
  253. return new[]
  254. {
  255. new KeyValuePair<string,string>("Result", resXML),
  256. new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
  257. new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
  258. new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
  259. };
  260. }
  261. private IEnumerable<KeyValuePair<string, string>> HandleX_BrowseByLetter(IDictionary<string, string> sparams, User user, string deviceId)
  262. {
  263. // TODO: Implement this method
  264. return HandleSearch(sparams, user, deviceId);
  265. }
  266. private IEnumerable<KeyValuePair<string, string>> HandleSearch(IDictionary<string, string> sparams, User user, string deviceId)
  267. {
  268. var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", ""));
  269. var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
  270. var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
  271. // sort example: dc:title, dc:date
  272. // Default to null instead of 0
  273. // Upnp inspector sends 0 as requestedCount when it wants everything
  274. int? requestedCount = null;
  275. int? start = 0;
  276. if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out var requestedVal) && requestedVal > 0)
  277. {
  278. requestedCount = requestedVal;
  279. }
  280. if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out var startVal) && startVal > 0)
  281. {
  282. start = startVal;
  283. }
  284. var settings = new XmlWriterSettings
  285. {
  286. Encoding = Encoding.UTF8,
  287. CloseOutput = false,
  288. OmitXmlDeclaration = true,
  289. ConformanceLevel = ConformanceLevel.Fragment
  290. };
  291. StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
  292. int totalCount = 0;
  293. int provided = 0;
  294. using (var writer = XmlWriter.Create(builder, settings))
  295. {
  296. //writer.WriteStartDocument();
  297. writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
  298. writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
  299. writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
  300. writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
  301. //didl.SetAttribute("xmlns:sec", NS_SEC);
  302. DidlBuilder.WriteXmlRootAttributes(_profile, writer);
  303. var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
  304. var item = serverItem.Item;
  305. var childrenResult = (GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount));
  306. totalCount = childrenResult.TotalRecordCount;
  307. provided = childrenResult.Items.Count;
  308. var dlnaOptions = _config.GetDlnaConfiguration();
  309. foreach (var i in childrenResult.Items)
  310. {
  311. if (i.IsDisplayedAsFolder)
  312. {
  313. var childCount = (GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0))
  314. .TotalRecordCount;
  315. _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
  316. }
  317. else
  318. {
  319. _didlBuilder.WriteItemElement(dlnaOptions, writer, i, user, item, serverItem.StubType, deviceId, filter);
  320. }
  321. }
  322. writer.WriteFullEndElement();
  323. //writer.WriteEndDocument();
  324. }
  325. var resXML = builder.ToString();
  326. return new List<KeyValuePair<string, string>>
  327. {
  328. new KeyValuePair<string,string>("Result", resXML),
  329. new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
  330. new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
  331. new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
  332. };
  333. }
  334. private QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
  335. {
  336. var folder = (Folder)item;
  337. var sortOrders = new List<(string, SortOrder)>();
  338. if (!folder.IsPreSorted)
  339. {
  340. sortOrders.Add((ItemSortBy.SortName, sort.SortOrder));
  341. }
  342. var mediaTypes = new List<string>();
  343. bool? isFolder = null;
  344. if (search.SearchType == SearchType.Audio)
  345. {
  346. mediaTypes.Add(MediaType.Audio);
  347. isFolder = false;
  348. }
  349. else if (search.SearchType == SearchType.Video)
  350. {
  351. mediaTypes.Add(MediaType.Video);
  352. isFolder = false;
  353. }
  354. else if (search.SearchType == SearchType.Image)
  355. {
  356. mediaTypes.Add(MediaType.Photo);
  357. isFolder = false;
  358. }
  359. else if (search.SearchType == SearchType.Playlist)
  360. {
  361. //items = items.OfType<Playlist>();
  362. isFolder = true;
  363. }
  364. else if (search.SearchType == SearchType.MusicAlbum)
  365. {
  366. //items = items.OfType<MusicAlbum>();
  367. isFolder = true;
  368. }
  369. return folder.GetItems(new InternalItemsQuery
  370. {
  371. Limit = limit,
  372. StartIndex = startIndex,
  373. OrderBy = sortOrders,
  374. User = user,
  375. Recursive = true,
  376. IsMissing = false,
  377. ExcludeItemTypes = new[] { typeof(Book).Name },
  378. IsFolder = isFolder,
  379. MediaTypes = mediaTypes.ToArray(),
  380. DtoOptions = GetDtoOptions()
  381. });
  382. }
  383. private DtoOptions GetDtoOptions()
  384. {
  385. return new DtoOptions(true);
  386. }
  387. private QueryResult<ServerItem> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
  388. {
  389. if (item is MusicGenre)
  390. {
  391. return GetMusicGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
  392. }
  393. if (item is MusicArtist)
  394. {
  395. return GetMusicArtistItems(item, Guid.Empty, user, sort, startIndex, limit);
  396. }
  397. if (item is Genre)
  398. {
  399. return GetGenreItems(item, Guid.Empty, user, sort, startIndex, limit);
  400. }
  401. if ((!stubType.HasValue || stubType.Value != StubType.Folder)
  402. && item is IHasCollectionType collectionFolder)
  403. {
  404. if (string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
  405. {
  406. return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
  407. }
  408. else if (string.Equals(CollectionType.Movies, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
  409. {
  410. return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
  411. }
  412. else if (string.Equals(CollectionType.TvShows, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
  413. {
  414. return GetTvFolders(item, user, stubType, sort, startIndex, limit);
  415. }
  416. else if (string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
  417. {
  418. return GetFolders(item, user, stubType, sort, startIndex, limit);
  419. }
  420. else if (string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
  421. {
  422. return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
  423. }
  424. }
  425. if (stubType.HasValue)
  426. {
  427. if (stubType.Value != StubType.Folder)
  428. {
  429. return ApplyPaging(new QueryResult<ServerItem>(), startIndex, limit);
  430. }
  431. }
  432. var folder = (Folder)item;
  433. var query = new InternalItemsQuery(user)
  434. {
  435. Limit = limit,
  436. StartIndex = startIndex,
  437. IsVirtualItem = false,
  438. ExcludeItemTypes = new[] { typeof(Book).Name },
  439. IsPlaceHolder = false,
  440. DtoOptions = GetDtoOptions()
  441. };
  442. SetSorting(query, sort, folder.IsPreSorted);
  443. var queryResult = folder.GetItems(query);
  444. return ToResult(queryResult);
  445. }
  446. private QueryResult<ServerItem> GetLiveTvChannels(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
  447. {
  448. var query = new InternalItemsQuery(user)
  449. {
  450. StartIndex = startIndex,
  451. Limit = limit,
  452. };
  453. query.IncludeItemTypes = new[] { typeof(LiveTvChannel).Name };
  454. SetSorting(query, sort, false);
  455. var result = _libraryManager.GetItemsResult(query);
  456. return ToResult(result);
  457. }
  458. private QueryResult<ServerItem> GetMusicFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
  459. {
  460. var query = new InternalItemsQuery(user)
  461. {
  462. StartIndex = startIndex,
  463. Limit = limit
  464. };
  465. SetSorting(query, sort, false);
  466. if (stubType.HasValue && stubType.Value == StubType.Latest)
  467. {
  468. return GetMusicLatest(item, user, query);
  469. }
  470. if (stubType.HasValue && stubType.Value == StubType.Playlists)
  471. {
  472. return GetMusicPlaylists(item, user, query);
  473. }
  474. if (stubType.HasValue && stubType.Value == StubType.Albums)
  475. {
  476. return GetMusicAlbums(item, user, query);
  477. }
  478. if (stubType.HasValue && stubType.Value == StubType.Artists)
  479. {
  480. return GetMusicArtists(item, user, query);
  481. }
  482. if (stubType.HasValue && stubType.Value == StubType.AlbumArtists)
  483. {
  484. return GetMusicAlbumArtists(item, user, query);
  485. }
  486. if (stubType.HasValue && stubType.Value == StubType.FavoriteAlbums)
  487. {
  488. return GetFavoriteAlbums(item, user, query);
  489. }
  490. if (stubType.HasValue && stubType.Value == StubType.FavoriteArtists)
  491. {
  492. return GetFavoriteArtists(item, user, query);
  493. }
  494. if (stubType.HasValue && stubType.Value == StubType.FavoriteSongs)
  495. {
  496. return GetFavoriteSongs(item, user, query);
  497. }
  498. if (stubType.HasValue && stubType.Value == StubType.Songs)
  499. {
  500. return GetMusicSongs(item, user, query);
  501. }
  502. if (stubType.HasValue && stubType.Value == StubType.Genres)
  503. {
  504. return GetMusicGenres(item, user, query);
  505. }
  506. var list = new List<ServerItem>();
  507. list.Add(new ServerItem(item)
  508. {
  509. StubType = StubType.Latest
  510. });
  511. list.Add(new ServerItem(item)
  512. {
  513. StubType = StubType.Playlists
  514. });
  515. list.Add(new ServerItem(item)
  516. {
  517. StubType = StubType.Albums
  518. });
  519. list.Add(new ServerItem(item)
  520. {
  521. StubType = StubType.AlbumArtists
  522. });
  523. list.Add(new ServerItem(item)
  524. {
  525. StubType = StubType.Artists
  526. });
  527. list.Add(new ServerItem(item)
  528. {
  529. StubType = StubType.Songs
  530. });
  531. list.Add(new ServerItem(item)
  532. {
  533. StubType = StubType.Genres
  534. });
  535. list.Add(new ServerItem(item)
  536. {
  537. StubType = StubType.FavoriteArtists
  538. });
  539. list.Add(new ServerItem(item)
  540. {
  541. StubType = StubType.FavoriteAlbums
  542. });
  543. list.Add(new ServerItem(item)
  544. {
  545. StubType = StubType.FavoriteSongs
  546. });
  547. return new QueryResult<ServerItem>
  548. {
  549. Items = list,
  550. TotalRecordCount = list.Count
  551. };
  552. }
  553. private QueryResult<ServerItem> GetMovieFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
  554. {
  555. var query = new InternalItemsQuery(user)
  556. {
  557. StartIndex = startIndex,
  558. Limit = limit
  559. };
  560. SetSorting(query, sort, false);
  561. if (stubType.HasValue && stubType.Value == StubType.ContinueWatching)
  562. {
  563. return GetMovieContinueWatching(item, user, query);
  564. }
  565. if (stubType.HasValue && stubType.Value == StubType.Latest)
  566. {
  567. return GetMovieLatest(item, user, query);
  568. }
  569. if (stubType.HasValue && stubType.Value == StubType.Movies)
  570. {
  571. return GetMovieMovies(item, user, query);
  572. }
  573. if (stubType.HasValue && stubType.Value == StubType.Collections)
  574. {
  575. return GetMovieCollections(item, user, query);
  576. }
  577. if (stubType.HasValue && stubType.Value == StubType.Favorites)
  578. {
  579. return GetMovieFavorites(item, user, query);
  580. }
  581. if (stubType.HasValue && stubType.Value == StubType.Genres)
  582. {
  583. return GetGenres(item, user, query);
  584. }
  585. var list = new List<ServerItem>();
  586. list.Add(new ServerItem(item)
  587. {
  588. StubType = StubType.ContinueWatching
  589. });
  590. list.Add(new ServerItem(item)
  591. {
  592. StubType = StubType.Latest
  593. });
  594. list.Add(new ServerItem(item)
  595. {
  596. StubType = StubType.Movies
  597. });
  598. list.Add(new ServerItem(item)
  599. {
  600. StubType = StubType.Collections
  601. });
  602. list.Add(new ServerItem(item)
  603. {
  604. StubType = StubType.Favorites
  605. });
  606. list.Add(new ServerItem(item)
  607. {
  608. StubType = StubType.Genres
  609. });
  610. return new QueryResult<ServerItem>
  611. {
  612. Items = list,
  613. TotalRecordCount = list.Count
  614. };
  615. }
  616. private QueryResult<ServerItem> GetFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
  617. {
  618. var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
  619. .OrderBy(i => i.SortName)
  620. .Select(i => new ServerItem(i)
  621. {
  622. StubType = StubType.Folder
  623. })
  624. .ToArray();
  625. return ApplyPaging(new QueryResult<ServerItem>
  626. {
  627. Items = folders,
  628. TotalRecordCount = folders.Length
  629. }, startIndex, limit);
  630. }
  631. private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
  632. {
  633. var query = new InternalItemsQuery(user)
  634. {
  635. StartIndex = startIndex,
  636. Limit = limit
  637. };
  638. SetSorting(query, sort, false);
  639. if (stubType.HasValue && stubType.Value == StubType.ContinueWatching)
  640. {
  641. return GetMovieContinueWatching(item, user, query);
  642. }
  643. if (stubType.HasValue && stubType.Value == StubType.NextUp)
  644. {
  645. return GetNextUp(item, user, query);
  646. }
  647. if (stubType.HasValue && stubType.Value == StubType.Latest)
  648. {
  649. return GetTvLatest(item, user, query);
  650. }
  651. if (stubType.HasValue && stubType.Value == StubType.Series)
  652. {
  653. return GetSeries(item, user, query);
  654. }
  655. if (stubType.HasValue && stubType.Value == StubType.FavoriteSeries)
  656. {
  657. return GetFavoriteSeries(item, user, query);
  658. }
  659. if (stubType.HasValue && stubType.Value == StubType.FavoriteEpisodes)
  660. {
  661. return GetFavoriteEpisodes(item, user, query);
  662. }
  663. if (stubType.HasValue && stubType.Value == StubType.Genres)
  664. {
  665. return GetGenres(item, user, query);
  666. }
  667. var list = new List<ServerItem>();
  668. list.Add(new ServerItem(item)
  669. {
  670. StubType = StubType.ContinueWatching
  671. });
  672. list.Add(new ServerItem(item)
  673. {
  674. StubType = StubType.NextUp
  675. });
  676. list.Add(new ServerItem(item)
  677. {
  678. StubType = StubType.Latest
  679. });
  680. list.Add(new ServerItem(item)
  681. {
  682. StubType = StubType.Series
  683. });
  684. list.Add(new ServerItem(item)
  685. {
  686. StubType = StubType.FavoriteSeries
  687. });
  688. list.Add(new ServerItem(item)
  689. {
  690. StubType = StubType.FavoriteEpisodes
  691. });
  692. list.Add(new ServerItem(item)
  693. {
  694. StubType = StubType.Genres
  695. });
  696. return new QueryResult<ServerItem>
  697. {
  698. Items = list,
  699. TotalRecordCount = list.Count
  700. };
  701. }
  702. private QueryResult<ServerItem> GetMovieContinueWatching(BaseItem parent, User user, InternalItemsQuery query)
  703. {
  704. query.Recursive = true;
  705. query.Parent = parent;
  706. query.SetUser(user);
  707. query.OrderBy = new[]
  708. {
  709. (ItemSortBy.DatePlayed, SortOrder.Descending),
  710. (ItemSortBy.SortName, SortOrder.Ascending)
  711. };
  712. query.IsResumable = true;
  713. query.Limit = 10;
  714. var result = _libraryManager.GetItemsResult(query);
  715. return ToResult(result);
  716. }
  717. private QueryResult<ServerItem> GetSeries(BaseItem parent, User user, InternalItemsQuery query)
  718. {
  719. query.Recursive = true;
  720. query.Parent = parent;
  721. query.SetUser(user);
  722. query.IncludeItemTypes = new[] { typeof(Series).Name };
  723. var result = _libraryManager.GetItemsResult(query);
  724. return ToResult(result);
  725. }
  726. private QueryResult<ServerItem> GetMovieMovies(BaseItem parent, User user, InternalItemsQuery query)
  727. {
  728. query.Recursive = true;
  729. query.Parent = parent;
  730. query.SetUser(user);
  731. query.IncludeItemTypes = new[] { typeof(Movie).Name };
  732. var result = _libraryManager.GetItemsResult(query);
  733. return ToResult(result);
  734. }
  735. private QueryResult<ServerItem> GetMovieCollections(BaseItem parent, User user, InternalItemsQuery query)
  736. {
  737. query.Recursive = true;
  738. //query.Parent = parent;
  739. query.SetUser(user);
  740. query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
  741. var result = _libraryManager.GetItemsResult(query);
  742. return ToResult(result);
  743. }
  744. private QueryResult<ServerItem> GetMusicAlbums(BaseItem parent, User user, InternalItemsQuery query)
  745. {
  746. query.Recursive = true;
  747. query.Parent = parent;
  748. query.SetUser(user);
  749. query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name };
  750. var result = _libraryManager.GetItemsResult(query);
  751. return ToResult(result);
  752. }
  753. private QueryResult<ServerItem> GetMusicSongs(BaseItem parent, User user, InternalItemsQuery query)
  754. {
  755. query.Recursive = true;
  756. query.Parent = parent;
  757. query.SetUser(user);
  758. query.IncludeItemTypes = new[] { typeof(Audio).Name };
  759. var result = _libraryManager.GetItemsResult(query);
  760. return ToResult(result);
  761. }
  762. private QueryResult<ServerItem> GetFavoriteSongs(BaseItem parent, User user, InternalItemsQuery query)
  763. {
  764. query.Recursive = true;
  765. query.Parent = parent;
  766. query.SetUser(user);
  767. query.IsFavorite = true;
  768. query.IncludeItemTypes = new[] { typeof(Audio).Name };
  769. var result = _libraryManager.GetItemsResult(query);
  770. return ToResult(result);
  771. }
  772. private QueryResult<ServerItem> GetFavoriteSeries(BaseItem parent, User user, InternalItemsQuery query)
  773. {
  774. query.Recursive = true;
  775. query.Parent = parent;
  776. query.SetUser(user);
  777. query.IsFavorite = true;
  778. query.IncludeItemTypes = new[] { typeof(Series).Name };
  779. var result = _libraryManager.GetItemsResult(query);
  780. return ToResult(result);
  781. }
  782. private QueryResult<ServerItem> GetFavoriteEpisodes(BaseItem parent, User user, InternalItemsQuery query)
  783. {
  784. query.Recursive = true;
  785. query.Parent = parent;
  786. query.SetUser(user);
  787. query.IsFavorite = true;
  788. query.IncludeItemTypes = new[] { typeof(Episode).Name };
  789. var result = _libraryManager.GetItemsResult(query);
  790. return ToResult(result);
  791. }
  792. private QueryResult<ServerItem> GetMovieFavorites(BaseItem parent, User user, InternalItemsQuery query)
  793. {
  794. query.Recursive = true;
  795. query.Parent = parent;
  796. query.SetUser(user);
  797. query.IsFavorite = true;
  798. query.IncludeItemTypes = new[] { typeof(Movie).Name };
  799. var result = _libraryManager.GetItemsResult(query);
  800. return ToResult(result);
  801. }
  802. private QueryResult<ServerItem> GetFavoriteAlbums(BaseItem parent, User user, InternalItemsQuery query)
  803. {
  804. query.Recursive = true;
  805. query.Parent = parent;
  806. query.SetUser(user);
  807. query.IsFavorite = true;
  808. query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name };
  809. var result = _libraryManager.GetItemsResult(query);
  810. return ToResult(result);
  811. }
  812. private QueryResult<ServerItem> GetGenres(BaseItem parent, User user, InternalItemsQuery query)
  813. {
  814. var genresResult = _libraryManager.GetGenres(new InternalItemsQuery(user)
  815. {
  816. AncestorIds = new[] { parent.Id },
  817. StartIndex = query.StartIndex,
  818. Limit = query.Limit
  819. });
  820. var result = new QueryResult<BaseItem>
  821. {
  822. TotalRecordCount = genresResult.TotalRecordCount,
  823. Items = genresResult.Items.Select(i => i.Item1).ToArray()
  824. };
  825. return ToResult(result);
  826. }
  827. private QueryResult<ServerItem> GetMusicGenres(BaseItem parent, User user, InternalItemsQuery query)
  828. {
  829. var genresResult = _libraryManager.GetMusicGenres(new InternalItemsQuery(user)
  830. {
  831. AncestorIds = new[] { parent.Id },
  832. StartIndex = query.StartIndex,
  833. Limit = query.Limit
  834. });
  835. var result = new QueryResult<BaseItem>
  836. {
  837. TotalRecordCount = genresResult.TotalRecordCount,
  838. Items = genresResult.Items.Select(i => i.Item1).ToArray()
  839. };
  840. return ToResult(result);
  841. }
  842. private QueryResult<ServerItem> GetMusicAlbumArtists(BaseItem parent, User user, InternalItemsQuery query)
  843. {
  844. var artists = _libraryManager.GetAlbumArtists(new InternalItemsQuery(user)
  845. {
  846. AncestorIds = new[] { parent.Id },
  847. StartIndex = query.StartIndex,
  848. Limit = query.Limit
  849. });
  850. var result = new QueryResult<BaseItem>
  851. {
  852. TotalRecordCount = artists.TotalRecordCount,
  853. Items = artists.Items.Select(i => i.Item1).ToArray()
  854. };
  855. return ToResult(result);
  856. }
  857. private QueryResult<ServerItem> GetMusicArtists(BaseItem parent, User user, InternalItemsQuery query)
  858. {
  859. var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
  860. {
  861. AncestorIds = new[] { parent.Id },
  862. StartIndex = query.StartIndex,
  863. Limit = query.Limit
  864. });
  865. var result = new QueryResult<BaseItem>
  866. {
  867. TotalRecordCount = artists.TotalRecordCount,
  868. Items = artists.Items.Select(i => i.Item1).ToArray()
  869. };
  870. return ToResult(result);
  871. }
  872. private QueryResult<ServerItem> GetFavoriteArtists(BaseItem parent, User user, InternalItemsQuery query)
  873. {
  874. var artists = _libraryManager.GetArtists(new InternalItemsQuery(user)
  875. {
  876. AncestorIds = new[] { parent.Id },
  877. StartIndex = query.StartIndex,
  878. Limit = query.Limit,
  879. IsFavorite = true
  880. });
  881. var result = new QueryResult<BaseItem>
  882. {
  883. TotalRecordCount = artists.TotalRecordCount,
  884. Items = artists.Items.Select(i => i.Item1).ToArray()
  885. };
  886. return ToResult(result);
  887. }
  888. private QueryResult<ServerItem> GetMusicPlaylists(BaseItem parent, User user, InternalItemsQuery query)
  889. {
  890. query.Parent = null;
  891. query.IncludeItemTypes = new[] { typeof(Playlist).Name };
  892. query.SetUser(user);
  893. query.Recursive = true;
  894. var result = _libraryManager.GetItemsResult(query);
  895. return ToResult(result);
  896. }
  897. private QueryResult<ServerItem> GetMusicLatest(BaseItem parent, User user, InternalItemsQuery query)
  898. {
  899. query.OrderBy = Array.Empty<(string, SortOrder)>();
  900. var items = _userViewManager.GetLatestItems(new LatestItemsQuery
  901. {
  902. UserId = user.Id,
  903. Limit = 50,
  904. IncludeItemTypes = new[] { typeof(Audio).Name },
  905. ParentId = parent == null ? Guid.Empty : parent.Id,
  906. GroupItems = true
  907. }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
  908. return ToResult(items);
  909. }
  910. private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
  911. {
  912. query.OrderBy = Array.Empty<(string, SortOrder)>();
  913. var result = _tvSeriesManager.GetNextUp(new NextUpQuery
  914. {
  915. Limit = query.Limit,
  916. StartIndex = query.StartIndex,
  917. UserId = query.User.Id
  918. }, new[] { parent }, query.DtoOptions);
  919. return ToResult(result);
  920. }
  921. private QueryResult<ServerItem> GetTvLatest(BaseItem parent, User user, InternalItemsQuery query)
  922. {
  923. query.OrderBy = Array.Empty<(string, SortOrder)>();
  924. var items = _userViewManager.GetLatestItems(new LatestItemsQuery
  925. {
  926. UserId = user.Id,
  927. Limit = 50,
  928. IncludeItemTypes = new[] { typeof(Episode).Name },
  929. ParentId = parent == null ? Guid.Empty : parent.Id,
  930. GroupItems = false
  931. }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
  932. return ToResult(items);
  933. }
  934. private QueryResult<ServerItem> GetMovieLatest(BaseItem parent, User user, InternalItemsQuery query)
  935. {
  936. query.OrderBy = Array.Empty<(string, SortOrder)>();
  937. var items = _userViewManager.GetLatestItems(new LatestItemsQuery
  938. {
  939. UserId = user.Id,
  940. Limit = 50,
  941. IncludeItemTypes = new[] { typeof(Movie).Name },
  942. ParentId = parent == null ? Guid.Empty : parent.Id,
  943. GroupItems = true
  944. }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray();
  945. return ToResult(items);
  946. }
  947. private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
  948. {
  949. var query = new InternalItemsQuery(user)
  950. {
  951. Recursive = true,
  952. ParentId = parentId,
  953. ArtistIds = new[] { item.Id },
  954. IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
  955. Limit = limit,
  956. StartIndex = startIndex,
  957. DtoOptions = GetDtoOptions()
  958. };
  959. SetSorting(query, sort, false);
  960. var result = _libraryManager.GetItemsResult(query);
  961. return ToResult(result);
  962. }
  963. private QueryResult<ServerItem> GetGenreItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
  964. {
  965. var query = new InternalItemsQuery(user)
  966. {
  967. Recursive = true,
  968. ParentId = parentId,
  969. GenreIds = new[] { item.Id },
  970. IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name },
  971. Limit = limit,
  972. StartIndex = startIndex,
  973. DtoOptions = GetDtoOptions()
  974. };
  975. SetSorting(query, sort, false);
  976. var result = _libraryManager.GetItemsResult(query);
  977. return ToResult(result);
  978. }
  979. private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid parentId, User user, SortCriteria sort, int? startIndex, int? limit)
  980. {
  981. var query = new InternalItemsQuery(user)
  982. {
  983. Recursive = true,
  984. ParentId = parentId,
  985. GenreIds = new[] { item.Id },
  986. IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
  987. Limit = limit,
  988. StartIndex = startIndex,
  989. DtoOptions = GetDtoOptions()
  990. };
  991. SetSorting(query, sort, false);
  992. var result = _libraryManager.GetItemsResult(query);
  993. return ToResult(result);
  994. }
  995. private QueryResult<ServerItem> ToResult(BaseItem[] result)
  996. {
  997. var serverItems = result
  998. .Select(i => new ServerItem(i))
  999. .ToArray();
  1000. return new QueryResult<ServerItem>
  1001. {
  1002. TotalRecordCount = result.Length,
  1003. Items = serverItems
  1004. };
  1005. }
  1006. private QueryResult<ServerItem> ToResult(QueryResult<BaseItem> result)
  1007. {
  1008. var serverItems = result
  1009. .Items
  1010. .Select(i => new ServerItem(i))
  1011. .ToArray();
  1012. return new QueryResult<ServerItem>
  1013. {
  1014. TotalRecordCount = result.TotalRecordCount,
  1015. Items = serverItems
  1016. };
  1017. }
  1018. private void SetSorting(InternalItemsQuery query, SortCriteria sort, bool isPreSorted)
  1019. {
  1020. if (isPreSorted)
  1021. {
  1022. query.OrderBy = Array.Empty<(string, SortOrder)>();
  1023. }
  1024. else
  1025. {
  1026. query.OrderBy = new[] { (ItemSortBy.SortName, sort.SortOrder) };
  1027. }
  1028. }
  1029. private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit)
  1030. {
  1031. result.Items = result.Items.Skip(startIndex ?? 0).Take(limit ?? int.MaxValue).ToArray();
  1032. return result;
  1033. }
  1034. private ServerItem GetItemFromObjectId(string id, User user)
  1035. {
  1036. return DidlBuilder.IsIdRoot(id)
  1037. ? new ServerItem(_libraryManager.GetUserRootFolder())
  1038. : ParseItemId(id, user);
  1039. }
  1040. private ServerItem ParseItemId(string id, User user)
  1041. {
  1042. StubType? stubType = null;
  1043. // After using PlayTo, MediaMonkey sends a request to the server trying to get item info
  1044. const string paramsSrch = "Params=";
  1045. var paramsIndex = id.IndexOf(paramsSrch, StringComparison.OrdinalIgnoreCase);
  1046. if (paramsIndex != -1)
  1047. {
  1048. id = id.Substring(paramsIndex + paramsSrch.Length);
  1049. var parts = id.Split(';');
  1050. id = parts[23];
  1051. }
  1052. var enumNames = Enum.GetNames(typeof(StubType));
  1053. foreach (var name in enumNames)
  1054. {
  1055. if (id.StartsWith(name + "_", StringComparison.OrdinalIgnoreCase))
  1056. {
  1057. stubType = (StubType)Enum.Parse(typeof(StubType), name, true);
  1058. id = id.Split(new[] { '_' }, 2)[1];
  1059. break;
  1060. }
  1061. }
  1062. if (Guid.TryParse(id, out var itemId))
  1063. {
  1064. var item = _libraryManager.GetItemById(itemId);
  1065. return new ServerItem(item)
  1066. {
  1067. StubType = stubType
  1068. };
  1069. }
  1070. _logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id);
  1071. return new ServerItem(_libraryManager.GetUserRootFolder());
  1072. }
  1073. }
  1074. internal class ServerItem
  1075. {
  1076. public BaseItem Item { get; set; }
  1077. public StubType? StubType { get; set; }
  1078. public ServerItem(BaseItem item)
  1079. {
  1080. Item = item;
  1081. if (item is IItemByName && !(item is Folder))
  1082. {
  1083. StubType = Dlna.ContentDirectory.StubType.Folder;
  1084. }
  1085. }
  1086. }
  1087. public enum StubType
  1088. {
  1089. Folder = 0,
  1090. Latest = 2,
  1091. Playlists = 3,
  1092. Albums = 4,
  1093. AlbumArtists = 5,
  1094. Artists = 6,
  1095. Songs = 7,
  1096. Genres = 8,
  1097. FavoriteSongs = 9,
  1098. FavoriteArtists = 10,
  1099. FavoriteAlbums = 11,
  1100. ContinueWatching = 12,
  1101. Movies = 13,
  1102. Collections = 14,
  1103. Favorites = 15,
  1104. NextUp = 16,
  1105. Series = 17,
  1106. FavoriteSeries = 18,
  1107. FavoriteEpisodes = 19
  1108. }
  1109. }