mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-05 16:58:59 +00:00

- Now uses the Documents directory instead of the Caches one, to avoid losing data in low disk space. - Exisitng data is copied to the new location if needed.
213 lines
8.6 KiB
Objective-C
213 lines
8.6 KiB
Objective-C
//
|
|
// OfflineFetchImages.m
|
|
// NewsBlur
|
|
//
|
|
// Created by Samuel Clay on 7/15/13.
|
|
// Copyright (c) 2013 NewsBlur. All rights reserved.
|
|
//
|
|
|
|
#import "OfflineFetchImages.h"
|
|
#import "NewsBlurAppDelegate.h"
|
|
#import "FMDatabase.h"
|
|
#import "FMDatabaseAdditions.h"
|
|
#import "Utilities.h"
|
|
#import "NewsBlur-Swift.h"
|
|
|
|
@implementation OfflineFetchImages
|
|
@synthesize appDelegate;
|
|
|
|
- (void)main {
|
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
|
|
});
|
|
|
|
while (YES) {
|
|
BOOL fetched = [self fetchImages];
|
|
if (!fetched) break;
|
|
}
|
|
}
|
|
|
|
- (BOOL)fetchImages {
|
|
if (self.isCancelled) {
|
|
NSLog(@"Images cancelled.");
|
|
return NO;
|
|
}
|
|
|
|
NSLog(@"Fetching images...");
|
|
NSArray *urls = [self uncachedImageUrls];
|
|
|
|
if (![[[NSUserDefaults standardUserDefaults]
|
|
objectForKey:@"offline_image_download"] boolValue] ||
|
|
![[[NSUserDefaults standardUserDefaults]
|
|
objectForKey:@"offline_allowed"] boolValue] ||
|
|
[urls count] == 0) {
|
|
NSLog(@"Finished caching images. %ld total", (long)self.appDelegate.totalUncachedImagesCount);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self.appDelegate.feedsViewController showDoneNotifier];
|
|
[self.appDelegate.feedsViewController hideNotifier];
|
|
[self.appDelegate cleanImageCache];
|
|
[self.appDelegate finishBackground];
|
|
});
|
|
return NO;
|
|
}
|
|
|
|
if (![self.appDelegate isReachableForOffline]) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self.appDelegate.feedsViewController showDoneNotifier];
|
|
[self.appDelegate.feedsViewController hideNotifier];
|
|
});
|
|
return NO;
|
|
}
|
|
|
|
|
|
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
|
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
|
|
[manager.requestSerializer setTimeoutInterval:5];
|
|
manager.responseSerializer = [AFImageResponseSerializer serializer];
|
|
manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
|
|
|
|
dispatch_group_t group = dispatch_group_create();
|
|
|
|
for (NSArray *urlArray in urls) {
|
|
NSString *urlString = [[urlArray objectAtIndex:0] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
|
NSString *storyHash = [urlArray objectAtIndex:1];
|
|
NSInteger storyTimestamp = [[urlArray objectAtIndex:2] integerValue];
|
|
dispatch_group_enter(group);
|
|
// NSLog(@" ---> Fetching offline image: %@", urlString);
|
|
[manager GET:urlString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
|
|
// NSLog(@" ---> Fetched %@: %@", storyHash, urlString);
|
|
UIImage *image = (UIImage *)responseObject;
|
|
[self storeCachedImage:urlString withImage:image storyHash:storyHash storyTimestamp:storyTimestamp];
|
|
dispatch_group_leave(group);
|
|
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
|
|
// NSLog(@" ---> Failed to fetch image %@: %@", storyHash, urlString);
|
|
[self storeFailedImage:storyHash];
|
|
dispatch_group_leave(group);
|
|
}];
|
|
}
|
|
|
|
// dispatch_sync(dispatch_get_main_queue(), ^{
|
|
// [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
|
|
// });
|
|
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
|
NSLog(@"Queue finished: %ld total (%ld remaining)", (long)appDelegate.totalUncachedImagesCount, (long)appDelegate.remainingUncachedImagesCount);
|
|
[self updateProgress];
|
|
// dispatch_sync(dispatch_get_main_queue(), ^{
|
|
// [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
|
|
// });
|
|
|
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
|
// [appDelegate.feedsViewController hideNotifier];
|
|
// });
|
|
return YES;
|
|
}
|
|
|
|
- (NSArray *)uncachedImageUrls {
|
|
NSMutableArray *urls = [NSMutableArray array];
|
|
|
|
[self.appDelegate.database inDatabase:^(FMDatabase *db) {
|
|
NSString *commonQuery = @"FROM cached_images c "
|
|
"INNER JOIN unread_hashes u ON (c.story_hash = u.story_hash) "
|
|
"WHERE c.image_cached is null ";
|
|
int count = [db intForQuery:[NSString stringWithFormat:@"SELECT COUNT(1) %@", commonQuery]];
|
|
if (self.appDelegate.totalUncachedImagesCount == 0) {
|
|
self.appDelegate.totalUncachedImagesCount = count;
|
|
self.appDelegate.remainingUncachedImagesCount = self.appDelegate.totalUncachedImagesCount;
|
|
} else {
|
|
self.appDelegate.remainingUncachedImagesCount = count;
|
|
}
|
|
|
|
int limit = 120;
|
|
NSString *order;
|
|
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"default_order"] isEqualToString:@"oldest"]) {
|
|
order = @"ASC";
|
|
} else {
|
|
order = @"DESC";
|
|
}
|
|
NSString *sql = [NSString stringWithFormat:@"SELECT c.image_url, c.story_hash, u.story_timestamp %@ ORDER BY u.story_timestamp %@ LIMIT %d", commonQuery, order, limit];
|
|
FMResultSet *cursor = [db executeQuery:sql];
|
|
|
|
while ([cursor next]) {
|
|
[urls addObject:@[[cursor objectForColumnName:@"image_url"],
|
|
[cursor objectForColumnName:@"story_hash"],
|
|
[cursor objectForColumnName:@"story_timestamp"]]];
|
|
}
|
|
|
|
[cursor close];
|
|
}];
|
|
|
|
return urls;
|
|
}
|
|
|
|
- (void)updateProgress {
|
|
if (self.isCancelled) return;
|
|
|
|
NSInteger start = (NSInteger)[[NSDate date] timeIntervalSince1970];
|
|
NSInteger end = self.appDelegate.latestCachedImageDate;
|
|
NSInteger seconds = start - (end ? end : start);
|
|
__block int hours = (int)round(seconds / 60.f / 60.f);
|
|
|
|
__block float progress = 0.f;
|
|
if (self.appDelegate.totalUncachedImagesCount) {
|
|
progress = 1.f - ((float)appDelegate.remainingUncachedImagesCount /
|
|
(float)appDelegate.totalUncachedImagesCount);
|
|
}
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self.appDelegate.feedsViewController showCachingNotifier:@"Images" progress:progress hoursBack:hours];
|
|
});
|
|
}
|
|
|
|
- (void)storeCachedImage:(NSString *)imageUrl withImage:(UIImage *)image storyHash:(NSString *)storyHash storyTimestamp:(NSInteger)storyTimestamp {
|
|
if (self.isCancelled) {
|
|
NSLog(@"Image cancelled.");
|
|
return;
|
|
}
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,
|
|
(unsigned long)NULL), ^{
|
|
|
|
NSData *responseData = UIImageJPEGRepresentation(image, 0.6);
|
|
NSString *md5Url = [Utilities md5:imageUrl];
|
|
// NSLog(@"Storing image: %@ (%d bytes - %d in queue)", storyHash, [responseData length], [imageDownloadOperationQueue requestsCount]);
|
|
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
NSString *cacheDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
|
|
NSString *fullPath = [cacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", md5Url, [imageUrl pathExtension]]];
|
|
|
|
[fileManager createFileAtPath:fullPath contents:responseData attributes:nil];
|
|
|
|
[self.appDelegate.database inDatabase:^(FMDatabase *db) {
|
|
[db executeUpdate:@"UPDATE cached_images SET "
|
|
"image_cached = 1 WHERE story_hash = ?",
|
|
storyHash];
|
|
}];
|
|
|
|
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"default_order"] isEqualToString:@"oldest"]) {
|
|
if (storyTimestamp > self.appDelegate.latestCachedImageDate) {
|
|
self.appDelegate.latestCachedImageDate = storyTimestamp;
|
|
}
|
|
} else {
|
|
if (!self.appDelegate.latestCachedImageDate || storyTimestamp < self.appDelegate.latestCachedImageDate) {
|
|
self.appDelegate.latestCachedImageDate = storyTimestamp;
|
|
}
|
|
}
|
|
|
|
@synchronized (self) {
|
|
self.appDelegate.remainingUncachedImagesCount--;
|
|
if (self.appDelegate.remainingUncachedImagesCount % 10 == 0) {
|
|
[self updateProgress];
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)storeFailedImage:(NSString *)storyHash {
|
|
[self.appDelegate.database inDatabase:^(FMDatabase *db) {
|
|
[db executeUpdate:@"UPDATE cached_images SET "
|
|
"image_cached = 1, failed = 1 WHERE story_hash = ?",
|
|
storyHash];
|
|
}];
|
|
}
|
|
|
|
@end
|