🌤
📍 Определение...
📅 23.10.2026
👤 15
← Назад к новостям

Главное — уши! Пишем аннотатор музыкальной коллекции и учим PHP

В этой статье мы реали­зуем hexdump на PHP, изу­чим стро­ение тегов ID3v2 с информа­цией о содер­жимом MP3-фай­лов и будем их менять базовы­ми средс­тва­ми язы­ка. Заод­но ты нем­ного осво­ишь и сам PHP, что для пен­тесте­ра будет сов­сем не лиш­ним.

«Нам пес­ня стро­ить и жить помога­ет!» — заяв­лял Леонид Уте­сов. Сегод­ня стро­ить и жить нам помога­ют под­касты, лек­ции и ауди­окни­ги. Сов­ремен­ные тех­нологии поз­воля­ют дер­жать всю ауди­окол­лекцию под рукой и не зависеть от погоды, исполни­телей и рас­писаний выс­тупле­ний.

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

Му­зыка чаще все­го хра­нит­ся в MP3, а стан­дарт хра­нения метадан­ных в MP3 называ­ется ID3.

 

Как FFmpeg подвел меня

Для обра­бот­ки меди­афай­лов есть пре­вос­ходный инс­тру­мент коман­дной стро­ки — прог­рамма FFmpeg. Я час­то исполь­зовал его для переко­диро­вания ауди­оза­писей и кор­ректи­ров­ки их метадан­ных. Но однажды он меня под­вел.

Ауди­ома­тери­алы неред­ко пред­став­лены отдель­ными фраг­мента­ми, нап­ример ком­позици­ями аль­бома или гла­вами ауди­окни­ги. Обыч­но это удоб­но, но иног­да луч­ше иметь один файл с целым про­изве­дени­ем.

Ауди­одан­ные в MP3-фай­ле пред­став­ляют собой пос­ледова­тель­ность бло­ков, каж­дый со сво­им заголов­ком. Декоде­ры MP3 ищут в потоке дан­ных кор­рек­тные заголов­ки и обра­баты­вают соот­ветс­тву­ющие бло­ки, игно­рируя все осталь­ное. Поэто­му фраг­менты part1.mp3, part2.mp3 и part3.mp3 мож­но скле­ить в монолит­ный файл all.mp3 прос­той коман­дой. В Windows:

COPY /B part1.mp3 + part2.mp3 + part3.mp3 all.mp3

В Linux:

cat part1.mp3 part2.mp3 part3.mp3 > all.mp3

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

Та­кой эле­мен­тарный под­ход допус­тим, но мне боль­ше приг­лянулась реали­зация в спе­циали­зиро­ван­ной ути­лите Mp3Wrap. Она фор­миру­ет при­год­ный для прос­лушива­ния монолит­ный MP3-файл без переко­диро­вания и потери качес­тва, а при необ­ходимос­ти поз­воля­ет точ­но вос­ста­новить исходные фай­лы‑фраг­менты. Это удоб­но для архивно­го хра­нения кол­лекции: в монолит, помимо ауди­офай­лов, мож­но добавить любые дру­гие дан­ные.

Mp3Wrap

Ис­ходные тек­сты и бинар­ные фай­лы Mp3Wrap можешь заг­рузить с офи­циаль­ного сай­та. Для вос­ста­нов­ления фай­лов из моноли­та понадо­бит­ся ути­лита Mp3splt с ее сай­та.

В статье мы не будем ее исполь­зовать, но тебе могут при­годить­ся дру­гие ее фун­кции. Она поз­воля­ет раз­бивать MP3-, OGG- и FLAC-фай­лы на час­ти фик­сирован­ной дли­тель­нос­ти, по задан­ным отметкам вре­мени или с помощью встро­енно­го детек­тора тишины.

Да­лее я покажу обра­бот­ку MP3-фай­лов из аль­бома vintage исполни­теля Robert80z. Что­бы пов­торить экспе­римен­ты, можешь заг­рузить с Archive.org ар­хив jamendo-130889.zip с эти­ми фай­лами. Он занима­ет око­ло 14 Мбайт и содер­жит три ауди­отре­ка.

Что­бы объ­еди­нить тре­ки 01-1097626-Robert80z-Time Machine.mp3, 02-1097624-Robert80z-Boat Trip.mp3 в файл album_MP3WRAP.mp3, я выпол­нил коман­ду

mp3wrap album_MP3WRAP.mp3 *Robert80z*.mp3

Соз­данный монолит­ный MP3-файл поч­ти пол­ностью меня устро­ил, если бы не один прин­ципи­аль­ный момент. В фай­ле начис­то отсутс­тво­вали метадан­ные о записан­ных ауди­ома­тери­алах.

Мне бы хотелось, что­бы при вос­про­изве­дении моноли­та пле­ер показы­вал наз­вание аль­бома, имя исполни­теля и обложку. Вмес­то это­го я уви­дел мно­жес­тво рек­ламных над­писей о том, что файл соб­ран с помощью прог­раммы Mp3Wrap. И, естес­твен­но, никакой обложки.

