Commit d773cda8 authored by Pierre's avatar Pierre

Disable thumbnailing for crashy files.

parent 479f9bf6
......@@ -47,6 +47,9 @@ extern NSString *kMLFileTypeTVShowEpisode;
@property (nonatomic, retain) NSNumber *isOnDisk;
@property (nonatomic, retain) NSNumber *duration;
@property (nonatomic, assign) BOOL isSafe;
@property (nonatomic, assign) BOOL isBeingParsed;
/**
* the data in this object are about to be put on screen
*
......
......@@ -24,6 +24,8 @@
- (NSFetchRequest *)fetchRequestForEntity:(NSString *)entity;
- (id)createObjectForEntity:(NSString *)entity;
- (void)applicationWillExit;
- (void)save;
- (void)libraryDidDisappear;
- (void)libraryDidAppear;
......
......@@ -29,6 +29,7 @@
6320367E11F76EA9002861C2 /* MLTVShowInfoGrabber.m in Sources */ = {isa = PBXBuildFile; fileRef = 6320365C11F76EA9002861C2 /* MLTVShowInfoGrabber.m */; };
6320367F11F76EA9002861C2 /* MLURLConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 6320365D11F76EA9002861C2 /* MLURLConnection.m */; };
6320368011F76EA9002861C2 /* NSXMLNode_Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6320365E11F76EA9002861C2 /* NSXMLNode_Additions.m */; };
63AC766C1247DBC500C4E4B8 /* MLCrashPreventer.m in Sources */ = {isa = PBXBuildFile; fileRef = 63AC766B1247DBC500C4E4B8 /* MLCrashPreventer.m */; };
63C092E911F77CF200A824BC /* MediaLibrary.xcdatamodel in Sources */ = {isa = PBXBuildFile; fileRef = 63C092E811F77CF200A824BC /* MediaLibrary.xcdatamodel */; };
63C093DB11F78D8100A824BC /* MLThumbnailerQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 63C093D911F78D8100A824BC /* MLThumbnailerQueue.m */; };
63CD005D12109CD100414314 /* MLFileParserQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CD005C12109CD100414314 /* MLFileParserQueue.m */; };
......@@ -114,6 +115,8 @@
6320368F11F76EC3002861C2 /* MLLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLLabel.h; sourceTree = "<group>"; };
6320369011F76EC3002861C2 /* MLMediaLibrary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLMediaLibrary.h; sourceTree = "<group>"; };
6320369111F76EC3002861C2 /* MLShow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLShow.h; sourceTree = "<group>"; };
63AC766A1247DBC500C4E4B8 /* MLCrashPreventer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLCrashPreventer.h; sourceTree = "<group>"; };
63AC766B1247DBC500C4E4B8 /* MLCrashPreventer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLCrashPreventer.m; sourceTree = "<group>"; };
63C0920A11F776D300A824BC /* MLShowEpisode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLShowEpisode.h; sourceTree = "<group>"; };
63C092E811F77CF200A824BC /* MediaLibrary.xcdatamodel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = wrapper.xcdatamodel; path = MediaLibrary.xcdatamodel; sourceTree = SOURCE_ROOT; };
63C093D811F78D8100A824BC /* MLThumbnailerQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLThumbnailerQueue.h; sourceTree = "<group>"; };
......@@ -265,6 +268,8 @@
63C093D911F78D8100A824BC /* MLThumbnailerQueue.m */,
63CD005B12109CD100414314 /* MLFileParserQueue.h */,
63CD005C12109CD100414314 /* MLFileParserQueue.m */,
63AC766A1247DBC500C4E4B8 /* MLCrashPreventer.h */,
63AC766B1247DBC500C4E4B8 /* MLCrashPreventer.m */,
63C092E811F77CF200A824BC /* MediaLibrary.xcdatamodel */,
);
path = Sources;
......@@ -366,7 +371,14 @@
isa = PBXProject;
buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "MobileMediaLibraryKit" */;
compatibilityVersion = "Xcode 3.1";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
);
mainGroup = 0867D691FE84028FC02AAC07 /* MobileMediaLibraryKit */;
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
projectDirPath = "";
......@@ -407,6 +419,7 @@
63C092E911F77CF200A824BC /* MediaLibrary.xcdatamodel in Sources */,
63C093DB11F78D8100A824BC /* MLThumbnailerQueue.m in Sources */,
63CD005D12109CD100414314 /* MLFileParserQueue.m in Sources */,
63AC766C1247DBC500C4E4B8 /* MLCrashPreventer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -471,7 +484,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
OTHER_LDFLAGS = "-ObjC";
PREBINDING = NO;
SDKROOT = iphoneos3.2;
SDKROOT = iphoneos4.2;
};
name = Debug;
};
......@@ -484,7 +497,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
OTHER_LDFLAGS = "-ObjC";
PREBINDING = NO;
SDKROOT = iphoneos3.2;
SDKROOT = iphoneos4.2;
};
name = Release;
};
......
//
// MLCrashPreventer.h
// MobileMediaLibraryKit
//
// Created by Pierre d'Herbemont on 9/20/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
@class MLFile;
@interface MLCrashPreventer : NSObject {
NSMutableArray *_parsedFiles;
}
+ (id)sharedPreventer;
- (void)cancelAllFileParse;
- (void)markCrasherFiles;
- (BOOL)isFileSafe:(MLFile *)file;
- (void)willParseFile:(MLFile *)file;
- (void)didParseFile:(MLFile *)file;
@end
//
// MLCrashPreventer.m
// MobileMediaLibraryKit
//
// Created by Pierre d'Herbemont on 9/20/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "MLCrashPreventer.h"
#import "MLThumbnailerQueue.h"
#import "MLFileParserQueue.h"
#import "MLCrashPreventer.h"
#import "MLFile.h"
#import "MLMediaLibrary.h"
@implementation MLCrashPreventer
+ (id)sharedPreventer
{
static MLCrashPreventer *crashPreventer;
if (!crashPreventer)
crashPreventer = [[MLCrashPreventer alloc] init];
// Use the same queue for the two objects, because we wan't to track accurately
// which operation causes a crash.
[MLThumbnailerQueue sharedThumbnailerQueue].queue = [MLFileParserQueue sharedFileParserQueue].queue;
return crashPreventer;
}
- (id)init
{
self = [super init];
if (self) {
_parsedFiles = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc
{
NSAssert([_parsedFiles count] == 0, @"You should call -cancelAllFileParse before releasing");
[_parsedFiles release];
[super dealloc];
}
- (void)cancelAllFileParse
{
MLLog(@"Cancelling file parsing");
for (MLFile *file in _parsedFiles)
file.isBeingParsed = NO;
[_parsedFiles removeAllObjects];
[[MLMediaLibrary sharedMediaLibrary] save];
}
- (void)markCrasherFiles
{
for (MLFile *file in [MLFile allFiles]) {
if ([file isBeingParsed]) {
file.isSafe = NO;
file.isBeingParsed = NO;
}
}
}
- (BOOL)isFileSafe:(MLFile *)file
{
return file.isSafe;
}
- (void)willParseFile:(MLFile *)file
{
NSAssert([MLThumbnailerQueue sharedThumbnailerQueue].queue == [MLFileParserQueue sharedFileParserQueue].queue, @"");
NSAssert([_parsedFiles count] < 1, @"Parsing multiple files at the same time. Crash preventer can't work accuratly.");
file.isBeingParsed = YES;
// Force to save the media library in case of crash.
[[MLMediaLibrary sharedMediaLibrary] save];
[_parsedFiles addObject:file];
}
- (void)didParseFile:(MLFile *)file
{
file.isBeingParsed = NO;
[_parsedFiles removeObject:file];
}
@end
......@@ -21,6 +21,11 @@ NSString *kMLFileTypeTVShowEpisode = @"tvShowEpisode";
@implementation MLFile
- (NSString *)description
{
return [NSString stringWithFormat:@"<MLFile title='%@'>", [self title]];
}
+ (NSArray *)allFiles
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
......@@ -32,8 +37,12 @@ NSString *kMLFileTypeTVShowEpisode = @"tvShowEpisode";
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:descriptor]];
NSArray *movies = [moc executeFetchRequest:request error:nil];
NSError *error;
NSArray *movies = [moc executeFetchRequest:request error:&error];
[request release];
if (!movies) {
NSLog(@"WARNING: %@", error);
}
return movies;
}
......@@ -100,6 +109,37 @@ NSString *kMLFileTypeTVShowEpisode = @"tvShowEpisode";
@dynamic isOnDisk;
@dynamic duration;
- (BOOL)isSafe
{
[self willAccessValueForKey:@"isSafe"];
NSNumber *ret = [self primitiveValueForKey:@"isSafe"];
[self didAccessValueForKey:@"isSafe"];
return [ret boolValue];
}
- (void)setIsSafe:(BOOL)isSafe
{
[self willChangeValueForKey:@"isSafe"];
[self setPrimitiveValue:[NSNumber numberWithBool:isSafe] forKey:@"isSafe"];
[self willChangeValueForKey:@"isSafe"];
}
- (BOOL)isBeingParsed
{
[self willAccessValueForKey:@"isBeingParsed"];
NSNumber *ret = [self primitiveValueForKey:@"isBeingParsed"];
[self didAccessValueForKey:@"isBeingParsed"];
return [ret boolValue];
}
- (void)setIsBeingParsed:(BOOL)isBeingParsed
{
[self willChangeValueForKey:@"isBeingParsed"];
[self setPrimitiveValue:[NSNumber numberWithBool:isBeingParsed] forKey:@"isBeingParsed"];
[self willChangeValueForKey:@"isBeingParsed"];
}
- (void)willDisplay
{
[[MLThumbnailerQueue sharedThumbnailerQueue] setHighPriorityForFile:self];
......
......@@ -10,8 +10,9 @@
@class MLFile;
@interface MLFileParserQueue : NSOperationQueue {
@interface MLFileParserQueue : NSObject {
NSDictionary *_fileDescriptionToOperation;
NSOperationQueue *_queue;
}
+ (MLFileParserQueue *)sharedFileParserQueue;
- (void)addFile:(MLFile *)file;
......@@ -20,4 +21,6 @@
- (void)stop;
- (void)resume;
@property (nonatomic, retain) NSOperationQueue *queue;
@end
......@@ -9,6 +9,7 @@
#import "MLFileParserQueue.h"
#import "MLFile.h"
#import "MLMediaLibrary.h"
#import "MLCrashPreventer.h"
@interface MLParsingOperation : NSOperation
{
......@@ -42,14 +43,18 @@
- (void)parse
{
NSAssert(!_media, @"We are already parsing");
MLLog(@"Starting parsing %@", self.file);
[[MLCrashPreventer sharedPreventer] willParseFile:self.file];
_media = [[VLCMedia mediaWithURL:[NSURL URLWithString:self.file.url]] retain];
_media.delegate = self;
[_media parse];
MLFileParserQueue *parserQueue = [MLFileParserQueue sharedFileParserQueue];
[parserQueue setSuspended:YES]; // Balanced in -mediaDidFinishParsing
[parserQueue didFinishOperation:self];
[parserQueue.queue setSuspended:YES]; // Balanced in -mediaDidFinishParsing
[self retain]; // Balanced in -mediaDidFinishParsing:
}
- (void)main
{
[self performSelectorOnMainThread:@selector(parse) withObject:nil waitUntilDone:YES];
......@@ -83,20 +88,24 @@
// NSAssert([[self.file tracks] count] == 0, @"Reparsing a file with existing tracks"); // Don't assert here as we may want to re-parse, after all
[self.file setTracks:tracksSet];
[self.file setDuration:[[_media length] numberValue]];
[[MLFileParserQueue sharedFileParserQueue] setSuspended:NO];
MLFileParserQueue *parserQueue = [MLFileParserQueue sharedFileParserQueue];
[[MLCrashPreventer sharedPreventer] didParseFile:self.file];
[parserQueue.queue setSuspended:NO];
[parserQueue didFinishOperation:self];
[_media autorelease];
_media = nil;
[self release];
}
@end
@implementation MLFileParserQueue
@synthesize queue=_queue;
+ (MLFileParserQueue *)sharedFileParserQueue
{
static MLFileParserQueue *shared = nil;
if (!shared) {
shared = [[MLFileParserQueue alloc] init];
[shared setMaxConcurrentOperationCount:1];
}
return shared;
}
......@@ -106,12 +115,15 @@
self = [super init];
if (self != nil) {
_fileDescriptionToOperation = [[NSMutableDictionary alloc] init];
_queue = [[NSOperationQueue alloc] init];
[_queue setMaxConcurrentOperationCount:1];
}
return self;
}
- (void)dealloc
{
[_queue release];
[_fileDescriptionToOperation release];
[super dealloc];
}
......@@ -129,19 +141,25 @@ static inline NSString *hashFromFile(MLFile *file)
- (void)addFile:(MLFile *)file
{
if ([_fileDescriptionToOperation objectForKey:hashFromFile(file)])
return;
if (![[MLCrashPreventer sharedPreventer] isFileSafe:file]) {
MLLog(@"%@ is unsafe and will crash, ignoring", file);
return;
}
MLParsingOperation *op = [[MLParsingOperation alloc] initWithFile:file];
[_fileDescriptionToOperation setValue:op forKey:hashFromFile(file)];
[self addOperation:op];
[self.queue addOperation:op];
}
- (void)stop
{
[self setMaxConcurrentOperationCount:0];
[_queue setMaxConcurrentOperationCount:0];
}
- (void)resume
{
[self setMaxConcurrentOperationCount:1];
[_queue setMaxConcurrentOperationCount:1];
}
- (void)setHighPriorityForFile:(MLFile *)file
......
......@@ -17,6 +17,7 @@
#import "MLThumbnailerQueue.h"
#import "MLShow.h"
#import "MLFileParserQueue.h"
#import "MLCrashPreventer.h"
#define DEBUG 1
......@@ -43,7 +44,10 @@ static NSString *kLastTVDBUpdateServerTime = @"MLLastTVDBUpdateServerTime";
if (!sharedMediaLibrary) {
sharedMediaLibrary = [[[self class] alloc] init];
MLLog(@"Initializing db in %@", [sharedMediaLibrary databaseFolderPath]);
[sharedMediaLibrary updateDatabase];
// Also force to init the crash preventer
// Because it will correctly set up the parser and thumbnail queue
[MLCrashPreventer sharedPreventer];
}
return sharedMediaLibrary;
}
......@@ -596,15 +600,20 @@ static NSString *kLastTVDBUpdateServerTime = @"MLLastTVDBUpdateServerTime";
NSFetchRequest *request = [self fetchRequestForEntity:@"File"];
NSArray *results = [[self managedObjectContext] executeFetchRequest:request error:nil];
NSFileManager *fileManager = [NSFileManager defaultManager];
for (MLFile *file in results) {
NSString *urlString = [file url];
NSURL *fileURL = [NSURL URLWithString:urlString];
BOOL exists = [fileManager fileExistsAtPath:[fileURL path]];
if (!exists)
if (!exists) {
MLLog(@"Marking - %@", [fileURL absoluteString]);
file.isSafe = YES; // It doesn't exists, it's safe.
}
file.isOnDisk = [NSNumber numberWithBool:exists];
}
[[MLCrashPreventer sharedPreventer] markCrasherFiles];
// Get the file to parse
request = [self fetchRequestForEntity:@"File"];
[request setPredicate:[NSPredicate predicateWithFormat:@"isOnDisk == YES && tracks.@count == 0"]];
......@@ -663,6 +672,11 @@ static NSString *kLastTVDBUpdateServerTime = @"MLLastTVDBUpdateServerTime";
[self performSelector:@selector(updateDatabase) withObject:nil afterDelay:60 * 60];
}
- (void)applicationWillExit
{
[[MLCrashPreventer sharedPreventer] cancelAllFileParse];
}
- (void)libraryDidDisappear
{
// Stop expansive work
......
......@@ -10,14 +10,18 @@
@class MLFile;
@interface MLThumbnailerQueue : NSOperationQueue {
@interface MLThumbnailerQueue : NSObject {
NSDictionary *_fileDescriptionToOperation;
NSOperationQueue *_queue;
}
+ (MLThumbnailerQueue *)sharedThumbnailerQueue;
- (void)addFile:(MLFile *)file;
- (void)setHighPriorityForFile:(MLFile *)file;
- (void)setDefaultPriorityForFile:(MLFile *)file;
- (void)stop;
- (void)resume;
@property (nonatomic, retain) NSOperationQueue *queue;
@end
......@@ -6,11 +6,13 @@
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "MLThumbnailerQueue.h"
#import "VLCMediaThumbnailer.h"
#import "VLCMedia.h"
#import "MLFile.h"
#import <UIKit/UIKit.h>
#import "MLCrashPreventer.h"
@interface ThumbnailOperation : NSOperation <VLCMediaThumbnailerDelegate>
......@@ -42,11 +44,14 @@
- (void)fetchThumbnail
{
MLLog(@"Starting THUMB %@", self.file);
[[MLCrashPreventer sharedPreventer] willParseFile:self.file];
VLCMedia *media = [VLCMedia mediaWithURL:[NSURL URLWithString:self.file.url]];
VLCMediaThumbnailer *thumbnailer = [VLCMediaThumbnailer thumbnailerWithMedia:media andDelegate:self];
[thumbnailer fetchThumbnail];
[[MLThumbnailerQueue sharedThumbnailerQueue] setSuspended:YES]; // Balanced in -mediaThumbnailer:didFinishThumbnail
[[MLThumbnailerQueue sharedThumbnailerQueue] didFinishOperation:self];
[[MLThumbnailerQueue sharedThumbnailerQueue].queue setSuspended:YES]; // Balanced in -mediaThumbnailer:didFinishThumbnail
[self retain]; // Balanced in -mediaThumbnailer:didFinishThumbnail:
}
- (void)main
......@@ -59,18 +64,22 @@
mediaThumbnailer.delegate = nil;
MLLog(@"Finished thumbnail for %@", self.file.title);
self.file.computedThumbnail = UIImagePNGRepresentation([UIImage imageWithCGImage:thumbnail]);
[[MLThumbnailerQueue sharedThumbnailerQueue] setSuspended:NO];
[[MLCrashPreventer sharedPreventer] didParseFile:self.file];
MLThumbnailerQueue *thumbnailer = [MLThumbnailerQueue sharedThumbnailerQueue];
[thumbnailer.queue setSuspended:NO];
[thumbnailer didFinishOperation:self];
[self release];
}
@end
@implementation MLThumbnailerQueue
@synthesize queue=_queue;
+ (MLThumbnailerQueue *)sharedThumbnailerQueue
{
static MLThumbnailerQueue *shared = nil;
if (!shared) {
shared = [[MLThumbnailerQueue alloc] init];
[shared setMaxConcurrentOperationCount:1];
}
return shared;
}
......@@ -80,12 +89,15 @@
self = [super init];
if (self != nil) {
_fileDescriptionToOperation = [[NSMutableDictionary alloc] init];
_queue = [[NSOperationQueue alloc] init];
[_queue setMaxConcurrentOperationCount:1];
}
return self;
}
- (void)dealloc
{
[_queue release];
[_fileDescriptionToOperation release];
[super dealloc];
}
......@@ -103,19 +115,25 @@ static inline NSString *hashFromFile(MLFile *file)
- (void)addFile:(MLFile *)file
{
if ([_fileDescriptionToOperation objectForKey:hashFromFile(file)])
return;
if (![[MLCrashPreventer sharedPreventer] isFileSafe:file]) {
NSLog(@"'%@' is unsafe and will crash, ignoring", file.title);
return;
}
ThumbnailOperation *op = [[ThumbnailOperation alloc] initWithFile:file];
[_fileDescriptionToOperation setValue:op forKey:hashFromFile(file)];
[self addOperation:op];
[self.queue addOperation:op];
}
- (void)stop
{
[self setMaxConcurrentOperationCount:0];
[_queue setMaxConcurrentOperationCount:0];
}
- (void)resume
{
[self setMaxConcurrentOperationCount:1];
[_queue setMaxConcurrentOperationCount:1];
}
- (void)setHighPriorityForFile:(MLFile *)file
......
......@@ -60,7 +60,10 @@ static inline NSNumber *numberFromTwoChars(char high, char low)
+ (NSDictionary *)tvShowEpisodeInfoFromString:(NSString *)string
{
const char *str = [[string lowercaseString] UTF8String];
if (!string)
return nil;
NSString *lowercaseString = [string lowercaseString];
const char *str = [string UTF8String];
// Search for s01e10.
for (unsigned i = 0; str[i]; i++) {
......@@ -73,7 +76,7 @@ static inline NSNumber *numberFromTwoChars(char high, char low)
{
NSNumber *season = numberFromTwoChars(str[i+1], str[i+2]);
NSNumber *episode = numberFromTwoChars(str[i+4], str[i+5]);
NSString *tvShowName = i > 0 ? [[string lowercaseString] substringToIndex:i-1] : nil;
NSString *tvShowName = i > 0 ? [lowercaseString substringToIndex:i-1] : nil;
tvShowName = tvShowName ? [[MLTitleDecrapifier decrapify:tvShowName] capitalizedString] : nil;
return [NSDictionary dictionaryWithObjectsAndKeys:season, @"season", episode, @"episode", tvShowName, @"tvShowName", nil];
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment