@@ -36,6 +36,7 @@ import {parseBlob, parseWebStream} from 'music-metadata';
36
36
import './scrollIntoViewIfNeeded-polyfill.js' ;
37
37
import { get , set , del } from 'idb-keyval' ;
38
38
import * as yaml from 'js-yaml' ;
39
+ import pLimit from 'p-limit' ;
39
40
40
41
import Sortable , { MultiDrag } from 'sortablejs' ;
41
42
Sortable . mount ( new MultiDrag ( ) ) ;
@@ -958,6 +959,9 @@ const getCurrentSettings = _ => ({
958
959
weighting : getControlValue ( elWeighting )
959
960
} ) ;
960
961
962
+ // Limit the number of parallel metadata requests
963
+ const metadataRequestLimit = pLimit ( MAX_METADATA_REQUESTS ) ;
964
+
961
965
// get the array index for a preset key, or validate a given index; if invalid or not found returns -1
962
966
const getPresetIndex = key => {
963
967
const index = ( + key == key ) ? key : presets . findIndex ( item => item . key == key ) ;
@@ -1251,7 +1255,7 @@ async function addSongToPlayQueue( fileObject, content ) {
1251
1255
if ( FILE_EXT_AUDIO . includes ( extension ) || ! extension ) {
1252
1256
// disable retrieving metadata of video files for now - https://github.com/Borewit/music-metadata-browser/issues/950
1253
1257
trackData . retrieve = 1 ; // flag this item as needing metadata
1254
- await retrieveMetadata ( ) ;
1258
+ retrieveMetadata ( ) ; // ToDo improve handling promise
1255
1259
}
1256
1260
1257
1261
if ( queueLength ( ) === 1 && ! isPlaying ( ) )
@@ -1266,22 +1270,18 @@ async function addSongToPlayQueue( fileObject, content ) {
1266
1270
/**
1267
1271
* Add a song or playlist to the play queue
1268
1272
*/
1269
- function addToPlayQueue ( fileObject , autoplay = false ) {
1270
-
1271
- let ret ;
1273
+ async function addToPlayQueue ( fileObject , autoplay = false ) {
1272
1274
1275
+ let n ;
1273
1276
if ( FILE_EXT_PLIST . includes ( parsePath ( fileObject . file ) . extension ) )
1274
- ret = loadPlaylist ( fileObject ) ;
1277
+ n = await loadPlaylist ( fileObject ) ;
1275
1278
else
1276
- ret = addSongToPlayQueue ( fileObject ) ;
1279
+ n = await addSongToPlayQueue ( fileObject ) ;
1277
1280
1278
1281
// when promise resolved, if autoplay requested start playing the first added song
1279
- ret . then ( n => {
1280
- if ( autoplay && ! isPlaying ( ) && n > 0 )
1281
- playSong ( queueLength ( ) - n ) ;
1282
- } ) ;
1283
1282
1284
- return ret ;
1283
+ if ( autoplay && ! isPlaying ( ) && n > 0 )
1284
+ playSong ( queueLength ( ) - n ) ;
1285
1285
}
1286
1286
1287
1287
/**
@@ -1290,7 +1290,7 @@ function addToPlayQueue( fileObject, autoplay = false ) {
1290
1290
function changeFsHeight ( incr ) {
1291
1291
const val = + elFsHeight . value ;
1292
1292
1293
- if ( incr == 1 && val < + elFsHeight . max || incr == - 1 && val > + elFsHeight . min ) {
1293
+ if ( incr === 1 && val < + elFsHeight . max || incr = == - 1 && val > + elFsHeight . min ) {
1294
1294
elFsHeight . value = val + elFsHeight . step * incr ;
1295
1295
setProperty ( elFsHeight ) ;
1296
1296
}
@@ -3274,81 +3274,99 @@ async function retrieveBackgrounds() {
3274
3274
catch ( e ) { } // needs permission to access local device
3275
3275
}
3276
3276
3277
- if ( bgLocation != BGFOLDER_NONE ) {
3277
+ if ( bgLocation !== BGFOLDER_NONE ) {
3278
3278
const imageCount = bgImages . length ,
3279
3279
videoCount = bgVideos . length ;
3280
3280
3281
- consoleLog ( 'Found ' + ( imageCount + videoCount == 0 ? 'no media' : imageCount + ' image files and ' + videoCount + ' video' ) + ' files in the backgrounds folder' ) ;
3281
+ consoleLog ( 'Found ' + ( imageCount + videoCount === 0 ? 'no media' : imageCount + ' image files and ' + videoCount + ' video' ) + ' files in the backgrounds folder' ) ;
3282
3282
}
3283
3283
3284
3284
populateBackgrounds ( ) ;
3285
3285
}
3286
3286
3287
3287
/**
3288
- * Retrieve metadata for the first MAX_METADATA_REQUESTS files in the play queue,
3288
+ * Retrieve metadata for queueItem
3289
3289
* which have no metadata assigned yet
3290
3290
*/
3291
- async function retrieveMetadata ( ) {
3291
+ async function retrieveMetadataForQueueItem ( queueItem ) {
3292
3292
3293
- // Process in sequential order
3294
- for ( const queueItem of playlist . children ) {
3295
-
3296
- if ( ! queueItem . dataset . retrieve ) continue ;
3297
- delete queueItem . dataset . retrieve ;
3298
-
3299
- let metadata ;
3300
- let file ;
3293
+ let metadata ;
3294
+ let file ;
3301
3295
3302
- try {
3303
- if ( queueItem . handle ) {
3296
+ try {
3297
+ if ( queueItem . handle ) {
3304
3298
3305
- // Fetch metadata from File object
3306
- if ( await queueItem . handle . requestPermission ( ) !== 'granted' )
3307
- return ;
3299
+ // Fetch metadata from File object
3300
+ if ( await queueItem . handle . requestPermission ( ) !== 'granted' )
3301
+ return ;
3308
3302
3309
- file = await queueItem . handle . getFile ( ) ;
3310
- metadata = await parseBlob ( file ) ;
3311
- }
3312
- else
3313
- {
3314
- // Fetch metadata from URI
3315
- const response = await fetch ( queueItem . dataset . file ) ;
3316
- if ( response . ok ) {
3317
- if ( response . body ?. getReader ) {
3318
- const contentType = response . headers . get ( "Content-Type" ) ;
3319
- const contentSize = response . headers . get ( "Content-Length" ) ;
3320
- try {
3321
- metadata = await parseWebStream ( response . body , {
3322
- mimeType : contentType ,
3323
- size : contentSize ? Number . parseInt ( contentSize , 10 ) : undefined
3324
- } , { skipPostHeaders : true } ) ;
3325
- } finally {
3326
- await response . body . cancel ( ) ;
3327
- }
3328
- } else {
3329
- // Fallback to Blob, in case the HTTP Result cannot be streamed
3330
- metadata = await parseBlob ( await response . blob ( ) ) ;
3303
+ file = await queueItem . handle . getFile ( ) ;
3304
+ metadata = await parseBlob ( file ) ;
3305
+ }
3306
+ else
3307
+ {
3308
+ // Fetch metadata from URI
3309
+ const response = await fetch ( queueItem . dataset . file ) ;
3310
+ if ( response . ok ) {
3311
+ if ( response . body ?. getReader ) {
3312
+ const contentType = response . headers . get ( "Content-Type" ) ;
3313
+ const contentSize = response . headers . get ( "Content-Length" ) ;
3314
+ try {
3315
+ metadata = await parseWebStream ( response . body , {
3316
+ mimeType : contentType ,
3317
+ size : contentSize ? Number . parseInt ( contentSize , 10 ) : undefined
3318
+ } , { skipPostHeaders : true } ) ;
3319
+ } finally {
3320
+ await response . body . cancel ( ) ;
3331
3321
}
3332
3322
} else {
3333
- consoleLog ( `Failed to fetch metadata http-response=${ response . status } for url=${ queueItem . dataset . file } ` , true ) ;
3323
+ // Fallback to Blob, in case the HTTP Result cannot be streamed
3324
+ metadata = await parseBlob ( await response . blob ( ) ) ;
3334
3325
}
3326
+ } else {
3327
+ consoleLog ( `Failed to fetch metadata http-response=${ response . status } for url=${ queueItem . dataset . file } ` , true ) ;
3328
+ return ;
3335
3329
}
3336
3330
}
3337
- catch ( e ) {
3338
- consoleLog ( `Error converting queued file="${ queueItem . dataset . file ?? '?' } " to URI` , e ) ;
3339
- return ;
3340
- }
3331
+ }
3332
+ catch ( e ) {
3333
+ consoleLog ( `Error converting queued file="${ queueItem . dataset . file ?? '?' } " to URI` , e ) ;
3334
+ return ;
3335
+ }
3341
3336
3342
- console . log ( `Fetched metadata successful for url=${ queueItem . dataset . file } ` ) ;
3343
- addMetadata ( metadata , queueItem ) ; // add metadata to play queue item
3337
+ addMetadata ( metadata , queueItem ) ; // add metadata to play queue item
3344
3338
3345
- // If no embedded picture, try folder cover
3346
- if ( ! ( metadata . common . picture && metadata . common . picture . length > 0 ) ) {
3347
- queueItem . dataset . cover = await getFolderCover ( queueItem ) ;
3348
- }
3339
+ // If no embedded picture, try folder cover
3340
+ if ( ! ( metadata . common . picture && metadata . common . picture . length > 0 ) ) {
3341
+ queueItem . dataset . cover = await getFolderCover ( queueItem ) ;
3342
+ }
3343
+
3344
+ syncMetadataToAudioElements ( queueItem ) ;
3345
+
3346
+ }
3347
+
3348
+ /**
3349
+ * Retrieve metadata for each entry of the queue which has not metadata assigned yet
3350
+ * which have no metadata assigned yet
3351
+ */
3352
+ async function retrieveMetadata ( ) {
3353
+
3354
+
3355
+ const promises = [ ] ;
3349
3356
3350
- syncMetadataToAudioElements ( queueItem ) ;
3357
+ // Process in sequential order
3358
+ for ( const queueItem of playlist . children ) {
3359
+
3360
+ // Only process items with metadata
3361
+ if ( queueItem . dataset . retrieve ) {
3362
+ // Clear metadata retrieval flag
3363
+ delete queueItem . dataset . retrieve ;
3364
+ // Sets a global limit, as the function is call for each entry added
3365
+ promises . push ( metadataRequestLimit ( ( ) => retrieveMetadataForQueueItem ( queueItem ) ) ) ;
3366
+ }
3351
3367
}
3368
+
3369
+ return Promise . all ( promises ) ;
3352
3370
}
3353
3371
3354
3372
/**
0 commit comments