HOW-TO: Программа на Python, Часть 9 Сравнение версий

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
fullcircle:35:python_ч_9 [2010/06/25 19:37]
fullcircle:35:python_ч_9 [2011/02/20 16:26] (текущий)
Строка 3: Строка 3:
 //​Автор — Грэг Валтерс (Greg Walters)// //​Автор — Грэг Валтерс (Greg Walters)//
 </​style>​ </​style>​
 +
  
   - [[..:​27:​python_ч_1|Программа на Python — часть 1]]   - [[..:​27:​python_ч_1|Программа на Python — часть 1]]
Строка 15: Строка 16:
   - [[..:​36:​python_ч_10|Программа на Python — часть 10]]   - [[..:​36:​python_ч_10|Программа на Python — часть 10]]
   - [[..:​37:​python_ч_11|Программа на Python — часть 11]]   - [[..:​37:​python_ч_11|Программа на Python — часть 11]]
 +  - [[..:​38:​python_ч_12|Программа на Python — часть 12]]
 +  - [[..:​39:​python_ч_13|Программа на Python — часть 13]]
 +  - [[..:​40:​python_ч_14|Программа на Python — часть 14]]
 +
 +Ваша любимая музыка тоже хранится на вашем компьютере в формате MP3? Тогда мы с вами в чём-то похожи. Согласи-тесь,​ когда у вас в пределах тысячи музыкальных файлов,​ то довольно просто разобраться что и где лежит. Но у меня, нап-ример,​ музыки значительно боль-ше,​ потому что раньше я был диджеем и уже давно перевёл большую её часть в MP3. Снача-ла главной головной болью было пространство на жёстком диске, а теперь — вспомнить,​ что у меня есть и где это искать.  ​
 +В этой и следующей частях мы рассмотрим,​ как организо-вать собственный каталог MP3-файлов. Заодно пройдёмся по новым свойствам языка Python и вспомним,​ как работать с базами данных.
 +
 +Начнём с того, что MP3-файл может содержать информацию о самом себе. Эта информация может включать в себя назва-ние песни, название альбома,​ имя исполнителя,​ что угодно. Она хранится в специальных ID3-метках и носит название «метаданные». Раньше в MP3-файлах можно было хранить только ограниченное количес-тво информации. Она записыва-лась в конец файла блоком в 128 байт и из-за этого макси-мальный размер названия пес-ни,​ имени исполнителя или какой либо другой информации не мог превышать 30 байт. Этого вполне хватало для большинства файлов,​ но для одной из моих любимых песен под названием «Clowns (The Demise of the European Circus with No Thanks to Fellini)» этого явно было недостаточно. Мно-гих такая ситуация не устраива-ла,​ поэтому стандарт меток ID3 был переименован ID3v1, и был создан новый формат,​ названный,​ как ни странно,​ ID3v2. Такая метка позволяет хранить данные произвольной длины и размещается она в самом начале файла, в то время как старые метаданные ID3v1 всё так же добавляются в конец файла для поддержки старых проигрывателей. Общий размер контейнера метаданных может составлять до 256Мб, и такой размер просто идеально подходит для радиостанций и сумасшедших парней,​ вроде меня. В метке ID3v2 каждый блок информации хранится во фрейме,​ у которого есть свой идентификатор. В ранних версиях ID3v2 длина иденти-фикатора фрейма равнялась трём символам,​ а последняя версия (ID3v2.4) использует уже четыре символа.
 +
 +Раньше,​ когда ещё не сущес-твовало удобных библиотек для работы с ID3-метками,​ нам пришлось бы открывать файл в бинарном режиме и ковыряться в нём в поисках нужной инфор-мации,​ что заняло бы уйму вре-мени. К счастью,​ сейчас таких библиотек создано достаточ-ное количество,​ и мы восполь-зуемся одной из них, которая называется Mutagen. Вам следует открыть Synaptic и установить пакет python-mutagen. Если при желании задать «ID3» в поиске Synaptic, то найдётся более 90 пакетов (в версии Karmic). Но если добавить к поиску «Python», то отобразят-ся восемь. У каждого пакета есть свои плюсы и минусы. Для нашего проекта мы будем ис-пользовать Mutagen, а осталь-ные я предлагаю вам изучить самостоятельно.
 +
 +Как только Mutagen установ-лен,​ приступаем к программи-рованию.
 +
 +Создайте новый проект и назовите его «mCat». Начнём с подключения модулей.
 +
 +<​code>​from mutagen.mp3 import MP3
 +
 +import os
 +
 +from os.path import join,​getsize,​exists
 +
 +import sys
 +
 +import apsw</​code>​
 +
 +Большая часть этого кода должна быть уже вам знакома. Далее создадим заголовки наших функций.
 +
 +
 +<​code>​def MakeDataBase():​pass
 +def S2HMS(t):​pass
 +def WalkThePath(musicpath):​pass
 +def error(message):​pass
 +def main():pass
 +def usage():​pass</​code>​
 +
 +Ага, что-то новенькое. Теперь у нас есть две функции:​ main и usage. Давайте добавим ещё кое-что и после этого обсудим,​ для чего они нужны.
 +
 +<​code>​codeif __name__ == '​__main__':​main()</​code>​
 +
 +Это ещё что? А это такой специальный приём, который позволяет использовать наш код одновременно и как самос-тоятельное приложение,​ и как подключаемый модуль. Эту часть можно озвучить так: «Если файл вызван как самосто-ятельное приложение,​ то вызы-ваем функцию main. В против-ном случае позволим внешнему приложению импортировать наши функции и вызывать их напрямую.»
 +
 +Далее мы реализуем функцию usage. Её полный код приведён ниже.
 +
 +Здесь мы организуем вывод сообщения на экран пользова-теля в случае,​ если программа была запущена без необходи-мого для работы параметра. Обратите внимание,​ что мы используем «\n» для перехода на новую строку,​ «\t» для вставки табуляции и «{0}» для подстановки имени приложения из переменной sys.argv[0]. После этого мы выводим сообще-ние на экран с помощью функ-ции error и выходим из приложе-ния (sys.exit(1)).
 +
 +Теперь давайте создадим функцию error.
 +
 +<​code>​def error(message):​
 +print >> sys.stderr, str(message)</​code>​
 +
 +Тут мы используем операцию перенаправления («>>​»). С по-мощью функции print мы сообщаем интерпретатору Python о нашем намерении вывести данные на устройство вывода (например,​ терминал,​ в котором работает наша программа). Для обычного вывода негласно используется поток stdout, а для вывода информации об ошибках — stderr, который тоже является терминалом. В нашей програм-ме мы перенаправляем вывод в поток stderr.
 +
 +А теперь самое время занять-ся функцией main. В ней мы соз-дадим подключение к базе данных и курсор для работы с запросами,​ проверим парамет-ры командной строки и, если всё прошло удачно,​ то вызовем другие функции,​ чтобы они сделали остальную часть работы:​
 +
 +Так же, как и в прошлый раз, для работы с базой мы создаём две глобальные переменные:​ connection и cursor. Затем с помощью sys.argv проверяем параметры командной строки. Нам должны быть переданы два параметра:​ имя нашего приложения (передаётся систе-мой автоматически) и путь к каталогу с MP3-файлами. Если эти два параметра не переда-ны,​ то вызываем функцию usage, которая выводит справку о программе на экран и завер-шает работу. А если переданы,​ то мы оказываемся в другой ветке оператора IF, где мы помещаем значение второго параметра в переменную StartFolder. Обратите внимание,​ что если в пути до каталога встречаются знаки пробела (например /​mnt/​musicmain/​Adult Contemporary),​ то часть строки после пробела будет считаться уже следующим параметром,​ поэтому всегда заключайте такие параметры командной строки в кавычки. Далее мы устанавливаем соединение и курсор,​ создаём базу данных,​ выполняем необходимую работу с помощью функции WalkThePath,​ закрываем курсор и соединение и в конце сообщаем пользователю об успешном окончании работы. Полный код функции WalkThePath доступен по адресу http://​pastebin.com/​CegsAXjW.
 +
 +Сперва мы сбрасываем все три счётчика,​ с помощью кото-рых мы будем отслеживать ход процесса. Затем открываем файл, в который будем записы-вать сообщения об ошибках,​ если они произойдут. После этого выполняем рекурсивный обход переданного в качестве параметра каталога,​ начиная с самого каталога и «заглядывая» во все внутренние каталоги в поисках файлов с расширением «.mp3». Чтобы вести подсчёт количества данных,​ которые мы уже обработали,​ мы на каждом шаге увеличиваем счётчики каталогов и файлов. Далее мы обрабатываем каждый найден-ный файл. Сначала сбрасываем локальные переменные,​ в кото-рых хранится информация о каждой песне. Затем, с помощью функции join (из модуля os.path) мы формируем полное имя файла, чтобы Mutagen смог его найти, пере-даём это имя в класс MP3 и получаем объект «audio». Далее мы проходим по всем ID3-меткам в этом файле и сохраняем значения интересующих нас меток во временные перемен-ные. Это позволяет нам свести ошибки к минимуму. Обратите внимание на часть кода, кото-рая обрабатывает номер песни. Mutagen может вернуть номер песни как в виде числа, так и в виде строки «4/18», и даже в виде массива _trk или же вовсе не вернуть ничего. Поэтому для обработки возможных ошибок мы используем блок try/except. Теперь посмотрите,​ каким образом мы заносим значения в базу данных. Мы это делаем несколько иначе по сравнению с прошлым разом. В принципе,​ мы создаём точно такой же SQL-запрос,​ но вместо значений ставим «?», а сами значения подставляем уже в вызов cursor.statement. Веб-сайт ASPW утверждает,​ что это наилучший способ передачи данных,​ и я с ними вполне согласен. В конце мы обрабатываем все возмож-ные ошибки. В основном это ошибки типов TypeError или ValueError, которые могут возникнуть из-за символов Unicode, которые не получилось обработать. И ещё, обратите внимание на наш хитрый способ форматирования строк. Вместо символа подстановки «%» мы используем «{0}». Этот способ является частью спецификации Python версии 3.0 и выше, и общий синтаксис такой подста-новки выглядит следующим образом:​
 +
 +<​code>​print('​String that will be printed with {0} 
 +number of statements'​.format(replacement values))</​code>​
 +
 +В вызовах функций efile.writelines,​ помимо нового способа,​ мы так же используем и старый способ.
 +
 +В конце стоит взглянуть на функцию S2HMS. Эта функция преобразовывает длину песни, которую Mutagen возвращает как количество секунд,​ в один из двух форматов:​ либо в строку формата «Часы:​Минуты:​ Секунды»,​ либо в строку форма-та «Минуты:​Секунды». Посмот-рите на выражения возврата значения (return). Мы снова используем синтаксис форма-тирования Python 3.x, но привно-сим кое-что новое. У нас исполь-зованы три стандартных выра-жения подстановки. Но что это за «:02n» в конце первого и второго выражений?​ Это нужно для того, чтобы указать,​ что мы хотим дополнить указанные строки ведущими нулями таким образом,​ чтобы в случае если, например,​ длина песни состав-ляет 2 минуты и 4 секунды,​ то возвращаемая строка имела бы вид «2:04», а не «2:4».
 +
 +Полный исходный код нашей программы доступен по ссылке http://​pastebin.com/​rFf4Gm7E.
 +
 +Советую вам поискать в интернете подробную инфор-мацию о библиотеке Mutagen. Список её возможностей далеко не ограничен одними только MP3-файлами.
 +
 +=====Коды=====
 +
 +<​code>​def usage():
 +    message = (
 +       '​==============================================\n' ​      
 + 'mCat — находит все *.mp3 файлы в указанной и вложенных папках,​\n'​
 + '​\tчитает id3-метки и записывает информацию в базу данных SQLite.\n\n'​
 + '​Синтаксис:​\n'​
 + '​\t{0} <​имя_папки>​\n'​
 + '\t где <​имя_папки>​ — путь к вашим MP3-файлам.\n\n'​
 + '​Автор:​ Грег Уолтерс\n'​
 + '​Для Full Circle Magazine\n'​
 +       '​==============================================\n'​
 +       ​).format(sys.argv[0])
 +    error(message)
 +    sys.exit(1)
 +def main():
 +    global connection
 +    global cursor
 +    #​----------------------------------------------
 +    if len(sys.argv) != 2:
 +        usage()
 +    else:
 +        StartFolder = sys.argv[1]
 +        if not exists(StartFolder):​ # Из os.path
 +            print('​Путь {0} не найден… Завершение работы'​).format(StartFolder)
 +            sys.exit(1)
 +        else:
 +            print('​Работаем в каталоге {0}:'​).format(StartFolder) ​           ​
 +    # Создаём соединение и курсор.
 +        connection=apsw.Connection("​mCat.db3"​)
 +        cursor=connection.cursor()
 +   # Создаём базу данных,​ если она ёще не           существует…
 +        MakeDataBase()
 +   # Выполняем основную работу…
 +        WalkThePath(StartFolder)
 +   # Закрываем курсор и соединение…
 +        cursor.close()
 +        connection.close()
 +   # Сообщим,​ что работа завершена…
 +   print("​ГОТОВО!"​)
 +</​code>​
 +---------------------------------------
 +
 +<style center>
 +//​[[..:​35|К содержанию номера]]//​
 +
 +//​[[:​fullcircle|К архиву журналов]]//​
 +</​style>​
  
 +{{tag>​howto Full_Circle}}