I did some digging and your question has led me to learn a lot about sorting and auto-playlists that I did not before. Several notes:
1. Documentation on tracklist sorting is terribly lacking. I've updated the documentation, but it'll be a bit before it's live on the site. Here's a copy of setAutoSortAsync and setSortRule that I've written:
setAutoSortAsync:
ASYNCHRONOUSLY sorts the list with the given sorting rule and updates the auto-sort rule.
To specify sort direction, append ' ASC' or ' DESC' to the tag name and separate them by semicolons.
Fields are NOT case sensitive (e.g. 'artist', 'Artist', and 'ArTiSt') are all valid, but sort direction IS case sensitive ('ASC' and 'DESC' are valid; 'asc' and 'desc' are not)
Valid examples:
list.setAutoSortAsync('title');
list.setAutoSortAsync('artist; title');
list.setAutoSortAsync('rating DESC; title ASC');
list.setAutoSortAsync('Rating DESC; TITLE;');
Invalid examples:
list.setAutoSortAsync('tagThatDoesNotExist');
list.setAutoSortAsync('rating desc; title asc');
setSortRule:
SYNCHRONOUSLY sorts the list with the given sorting rule and disables auto-sort.
To specify sort direction, append ' ASC' or ' DESC' to the tag name and separate them by semicolons.
Fields are NOT case sensitive (e.g. 'artist', 'Artist', and 'ArTiSt') are all valid, but sort direction IS case sensitive ('ASC' and 'DESC' are valid; 'asc' and 'desc' are not)
Valid examples:
list.setSortRule('title');
list.setSortRule('artist; title');
list.setSortRule('rating DESC; title ASC');
list.setSortRule('Rating DESC; TITLE;');
Invalid examples:
list.setSortRule('tagThatDoesNotExist');
list.setSortRule('rating desc; title asc');
2. You don't need to do beginUpdate() and endUpdate() in this case. It is useful when we have UI controls who have listened to certain events from their dataSources.
BUT since this is a brand-new clone, and you have not listened to events (e.g. app.listen() or localListen()), it'll have no impact (description of beginUpdate:
Lock object to update state. Events are not called when in update state.)
3. You'll actually need different code for auto-playlists and regular playlists. You were using reorderAsync() correctly for a regular playlist, but auto-playlists use a more complex sorting method and reorderAsync() does not work for them. (I've also updated the docs to note this)
Auto Playlists use a
QueryData object to handle their sorting. You can check searchEditor.js and playlistHeader.js for how these QueryData objects are created and updated. Also, the property isAutoPlaylist can be read to check if a given playlist is an auto-playlist AND updated in case you want to switch its type.
4. It's better to use tracks_sort.setAutoSortAsync() to avoid blocking the main UI thread.
5. Because of all the async functions required, it'll be much easier to use an async function and use await each in order. (Otherwise, you'd be doing a lot of .then()s and lots of annoying indentation, a.k.a. "callback hell")
All together:
Code: Select all
pl.createCopyAsync().then(async (pl_sort) => {
pl_sort.name = pl.name + ' (sorted)';
pl_sort.parent = pl;
// Do QueryData shenanigans if it's an auto-playlist
if (pl_sort.isAutoPlaylist) {
let queryData = await app.db.getQueryData({ category: 'empty' }); // Create a new QueryData object
queryData.loadFromString(pl_sort.queryData); // Transfer all its properties from the playlist (essentially creating a copy)
queryData.setSortOrders([ // Set its sort order: this time it's an array of objects insteada of a string
{
name: 'Rating',
ascending: false,
},
{
name: 'Bitrate',
ascending: false,
},
{
name: 'Length',
ascending: false,
},
]);
pl_sort.queryData = qd.saveToString(); // Update the playlist's queryData
// (Note: TPlaylist.queryData isn't actually stored as a string. Internally, it uses saveToString() as a getter and loadFromString() as a setter.)
pl_sort.commitAsync(); // Commit the playlist
pl_sort.notifyChanged('tracklist'); // to live update tracks -- is listened e.g. by viewHandlers.tracklistBase.onShow
}
// otherwise, use the tracklist sort method
else {
let tracks_sort = pl_sort.getTracklist();
await tracks_sort.whenLoaded();
await tracks_sort.setAutoSortAsync("Rating DESC; Bitrate DESC; Length DESC;");
await pl_sort.reorderAsync(tracks_sort);
pl_sort.commitAsync();
}
});
telecore wrote: ↑Mon Sep 19, 2022 1:33 pm
still having difficulties with async programming in js - sometimes bad code will lock up MM5 -
Last note is unrelated to playlists and tracklists: MM5 will often lock up and crash if you do something wrong with a native Delphi object or method (All of the objects we've been messing with here are native MM objects). When in doubt, check the documentation, and if the documentation isn't clear, don't hesitate to ask for us to clarify (and update the documentation to be more clear).
I did some digging and your question has led me to learn a lot about sorting and auto-playlists that I did not before. Several notes:
1. Documentation on tracklist sorting is terribly lacking. I've updated the documentation, but it'll be a bit before it's live on the site. Here's a copy of setAutoSortAsync and setSortRule that I've written:
[b]setAutoSortAsync:[/b]
ASYNCHRONOUSLY sorts the list with the given sorting rule and updates the auto-sort rule.
To specify sort direction, append ' ASC' or ' DESC' to the tag name and separate them by semicolons.
Fields are NOT case sensitive (e.g. 'artist', 'Artist', and 'ArTiSt') are all valid, but sort direction IS case sensitive ('ASC' and 'DESC' are valid; 'asc' and 'desc' are not)
Valid examples:
[i] list.setAutoSortAsync('title');
list.setAutoSortAsync('artist; title');
list.setAutoSortAsync('rating DESC; title ASC');
list.setAutoSortAsync('Rating DESC; TITLE;');[/i]
Invalid examples:
[i] list.setAutoSortAsync('tagThatDoesNotExist');
list.setAutoSortAsync('rating desc; title asc');[/i]
[b]setSortRule:[/b]
SYNCHRONOUSLY sorts the list with the given sorting rule and disables auto-sort.
To specify sort direction, append ' ASC' or ' DESC' to the tag name and separate them by semicolons.
Fields are NOT case sensitive (e.g. 'artist', 'Artist', and 'ArTiSt') are all valid, but sort direction IS case sensitive ('ASC' and 'DESC' are valid; 'asc' and 'desc' are not)
Valid examples:
[i] list.setSortRule('title');
list.setSortRule('artist; title');
list.setSortRule('rating DESC; title ASC');
list.setSortRule('Rating DESC; TITLE;');[/i]
Invalid examples:
[i] list.setSortRule('tagThatDoesNotExist');
list.setSortRule('rating desc; title asc');[/i]
2. You don't need to do beginUpdate() and endUpdate() in this case. It is useful when we have UI controls who have listened to certain events from their dataSources. [b]BUT[/b] since this is a brand-new clone, and you have not listened to events (e.g. app.listen() or localListen()), it'll have no impact (description of beginUpdate: [i]Lock object to update state. Events are not called when in update state.[/i])
3. You'll actually need different code for auto-playlists and regular playlists. You were using reorderAsync() correctly for a regular playlist, but auto-playlists use a more complex sorting method and reorderAsync() does not work for them. (I've also updated the docs to note this)
Auto Playlists use a [url=https://www.mediamonkey.com/docs/api/classes/QueryData.html]QueryData[/url] object to handle their sorting. You can check searchEditor.js and playlistHeader.js for how these QueryData objects are created and updated. Also, the property isAutoPlaylist can be read to check if a given playlist is an auto-playlist AND updated in case you want to switch its type.
4. It's better to use tracks_sort.setAutoSortAsync() to avoid blocking the main UI thread.
5. Because of all the async functions required, it'll be much easier to use an async function and use await each in order. (Otherwise, you'd be doing a lot of .then()s and lots of annoying indentation, a.k.a. "callback hell")
All together:
[code]
pl.createCopyAsync().then(async (pl_sort) => {
pl_sort.name = pl.name + ' (sorted)';
pl_sort.parent = pl;
// Do QueryData shenanigans if it's an auto-playlist
if (pl_sort.isAutoPlaylist) {
let queryData = await app.db.getQueryData({ category: 'empty' }); // Create a new QueryData object
queryData.loadFromString(pl_sort.queryData); // Transfer all its properties from the playlist (essentially creating a copy)
queryData.setSortOrders([ // Set its sort order: this time it's an array of objects insteada of a string
{
name: 'Rating',
ascending: false,
},
{
name: 'Bitrate',
ascending: false,
},
{
name: 'Length',
ascending: false,
},
]);
pl_sort.queryData = qd.saveToString(); // Update the playlist's queryData
// (Note: TPlaylist.queryData isn't actually stored as a string. Internally, it uses saveToString() as a getter and loadFromString() as a setter.)
pl_sort.commitAsync(); // Commit the playlist
pl_sort.notifyChanged('tracklist'); // to live update tracks -- is listened e.g. by viewHandlers.tracklistBase.onShow
}
// otherwise, use the tracklist sort method
else {
let tracks_sort = pl_sort.getTracklist();
await tracks_sort.whenLoaded();
await tracks_sort.setAutoSortAsync("Rating DESC; Bitrate DESC; Length DESC;");
await pl_sort.reorderAsync(tracks_sort);
pl_sort.commitAsync();
}
});
[/code]
[quote=telecore post_id=501088 time=1663612401 user_id=111910]
still having difficulties with async programming in js - sometimes bad code will lock up MM5 -
[/quote]
Last note is unrelated to playlists and tracklists: MM5 will often lock up and crash if you do something wrong with a native Delphi object or method (All of the objects we've been messing with here are native MM objects). When in doubt, check the documentation, and if the documentation isn't clear, don't hesitate to ask for us to clarify (and update the documentation to be more clear).