+
+
+class Tracklist:
+ def __init__(self, zone_settings, zone_ids, recent_tracks_id=None, filter_kwargs={}, k=30):
+ self.zone_settings = zone_settings
+ self.zone_ids = zone_ids
+ self.playlist = []
+ self.recent_tracks_id = recent_tracks_id or []
+ self.filter_kwargs = filter_kwargs
+ self.k = k
+
+ def append(self, track):
+ # track or jingle
+ self.playlist.append(track)
+
+ def pop(self):
+ return self.playlist.pop() if self.playlist else None
+
+ def get_recent_track_ids(self):
+ return self.recent_tracks_id + [x.id for x in self.playlist if isinstance(x, Track)]
+
+ def get_duration(self):
+ return sum([x.duration for x in self.playlist], datetime.timedelta(seconds=0))
+
+ def get_random_tracks(self, k=30):
+ weights = self.zone_settings.weights
+
+ while True:
+ # pick tracks from db
+ tracks = Track.objects.filter(
+ nonstop_zones__in=self.zone_ids,
+ duration__isnull=False,
+ **self.filter_kwargs).exclude(
+ id__in=self.get_recent_track_ids()
+ ).order_by('?')[:k*10]
+ if len(tracks) == 0:
+ self.recent_tracks_id = self.recent_tracks_id[:len(self.recent_tracks_id) // 2]
+ continue
+
+ def compute_weight(track):
+ weight = 0
+ for weight_key, weight_value in weights.items():
+ if track.match_criteria(weight_key):
+ weight += weight_value
+ if weight < 0:
+ weight = 1 + (weight / 20)
+ else:
+ weight = 1 + (weight / 2)
+ return weight
+
+ track_weights = [compute_weight(x) for x in tracks]
+ tracks = random.choices(tracks, weights=track_weights, k=k)
+
+ seen = set()
+ for track in tracks:
+ if track in seen:
+ continue
+ yield track
+ seen.add(track)