Привет! Сегодня я разрешу самую заветную мечту любого mp3-варезника. Если ты бывал в шкуре оного, то тебе несомненно приходилось сталкиваться с индексацией всей твоей коллекции. Для этого требуется отдельный софт, который мы сегодня и напишем. Наша задача сводится к кодингу собственного mp3-менеджера, незаменимого для пирата 21 века, либо простого юзера с домашней коллекцией.

Кодить буду как всегда на Perl. Ибо это просто и без лишнего геморроя.
Перловый манагер снабжен рекурсивной функцией сканирования всех каталогов на диске (либо диска целиком). После того, как он находит mp3-файл, происходит переход на процедуру сдирания тегов. Ко всему этому логируется номер текущей сессии сканирования (для того, чтобы заводить коллекции сидюков). Уникальный нумбер также запоминается. Затем происходит занесение всех данных во временной файл. Как только скан закончился, вызывается функция экспорта данных в Excel-документ (а ты думаешь, все так просто? ;)) и только после всего этого, менеджер завершает работу. Ах да, забыл. Прежде чем запускать скан каталогов, читается экзель-документ с предыдущей сесией, из которого выдираются все значения и заносятся во временной файл. Это сделано потому, что модуль работы с Экзелем не позволяет дозаписывать инфу в старый Excel-файл :(.

Думаю, алгоритм понятен. Теперь рассмотрим код. В шапке скрипта мы видим следующее:

use File::Copy;
use Spreadsheet::ParseExcel;
use Spreadsheet::WriteExcel; #
Для работы с Excel
use Getopt::Std; #
Для обработки параметров
use MP3::ID3v1Tag; #
Для выделения mp3-тегов
use Cwd; #
Для определения текущего каталога
$path=getcwd(); #
Путь, где лежит сценарий.
$path=~s(\/)(\\)g; #
Приводим к виндовому виду
$dir=’f:\mp3′; #
Директорий с mp3-файлами
$tmp=»tmp.txt»; #
Временный файл
$export = «mp3.xls»; ##
Excel-документ
$debug = 1; #
Мусорим в консоль или нет
$startnumber = 1000; #
Используем фиксированный номер сессии
# $startnumber=1000+int(rand(9000)); #
Либо рандомный

Думаю, здесь все ясно. Юзается модуль для копирования файла (его применение ты увидишь позже), два модуля для экзеля. Первый — читает, второй пишет. Следующий модуль сканирует командную строку на наличие параметров. Их может быть три: путь, который сканируем рекурсивно, файл, в который экспортим инфу и фиксированный номер сессии.

Далее юзается модуль для выдирание mp3-тегов. Он очень простой, поэтому останавливаться на нем не буду. Следующие 2 строчки определяют текущий каталог в *nix-стиле. Он нужен для правильного открытия файлов, а также юзается в сабе сканирования. Затем задаем важные переменные, включая номер сессии. Он может быть рандомным (от 1000 до 9999).

Все просто? Отлично! Идем дальше. Далее необходимо отловить командную строку getopt()’ом и переопределить дефолтовые переменные, если они заданы с консоли. Следующая интересная конструкция выглядит так:

export_old()
if (-f «$export»); ##
Экспортируем старые данные

Этим мы вызовем процедуру записи старых данных, если файл экспорта существует. Что касается процедуры, вот ее красивый код.

sub export_old {
print «Got info about previous sessions…\n»;
my $excel = new Spreadsheet::ParseExcel; 
my $book = $excel->Parse(«$export»);
$workbook=$book->{Worksheet}[0];
for($i=2;$i<=$workbook->{MaxRow};$i++) {
$n=$workbook->{Cells}[$i][0];
$n=$n->{Val};
$f=$workbook->{Cells}[$i][1];
$f=$f->{Val};
$fn=$workbook->{Cells}[$i][2];
$fn=$fn->{Val};
$ar=$workbook->{Cells}[$i][3];
$ar=$ar->{Val};
$tit=$workbook->{Cells}[$i][4];
$tit=$tit->{Val};
$alb=$workbook->{Cells}[$i][5];
$alb=$alb->{Val};
$year=$workbook->{Cells}[$i][6];
$year=$year->{Val};
open(TMP,»>>$tmp»);
print TMP «$n;;$fn;;$ar;;$tit;;$alb;;$year\n»;
close(TMP);
$number_write = $n, $count = $i 
if ($i eq $workbook->{MaxRow});
}
}

Не привожу комментариев с целью экономия места :). Скажу, что модуль Spreadsheet::ParseExcel очень сложен в понимании и у меня ушло минут 10, чтобы разобраться в наглядном примере. Собственно, происходит открытие xls от предыдущей сессии, создание цикла от 2 (первые две колонки в экзеле отведено под заголовок и пустую строку). Получим значения номера сессии, имени файла, имени пути, артиста, название песни, название альбома и год выпуска… и запишем все это во временной файл. Также запомним номер
последней сессии для последующей инкрементации.

