ControlHandler.cs 47 KB

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