Показаны сообщения с ярлыком id3project. Показать все сообщения
Показаны сообщения с ярлыком id3project. Показать все сообщения

13 июл. 2009 г.

id3project is alive!

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

но о чём это я?.. об id3project!
за эту неделю я многое сделал. хотя вспомню я сейчас не всё и не в хронологическом порядке.
помимо изменений в самой библиотеке, о которых речь пойдёт дальше, я начал использовать darcs - хаскеллевскую систему контроля версий. то есть он у меня и раньше был. но теперь я попробовал подружиться с ним и действительно понять зачем мне это надо и как что-нибудь с его помощью сделать. удобно, наверное..
а ещё я нашёл бесплатных хостинг для проектов использующих darcs на patch-tag.com
не знаю что в нём ещё хорошего, кроме того, что он бесплатный и для darcs.. но мне-то и этого хватит сполна.
итак, дело приняло серьёзный оборот! думаю, скоро можно будет уже выкладывать библиотеку в hackage.

теперь, о самой библиотеке:
- были сделаны значительные усовершенствования в структурах данных, хранящих информацию тега. не упоминая конкретики самой структуры, сложно рассказать о каких-то изменениях в общем. упомяну лишь, что я стал использовать (впрочем давно) такую штуку как аксессоры из пакета data-accessor (не знаю как это можно разумно перевести). все структуры данных, которые я использую - записи (records). у записей есть поля содержащие информацию. так вот стандартный интерфейс доступа к этим полям в хаскелле довольно неудобный (имхо). аксессоры предлагают (1) более удобный интерфейс, (2) абстракцию, которая позволяет менять эти методы доступа к полям записи. так что когда я менял структуры, мне не приходилось исправлять код везде, где они используются - я просто менял аксессоры.
- функция чтения тега из файла стала лучше, проще, надёжнее.
- функция записи тега в файл была наконец таки сделана! и если я чего-нибудь не упустил,, работает она не только правильно, но и эффективно. в чём была сложность: тег хранится вначале музыкального файла. у тега есть паддинг - резервное место для дописывания (кусок файла забитый нулями). это нужно для того, чтобы если тег увеличится, не переписывать весь файл заново (у меня например встречаются mp3 по 30-40 мегабайт), а записать его поверх старого, уменьшив тем самым паддинг (пэддинг). я разобрался как это сделать аккуратно и вроде бы у меня получилось.
- и наконец, самое(?) главное! я сделал "простой интерфейс" для этой библиотеки. простой интерфейс в кавычках, потому что это - во-первых (мягко говоря) урезанная функциональность, во-вторых это называется интерфейсом в смысле программирования, а не пользования. то есть интерфейс для тех, кто программирует на хаскелле. он действительно простой. но если это то, ради чего я делал всё что я делал (а это разумеется не так),, то я был настоящим извращенцем - это как строить большой, очень надёжный самолёт, с хорошими аэродинамическими характеристиками, в котором для пилота предусмотрена удобная приборная панель с бесчётным числом рычажков, кнопочек, тумблеров и прочих элементов управления,, а потом сделать для него пульт дистанционного управления (это ведь удобно) с двумя кнопками (всё просто): "/ехать/ вперёд" и "/ехать/ назад" (ничего лишнего).
вот примерно также и с этим моим простым интерфейсом..
там есть несколько функций такого вида:
getArtist :: Tag → Maybe String
она берёт тэг и возвращает либо Nothing - ежели информации об исполнителе в тэге не имеется, либо (Just "имярек") - ежели исполнитель имярек.

setArtist :: String → Tag → Tag
эта функция берёт имя которое вы хотите записать, тэг и возвращает изменённый тэг. при этом если информация об исполнителе в тэге имелась - она будет изменена,, а если е\ не было - будет создана.

в модуле ещё несколько точно таких же функций для названия трека, номера трека, года записи и названия альбома - в ближайшее время добавлю ещё парочку (комментарий, жанр и т.п.). всё очень просто - даже не надо знать что такое Tag. кстати, откуда он тогда берётся? из файла очевидно,, с помощью функции readTag:
readTag :: FilePath → IO (Maybe Tag)
она берёт имя файла, а возвращает "может быть тэг". это значит, что если в данном файле есть id3-тэг - значение будет иметь вид (Just tag),, а если нет - Nothing.

ну и для полноты картины надо упомянуть обратную функцию - для записи тега в файл:
writeTag :: FilePath -> Tag -> IO ()
куда писать и что писать - всё просто.

итак,, стоит написать маленький пример, чтобы показать как это всё работает и о чём вообще речь.
суть нижеследующего листинга в следующем: мы прочитаем тэг, посмотрим, есть ли в нём информация о номере трека, и если есть, отредактируем - если это цифра, то поставим перед ней ноль. по-моему вполне разумное изменение - некоторые плееры некорректно сортируют треки по номерам, если не проставлены нули слева:
1, 10, 11, 2, 3 .. - это происходит потому, что номер трека хранится в теге ввиде строки, а не числа и в данном случае эти строки сортируются в лексикографическом порядке - по алфавиту.
а мы сделаем так: 01, 02, 03 .. 10, 11.


 1 import System       (getArgs)
 2 import ID3.Simple
 3
 4 main = do
 5     files ← getArgs                         -- аргументы программы - список файлов, для редактирования
 6     mapM_ (withTag correctTrack) files      -- применим к каждому преобразование
 7
 8 -- в этой функции мы изолируем ввод-ввывод    (надо бы мне добавить её в библиотеку)
 9 withTag :: (Tag → Tag) → FilePath → IO ()
