Apple

индекс
164,53

iPod и Python: сортируем альбомы в хронологическом порядке

Проблема


Иногда мне кажется, что Apple изо всех сил старается упростить свою продукцию. С одной стороны это замечательно, а с другой — весьма прискорбно. Ведь дополнительные настройки хоть и усложняют нашу жизнь в первые пару недель использования продукта (пока привыкнешь, разберешься), но зато в последствии позволяют подогнать его под себя и получить гаджет (или программу) своей мечты.

Album by YearЕсли Вы являетесь счастливым обладателем iPod-a, то наверно заметили, что все альбомы на нем (именно на плеере) сортируются строго в алфавитном порядке. И никак иначе. И никаких Вам настроек и expert-модов. Сказали «в алфавитном», значит в алфавитном. Что примечательно — в iTunes такая кнопочка (Album by Year) есть. Но там она мне была меньше всего нужна.

К счастью, эта проблема (как и проблема добавления отдельно скачанных эпизодов в имеющийся iTunes-подкаст) имеет решение. Не столь простое, как хотелось бы, но зато весьма увлекательное.


Решаем в лоб


Совершенно простое, но не слишком элегантное решение приходит в первые в первые пару секунд. Просто для всех альбомов превращаем содержимое тега %album% в %year% — %album%, и проблема решена. И сортируется как надо, да еще и год можно прямо на iPod-e посмотреть (без такого рода подготовки год выпуска альбома на самом плеере не выяснить).

Возможно Вас устроит такой вариант, но мне он показался неудовлетворительным. Одно дело папка на диске (их я привык называю в формате %year% — %album%), а другое дело — нарядный альбом с albumart-ом на iPod-e. Ко всему прочему меня не интересует конкретный год, в котором альбом выпустили. Я лишь хочу знать, какие альбомы относятся к раннему творчеству той или иной группы, а какие были выпущены совсем недавно, чтобы видеть, как группа менялась со временем, как менялся ее стиль. Именно это и побудило меня на более детальное изучение проблемы и поиск ее решения.

iTunes


Нет идеальных программ. iTunes не исключение. И тем не менее это удобное и приятное в работе приложение. По крайней мере так я начал думать после нескольких месяцев использования, когда уже окончательно смирился с этой Apple-овской простотой. А когда выяснилось, что в iTunes есть то, что в определенной степени поможет решить проблему, я окончательно избавился от мыслей о поиске замены iTunes-у.

Он сказал: «Поехали!»


Вступления позади и наконец-то мы начинаем действовать. Выделяем все треки какого-нибудь альбома и открываем контекстное меню. Там мы обнаруживаем замечательный пункт Get Info, за которым скрывается вся информация о выбранных треках. В настоящий момент нас интересует вкладка Sorting:
Sorting tab

Назначение этих полей я в документации не уточнял, но весь мой жизненный опыт подсказывает, что если поле Sort Album заполнено, то при сортировке альбомов в iTunes (если в колонке альбомов выбрано не «Album by Year», а просто «Album») в качестве ключа будет выступать именно оно (аналогично с остальными sort-полями). И, что самое главное, этот же ключ будет использоваться и при сортировке альбомов непосредственно на самом плеере. Это-то нам и надо. Вводим в указанное поле «2007 — Are You Listening» и радуемся, как у нас все складно вышло. Теперь мы имеем ровно один альбом, который сортируется (сам с собой) в хронологическом порядке =)

А впереди — вечность


Проблема казалось бы уже решена. А на самом деле все еще только начинается. Ведь выпустив плеер на n-ое количество гигабайт, в Apple должно быть просто не ожидали, что столько музыки можно купить в Apple Store (а другие источники в рассчет вероятно не брали). А может они не ожидали, что кому-то в один прекрасный день захочется отсортировать все альбомы по дате. Дело в том, что повторить указанную процедуру с остальными пятьюстами альбомами в своей фонотеке я просто не в состоянии. Как было сказано в недавней C# головоломке: «Never send a human to do a machine's job».

И тут на сцене появляется Python


Именно его мы и используем, чтобы обработать нашу библиотеку. Всю и сразу.

