Skip to content

Migrate music-metadata-browser to music-metadata #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,10 @@ And all the developers and artists, for the following resources:

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

* [buffer](https://www.npmjs.com/package/buffer) - Node.js Buffer API, for the browser
* [http-server](https://www.npmjs.com/package/http-server) - a simple zero-configuration command-line http server
* [idb-keyval](https://www.npmjs.com/package/idb-keyval) - super-simple promise-based keyval store implemented with IndexedDB
* [music-metadata-browser](https://www.npmjs.com/package/music-metadata-browser) - stream and file based music metadata parser for the browser
* [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.
* [notie](https://www.npmjs.com/package/notie) - clean and simple notification, input, and selection suite for javascript, with no dependencies
* [process](https://www.npmjs.com/package/process) - process information for node.js and browsers
* [scrollIntoViewIfNeeded 4 everyone](https://gist.github.com/hsablonniere/2581101) - polyfill for non-standard scrollIntoViewIfNeeded() method
* [sortablejs](https://www.npmjs.com/package/sortablejs) - JavaScript library for reorderable drag-and-drop lists
* [webpack](https://www.npmjs.com/package/webpack) - JavaScript module bundler for the browser
Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@
},
"devDependencies": {
"audiomotion-analyzer": "^4.5.1",
"buffer": "^6.0.3",
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0",
"http-server": "^14.1.1",
"idb-keyval": "^6.2.1",
"js-yaml": "^4.1.0",
"mini-css-extract-plugin": "^2.9.0",
"music-metadata-browser": "^2.5.10",
"music-metadata": "^11.7.1",
"notie": "^4.3.1",
"process": "^0.11.10",
"sortablejs": "^1.15.2",
"style-loader": "^4.0.0",
"webpack": "^5.91.0",
Expand Down
111 changes: 69 additions & 42 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import AudioMotionAnalyzer from 'audiomotion-analyzer';
import packageJson from '../package.json';
import * as fileExplorer from './file-explorer.js';
import * as mm from 'music-metadata-browser';
import {parseBlob, parseWebStream} from 'music-metadata';
import './scrollIntoViewIfNeeded-polyfill.js';
import { get, set, del } from 'idb-keyval';
import * as yaml from 'js-yaml';
Expand Down Expand Up @@ -2017,7 +2017,7 @@ function keyboardControls( event ) {
}

/**
* Sets (or removes) the `src` attribute of a audio element and
* Sets (or removes) the `src` attribute of an audio element and
* releases any data blob (File System API) previously in use by it
*
* @param {object} audio element
Expand Down Expand Up @@ -2121,7 +2121,8 @@ function loadGradientIntoCurrentGradient(gradientKey) {
/**
* Load a music file from the user's computer
*/
function loadLocalFile( obj ) {
async function loadLocalFile( obj ) {

const fileBlob = obj.files[0];

if ( fileBlob ) {
Expand All @@ -2130,11 +2131,17 @@ function loadLocalFile( obj ) {
audioEl.dataset.file = fileBlob.name;
audioEl.dataset.title = parsePath( fileBlob.name ).baseName;

// load and play
loadFileBlob( fileBlob, audioEl, true )
.then( url => mm.fetchFromUrl( url ) )
.then( metadata => addMetadata( metadata, audioEl ) )
.catch( e => {} );
try {
// Start both tasks, but only await parseBlob immediately
const loadTask = loadFileBlob(fileBlob, audioEl, true);
const metadata = await parseBlob( fileBlob );
await addMetadata(metadata, audioEl);

// Wait for loadTask to complete
await loadTask;
} catch( error ) {
consoleLog("Failed to load local file", error);
}
}
}

Expand Down Expand Up @@ -3295,60 +3302,80 @@ async function retrieveBackgrounds() {
}

/**
* Retrieve metadata for files in the play queue
* Retrieve metadata for the first MAX_METADATA_REQUESTS files in the play queue,
* which have no metadata assigned yet
*/
async function retrieveMetadata() {
// leave when we already have enough concurrent requests pending
// ToDo:
// Very arguable way to restrict parallel processing
// Without calling this to often, leaves items unprocessed
if ( waitingMetadata >= MAX_METADATA_REQUESTS )
return;

// find the first play queue item for which we haven't retrieved the metadata yet
const queueItem = Array.from( playlist.children ).find( el => el.dataset.retrieve );
++waitingMetadata;

if ( queueItem ) {
// Process in parallel
for ( const queueItem of playlist.children ) {

let uri = queueItem.dataset.file,
revoke = false;

waitingMetadata++;
if (!queueItem.dataset.retrieve) continue;
delete queueItem.dataset.retrieve;

queryMetadata: {
let metadata;
let file;

try {
if ( queueItem.handle ) {
try {
if ( await queueItem.handle.requestPermission() != 'granted' )
break queryMetadata;

uri = URL.createObjectURL( await queueItem.handle.getFile() );
revoke = true;
}
catch( e ) {
break queryMetadata;
}
}
// Fetch metadata from File object
if (await queueItem.handle.requestPermission() !== 'granted')
return;

try {
const metadata = await mm.fetchFromUrl( uri, { skipPostHeaders: true } );
if ( metadata ) {
addMetadata( metadata, queueItem ); // add metadata to play queue item
syncMetadataToAudioElements( queueItem );
if ( ! ( metadata.common.picture && metadata.common.picture.length ) ) {
getFolderCover( queueItem ).then( cover => {
queueItem.dataset.cover = cover;
syncMetadataToAudioElements( queueItem );
});
file = await queueItem.handle.getFile();
metadata = await parseBlob(file);
}
else
{
// Fetch metadata from URI
const response = await fetch(queueItem.dataset.file);
if (response.ok) {
if (response.body?.getReader) {
const contentType = response.headers.get("Content-Type");
const contentSize = response.headers.get("Content-Length");
try {
metadata = await parseWebStream(response.body, {
mimeType: contentType,
size: contentSize ? Number.parseInt(contentSize, 10) : undefined
}, {skipPostHeaders: true});
} finally {
await response.body.cancel();
}
} else {
// Fallback to Blob, in case the HTTP Result cannot be streamed
metadata = await parseBlob(await response.blob());
}
} else {
consoleLog(`Failed to fetch metadata http-response=${response.status} for url=${queueItem.dataset.file}`, true);
}
}
catch( e ) {}
}
catch( e ) {
consoleLog(`Error converting queued file="${queueItem.dataset.file ?? '?'}" to URI`, e);
return;
}

if ( revoke )
URL.revokeObjectURL( uri );
console.log(`Fetched metadata successful for url=${queueItem.dataset.file}`);
addMetadata( metadata, queueItem ); // add metadata to play queue item

// If no embedded picture, try folder cover
if ( ! ( metadata.common.picture && metadata.common.picture.length > 0) ) {
queueItem.dataset.cover = await getFolderCover( queueItem );
}

waitingMetadata--;
retrieveMetadata(); // call again to continue processing the queue
syncMetadataToAudioElements( queueItem );
}

--waitingMetadata;
}

/**
Expand Down
4 changes: 0 additions & 4 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ module.exports = {
new MiniCssExtractPlugin({
filename: 'styles.css',
}),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser.js',
}),
],
output: {
filename: pathData => {
Expand Down
4 changes: 0 additions & 4 deletions webpack.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ module.exports = {
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser.js',
}),
],
output: {
filename: 'audioMotion.js',
Expand Down