Skip to content

Commit

Permalink
Better safeguard against missing resources or missing thumbnails in A…
Browse files Browse the repository at this point in the history
…pple parsers

Also did some code refactoring within this context
  • Loading branch information
Joerg Jacobsen committed Dec 7, 2012
1 parent 16185d9 commit ea69d4b
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 104 deletions.
2 changes: 1 addition & 1 deletion IMBApertureParser.m
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ - (Class) objectClass
// Returns an empty dictionary for now.
// TODO: Verify whether we need to return some "true" values for "KeyList", "KeyPhotoKey" and "PhotoCount"

- (NSDictionary*) childrenInfoForNode:(IMBNode*)inNode images:(NSDictionary*)inImages
- (NSDictionary*) childrenInfoForNode:(NSDictionary*)inNodeDict images:(NSDictionary*)inImages
{
return [NSDictionary dictionaryWithObjectsAndKeys:nil];
}
Expand Down
12 changes: 7 additions & 5 deletions IMBAppleMediaParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,17 @@ extern NSString* const kIMBiPhotoNodeObjectTypeFace; // = @"faces"

// Returns the image location for the image represented by inImageKey in the master image list (aka dictionary)

- (NSString*) imagePathForImageKey:(NSString*)inImageKey;
- (NSString*) thumbnailPathForImageKey:(NSString*)inImageKey;

// Specific method for populating Faces nodes (in Aperture and iPhoto library)

- (void) populateFacesNode:(IMBNode*)inNode withFaces:(NSDictionary*)inFaces images:(NSDictionary*)inImages;

// Checks inKey whether it is a valid key in resource list and returns it if valid.
// If not checks all other candidates for validity and returns the first that is valid.
// If not returns nil.
// Tries to ensure that a skimmable node (face or event or project) has a valid key photo key (in terms of
// exists in master image list). Will replace that key if invalid with any other valid key of that node.
// Returns NO if no valid key was found, otherwise returns YES.
// NOTE: inNodeDict must have the following keys: "name" or "RollName", "KeyPhotoKey", "KeyList"

- (NSString *)validatedResourceKey:(NSString *)inKey relativeToResourceList:(NSDictionary *)inResources otherCandidates:(NSArray *)inKeyList;
- (BOOL)ensureValidKeyPhotoKeyForSkimmableNode:(NSDictionary *)inNodeDict
relativeToMasterImageList:(NSDictionary *)inMasterImages;
@end
113 changes: 83 additions & 30 deletions IMBAppleMediaParser.m
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ of this software and associated documentation files (the "Software"), to deal

@interface IMBAppleMediaParser ()

- (NSString*) imagePathForImageKey:(NSString*)inImageKey;
- (NSString*) thumbnailPathForImageKey:(NSString*)inImageKey;
- (NSString*) imagePathForFaceIndex:(NSNumber*)inFaceIndex inImageWithKey:(NSString*)inImageKey;
- (BOOL) supportsPhotoStreamFeatureInVersion:(NSString *)inVersion;
- (NSString *) rootNodeIdentifier;
Expand Down Expand Up @@ -475,7 +475,7 @@ - (NSString*) imagePathForChildOfNodeObject:(IMBNodeObject*)inNodeObject atIndex
}

// Events
return [self imagePathForImageKey:imageKey];
return [self thumbnailPathForImageKey:imageKey];
}


Expand All @@ -496,7 +496,7 @@ - (NSString*) imagePathForKeyChildOfNodeObject:(IMBNodeObject*)inNodeObject user
}

// Events
return [self imagePathForImageKey:imageKey];
return [self thumbnailPathForImageKey:imageKey];
}


Expand Down Expand Up @@ -573,7 +573,7 @@ - (BOOL) shouldUseAlbum:(NSDictionary*)inAlbumDict images:(NSDictionary*)inImage
// (The values provided by the according dictionary in .plist are mostly wrong because we separate node children by
// media types 'Image' and 'Movie' into different views.) Must be subclassed.

- (NSDictionary*) childrenInfoForNode:(IMBNode*)inNode images:(NSDictionary*)inImages
- (NSDictionary*) childrenInfoForNode:(NSDictionary*)inNodeDict images:(NSDictionary*)inImages
{
NSLog(@"%s Please use a custom subclass of IMBAppleMediaParser...",__FUNCTION__);
[[NSException exceptionWithName:@"IMBProgrammerError" reason:@"Please use a custom subclass of IMBAppleMediaParser" userInfo:nil] raise];
Expand Down Expand Up @@ -655,6 +655,9 @@ - (id)thumbnailForObject:(IMBObject *)inObject error:(NSError **)outError
IMBSkimmableObject *skimmableObject = (IMBSkimmableObject *)inObject;

skimmableObject.imageLocation = [skimmableObject imageLocationForCurrentSkimmingIndex];
if (!skimmableObject.imageLocation) {
return nil;
}
}