Как выяснилось, iTunes сохраняет значение поля "Sort Album" в теге TSOA mp3-файла (про остальные теги iTunes можно почитать тут). Зная это, мы можем соответствующим образом заполнить этот тег в наших файлах, а затем добавить их в iTunes-библиотеку.

Python библиотек для работы с id3 тегами оказалось не много, но и не мало. Мой выбор пал на mutagen (с другими вариантами можно ознакомиться тут).

Mutagen позволяет работать с метаданными достаточно большого количества аудио форматов (ASF, FLAC, M4A, Monkey's Audio, MP3, Musepack, Ogg FLAC, Ogg Speex, Ogg Theora и т.д.) Нас же интересуют именно id3 теги в mp3-файлах. Для манипулирования ими библиотека предоставляет два интерфейса: ID3 и EasyID3. Думаю разницу пояснять не надо. Я сначала поигрался с первым, но потом выяснилось, что и возможностей второго вполне достаточно.

Собственно код


Сразу оговорюсь, что мой опыт программирования на Python-e стремиться к бесконечности, но пока приблизительно равен нулю. Я старался написать все так, чтобы не стыдно было показать обществу, но не уверен, что получилось достаточно хорошо. Поэтому буду признателен за любые содержательные замечания, выраженные в дружественной форме.

Что же касается алгоритма, то он вполне очевиден: пробегаем по нужным файлам, достаем год выпуска и название альбома из тегов и соответствующим образом заполняем тег TSOA. Я пришел к выводу, что меня больше всего устроит формат %artist% — %year% — %album%. С таким значением тега TSOA альбомы будут сортироваться по исполнителю, затем по году, а затем по алфавиту (если вдруг в один прекрасный год Ваша любимая группа расстаралась более чем на один альбом). Python, говорят, обладает весьма дружественным синтаксисом, поэтому, даже если Вы с ним не знакомы, внести небольшие коррективы по своему вкусу в приведенный ниже скрипт проблем не составит.

В процессе обработки пяти тысяч треков может произойти многое, поэтому я добавил немного служебного вывода, чтобы наблюдать, как продвигается дело, и лог для вывода ошибок о нехватке тегов в файле. Как-никак добавлять в iTunes файл без исполнителя или альбома в тегах — гиблое дело, поэтому будет полезно выяснить, какие файлы у нас еще не готовы к миграции на iPod.

C docstring-ами я поленился, но комментариями код разбавил. Надеюсь это поможет сориентироваться тем, кто не сильно знаком с Python-ом.

Код:
Copy Source | Copy HTML
  1. #!usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # Created by KL-7
  4.  
  5. import logging
  6. import sys
  7. from os import path
  8.  
  9. from mutagen.easyid3 import EasyID3
  10. from mutagen.id3 import ID3NoHeaderError
  11.  
  12. # Количество успешно обработанных файлов
  13. totalfiles =  0
  14.  
  15.  
  16. # Вывод информации об ошибках
  17. def error(err):
  18.     # Настраиваем лог-файл для ошибок
  19.     logging.basicConfig(filename="error.log", level=logging.ERROR)
  20.     logging.error(err)
  21.  
  22.  
  23. # Получаем значение тега tagname из файла audio
  24. def gettag(audio, tagname):
  25.     taglist = audio.get(tagname, None)
  26.     if taglist:
  27.         return taglist[ 0]
  28.     else:
  29.         return ""
  30.  
  31.  
  32. # Заполняем тег TSOA
  33. def fill_tsoa(fpath):
  34.     audio = EasyID3(fpath)
  35.  
  36.     artist = gettag(audio, "artist")
  37.     if not artist:
  38.         error("Missing artist in %s" % fpath)
  39.  
  40.     album = gettag(audio, "album")
  41.     if not album:
  42.         error("Missing album in %s" % fpath)
  43.  
  44.     year = gettag(audio, "date")
  45.     if not year:
  46.         error("Missing year in %s" % fpath)
  47.  
  48.     # Формируем значение для тега TSOA
  49.     tsoa = "%s -- %s - %s" % (artist, year, album)
  50.  
  51.     # EasyID3 репамит названия тегов для большей наглядности.
  52.     # Тег TSOA в этих обозначениях называется albumsort.
  53.     audio["albumsort"] = tsoa
  54.     audio.save()
  55.  
  56.  
  57. def fill_rec(root):
  58.     # Эта функция будет вызваться в директории root и 
  59.     # в каждой ее поддиректории dirname.
  60.     # fnames - содержимое текущей директории dirname.
  61.     # Аргумент arg обязателен, но его мы не используем.
  62.     def process(arg, dirname, fnames):
  63.         for fname in fnames:
  64.             # Конвертируем русские названия в Unicode
  65.             fname = fname.decode("cp1251")
  66.             name, ext = path.splitext(fname)
  67.             fname = path.join(dirname, fname)
  68.             if path.isfile(fname) and ext.lower() == ".mp3":
  69.                 try:
  70.                     fill_tsoa(fname)
  71.                     global totalfiles
  72.                     totalfiles = totalfiles + 1
  73.                     print fname
  74.                 except ID3NoHeaderError:
  75.                     error("No tags in %s" % fname)
  76.  
  77.     # Запускаем рекурсивный обход из директории root
  78.     # с функциией process; None будет передан
  79.     # функции process первым параметром.
  80.     path.walk(root, process, None)
  81.  
  82.  
  83. if __name__ == "__main__":
  84.     # Если есть параметры, интерпретируем их как директории,
  85.     # которые надо обработать.
  86.     if sys.argv[1:]:
  87.         for dirname in sys.argv[1:]:
  88.             fill_rec(dirname)
  89.     # Если параметров нет, то работаем с текущей директорией.
  90.     else:
  91.         fill_rec(".")
  92.  
  93.     print "\nTotal files: %d" % totalfiles
  94.     raw_input();
  95.  


Можно скопировать скрипт в директорию с музыкой и запустить без параметров. В таком случае будут обработаны все mp3-файлы в текущей директории и во всех поддиректориях. А можно самостоятельно передать скрипту в виде параметров список нужных директорий (поддиректории будут обработаны и в этом случае).

Когда скрипт завершит работу (длится это не слишком долго), останется обновиться данные в библиотеке iTunes. Если эти файлы Вы еще не добавляли туда, то сейчас самое время. Если же вы редактировали теги в файлах, уже присутствующих в iTunes, то необходимо просто выделить их в окне iTunes, открыть Get Info через контекстное меню и, ничего там не редактируя, нажать Ok. iTunes просто обновит все теги из выделенных файлов.

AS IS


Несколько замечаний по поводу скрипта:
  1. Если Вы дорожите свой библиотекой, то возможно стоит сначала потестировать скрипт на нескольких файлах — мало ли что может произойти. У меня был back-up всей коллекции, поэтому я не волновался.
  2. Вполне возможно, что могут возникнуть проблемы при добавлении в тег TSOA слишком длинной строки (я не стал вчитываться в документацию по внутренней структуре тег-блока в mp3-файлах). Самая длинная строка в теге TSOA (с моим форматом %artist% — %year% — %album%), которую мне довелось встретить в своей библиотеке, содержала 93 символа. Тег успешно сохранился в файле и все у него было хорошо.
  3. Скрипт без проблем обработал 5000 файлов с англоязычными названиям. Русской музыки у меня в библиотеке не оказалось, но тест показал, что теги в файлы с кириллицей в названии успешно добавляются. Чтобы в консоль (cmd) названия русскоязычных файлов выводились в читабельном виде пришлось добавить декодирование названия (строка 58). Дело было под Windows, как под другими осями — не знаю. Поэтому если Вы не имеете дела с русскими названиями файлов, лучше пожалуй эту строку закомментировать.
  4. Немецкие и прочие названия файлов с диакритическими знаками лично у меня обрабатываться не захотели. Как решить проблему я пока не выяснил. Проблема в том, что файл называется, например, «Verführer.mp3», а скрипт получает название «Verfuhrer.mp3». Файла с таким именем в папке, ясное дело, нет. Опять-таки это все Windows. Лично у меня диакритические знаки встречаются в тегах (там юникод и все отлично), но в названиях файлов я их избегаю. Поэтому все прошло успешно.

Послесловие


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

На этом пока все. Буду рад, если кому-то этот скрипт окажется полезным или по крайней мере интересным.

PS
Кто-нибудь может подсказать, что побудило s-c.me добавить пробел перед нулем в 22ой строке? В исходнике его не было. Если же в html-е убрать в этом месте nbsp, то вместе с пробелом пропадает и 0. А это не хорошо.
+19
7 февраля 2010, 21:29
18
KL7

комментарии (28)

0
Deavy #
Если честно, я еще ни разу не испытывал неудобства от алфавитной сортировки именно в плеере.
Но за решения спасибо, авось когда-нибудь пригодится :)
+1
KL7 #
Я тоже неудобства не испытывал. Было просто ощущение неудовлетворенности =)
После миранды и firefox-а (с его плагинами) в iTunes оказалось слишком мало настроек. Я был искренне удивлен.
0
alkk #
Лично я предпочитаю работать с айтюнсом через его COM интерфейс, примерно так:
0
alkk #
self.iTunes = win32com.client.gencache.EnsureDispatch(«iTunes.Application.1»)

