📜 ⬆️ ⬇️

Taming iTunes with Xcode and scripting Bridge.

Last summer, a terrible thing happened ... I covered the screw in my MacBook. All important data was easily recovered. Glory to the Time Machine! But the music collection, which I painstakingly cultivated over the years, was not lucky. It’s not a problem to restore the music itself, but the ratings and the number of plays whose history goes back to the very appearance of iTunes under Windows, oh, what a pity. She experienced a lot of music library, including the transfer from Windows to Hakintosh, and then to MacBook.

The blessing of the midiatek was not all lost - there were only horns and legs, or rather the cherished file " iTunes Librar y". The other day I decided to reanimate it.

Mac OS Solution


image The obvious solution is to use AppleScript . With it, almost any application in Mac OS can be automated. The language is so close to English and far from other programming languages ​​that it put me in a stupor. I did not know which side to approach him.
I had to use XCode and some familiar Objective-C . Working with XML caused almost no problems. NSXML ... and MSXML turned out to be a very similar API, not just the name.
')
With iTunes, things are more complicated. In addition to AppleScript itself, which can be used in Xcode projects, I found only two ways. Both are essentially wrappers for AppleScript.


1. AppScript Framework
Open Source project from third-party developers. Suitable for Mac OS X since 10.3.9 . Supports Objective-C , Ruby and Python . The syntax seemed complicated, and I didn’t manage to compile it correctly, so I didn’t understand it.

2. Scripting Bridge Framework
Apple’s own proprietary solution, which was released only in Mac OS 10.5 . As the name implies, Scripting Bridge dynamically implements a bridge between Objective-C and applications with AppleScript support. This generates Objective-C classes based on the description of the script interface. They include objects and methods representing properties, elements, commands, and so on. "
On the second option, I stopped.
The first thing to do is add /System/Library/Frameworks/ScriptingBridge.framework to the project. Then create a special header file to know how to access an ActionScript-enabled application.
This is done by a command in the terminal:
sdef /Applications/iTunes.app | sdp -fh --basename iTunes

The file iTunes.h appears in the current folder, which you need to add to the Xcode project and you can access iTunes.
Similarly, you can do with any application that supports AppleScript.

Communication example


For example, the code that stores the list of songs, rating and number of performances:

- ( void ) ExportLibrary
{
Boolean shouldExportTrack = NO;
// iTunes
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier: @"com.apple.iTunes" ];

// iTunes ( , )
iTunesLibraryPlaylist *library = [[[[iTunes sources] objectAtIndex:0] playlists] objectAtIndex:0];
iTunesTrack *track;

// XML
NSXMLDocument *xmlNew = [[NSXMLDocument alloc] initWithXMLString: @"<?xml version=\"1.0\"?><LIBRARY/>" options:0 error:NULL];
NSXMLElement *rootNode = [xmlNew rootElement], *curNode;

//
NSInteger curLibTrack = 0, nLibTracks = [[library tracks] count];
NSLog( @"%i" , nLibTracks);

//
for (curLibTrack = 0; curLibTrack < nLibTracks; curLibTrack++)
{
track = [[library tracks] objectAtIndex:curLibTrack];
shouldExportTrack = (track.rating > 0 || track.playedCount > 0); //
if (shouldExportTrack)
{
// XML
curNode = [[NSXMLElement alloc] initWithXMLString: @"<TRACK n=\"\" name=\"\" played=\"\" rating=\"\" />" error:NULL];
[[curNode attributeForName: @"n" ] setStringValue:[NSString stringWithFormat: @"%i" , curLibTrack]];
// getTrackMetaName, ,
// , , ( Beyoncé Anggun)
[[curNode attributeForName: @"name" ] setStringValue:[self getTrackMetaName:track]];
[[curNode attributeForName: @"played" ] setStringValue:[NSString stringWithFormat: @"%i" , track.playedCount]];
[[curNode attributeForName: @"rating" ] setStringValue:[NSString stringWithFormat: @"%i" , track.rating]];
[rootNode addChild:curNode];
[curNode release];
}

}

//
[[xmlNew XMLData] writeToFile:( @"/Users/max/Desktop/old-library.xml" ) atomically:NO];
[xmlNew release];

}


* This source code was highlighted with Source Code Highlighter .


Conclusion


Work on the restoration of the long-suffering library has not yet been completed. It is necessary to deal with the sorting of tracks by the MetaName hash and with effective matching. There was an impression that it would be faster to fix everything manually, than to figure it all out for hours =)
Everything works fine. But in a semi-automatic mode, because there was no desire to mess around anymore.

On the other hand, I learned a lot of new things, instead of engaging in such a routine.

If there is an opportunity to automate something, it is better to do it once, than then periodically spend time on manual work.

Source: https://habr.com/ru/post/52137/


All Articles