// IKImageBrowser can also deal with NSData type (IKImageBrowserNSDataRepresentationType)
Expand All @@ -673,6 +676,7 @@ - (id)thumbnailForObject:(IMBObject *)inObject error:(NSError **)outError
}
else
{
inObject.imageRepresentationType = IKImageBrowserCGImageRepresentationType;
return (id)[self thumbnailFromLocalImageFileForObject:inObject error:outError];
}
}
Expand All @@ -692,12 +696,20 @@ - (NSString*) imageLocationForObject:(NSDictionary*)inObjectDict
//----------------------------------------------------------------------------------------------------------------------
// Returns the image location for the image represented by inImageKey in the master image list (aka dictionary)

- (NSString*) imagePathForImageKey:(NSString*)inImageKey
- (NSString*) thumbnailPathForImageKey:(NSString*)inImageKey
{
NSString* imagePath = nil;

NSDictionary* images = [[self plist] objectForKey:@"Master Image List"];
NSDictionary* imageDict = [images objectForKey:inImageKey];
NSString* imagePath = [self imageLocationForObject:imageDict];


if (imageDict) {
imagePath = [self imageLocationForObject:imageDict];
}
if (!imagePath) {
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
imagePath = [bundle pathForResource:@"missing-thumbnail" ofType:@"jpg"];
}
return imagePath;
}

Expand All @@ -708,12 +720,18 @@ - (NSString*) imagePathForImageKey:(NSString*)inImageKey

- (NSString*) imagePathForFaceIndex:(NSNumber*)inFaceIndex inImageWithKey:(NSString*)inImageKey
{
NSString* imagePath = [self imagePathForImageKey:inImageKey];
NSString* imagePath = [self thumbnailPathForImageKey:inImageKey];

return [NSString stringWithFormat:@"%@_face%@.%@",
[imagePath stringByDeletingPathExtension],
inFaceIndex,
[imagePath pathExtension]];
if (imagePath) {
return [NSString stringWithFormat:@"%@_face%@.%@",
[imagePath stringByDeletingPathExtension],
inFaceIndex,
[imagePath pathExtension]];
} else {
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
NSString* path = [bundle pathForResource:@"missing-thumbnail" ofType:@"jpg"];
return path;
}
}


Expand Down Expand Up @@ -883,19 +901,31 @@ - (void) populateFacesNode:(IMBNode*)inNode
// Setup the loop

NSUInteger index = 0;
NSString* faceKeyPhotoKey = nil;
NSString* path = nil;
NSString* thumbnailPath = nil;
IMBFaceNodeObject* object = nil;
NSString* subNodeType = @"Face";

for (NSDictionary* faceDict in sortedFaces)
for (id faceDict in sortedFaces)
{
NSString* subnodeName = [faceDict objectForKey:@"name"];

if ([self shouldUseAlbumType:subNodeType] &&
[self shouldUseAlbum:faceDict images:inImages])
{
// Validate node dictionary and repair if necessary
// For that we need a mutable version of node dictionary

faceDict = [NSMutableDictionary dictionaryWithDictionary:faceDict];

// Adjust keys "KeyPhotoKey", "KeyList", and "PhotoCount" in metadata dictionary
// because movies and images are not jointly displayed in iMedia browser...

[faceDict addEntriesFromDictionary:[self childrenInfoForNode:faceDict images:inImages]];

if (![self ensureValidKeyPhotoKeyForSkimmableNode:faceDict relativeToMasterImageList:inImages]) {
continue;
}

// Create subnode for this node...

IMBNode* subnode = [[[IMBNode alloc] initWithParser:self topLevel:NO] autorelease];
Expand Down Expand Up @@ -929,31 +959,21 @@ - (void) populateFacesNode:(IMBNode*)inNode
[objects addObject:object];
[object release];

// Adjust keys "KeyPhotoKey", "KeyList", and "PhotoCount" in metadata dictionary
// because movies and images are not jointly displayed in iMedia browser...

NSMutableDictionary* preliminaryMetadata = [NSMutableDictionary dictionaryWithDictionary:faceDict];
[preliminaryMetadata addEntriesFromDictionary:[self childrenInfoForNode:subnode images:inImages]];

object.preliminaryMetadata = preliminaryMetadata; // This metadata from the XML file is available immediately
object.preliminaryMetadata = faceDict; // This metadata from the XML file is available immediately
[object resetCurrentSkimmingIndex]; // Must be done *after* preliminaryMetadata is set
object.metadata = nil; // Build lazily when needed (takes longer)
object.metadataDescription = nil; // Build lazily when needed (takes longer)

// Obtain key photo dictionary (key photo is displayed while not skimming)...

faceKeyPhotoKey = [object.preliminaryMetadata objectForKey:@"KeyPhotoKey"];
NSDictionary* keyPhotoDict = [inImages objectForKey:faceKeyPhotoKey];
path = [keyPhotoDict objectForKey:@"ImagePath"];

object.representedNodeIdentifier = subnode.identifier;
object.location = [NSURL fileURLWithPath:path isDirectory:NO];

// NOTE: Since faces represent multiple resources we do not set their "location" property

object.name = subnode.name;
object.parserIdentifier = [self identifier];
object.index = index++;

thumbnailPath = [self imagePathForFaceIndex:[faceDict objectForKey:@"key image face index"]
inImageWithKey:faceKeyPhotoKey];
inImageWithKey:[faceDict objectForKey:@"KeyPhotoKey"]];
object.imageLocation = (id)[NSURL fileURLWithPath:thumbnailPath isDirectory:NO];
object.imageRepresentationType = [self requestedImageRepresentationType];
object.imageRepresentation = nil;
Expand Down Expand Up @@ -1151,4 +1171,37 @@ - (NSString *)validatedResourceKey:(NSString *)inKey relativeToResourceList:(NSD
return nil;
}

// Tries to ensure that a skimmable node (face or event or project) has a valid key photo key (in terms of
// exists in master image list). Will replace that key if invalid with any other valid key of that node.
// Returns NO if no valid key was found, otherwise returns YES.
// NOTE: inNodeDict must have the following keys: "name" or "RollName", "KeyPhotoKey", "KeyList"

- (BOOL)ensureValidKeyPhotoKeyForSkimmableNode:(NSMutableDictionary *)ioNodeDict
relativeToMasterImageList:(NSDictionary *)inMasterImages
{
// Check for valid key photo key in node dict and try to replace with some other if necessary
// (We've had occurences where key photo key was invalid (not key in master image list))

NSString* keyPhotoKeyCandidate = [ioNodeDict objectForKey:@"KeyPhotoKey"];
NSString* validKeyPhotoKey = [self validatedResourceKey:keyPhotoKeyCandidate
relativeToResourceList:inMasterImages
otherCandidates:[ioNodeDict objectForKey:@"KeyList"]];

if (!validKeyPhotoKey)
{
NSString *nodeName = [ioNodeDict objectForKey:@"name"] ?
[ioNodeDict objectForKey:@"name"] :
[ioNodeDict objectForKey:@"RollName"];

NSLog(@"%s Could not create skimmable node %@ because could not determine key photo", __FUNCTION__, nodeName);
return NO;
}

if (![keyPhotoKeyCandidate isEqualToString:validKeyPhotoKey])
{
// Replace
[ioNodeDict setObject:validKeyPhotoKey forKey:@"KeyPhotoKey"];
}
return YES;
}
@end
3 changes: 2 additions & 1 deletion IMBFaceNodeObject.m
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,9 @@ - (IMBSkimmableObject *)thumbnailProvider
- (id) imageLocationForCurrentSkimmingIndex
{
IMBAppleMediaParser *parser = (IMBAppleMediaParser *)[self.parserMessenger parserWithIdentifier:self.parserIdentifier];
NSString *thumbnailPath = [parser imagePathForFaceIndex:self.currentFaceIndex inImageWithKey:self.currentImageKey];

return [NSURL fileURLWithPath:[parser imagePathForFaceIndex:self.currentFaceIndex inImageWithKey:self.currentImageKey] isDirectory:NO];
return [NSURL fileURLWithPath:thumbnailPath isDirectory:NO];
}


Expand Down
2 changes: 1 addition & 1 deletion IMBiPhotoEventNodeObject.m
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ - (id) imageLocationForCurrentSkimmingIndex
{
IMBiPhotoParser *parser = (IMBiPhotoParser *)[self.parserMessenger parserWithIdentifier:self.parserIdentifier];

return [NSURL fileURLWithPath:[parser imagePathForImageKey:_currentImageKey] isDirectory:NO];
return [NSURL fileURLWithPath:[parser thumbnailPathForImageKey:_currentImageKey] isDirectory:NO];
}


Expand Down
Loading

0 comments on commit ea69d4b

Please sign in to comment.