allTracks = self.iTunes.LibraryPlaylist.Tracks
for i in xrange(1, allTracks.Count + 1):
  track = self.iTunes.getTrackFromCollection(allTracks, i)
0
KL7 #
Спасибо за информацию. Надо будет почитать — наверняка пригодится.
0
sigizmund #
Надо думать, то же самое можно сделать и через Applescript…
0
alkk #
Естественно. Под Win32 — COM, под OS X — Applescript.
0
KL7 #
А под Linux? =) Про Applelscript я слышал, кода пытался добавить подкасты из папки на диске в iTunes. Писали, что под OS X есть решение. В очередной раз расстроился, что пока мне мак не светит. Но потом и сам разобрался.
+3
KL7 #
Решение с использованием COM наверняка страдало бы нехваткой кроссплатформенности.
0
ptenez #
Мне интересно, а как это отображается на сортировке альбомов в меню все альбомы? И что отображается в правой колонке? (где обычно алфавит, который также служит и слайдером).

При использовании других названий в сортировочных полях — алфавит и слайдер окна расходятся в порядке.

Может создать список плейлистов вида Артист — Год — Альбом, так будут работать все варианты.

0
KL7 #
Что за правая колонка? Просто я имею дело с iPod Classic. Единственный алфавит, который я там видел — это когда кучу альбомов прокручиваешь (при обычной сортировке), то он начинает скролить по первой букве названия альбомов. Только что проверил: при моей сортировке он скролит по первой букве артиста, как и должно быть. Так что все работает.

