UserViewBuilder.cs 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. #nullable disable
  2. #pragma warning disable CS1591
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.Linq;
  7. using Jellyfin.Data.Entities;
  8. using Jellyfin.Data.Enums;
  9. using Jellyfin.Extensions;
  10. using MediaBrowser.Controller.Library;
  11. using MediaBrowser.Controller.TV;
  12. using MediaBrowser.Model.Entities;
  13. using MediaBrowser.Model.Querying;
  14. using Microsoft.Extensions.Logging;
  15. using Episode = MediaBrowser.Controller.Entities.TV.Episode;
  16. using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
  17. using Series = MediaBrowser.Controller.Entities.TV.Series;
  18. namespace MediaBrowser.Controller.Entities
  19. {
  20. public class UserViewBuilder
  21. {
  22. private readonly IUserViewManager _userViewManager;
  23. private readonly ILibraryManager _libraryManager;
  24. private readonly ILogger<BaseItem> _logger;
  25. private readonly IUserDataManager _userDataManager;
  26. private readonly ITVSeriesManager _tvSeriesManager;
  27. public UserViewBuilder(
  28. IUserViewManager userViewManager,
  29. ILibraryManager libraryManager,
  30. ILogger<BaseItem> logger,
  31. IUserDataManager userDataManager,
  32. ITVSeriesManager tvSeriesManager)
  33. {
  34. _userViewManager = userViewManager;
  35. _libraryManager = libraryManager;
  36. _logger = logger;
  37. _userDataManager = userDataManager;
  38. _tvSeriesManager = tvSeriesManager;
  39. }
  40. public QueryResult<BaseItem> GetUserItems(Folder queryParent, Folder displayParent, string viewType, InternalItemsQuery query)
  41. {
  42. var user = query.User;
  43. // if (query.IncludeItemTypes != null &&
  44. // query.IncludeItemTypes.Length == 1 &&
  45. // string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase))
  46. // {
  47. // if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
  48. // {
  49. // return await FindPlaylists(queryParent, user, query).ConfigureAwait(false);
  50. // }
  51. // }
  52. switch (viewType)
  53. {
  54. case CollectionType.Folders:
  55. return GetResult(_libraryManager.GetUserRootFolder().GetChildren(user, true), query);
  56. case CollectionType.TvShows:
  57. return GetTvView(queryParent, user, query);
  58. case CollectionType.Movies:
  59. return GetMovieFolders(queryParent, user, query);
  60. case SpecialFolder.TvShowSeries:
  61. return GetTvSeries(queryParent, user, query);
  62. case SpecialFolder.TvGenres:
  63. return GetTvGenres(queryParent, user, query);
  64. case SpecialFolder.TvGenre:
  65. return GetTvGenreItems(queryParent, displayParent, user, query);
  66. case SpecialFolder.TvResume:
  67. return GetTvResume(queryParent, user, query);
  68. case SpecialFolder.TvNextUp:
  69. return GetTvNextUp(queryParent, query);
  70. case SpecialFolder.TvLatest:
  71. return GetTvLatest(queryParent, user, query);
  72. case SpecialFolder.MovieFavorites:
  73. return GetFavoriteMovies(queryParent, user, query);
  74. case SpecialFolder.MovieLatest:
  75. return GetMovieLatest(queryParent, user, query);
  76. case SpecialFolder.MovieGenres:
  77. return GetMovieGenres(queryParent, user, query);
  78. case SpecialFolder.MovieGenre:
  79. return GetMovieGenreItems(queryParent, displayParent, user, query);
  80. case SpecialFolder.MovieResume:
  81. return GetMovieResume(queryParent, user, query);
  82. case SpecialFolder.MovieMovies:
  83. return GetMovieMovies(queryParent, user, query);
  84. case SpecialFolder.MovieCollections:
  85. return GetMovieCollections(user, query);
  86. case SpecialFolder.TvFavoriteEpisodes:
  87. return GetFavoriteEpisodes(queryParent, user, query);
  88. case SpecialFolder.TvFavoriteSeries:
  89. return GetFavoriteSeries(queryParent, user, query);
  90. default:
  91. {
  92. if (queryParent is UserView)
  93. {
  94. return GetResult(GetMediaFolders(user).OfType<Folder>().SelectMany(i => i.GetChildren(user, true)), query);
  95. }
  96. return queryParent.GetItems(query);
  97. }
  98. }
  99. }
  100. private int GetSpecialItemsLimit()
  101. {
  102. return 50;
  103. }
  104. private QueryResult<BaseItem> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
  105. {
  106. if (query.Recursive)
  107. {
  108. query.Recursive = true;
  109. query.SetUser(user);
  110. if (query.IncludeItemTypes.Length == 0)
  111. {
  112. query.IncludeItemTypes = new[] { BaseItemKind.Movie };
  113. }
  114. return parent.QueryRecursive(query);
  115. }
  116. var list = new List<BaseItem>
  117. {
  118. GetUserView(SpecialFolder.MovieResume, "HeaderContinueWatching", "0", parent),
  119. GetUserView(SpecialFolder.MovieLatest, "Latest", "1", parent),
  120. GetUserView(SpecialFolder.MovieMovies, "Movies", "2", parent),
  121. GetUserView(SpecialFolder.MovieCollections, "Collections", "3", parent),
  122. GetUserView(SpecialFolder.MovieFavorites, "Favorites", "4", parent),
  123. GetUserView(SpecialFolder.MovieGenres, "Genres", "5", parent)
  124. };
  125. return GetResult(list, query);
  126. }
  127. private QueryResult<BaseItem> GetFavoriteMovies(Folder parent, User user, InternalItemsQuery query)
  128. {
  129. query.Recursive = true;
  130. query.Parent = parent;
  131. query.SetUser(user);
  132. query.IsFavorite = true;
  133. query.IncludeItemTypes = new[] { BaseItemKind.Movie };
  134. return _libraryManager.GetItemsResult(query);
  135. }
  136. private QueryResult<BaseItem> GetFavoriteSeries(Folder parent, User user, InternalItemsQuery query)
  137. {
  138. query.Recursive = true;
  139. query.Parent = parent;
  140. query.SetUser(user);
  141. query.IsFavorite = true;
  142. query.IncludeItemTypes = new[] { BaseItemKind.Series };
  143. return _libraryManager.GetItemsResult(query);
  144. }
  145. private QueryResult<BaseItem> GetFavoriteEpisodes(Folder parent, User user, InternalItemsQuery query)
  146. {
  147. query.Recursive = true;
  148. query.Parent = parent;
  149. query.SetUser(user);
  150. query.IsFavorite = true;
  151. query.IncludeItemTypes = new[] { BaseItemKind.Episode };
  152. return _libraryManager.GetItemsResult(query);
  153. }
  154. private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
  155. {
  156. query.Recursive = true;
  157. query.Parent = parent;
  158. query.SetUser(user);
  159. query.IncludeItemTypes = new[] { BaseItemKind.Movie };
  160. return _libraryManager.GetItemsResult(query);
  161. }
  162. private QueryResult<BaseItem> GetMovieCollections(User user, InternalItemsQuery query)
  163. {
  164. query.Parent = null;
  165. query.IncludeItemTypes = new[] { BaseItemKind.BoxSet };
  166. query.SetUser(user);
  167. query.Recursive = true;
  168. return _libraryManager.GetItemsResult(query);
  169. }
  170. private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
  171. {
  172. query.OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) };
  173. query.Recursive = true;
  174. query.Parent = parent;
  175. query.SetUser(user);
  176. query.Limit = GetSpecialItemsLimit();
  177. query.IncludeItemTypes = new[] { BaseItemKind.Movie };
  178. return ConvertToResult(_libraryManager.GetItemList(query));
  179. }
  180. private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
  181. {
  182. query.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) };
  183. query.IsResumable = true;
  184. query.Recursive = true;
  185. query.Parent = parent;
  186. query.SetUser(user);
  187. query.Limit = GetSpecialItemsLimit();
  188. query.IncludeItemTypes = new[] { BaseItemKind.Movie };
  189. return ConvertToResult(_libraryManager.GetItemList(query));
  190. }
  191. private QueryResult<BaseItem> ConvertToResult(List<BaseItem> items)
  192. {
  193. var arr = items.ToArray();
  194. return new QueryResult<BaseItem>
  195. {
  196. Items = arr,
  197. TotalRecordCount = arr.Length
  198. };
  199. }
  200. private QueryResult<BaseItem> GetMovieGenres(Folder parent, User user, InternalItemsQuery query)
  201. {
  202. var genres = parent.QueryRecursive(new InternalItemsQuery(user)
  203. {
  204. IncludeItemTypes = new[] { BaseItemKind.Movie },
  205. Recursive = true,
  206. EnableTotalRecordCount = false
  207. }).Items
  208. .SelectMany(i => i.Genres)
  209. .DistinctNames()
  210. .Select(i =>
  211. {
  212. try
  213. {
  214. return _libraryManager.GetGenre(i);
  215. }
  216. catch (Exception ex)
  217. {
  218. _logger.LogError(ex, "Error getting genre");
  219. return null;
  220. }
  221. })
  222. .Where(i => i != null)
  223. .Select(i => GetUserViewWithName(SpecialFolder.MovieGenre, i.SortName, parent));
  224. return GetResult(genres, query);
  225. }
  226. private QueryResult<BaseItem> GetMovieGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
  227. {
  228. query.Recursive = true;
  229. query.Parent = queryParent;
  230. query.GenreIds = new[] { displayParent.Id };
  231. query.SetUser(user);
  232. query.IncludeItemTypes = new[] { BaseItemKind.Movie };
  233. return _libraryManager.GetItemsResult(query);
  234. }
  235. private QueryResult<BaseItem> GetTvView(Folder parent, User user, InternalItemsQuery query)
  236. {
  237. if (query.Recursive)
  238. {
  239. query.Recursive = true;
  240. query.SetUser(user);
  241. if (query.IncludeItemTypes.Length == 0)
  242. {
  243. query.IncludeItemTypes = new[]
  244. {
  245. BaseItemKind.Series,
  246. BaseItemKind.Season,
  247. BaseItemKind.Episode
  248. };
  249. }
  250. return parent.QueryRecursive(query);
  251. }
  252. var list = new List<BaseItem>
  253. {
  254. GetUserView(SpecialFolder.TvResume, "HeaderContinueWatching", "0", parent),
  255. GetUserView(SpecialFolder.TvNextUp, "HeaderNextUp", "1", parent),
  256. GetUserView(SpecialFolder.TvLatest, "Latest", "2", parent),
  257. GetUserView(SpecialFolder.TvShowSeries, "Shows", "3", parent),
  258. GetUserView(SpecialFolder.TvFavoriteSeries, "HeaderFavoriteShows", "4", parent),
  259. GetUserView(SpecialFolder.TvFavoriteEpisodes, "HeaderFavoriteEpisodes", "5", parent),
  260. GetUserView(SpecialFolder.TvGenres, "Genres", "6", parent)
  261. };
  262. return GetResult(list, query);
  263. }
  264. private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
  265. {
  266. query.OrderBy = new[] { (ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) };
  267. query.Recursive = true;
  268. query.Parent = parent;
  269. query.SetUser(user);
  270. query.Limit = GetSpecialItemsLimit();
  271. query.IncludeItemTypes = new[] { BaseItemKind.Episode };
  272. query.IsVirtualItem = false;
  273. return ConvertToResult(_libraryManager.GetItemList(query));
  274. }
  275. private QueryResult<BaseItem> GetTvNextUp(Folder parent, InternalItemsQuery query)
  276. {
  277. var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows, string.Empty });
  278. var result = _tvSeriesManager.GetNextUp(
  279. new NextUpQuery
  280. {
  281. Limit = query.Limit,
  282. StartIndex = query.StartIndex,
  283. UserId = query.User.Id
  284. },
  285. parentFolders,
  286. query.DtoOptions);
  287. return result;
  288. }
  289. private QueryResult<BaseItem> GetTvResume(Folder parent, User user, InternalItemsQuery query)
  290. {
  291. query.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending) };
  292. query.IsResumable = true;
  293. query.Recursive = true;
  294. query.Parent = parent;
  295. query.SetUser(user);
  296. query.Limit = GetSpecialItemsLimit();
  297. query.IncludeItemTypes = new[] { BaseItemKind.Episode };
  298. return ConvertToResult(_libraryManager.GetItemList(query));
  299. }
  300. private QueryResult<BaseItem> GetTvSeries(Folder parent, User user, InternalItemsQuery query)
  301. {
  302. query.Recursive = true;
  303. query.Parent = parent;
  304. query.SetUser(user);
  305. query.IncludeItemTypes = new[] { BaseItemKind.Series };
  306. return _libraryManager.GetItemsResult(query);
  307. }
  308. private QueryResult<BaseItem> GetTvGenres(Folder parent, User user, InternalItemsQuery query)
  309. {
  310. var genres = parent.QueryRecursive(new InternalItemsQuery(user)
  311. {
  312. IncludeItemTypes = new[] { BaseItemKind.Series },
  313. Recursive = true,
  314. EnableTotalRecordCount = false
  315. }).Items
  316. .SelectMany(i => i.Genres)
  317. .DistinctNames()
  318. .Select(i =>
  319. {
  320. try
  321. {
  322. return _libraryManager.GetGenre(i);
  323. }
  324. catch (Exception ex)
  325. {
  326. _logger.LogError(ex, "Error getting genre");
  327. return null;
  328. }
  329. })
  330. .Where(i => i != null)
  331. .Select(i => GetUserViewWithName(SpecialFolder.TvGenre, i.SortName, parent));
  332. return GetResult(genres, query);
  333. }
  334. private QueryResult<BaseItem> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
  335. {
  336. query.Recursive = true;
  337. query.Parent = queryParent;
  338. query.GenreIds = new[] { displayParent.Id };
  339. query.SetUser(user);
  340. query.IncludeItemTypes = new[] { BaseItemKind.Series };
  341. return _libraryManager.GetItemsResult(query);
  342. }
  343. private QueryResult<BaseItem> GetResult<T>(QueryResult<T> result)
  344. where T : BaseItem
  345. {
  346. return new QueryResult<BaseItem>
  347. {
  348. Items = result.Items, // TODO Fix The co-variant conversion between T[] and BaseItem[], this can generate runtime issues if T is not BaseItem.
  349. TotalRecordCount = result.TotalRecordCount
  350. };
  351. }
  352. private QueryResult<BaseItem> GetResult<T>(
  353. IEnumerable<T> items,
  354. InternalItemsQuery query)
  355. where T : BaseItem
  356. {
  357. items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager));
  358. return PostFilterAndSort(items, null, query, _libraryManager);
  359. }
  360. public static bool FilterItem(BaseItem item, InternalItemsQuery query)
  361. {
  362. return Filter(item, query.User, query, BaseItem.UserDataManager, BaseItem.LibraryManager);
  363. }
  364. public static QueryResult<BaseItem> PostFilterAndSort(
  365. IEnumerable<BaseItem> items,
  366. int? totalRecordLimit,
  367. InternalItemsQuery query,
  368. ILibraryManager libraryManager)
  369. {
  370. var user = query.User;
  371. // This must be the last filter
  372. if (!string.IsNullOrEmpty(query.AdjacentTo))
  373. {
  374. items = FilterForAdjacency(items.ToList(), query.AdjacentTo);
  375. }
  376. return SortAndPage(items, totalRecordLimit, query, libraryManager, true);
  377. }
  378. public static QueryResult<BaseItem> SortAndPage(
  379. IEnumerable<BaseItem> items,
  380. int? totalRecordLimit,
  381. InternalItemsQuery query,
  382. ILibraryManager libraryManager,
  383. bool enableSorting)
  384. {
  385. if (enableSorting)
  386. {
  387. if (query.OrderBy.Count > 0)
  388. {
  389. items = libraryManager.Sort(items, query.User, query.OrderBy);
  390. }
  391. }
  392. var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();
  393. var totalCount = itemsArray.Length;
  394. if (query.Limit.HasValue)
  395. {
  396. itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray();
  397. }
  398. else if (query.StartIndex.HasValue)
  399. {
  400. itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray();
  401. }
  402. return new QueryResult<BaseItem>
  403. {
  404. TotalRecordCount = totalCount,
  405. Items = itemsArray
  406. };
  407. }
  408. public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
  409. {
  410. if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
  411. {
  412. return false;
  413. }
  414. if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetBaseItemKind()))
  415. {
  416. return false;
  417. }
  418. if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetBaseItemKind()))
  419. {
  420. return false;
  421. }
  422. if (query.IsVirtualItem.HasValue && item.IsVirtualItem != query.IsVirtualItem.Value)
  423. {
  424. return false;
  425. }
  426. if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder)
  427. {
  428. return false;
  429. }
  430. UserItemData userData = null;
  431. if (query.IsLiked.HasValue)
  432. {
  433. userData = userDataManager.GetUserData(user, item);
  434. if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
  435. {
  436. return false;
  437. }
  438. }
  439. if (query.IsFavoriteOrLiked.HasValue)
  440. {
  441. userData = userData ?? userDataManager.GetUserData(user, item);
  442. var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);
  443. if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
  444. {
  445. return false;
  446. }
  447. }
  448. if (query.IsFavorite.HasValue)
  449. {
  450. userData = userData ?? userDataManager.GetUserData(user, item);
  451. if (userData.IsFavorite != query.IsFavorite.Value)
  452. {
  453. return false;
  454. }
  455. }
  456. if (query.IsResumable.HasValue)
  457. {
  458. userData = userData ?? userDataManager.GetUserData(user, item);
  459. var isResumable = userData.PlaybackPositionTicks > 0;
  460. if (isResumable != query.IsResumable.Value)
  461. {
  462. return false;
  463. }
  464. }
  465. if (query.IsPlayed.HasValue)
  466. {
  467. if (item.IsPlayed(user) != query.IsPlayed.Value)
  468. {
  469. return false;
  470. }
  471. }
  472. // Filter by Video3DFormat
  473. if (query.Is3D.HasValue)
  474. {
  475. var val = query.Is3D.Value;
  476. var video = item as Video;
  477. if (video == null || val != video.Video3DFormat.HasValue)
  478. {
  479. return false;
  480. }
  481. }
  482. /*
  483. * fuck - fix this
  484. if (query.IsHD.HasValue)
  485. {
  486. if (item.IsHD != query.IsHD.Value)
  487. {
  488. return false;
  489. }
  490. }
  491. */
  492. if (query.IsLocked.HasValue)
  493. {
  494. var val = query.IsLocked.Value;
  495. if (item.IsLocked != val)
  496. {
  497. return false;
  498. }
  499. }
  500. if (query.HasOverview.HasValue)
  501. {
  502. var filterValue = query.HasOverview.Value;
  503. var hasValue = !string.IsNullOrEmpty(item.Overview);
  504. if (hasValue != filterValue)
  505. {
  506. return false;
  507. }
  508. }
  509. if (query.HasImdbId.HasValue)
  510. {
  511. var filterValue = query.HasImdbId.Value;
  512. var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Imdb));
  513. if (hasValue != filterValue)
  514. {
  515. return false;
  516. }
  517. }
  518. if (query.HasTmdbId.HasValue)
  519. {
  520. var filterValue = query.HasTmdbId.Value;
  521. var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tmdb));
  522. if (hasValue != filterValue)
  523. {
  524. return false;
  525. }
  526. }
  527. if (query.HasTvdbId.HasValue)
  528. {
  529. var filterValue = query.HasTvdbId.Value;
  530. var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tvdb));
  531. if (hasValue != filterValue)
  532. {
  533. return false;
  534. }
  535. }
  536. if (query.HasOfficialRating.HasValue)
  537. {
  538. var filterValue = query.HasOfficialRating.Value;
  539. var hasValue = !string.IsNullOrEmpty(item.OfficialRating);
  540. if (hasValue != filterValue)
  541. {
  542. return false;
  543. }
  544. }
  545. if (query.IsPlaceHolder.HasValue)
  546. {
  547. var filterValue = query.IsPlaceHolder.Value;
  548. var isPlaceHolder = false;
  549. if (item is ISupportsPlaceHolders hasPlaceHolder)
  550. {
  551. isPlaceHolder = hasPlaceHolder.IsPlaceHolder;
  552. }
  553. if (isPlaceHolder != filterValue)
  554. {
  555. return false;
  556. }
  557. }
  558. if (query.HasSpecialFeature.HasValue)
  559. {
  560. var filterValue = query.HasSpecialFeature.Value;
  561. if (item is IHasSpecialFeatures movie)
  562. {
  563. var ok = filterValue
  564. ? movie.SpecialFeatureIds.Count > 0
  565. : movie.SpecialFeatureIds.Count == 0;
  566. if (!ok)
  567. {
  568. return false;
  569. }
  570. }
  571. else
  572. {
  573. return false;
  574. }
  575. }
  576. if (query.HasSubtitles.HasValue)
  577. {
  578. var val = query.HasSubtitles.Value;
  579. var video = item as Video;
  580. if (video == null || val != video.HasSubtitles)
  581. {
  582. return false;
  583. }
  584. }
  585. if (query.HasParentalRating.HasValue)
  586. {
  587. var val = query.HasParentalRating.Value;
  588. var rating = item.CustomRating;
  589. if (string.IsNullOrEmpty(rating))
  590. {
  591. rating = item.OfficialRating;
  592. }
  593. if (val)
  594. {
  595. if (string.IsNullOrEmpty(rating))
  596. {
  597. return false;
  598. }
  599. }
  600. else
  601. {
  602. if (!string.IsNullOrEmpty(rating))
  603. {
  604. return false;
  605. }
  606. }
  607. }
  608. if (query.HasTrailer.HasValue)
  609. {
  610. var val = query.HasTrailer.Value;
  611. var trailerCount = 0;
  612. if (item is IHasTrailers hasTrailers)
  613. {
  614. trailerCount = hasTrailers.GetTrailerCount();
  615. }
  616. var ok = val ? trailerCount > 0 : trailerCount == 0;
  617. if (!ok)
  618. {
  619. return false;
  620. }
  621. }
  622. if (query.HasThemeSong.HasValue)
  623. {
  624. var filterValue = query.HasThemeSong.Value;
  625. var themeCount = item.GetThemeSongs().Count;
  626. var ok = filterValue ? themeCount > 0 : themeCount == 0;
  627. if (!ok)
  628. {
  629. return false;
  630. }
  631. }
  632. if (query.HasThemeVideo.HasValue)
  633. {
  634. var filterValue = query.HasThemeVideo.Value;
  635. var themeCount = item.GetThemeVideos().Count;
  636. var ok = filterValue ? themeCount > 0 : themeCount == 0;
  637. if (!ok)
  638. {
  639. return false;
  640. }
  641. }
  642. // Apply genre filter
  643. if (query.Genres.Count > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparison.OrdinalIgnoreCase)))
  644. {
  645. return false;
  646. }
  647. // Filter by VideoType
  648. if (query.VideoTypes.Length > 0)
  649. {
  650. var video = item as Video;
  651. if (video == null || !query.VideoTypes.Contains(video.VideoType))
  652. {
  653. return false;
  654. }
  655. }
  656. if (query.ImageTypes.Length > 0 && !query.ImageTypes.Any(item.HasImage))
  657. {
  658. return false;
  659. }
  660. // Apply studio filter
  661. if (query.StudioIds.Length > 0 && !query.StudioIds.Any(id =>
  662. {
  663. var studioItem = libraryManager.GetItemById(id);
  664. return studioItem != null && item.Studios.Contains(studioItem.Name, StringComparison.OrdinalIgnoreCase);
  665. }))
  666. {
  667. return false;
  668. }
  669. // Apply genre filter
  670. if (query.GenreIds.Count > 0 && !query.GenreIds.Any(id =>
  671. {
  672. var genreItem = libraryManager.GetItemById(id);
  673. return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparison.OrdinalIgnoreCase);
  674. }))
  675. {
  676. return false;
  677. }
  678. // Apply year filter
  679. if (query.Years.Length > 0)
  680. {
  681. if (!(item.ProductionYear.HasValue && query.Years.Contains(item.ProductionYear.Value)))
  682. {
  683. return false;
  684. }
  685. }
  686. // Apply official rating filter
  687. if (query.OfficialRatings.Length > 0 && !query.OfficialRatings.Contains(item.OfficialRating ?? string.Empty))
  688. {
  689. return false;
  690. }
  691. if (query.ItemIds.Length > 0)
  692. {
  693. if (!query.ItemIds.Contains(item.Id))
  694. {
  695. return false;
  696. }
  697. }
  698. // Apply tag filter
  699. var tags = query.Tags;
  700. if (tags.Length > 0)
  701. {
  702. if (!tags.Any(v => item.Tags.Contains(v, StringComparison.OrdinalIgnoreCase)))
  703. {
  704. return false;
  705. }
  706. }
  707. if (query.MinCommunityRating.HasValue)
  708. {
  709. var val = query.MinCommunityRating.Value;
  710. if (!(item.CommunityRating.HasValue && item.CommunityRating >= val))
  711. {
  712. return false;
  713. }
  714. }
  715. if (query.MinCriticRating.HasValue)
  716. {
  717. var val = query.MinCriticRating.Value;
  718. if (!(item.CriticRating.HasValue && item.CriticRating >= val))
  719. {
  720. return false;
  721. }
  722. }
  723. if (query.MinIndexNumber.HasValue)
  724. {
  725. var val = query.MinIndexNumber.Value;
  726. if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
  727. {
  728. return false;
  729. }
  730. }
  731. if (query.MinPremiereDate.HasValue)
  732. {
  733. var val = query.MinPremiereDate.Value;
  734. if (!(item.PremiereDate.HasValue && item.PremiereDate.Value >= val))
  735. {
  736. return false;
  737. }
  738. }
  739. if (query.MaxPremiereDate.HasValue)
  740. {
  741. var val = query.MaxPremiereDate.Value;
  742. if (!(item.PremiereDate.HasValue && item.PremiereDate.Value <= val))
  743. {
  744. return false;
  745. }
  746. }
  747. if (query.ParentIndexNumber.HasValue)
  748. {
  749. var filterValue = query.ParentIndexNumber.Value;
  750. if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value != filterValue)
  751. {
  752. return false;
  753. }
  754. }
  755. if (query.SeriesStatuses.Length > 0)
  756. {
  757. var ok = new[] { item }.OfType<Series>().Any(p => p.Status.HasValue && query.SeriesStatuses.Contains(p.Status.Value));
  758. if (!ok)
  759. {
  760. return false;
  761. }
  762. }
  763. if (query.AiredDuringSeason.HasValue)
  764. {
  765. var episode = item as Episode;
  766. if (episode == null)
  767. {
  768. return false;
  769. }
  770. if (!Series.FilterEpisodesBySeason(new[] { episode }, query.AiredDuringSeason.Value, true).Any())
  771. {
  772. return false;
  773. }
  774. }
  775. return true;
  776. }
  777. private IEnumerable<BaseItem> GetMediaFolders(User user)
  778. {
  779. if (user == null)
  780. {
  781. return _libraryManager.RootFolder
  782. .Children
  783. .OfType<Folder>()
  784. .Where(UserView.IsEligibleForGrouping);
  785. }
  786. return _libraryManager.GetUserRootFolder()
  787. .GetChildren(user, true)
  788. .OfType<Folder>()
  789. .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
  790. }
  791. private BaseItem[] GetMediaFolders(User user, IEnumerable<string> viewTypes)
  792. {
  793. if (user == null)
  794. {
  795. return GetMediaFolders(null)
  796. .Where(i =>
  797. {
  798. var folder = i as ICollectionFolder;
  799. return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
  800. }).ToArray();
  801. }
  802. return GetMediaFolders(user)
  803. .Where(i =>
  804. {
  805. var folder = i as ICollectionFolder;
  806. return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
  807. }).ToArray();
  808. }
  809. private BaseItem[] GetMediaFolders(Folder parent, User user, IEnumerable<string> viewTypes)
  810. {
  811. if (parent == null || parent is UserView)
  812. {
  813. return GetMediaFolders(user, viewTypes);
  814. }
  815. return new BaseItem[] { parent };
  816. }
  817. private UserView GetUserViewWithName(string type, string sortName, BaseItem parent)
  818. {
  819. return _userViewManager.GetUserSubView(parent.Id, parent.Id.ToString("N", CultureInfo.InvariantCulture), type, sortName);
  820. }
  821. private UserView GetUserView(string type, string localizationKey, string sortName, BaseItem parent)
  822. {
  823. return _userViewManager.GetUserSubView(parent.Id, type, localizationKey, sortName);
  824. }
  825. public static IEnumerable<BaseItem> FilterForAdjacency(List<BaseItem> list, string adjacentToId)
  826. {
  827. var adjacentToIdGuid = new Guid(adjacentToId);
  828. var adjacentToItem = list.FirstOrDefault(i => i.Id == adjacentToIdGuid);
  829. var index = list.IndexOf(adjacentToItem);
  830. var previousId = Guid.Empty;
  831. var nextId = Guid.Empty;
  832. if (index > 0)
  833. {
  834. previousId = list[index - 1].Id;
  835. }
  836. if (index < list.Count - 1)
  837. {
  838. nextId = list[index + 1].Id;
  839. }
  840. return list.Where(i => i.Id == previousId || i.Id == nextId || i.Id == adjacentToIdGuid);
  841. }
  842. }
  843. }