Exporting data from iTunes

My parents are DJs, and spend much more time using iTunes than I ever do. My dad in particular is meticulous about keeping track of their music, what music they have played on their shows, and information about the songs in general. He does this with a combination of iTunes and a database. Until somewhat recently, that was iTunes running on Mac OS 10.6.8 and an Appleworks database. I had been wanting to move him off of Appleworks, since I knew he was one hard drive failure away from a new computer, and the end of Appleworks support. His database is extensive, and he had been outgrowing Appleworks for a while. But, it worked, and if it ain’t broke, don’t fix it. He ultimately ended up moving to Filemaker - nothing else came close in terms of power and efficiency. He also had to move to the latest, “greatest” iTunes. In his case, not so great, since it seems like Apple enjoys changing various things about its core programs, just for the sake of change. Although I was hesitant to move him to Filemaker, since it seemed to have a steep learning curve and was perhaps more app then he really needed, that part went relatively smoothly. You can see the common ancestry Filemaker and Appleworks have shared. Plus, it was blazingly fast. Totally worth the money.

One big part of his workflow that changed was how he takes albums from iTunes and enters them into the database. It used to be that he could copy and paste multiple songs directly from iTunes into the AppleWorks database (when it was running in the “spreadsheet” view). Filemaker, for all of its power, doesn’t seem to like accepting multiple records of input (or even multiple fields of one record) via copy and paste. So, another solution was needed. I also learned that he uses many of the extra fields in iTunes, such as the “Show” field (normally intended for TV shows, I think, but he was using it to track his radio shows). Here’s the workflow we finally came up with.

First, make sure that all of the fields you are interested in exporting from iTunes are displayed in its list mode (choose Songs from the popup menu in the upper right corner). Reorder these fields to match the order of the fields in your database. This step isn’t strictly necessary, since you can change the order of the fields when you import the data later, but it makes life easier. Then, select the songs you are interested in exporting, and copy them to the clipboard. The data copied to the clipboard by iTunes is a bit annoying - if you just paste it, depending on where you do the paste, you’ll get little media players for each song, or perhaps some odd version of the data. The step that is needed here it to get just the plain text version of the clipboard data. There are, I’m sure, lots of tools that will do this for you. I ended up writing a simple Automator script that takes the text on the clipboard, and saves it as plain text to a file named “SongImport.txt” on the Desktop. It’s easy to find and always named the same thing. That file, as is, can now be imported into Filemaker. Filemaker is smart enough to let you break it up into fields, and even remembers the last order you put the fields into, if this is not the first time you’ve done the same kind of import into the same database.

Hopefully this can be helpful to other people who would like to get song data out of iTunes and into a different database or spreadsheet.

AlphaBaby 3.0.3 Released

A new version of AlphaBaby for Mac, which fixes the bug causing some persistent crashes, has been released in the App Store. Download it today!

Major New Version of Mac AlphaBaby

I am excited to announce the newest version of AlphaBaby for MacOS. This is a major new version of AlphaBaby, and it adds many great features from the IOS version of AlphaBaby. You can now record your own voice for letters, shapes, numbers, and photos, right from inside of AlphaBaby. You can create configurable sets of options. Maybe one day you want to work on letters and numbers, and other day look at photos of family members. With the Sets feature of AlphaBaby, it is easy to switch between different set of options. You can even share sets created on the iOS version of AlphaBaby with the Mac version, or send your Mac sets to your iOS devices.

Perhaps the most important new feature is one that has been requested for years -- the ability to completely block the keys at the top row of the keyboard, including volume and brightness! Your computer is more protected than ever when running AlphaBaby!

Try out AlphaBaby 3.0 today! It’s available exclusively in the Mac App Store for only $.99 USD.

Global variables vs method calls

During my rewrite of Mac AlphaBaby, I’ve been trying to do things the “right way”. That has meant a lot of restructuring of code, trying to reuse classes where possible, and general consolidation. I’ve learned a lot since I first started writing Mac AlphaBaby back in 2003 and I want to bring the code base up to more modern standards.

One of the things I’ve often wondered about in Objective-C is the cost for doing things the “right” way. Every time I make 3 method calls (at least!) for something that might have been a single structure access or global variable access in a C program of yesteryear, I cringe a bit. I know that computers have gotten so much faster that it doesn’t really matter how many virtual or dynamic method calls I’m making for something that isn’t in a speed critical path. However, the part of me that learned C and debugged embedded programs in assembly to optimize for speed still didn’t feel great about all that extravagant method calling.