Вариант с плейлистами я сразу отмел, потому что сильно много будет мороки со всеми этими плейлистами. К тому там не будет albumart-ов, которые доставляют мне большое удовольствие =)
0
ptenez #
Я говорил про список альбомов — blogs.pcworld.com/staffblog/archives/LIST%20VIEW%20BETTER.jpg (скрин немного не тот, но со списком альбомов ситуация та же)
–3
hmage #
Аааа1!!!!11 Виндовый itunes!!!111
0
Evengard #
Благодарю за статью — очень познавательно и полезно…
Насчёт кодировки символов с диакритическими знаками — это морока только Винды, ибо:
Во первых кодировка имён файлов в FS явно не благородный utf
Во вторых, от этого глючит и сама винда (много имел дела с французской виндой, там папка Пуска имела диакритические символы — и при переводе кодовой страницы на русскую все установщики шли лесом и добавляли в папку без этого диакритического символа — как следствие ничего в Пуске не отображалось)
В третьих, уверен на 98%, смени ты кодировку перед процессом на немецкую — всё прошло бы как по маслу
0
Evengard #
Под кодировкой имел ввиду кодовую страницу, code page
0
pavelzuev #
Имена файлов в NTFS всегда были юникодные. Но многие программы (включая python 2.x и разные инсталляторы) используют старые системные API (например, CreateFileA), работающие с именами файлов в однобайтовой кодировке. По умолчанию эти API используют кодировку, указанную в настройках винды в качестве «языка программ, не поддерживающих юникод». Юникодные имена прозрачно для программы конвертируются в выбранную кодировку и обратно, но хитрые диакритические знаки и символы из других языков при этом сильно страдают.
0
Evengard #
Сори, был неправ, исправлюсь… Давно вдумчиво на Виндах не сидел, всё больше на Линуксах :)
А ведь верно, и частенько создатели программ выдают два вида исходников: юникодные и неюникодные… (пример — миранда) Неюникодные не работают на Вин, которые не из линейки NT — в тогдашнем api вообще не было CreateFileW и компании… Так что обычно это делают из за обратной совместимости.
0
Evengard #
Тьфу, не исходников, а бинарников! это флаг при сборке же… для умных компиляторов во всяком случае :)
+2
sfoid #
На самом деле, если вы работаете с iTunes для Windows, то есть путь проще, средствами windows script и iTunes COM SDK. Пример можно посмотреть тут sfoid.habrahabr.ru/blog/81291/
Этот SDK предельно простой и довольно удобный, можно достаточно просто и гибко управлять iTunes библиотекой и дофичивать автоматизацию всего, что нужно.
Хотя ваше решение кросплатформенное, это очень хорошо, уважаю.

