Статьи

Работа с MP3, ID3 и PhoneGap / Cordova

Как человек, который помнит, когда MP3 стали стандартом де-факто для аудиофайлов (*), я хорошо знаком с форматом, используемым для встраивания в них метаданных —  ID3 . Если вы когда-нибудь задумывались над тем, как ваш любимый MP3-плеер отображает данные о вашей музыке (исполнитель, альбом, год и т. Д.), Скорее всего, это связано с тегами ID3, встроенными в файл. Почти десять лет назад я даже  писал  о том, что анализировал их с помощью ColdFusion. Я подумал, что было бы интересно взглянуть на то, как можно выполнить разбор ID3 в приложении PhoneGap / Cordova.

Для тестирования я решил использовать проект JavaScript ID3 с открытым исходным кодом на GitHub:  JavaScript-ID3-Reader . Моя самая большая проблема была производительность. В ReadMe для проекта они упоминают, что если ваш веб-сервер поддерживает функцию HTTP Range, он будет захватывать только те биты, которые ему нужны. (Если я правильно помню, все данные ID3 находятся в конце файла MP3.) В противном случае они считываются во всем файле, что, очевидно, не будет работать хорошо. В любом случае я решил попробовать и посмотреть, каковы будут мои результаты.

Я решил проверить это на своем HTC M8. У меня на SD-карте есть несколько MP3-файлов, и я написал краткое подтверждение концепции, которая будет сканировать один жестко закодированный каталог. Одна из замечательных особенностей JavaScript-ID3-Reader заключается в том, что он поддерживает объекты FileEntry, и вы можете получить объекты FileEntry с помощью плагина Cordova’s File. Давайте посмотрим на мой первый удар в этом. Я делюсь только JavaScript, так как HTML буквально просто блок div для меня, чтобы написать дерьмо тоже. В следующем посте я добавлю правильный интерфейс к этому.

document.addEventListener("deviceready", init, false);

//A hard coded folder, to keep things simple
var mp3Folder = "Music/Depeche_Mode/101_(1_of_2)/";
var result;

function init() {


	window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory + mp3Folder,
	function(dir) {
			var reader = dir.createReader();
			//read it
			reader.readEntries(function(entries) {
					console.log("readEntries");
					console.dir(entries);

					entries.forEach(function(entry) {

						var name = entry.name;
						console.log(name);

						entry.file(function(file) {

							ID3.loadTags(name,function() {
								var tags = ID3.getAllTags(name);
								console.log("got tags for "+name, tags);
							},{
								dataReader:FileAPIReader(file)
							});

						});


					});

			});

	}, function(err) {

	});

}

Хорошо, давайте возьмем это сверху. Я использую псевдоним плагина Cordova File для SD-карты. Это псевдоним только для Android, но, как я уже сказал, я тестирую на своем телефоне. Кстати, я использовал приложение для просмотра одной конкретной папки (в данном случае это один из великолепных живых альбомов Depeche Mode, 101). Как только я преобразую этот путь в объект каталога, тогда просто прочитать записи из каталога. Получив список записей, я зацикливаю их и использую API библиотеки Javascript для получения тегов ID3. Если вы проверите их документы, вы можете увидеть дополнительные опции, но по большей части я просто хотел, чтобы все теги соответствовали тому, что я сделал.

Это быстро сломало и сломало кровать с ошибкой нехватки памяти. Затем я понял, что вызовы entry.file — будучи асинхронными — запускали парсер ID3 для нескольких mp3-файлов одновременно. Затем я переписал логику для обработки вызовов в порядке. Это не обязательно красиво, но это сработало сразу:

document.addEventListener("deviceready", init, false);

//A hard coded folder, to keep things simple
var mp3Folder = "Music/Depeche_Mode/101_(1_of_2)/";
var result;

function init() {

	result = document.querySelector("#results");

	result.innerHTML = "Stand by, parsing MP3s...<br/>";

	window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory + mp3Folder,
	function(dir) {
			var reader = dir.createReader();
			//read it
			reader.readEntries(function(entries) {
					console.log("readEntries");
					console.dir(entries);
					result.innerHTML += entries.length + " files to process.<br/>";

					var data = [];

					var process = function(index, cb) {
						console.log("doing index "+index);
						var entry = entries[index];
						var name = entry.name;
						entry.file(function(file) {

							ID3.loadTags(entry.name,function() {
								var tags = ID3.getAllTags(name);
								data.push({name:entry.name, tags:tags});
								console.log("got tags for "+entry.name, tags);
								result.innerHTML += "*";
								if(index+1 < entries.length) {
									process(++index, cb);
								} else {
									cb(data);
								}
							},{
								dataReader:FileAPIReader(file)
							});

						});

					};

					process(0, function(data) {
						console.log("Done processing");
						console.dir(data);
						//make a simple str to show stuff
						var s = "";
						for(var i=0; i<data.length; i++) {
							s += "<p>";
							s += "<b>"+data[i].tags.title+"</b><br/>";
							s += "By "+data[i].tags.artist+"<br/>";
							s += "Album: "+data[i].tags.album+"<br/>";
							s += "</p>";
						}
						result.innerHTML = s;
					});


			});

	}, function(err) {

	});

}

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

Устройство-2015-04-29-170149

Так насколько хорошо это работает? Я запустил Chrome Remote Debug и посмотрел, как он работает. Я бы сказал, что анализ каждого файла занимает около 1 секунды. Это не быстро, но вы можете легко кешировать эти результаты, чтобы не анализировать MP3 при каждом запросе. Вы также можете быстро отобразить имя файла (soandso.mp3), пока не получите правильный заголовок из информации ID3. Таким образом, пользователь может видеть имена, воспроизводить файлы и т. Д., А затем ваш код может обновлять отображение по мере их получения.

В следующей версии этого проекта я добавлю в код интерфейс Ionic и сделаю его немного красивее. Я также сделаю его более общим, чтобы он мог работать на iOS и Android. Я поделюсь полным исходным кодом, но я хочу завершить вторую версию, прежде чем перейти к моему  репозиторию Cordova  examples.

* Я достаточно взрослый, чтобы помнить, что я загружал музыкальные файлы в… формате AIFF, я думаю… в 96 или около того. Я помню, как думал, что это круто (и незаконно), что я могу скачать 5-6 мегабайт файлов — если я правильно помню — Путешествия. О, и эти файлы заняли около 15 минут, чтобы загрузить что-то в Sun моего колледжа или другую компьютерную лабораторию. Да, я старый