Покупайте наших слонов!

В докумен­тации к прог­рамме ска­зано, что метадан­ные мож­но изме­нять без переко­диро­вания фай­ла. Я поп­робовал с помощью FFmpeg уста­новить для парамет­ра album (Название альбома) зна­чение vintage:

ffmpeg -i album_MP3WRAP.mp3 -c:a copy -id3v2_version 3 -metadata album="vintage" music_MP3WRAP.mp3

И про­верил целос­тность резуль­тата коман­дой

mp3wrap -l music_MP3WRAP.mp3

Вмес­то спис­ка фай­лов‑фраг­ментов на экра­не появи­лось сооб­щение:

Error: this is not a valid mp3wrap file!

Прог­рамма FFmpeg соч­ла дан­ные, внед­ренные в файл ути­литой Mp3Wrap, информа­цион­ным мусором и вмес­те с исправ­лени­ем метадан­ных про­изве­ла «убор­ку». Поэто­му от при­выч­ного инс­тру­мен­та приш­лось отка­зать­ся.

 

Выбор подхода

Как же быть? Быс­трый поиск в интерне­те под­ска­зал три под­хода к решению задачи:

  • он­лайн‑сер­висы;
  • спе­циали­зиро­ван­ные ути­литы;
  • пря­мое редак­тирова­ние MP3-фай­ла.

Пер­вый вари­ант я отверг с негодо­вани­ем, вто­рой отло­жил на край­ний слу­чай, а тре­тий выз­вал неожи­дан­ный инте­рес. У меня как раз появи­лось сво­бод­ное вре­мя, что­бы узнать что‑то новое. А орга­низа­ция метадан­ных в MP3-фай­ле — не кос­мичес­кая наука, дос­тупная лишь избран­ным.

По­раз­мыслив, я решил научить­ся редак­тировать опи­сания MP3-фай­лов на самом низ­ком уров­не. Пос­коль­ку руч­ная работа байт за бай­том в дво­ичном редак­торе вряд ли дос­тавила бы мне удо­воль­ствие, я стал выбирать средс­тво авто­мати­зации.

Как ока­залось, в стан­дар­тных биб­лиоте­ках зна­комых мне язы­ков прог­рамми­рова­ния нет средств работы с метадан­ными меди­афай­лов. Для Python, на который я силь­но рас­счи­тывал, обыч­но исполь­зуют допол­нитель­ный модуль Mutagen.

Тог­да почему бы не поп­робовать PHP? На мой взгляд, у некото­рых спе­циалис­тов сло­жилось к нему снис­ходитель­ное отно­шение как к несерь­езно­му язы­ку, чему‑то вро­де бей­сика в 1980-х. В управля­ющих струк­турах PHP дей­стви­тель­но есть чер­ты бей­сика (а еще язы­ка управле­ния базами дан­ных DBase, осо­бен­но в аль­тер­натив­ном син­такси­се без фигур­ных ско­бок).

Но, как и бей­сик, PHP — уни­вер­саль­ный язык прог­рамми­рова­ния для решения любых задач обра­бот­ки дан­ных. Тебя, как хакера, не может не вооду­шевить, что PHP, в отли­чие от Python, мож­но обна­ружить прак­тичес­ки на любом веб‑сер­вере, — это стан­дар­тный ком­понент даже бес­плат­ного shared-хос­тинга.

В этой статье мы будем исполь­зовать PHP в режиме коман­дной стро­ки, без при­вяз­ки к веб‑сер­веру. Про­верь дос­тупность PHP коман­дой php -v: она дол­жна отоб­разить его вер­сию. На момент написа­ния статьи акту­аль­на вер­сия 8.5.8, но опи­сан­ные сце­нарии будут работать и в вер­сии 5.5.38. Ну а если вмес­то номера вер­сии у тебя появи­лось сооб­щение о неп­равиль­ной коман­де, PHP надо уста­новить и добавить в пути.

 

Подготовка инструмента

Для уста­нов­ки PHP в Linux на базе Debian или Ubuntu выпол­ни коман­ду

sudo apt install php-cli php-mbstring

В Windows заг­рузи с офи­циаль­ного сай­та ZIP-архив с бинар­ными фай­лами для нуж­ной архи­тек­туры — x86 или x64. Там пред­став­лены два вари­анта: потоко­безо­пас­ный Thread Safe и для выпол­нения в отдель­ных про­цес­сах Non Thread Safe — в фай­лах с суф­фиксом nts. Если не зна­ешь, какой нужен, выбирай более уни­вер­саль­ный Thread Safe. Для задач из этой статьи подой­дет любой вари­ант.

