@@ -36,6 +36,7 @@ import {parseBlob, parseWebStream} from 'music-metadata';
3636import './scrollIntoViewIfNeeded-polyfill.js' ;
3737import { get , set , del } from 'idb-keyval' ;
3838import * as yaml from 'js-yaml' ;
39+ import pLimit from 'p-limit' ;
3940
4041import Sortable , { MultiDrag } from 'sortablejs' ;
4142Sortable . mount ( new MultiDrag ( ) ) ;
@@ -909,6 +910,9 @@ const getCurrentSettings = _ => ({
909910 weighting : getControlValue ( elWeighting )
910911} ) ;
911912
913+ // Limit the number of parallel metadata requests
914+ const metadataRequestLimit = pLimit ( MAX_METADATA_REQUESTS ) ;
915+
912916// get the array index for a preset key, or validate a given index; if invalid or not found returns -1
913917const getPresetIndex = key => {
914918 const index = ( + key == key ) ? key : presets . findIndex ( item => item . key == key ) ;
@@ -1205,7 +1209,7 @@ async function addSongToPlayQueue( fileObject, content ) {
12051209 if ( FILE_EXT_AUDIO . includes ( extension ) || ! extension ) {
12061210 // disable retrieving metadata of video files for now - https://github.com/Borewit/music-metadata-browser/issues/950
12071211 trackData . retrieve = 1 ; // flag this item as needing metadata
1208- await retrieveMetadata ( ) ;
1212+ retrieveMetadata ( ) ; // ToDo improve handling promise
12091213 }
12101214
12111215 if ( queueLength ( ) === 1 && ! isPlaying ( ) ) {
@@ -1222,22 +1226,18 @@ async function addSongToPlayQueue( fileObject, content ) {
12221226/**
12231227 * Add a song or playlist to the play queue
12241228 */
1225- function addToPlayQueue ( fileObject , autoplay = false ) {
1226-
1227- let ret ;
1229+ async function addToPlayQueue ( fileObject , autoplay = false ) {
12281230
1231+ let n ;
12291232 if ( FILE_EXT_PLIST . includes ( parsePath ( fileObject . file ) . extension ) )
1230- ret = loadPlaylist ( fileObject ) ;
1233+ n = await loadPlaylist ( fileObject ) ;
12311234 else
1232- ret = addSongToPlayQueue ( fileObject ) ;
1235+ n = await addSongToPlayQueue ( fileObject ) ;
12331236
12341237 // 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- } ) ;
12391238
1240- return ret ;
1239+ if ( autoplay && ! isPlaying ( ) && n > 0 )
1240+ playSong ( queueLength ( ) - n ) ;
12411241}
12421242
12431243/**
@@ -1246,7 +1246,7 @@ function addToPlayQueue( fileObject, autoplay = false ) {
12461246function changeFsHeight ( incr ) {
12471247 const val = + elFsHeight . value ;
12481248
1249- if ( incr == 1 && val < + elFsHeight . max || incr == - 1 && val > + elFsHeight . min ) {
1249+ if ( incr === 1 && val < + elFsHeight . max || incr = == - 1 && val > + elFsHeight . min ) {
12501250 elFsHeight . value = val + elFsHeight . step * incr ;
12511251 setProperty ( elFsHeight ) ;
12521252 }
@@ -3226,81 +3226,99 @@ async function retrieveBackgrounds() {
32263226 catch ( e ) { } // needs permission to access local device
32273227 }
32283228
3229- if ( bgLocation != BGFOLDER_NONE ) {
3229+ if ( bgLocation !== BGFOLDER_NONE ) {
32303230 const imageCount = bgImages . length ,
32313231 videoCount = bgVideos . length ;
32323232
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' ) ;
32343234 }
32353235
32363236 populateBackgrounds ( ) ;
32373237}
32383238
32393239/**
3240- * Retrieve metadata for the first MAX_METADATA_REQUESTS files in the play queue,
3240+ * Retrieve metadata for queueItem
32413241 * which have no metadata assigned yet
32423242 */
3243- async function retrieveMetadata ( ) {
3243+ async function retrieveMetadataForQueueItem ( queueItem ) {
32443244
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 ;
32533247
3254- try {
3255- if ( queueItem . handle ) {
3248+ try {
3249+ if ( queueItem . handle ) {
32563250
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 ;
32603254
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 ( ) ;
32833273 }
32843274 } 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 ( ) ) ;
32863277 }
3278+ } else {
3279+ consoleLog ( `Failed to fetch metadata http-response=${ response . status } for url=${ queueItem . dataset . file } ` , true ) ;
3280+ return ;
32873281 }
32883282 }
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+ }
32933288
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
32963290
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 = [ ] ;
33013308
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+ }
33033319 }
3320+
3321+ return Promise . all ( promises ) ;
33043322}
33053323
33063324/**
0 commit comments