После отработки сабы идет рекурсивный поиск mp3-шек. Код рекурсии не привожу, ибо
писал об этом отдельную статью. В этой процедуре одним интересным моментом является перевод крякозябриков в читабельные символы (которые в DOS-окошке мелькают). Вот эти заветные строчки:

$td=»$dir$_»;
$td=~tr/абвгдеёжзийклмнопрстуфхцч
шщъыьэюяАБВГДЕЁЖЗИЙКЛМНОП
РСТУФХЦЧШЩЪЫЬЭЮЯ/ ЎўЈ¤Ґс¦§Ё©Є«¬®Їабвгдежз
ийклмнопЂЃ‚ѓ„…р†‡€‰Љ‹Њ ЌЋЏђ‘’“”•–—™љ›њќћџ/;
##
?™љ›њќћџ/; ## Преобразуем крякозябры в
print «Processing directory: $td\n» if $debug; ##
Напишем, что это директория.

Все остальное уже было описано ранее.

Как только наткнулись на mp3 — вызываем сканирование тегов. Этим занимается красивая саба с названием strip_tags().

sub strip_tags { ## Процедура для работы с mp3-тегами
my($file,$tags)=@_; ##
Получаем файл из верхнего модуля
my($mp3_file,$mp3_title,$mp3_artist, $mp3_album,$mp3_year);
if ($tags) { ##
Если необходимо выдирать теги
$mp3_file = new MP3::ID3v1Tag(«$file»,1) || return; ##
Открываем его только для чтения тегов
$mp3_title = $mp3_file->get_title();
$mp3_artist = $mp3_file->get_artist();
$mp3_album = $mp3_file->get_album();
$mp3_year = $mp3_file->get_year(); ##
Получаем теги: название, артист, альбом, год
}
open(TMP,»>>$tmp»); ##
Открываем временный файл для занесения данных.
print TMP «$number;;$file;;$mp3_artist;;$mp3_title;;
$mp3_album;;$mp3_year\n»; ##
Заносим данные для конкретного трека. В случае не mp3-файла, все $mp3_ станут undef и с-но в Ёкселе будут пустые поля
close(TMP) ##
Закрывам временный файл
}

Параметр $tags означает необходимость сканирования (помимо mp3 менеджер умеет искать и ogg, и wma, и midi-файлы). Единичка в конструкторе ID3v1Tag означает открытия файла на чтения (модуль умеет записывать теги, поэтому по дефолту открывает файло на запись). Представь, что поиск ведется на CD или других защищенных от записи носителях — при открытии звукового файла в режиме записи, произойдет фатальный сбой. Как видно, все данные дозаписываются в TMP-файл (если была предыдущая сессия — во временном файле мы будем иметь уже две просканированных сессии). Следует помнить, что $number — фиксированное число, которое без проблем пишется в тот же TMP-файл. Что я сделал с этим числом, чтобы разделить сессии — смотри дальше.

Наконец, последняя процедура называется export_data(). Она экспортирует все данные из временного файла в полноценный excel-документ.

