Couple of different ways you can do this. There is an optimal solution that gets you as close to your target as possible but you want the playlist to be random and everything to get a play eventually so something like this should do the job...
from random import randint, shuffle
# create some tracks between 2-4 minutes for testing
tracks = { "Track {0}".format(i): "{0}:{1:02d}".format(randint(2, 3), randint(0, 59)) for i in range(20) }
print(tracks)
# covert to a list of elements with the time in seconds
def min_to_sec(x):
m, s = x.split(":")
return int(m) * 60 + int(s)
items = [ (k, min_to_sec(v)) for k, v in tracks.items() ]
# randomise
shuffle(items)
# a function that sums the lengths from an index position till
# it exceeds the max.
def select_tracks(items, index, max_seconds):
total = 0
selected = []
for name, length in items[index:]:
if total + length > max_seconds:
break
total += length
selected.append(name)
return total, selected
# run at each start position
results = [ select_tracks(items, i, 30*60) for i in range(len(items)) ]
# if we sort and select the last that is the best for this randomised order
results.sort()
playlist = results[-1]
print("\nPlaylist: {0} ({1} seconds)".format(", ".join(playlist[1]), playlist[0]))
Output:
{'Track 0': '2:51', 'Track 1': '2:25', 'Track 2': '3:21', 'Track 3': '3:03', 'Track 4': '3:22', 'Track 5': '3:03', 'Track 6': '3:32', 'Track 7': '3:58', 'Track 8': '3:40', 'Track 9': '2:16', 'Track 10': '3:32', 'Track 11': '3:52', 'Track 12': '3:03', 'Track 13': '2:37', 'Track 14': '2:45', 'Track 15': '2:57', 'Track 16': '3:19', 'Track 17': '3:06', 'Track 18': '2:15', 'Track 19': '3:05'}
Playlist: Track 5, Track 14, Track 18, Track 9, Track 8, Track 17, Track 10, Track 19, Track 2, Track 0 (1794 seconds)