ImageService.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. using MediaBrowser.Common.Configuration;
  2. using MediaBrowser.Common.Extensions;
  3. using MediaBrowser.Common.IO;
  4. using MediaBrowser.Common.Net;
  5. using MediaBrowser.Controller;
  6. using MediaBrowser.Controller.Dto;
  7. using MediaBrowser.Controller.Entities;
  8. using MediaBrowser.Controller.Entities.Audio;
  9. using MediaBrowser.Controller.Entities.TV;
  10. using MediaBrowser.Controller.Library;
  11. using MediaBrowser.Controller.Providers;
  12. using MediaBrowser.Model.Dto;
  13. using MediaBrowser.Model.Entities;
  14. using ServiceStack.ServiceHost;
  15. using ServiceStack.Text.Controller;
  16. using System;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Linq;
  20. using System.Threading;
  21. using System.Threading.Tasks;
  22. namespace MediaBrowser.Api.Images
  23. {
  24. /// <summary>
  25. /// Class GetItemImage
  26. /// </summary>
  27. [Route("/Items/{Id}/Images", "GET")]
  28. [Api(Description = "Gets information about an item's images")]
  29. public class GetItemImageInfos : IReturn<List<ImageInfo>>
  30. {
  31. /// <summary>
  32. /// Gets or sets the id.
  33. /// </summary>
  34. /// <value>The id.</value>
  35. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  36. public string Id { get; set; }
  37. }
  38. [Route("/Items/{Id}/Images/{Type}", "GET")]
  39. [Route("/Items/{Id}/Images/{Type}/{Index}", "GET")]
  40. [Api(Description = "Gets an item image")]
  41. public class GetItemImage : ImageRequest
  42. {
  43. /// <summary>
  44. /// Gets or sets the id.
  45. /// </summary>
  46. /// <value>The id.</value>
  47. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  48. public string Id { get; set; }
  49. }
  50. /// <summary>
  51. /// Class UpdateItemImageIndex
  52. /// </summary>
  53. [Route("/Items/{Id}/Images/{Type}/{Index}/Index", "POST")]
  54. [Api(Description = "Updates the index for an item image")]
  55. public class UpdateItemImageIndex : IReturnVoid
  56. {
  57. /// <summary>
  58. /// Gets or sets the id.
  59. /// </summary>
  60. /// <value>The id.</value>
  61. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  62. public Guid Id { get; set; }
  63. /// <summary>
  64. /// Gets or sets the type of the image.
  65. /// </summary>
  66. /// <value>The type of the image.</value>
  67. [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  68. public ImageType Type { get; set; }
  69. /// <summary>
  70. /// Gets or sets the index.
  71. /// </summary>
  72. /// <value>The index.</value>
  73. [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
  74. public int Index { get; set; }
  75. /// <summary>
  76. /// Gets or sets the new index.
  77. /// </summary>
  78. /// <value>The new index.</value>
  79. [ApiMember(Name = "NewIndex", Description = "The new image index", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
  80. public int NewIndex { get; set; }
  81. }
  82. /// <summary>
  83. /// Class DeleteItemImage
  84. /// </summary>
  85. [Route("/Items/{Id}/Images/{Type}", "DELETE")]
  86. [Route("/Items/{Id}/Images/{Type}/{Index}", "DELETE")]
  87. [Api(Description = "Deletes an item image")]
  88. public class DeleteItemImage : DeleteImageRequest, IReturnVoid
  89. {
  90. /// <summary>
  91. /// Gets or sets the id.
  92. /// </summary>
  93. /// <value>The id.</value>
  94. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  95. public Guid Id { get; set; }
  96. }
  97. /// <summary>
  98. /// Class GetPersonImage
  99. /// </summary>
  100. [Route("/Persons/{Name}/Images/{Type}", "GET")]
  101. [Route("/Persons/{Name}/Images/{Type}/{Index}", "GET")]
  102. [Api(Description = "Gets a person image")]
  103. public class GetPersonImage : ImageRequest
  104. {
  105. /// <summary>
  106. /// Gets or sets the name.
  107. /// </summary>
  108. /// <value>The name.</value>
  109. [ApiMember(Name = "Name", Description = "Person name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  110. public string Name { get; set; }
  111. }
  112. /// <summary>
  113. /// Class GetArtistImage
  114. /// </summary>
  115. [Route("/Artists/{Name}/Images/{Type}", "GET")]
  116. [Route("/Artists/{Name}/Images/{Type}/{Index}", "GET")]
  117. [Api(Description = "Gets an artist image")]
  118. public class GetArtistImage : ImageRequest
  119. {
  120. /// <summary>
  121. /// Gets or sets the name.
  122. /// </summary>
  123. /// <value>The name.</value>
  124. [ApiMember(Name = "Name", Description = "Artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  125. public string Name { get; set; }
  126. }
  127. /// <summary>
  128. /// Class GetStudioImage
  129. /// </summary>
  130. [Route("/Studios/{Name}/Images/{Type}", "GET")]
  131. [Route("/Studios/{Name}/Images/{Type}/{Index}", "GET")]
  132. [Api(Description = "Gets a studio image")]
  133. public class GetStudioImage : ImageRequest
  134. {
  135. /// <summary>
  136. /// Gets or sets the name.
  137. /// </summary>
  138. /// <value>The name.</value>
  139. [ApiMember(Name = "Name", Description = "Studio name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  140. public string Name { get; set; }
  141. }
  142. /// <summary>
  143. /// Class GetGenreImage
  144. /// </summary>
  145. [Route("/Genres/{Name}/Images/{Type}", "GET")]
  146. [Route("/Genres/{Name}/Images/{Type}/{Index}", "GET")]
  147. [Api(Description = "Gets a genre image")]
  148. public class GetGenreImage : ImageRequest
  149. {
  150. /// <summary>
  151. /// Gets or sets the name.
  152. /// </summary>
  153. /// <value>The name.</value>
  154. [ApiMember(Name = "Name", Description = "Genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  155. public string Name { get; set; }
  156. }
  157. /// <summary>
  158. /// Class GetYearImage
  159. /// </summary>
  160. [Route("/Years/{Year}/Images/{Type}", "GET")]
  161. [Route("/Years/{Year}/Images/{Type}/{Index}", "GET")]
  162. [Api(Description = "Gets a year image")]
  163. public class GetYearImage : ImageRequest
  164. {
  165. /// <summary>
  166. /// Gets or sets the year.
  167. /// </summary>
  168. /// <value>The year.</value>
  169. [ApiMember(Name = "Year", Description = "Year", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
  170. public int Year { get; set; }
  171. }
  172. /// <summary>
  173. /// Class GetUserImage
  174. /// </summary>
  175. [Route("/Users/{Id}/Images/{Type}", "GET")]
  176. [Route("/Users/{Id}/Images/{Type}/{Index}", "GET")]
  177. [Api(Description = "Gets a user image")]
  178. public class GetUserImage : ImageRequest
  179. {
  180. /// <summary>
  181. /// Gets or sets the id.
  182. /// </summary>
  183. /// <value>The id.</value>
  184. [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
  185. public Guid Id { get; set; }
  186. }
  187. /// <summary>
  188. /// Class DeleteUserImage
  189. /// </summary>
  190. [Route("/Users/{Id}/Images/{Type}", "DELETE")]
  191. [Route("/Users/{Id}/Images/{Type}/{Index}", "DELETE")]
  192. [Api(Description = "Deletes a user image")]
  193. public class DeleteUserImage : DeleteImageRequest, IReturnVoid
  194. {
  195. /// <summary>
  196. /// Gets or sets the id.
  197. /// </summary>
  198. /// <value>The id.</value>
  199. [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
  200. public Guid Id { get; set; }
  201. }
  202. /// <summary>
  203. /// Class PostUserImage
  204. /// </summary>
  205. [Route("/Users/{Id}/Images/{Type}", "POST")]
  206. [Route("/Users/{Id}/Images/{Type}/{Index}", "POST")]
  207. [Api(Description = "Posts a user image")]
  208. public class PostUserImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
  209. {
  210. /// <summary>
  211. /// Gets or sets the id.
  212. /// </summary>
  213. /// <value>The id.</value>
  214. [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  215. public Guid Id { get; set; }
  216. /// <summary>
  217. /// The raw Http Request Input Stream
  218. /// </summary>
  219. /// <value>The request stream.</value>
  220. public Stream RequestStream { get; set; }
  221. }
  222. /// <summary>
  223. /// Class PostItemImage
  224. /// </summary>
  225. [Route("/Items/{Id}/Images/{Type}", "POST")]
  226. [Route("/Items/{Id}/Images/{Type}/{Index}", "POST")]
  227. [Api(Description = "Posts an item image")]
  228. public class PostItemImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
  229. {
  230. /// <summary>
  231. /// Gets or sets the id.
  232. /// </summary>
  233. /// <value>The id.</value>
  234. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
  235. public string Id { get; set; }
  236. /// <summary>
  237. /// The raw Http Request Input Stream
  238. /// </summary>
  239. /// <value>The request stream.</value>
  240. public Stream RequestStream { get; set; }
  241. }
  242. /// <summary>
  243. /// Class ImageService
  244. /// </summary>
  245. public class ImageService : BaseApiService
  246. {
  247. /// <summary>
  248. /// The _user manager
  249. /// </summary>
  250. private readonly IUserManager _userManager;
  251. /// <summary>
  252. /// The _library manager
  253. /// </summary>
  254. private readonly ILibraryManager _libraryManager;
  255. private readonly IApplicationPaths _appPaths;
  256. private readonly IProviderManager _providerManager;
  257. /// <summary>
  258. /// Initializes a new instance of the <see cref="ImageService" /> class.
  259. /// </summary>
  260. /// <param name="userManager">The user manager.</param>
  261. /// <param name="libraryManager">The library manager.</param>
  262. /// <param name="appPaths">The app paths.</param>
  263. /// <param name="providerManager">The provider manager.</param>
  264. public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager)
  265. {
  266. _userManager = userManager;
  267. _libraryManager = libraryManager;
  268. _appPaths = appPaths;
  269. _providerManager = providerManager;
  270. }
  271. /// <summary>
  272. /// Gets the specified request.
  273. /// </summary>
  274. /// <param name="request">The request.</param>
  275. /// <returns>System.Object.</returns>
  276. public object Get(GetItemImageInfos request)
  277. {
  278. var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager);
  279. var result = GetItemImageInfos(item).Result;
  280. return ToOptimizedResult(result);
  281. }
  282. /// <summary>
  283. /// Gets the item image infos.
  284. /// </summary>
  285. /// <param name="item">The item.</param>
  286. /// <returns>Task{List{ImageInfo}}.</returns>
  287. public async Task<List<ImageInfo>> GetItemImageInfos(BaseItem item)
  288. {
  289. var list = new List<ImageInfo>();
  290. foreach (var image in item.Images)
  291. {
  292. var path = image.Value;
  293. var fileInfo = new FileInfo(path);
  294. var dateModified = Kernel.Instance.ImageManager.GetImageDateModified(item, path);
  295. var size = await Kernel.Instance.ImageManager.GetImageSize(path, dateModified).ConfigureAwait(false);
  296. list.Add(new ImageInfo
  297. {
  298. Path = path,
  299. ImageType = image.Key,
  300. ImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, image.Key, path),
  301. Size = fileInfo.Length,
  302. Width = Convert.ToInt32(size.Width),
  303. Height = Convert.ToInt32(size.Height)
  304. });
  305. }
  306. var index = 0;
  307. foreach (var image in item.BackdropImagePaths)
  308. {
  309. var fileInfo = new FileInfo(image);
  310. var dateModified = Kernel.Instance.ImageManager.GetImageDateModified(item, image);
  311. var size = await Kernel.Instance.ImageManager.GetImageSize(image, dateModified).ConfigureAwait(false);
  312. list.Add(new ImageInfo
  313. {
  314. Path = image,
  315. ImageIndex = index,
  316. ImageType = ImageType.Backdrop,
  317. ImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Backdrop, image),
  318. Size = fileInfo.Length,
  319. Width = Convert.ToInt32(size.Width),
  320. Height = Convert.ToInt32(size.Height)
  321. });
  322. index++;
  323. }
  324. index = 0;
  325. foreach (var image in item.ScreenshotImagePaths)
  326. {
  327. var fileInfo = new FileInfo(image);
  328. var dateModified = Kernel.Instance.ImageManager.GetImageDateModified(item, image);
  329. var size = await Kernel.Instance.ImageManager.GetImageSize(image, dateModified).ConfigureAwait(false);
  330. list.Add(new ImageInfo
  331. {
  332. Path = image,
  333. ImageIndex = index,
  334. ImageType = ImageType.Screenshot,
  335. ImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Screenshot, image),
  336. Size = fileInfo.Length,
  337. Width = Convert.ToInt32(size.Width),
  338. Height = Convert.ToInt32(size.Height)
  339. });
  340. index++;
  341. }
  342. var video = item as Video;
  343. if (video != null)
  344. {
  345. index = 0;
  346. foreach (var chapter in video.Chapters)
  347. {
  348. if (!string.IsNullOrEmpty(chapter.ImagePath))
  349. {
  350. var image = chapter.ImagePath;
  351. var fileInfo = new FileInfo(image);
  352. var dateModified = Kernel.Instance.ImageManager.GetImageDateModified(item, image);
  353. var size = await Kernel.Instance.ImageManager.GetImageSize(image, dateModified).ConfigureAwait(false);
  354. list.Add(new ImageInfo
  355. {
  356. Path = image,
  357. ImageIndex = index,
  358. ImageType = ImageType.Chapter,
  359. ImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Chapter, image),
  360. Size = fileInfo.Length,
  361. Width = Convert.ToInt32(size.Width),
  362. Height = Convert.ToInt32(size.Height)
  363. });
  364. }
  365. index++;
  366. }
  367. }
  368. return list;
  369. }
  370. /// <summary>
  371. /// Gets the specified request.
  372. /// </summary>
  373. /// <param name="request">The request.</param>
  374. /// <returns>System.Object.</returns>
  375. public object Get(GetItemImage request)
  376. {
  377. var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager);
  378. return GetImage(request, item);
  379. }
  380. /// <summary>
  381. /// Gets the specified request.
  382. /// </summary>
  383. /// <param name="request">The request.</param>
  384. /// <returns>System.Object.</returns>
  385. public object Get(GetUserImage request)
  386. {
  387. var item = _userManager.Users.First(i => i.Id == request.Id);
  388. return GetImage(request, item);
  389. }
  390. /// <summary>
  391. /// Gets the specified request.
  392. /// </summary>
  393. /// <param name="request">The request.</param>
  394. /// <returns>System.Object.</returns>
  395. public object Get(GetYearImage request)
  396. {
  397. var item = _libraryManager.GetYear(request.Year).Result;
  398. return GetImage(request, item);
  399. }
  400. /// <summary>
  401. /// Gets the specified request.
  402. /// </summary>
  403. /// <param name="request">The request.</param>
  404. /// <returns>System.Object.</returns>
  405. public object Get(GetStudioImage request)
  406. {
  407. var item = _libraryManager.GetStudio(request.Name).Result;
  408. return GetImage(request, item);
  409. }
  410. /// <summary>
  411. /// Gets the specified request.
  412. /// </summary>
  413. /// <param name="request">The request.</param>
  414. /// <returns>System.Object.</returns>
  415. public object Get(GetPersonImage request)
  416. {
  417. var item = _libraryManager.GetPerson(request.Name).Result;
  418. return GetImage(request, item);
  419. }
  420. /// <summary>
  421. /// Gets the specified request.
  422. /// </summary>
  423. /// <param name="request">The request.</param>
  424. /// <returns>System.Object.</returns>
  425. public object Get(GetArtistImage request)
  426. {
  427. var item = _libraryManager.GetArtist(request.Name).Result;
  428. return GetImage(request, item);
  429. }
  430. /// <summary>
  431. /// Gets the specified request.
  432. /// </summary>
  433. /// <param name="request">The request.</param>
  434. /// <returns>System.Object.</returns>
  435. public object Get(GetGenreImage request)
  436. {
  437. var item = _libraryManager.GetGenre(request.Name).Result;
  438. return GetImage(request, item);
  439. }
  440. /// <summary>
  441. /// Posts the specified request.
  442. /// </summary>
  443. /// <param name="request">The request.</param>
  444. public void Post(PostUserImage request)
  445. {
  446. var pathInfo = PathInfo.Parse(RequestContext.PathInfo);
  447. var id = new Guid(pathInfo.GetArgumentValue<string>(1));
  448. request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(3), true);
  449. var item = _userManager.Users.First(i => i.Id == id);
  450. var task = PostImage(item, request.RequestStream, request.Type, RequestContext.ContentType);
  451. Task.WaitAll(task);
  452. }
  453. /// <summary>
  454. /// Posts the specified request.
  455. /// </summary>
  456. /// <param name="request">The request.</param>
  457. public void Post(PostItemImage request)
  458. {
  459. var pathInfo = PathInfo.Parse(RequestContext.PathInfo);
  460. var id = new Guid(pathInfo.GetArgumentValue<string>(1));
  461. request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(3), true);
  462. var item = _libraryManager.GetItemById(id);
  463. var task = PostImage(item, request.RequestStream, request.Type, RequestContext.ContentType);
  464. Task.WaitAll(task);
  465. }
  466. /// <summary>
  467. /// Deletes the specified request.
  468. /// </summary>
  469. /// <param name="request">The request.</param>
  470. public void Delete(DeleteUserImage request)
  471. {
  472. var item = _userManager.Users.First(i => i.Id == request.Id);
  473. var task = item.DeleteImage(request.Type, request.Index);
  474. Task.WaitAll(task);
  475. }
  476. /// <summary>
  477. /// Deletes the specified request.
  478. /// </summary>
  479. /// <param name="request">The request.</param>
  480. public void Delete(DeleteItemImage request)
  481. {
  482. var item = _libraryManager.GetItemById(request.Id);
  483. var task = item.DeleteImage(request.Type, request.Index);
  484. Task.WaitAll(task);
  485. }
  486. /// <summary>
  487. /// Posts the specified request.
  488. /// </summary>
  489. /// <param name="request">The request.</param>
  490. public void Post(UpdateItemImageIndex request)
  491. {
  492. var item = _libraryManager.GetItemById(request.Id);
  493. var task = UpdateItemIndex(item, request.Type, request.Index, request.NewIndex);
  494. Task.WaitAll(task);
  495. }
  496. /// <summary>
  497. /// Updates the index of the item.
  498. /// </summary>
  499. /// <param name="item">The item.</param>
  500. /// <param name="type">The type.</param>
  501. /// <param name="currentIndex">Index of the current.</param>
  502. /// <param name="newIndex">The new index.</param>
  503. /// <returns>Task.</returns>
  504. /// <exception cref="System.ArgumentException">The change index operation is only applicable to backdrops and screenshots</exception>
  505. private Task UpdateItemIndex(BaseItem item, ImageType type, int currentIndex, int newIndex)
  506. {
  507. string file1;
  508. string file2;
  509. if (type == ImageType.Screenshot)
  510. {
  511. file1 = item.ScreenshotImagePaths[currentIndex];
  512. file2 = item.ScreenshotImagePaths[newIndex];
  513. }
  514. else if (type == ImageType.Backdrop)
  515. {
  516. file1 = item.BackdropImagePaths[currentIndex];
  517. file2 = item.BackdropImagePaths[newIndex];
  518. }
  519. else
  520. {
  521. throw new ArgumentException("The change index operation is only applicable to backdrops and screenshots");
  522. }
  523. SwapFiles(file1, file2);
  524. // Directory watchers should repeat this, but do a quick refresh first
  525. return item.RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false);
  526. }
  527. /// <summary>
  528. /// Swaps the files.
  529. /// </summary>
  530. /// <param name="file1">The file1.</param>
  531. /// <param name="file2">The file2.</param>
  532. private void SwapFiles(string file1, string file2)
  533. {
  534. var temp1 = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
  535. var temp2 = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
  536. File.Copy(file1, temp1);
  537. File.Copy(file2, temp2);
  538. File.Copy(temp1, file2, true);
  539. File.Copy(temp2, file1, true);
  540. }
  541. /// <summary>
  542. /// Gets the image.
  543. /// </summary>
  544. /// <param name="request">The request.</param>
  545. /// <param name="item">The item.</param>
  546. /// <returns>System.Object.</returns>
  547. /// <exception cref="ResourceNotFoundException">
  548. /// </exception>
  549. private object GetImage(ImageRequest request, BaseItem item)
  550. {
  551. var kernel = Kernel.Instance;
  552. var index = request.Index ?? 0;
  553. var imagePath = GetImagePath(kernel, request, item);
  554. if (string.IsNullOrEmpty(imagePath))
  555. {
  556. throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
  557. }
  558. // See if we can avoid a file system lookup by looking for the file in ResolveArgs
  559. var originalFileImageDateModified = kernel.ImageManager.GetImageDateModified(item, request.Type, index);
  560. var supportedImageEnhancers = kernel.ImageEnhancers.Where(i => i.Supports(item, request.Type)).ToList();
  561. // If the file does not exist GetLastWriteTimeUtc will return jan 1, 1601 as opposed to throwing an exception
  562. // http://msdn.microsoft.com/en-us/library/system.io.file.getlastwritetimeutc.aspx
  563. if (originalFileImageDateModified.Year == 1601 && !File.Exists(imagePath))
  564. {
  565. throw new ResourceNotFoundException(string.Format("File not found: {0}", imagePath));
  566. }
  567. var contentType = MimeTypes.GetMimeType(imagePath);
  568. var dateLastModified = (supportedImageEnhancers.Select(e => e.LastConfigurationChange(item, request.Type)).Concat(new[] { originalFileImageDateModified })).Max();
  569. var cacheGuid = kernel.ImageManager.GetImageCacheTag(imagePath, originalFileImageDateModified, supportedImageEnhancers, item, request.Type);
  570. TimeSpan? cacheDuration = null;
  571. if (!string.IsNullOrEmpty(request.Tag) && cacheGuid == new Guid(request.Tag))
  572. {
  573. cacheDuration = TimeSpan.FromDays(365);
  574. }
  575. return ToCachedResult(cacheGuid, dateLastModified, cacheDuration, () => new ImageWriter
  576. {
  577. Item = item,
  578. Request = request,
  579. CropWhiteSpace = request.Type == ImageType.Logo || request.Type == ImageType.Art,
  580. OriginalImageDateModified = originalFileImageDateModified
  581. }, contentType);
  582. }
  583. /// <summary>
  584. /// Gets the image path.
  585. /// </summary>
  586. /// <param name="kernel">The kernel.</param>
  587. /// <param name="request">The request.</param>
  588. /// <param name="item">The item.</param>
  589. /// <returns>System.String.</returns>
  590. private string GetImagePath(Kernel kernel, ImageRequest request, BaseItem item)
  591. {
  592. var index = request.Index ?? 0;
  593. return kernel.ImageManager.GetImagePath(item, request.Type, index);
  594. }
  595. /// <summary>
  596. /// Posts the image.
  597. /// </summary>
  598. /// <param name="entity">The entity.</param>
  599. /// <param name="inputStream">The input stream.</param>
  600. /// <param name="imageType">Type of the image.</param>
  601. /// <param name="mimeType">Type of the MIME.</param>
  602. /// <returns>Task.</returns>
  603. private async Task PostImage(BaseItem entity, Stream inputStream, ImageType imageType, string mimeType)
  604. {
  605. using (var reader = new StreamReader(inputStream))
  606. {
  607. var text = await reader.ReadToEndAsync().ConfigureAwait(false);
  608. var bytes = Convert.FromBase64String(text);
  609. string filename;
  610. switch (imageType)
  611. {
  612. case ImageType.Art:
  613. filename = "clearart";
  614. break;
  615. case ImageType.Primary:
  616. filename = entity is Episode ? Path.GetFileNameWithoutExtension(entity.Path) : "folder";
  617. break;
  618. case ImageType.Backdrop:
  619. filename = GetBackdropFilenameToSave(entity);
  620. break;
  621. case ImageType.Screenshot:
  622. filename = GetScreenshotFilenameToSave(entity);
  623. break;
  624. default:
  625. filename = imageType.ToString().ToLower();
  626. break;
  627. }
  628. var extension = mimeType.Split(';').First().Split('/').Last();
  629. string oldImagePath;
  630. switch (imageType)
  631. {
  632. case ImageType.Backdrop:
  633. case ImageType.Screenshot:
  634. oldImagePath = null;
  635. break;
  636. default:
  637. oldImagePath = entity.GetImage(imageType);
  638. break;
  639. }
  640. // Don't save locally if there's no parent (special feature, trailer, etc)
  641. var saveLocally = !(entity is Audio) && entity.Parent != null;
  642. if (imageType != ImageType.Primary)
  643. {
  644. if (entity is Episode)
  645. {
  646. saveLocally = false;
  647. }
  648. }
  649. var imagePath = _providerManager.GetSavePath(entity, filename + "." + extension, saveLocally);
  650. // Save to file system
  651. using (var fs = new FileStream(imagePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true))
  652. {
  653. await fs.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
  654. }
  655. if (imageType == ImageType.Screenshot)
  656. {
  657. entity.ScreenshotImagePaths.Add(imagePath);
  658. }
  659. else if (imageType == ImageType.Backdrop)
  660. {
  661. entity.BackdropImagePaths.Add(imagePath);
  662. }
  663. else
  664. {
  665. // Set the image
  666. entity.SetImage(imageType, imagePath);
  667. }
  668. // If the new and old paths are different, delete the old one
  669. if (!string.IsNullOrEmpty(oldImagePath) && !oldImagePath.Equals(imagePath, StringComparison.OrdinalIgnoreCase))
  670. {
  671. File.Delete(oldImagePath);
  672. }
  673. // Directory watchers should repeat this, but do a quick refresh first
  674. await entity.RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false).ConfigureAwait(false);
  675. }
  676. }
  677. /// <summary>
  678. /// Gets the backdrop filename to save.
  679. /// </summary>
  680. /// <param name="item">The item.</param>
  681. /// <returns>System.String.</returns>
  682. private string GetBackdropFilenameToSave(BaseItem item)
  683. {
  684. var paths = item.BackdropImagePaths.ToList();
  685. if (!paths.Any(i => string.Equals(Path.GetFileNameWithoutExtension(i), "backdrop", StringComparison.OrdinalIgnoreCase)))
  686. {
  687. return "screenshot";
  688. }
  689. var index = 1;
  690. while (paths.Any(i => string.Equals(Path.GetFileNameWithoutExtension(i), "backdrop" + index, StringComparison.OrdinalIgnoreCase)))
  691. {
  692. index++;
  693. }
  694. return "backdrop" + index;
  695. }
  696. /// <summary>
  697. /// Gets the screenshot filename to save.
  698. /// </summary>
  699. /// <param name="item">The item.</param>
  700. /// <returns>System.String.</returns>
  701. private string GetScreenshotFilenameToSave(BaseItem item)
  702. {
  703. var paths = item.ScreenshotImagePaths.ToList();
  704. if (!paths.Any(i => string.Equals(Path.GetFileNameWithoutExtension(i), "screenshot", StringComparison.OrdinalIgnoreCase)))
  705. {
  706. return "screenshot";
  707. }
  708. var index = 1;
  709. while (paths.Any(i => string.Equals(Path.GetFileNameWithoutExtension(i), "screenshot" + index, StringComparison.OrdinalIgnoreCase)))
  710. {
  711. index++;
  712. }
  713. return "screenshot" + index;
  714. }
  715. }
  716. }