TVSeriesManager.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using MediaBrowser.Controller.Configuration;
  6. using MediaBrowser.Controller.Dto;
  7. using MediaBrowser.Controller.Entities;
  8. using MediaBrowser.Controller.Entities.TV;
  9. using MediaBrowser.Controller.Library;
  10. using MediaBrowser.Controller.TV;
  11. using MediaBrowser.Model.Entities;
  12. using MediaBrowser.Model.Querying;
  13. namespace Emby.Server.Implementations.TV
  14. {
  15. public class TVSeriesManager : ITVSeriesManager
  16. {
  17. private readonly IUserManager _userManager;
  18. private readonly IUserDataManager _userDataManager;
  19. private readonly ILibraryManager _libraryManager;
  20. private readonly IServerConfigurationManager _config;
  21. public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IServerConfigurationManager config)
  22. {
  23. _userManager = userManager;
  24. _userDataManager = userDataManager;
  25. _libraryManager = libraryManager;
  26. _config = config;
  27. }
  28. public QueryResult<BaseItem> GetNextUp(NextUpQuery request, DtoOptions dtoOptions)
  29. {
  30. var user = _userManager.GetUserById(request.UserId);
  31. if (user == null)
  32. {
  33. throw new ArgumentException("User not found");
  34. }
  35. string presentationUniqueKey = null;
  36. if (!string.IsNullOrEmpty(request.SeriesId))
  37. {
  38. var series = _libraryManager.GetItemById(request.SeriesId) as Series;
  39. if (series != null)
  40. {
  41. presentationUniqueKey = GetUniqueSeriesKey(series);
  42. }
  43. }
  44. if (!string.IsNullOrEmpty(presentationUniqueKey))
  45. {
  46. return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
  47. }
  48. var parentIdGuid = string.IsNullOrEmpty(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
  49. BaseItem[] parents;
  50. if (parentIdGuid.HasValue)
  51. {
  52. var parent = _libraryManager.GetItemById(parentIdGuid.Value);
  53. if (parent != null)
  54. {
  55. parents = new[] { parent };
  56. }
  57. else
  58. {
  59. parents = Array.Empty<BaseItem>();
  60. }
  61. }
  62. else
  63. {
  64. parents = _libraryManager.GetUserRootFolder().GetChildren(user, true)
  65. .Where(i => i is Folder)
  66. .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture)))
  67. .ToArray();
  68. }
  69. return GetNextUp(request, parents, dtoOptions);
  70. }
  71. public QueryResult<BaseItem> GetNextUp(NextUpQuery request, BaseItem[] parentsFolders, DtoOptions dtoOptions)
  72. {
  73. var user = _userManager.GetUserById(request.UserId);
  74. if (user == null)
  75. {
  76. throw new ArgumentException("User not found");
  77. }
  78. string presentationUniqueKey = null;
  79. int? limit = null;
  80. if (!string.IsNullOrEmpty(request.SeriesId))
  81. {
  82. var series = _libraryManager.GetItemById(request.SeriesId) as Series;
  83. if (series != null)
  84. {
  85. presentationUniqueKey = GetUniqueSeriesKey(series);
  86. limit = 1;
  87. }
  88. }
  89. if (!string.IsNullOrEmpty(presentationUniqueKey))
  90. {
  91. return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
  92. }
  93. if (limit.HasValue)
  94. {
  95. limit = limit.Value + 10;
  96. }
  97. var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
  98. {
  99. IncludeItemTypes = new[] { typeof(Episode).Name },
  100. OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
  101. SeriesPresentationUniqueKey = presentationUniqueKey,
  102. Limit = limit,
  103. DtoOptions = new DtoOptions
  104. {
  105. Fields = new ItemFields[]
  106. {
  107. ItemFields.SeriesPresentationUniqueKey
  108. },
  109. EnableImages = false
  110. },
  111. GroupBySeriesPresentationUniqueKey = true
  112. }, parentsFolders.ToList()).Cast<Episode>().Select(GetUniqueSeriesKey);
  113. // Avoid implicitly captured closure
  114. var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
  115. return GetResult(episodes, request);
  116. }
  117. public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<string> seriesKeys, DtoOptions dtoOptions)
  118. {
  119. // Avoid implicitly captured closure
  120. var currentUser = user;
  121. var allNextUp = seriesKeys
  122. .Select(i => GetNextUp(i, currentUser, dtoOptions));
  123. //allNextUp = allNextUp.OrderByDescending(i => i.Item1);
  124. // If viewing all next up for all series, remove first episodes
  125. // But if that returns empty, keep those first episodes (avoid completely empty view)
  126. var alwaysEnableFirstEpisode = !string.IsNullOrEmpty(request.SeriesId);
  127. var anyFound = false;
  128. return allNextUp
  129. .Where(i =>
  130. {
  131. if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue)
  132. {
  133. anyFound = true;
  134. return true;
  135. }
  136. if (!anyFound && i.Item1 == DateTime.MinValue)
  137. {
  138. return true;
  139. }
  140. return false;
  141. })
  142. .Select(i => i.Item2())
  143. .Where(i => i != null);
  144. }
  145. private static string GetUniqueSeriesKey(Episode episode)
  146. {
  147. return episode.SeriesPresentationUniqueKey;
  148. }
  149. private static string GetUniqueSeriesKey(Series series)
  150. {
  151. return series.GetPresentationUniqueKey();
  152. }
  153. /// <summary>
  154. /// Gets the next up.
  155. /// </summary>
  156. /// <returns>Task{Episode}.</returns>
  157. private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions)
  158. {
  159. var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
  160. {
  161. AncestorWithPresentationUniqueKey = null,
  162. SeriesPresentationUniqueKey = seriesKey,
  163. IncludeItemTypes = new[] { typeof(Episode).Name },
  164. OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
  165. IsPlayed = true,
  166. Limit = 1,
  167. ParentIndexNumberNotEquals = 0,
  168. DtoOptions = new DtoOptions
  169. {
  170. Fields = new ItemFields[]
  171. {
  172. ItemFields.SortName
  173. },
  174. EnableImages = false
  175. }
  176. }).FirstOrDefault();
  177. Func<Episode> getEpisode = () =>
  178. {
  179. return _libraryManager.GetItemList(new InternalItemsQuery(user)
  180. {
  181. AncestorWithPresentationUniqueKey = null,
  182. SeriesPresentationUniqueKey = seriesKey,
  183. IncludeItemTypes = new[] { typeof(Episode).Name },
  184. OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
  185. Limit = 1,
  186. IsPlayed = false,
  187. IsVirtualItem = false,
  188. ParentIndexNumberNotEquals = 0,
  189. MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName,
  190. DtoOptions = dtoOptions
  191. }).Cast<Episode>().FirstOrDefault();
  192. };
  193. if (lastWatchedEpisode != null)
  194. {
  195. var userData = _userDataManager.GetUserData(user, lastWatchedEpisode);
  196. var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1);
  197. return new Tuple<DateTime, Func<Episode>>(lastWatchedDate, getEpisode);
  198. }
  199. // Return the first episode
  200. return new Tuple<DateTime, Func<Episode>>(DateTime.MinValue, getEpisode);
  201. }
  202. private static QueryResult<BaseItem> GetResult(IEnumerable<BaseItem> items, NextUpQuery query)
  203. {
  204. int totalCount = 0;
  205. if (query.EnableTotalRecordCount)
  206. {
  207. var list = items.ToList();
  208. totalCount = list.Count;
  209. items = list;
  210. }
  211. if (query.StartIndex.HasValue)
  212. {
  213. items = items.Skip(query.StartIndex.Value);
  214. }
  215. if (query.Limit.HasValue)
  216. {
  217. items = items.Take(query.Limit.Value);
  218. }
  219. return new QueryResult<BaseItem>
  220. {
  221. TotalRecordCount = totalCount,
  222. Items = items.ToArray()
  223. };
  224. }
  225. }
  226. }