VLCThumbnailsCache.m 6.89 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * VLCThumbnailsCache.m
 * VLC for iOS
 *****************************************************************************
Felix Paul Kühne's avatar
Felix Paul Kühne committed
5
 * Copyright (c) 2013-2014 VideoLAN. All rights reserved.
6
7
8
9
10
11
12
 * $Id$
 *
 * Authors: Gleb Pinigin <gpinigin # gmail.com>
 *          Felix Paul Kühne <fkuehne # videolan.org>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
13
14

#import "VLCThumbnailsCache.h"
15
#import <CommonCrypto/CommonDigest.h>
16
17

static NSInteger MaxCacheSize;
18
static NSCache *_thumbnailCache;
19
20
21
22
23
24
25
26
27
28
29

@implementation VLCThumbnailsCache

#define MAX_CACHE_SIZE_IPHONE 21  // three times the number of items shown on iPhone 5
#define MAX_CACHE_SIZE_IPAD   27  // three times the number of items shown on iPad

+(void)initialize
{
    MaxCacheSize = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)?
                                MAX_CACHE_SIZE_IPAD: MAX_CACHE_SIZE_IPHONE;

30
31
    _thumbnailCache = [[NSCache alloc] init];
    [_thumbnailCache setCountLimit: MaxCacheSize];
32
33
}

34
35
36
37
+ (NSString *)_md5FromString:(NSString *)string
{
    const char *ptr = [string UTF8String];
    unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
38
    CC_MD5(ptr, (unsigned int)strlen(ptr), md5Buffer);
39
40
41
42
43
44
45
    NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x",md5Buffer[i]];

    return [NSString stringWithString:output];
}

46
+ (UIImage *)thumbnailForMediaItemWithTitle:(NSString *)title Artist:(NSString*)artist andAlbumName:(NSString*)albumname
47
{
48
49
    return [UIImage imageWithContentsOfFile:[self artworkPathForMediaItemWithTitle:title Artist:artist andAlbumName:albumname]];
}
50

51
52
53
+ (NSString *)artworkPathForMediaItemWithTitle:(NSString *)title Artist:(NSString*)artist andAlbumName:(NSString*)albumname
{
    NSString *artworkURL;
54
55
56
57
    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *cacheDir = searchPaths[0];
    cacheDir = [cacheDir stringByAppendingFormat:@"/%@", [[NSBundle mainBundle] bundleIdentifier]];

58
    if (artist.length == 0 || albumname.length == 0) {
59
60
61
62
        /* Use generated hash to find art */
        artworkURL = [cacheDir stringByAppendingFormat:@"/art/arturl/%@/art.jpg", [self _md5FromString:title]];
    } else {
        /* Otherwise, it was cached by artist and album */
63
        artworkURL = [cacheDir stringByAppendingFormat:@"/art/artistalbum/%@/%@/art.jpg", artist, albumname];
64
65
66
67
68
    }

    return artworkURL;
}

69
70
71
72
73
74
75
76
77
78
79
80
81
+ (NSString *)_getArtworkPathFromMedia:(MLFile *)file
{
    NSString *artist, *album, *title;

    if (file.isAlbumTrack) {
        artist = file.albumTrack.artist;
        album = file.albumTrack.album.name;
    }
    title = file.title;

    return [self artworkPathForMediaItemWithTitle:title Artist:artist andAlbumName:album];
}

82
83
84
85
86
87
+ (UIImage *)thumbnailForMediaFile:(MLFile *)mediaFile
{
    if (mediaFile == nil || mediaFile.objectID == nil)
        return nil;

    NSManagedObjectID *objID = mediaFile.objectID;
88
89
90
91
92
    UIImage *displayedImage = [_thumbnailCache objectForKey:objID];

    if (displayedImage)
        return displayedImage;

93
94
95
96
97
98
    if (mediaFile.isAlbumTrack || mediaFile.isShowEpisode)
        displayedImage = [UIImage imageWithContentsOfFile:[self _getArtworkPathFromMedia:mediaFile]];

    if (!displayedImage)
        displayedImage = mediaFile.computedThumbnail;

99
100
    if (displayedImage)
        [_thumbnailCache setObject:displayedImage forKey:objID];
101
102
103
104

    return displayedImage;
}