The current case in question is when creating a string whose value needs to be created a runtime, but then is constant after that, is better off being accessed via a global variable or by some kind of method call on a class. Here’s my test class, with all different ways to access a string:

// Globaler.h#import extern NSString *globalString;@interface Globaler : NSObject{ NSString *myInstanceString; NSString *propertyString;}@property (nonatomic, retain) NSString *propertyString;+ (NSString *)staticString;+ (Globaler *)sharedGlobaler;- (NSString *)instanceString;@end
static NSString *myStaticString;NSString *globalString;@implementation Globaler@synthesize propertyString;+ (void)initialize{ myStaticString = [NSString stringWithFormat:@"Hello %@ world", @"static"]; [myStaticString retain]; globalString = [NSString stringWithFormat:@"Hello %@ world", @"global"]; [globalString retain];}+ (NSString *)staticString{ return myStaticString;}+ (Globaler *)sharedGlobaler{ static Globaler *theGlobaler = nil; if (theGlobaler == nil) { theGlobaler = [[Globaler alloc] init]; } return theGlobaler;}- (NSString *)instanceString{ return myInstanceString;}- (id)init{ self = [super init]; if (self != nil) { myInstanceString = [NSString stringWithFormat:@"Hello %@ world", @"instance"]; [myInstanceString retain]; self.propertyString = [NSString stringWithFormat:@"Hello %@ world", @"property"]; } return self;}@end
And here’s the code which accesses the strings in different ways:

NSString *refString = @"reference string";- (void)dealloc{ [super dealloc];}#define NUM_ITERS 100000000- (BOOL)doStuffWithString:(NSString *)str{ if (str == refString) { return YES; } return NO;}- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{ // Insert code here to initialize your application NSString *str; Globaler *g; NSTimeInterval tStart, tEnd; int i; g = [Globaler sharedGlobaler]; tStart = [NSDate timeIntervalSinceReferenceDate]; for (i = 0; i < NUM_ITERS; i++) { str = globalString; if ([self doStuffWithString:str]) { break; } } tEnd = [NSDate timeIntervalSinceReferenceDate]; NSLog(@"global variable time: %f: %f", tEnd - tStart, ((tEnd - tStart) / (NUM_ITERS * 1.0) * 1000.0)); tStart = [NSDate timeIntervalSinceReferenceDate]; for (i = 0; i < NUM_ITERS; i++) { str = [Globaler staticString]; if ([self doStuffWithString:str]) { break; } } tEnd = [NSDate timeIntervalSinceReferenceDate]; NSLog(@"static variable time: %f", tEnd - tStart); tStart = [NSDate timeIntervalSinceReferenceDate]; for (i = 0; i < NUM_ITERS; i++) { str = [g instanceString]; if ([self doStuffWithString:str]) { break; } } tEnd = [NSDate timeIntervalSinceReferenceDate]; NSLog(@"instance variable time: %f", tEnd - tStart); tStart = [NSDate timeIntervalSinceReferenceDate]; for (i = 0; i < NUM_ITERS; i++) { str = g.propertyString; if ([self doStuffWithString:str]) { break; } } tEnd = [NSDate timeIntervalSinceReferenceDate]; NSLog(@"property variable time: %f", tEnd - tStart); tStart = [NSDate timeIntervalSinceReferenceDate]; for (i = 0; i < NUM_ITERS; i++) { str = [Globaler sharedGlobaler].propertyString; if ([self doStuffWithString:str]) { break; } } tEnd = [NSDate timeIntervalSinceReferenceDate]; NSLog(@"shared property variable time: %f", tEnd - tStart);}
What did I find? About what I expected to find. The raw global variable access is the fastest, and it pretty much gets slower as you go on down the line. But, calling a static function which returns a pre-computed static variable came in second. This has some advantages: a relatively small amount of characters to type, you can initialize the static string in the +(void)initialize method, so it only gets created when needed, and it can be put into a more general utility-type class, which lets you group such variables into named collections (named by the class they are in). Here are the timing results:

global variable time: 0.695643static variable time: 1.075534instance variable time: 1.268580property variable time: 1.128956shared property variable time: 1.496929
The most expensive way to call - using a static method to get a global shared object, whose property we then access, is about twice as slow as the raw global variable. But, the static variable is at least a little closer, and seems just as clean. Bottom line, I had to bump up the iteration count pretty high to even get these numbers (measured in seconds), so all of this stuff is just ridiculously fast, and I probably don’t have to worry about it. But, the low-level tech geek person in me just wanted to know, and it will help me make some design choices (and avoid bad ones for the sake of nonexistent performance concerns).

Snowtober wrecked my Inbox

I had been doing so well with the Inbox. Keeping it under control. Responding quickly to messages.

Then Snowtober hit. Four days without power (and therefore without magic Inbox cleaning) has reduced me back to 447 messages, 251 unread. Mostly this is because I was still checking mail on my phone, but was unable to file it there. And since I use POP instead of IMAP for accessing my mail accounts, I basically have to deal with the same messages all over again. I’m sure msgFiler will once again be a huge help, but all I can say is “Curse you, Snowtober!”

Load Images quickly in Mail

Msgfiler is still doing wonderful things for my Inbox. However, once I was so efficient and keyboard-based for all of my filing needs, the one place that still required a mouse was more obvious: clicking the “Load Images” button for a HTML email message with remote images. By default, I have “load remote images” turned off, but there is the occasional message where I would like to load them. A Google search turned up similar questions, but no real solutions.

So today, I tried my hand at a bit of Applescript programming, and was able to solve it! This is for Mail on Lion - it may require some tweaking on earlier versions of Mac OS X. It also requires that you have “Enable access for assistive devices” turned on in the “Universal Access” preference pane. The script uses the UI Scripting ability of Mac OS X, which lets you target individual UI elements. So, as long as Apple doesn’t move or rename the “Load Images” button, this script should continue to work, no matter the size or location of your Mail window. I trigger it from Butler, but you could save the Applescript as an executable, and then run it from Spotlight, or have it triggered from your LauncherOfChoice. It’s quick and easy and one less reason to use the mouse!

Hope this helps someone else!

tell application "System Events" tell process "Mail" tell UI element 1 of row 1 of table 1 of scroll area 1 of splitter group 1 of splitter group 1 of window 1 tell button "Load Images" click end tell end tell end tellend tell

Mail orgainzation update

Quick update: After purchasing MsgFiler yesterday, I have filed 5609 messages! Wow. My Inbox is now officially clean and ready for business. If anyone has any AlphaBaby or BabyProof features or suggestions to send me, I can assure you I will most definitely see them, and reply quickly! :)

One tip for using MsgFiler, which I found in their support section, but not in the documentation, is that you can delete a message while in MsgFiler by using command-shift-delete. Definitely a must-have key combo when doing a major clean-out.

How should I organize my mail?

I’ve been investigating add-ons for Apple’s, looking for ways to help me better organize my Inbox. I keep coming down to a basic question: use tags or folders to organize messages? Then I discovered this paper from the University of Washington. Bottom line - they did a whole study, and their answer is still - “it depends”. So, in my grand quest to be better organized, it looks like I’ll be purchasing both MailTags and MsgFiler. Mail ActOn is intriguing too - might not rule out a triple purchase!

I’ll start out with MsgFiler, and see if I can truly make a difference in my Inbox with a bit more diligent filing.

(In looking for the link to the first paper, I discovered another paper from IBM Research on a similar subject. Who knew that email organization was a hot research topic?)

Removing old Time Machine backups

Here’s a quick note about removing old Time Machine backups, in case you ever run into a similar situation.

I had moved my Time Machine backups to a new, larger drive. However, I left the old backup on the original drive, and had added files to that drive. So, I couldn’t just reformat the old drive to wipe out the Time Machine backup file. Dragging it to the trash, and then emptying the trash didn’t work - it sat there for hours computing > 3 millions of files to be delete before I finally gave up and stopped it.

A few Google searches later, here’s what finally worked for me:

Drag the old, unwanted Backups.backupdb folder to the Trash. In a Terminal window, cd to the root of the volume where the folder used to live. Use the Unix rm command to delete the backup, as root:

cd /Volumes/OldDrive
sudo rm -rf .Trashes/501/Backups.backupdb

This took almost a day, and seemed to hang overnight, so I restarted it, but it did successfully delete the entire backup.

In the future, I’ll definitely make sure I use Disk Utility to erase any disk that has an old Time Machine backup on it, instead of trying to remove it myself!
© 2010-2016 Little Potato Software   Follow littlepotatosw on Twitter        Site Map     Privacy Policy     Contact Me