Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Cleaned up code

Added more comments
  • Loading branch information...
commit d34f5c99fbec2fd2d7fb05e128a543638846eadc 1 parent d28752c
agillesp@gmail.com authored
View
10 Classes/TSLibraryImport.h
@@ -15,13 +15,21 @@
/**
* Pass in the NSURL* you get from an MPMediaItem's
- * MPMediaItemAssetURL property to get the file's extension.
+ * MPMediaItemPropertyAssetURL property to get the file's extension.
*
* Helpful in constructing the destination url for the
* imported file.
*/
+ (NSString*)extensionForAssetURL:(NSURL*)assetURL;
+/**
+ * @param: assetURL The NSURL* returned by MPMediaItemPropertyAssetURL property of MPMediaItem.
+ * @param: destURL The file URL to write the imported file to. You'll get an exception if a file
+ * exists at this location.
+ * @param completionBlock This block is called when the import completes. Note that
+ * completion doesn't imply success. Be sure to check the status and error properties
+ * of the TSLibraryImport* instance from your completionBlock.
+ */
- (void)importAsset:(NSURL*)assetURL toURL:(NSURL*)destURL completionBlock:(void (^)(TSLibraryImport* import))completionBlock;
@property (readonly) NSError* error;
View
22 Classes/TSLibraryImport.m
@@ -60,6 +60,8 @@ - (void)importAsset:(NSURL*)assetURL toURL:(NSURL*)destURL completionBlock:(void
if (nil == export)
@throw [NSException exceptionWithName:@"TSUnknownError" reason:@"Couldn't create AVAssetExportSession" userInfo:nil];
+ //TODO: instead of putting this in the same directory as the dest file, we should probably stuff
+ //this in tmp
NSURL* tmpURL = [[destURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"mov"];
[[NSFileManager defaultManager] removeItemAtURL:tmpURL error:nil];
export.outputURL = tmpURL;
@@ -71,10 +73,17 @@ - (void)importAsset:(NSURL*)assetURL toURL:(NSURL*)destURL completionBlock:(void
} else if (export.status == AVAssetExportSessionStatusCancelled) {
completionBlock(self);
} else {
- [self extractQuicktimeMovie:tmpURL toFile:destURL];
+ @try {
+ [self extractQuicktimeMovie:tmpURL toFile:destURL];
+ }
+ @catch (NSException * e) {
+ //TODO: Crap, since we're delegating status/error to our AVAssetExportSession instance, we have a problem here
+ // Need to create our own status/error ivars
+ }
+ //clean up the tmp .mov file
+ [[NSFileManager defaultManager] removeItemAtURL:tmpURL error:nil];
completionBlock(self);
}
-
[export release];
export = nil;
}];
@@ -83,7 +92,7 @@ - (void)importAsset:(NSURL*)assetURL toURL:(NSURL*)destURL completionBlock:(void
- (void)extractQuicktimeMovie:(NSURL*)movieURL toFile:(NSURL*)destURL {
FILE* src = fopen([[movieURL path] cStringUsingEncoding:NSUTF8StringEncoding], "r");
if (NULL == src) {
- //TODO: failure
+ @throw [NSException exceptionWithName:@"TSUnknownException" reason:@"Couldn't open source file" userInfo:nil];
return;
}
char atom_name[5];
@@ -100,7 +109,8 @@ - (void)extractQuicktimeMovie:(NSURL*)movieURL toFile:(NSURL*)destURL {
FILE* dst = fopen([[destURL path] cStringUsingEncoding:NSUTF8StringEncoding], "w");
unsigned char buf[4];
if (NULL == dst) {
- //TODO: this is unlikely, but bad
+ fclose(src);
+ @throw [NSException exceptionWithName:@"TSUnknownException" reason:@"Couldn't open destination file" userInfo:nil];
}
for (uint32_t ii=0; ii<atom_size; ii+=4) {
fread(buf, 4, 1, src);
@@ -110,10 +120,12 @@ - (void)extractQuicktimeMovie:(NSURL*)movieURL toFile:(NSURL*)destURL {
fclose(src);
return;
}
+ if (atom_size == 0)
+ break; //0 atom size means to the end of file... if it's not the mdat chunk, we're done
fseek(src, atom_size, SEEK_CUR);
}
fclose(src);
- //TODO: failure
+ @throw [NSException exceptionWithName:@"TSUnknownException" reason:@"Didn't find mdat chunk" userInfo:nil];
}
- (NSError*)error {
View
2  Classes/iPodLibraryAccessViewController.h
@@ -18,7 +18,7 @@
}
- (IBAction)pickSong:(id)sender;
-- (void)exportAssetAtURL:(NSURL*)assetURL;
+- (void)exportAssetAtURL:(NSURL*)assetURL withTitle:(NSString*)title;
@end
View
119 Classes/iPodLibraryAccessViewController.m
@@ -12,50 +12,6 @@
@implementation iPodLibraryAccessViewController
-/*
-// The designated initializer. Override to perform setup that is required before the view is loaded.
-- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
- if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
- // Custom initialization
- }
- return self;
-}
-*/
-
-/*
-// Implement loadView to create a view hierarchy programmatically, without using a nib.
-- (void)loadView {
-}
-*/
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- if ([keyPath compare:@"status"] == 0) {
- AVAssetExportSession* export = (AVAssetExportSession*)object;
- switch (export.status) {
- case AVAssetExportSessionStatusUnknown:
- NSLog(@"AVAssetExportSessionStatusUnknown");
- break;
- case AVAssetExportSessionStatusExporting:
- NSLog(@"AVAssetExportSessionStatusExporting");
- break;
- case AVAssetExportSessionStatusCompleted:
- NSLog(@"AVAssetExportSessionStatusCompleted");
- break;
- case AVAssetExportSessionStatusFailed:
- NSLog(@"AVAssetExportSessionStatusFailed");
- break;
- case AVAssetExportSessionStatusCancelled:
- NSLog(@"AVAssetExportSessionStatusCancelled");
- break;
- case AVAssetExportSessionStatusWaiting:
- NSLog(@"AVAssetExportSessionStatusWaiting");
- break;
- default:
- break;
- }
- }
-}
-
- (void)viewDidLoad {
[super viewDidLoad];
@@ -63,31 +19,16 @@ - (void)viewDidLoad {
AVAudioSession* session = [AVAudioSession sharedInstance];
NSError* error = nil;
- // Thought maybe setting this to one of the "exclusive" categories
- // would give us faster results, e.g., by granting us access to the
- // hardware encoder, but with any other category, you get -11820 errors
- // from AVAssetExportSession
- if(![session setCategory:AVAudioSessionCategoryAmbient error:&error]) {
+ if(![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Couldn't set audio session category: %@", error);
- }
-
+ }
if(![session setActive:YES error:&error]) {
NSLog(@"Couldn't make audio session active: %@", error);
}
-
-}
-
-/*
-// Override to allow orientations other than the default portrait orientation.
-- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
- // Return YES for supported orientations
- return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-*/
-
- (void)progressTimer:(NSTimer*)timer {
- AVAssetExportSession* export = (AVAssetExportSession*)timer.userInfo;
+ TSLibraryImport* export = (TSLibraryImport*)timer.userInfo;
switch (export.status) {
case AVAssetExportSessionStatusExporting:
{
@@ -108,18 +49,48 @@ - (void)progressTimer:(NSTimer*)timer {
}
}
-- (void)exportAssetAtURL:(NSURL*)assetURL {
+- (void)exportAssetAtURL:(NSURL*)assetURL withTitle:(NSString*)title {
// create destination URL
NSString* ext = [TSLibraryImport extensionForAssetURL:assetURL];
-
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
- NSURL* outURL = [[NSURL fileURLWithPath:[documentsDirectory stringByAppendingPathComponent:@"test"]] URLByAppendingPathExtension:ext];
+ NSURL* outURL = [[NSURL fileURLWithPath:[documentsDirectory stringByAppendingPathComponent:title]] URLByAppendingPathExtension:ext];
+ // we're responsible for making sure the destination url doesn't already exist
+ [[NSFileManager defaultManager] removeItemAtURL:outURL error:nil];
+ // create the import object
TSLibraryImport* import = [[TSLibraryImport alloc] init];
+ startTime = [NSDate timeIntervalSinceReferenceDate];
+ NSTimer* timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(progressTimer:) userInfo:import repeats:YES];
+ [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[import importAsset:assetURL toURL:outURL completionBlock:^(TSLibraryImport* import) {
- NSLog(@"export complete!");
+ /*
+ * If the export was successful (check the status and error properties of
+ * the TSLibraryImport instance) you know have a local copy of the file
+ * at `outURL` You can get PCM samples for processing by opening it with
+ * ExtAudioFile. Yay!
+ *
+ * Here we're just playing it with AVPlayer
+ */
+ if (import.status != AVAssetExportSessionStatusCompleted) {
+ // something went wrong with the import
+ NSLog(@"Error importing: %@", import.error);
+ [import release];
+ import = nil;
+ return;
+ }
+
+ // import completed
+ [import release];
+ import = nil;
+ if (!player) {
+ player = [[AVPlayer alloc] initWithURL:outURL];
+ } else {
+ [player pause];
+ [player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:outURL]];
+ }
+ [player play];
}];
}
@@ -129,8 +100,14 @@ - (void)mediaPicker:(MPMediaPickerController *)mediaPicker
for (MPMediaItem* item in mediaItemCollection.items) {
NSString* title = [item valueForProperty:MPMediaItemPropertyTitle];
NSURL* assetURL = [item valueForProperty:MPMediaItemPropertyAssetURL];
- NSLog(@"title: %@, url: %@", title, assetURL);
- [self exportAssetAtURL:assetURL];
+ if (nil == assetURL) {
+ /**
+ * !!!: When MPMediaItemPropertyAssetURL is nil, it typically means the file
+ * in question is protected by DRM. (old m4p files)
+ */
+ return;
+ }
+ [self exportAssetAtURL:assetURL withTitle:title];
}
}
@@ -151,6 +128,9 @@ - (void)viewDidUnload {
}
- (void)showMediaPicker {
+ /*
+ * ???: Can we filter the media picker so we don't see m4p files?
+ */
MPMediaPickerController* mediaPicker = [[[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeMusic] autorelease];
mediaPicker.delegate = self;
[self presentModalViewController:mediaPicker animated:YES];
@@ -161,6 +141,9 @@ - (IBAction)pickSong:(id)sender {
}
- (void)dealloc {
+ [player release];
+ [progressView release];
+ [elapsedLabel release];
[super dealloc];
}
View
6 Tests.m
@@ -69,8 +69,8 @@ - (void)testExtensionParsing {
- (void)testExportNilParameters {
TSLibraryImport* import = [[[TSLibraryImport alloc] init] autorelease];
NSURL* dummyURL = [NSURL URLWithString:@"ipod-library://item/item.mp3?id=1425010501608620615"];
- GHAssertThrowsSpecificNamed([import importAsset:dummyURL toURL:nil], NSException, NSInvalidArgumentException, @"nil parameter should throw NSInvalidArgumentException");
- GHAssertThrowsSpecificNamed([import importAsset:nil toURL:dummyURL], NSException, NSInvalidArgumentException, @"nil parameter should throw NSInvalidArgumentException");
+ GHAssertThrowsSpecificNamed([import importAsset:dummyURL toURL:nil completionBlock:nil], NSException, NSInvalidArgumentException, @"nil parameter should throw NSInvalidArgumentException");
+ GHAssertThrowsSpecificNamed([import importAsset:nil toURL:dummyURL completionBlock:nil], NSException, NSInvalidArgumentException, @"nil parameter should throw NSInvalidArgumentException");
}
- (void)testExportInvalidURL {
@@ -81,7 +81,7 @@ - (void)testExportInvalidURL {
NSString *documentsDirectory = [paths objectAtIndex:0];
NSURL* outURL = [NSURL fileURLWithPath:[documentsDirectory stringByAppendingPathComponent:@"test.mov"]];
- GHAssertThrowsSpecificNamed([import importAsset:badURL toURL:outURL], NSException, NSInvalidArgumentException, @"importAsset: should throw NSInvalidArgumentException for %@", badURL);
+ GHAssertThrowsSpecificNamed([import importAsset:badURL toURL:outURL completionBlock:nil], NSException, NSInvalidArgumentException, @"importAsset: should throw NSInvalidArgumentException for %@", badURL);
}
@end
Please sign in to comment.
Something went wrong with that request. Please try again.