Что меня смущает во всех этих скриптах (и в моем, и в вашем), это низкая их интеграция со всем остальным в системе. Например, если вы обновили свою библиотеку, вам надо повторно запускать ваш скрипт руками. В этом смысле есть идея написания небольшого «демона», который будет следить за событиями в iTunes и запускать нужные скрипты. Например, в iTunes COM SDK есть событие OnDatabaseChangedEvent, т.е. обновление библиотеки iTunes (добавился альбом, появился новый подкаст и т.д.). Правда времени на этого «демона» сейчас, к сожалению, нет.
+1
KL7 #
>если вы работаете с iTunes для Windows
Так и есть, но большой гордости от этого не испытываю =)

Я даже не знаю, что меня больше интересовало: решение проблемы или кодинг на Python-e. Так что своим решением я доволен. Плюс ко всему оно заполняет только теги, поэтому даже не привязано к iTunes-у. Думаю, если заливать через другой какой-нибудь менеджер, то результат будет тот же. А COM — это получится только под Windows и iTunes.

Но запуск скрипта вручную и меня смущает. Музыка у меня появляется весьма организованно, поэтому проблемы не будет запустить ее в новых папках, но все равно как-то не солидно =)
–4
neithere #
Есть более радикальный способ: поставить RockBox. :)

Честно говоря, думал лучше об исходной прошивке айпода. Самому сталкиваться с ней не приходилось. Отсутствие хронологической сортировки раздражало бы неимоверно.
0
KL7 #
Если мне не изменяет память, то только на Classic RockBox и не ставится. Честно говоря и не очень-то и хотелось.
0
rule #
Прикольно и просто, спасибо, может покапаюсь попозжее.
Хороший рецепт вообще.
+1
pepelsbey #
Имеет смысл обрезать у названий альбомов «The», как делается с исполнителями.
Чтобы два альбома одного года сортировались по алфавиту.
0
KL7 #
Лично меня это обрезание «the» только сбивает. Потому что The Cranberries я воспринимаю именно как THE Cranberries и поэтому в первое время очень расстраивался, когда не находил их в конце списка =)

А если Вам это действительно надо, то берите скрипт и редактируйте =)
+1
pepelsbey #
Меня как раз наоборот всегда смущало почему The Smiths и The Doors должны находиться рядом с Thom Yorke. И я рад поведению iTunes.

Я исключительно предложил, мне самому скрипт не очень нужен.
0
KL7 #
Прекрасно Вас понимаю. Возможно я слишком ответственно отношусь к артиклям =)
Но со временем привык и сейчас проблем не возникает.
0
icekeeper #
Спасибо огромное за статью! Уже третий год являюсь счастливым обладателем айпода, и сколько бы я не гуглил на эту тему, решения проблемы не находил. А использовать дополнительные теги не догадывался… Попробую то же самое на Applescript теперь реализовать, для удобства.

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