ItemRefreshService.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. using MediaBrowser.Controller.Dto;
  2. using MediaBrowser.Controller.Entities;
  3. using MediaBrowser.Controller.Entities.Audio;
  4. using MediaBrowser.Controller.Library;
  5. using MediaBrowser.Controller.Providers;
  6. using ServiceStack;
  7. using System;
  8. using System.Linq;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. namespace MediaBrowser.Api
  12. {
  13. public class BaseRefreshRequest : IReturnVoid
  14. {
  15. [ApiMember(Name = "Forced", Description = "Indicates if a normal or forced refresh should occur.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
  16. public bool Forced { get; set; }
  17. }
  18. [Route("/Items/{Id}/Refresh", "POST")]
  19. [Api(Description = "Refreshes metadata for an item")]
  20. public class RefreshItem : BaseRefreshRequest
  21. {
  22. [ApiMember(Name = "Recursive", Description = "Indicates if the refresh should occur recursively.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
  23. public bool Recursive { get; set; }
  24. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  25. public string Id { get; set; }
  26. }
  27. [Route("/Artists/{Name}/Refresh", "POST")]
  28. [Api(Description = "Refreshes metadata for an artist")]
  29. public class RefreshArtist : BaseRefreshRequest
  30. {
  31. [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  32. public string Name { get; set; }
  33. }
  34. [Route("/Genres/{Name}/Refresh", "POST")]
  35. [Api(Description = "Refreshes metadata for a genre")]
  36. public class RefreshGenre : BaseRefreshRequest
  37. {
  38. [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  39. public string Name { get; set; }
  40. }
  41. [Route("/MusicGenres/{Name}/Refresh", "POST")]
  42. [Api(Description = "Refreshes metadata for a music genre")]
  43. public class RefreshMusicGenre : BaseRefreshRequest
  44. {
  45. [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  46. public string Name { get; set; }
  47. }
  48. [Route("/GameGenres/{Name}/Refresh", "POST")]
  49. [Api(Description = "Refreshes metadata for a game genre")]
  50. public class RefreshGameGenre : BaseRefreshRequest
  51. {
  52. [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  53. public string Name { get; set; }
  54. }
  55. [Route("/Persons/{Name}/Refresh", "POST")]
  56. [Api(Description = "Refreshes metadata for a person")]
  57. public class RefreshPerson : BaseRefreshRequest
  58. {
  59. [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  60. public string Name { get; set; }
  61. }
  62. [Route("/Studios/{Name}/Refresh", "POST")]
  63. [Api(Description = "Refreshes metadata for a studio")]
  64. public class RefreshStudio : BaseRefreshRequest
  65. {
  66. [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  67. public string Name { get; set; }
  68. }
  69. public class ItemRefreshService : BaseApiService
  70. {
  71. private readonly ILibraryManager _libraryManager;
  72. private readonly IDtoService _dtoService;
  73. public ItemRefreshService(ILibraryManager libraryManager, IDtoService dtoService)
  74. {
  75. _libraryManager = libraryManager;
  76. _dtoService = dtoService;
  77. }
  78. public void Post(RefreshArtist request)
  79. {
  80. var task = RefreshArtist(request);
  81. Task.WaitAll(task);
  82. }
  83. private async Task RefreshArtist(RefreshArtist request)
  84. {
  85. var item = GetArtist(request.Name, _libraryManager);
  86. var cancellationToken = CancellationToken.None;
  87. var albums = _libraryManager.RootFolder
  88. .RecursiveChildren
  89. .OfType<MusicAlbum>()
  90. .Where(i => i.HasArtist(item.Name))
  91. .ToList();
  92. var musicArtists = albums
  93. .Select(i => i.Parent)
  94. .OfType<MusicArtist>()
  95. .ToList();
  96. var options = GetRefreshOptions(request);
  97. var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new Progress<double>(), cancellationToken, options, true));
  98. await Task.WhenAll(musicArtistRefreshTasks).ConfigureAwait(false);
  99. try
  100. {
  101. await item.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false);
  102. }
  103. catch (Exception ex)
  104. {
  105. Logger.ErrorException("Error refreshing library", ex);
  106. }
  107. }
  108. public void Post(RefreshGenre request)
  109. {
  110. var task = RefreshGenre(request);
  111. Task.WaitAll(task);
  112. }
  113. private async Task RefreshGenre(RefreshGenre request)
  114. {
  115. var item = GetGenre(request.Name, _libraryManager);
  116. try
  117. {
  118. await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false);
  119. }
  120. catch (Exception ex)
  121. {
  122. Logger.ErrorException("Error refreshing library", ex);
  123. }
  124. }
  125. public void Post(RefreshMusicGenre request)
  126. {
  127. var task = RefreshMusicGenre(request);
  128. Task.WaitAll(task);
  129. }
  130. private async Task RefreshMusicGenre(RefreshMusicGenre request)
  131. {
  132. var item = GetMusicGenre(request.Name, _libraryManager);
  133. try
  134. {
  135. await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false);
  136. }
  137. catch (Exception ex)
  138. {
  139. Logger.ErrorException("Error refreshing library", ex);
  140. }
  141. }
  142. public void Post(RefreshGameGenre request)
  143. {
  144. var task = RefreshGameGenre(request);
  145. Task.WaitAll(task);
  146. }
  147. private async Task RefreshGameGenre(RefreshGameGenre request)
  148. {
  149. var item = GetGameGenre(request.Name, _libraryManager);
  150. try
  151. {
  152. await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false);
  153. }
  154. catch (Exception ex)
  155. {
  156. Logger.ErrorException("Error refreshing library", ex);
  157. }
  158. }
  159. public void Post(RefreshPerson request)
  160. {
  161. var task = RefreshPerson(request);
  162. Task.WaitAll(task);
  163. }
  164. private async Task RefreshPerson(RefreshPerson request)
  165. {
  166. var item = GetPerson(request.Name, _libraryManager);
  167. try
  168. {
  169. await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false);
  170. }
  171. catch (Exception ex)
  172. {
  173. Logger.ErrorException("Error refreshing library", ex);
  174. }
  175. }
  176. public void Post(RefreshStudio request)
  177. {
  178. var task = RefreshStudio(request);
  179. Task.WaitAll(task);
  180. }
  181. private async Task RefreshStudio(RefreshStudio request)
  182. {
  183. var item = GetStudio(request.Name, _libraryManager);
  184. try
  185. {
  186. await item.RefreshMetadata(GetRefreshOptions(request), CancellationToken.None).ConfigureAwait(false);
  187. }
  188. catch (Exception ex)
  189. {
  190. Logger.ErrorException("Error refreshing library", ex);
  191. }
  192. }
  193. /// <summary>
  194. /// Posts the specified request.
  195. /// </summary>
  196. /// <param name="request">The request.</param>
  197. public void Post(RefreshItem request)
  198. {
  199. var task = RefreshItem(request);
  200. Task.WaitAll(task);
  201. }
  202. /// <summary>
  203. /// Refreshes the item.
  204. /// </summary>
  205. /// <param name="request">The request.</param>
  206. /// <returns>Task.</returns>
  207. private async Task RefreshItem(RefreshItem request)
  208. {
  209. var item = _dtoService.GetItemByDtoId(request.Id);
  210. var options = GetRefreshOptions(request);
  211. try
  212. {
  213. await item.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false);
  214. if (item.IsFolder)
  215. {
  216. // Collection folders don't validate their children so we'll have to simulate that here
  217. var collectionFolder = item as CollectionFolder;
  218. if (collectionFolder != null)
  219. {
  220. await RefreshCollectionFolderChildren(request, collectionFolder).ConfigureAwait(false);
  221. }
  222. else
  223. {
  224. var folder = (Folder)item;
  225. await folder.ValidateChildren(new Progress<double>(), CancellationToken.None, options, request.Recursive).ConfigureAwait(false);
  226. }
  227. }
  228. }
  229. catch (Exception ex)
  230. {
  231. Logger.ErrorException("Error refreshing library", ex);
  232. }
  233. }
  234. /// <summary>
  235. /// Refreshes the collection folder children.
  236. /// </summary>
  237. /// <param name="request">The request.</param>
  238. /// <param name="collectionFolder">The collection folder.</param>
  239. /// <returns>Task.</returns>
  240. private async Task RefreshCollectionFolderChildren(RefreshItem request, CollectionFolder collectionFolder)
  241. {
  242. var options = GetRefreshOptions(request);
  243. foreach (var child in collectionFolder.Children.ToList())
  244. {
  245. await child.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false);
  246. if (child.IsFolder)
  247. {
  248. var folder = (Folder)child;
  249. await folder.ValidateChildren(new Progress<double>(), CancellationToken.None, options, request.Recursive).ConfigureAwait(false);
  250. }
  251. }
  252. }
  253. private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request)
  254. {
  255. return new MetadataRefreshOptions
  256. {
  257. MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
  258. ImageRefreshMode = ImageRefreshMode.FullRefresh,
  259. ReplaceAllMetadata = request.Forced
  260. };
  261. }
  262. }
  263. }