sub export_data { ## Процедура сохранения информации в Excel
if (-f $export) {
copy($export,»$export.bak»); ##
Создаем бэкап, если файл существует
}
my $excel_save = Spreadsheet::WriteExcel->new(«$export»); ##
Откроем файл для экспорта
my ($ph);
$excel = $excel_save->add_worksheet(«mp3»); ##
Добавляем лист с названием mp3
$format = $excel_save->add_format(); ##
Задаем новый формат записи
$format->set_bold(); ##
А именно: жирный цвет
$excel->set_column(0,0,5); 
$excel->set_column(1,1,50); 
$excel->set_column(2,2,100); 
$excel->set_column(3,3,30);
$excel->set_column(4,4,30);
$excel->set_column(5,5,30); 
$excel->set_column(6,6,4); ##
Устанавливаем оптимальные размеры колонок
$excel->write($x,0,»№ CD», $format);
$excel->write($x,1,»Файл», $format);
$excel->write($x,2,»Имя файла», $format);
$excel->write($x,3,»Исполнитель», $format);
$excel->write($x,4,»Название», $format);
$excel->write($x,5,»Альбом», $format);
$excel->write($x,6,»Год», $format); ##
Заполняем шапку таблицы жирным цветом
$excel->write($x++,0,»»); ##
А также заносим пустую строку
open(TMP,»$tmp»); ##
Открываем временный файл (уже для чтения)
while() { ##
Движемся по каждой строке 
$x++; #
Инкрементируем положение строки
($n,$f,$a,$t,$al,$y) = split(‘;;’); ##
Получаем данные из временного файла по каждому треку
chomp($y); ##
Обрезаем символ конца строки
@fields = split (/\\/,$f); ##
Сплитуем полный путь файла на составляющие по
‘\’

$fn=$fields[$#fields]; ##
Последний элемент — имя файла
if ($count && $x >= $count+1 ) { $n = $number_write+1 }
$excel->write($x,0,$n);
$excel->write($x,1,$fn);
$excel->write($x,2,$f);
$excel->write($x,3,$a);
$excel->write($x,4,$t);
$excel->write($x,5,$al);
$excel->write($x,6,$y); ##
Перегоняем параметры в созданный лист
}
close(TMP); ##
Затем закроем временный файл
unlink(«$tmp»); ##
И удалим его
}

Прежде чем, что либо делать, создадим бэкап файла. Это убережет от многочисленных потерь. Затем откроем файл для записи. Внимание, при вызове конструктора, инфа из документа бесследно исчезнет. Добавим в xls лист mp3 и будем работать только с ним. Прежде чем, что либо записывать, произведем добавление формата жирного цвета, установку оптимальных размеров колонок и запись заголовков. Их имена смотри выше. До кучи добавляем пустую строчку и начинаем работу по существу. Нехитрым сплитом строк во временном файле выделяем все параметры. После этого еще раз сплитанем полное имя файла по символу ‘\’. Это нужно для выделения конечного имени. Регвыры юзать не хотелось, поэтому я обошелся обычной функцией split() и временным массивом @fields. Последний его элемент как раз и будет являться именем текущей mp3-шки. Затем смотрим: если существует переменная $count (она равна числу записей в прошлых сессиях), то сессий несколько. Как только мы пересекли границу предыдущих, инкрементируем номер текущей сессии на единицу и записываем его в Ёксель-документ. Таким образом, происходит полное разграничение всех сканенных сессий. После проработки цикла, удалим временной файл и закончим работу.

Вот и весь простой алгоритм. Я тебе выложил все свои мысли на блюдечке, чтобы ты понимал принцип создания подобных проектов. Что касается скорости, то на кусочную запись 40 гигового mp3-архива затратилось всего 5 секунд, а на чтение сессий около 10 секунд. Я считаю это великолепным результатом, если учитывать то, что MP3::ID3v1Tag кэширует все теги при повторной индексации. Таким образом, последний скан занял у меня всего 15 секунд.

Прежде чем юзать менеджер, обязательно установи нужные модули. Для этого запусти ppm, набери install Spreadsheet::ParseExcel (например) и жди положительного результата. Готовый проект ты можешь скачать по адресу
http://kamensk.net.ru/forb/1/x/mp3.tar.gz.

Оставить мнение

Check Also

Конкурс хаков: пишем на PowerShell скрипт, который уведомляет о днях рождения пользователей Active Directory

В компаниях часто встречается задача уведомлять сотрудников о приближающихся днях рождения…