105
+ (UIImage *)thumbnailForShow:(MLShow *)mediaShow forceRefresh:(BOOL)forceRefresh
106
107
{
    NSManagedObjectID *objID = mediaShow.objectID;
108
    UIImage *displayedImage;
109

110
111
112
113
114
    if (!forceRefresh) {
        displayedImage = [_thumbnailCache objectForKey:objID];
        if (displayedImage)
            return displayedImage;
    }
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

    NSUInteger count = [mediaShow.episodes count];
    NSUInteger fileNumber = count > 3 ? 3 : count;
    NSArray *episodes = [mediaShow.episodes allObjects];
    NSMutableArray *files = [[NSMutableArray alloc] init];
    for (NSUInteger x = 0; x < count; x++)
        [files addObject:[episodes[x] files].anyObject];

    displayedImage = [self clusterThumbFromFiles:files andNumber:fileNumber];
    if (displayedImage)
        [_thumbnailCache setObject:displayedImage forKey:objID];

    return displayedImage;
}

130
+ (UIImage *)thumbnailForLabel:(MLLabel *)mediaLabel forceRefresh:(BOOL)forceRefresh
131
132
{
    NSManagedObjectID *objID = mediaLabel.objectID;
133
    UIImage *displayedImage;
134

135
136
137
138
139
    if (!forceRefresh) {
        displayedImage = [_thumbnailCache objectForKey:objID];
        if (displayedImage)
            return displayedImage;
    }
140

141
142
    NSUInteger count = [mediaLabel.files count];
    NSUInteger fileNumber = count > 3 ? 3 : count;
143
    NSArray *files = [mediaLabel.files allObjects];
144
145
146
147
148
149
    displayedImage = [self clusterThumbFromFiles:files andNumber:fileNumber];
    if (displayedImage)
        [_thumbnailCache setObject:displayedImage forKey:objID];

    return displayedImage;
}
150

151
152
153
+ (UIImage *)clusterThumbFromFiles:(NSArray *)files andNumber:(NSUInteger)fileNumber
{
    UIImage *clusterThumb;
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
    CGSize imageSize;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        if ([UIScreen mainScreen].scale==2.0)
            imageSize = CGSizeMake(540., 405.);
        else
            imageSize = CGSizeMake(272, 204.);
    } else {
        if (SYSTEM_RUNS_IOS7_OR_LATER)
            imageSize = CGSizeMake(480., 270.);
        else {
            if ([UIScreen mainScreen].scale==2.0)
                imageSize = CGSizeMake(480., 270.);
            else
                imageSize = CGSizeMake(540., 405.);
        }
    }
170
171
172
173

    UIGraphicsBeginImageContext(imageSize);
    for (NSUInteger i = 0; i < fileNumber; i++) {
        MLFile *file =  [files objectAtIndex:i];
174
        clusterThumb = [VLCThumbnailsCache thumbnailForMediaFile:file];
175
176
177
178
179
180
181
182
183
184
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGFloat imagePartWidth = (imageSize.width / fileNumber);
        //the rect in which the image should be drawn
        CGRect clippingRect = CGRectMake(imagePartWidth * i, 0, imagePartWidth, imageSize.height);
        CGContextSaveGState(context);
        CGContextClipToRect(context, clippingRect);
        //take the center of the clippingRect and calculate the offset from the original center
        CGFloat centerOffset = (imagePartWidth * i + imagePartWidth / 2) - imageSize.width / 2;
        //shift the rect to draw the middle of the image in the clippingrect
        CGRect drawingRect = CGRectMake(centerOffset, 0, imageSize.width, imageSize.height);
185
        [clusterThumb drawInRect:drawingRect];
186
187
188
        //get rid of the old clippingRect
        CGContextRestoreGState(context);
    }
189
    clusterThumb = UIGraphicsGetImageFromCurrentImageContext();
190
191
    UIGraphicsEndImageContext();

192
    return clusterThumb;
193
194
}

195
@end