Рас­пакуй дис­три­бутив­ный ZIP-архив в каталог на дис­ке, нап­ример C:\PHP, и ско­пируй кон­фиг php.ini-development в php.ini. Можешь добавить путь к это­му катало­гу в перемен­ную сре­ды PATH. Ина­че для выпол­нения сце­нария script.php при­дет­ся набирать в коман­дной стро­ке

C:\PHP\php.exe script.php

Что­бы выпол­нять опи­сан­ные в статье сце­нарии, надо акти­виро­вать модуль работы с тек­стом в мно­гобай­товых кодиров­ках. Для это­го в кон­фиге php.ini, рас­положен­ном в пап­ке с исполня­емым фай­лом Windows или в катало­ге /etc/php/*/cli/php.ini Linux, надо рас­коммен­тировать стро­ку ;extension=mbstring — уда­лить началь­ную точ­ку с запятой. Воз­можно, при­дет­ся ука­зать путь к допол­нитель­ным модулям: extension_dir = "ext".

Как запускать скрипты на PHP

Ес­ли ты рань­ше не исполь­зовал PHP, нуж­ны некото­рые пояс­нения. Исходный текст сце­нария сох­раняй в прос­том тек­сто­вом фай­ле меж­ду тегами <?php и ?>. Все име­на перемен­ных в PHP начина­ются со зна­ка дол­лара. Это отли­чает их от дру­гих иден­тифика­торов и клю­чевых слов и пре­дот­вра­щает мно­жес­тво оши­бок. Кон­стан­та PHP_EOL обоз­нача­ет сим­вол перево­да стро­ки, при­нятый в исполь­зуемой опе­раци­онной сис­теме.

Проб­лем с отоб­ражени­ем сим­волов наци­ональ­ных алфа­витов не воз­никнет, если кодиров­ка фай­ла соот­ветс­тву­ет локали опе­раци­онной сис­темы. Узнать кодиров­ку локали в Linux мож­но коман­дой locale | grep LANG=. Ско­рее все­го, это будет UTF-8. В Windows коман­да MODE CON: CP — она обыч­но показы­вает CP866. Для еди­нооб­разия про­ще перек­лючить коман­дную стро­ку Windows на UTF-8 коман­дой CHCP 65001.

Что­бы боль­ше не воз­вра­щать­ся к нас­трой­ке сре­ды выпол­нения, соз­дай тек­сто­вый файл start.php в кодиров­ке UTF-8 без BOM с таким сце­нари­ем:

start.php
<?php
$intenc = mb_internal_encoding ();
echo "Внутренняя кодировка: $intenc", PHP_EOL;
?>

За­тем поп­робуй выпол­нить сце­нарий коман­дой php start.php. Воз­можны нес­коль­ко вари­антов.

  1. На экра­не появи­лось сооб­щение о внут­ренней кодиров­ке мно­гобай­товых строк PHP. Она может отли­чать­ся от кодиров­ки фай­лов и локали — пусть это тебя не сму­щает. Тест прой­ден, дви­гай­ся даль­ше.
  2. По­яви­лось сооб­щение о вызове неоп­ределен­ной фун­кции mb_internal_encoding. PHP не смог под­клю­чить модуль mbstring. Про­верь текст прог­раммы, нас­трой­ки PHP и наличие модуля.
  3. Опе­раци­онная сис­тема не смог­ла выпол­нить php. Либо PHP не уста­нов­лен, либо путь к нему не про­писан в перемен­ной окру­жения PATH. Исправ­ляй!
  4. Вмес­то сооб­щения о внут­ренней кодиров­ке отоб­разилось содер­жимое фай­ла start.php. Ско­рее все­го, ты забыл или неп­равиль­но наб­рал пре­фикс <?php, поэто­му интер­пре­татор не выпол­нил файл.
  5. Отоб­разилось сооб­щение об ошиб­ке, нап­ример PHP Parse error. Про­верь все сим­волы, исправь ошиб­ку и поп­робуй сно­ва.
 

Изготовление приспособления

На­деюсь, ты спра­вил­ся с уста­нов­кой и нас­трой­кой PHP. Идем даль­ше.

Пред­лагаю решать задачу пос­ледова­тель­но, чередуя изу­чение теории с прак­тикой. Пос­тигать детали реали­зации удоб­но на готовых, про­верен­ных образцах. Такими образца­ми в этой статье пос­лужат «сту­дий­ные» MP3-фай­лы.

Сна­чала раз­берем­ся, как в них внед­рены метадан­ные, извле­чем их из фай­ла в опе­ратив­ную память, а потом научим­ся фор­мировать информа­цион­ные бло­ки для записи в фай­лы. Осво­ив такую раз­борку‑сбор­ку и убе­див­шись, что выпол­няем ее пра­виль­но, мы смо­жем перед выг­рузкой поп­равить метадан­ные для решения исходной задачи.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

📊 Источник: Xakep | Оригинал
← Предыдущая Retry policy на практике: подводные камни внедрения Следующая → Как мы заставили трамвай ехать без GPS: калибровка CAN‑скорости с помощью GNSS