How to alternate a variable number of playlists? #218
-
First of all of thank you for this fantastic project! I had been using SmarterPlaylists for many years, and also wrote my own python library for complex playlists involving everynoise. I am now migrating all my SmarterPlaylists programs to Goofy. I will keep using my python libraries though, due to the 5 mins limit on google app script execution, and mine can take more than 1 hour. I am not too familiar with Javascript, and could appreciate if someone could help me. One of the functions I wrote is to alternate tracks from 2 playlists, while performing dedup, shuffle, artist separation, and an optional max track counts. The important thing is to keep the alternation, even with shuffle and separation. I tried to make the algorithm efficient by retrieving only the number of tracks needed. function alternate2Playlists(name, id, playlist1, playlist2, count = 0) {
Logger.log("Creating combination playlist: " + name);
// Get tracks from the source playlists, with shuffling
// Limit the number of tracks returned to the max numbers requested / number of source playlists
// If this is 0 (default value), all tracks will be included
let tracks1 = Source.getTracks([{ id: playlist1, count: count/2, inRow: false }]);
let tracks2 = Source.getTracks([{ id: playlist2, count: count/2, inRow: false }]);
// Separate artists, with 3 slots, no shuffling necessary
Order.separateArtists(tracks1, 3, false);
Order.separateArtists(tracks2, 3, false);
// Mix-in the playlists, alternating 1 track from each
let tracks = Combiner.alternate( 'max', tracks1, tracks2 );
// Remove duplicates, both by id and name
Filter.dedupTracks(tracks);
// Save playlist
Playlist.saveWithReplace({
id: id,
name: name,
tracks: tracks,
});
} I want to do the same for 3 or more source playlists. One way I can do is by writing separate functions to deal with 3, 4, ..., playlists. Would there be a away though to use a single function with a dynamic source playlist array? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
What algorithm do you use? With my experience, 99% tasks can be resolve about 6 minutes. Including new releases, EveryNoise and many other.
In order to alternate more than 2 playlists just use mixinMulti. function debug() {
let trackGroups = [
'37i9dQZF1DX26DKvjp0s9M',
'37i9dQZF1DX9LbdoYID5v7',
'37i9dQZF1DX873GaRGUmPl'
].map(id => {
let plsTracks = Source.getPlaylistTracks('', id)
// your filters for plsTracks here
return plsTracks
})
let result = Combiner.mixinMulti({
source: trackGroups,
inRow: [2, 1, 1], // count elements = count playlist ids
toLimitOn: true
});
Playlist.saveWithReplace({
name: 'Your name',
tracks: result
})
} If you like the project, you can recommend it to friends. |
Beta Was this translation helpful? Give feedback.
-
Here is a python example of a function to filter tracks based on acoustic properties (for chilled Hip-Hop and RnB), and calculate a custom popularity score, for an album : def get_albumtracks_uri_filtered(spotify, album, filter = 'none'):
"""
Retrieve all tracks for a specific album
Args:
playlist: album id in spotify URI format
Returns:
list of all the album tracks uri
"""
# retrieve all tracks for a specific album
all_tracks = [] #create empty list
#
results = {}
results = spotify.album(album)
#
# get main artist popularity
uri_artist = results['artists'][0]['uri']
artist_popularity = spotify.artist(uri_artist)['popularity']
# get album popularity
album_popularity = results['popularity']
#
tracks = results['tracks'] # extract the tracks
track_items = tracks['items'] # extract the tracks info
# spotify returns results in a pager; get next results if more than 100 returned.
while tracks["next"]:
tracks = spotify.next(tracks) # get the next tracks
track_items.extend(tracks["items"]) # extract and append the items list
# process the list of track items, to extract all the tracks uri
for item in track_items:
uri = item['uri']
try:
# obtain all track details from spotify
track_details = spotify.track(uri)
# calculate combined popularity score
track_popularity = track_details['popularity']
popularity = int(float((artist_popularity + 2*album_popularity + 3*track_popularity)/6))
if filter == 'chillhop':
# audio features for filtering
audio_features = spotify.audio_features(uri)
if audio_features != [None]:
track_energy = audio_features[0]['energy']
track_instrumentalness = audio_features[0]['instrumentalness']
track_speechiness = audio_features[0]['speechiness']
# only keep track with a minimum energy level, likelihood of not being instrumental, and not too much spoken like vocals
if track_energy > 0.3 and track_energy < 0.7 and track_instrumentalness < 0.5 and track_speechiness < 0.15:
# add track to our track list
tuple = (popularity,uri)
all_tracks += [tuple]
#print(popularity, '(', artist_popularity,album_popularity,track_popularity,') (e=', int(float(100*track_energy)), ') (i=', int(float(100*track_instrumentalness)),') (s=', int(float(100*track_speechiness)), '), ',track_details['artists'][0]['name'], ',', track_details['name'])
else:
# add track to our track list
tuple = (popularity,uri)
all_tracks += [tuple]
#print(popularity, '(', artist_popularity,album_popularity,track_popularity,') ',track_details['artists'][0]['name'], ',', track_details['name'])
except:
print('ERROR:', uri)
return all_tracks |
Beta Was this translation helpful? Give feedback.
-
The reason I use a custom popularity is for new releases, the track popularity is yet to be defined. So taking into account the past artist popularity can be useful. But you are right, using the album popularity might be unecessary. I wrote a similar parsing of the everynoise new release pages by using Beautiful Soup in Python. I will try to port my release trackers to Goofy though, and see how I can get it to perform within Google Apps Script limit. Anyway, thanks to you, I was able to finish my alternating playlists function in an nice and elegant way :) Here it is: function alternatePlaylists(name, id, playlists, count = 0) {
Logger.log("Creating combination playlist: " + name);
// Number of tracks to retrieve from each playlist
// If this is 0, all tracks will be retrieved
let plsCount = count / (playlists.length);
// Build up an array of track lists, corresponding to each playlist id
let trackGroups = playlists.map(id => {
// For efficiency, tracks are retrieved randomly, and limited to the number needed
let plsTracks = Source.getTracks([{ id: id, count: plsCount, inRow: false }])
// Separate artists, with 3 slots, no shuffling necessary
Order.separateArtists(plsTracks, 3, false)
return plsTracks
});
// Build up a simple array to define an equal mixing ratio for each playlist,
// of one track at a time from each track list: [1,1,1,...]
let ratio = playlists.map( id => {return 1} )
// Alternate the track lists, 1 track at a time
let tracks = Combiner.mixinMulti({
source: trackGroups,
inRow: ratio,
// toLimitOn: true. // stop processing as soon as the ratio cannot be adhered to
});
// Remove duplicates, both by id and name
Filter.dedupTracks(tracks);
// Save playlist
Playlist.saveWithReplace({
id: id,
name: name,
tracks: tracks,
});
} Example usage: alternatePlaylists('Test', '18VeLW3Zjy0AcJAd5WD0bT', ['49dOZG0Sys092mt1YtRmot', '3nG6oYuH9mScge1RFqrFap', '1sWIaLkLdX2gf6GYJetcO5'], 21); |
Beta Was this translation helpful? Give feedback.
What algorithm do you use? With my experience, 99% tasks can be resolve about 6 minutes. Including new releases, EveryNoise and many other.
In order to alternate more than 2 playlists just use mixinMulti.