Skip to content

Commit dd71b92

Browse files
committed
Migrate music-metadata-browser to music-metadata
1 parent 8c0b9a8 commit dd71b92

File tree

5 files changed

+73
-61
lines changed

5 files changed

+73
-61
lines changed

docs/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,10 @@ And all the developers and artists, for the following resources:
6565

6666
### JavaScript libraries <!-- {docsify-ignore} -->
6767

68-
* [buffer](https://www.npmjs.com/package/buffer) - Node.js Buffer API, for the browser
6968
* [http-server](https://www.npmjs.com/package/http-server) - a simple zero-configuration command-line http server
7069
* [idb-keyval](https://www.npmjs.com/package/idb-keyval) - super-simple promise-based keyval store implemented with IndexedDB
71-
* [music-metadata-browser](https://www.npmjs.com/package/music-metadata-browser) - stream and file based music metadata parser for the browser
70+
* [music-metadata](https://www.npmjs.com/package/music-metadata) - Metadata parser for audio and video media files. Supports file and stream inputs in Node.js and browser environments, extracting format, tag, and duration information.
7271
* [notie](https://www.npmjs.com/package/notie) - clean and simple notification, input, and selection suite for javascript, with no dependencies
73-
* [process](https://www.npmjs.com/package/process) - process information for node.js and browsers
7472
* [scrollIntoViewIfNeeded 4 everyone](https://gist.github.com/hsablonniere/2581101) - polyfill for non-standard scrollIntoViewIfNeeded() method
7573
* [sortablejs](https://www.npmjs.com/package/sortablejs) - JavaScript library for reorderable drag-and-drop lists
7674
* [webpack](https://www.npmjs.com/package/webpack) - JavaScript module bundler for the browser

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,14 @@
1212
},
1313
"devDependencies": {
1414
"audiomotion-analyzer": "^4.5.1",
15-
"buffer": "^6.0.3",
1615
"css-loader": "^7.1.2",
1716
"css-minimizer-webpack-plugin": "^7.0.0",
1817
"http-server": "^14.1.1",
1918
"idb-keyval": "^6.2.1",
2019
"js-yaml": "^4.1.0",
2120
"mini-css-extract-plugin": "^2.9.0",
22-
"music-metadata-browser": "^2.5.10",
21+
"music-metadata": "^11.7.1",
2322
"notie": "^4.3.1",
24-
"process": "^0.11.10",
2523
"sortablejs": "^1.15.2",
2624
"style-loader": "^4.0.0",
2725
"webpack": "^5.91.0",

src/index.js

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import AudioMotionAnalyzer from 'audiomotion-analyzer';
3333
import packageJson from '../package.json';
3434
import * as fileExplorer from './file-explorer.js';
35-
import * as mm from 'music-metadata-browser';
35+
import {parseBlob, parseWebStream} from 'music-metadata';
3636
import './scrollIntoViewIfNeeded-polyfill.js';
3737
import { get, set, del } from 'idb-keyval';
3838
import * as yaml from 'js-yaml';
@@ -841,7 +841,6 @@ let audioElement = [],
841841
supportsFileSystemAPI, // browser supports File System API (may be disabled via config.yaml)
842842
useFileSystemAPI, // load music from local device when in web server mode
843843
userPresets,
844-
waitingMetadata = 0,
845844
wasMuted, // mute status before switching to microphone input
846845
webServer; // web server available? (boolean)
847846

@@ -2017,7 +2016,7 @@ function keyboardControls( event ) {
20172016
}
20182017

20192018
/**
2020-
* Sets (or removes) the `src` attribute of a audio element and
2019+
* Sets (or removes) the `src` attribute of an audio element and
20212020
* releases any data blob (File System API) previously in use by it
20222021
*
20232022
* @param {object} audio element
@@ -2121,7 +2120,8 @@ function loadGradientIntoCurrentGradient(gradientKey) {
21212120
/**
21222121
* Load a music file from the user's computer
21232122
*/
2124-
function loadLocalFile( obj ) {
2123+
async function loadLocalFile( obj ) {
2124+
21252125
const fileBlob = obj.files[0];
21262126

21272127
if ( fileBlob ) {
@@ -2130,11 +2130,17 @@ function loadLocalFile( obj ) {
21302130
audioEl.dataset.file = fileBlob.name;
21312131
audioEl.dataset.title = parsePath( fileBlob.name ).baseName;
21322132

2133-
// load and play
2134-
loadFileBlob( fileBlob, audioEl, true )
2135-
.then( url => mm.fetchFromUrl( url ) )
2136-
.then( metadata => addMetadata( metadata, audioEl ) )
2137-
.catch( e => {} );
2133+
try {
2134+
// Start both tasks, but only await parseBlob immediately
2135+
const loadTask = loadFileBlob(fileBlob, audioEl, true);
2136+
const metadata = await parseBlob( fileBlob );
2137+
await addMetadata(metadata, audioEl);
2138+
2139+
// Wait for loadTask to complete
2140+
await loadTask;
2141+
} catch( error ) {
2142+
consoleLog("Failed to load local file", error);
2143+
}
21382144
}
21392145
}
21402146

@@ -3295,60 +3301,78 @@ async function retrieveBackgrounds() {
32953301
}
32963302

32973303
/**
3298-
* Retrieve metadata for files in the play queue
3304+
* Retrieve metadata for the first MAX_METADATA_REQUESTS files in the play queue,
3305+
* which have no metadata assigned yet
32993306
*/
33003307
async function retrieveMetadata() {
3301-
// leave when we already have enough concurrent requests pending
3302-
if ( waitingMetadata >= MAX_METADATA_REQUESTS )
3303-
return;
3304-
3305-
// find the first play queue item for which we haven't retrieved the metadata yet
3306-
const queueItem = Array.from( playlist.children ).find( el => el.dataset.retrieve );
33073308

3308-
if ( queueItem ) {
3309+
// find the first MAX_METADATA_REQUESTS items for which we haven't retrieved the metadata yet
3310+
const retrievalQueue = [];
3311+
for (const el of playlist.children) {
3312+
if (el.dataset.retrieve) {
3313+
retrievalQueue.push(el);
3314+
if (retrievalQueue.length >= MAX_METADATA_REQUESTS) break;
3315+
}
3316+
}
33093317

3310-
let uri = queueItem.dataset.file,
3311-
revoke = false;
3318+
// Process in parallel
3319+
return Promise.all(retrievalQueue.map(async queueItem => {
33123320

3313-
waitingMetadata++;
33143321
delete queueItem.dataset.retrieve;
33153322

3316-
queryMetadata: {
3323+
let metadata;
3324+
let file;
3325+
3326+
try {
33173327
if ( queueItem.handle ) {
3318-
try {
3319-
if ( await queueItem.handle.requestPermission() != 'granted' )
3320-
break queryMetadata;
33213328

3322-
uri = URL.createObjectURL( await queueItem.handle.getFile() );
3323-
revoke = true;
3324-
}
3325-
catch( e ) {
3326-
break queryMetadata;
3327-
}
3328-
}
3329+
// Fetch metadata from File object
3330+
if (await queueItem.handle.requestPermission() !== 'granted')
3331+
return;
33293332

3330-
try {
3331-
const metadata = await mm.fetchFromUrl( uri, { skipPostHeaders: true } );
3332-
if ( metadata ) {
3333-
addMetadata( metadata, queueItem ); // add metadata to play queue item
3334-
syncMetadataToAudioElements( queueItem );
3335-
if ( ! ( metadata.common.picture && metadata.common.picture.length ) ) {
3336-
getFolderCover( queueItem ).then( cover => {
3337-
queueItem.dataset.cover = cover;
3338-
syncMetadataToAudioElements( queueItem );
3339-
});
3333+
file = await queueItem.handle.getFile();
3334+
metadata = await parseBlob(file);
3335+
}
3336+
else
3337+
{
3338+
// Fetch metadata from URI
3339+
const response = await fetch(queueItem.dataset.file);
3340+
if (response.ok) {
3341+
if (response.body?.getReader) {
3342+
const contentType = response.headers.get("Content-Type");
3343+
const contentSize = response.headers.get("Content-Length");
3344+
try {
3345+
metadata = await parseWebStream(response.body, {
3346+
mimeType: contentType,
3347+
size: contentSize ? Number.parseInt(contentSize, 10) : undefined
3348+
}, {skipPostHeaders: true});
3349+
} finally {
3350+
await response.body.cancel();
3351+
}
3352+
} else {
3353+
// Fallback to Blob, in case the HTTP Result cannot be streamed
3354+
metadata = await parseBlob(await response.blob());
33403355
}
3356+
} else {
3357+
consoleLog(`Failed to fetch metadata http-response=${response.status} for url=${queueItem.dataset.file}`, true);
33413358
}
33423359
}
3343-
catch( e ) {}
3360+
}
3361+
catch( e ) {
3362+
consoleLog(`Error converting queued file="${queueItem.dataset.file ?? '?'}" to URI`, e);
3363+
return;
3364+
}
33443365

3345-
if ( revoke )
3346-
URL.revokeObjectURL( uri );
3366+
console.log(`Fetched metadata successful for url=${queueItem.dataset.file}`);
3367+
addMetadata( metadata, queueItem ); // add metadata to play queue item
3368+
3369+
// If no embedded picture, try folder cover
3370+
if ( ! ( metadata.common.picture && metadata.common.picture.length > 0) ) {
3371+
queueItem.dataset.cover = await getFolderCover( queueItem );
33473372
}
33483373

3349-
waitingMetadata--;
3350-
retrieveMetadata(); // call again to continue processing the queue
3351-
}
3374+
syncMetadataToAudioElements( queueItem );
3375+
}));
33523376
}
33533377

33543378
/**

webpack.config.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,6 @@ module.exports = {
3636
new MiniCssExtractPlugin({
3737
filename: 'styles.css',
3838
}),
39-
new webpack.ProvidePlugin({
40-
Buffer: ['buffer', 'Buffer'],
41-
process: 'process/browser.js',
42-
}),
4339
],
4440
output: {
4541
filename: pathData => {

webpack.dev.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ module.exports = {
3535
},
3636
plugins: [
3737
new webpack.HotModuleReplacementPlugin(),
38-
new webpack.ProvidePlugin({
39-
Buffer: ['buffer', 'Buffer'],
40-
process: 'process/browser.js',
41-
}),
4238
],
4339
output: {
4440
filename: 'audioMotion.js',

0 commit comments

Comments
 (0)