Smart Playlists - duplicate track removal = smart pruning

To discuss development of addons / skins / customization of MediaMonkey.

Moderators: jiri, drakinite, Addon Administrators

telecore
Posts: 18
Joined: Thu Jan 28, 2021 10:20 pm

Smart Playlists - duplicate track removal = smart pruning

Post by telecore »

In MM4, I had written a useful script that removes tracks with duplicate artists and titles from playlists. It worked by searching for matching artist and titles within the same playlist (with a significant amount of string processing for cleanup). The output was a manual playlist copy of the original (original=smart or manual) with the same name except for an asterisk at the end. For me, this was extremely useful for removing duplicate tracks from playlists that may be present on different albums - without having to delete one of the copies of the track, therefore, keeping all albums intact. The "pruned" playlist is what ends up being used for playback and synced to devices.

I would really like to duplicate this in MM5, however, it would have to be re-written from scratch as scripting has been totally changed. I have a programming background, but I really had to spend a lot of time writing the original MM4 script by "hacking" examples from others and I really don't have the time to re-do this (I tried a few months ago).

I think this could be a useful built-in feature of MM5, to add a "smart pruning" capability for playlists - the existing "remove duplicates", for manual playlists only doesn't really help - is this something that the developers would consider adding as a feature?
Ludek
Posts: 4270
Joined: Fri Mar 09, 2007 9:00 am

Re: Smart Playlists - duplicate track removal = smart pruning

Post by Ludek »

Hi, adding script/addon like this should be quite easy.

In actions.js there is already an action for removing playlist duplicates by track.id, i.e. this code:

Code: Select all

 playlistRemoveDuplicates: {
            title: function () {
                return _('Remove duplicates');
            },
            icon: 'remove',
            visible: function () {
                if (!window.uitools.getCanEdit())
                    return false;
                else {
                    var pl = resolveToValue(this.boundObject);
                    return (pl.parent != undefined && !pl.isAutoPlaylist); // to exclude root playlists node and auto-playlists
                }
            },
            execute: function () {
                var pl = resolveToValue(this.boundObject);
                var list = pl.getTracklist();
                list.whenLoaded().then(() => {
                    list.modifyAsync(() => {
                        var hasher = {};
                        listForEach(list, (track, idx) => {
                            if (hasher[track.id])
                                list.setSelected(idx, true); // duplicate
                            hasher[track.id] = true;
                        });
                        pl.removeSelectedTracksAsync(list);
                    });
                });
            }
        },
All that needs to be changed are the lines around hasher[track.id] like this:

Code: Select all

if (!hasher[track.title])
     newList.add(track); // not a duplicate
hasher[track.title] = true;
And create new playlist like this and put the new list there:

Code: Select all

var newplaylist = app.playlists.root.newPlaylist();
newplaylist.name = pl.name + ' (filtered)';
newplaylist.commitAsync().then(function () {
                    newplaylist.addTracksAsync(newList);
});

So in your addon create actions_add.js and put following there:

Code: Select all

actions.playlistRemoveDuplicatesByTitle = {
    title: function () {
        return _('Remove duplicates by title');
    },
    hotkeyAble: true,
    icon: 'remove',
    visible: function () {
        if (!window.uitools.getCanEdit())
            return false;
        else {
            var pl = resolveToValue(this.boundObject);
            return (pl.parent != undefined); // to exclude root playlists node
        }
    },
    execute: function () {
        var pl = resolveToValue(this.boundObject);
        var list = pl.getTracklist();
        var newList = app.utils.createTracklist();
        list.whenLoaded().then(() => {
                var hasher = {};
                listForEach(list, (track, idx) => {
                    if (!hasher[track.title])
                        newList.add(track); // not a duplicate
                    hasher[track.title] = true;
                });
                var newplaylist = app.playlists.root.newPlaylist();
                newplaylist.name = pl.name + ' (filtered)';
                newplaylist.commitAsync().then(function () {
                    newplaylist.addTracksAsync(newList);
                });
        });
    }
};

And then put to to context menu of playlist by creating viewHandlers_add.js and puttin this code:

Code: Select all

nodeHandlers.playlist.menuAddons = nodeHandlers.playlist.menuAddons || [];
nodeHandlers.playlist.menuAddons.push(function (node) {
    if (node && node.dataSource) {
        return [{
            action: bindAction(window.actions.playlistRemoveDuplicatesByTitle, () => {
                return node.dataSource;
            }),
            order: 40,
            grouporder: 10,
        }];
    };
    return [];
});
Last edited by Ludek on Fri Sep 17, 2021 8:55 am, edited 3 times in total.
Ludek
Posts: 4270
Joined: Fri Mar 09, 2007 9:00 am

Re: Smart Playlists - duplicate track removal = smart pruning

Post by Ludek »

The working script is here: https://www.dropbox.com/s/td1t3albp2jgr ... .mmip?dl=0
feel free to adjust/tune the code ;-)
telecore
Posts: 18
Joined: Thu Jan 28, 2021 10:20 pm

Re: Smart Playlists - duplicate track removal = smart pruning

Post by telecore »

Thanks! - I just saw this and downloaded

Update: Got it working well - although it is a different approach than my previous script (this new one uses hashing), it is working about 99% the same as my MM4 script for removing duplicate artists/titles - Thanks again!
Post Reply