Укрощение iTunes с помощью XCode и scripting Bridge.

    Прошлым летом случилось страшное… Накрылся винт в моем МакБуке. Все важные данные были легко восстановлены. Слава Машине Времени! А вот музыкальной коллекции, которую я кропотливо взращивал годами, не повезло. Саму музыку восстановить не проблема, а вот рейтинги и количество прослушиваний, чья история уходит корнями в самое появление iTunes под Windows, ой как жалко. Пережила фонотека многое, в том числе перенос с Windows на Хакинтош, а затем и на МакБук.

    Благо мадиатека была потеряна не вся — остались рожки да ножки, а точнее заветный файл "iTunes Library". На днях я решился его реанимировать.

    Решение под Mac OS


    imageОчевидное решение — использовать AppleScript. С помощью него практически любое приложение в Mac OS можно автоматизировать. Язык настолько близок к английскому и далек от других языков программирования, что ввел меня в ступор. Я не знал с какой стороны к нему подступиться.
    Пришлось использовать XCode и немного знакомый Objective-C. Работа с XML не вызвала практически никаких проблем. NSXML… и MSXML оказались API очень похожими не только названием.

    С общением с iTunes дела обстоят сложнее. Помимо собственно AppleScript, который можно использовать в проектах XCode, я нашел всего два способа. Оба по сути являются обертками для AppleScript.


    1. AppScript Framework
    Open Source проект от сторонних разработчиков. Подходит для Mac OS X начиная с 10.3.9. Поддерживает Objective-C, Ruby и Python. Синтаксис показался сложным, да и скомпилировать его правильно мне не удалось, поэтому с ним не разбирался.

    2. Scripting Bridge Framework
    Собственное запатентованное решение от Apple, которое увидело свет лишь в Mac OS 10.5. Как и следует из названия, Scripting Bridge "динамически реализует мост между Objective-C и приложениями с поддержкой AppleScript. При этом генерируются Objective-C классы на основе описания скриптового интерфейса. Они включают объекты и методы представляющие свойства, элементы, команды и так далее."
    На втором варианте я и остановился.
    Первое что надо сделать, это добавить /System/Library/Frameworks/ScriptingBridge.framework к проекту. Затем создать специальный заголовочный файл, чтобы знать как обращаться к конкретному приложению с поддержкой ActionScript.
    Делается это командой в терминале:
    sdef /Applications/iTunes.app | sdp -fh --basename iTunes

    В текущей папке появится файл iTunes.h, который надо добавить в проект XCode и можно обращаться к iTunes.
    Аналогично можно поступить с любым приложением поддерживающим AppleScript.
     

    Пример общения


    Для примера код, который сохраняет список песен, рейтинг и число исполнений:

    - (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.


    Заключение


    Работа над восстановлением многострадальной библиотеки пока не закончена. Нужно разобраться с сортировкой треков по хэшу MetaName и с эффективным поиском совпадений. Сложилось впечатление, что быстрее было бы вручную все поправить, чем часами разбираться во всем этом =)
    Все отлично работает. Но в полуавтоматическом режиме, ибо не было желания возиться дальше.

    С другой стороны узнал много нового, вместо того чтобы заниматься подобной рутиной.

    Если есть возможность что-то автоматизировать, лучше сделать это один раз, чем потом периодически тратить время на ручную работу.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 3

      0
      по objective-c какую документацию читали?
        0
        В основном встроенную в XCode. Еще Google, но он периодически посылал меня обратно в документацию XCode на сайте Apple =)
          0
          меня отсутствие достаточного кол-ва доков останавливает пока что

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое