fixAlbumArt

Post a reply

Smilies
:D :) :( :o :-? 8) :lol: :x :P :oops: :cry: :evil: :roll: :wink:

BBCode is ON
[img] is ON
[url] is ON
Smilies are ON

Topic review
   

Expand view Topic review: fixAlbumArt

Re: fixAlbumArt

by MPG » Fri Feb 11, 2022 10:20 am

Thanks Ludek! I look forward to trying it out!

Re: fixAlbumArt

by Ludek » Thu Feb 10, 2022 5:45 pm

OK, it wasn't so easy as the image format might need to be converted during that process (e.g. BMP to PNG), plus not all track types supports images stored in file tag (e.g. WAV, AIFF), etc.

Therefore I needed to introduce new method move2TagAsync, usage like this:

Code: Select all

                                            firstCover.move2TagAsync().then(() => {                                                
                                                next(); // succcess
                                            }, (error) => {       
                                                ODS('Error during moving cover to file tag: ' + error);                                                                                     
                                                next();
                                            });
The method will be included in the next 5.0.3 build and the whole code looks like this (tested that it works):

Code: Select all

                function removeAll(trackList) {

                    var cvr;
                    var cvrList;

                    // Create a background task so that the user can see the progress in the bottom bar
                    var taskProgress = app.backgroundTasks.createNew();
                    taskProgress.leadingText = ('Fixing art: ');
                    var trackCount = trackList.count;
                    var trackIdx = 0;

                    listAsyncForEach(trackList,
                        // callback for each track
                        (track, next) => {

                            // Update the background task text
                            trackIdx++;
                            taskProgress.text = sprintf('%d of %d', trackIdx, trackCount);

                            cvrList = track.loadCoverListAsync();
                            cvrList.whenLoaded().then(() => {
                                cvrList.modifyAsync(function () {
                                    if (cvrList.count > 0) {

                                        //loop through all covers to find if any cover identified as the front
                                        //any cover not identified as a front cover is set as "Not Specified"
                                        var blnFrontFound = false;
                                        for (var intCnt = 0; intCnt < cvrList.count; intCnt++) {
                                            cvr = cvrList.getValue(intCnt);
                                            if (blnFrontFound == true) {
                                                cvr.coverTypeDesc = "Not specified";
                                            } else {
                                                if (cvr.coverTypeDesc == "Cover (front)") {
                                                    blnFrontFound = true;
                                                } else {
                                                    cvr.coverTypeDesc = "Not specified";
                                                }
                                            }
                                        }

                                        //if none found make the first embedded cover the front
                                        if (blnFrontFound == false) {
                                            for (var intCnt = 0; intCnt < cvrList.count; intCnt++) {
                                                if (cvrList.getValue(intCnt).coverStorage == 0) {
                                                    cvr = cvrList.getValue(intCnt);
                                                    cvr.coverTypeDesc = "Cover (front)";
                                                    blnFrontFound = true;
                                                    intCnt = cvrList.count + 1;
                                                }
                                            }
                                        }

                                        //if none found, make first image the front
                                        if (blnFrontFound == false) {
                                            cvr = cvrList.getValue(0);
                                            cvr.coverTypeDesc = "Cover (front)";
                                        }

                                        //select all covers except front cover
                                        for (var intCnt = 0; intCnt < cvrList.count; intCnt++) {
                                            cvr = cvrList.getValue(intCnt);
                                            if (cvr.coverTypeDesc == "Cover (front)") {
                                                cvrList.setSelected(intCnt, false);
                                            } else {
                                                cvrList.setSelected(intCnt, true);
                                            }
                                        };
                                    }
                                    cvrList.deleteSelected();

                                    if (cvrList.count > 0) {
                                        var firstCover = cvrList.getValue(0);

                                        // The if (firstCover &&) part is just a sanity check to avoid a crash 
                                        if (firstCover && firstCover.coverStorage != 0) {
	                                   cvrList.modified = true; // just in case no cover has been deleted and we change the storage below                                                                            
                                            firstCover.move2TagAsync().then(() => {                                                
                                                next(); // succcess
                                            }, (error) => {       
                                                ODS('Error during moving cover to file tag: ' + error);                                                                                     
                                                next(); // error
                                            });
                                            return;
                                        }
                                    }
                                    // Commit track, then AFTER the commit is done, process the next track
                                    next();
                                });
                            });
                        },
                        // callback when done
                        () => {
                            // finish the background task
                            trackList.commitAsync().then(() => {
                                taskProgress.terminate();
                                messageDlg(_('Album Art is fixed!'), 'Information', ['btnOK'], {
                                    defaultButton: 'btnOK'
                                }, undefined);
                            });
                        }
                    );
                }

Re: fixAlbumArt

by MPG » Thu Feb 10, 2022 5:02 pm

Yes, the purpose is to make one image csTag by using a series of criteria and remove the other images.

Thanks I appreciate your assistance.

Re: fixAlbumArt

by Ludek » Thu Feb 10, 2022 4:53 pm

Ahh, I see, isn't it casued by this code:

Code: Select all

  if (firstCover && firstCover.coverStorage != 0) {
                                            firstCover.coverStorage = 0;                                            
                                        }
?

You seem to force the storage from csFile, csNotSaved to csTag this way, but I guess it fails whenever the "imageData" are not loaded thus subsequent writing to the file tag fails.

Is this a purpose to change the storage to csTag ? I am searching for a way how to force the "imageData" to load for the cover item (from JS) if there is one already...

EDIT: Just checking our code and the "imageData" are kept only for the new unsaved images, but once the images are saved then they are internally loaded only on purpose and then freed to not occupy memory needlessly. So I will need to add a function to load the data from JS or rather a new method to change the coverStorage (or add it to the coverStorage setter)

Re: fixAlbumArt

by MPG » Thu Feb 10, 2022 12:41 pm

Hi Ludek,
Thanks again for trying to help me. I copied your function into my code and ran the script against my test songs and it still doesn't work for me. My test songs have no embedded images. All images are linked to individual images within a folder (no thm file)
The code removes all of the images except one.
The last image has a label on it that says: "Save to tag"
If I click on the image, MM freezes. If I close and reopen MM without clicking on the image, the image isn't saved to the song.

When I run the code against a song that has an embedded image and other linked images, it works just fine.

Would it be possible for me to share the song and it's images with you so you can test on your machine?

Re: fixAlbumArt

by Ludek » Thu Feb 10, 2022 11:54 am

This is the code that works fine for me:

Code: Select all

function removeAll(trackList) {

                    var cvr;
                    var cvrList;

                    // Create a background task so that the user can see the progress in the bottom bar
                    var taskProgress = app.backgroundTasks.createNew();
                    taskProgress.leadingText = ('Fixing art: ');
                    var trackCount = trackList.count;
                    var trackIdx = 0;

                    listAsyncForEach(trackList,
                        // callback for each track
                        (track, next) => {

                            // Update the background task text
                            trackIdx++;
                            taskProgress.text = sprintf('%d of %d', trackIdx, trackCount);

                            cvrList = track.loadCoverListAsync();
                            cvrList.whenLoaded().then(() => {
                                cvrList.modifyAsync(function () {
                                    if (cvrList.count > 0) {

                                        //loop through all covers to find if any cover identified as the front
                                        //any cover not identified as a front cover is set as "Not Specified"
                                        var blnFrontFound = false;
                                        for (var intCnt = 0; intCnt < cvrList.count; intCnt++) {
                                            cvr = cvrList.getValue(intCnt);
                                            if (blnFrontFound == true) {
                                                cvr.coverTypeDesc = "Not specified";
                                            } else {
                                                if (cvr.coverTypeDesc == "Cover (front)") {
                                                    blnFrontFound = true;
                                                } else {
                                                    cvr.coverTypeDesc = "Not specified";
                                                }
                                            }
                                        }
                                        
                                        //if none found make the first embedded cover the front
                                        if (blnFrontFound == false) {
                                            for (var intCnt = 0; intCnt < cvrList.count; intCnt++) {
                                                if (cvrList.getValue(intCnt).coverStorage == 0) {
                                                    cvr = cvrList.getValue(intCnt);
                                                    cvr.coverTypeDesc = "Cover (front)";
                                                    blnFrontFound = true;
                                                    intCnt = cvrList.count + 1;
                                                }
                                            }
                                        }
                                        
                                        //if none found, make first image the front
                                        if (blnFrontFound == false) {
                                            cvr = cvrList.getValue(0);
                                            cvr.coverTypeDesc = "Cover (front)";
                                        }

                                        //select all covers except front cover
                                        for (var intCnt = 0; intCnt < cvrList.count; intCnt++) {
                                            cvr = cvrList.getValue(intCnt);
                                            if (cvr.coverTypeDesc == "Cover (front)") {
                                                cvrList.setSelected(intCnt, false);                                                
                                            } else {
                                                cvrList.setSelected(intCnt, true);
                                            }
                                        };
                                    }
                                    cvrList.deleteSelected();
                                    if (cvrList.count > 0) {
                                        var firstCover = cvrList.getValue(0);

                                        // The if (firstCover &&) part is just a sanity check to avoid a crash 
                                        if (firstCover && firstCover.coverStorage != 0) {
                                            firstCover.coverStorage = 0;                                            
                                        }
                                    }
                                    // Commit track, then AFTER the commit is done, process the next track
                                    cvrList.modified = true;                                    
                                    next();
                                });
                            });
                        },
                        // callback when done
                        () => {
                            // finish the background task
                            trackList.commitAsync().then(() => {
                                taskProgress.terminate();
                                messageDlg(_('Album Art is fixed!'), 'Information', ['btnOK'], {
                                    defaultButton: 'btnOK'
                                }, undefined);
                            });
                        }
                    );                   
                }

without a freeze and leaving only single artwork front cover / embedded for each track.

Re tracks.saveCoverListAsync : Sorry, just realized that this method is public, but not published, which means that isn't accessible from JS (only from our Delphi internal code). So use track(list).commitAsync() instead.

Re: fixAlbumArt

by MPG » Wed Feb 09, 2022 4:20 pm

sorry, one more item I just noticed...
When you open the song(s) that you run the script against, the image label says "Save to Tag", whereas a song with the embedded image says "Stored In Tag"...

Not sure what the difference is or how to get it changed...Any suggestions?

Re: fixAlbumArt

by drakinite » Wed Feb 09, 2022 4:11 pm

Btw: If the alerts are giving you trouble, considering the fact that they are synchronous & blocking, try just doing console.log. You can open the dev tools either by going to localhost:9222 on any browser (even on non debug builds) or by right click > inspect element on a debug build.

Re: fixAlbumArt

by MPG » Wed Feb 09, 2022 4:10 pm

One other item...I get a failure that track.saveCoverListAsync is not a function. :roll:

Re: fixAlbumArt

by MPG » Wed Feb 09, 2022 4:07 pm

Hi Ludek...Great news that it worked for you.

Can you double check your results by doing the following test...
After running the script on a song that has several links to images (none that are embedded), click on the image to open it. There is when the freeze is happening on my machine. If I close and reopen MM, the song doesn't have any images.

Edit:
I'm asking if you can do this test, as MM shows the expected result, but then freezes when I double click the image. If I just close MM after running the script and then check the song, it has no images.

Re: fixAlbumArt

by Ludek » Wed Feb 09, 2022 4:03 pm

BTW: Just tested you code and if I remove the alerts then it is working just fine here.

The only problem is that the message is shown too early, I guess you might want to move it to the end callback after the tracklist.commitAsync like this:

Code: Select all

...
                        () => {
                            // finish the background task
                            trackList.commitAsync().then(() => {
                                taskProgress.terminate();
                                messageDlg(_('Album Art is fixed!'), 'Information', ['btnOK'], {
                                    defaultButton: 'btnOK'
                                }, undefined);
                            });
                        }

Re: fixAlbumArt

by Ludek » Wed Feb 09, 2022 3:34 pm

Not tested, but by a quick look maybe

Code: Select all

track.commitAsync().then(next);
inside of the cvrList.modifyAsync could somehow cause it as it is still in the cvrList write lock?

So try to replace

Code: Select all

track.commitAsync().then(next);
just by

Code: Select all

next();
as subsequent calling of
trackList.commitAsync();
commits all the tracks anyhow.

EDIT: Also ensure that cvrList.modified is TRUE, which is done automatically whenever write lock is acquired, e.g. by modifyAsync (based on our internal code review), so just to ensure it is true (for it to save during commitAsync)

EDIT2: Alternativelly you can use method

Code: Select all

track.saveCoverListAsync();
(if you want to save just the coverList)

Re: fixAlbumArt

by MPG » Mon Feb 07, 2022 4:23 pm

I've noticed that if I close MM after running the script, there is a prompt asking if I wish to terminate background scripts. It closes before I ever get to say yes or no.

Re: fixAlbumArt

by MPG » Mon Feb 07, 2022 3:47 pm

Okay, so I completely redesigned it to set it up to be async. The code removes all of the art except one, sets it as Cover (front), and doesn't fail...yea me. Unfortunately, the results are not being saved. If I double click the image after running the script, MM freezes. If I just close MM and reopen it, the images are not saved.

Code: Select all

// A simple script that replaces a specified string in Song Title, Artist, Album and Album Artist

function removeAll( trackList) {
	
	var cvr;
	var cvrList;
	
	// Create a background task so that the user can see the progress in the bottom bar
	var taskProgress = app.backgroundTasks.createNew();
	taskProgress.leadingText = ('Fixing art: ');
	var trackCount = trackList.count;
	var trackIdx = 0;

	listAsyncForEach(trackList, 
		// callback for each track
		(track, next) => {

			// Update the background task text
			trackIdx++; 
			taskProgress.text = sprintf('%d of %d', trackIdx, trackCount);
			
			cvrList = track.loadCoverListAsync();
			cvrList.whenLoaded().then(() => {
				cvrList.modifyAsync(function () {
					if(cvrList.count > 0) {
						
						//loop through all covers to find if any cover identified as the front
						//any cover not identified as a front cover is set as "Not Specified"
						var blnFrontFound = false;
						for(var intCnt = 0; intCnt < cvrList.count; intCnt++){
							cvr = cvrList.getValue(intCnt);
							if (blnFrontFound == true){
								cvr.coverTypeDesc = "Not specified";
							} else {
								if( cvr.coverTypeDesc == "Cover (front)"){
									blnFrontFound = true;
								} else {
									cvr.coverTypeDesc = "Not specified";
								}
							}
						}
						alert("1: " + blnFrontFound);
						//if none found make the first embedded cover the front
						if (blnFrontFound == false){
							for(var intCnt = 0; intCnt < cvrList.count; intCnt++){
								if(cvrList.getValue(intCnt).coverStorage == 0){
									cvr = cvrList.getValue(intCnt);						
									cvr.coverTypeDesc = "Cover (front)";
									blnFrontFound = true;
									intCnt = cvrList.count + 1;
								}
							}
						}
						alert("2: " + blnFrontFound);
						//if none found, make first image the front
						if (blnFrontFound == false){
							cvr = cvrList.getValue(0);
							cvr.coverTypeDesc = "Cover (front)";
						}
		
						//select all covers except front cover
						for(var intCnt = 0; intCnt < cvrList.count; intCnt++){
							cvr = cvrList.getValue(intCnt);
							if( cvr.coverTypeDesc == "Cover (front)"){
								cvrList.setSelected(intCnt, false);
								alert("3: " + blnFrontFound);
							} else{
								cvrList.setSelected(intCnt, true);
							}
						};
					}
					cvrList.deleteSelected();
					if(cvrList.count > 0){
						var firstCover = cvrList.getValue(0);

						// The if (firstCover &&) part is just a sanity check to avoid a crash 
						if (firstCover && firstCover.coverStorage != 0){ 
							firstCover.coverStorage = 0;
							alert("4: " + blnFrontFound);							
						}
					}
					// Commit track, then AFTER the commit is done, process the next track
                    cvrList.modified = true;
					alert("track commit")
					track.commitAsync().then(next);
				});
			});
		}, 
		// callback when done
		() => {
			// finish the background task
			trackList.commitAsync();
			alert("tracklist Commit")
			return taskProgress.terminate();
		}
	);

	messageDlg(_('Album Art is fixed!'), 'Information', ['btnOK'], {
		defaultButton: 'btnOK'
	}, undefined);

}
Disregard the alerts....that's me debugging. :cry:

Re: fixAlbumArt

by drakinite » Mon Feb 07, 2022 2:30 pm

Also, this bit might be problematic?

Code: Select all

if(cvrList.getValue(intCnt).coverStorage == 0){
      cvr = cvrList.getValue(intCnt);	
Depends on whether cvrList.getValue(idx) can ever return null or undefined. Ludek, can it?

Top