VLCThumbnailsCache.m 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*****************************************************************************
  2. * VLCThumbnailsCache.m
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2013-2014 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Authors: Gleb Pinigin <gpinigin # gmail.com>
  9. * Felix Paul Kühne <fkuehne # videolan.org>
  10. *
  11. * Refer to the COPYING file of the official project for license.
  12. *****************************************************************************/
  13. #import "VLCThumbnailsCache.h"
  14. #import <CommonCrypto/CommonDigest.h>
  15. static NSInteger MaxCacheSize;
  16. static NSCache *_thumbnailCache;
  17. @implementation VLCThumbnailsCache
  18. #define MAX_CACHE_SIZE_IPHONE 21 // three times the number of items shown on iPhone 5
  19. #define MAX_CACHE_SIZE_IPAD 27 // three times the number of items shown on iPad
  20. +(void)initialize
  21. {
  22. MaxCacheSize = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)?
  23. MAX_CACHE_SIZE_IPAD: MAX_CACHE_SIZE_IPHONE;
  24. _thumbnailCache = [[NSCache alloc] init];
  25. [_thumbnailCache setCountLimit: MaxCacheSize];
  26. }
  27. + (NSString *)_md5FromString:(NSString *)string
  28. {
  29. const char *ptr = [string UTF8String];
  30. unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
  31. CC_MD5(ptr, (unsigned int)strlen(ptr), md5Buffer);
  32. NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
  33. for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
  34. [output appendFormat:@"%02x",md5Buffer[i]];
  35. return [NSString stringWithString:output];
  36. }
  37. + (UIImage *)thumbnailForMediaItemWithTitle:(NSString *)title Artist:(NSString*)artist andAlbumName:(NSString*)albumname
  38. {
  39. return [UIImage imageWithContentsOfFile:[self artworkPathForMediaItemWithTitle:title Artist:artist andAlbumName:albumname]];
  40. }
  41. + (NSString *)artworkPathForMediaItemWithTitle:(NSString *)title Artist:(NSString*)artist andAlbumName:(NSString*)albumname
  42. {
  43. NSString *artworkURL;
  44. NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  45. NSString *cacheDir = searchPaths[0];
  46. cacheDir = [cacheDir stringByAppendingFormat:@"/%@", [[NSBundle mainBundle] bundleIdentifier]];
  47. if (artist.length == 0 || albumname.length == 0) {
  48. /* Use generated hash to find art */
  49. artworkURL = [cacheDir stringByAppendingFormat:@"/art/arturl/%@/art.jpg", [self _md5FromString:title]];
  50. } else {
  51. /* Otherwise, it was cached by artist and album */
  52. artworkURL = [cacheDir stringByAppendingFormat:@"/art/artistalbum/%@/%@/art.jpg", artist, albumname];
  53. }
  54. return artworkURL;
  55. }
  56. + (NSString *)_getArtworkPathFromMedia:(MLFile *)file
  57. {
  58. NSString *artist, *album, *title;
  59. if (file.isAlbumTrack) {
  60. artist = file.albumTrack.artist;
  61. album = file.albumTrack.album.name;
  62. }
  63. title = file.title;
  64. return [self artworkPathForMediaItemWithTitle:title Artist:artist andAlbumName:album];
  65. }
  66. + (UIImage *)thumbnailForMediaFile:(MLFile *)mediaFile
  67. {
  68. if (mediaFile == nil || mediaFile.objectID == nil)
  69. return nil;
  70. NSManagedObjectID *objID = mediaFile.objectID;
  71. UIImage *displayedImage = [_thumbnailCache objectForKey:objID];
  72. if (displayedImage)
  73. return displayedImage;
  74. if (mediaFile.isAlbumTrack || mediaFile.isShowEpisode)
  75. displayedImage = [UIImage imageWithContentsOfFile:[self _getArtworkPathFromMedia:mediaFile]];
  76. if (!displayedImage)
  77. displayedImage = mediaFile.computedThumbnail;
  78. if (displayedImage)
  79. [_thumbnailCache setObject:displayedImage forKey:objID];
  80. return displayedImage;
  81. }
  82. + (UIImage *)thumbnailForShow:(MLShow *)mediaShow forceRefresh:(BOOL)forceRefresh
  83. {
  84. NSManagedObjectID *objID = mediaShow.objectID;
  85. UIImage *displayedImage;
  86. if (!forceRefresh) {
  87. displayedImage = [_thumbnailCache objectForKey:objID];
  88. if (displayedImage)
  89. return displayedImage;
  90. }
  91. NSUInteger count = [mediaShow.episodes count];
  92. NSUInteger fileNumber = count > 3 ? 3 : count;
  93. NSArray *episodes = [mediaShow.episodes allObjects];
  94. NSMutableArray *files = [[NSMutableArray alloc] init];
  95. for (NSUInteger x = 0; x < count; x++)
  96. [files addObject:[episodes[x] files].anyObject];
  97. displayedImage = [self clusterThumbFromFiles:files andNumber:fileNumber];
  98. if (displayedImage)
  99. [_thumbnailCache setObject:displayedImage forKey:objID];
  100. return displayedImage;
  101. }
  102. + (UIImage *)thumbnailForLabel:(MLLabel *)mediaLabel forceRefresh:(BOOL)forceRefresh
  103. {
  104. NSManagedObjectID *objID = mediaLabel.objectID;
  105. UIImage *displayedImage;
  106. if (!forceRefresh) {
  107. displayedImage = [_thumbnailCache objectForKey:objID];
  108. if (displayedImage)
  109. return displayedImage;
  110. }
  111. NSUInteger count = [mediaLabel.files count];
  112. NSUInteger fileNumber = count > 3 ? 3 : count;
  113. NSArray *files = [mediaLabel.files allObjects];
  114. displayedImage = [self clusterThumbFromFiles:files andNumber:fileNumber];
  115. if (displayedImage)
  116. [_thumbnailCache setObject:displayedImage forKey:objID];
  117. return displayedImage;
  118. }
  119. + (UIImage *)clusterThumbFromFiles:(NSArray *)files andNumber:(NSUInteger)fileNumber
  120. {
  121. UIImage *clusterThumb;
  122. CGSize imageSize;
  123. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  124. if ([UIScreen mainScreen].scale==2.0)
  125. imageSize = CGSizeMake(540., 405.);
  126. else
  127. imageSize = CGSizeMake(272, 204.);
  128. } else {
  129. if (SYSTEM_RUNS_IOS7_OR_LATER)
  130. imageSize = CGSizeMake(480., 270.);
  131. else {
  132. if ([UIScreen mainScreen].scale==2.0)
  133. imageSize = CGSizeMake(480., 270.);
  134. else
  135. imageSize = CGSizeMake(540., 405.);
  136. }
  137. }
  138. UIGraphicsBeginImageContext(imageSize);
  139. for (NSUInteger i = 0; i < fileNumber; i++) {
  140. MLFile *file = [files objectAtIndex:i];
  141. clusterThumb = [VLCThumbnailsCache thumbnailForMediaFile:file];
  142. CGContextRef context = UIGraphicsGetCurrentContext();
  143. CGFloat imagePartWidth = (imageSize.width / fileNumber);
  144. //the rect in which the image should be drawn
  145. CGRect clippingRect = CGRectMake(imagePartWidth * i, 0, imagePartWidth, imageSize.height);
  146. CGContextSaveGState(context);
  147. CGContextClipToRect(context, clippingRect);
  148. //take the center of the clippingRect and calculate the offset from the original center
  149. CGFloat centerOffset = (imagePartWidth * i + imagePartWidth / 2) - imageSize.width / 2;
  150. //shift the rect to draw the middle of the image in the clippingrect
  151. CGRect drawingRect = CGRectMake(centerOffset, 0, imageSize.width, imageSize.height);
  152. [clusterThumb drawInRect:drawingRect];
  153. //get rid of the old clippingRect
  154. CGContextRestoreGState(context);
  155. }
  156. clusterThumb = UIGraphicsGetImageFromCurrentImageContext();
  157. UIGraphicsEndImageContext();
  158. return clusterThumb;
  159. }
  160. @end