10 withTag function file = do
11     smth ← readTag file                            -- читаем "может быть тэг" из файла
12     case smth of
13          Nothing  → return ()                      -- если тэга в файле нет, то не делаем ничего
14          Just tag → writeTag file (function tag)   -- если тэг есть, преобразуем его и запишем обратно в файл
15
16 -- теперь, собственно, само преобразование
17 correctTrack :: Tag → Tag
18 correctTrack tag =
19     case getTrack tag of                 -- смотрим номер трека в тэге
20          Nothing  → tag                  -- если его там нет, то возвращаем тэг без изменений
21          Just num → if length num ≡ 1    -- а если есть - смотрим цифра ли это (один символ в строке)
22                        then setTrack ('0':num) tag     -- если да - добавляем слева ноль и пишем в тэг
23                        else tag                        -- если нет - ничего не меняем

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

стоит ещё сказать о том, как проверить, что вообще что-то произошло - как посмотреть сам тэг. очень просто: print tag.
поскольку я сделал все структуры данных используемые в библиотеке членами класса Show, можно просто выводить структуру на экран без дополнительной обработки. при этом информация будет вполне читабельно форматирована.

пост получился и так уж слишком большой.. так что о том, что ещё собираюсь сделать напишу в следующем.

p.s.
перечитал и заметил, что пишу то "тэг", то "тег".. а как правильно?

6 февр. 2009 г.

Синхрофазотрон и теги

не последние (продолжение следует) новости из мира id3project:
сегодня я доделывал обратное преобразование структур тегов в байт-последовательность. и всё бы хорошо,, ведь делается это просто: создаётся класс с одной полиморфной функцией(которая и делает преобразование) и для каждой структуры определяется как работает эта функция, а работает она рекурсивно - структуры-то составные,, вот и получается, что преобразование сложной структуры - это просто склейка преобразований её частей (и для частей выполняется тоже самое).

и вот из тех примитивов, для которых надо определять преобразование напрямую,, мне остались только числа (Integer) со значением размера. а тут особую сложность представляет такая неприятная штука как синхронизация "/ по англицки synchsafe integer - это что-то вроде синхробезопасное число о_О
я уже достаточно намучался с десинхронизацией в самом начале - поскольку размер тега и размеры фреймов в соответствии со стандартом id3v2.4 синхронизированы. до сих пор не понимаю зачем это вообще нудно делать /= но фиг с ним,, когда я разбирался с десинхронизацией, я не очень-то понял как она происходит,, грубо говоря, я просто взял какую-то готовую функцию на с++ и переписал на haskell'е. но теперь, когда мне нужно было сделать функцию обратную к этой - такой готовой в интернетах не нашлось. в общем мне пришлось разобраться с десинхронизацией и придумать обратную функцию для синхронизации. и я очень доволен что у меня это получилось (:

теперь вроде бы все структуры обратно-конвертируемы. и надо это проверять. то есть надо проверить на заданном множестве это преобразование - обратная к парсеру.
собственно будет повод разобраться с Quickcheck (или HUnit).

следующий шаг: тестирование, оформление и документирование того, что уже готово.

4 февр. 2009 г.

Трансформируемся! "/ тьфу! Абстрагируемся!

трудовой план на сегодня выполнен!
я внёс существенные изменения во внутреннюю структуру id3project. преобразования и улучшения были направлены на абстрагирование структур данных представляющих теги и их внутренности. теперь можно менять (усложнять/совершенствовать) эти структуры, не меняя при этом остальную часть библиотеки.
внешне же ничего не изменилось. по завершении работ, я запускаю тестовую программу точно также как и вчера,, и что я вижу?? да всё тоже самое! работает всё точно также как работало вчера, изменилиcm только внутренние связки и механизмы взаимодействия структур.

следующий шаг: обратное преобразование структур в данные для записи в файл.

3 февр. 2009 г.

readTag :: FilePath -> IO ID3Tag

доделал сегодня в более или менее приемлемой форме часть id3project отвечающую за чтение тегов.
то есть группу модулей с парсерами и модуль-обёртку с функцией, которая просто берёт файл и выдаёт весь тег в виде структуры данных.
тип этой структуры объявлен членом класса Show - так что можно просто применить функцию print к тегу и он будет выведен на экран в аккуратном, читабельном виде. и вообще можно таким образом преобразовывать структуру в текст, если нас не интересуют внутренние нюансы структуры тега.
сделал-то в общем я вчера. но сейчас подправил обработку кодировки - вроде всё правильно работает.

