Skip to content

Commit 88a7ff5

Browse files
committed
Migrate music-metadata-browser to music-metadata
Includes some code refactoring using `async`/`await` to simplify promise chaining.
1 parent 0f4fc18 commit 88a7ff5

File tree

3 files changed

+99
-91
lines changed

3 files changed

+99
-91
lines changed

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,13 @@
1111
},
1212
"devDependencies": {
1313
"audiomotion-analyzer": "^4.5.1",
14-
"buffer": "^6.0.3",
1514
"css-loader": "^7.1.2",
1615
"css-minimizer-webpack-plugin": "^7.0.0",
1716
"http-server": "^14.1.1",
1817
"idb-keyval": "^6.2.1",
1918
"mini-css-extract-plugin": "^2.9.0",
20-
"music-metadata-browser": "^2.5.10",
19+
"music-metadata": "^11.6.1",
2120
"notie": "^4.3.1",
22-
"process": "^0.11.10",
2321
"sortablejs": "^1.15.2",
2422
"webpack": "^5.91.0",
2523
"webpack-cli": "^5.1.4"

src/index.js

Lines changed: 98 additions & 84 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

@@ -1194,52 +1194,48 @@ function addMetadata( metadata, target ) {
11941194
* @param {object} { album, artist, codec, duration, title }
11951195
* @returns {Promise} resolves to 1 when song added, or 0 if queue is full
11961196
*/
1197-
function addSongToPlayQueue( fileObject, content ) {
1197+
async function addSongToPlayQueue( fileObject, content ) {
11981198

1199-
return new Promise( resolve => {
1200-
if ( queueLength() >= MAX_QUEUED_SONGS ) {
1201-
resolve(0);
1202-
return;
1203-
}
1199+
if ( queueLength() >= MAX_QUEUED_SONGS ) {
1200+
return 0;
1201+
}
12041202

1205-
const { fileName, baseName, extension } = parsePath( fileExplorer.decodeChars( fileObject.file ) ),
1206-
uri = normalizeSlashes( fileObject.file ),
1207-
newEl = document.createElement('li'), // create new list element
1208-
trackData = newEl.dataset;
1203+
const { fileName, baseName, extension } = parsePath( fileExplorer.decodeChars( fileObject.file ) ),
1204+
uri = normalizeSlashes( fileObject.file ),
1205+
newEl = document.createElement('li'), // create new list element
1206+
trackData = newEl.dataset;
12091207

1210-
Object.assign( trackData, DATASET_TEMPLATE ); // initialize element's dataset attributes
1208+
Object.assign( trackData, DATASET_TEMPLATE ); // initialize element's dataset attributes
12111209

1212-
if ( ! content )
1213-
content = parseTrackName( baseName );
1210+
if ( ! content )
1211+
content = parseTrackName( baseName );
12141212

1215-
trackData.album = content.album || '';
1216-
trackData.artist = content.artist || '';
1217-
trackData.title = content.title || fileName || uri.slice( uri.lastIndexOf('//') + 2 );
1218-
trackData.duration = content.duration || '';
1219-
trackData.codec = content.codec || extension.toUpperCase();
1213+
trackData.album = content.album || '';
1214+
trackData.artist = content.artist || '';
1215+
trackData.title = content.title || fileName || uri.slice( uri.lastIndexOf('//') + 2 );
1216+
trackData.duration = content.duration || '';
1217+
trackData.codec = content.codec || extension.toUpperCase();
12201218
// trackData.subs = + !! fileObject.subs; // show 'subs' badge in the playqueue (TO-DO: resolve CSS conflict)
12211219

1222-
trackData.file = uri; // for web server access
1223-
newEl.handle = fileObject.handle; // for File System API access
1224-
newEl.dirHandle = fileObject.dirHandle;
1225-
newEl.subs = fileObject.subs; // only defined when coming from the file explorer (not playlists)
1220+
trackData.file = uri; // for web server access
1221+
newEl.handle = fileObject.handle; // for File System API access
1222+
newEl.dirHandle = fileObject.dirHandle;
1223+
newEl.subs = fileObject.subs; // only defined when coming from the file explorer (not playlists)
12261224

1227-
playlist.appendChild( newEl );
1225+
playlist.appendChild( newEl );
12281226

1229-
if ( FILE_EXT_AUDIO.includes( extension ) || ! extension ) {
1230-
// disable retrieving metadata of video files for now - https://github.com/Borewit/music-metadata-browser/issues/950
1231-
trackData.retrieve = 1; // flag this item as needing metadata
1232-
retrieveMetadata();
1233-
}
1227+
if ( FILE_EXT_AUDIO.includes( extension ) || ! extension ) {
1228+
// disable retrieving metadata of video files for now - https://github.com/Borewit/music-metadata-browser/issues/950
1229+
trackData.retrieve = 1; // flag this item as needing metadata
1230+
await retrieveMetadata();
1231+
}
12341232

1235-
if ( queueLength() == 1 && ! isPlaying() )
1236-
loadSong(0).then( () => resolve(1) );
1237-
else {
1238-
if ( playlistPos > queueLength() - 3 )
1239-
loadSong( NEXT_TRACK );
1240-
resolve(1);
1241-
}
1242-
});
1233+
if ( queueLength() === 1 && ! isPlaying() ) {
1234+
await loadSong(0);
1235+
if ( playlistPos > queueLength() - 3 )
1236+
await loadNextSong();
1237+
}
1238+
return 1;
12431239
}
12441240

12451241
/**
@@ -1968,7 +1964,7 @@ function keyboardControls( event ) {
19681964
}
19691965

19701966
/**
1971-
* Sets (or removes) the `src` attribute of a audio element and
1967+
* Sets (or removes) the `src` attribute of an audio element and
19721968
* releases any data blob (File System API) previously in use by it
19731969
*
19741970
* @param {object} audio element
@@ -1996,17 +1992,34 @@ function loadAudioSource( audioEl, newSource ) {
19961992
* @param {boolean} `true` to start playing
19971993
* @returns {Promise} resolves to a string containing the URL created for the blob
19981994
*/
1999-
async function loadFileBlob( fileBlob, audioEl, playIt ) {
2000-
const url = URL.createObjectURL( fileBlob );
2001-
loadAudioSource( audioEl, url );
2002-
try {
2003-
await waitForLoadedData( audioEl );
2004-
if ( playIt )
2005-
audioEl.play();
2006-
}
2007-
catch ( e ) {}
1995+
function loadFileBlob(fileBlob, audioEl, playIt) {
1996+
return new Promise((resolve, reject) => {
1997+
const url = URL.createObjectURL(fileBlob);
1998+
loadAudioSource(audioEl, url);
1999+
2000+
// Success handler
2001+
audioEl.onloadeddata = () => {
2002+
cleanup();
2003+
if (playIt) {
2004+
audioEl.play().catch(err => {
2005+
consoleLog("Playback failed:", err);
2006+
});
2007+
}
2008+
resolve(url);
2009+
};
2010+
2011+
// Error handler
2012+
audioEl.onerror = () => {
2013+
cleanup();
2014+
reject(new Error("Failed to load audio from Blob"));
2015+
};
20082016

2009-
return url;
2017+
// Cleanup to avoid memory leaks
2018+
function cleanup() {
2019+
audioEl.onloadeddata = null;
2020+
audioEl.onerror = null;
2021+
}
2022+
});
20102023
}
20112024

20122025
/**
@@ -2072,7 +2085,8 @@ function loadGradientIntoCurrentGradient(gradientKey) {
20722085
/**
20732086
* Load a music file from the user's computer
20742087
*/
2075-
function loadLocalFile( obj ) {
2088+
async function loadLocalFile( obj ) {
2089+
20762090
const fileBlob = obj.files[0];
20772091

20782092
if ( fileBlob ) {
@@ -2081,11 +2095,14 @@ function loadLocalFile( obj ) {
20812095
audioEl.dataset.file = fileBlob.name;
20822096
audioEl.dataset.title = parsePath( fileBlob.name ).baseName;
20832097

2084-
// load and play
2085-
loadFileBlob( fileBlob, audioEl, true )
2086-
.then( url => mm.fetchFromUrl( url ) )
2087-
.then( metadata => addMetadata( metadata, audioEl ) )
2088-
.catch( e => {} );
2098+
try {
2099+
await loadFileBlob( fileBlob, audioEl, true );
2100+
// Maybe do this parallel?
2101+
const metadata = await parseBlob( fileBlob );
2102+
await addMetadata( metadata, audioEl );
2103+
} catch( error ) {
2104+
consoleLog("Failed to load local file", error);
2105+
}
20892106
}
20902107
}
20912108

@@ -3247,47 +3264,44 @@ async function retrieveMetadata() {
32473264

32483265
if ( queueItem ) {
32493266

3250-
let uri = queueItem.dataset.file,
3251-
revoke = false;
3267+
let uri = queueItem.dataset.file;
3268+
let file;
32523269

32533270
waitingMetadata++;
32543271
delete queueItem.dataset.retrieve;
3272+
let metadata;
32553273

3256-
queryMetadata: {
3257-
if ( queueItem.handle ) {
3258-
try {
3259-
if ( await queueItem.handle.requestPermission() != 'granted' )
3260-
break queryMetadata;
3274+
if ( queueItem.handle ) {
3275+
// Fetch metadata from File object
3276+
if ( await queueItem.handle.requestPermission() !== 'granted' )
3277+
return;
32613278

3262-
uri = URL.createObjectURL( await queueItem.handle.getFile() );
3263-
revoke = true;
3264-
}
3265-
catch( e ) {
3266-
break queryMetadata;
3267-
}
3279+
file = await queueItem.handle.getFile();
3280+
uri = URL.createObjectURL( file );
3281+
metadata = await parseBlob( file, { skipPostHeaders: true } );
3282+
} else {
3283+
// Fetch metadata from URI
3284+
const response = await fetch(uri);
3285+
if (response.body) {
3286+
metadata = await parseWebStream( response.body, { skipPostHeaders: true } );
3287+
} else {
3288+
throw new Error('Failed to stream response.body');
32683289
}
3290+
}
32693291

3270-
try {
3271-
const metadata = await mm.fetchFromUrl( uri, { skipPostHeaders: true } );
3272-
if ( metadata ) {
3273-
addMetadata( metadata, queueItem ); // add metadata to play queue item
3274-
syncMetadataToAudioElements( queueItem );
3275-
if ( ! ( metadata.common.picture && metadata.common.picture.length ) ) {
3276-
getFolderCover( queueItem ).then( cover => {
3277-
queueItem.dataset.cover = cover;
3278-
syncMetadataToAudioElements( queueItem );
3279-
});
3280-
}
3281-
}
3282-
}
3283-
catch( e ) {}
3292+
addMetadata( metadata, queueItem ); // add metadata to play queue item
3293+
syncMetadataToAudioElements( queueItem );
3294+
if ( ! queueItem.handle && ! ( metadata.common.picture && metadata.common.picture.length ) ) {
3295+
queueItem.dataset.cover = await getFolderCover( uri );
3296+
syncMetadataToAudioElements( queueItem );
3297+
}
32843298

3285-
if ( revoke )
3286-
URL.revokeObjectURL( uri );
3299+
if ( file ) {
3300+
URL.revokeObjectURL( uri );
32873301
}
32883302

32893303
waitingMetadata--;
3290-
retrieveMetadata(); // call again to continue processing the queue
3304+
await retrieveMetadata(); // call again to continue processing the queue
32913305
}
32923306
}
32933307

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 => {

0 commit comments

Comments
 (0)