@@ -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 ( ) ) ;
@@ -909,6 +910,9 @@ const getCurrentSettings = _ => ({
909
910
weighting : getControlValue ( elWeighting )
910
911
} ) ;
911
912
913
+ // Limit the number of parallel metadata requests
914
+ const metadataRequestLimit = pLimit ( MAX_METADATA_REQUESTS ) ;
915
+
912
916
// get the array index for a preset key, or validate a given index; if invalid or not found returns -1
913
917
const getPresetIndex = key => {
914
918
const index = ( + key == key ) ? key : presets . findIndex ( item => item . key == key ) ;
@@ -1205,7 +1209,7 @@ async function addSongToPlayQueue( fileObject, content ) {
1205
1209
if ( FILE_EXT_AUDIO . includes ( extension ) || ! extension ) {
1206
1210
// disable retrieving metadata of video files for now - https://github.com/Borewit/music-metadata-browser/issues/950
1207
1211
trackData . retrieve = 1 ; // flag this item as needing metadata
1208
- await retrieveMetadata ( ) ;
1212
+ retrieveMetadata ( ) ; // ToDo improve handling promise
1209
1213
}
1210
1214
1211
1215
if ( queueLength ( ) === 1 && ! isPlaying ( ) ) {
@@ -1222,22 +1226,18 @@ async function addSongToPlayQueue( fileObject, content ) {
1222
1226
/**
1223
1227
* Add a song or playlist to the play queue
1224
1228
*/
1225
- function addToPlayQueue ( fileObject , autoplay = false ) {
1226
-
1227
- let ret ;
1229
+ async function addToPlayQueue ( fileObject , autoplay = false ) {
1228
1230
1231
+ let n ;
1229
1232
if ( FILE_EXT_PLIST . includes ( parsePath ( fileObject . file ) . extension ) )
1230
- ret = loadPlaylist ( fileObject ) ;
1233
+ n = await loadPlaylist ( fileObject ) ;
1231
1234
else
1232
- ret = addSongToPlayQueue ( fileObject ) ;
1235
+ n = await addSongToPlayQueue ( fileObject ) ;
1233
1236
1234
1237
// when promise resolved, if autoplay requested start playing the first added song
1235
- ret . then ( n => {
1236
- if ( autoplay && ! isPlaying ( ) && n > 0 )
1237
- playSong ( queueLength ( ) - n ) ;
1238
- } ) ;
1239
1238
1240
- return ret ;
1239
+ if ( autoplay && ! isPlaying ( ) && n > 0 )
1240
+ playSong ( queueLength ( ) - n ) ;
1241
1241
}
1242
1242
1243
1243
/**
@@ -1246,7 +1246,7 @@ function addToPlayQueue( fileObject, autoplay = false ) {
1246
1246
function changeFsHeight ( incr ) {
1247
1247
const val = + elFsHeight . value ;
1248
1248
1249
- if ( incr == 1 && val < + elFsHeight . max || incr == - 1 && val > + elFsHeight . min ) {
1249
+ if ( incr === 1 && val < + elFsHeight . max || incr = == - 1 && val > + elFsHeight . min ) {
1250
1250
elFsHeight . value = val + elFsHeight . step * incr ;
1251
1251
setProperty ( elFsHeight ) ;
1252
1252
}
@@ -3226,81 +3226,99 @@ async function retrieveBackgrounds() {
3226
3226
catch ( e ) { } // needs permission to access local device
3227
3227
}
3228
3228
3229
- if ( bgLocation != BGFOLDER_NONE ) {
3229
+ if ( bgLocation !== BGFOLDER_NONE ) {
3230
3230
const imageCount = bgImages . length ,
3231
3231
videoCount = bgVideos . length ;
3232
3232
3233
- consoleLog ( 'Found ' + ( imageCount + videoCount == 0 ? 'no media' : imageCount + ' image files and ' + videoCount + ' video' ) + ' files in the backgrounds folder' ) ;
3233
+ consoleLog ( 'Found ' + ( imageCount + videoCount === 0 ? 'no media' : imageCount + ' image files and ' + videoCount + ' video' ) + ' files in the backgrounds folder' ) ;
3234
3234
}
3235
3235
3236
3236
populateBackgrounds ( ) ;
3237
3237
}
3238
3238
3239
3239
/**
3240
- * Retrieve metadata for the first MAX_METADATA_REQUESTS files in the play queue,
3240
+ * Retrieve metadata for queueItem
3241
3241
* which have no metadata assigned yet
3242
3242
*/
3243
- async function retrieveMetadata ( ) {
3243
+ async function retrieveMetadataForQueueItem ( queueItem ) {
3244
3244
3245
- // Process in sequential order
3246
- for ( const queueItem of elPlayqueue . children ) {
3247
-
3248
- if ( ! queueItem . dataset . retrieve ) continue ;
3249
- delete queueItem . dataset . retrieve ;
3250
-
3251
- let metadata ;
3252
- let file ;
3245
+ let metadata ;
3246
+ let file ;
3253
3247
3254
- try {
3255
- if ( queueItem . handle ) {
3248
+ try {
3249
+ if ( queueItem . handle ) {
3256
3250
3257
- // Fetch metadata from File object
3258
- if ( await queueItem . handle . requestPermission ( ) !== 'granted' )
3259
- return ;
3251
+ // Fetch metadata from File object
3252
+ if ( await queueItem . handle . requestPermission ( ) !== 'granted' )
3253
+ return ;
3260
3254
3261
- file = await queueItem . handle . getFile ( ) ;
3262
- metadata = await parseBlob ( file ) ;
3263
- }
3264
- else
3265
- {
3266
- // Fetch metadata from URI
3267
- const response = await fetch ( queueItem . dataset . file ) ;
3268
- if ( response . ok ) {
3269
- if ( response . body ?. getReader ) {
3270
- const contentType = response . headers . get ( "Content-Type" ) ;
3271
- const contentSize = response . headers . get ( "Content-Length" ) ;
3272
- try {
3273
- metadata = await parseWebStream ( response . body , {
3274
- mimeType : contentType ,
3275
- size : contentSize ? Number . parseInt ( contentSize , 10 ) : undefined
3276
- } , { skipPostHeaders : true } ) ;
3277
- } finally {
3278
- await response . body . cancel ( ) ;
3279
- }
3280
- } else {
3281
- // Fallback to Blob, in case the HTTP Result cannot be streamed
3282
- metadata = await parseBlob ( await response . blob ( ) ) ;
3255
+ file = await queueItem . handle . getFile ( ) ;
3256
+ metadata = await parseBlob ( file ) ;
3257
+ }
3258
+ else
3259
+ {
3260
+ // Fetch metadata from URI
3261
+ const response = await fetch ( queueItem . dataset . file ) ;
3262
+ if ( response . ok ) {
3263
+ if ( response . body ?. getReader ) {
3264
+ const contentType = response . headers . get ( "Content-Type" ) ;
3265
+ const contentSize = response . headers . get ( "Content-Length" ) ;
3266
+ try {
3267
+ metadata = await parseWebStream ( response . body , {
3268
+ mimeType : contentType ,
3269
+ size : contentSize ? Number . parseInt ( contentSize , 10 ) : undefined
3270
+ } , { skipPostHeaders : true } ) ;
3271
+ } finally {
3272
+ await response . body . cancel ( ) ;
3283
3273
}
3284
3274
} else {
3285
- consoleLog ( `Failed to fetch metadata http-response=${ response . status } for url=${ queueItem . dataset . file } ` , true ) ;
3275
+ // Fallback to Blob, in case the HTTP Result cannot be streamed
3276
+ metadata = await parseBlob ( await response . blob ( ) ) ;
3286
3277
}
3278
+ } else {
3279
+ consoleLog ( `Failed to fetch metadata http-response=${ response . status } for url=${ queueItem . dataset . file } ` , true ) ;
3280
+ return ;
3287
3281
}
3288
3282
}
3289
- catch ( e ) {
3290
- consoleLog ( `Error converting queued file="${ queueItem . dataset . file ?? '?' } " to URI` , e ) ;
3291
- return ;
3292
- }
3283
+ }
3284
+ catch ( e ) {
3285
+ consoleLog ( `Error converting queued file="${ queueItem . dataset . file ?? '?' } " to URI` , e ) ;
3286
+ return ;
3287
+ }
3293
3288
3294
- console . log ( `Fetched metadata successful for url=${ queueItem . dataset . file } ` ) ;
3295
- addMetadata ( metadata , queueItem ) ; // add metadata to play queue item
3289
+ addMetadata ( metadata , queueItem ) ; // add metadata to play queue item
3296
3290
3297
- // If no embedded picture, try folder cover
3298
- if ( ! ( metadata . common . picture && metadata . common . picture . length > 0 ) ) {
3299
- queueItem . dataset . cover = await getFolderCover ( queueItem ) ;
3300
- }
3291
+ // If no embedded picture, try folder cover
3292
+ if ( ! ( metadata . common . picture && metadata . common . picture . length > 0 ) ) {
3293
+ queueItem . dataset . cover = await getFolderCover ( queueItem ) ;
3294
+ }
3295
+
3296
+ syncMetadataToAudioElements ( queueItem ) ;
3297
+
3298
+ }
3299
+
3300
+ /**
3301
+ * Retrieve metadata for each entry of the queue which has not metadata assigned yet
3302
+ * which have no metadata assigned yet
3303
+ */
3304
+ async function retrieveMetadata ( ) {
3305
+
3306
+
3307
+ const promises = [ ] ;
3301
3308
3302
- syncMetadataToAudioElements ( queueItem ) ;
3309
+ // Process in sequential order
3310
+ for ( const queueItem of elPlayqueue . children ) {
3311
+
3312
+ // Only process items with metadata
3313
+ if ( queueItem . dataset . retrieve ) {
3314
+ // Clear metadata retrieval flag
3315
+ delete queueItem . dataset . retrieve ;
3316
+ // Sets a global limit, as the function is call for each entry added
3317
+ promises . push ( metadataRequestLimit ( ( ) => retrieveMetadataForQueueItem ( queueItem ) ) ) ;
3318
+ }
3303
3319
}
3320
+
3321
+ return Promise . all ( promises ) ;
3304
3322
}
3305
3323
3306
3324
/**
0 commit comments