на настоящий момент 100%-ного соответствия стандарту нету (кстати речь пока только о id3v2). поскольку там много тонкостей с флагами - во-первых я не очень понимаю назначение некоторых из них,, а во-вторых некоторые из них требуют использования дополнительных библиотек, как то компрессия или перекодирование фреймов и тому подобные хитрости.

из поддерживаемых фреймов - основные используемые и те, которые не требуют особенной обработки. надо ещё сделать парсинг фрейма с обложкой альбома - для этого тоже придётся использовать отдельную библиотеку, благо такая вроде бы уже есть в хаскелле.

100% поддержка стандарта пока не предполагается,, поскольку это не очень-то нужно. я полагаю что в тегах редко используется компрессия и тому подобные заморочки - никто не заморачивается. да и из фреймов используется небольшой набор. другое дело что попадаются те, которые используются в реальности, но не описаны в стандарте - например я встретил фрейм TYER который содержит год выпуска альбома, но в стандарте для этого используется TDRC. поэтому важно добавить поддержку "неофициальных", но реально используемых фреймов.

а так в общем-то всё здорово. следующий шаг - обратное преобразование: корректная запись абстрактной структуры тега в файл. дальше замена, добавление, удаление, редактирование тега - это всё просто. ну и разумеется надо сделать для этого какой-то интерфейс..
сложно придумать "интуитивно-понятный" интерфейс - это идеал. надо будет почитать ещё Джефа Раскина на тему интерфейсов - у него есть интересные идеи. хотя для меня Vim уже опроверг одну из его заповедей - он категорически против модальности (то есть интерфейса с режимами),, а у Vim'а на этом построена вся идеология - "в одном режиме он всё портит, а в другом бибикает" (:

28 янв. 2009 г.

$ man id3-project

NAME
id3-project - прожект по созданию инструментов работы с музыкальными тегами формата id3.

SYNOPSIS
Этот прожект включает в себя библиотеку (на Haskell'е) и программу для работы с тегами музыкальных файлов формата ID3v2. Библиотека включает в себя обширный набор парсеров и инструментов, для чтения и редактирования тегов. Программа имеет простой но удобный консольный shell-интерфейс и оставляет возможности для расширения.

DESCRIPTION
В настоящее время, для Haskell'а нет библиотеки для работы с музыкальными тегами. За исключением ,TagLib которая, к сожалению, имеет весьма ограниченную функциональность и является лишь привязкой к одноимённой библиотеке на C++. Функции в ней определяются вызовами соответствующих с++-функций из TagLib. В id3-project предполагается создание native-haskell библиотеки.
Для чтения тегов используются монадические комбинаторы парсеров из библиотеки PolyParse.
Для консольного интерфейса используется библиотека Shellac. Она позволяет в декларативном стиле очень просто и быстро создать весьма удобный и легко переносимый командный shell-интерфейс с заменяемым backend'ом, который предоставляет набор дополнительной функциональности, такой как история команд или автодополнение.

OPTIONS
о Состояние
В настоящее время проект находится в стадии активной разработки.

о Программа минимум
+ Корректное чтение тегов в соответствии со стандартом.
- Корректная запись, добавление и удаление тегов.
- Консольный интерфейс.
- Работа с группами файлов.

о Программа максимум
- Всё то, что содержит "программа минимум".
- User-friendly графический интерфейс.
- Извлечение информации из имени файла и его расположения в соответствии с заданным образцом и запись в тег.
- Наоборот: извлечение информации из тега и корректировка имени файла.
- Индексация медиатеки - составление сводной базы данных с собранной по тегам информации.
- Поиск соответствий в онлайн-базе и корректировка/дополнение тегов.
- Нахождение "похожих" тегов в разный файлах для корректировки опечаток и т.п.
- ... всё, что только может понадобиться практической необходимостью и удобством.

BUGS
нет информации.

PORTABILITY
Portable. Библиотека должна одинаково хорошо работать как под *nix-системами так и под windows.

AUTHOR
Автор предпочитает не упоминать своё настоящее имя в общедоступных источниках информации, но известен его псевдоним и адрес электронно почты, по которому с ним можно связаться: laughedelic [at] gmail [dot] com

SEE ALSO
man haqid3 (not released yet)

STANDARDS
Официальный сайт стандарта id3

HISTORY
laughedelic's short story:
"Причина возникновения этого проекта заключается в том,, что у меня накопилось довольно много музыки (около 100GB) и я пользуюсь iTunes. А это означает, что музыка упорядочивается именно по тегам и даже более или менее чёткая структура расположения музыки в файловой системе не играет особенной роли. Естественно, что теги прописаны далеко не все, которые хотелось бы,, и далеко не во всех файлах. Существует множество программ для редактирования тегов и каталогизации музыки,, но ни одна из них не удовлетворила меня полностью в плане настраиваемости и функциональности. Поэтому я решил сделать свою программу. Всё просто.
Кроме того, для меня, как для программиста на Haskell, особое значение имеет свой вклад в дело т.н. lambda-revolution (:"


September 28, 2009


:_