def _similar_tracks(self, artist_nameA, track_titleA, artist_nameB, track_titleB, match, source, locked=False): if not locked: self.acquire() #self._logger.debug(u"%s: [%s-%s]-[%s-%s] %2.2f" % (source, artist_nameA, # track_titleA, artist_nameB, track_titleB, match)) trackA = TrackFactory.by_key(TrackFactory.get_key(artist_nameA, track_titleA)) trackB = TrackFactory.by_key(TrackFactory.get_key(artist_nameB, track_titleB)) #if not trackA: self._logger.debug(u"similar_tracks[%s-%s]: not found" % # (artist_nameA, track_titleA)) #if not trackB: self._logger.debug(u"similar_tracks[%s-%s]: not found" % # (artist_nameB, track_titleB)) if trackA and trackB: relation = TrackRelationFactory.get(trackA, trackB) old_rating = relation.rating relation.rate(0.75 + 0.25 * match) self._logger.debug(u"%s [%s]-[%s] m(%2.2f) r(%2.2f|%2.2f)" % (source, trackA, trackB, match, relation.rating, old_rating)) if self._queue_lookup_results: if self._lastfm: self._lastfm.similar_tracks_low(self.similar_tracks, artist_nameB, track_titleB, self._thres_lastfm_lookup) if self._echonest: self._echonest.similar_tracks_low(self.similar_tracks, artist_nameB, track_titleB, self._thres_lastfm_lookup) if not locked: self.release()
def testArtistRatingPickle(self): Factories.clear() trackA= TrackFactory.get(self.artist, self.titleA) trackB = TrackFactory.get(self.artist, self.titleB) artist = trackA.artist trackA.played() trackB.skipped() rating = artist.rating i = 0 for r in RatingFactory.ratings(): i+=1 # track init rating: 1 # artist init rating: 2 # track play rating: 3 # artist play rating: 4 # track skip rating: 5 # artist skip rating: 6 self.assertEqual(i, 6) state = pickle.dumps(Factories.getstate()) Factories.clear() pickle.loads(state) i = 0 for r in RatingFactory.ratings(): i+=1 self.assertEqual(i, 6) track = TrackFactory.get(self.artist, self.titleA) artist = track.artist self.assertEqual(artist.rating, rating)
def testTrackClear(self): Factories.clear() track = TrackFactory.get(self.artist, self.title) track.test = self.track_test self.assertEqual(track.test, self.track_test) Factories.clear() track = TrackFactory.get(self.artist, self.title) self.assertEqual(track.test, None)
def testRatingClear(self): Factories.clear() track = TrackFactory.get(self.artist, self.title) track.played() track.skipped() Factories.clear() track = TrackFactory.get(self.artist, self.title) self.assertEqual(track.ratingref.playcount, 0) self.assertEqual(track.ratingref.skipcount, 0)
def testArtistRatingClear(self): Factories.clear() track = TrackFactory.get(self.artist, self.titleA) track.played() track.skipped() Factories.clear() track = TrackFactory.get(self.artist, self.titleA) artist = track.artist self.assertEqual(artist.rating, 0.5)
def __setstate__(self, dict): trackA = TrackFactory.by_key(dict['trackA_key']) del dict['trackA_key'] trackB = TrackFactory.by_key(dict['trackB_key']) del dict['trackB_key'] ratingref = RatingFactory.by_key(dict['ratingref_key']) del dict['ratingref_key'] self.__dict__ = dict self.trackA = trackA self.trackB = trackB self.ratingref = ratingref
def testTrackRef(self): Factories.clear() track = TrackFactory.get(self.artist, self.title) track.test = self.track_test track.artist.test = self.artist_test trackRef = TrackFactory.get(self.artist, self.title) self.assertEqual(trackRef.title, self.title) self.assertEqual(trackRef.test, self.track_test) self.assertEqual(trackRef.artist.name, self.artist) self.assertEqual(trackRef.artist.test, self.artist_test)
def testRatingRef(self): Factories.clear() track = TrackFactory.get(self.artist, self.title) track1 = TrackFactory.get(self.artist, self.title) track.played() track.skipped() self.assertEqual(track1.ratingref.stable, 0.5) track.played(0.5) track.skipped(0.5) self.assertEqual(track1.ratingref.playcount, 1) self.assertEqual(track1.ratingref.skipcount, 1) self.assertEqual(track1.ratingref.playfactor, 1.5) self.assertEqual(track1.ratingref.skipfactor, 1.5)
def testRatingRef2(self): Factories.clear() track = TrackFactory.get(self.artist, self.title) track2 = TrackFactory.get(self.artist2, self.title2) track.played() track.skipped() track.played(0.5) track.skipped(0.5) track.ratingref.test = self.rating_test track2.played() track2.skipped() track2.played(0.5) self.assertNotEqual(track.ratingref.test, track2.ratingref.test) track2.skipped(0.5) self.assertEqual(track.ratingref.test, track2.ratingref.test)
def testRatingPickle(self): Factories.clear() track = TrackFactory.get(self.artist, self.title) track.played() track.skipped() track.played(0.5) track.skipped(0.5) track.ratingref.test = self.rating_test state = pickle.dumps(Factories.getstate()) Factories.clear() pickle.loads(state) track = TrackFactory.get(self.artist, self.title) self.assertEqual(track.ratingref.playcount, 1) self.assertEqual(track.ratingref.skipcount, 1) self.assertEqual(track.ratingref.playfactor, 1.5) self.assertEqual(track.ratingref.skipfactor, 1.5) self.assertEqual(track.ratingref.test, self.rating_test)
def _reset(self, full=False): """ clear recent play history + unset last played track """ self.acquire() self._history.clear() self._lastplayed_track = None self._playing_track = None for track in TrackFactory.active_tracks(): track.ranking = 0.5 track.relation = 0.5 track.relation_old = 0.5 track.relation_cnt = 0 if full: for track in TrackFactory.active_tracks(): track.lastplayed = 0 track.laststarted = 0 track.lastqueued = 0 for artist in ArtistFactory.active_artists(): artist.lastplayed = 0 artist.laststarted = 0 artist.lastqueued = 0 self.release()
def testArtistRating(self): Factories.clear() track = TrackFactory.get(self.artist, self.titleA) track.ratingref.mix = 1.0 artist = track.artist artist.ratingref.mix = 1.0 artist.ratingfactor = 1.0 track.played() self.assertNotEqual(track.rating, 0.5) self.assertNotEqual(artist.rating, 0.5) track.skipped() self.assertEqual(track.rating, 0.5) self.assertEqual(artist.rating, 0.5)
def testTrackPickle(self): Factories.clear() track = TrackFactory.get(self.artist, self.title) track.test = self.track_test track.artist.test = self.artist_test state = pickle.dumps(Factories.getstate()) Factories.clear() pickle.loads(state) self.assertEqual(track.title, self.title) self.assertEqual(track.test, self.track_test) self.assertEqual(track.artist.name, self.artist) self.assertEqual(track.artist.test, self.artist_test)
def dump_stats(self): self._logger.info(u"%d artists, %d tracks %d files" % ( ArtistFactory.len(), TrackFactory.len(), FileFactory.len()))
def _update_ranking(self, locked=False): benchmark = Benchmark() if not locked: self.acquire() seed_track = None if self._lastplayed_track: seed_track = self._lastplayed_track elif self._playing_track: seed_track = self._playing_track if not self._relation_resetted: benchmark.start() self._relation_resetted = True for track in TrackFactory.active_tracks(): track.relation_old = track.relation track.relation_sum = 0.0 track.relation_cnt = 0 for artist in ArtistFactory.active_artists(): artist.relation_old = artist.relation artist.relation_sum = 0.0 artist.relation_cnt = 0 self._logger.info(u"update ranking: resetting took %s" % benchmark) has_active_tracks = False for track in TrackFactory.active_tracks(): has_active_tracks = True break benchmark.start() # new relation = old relation * decay # e.g. for decay = 0.5 (0.75) # decfacA = 0.5 * 0.5 = 0.25 (0.75 * 0.5 = 0.375) # decfacB = 1.0 - 0.5 = 0.5 (1.0 - 0.75 = 0.25) # relation_old=0.75 -> 0.25+0.5*0.75=0.625 (0.375+0.25*0.75=0.5625) decfacA = self._relation_decay * 0.5 decfacB = 1.0 - self._relation_decay for track in TrackFactory.active_tracks(): if (track.relation_old > 0.501) or (track.relation_old < 0.499): track.relation = (decfacA + decfacB * track.relation_old) else: track.relation = 0.5 for artist in ArtistFactory.active_artists(): if (artist.relation_old > 0.501) or (artist.relation_old < 0.499): artist.relation = (decfacA + decfacB * artist.relation_old) else: artist.relation = 0.5 self._logger.info(u"update ranking: set old relation + decay took %s" % benchmark) if has_active_tracks: self._ranking_updated = True self.seed(seed_track, True) benchmark.start() at = [] tt = [] for track in TrackFactory.active_tracks(): """ so we have: ranking [0-1.0] (old) rating [0-1.0] relation [0-1.0] random [0-1.0] and: factor min(track_lastplayed/started,artist_lastplayed/started) [0-1.0] ? moved to next_file() """ artist = track.artist r = random() # calculate new ranking if track.boost: self._logger.info(u"pre boost: %s" % track) track.relation = (track.relation + 99.0) / 100.0 self._logger.info(u"post boost: %s" % track) elif track.ban: track.relation = 0.0 # mix with artist relation if we don't have a track relation if track.relation_cnt == 0: if artist.relation_cnt > 0: track.relation = (0.75 * track.relation + 0.25 * artist.relation) top_ten(at, u'relation cnt = {} with {} now {} to {}'.format( artist.relation_cnt, scale_rating(artist.relation), scale_rating(track.relation), artist), track.relation) else: top_ten(tt, u'relation cnt = {} with {} to {}'.format( track.relation_cnt, scale_rating(track.relation), track), track.relation) track.ranking = ( self._factor_ranking['rating'] * track.get_rating() + self._factor_ranking['relation'] * track.relation + self._factor_ranking['random'] * r ) self._logger.info(u"update ranking: took %s" % benchmark) self._update_queued = False if not locked: self.release()
def reset_boost(self, locked=False): if not locked: self.acquire() for track in TrackFactory.active_tracks(): track.boost = False if not locked: self.release()
def next_file(self, locked=False): self._logger.info(u"next file: start") if not locked: self.acquire() if not self._ranking_updated: self.update_ranking() best_ranking = 0 best_tracks = [] tt = [] timestamp = now() # calculate last_*_timestamps (played / queued / started) track_lastplayed_timestamp = timestamp - self._thres_track_lastplayed track_laststarted_timestamp = timestamp - self._thres_track_laststarted track_lastqueued_timestamp = timestamp - self._thres_track_lastqueued artist_lastplayed_timestamp = timestamp - self._thres_artist_lastplayed artist_laststarted_timestamp = timestamp - self._thres_artist_laststarted artist_lastqueued_timestamp = timestamp - self._thres_artist_lastqueued has_active_tracks = False for track in TrackFactory.active_tracks(): has_active_tracks = True artist = track.artist factor = 1.0 if (track.lastplayed > track_lastplayed_timestamp): factor = min(factor, 1.0 - ((1.0 * track.lastplayed - track_lastplayed_timestamp) / self._thres_track_lastplayed)) if (artist.lastplayed > artist_lastplayed_timestamp): factor = min(factor, 1.0 - ((1.0 * artist.lastplayed - artist_lastplayed_timestamp) / self._thres_artist_lastplayed)) if (track.laststarted > track_laststarted_timestamp): factor = min(factor, 1.0 - ((1.0 * track.laststarted - track_laststarted_timestamp) / self._thres_track_laststarted)) if (artist.laststarted > artist_laststarted_timestamp): factor = min(factor, 1.0 - ((1.0 * artist.laststarted - artist_laststarted_timestamp) / self._thres_artist_laststarted)) if (track.lastqueued > track_lastqueued_timestamp): factor = min(factor, 1.0 - ((1.0 * track.lastqueued - track_lastqueued_timestamp) / self._thres_track_lastqueued)) if (artist.lastqueued > artist_lastqueued_timestamp): factor = min(factor, 1.0 - ((1.0 * artist.lastqueued - artist_lastqueued_timestamp) / self._thres_artist_lastqueued)) ranking = int(self._ranking_base * factor * track.ranking) if ranking > best_ranking: self._logger.debug("%2.2f (best=): %s" % (ranking, track)) best_ranking = ranking best_tracks = [track] elif ranking == best_ranking: self._logger.debug("%2.2f (best+): %s" % (ranking, track)) best_tracks.append(track) top_ten(tt, track, ranking) if not has_active_tracks: self._logger.error(u"No active tracks") if not locked: self.release() return None top_ten_dump(tt, self._logger.info, u"rank") self._logger.info("best tracks: %d" % (len(best_tracks))) best_track = choice(best_tracks) best_track.started() # pick the best file best_rating = 0.0 best_files = [] for file in best_track.files(): if not file.active: continue t = file.playcount / (1.0 + file.skipcount) if t > best_rating: best_rating = t best_files = [file] elif t == best_rating: best_files.append(file) if not locked: self.release() self._logger.info(u"next file: stop") return choice(best_files)
def __setstate__(self, dict): track = TrackFactory.by_key(dict['track_key']) del dict['track_key'] self.__dict__ = dict self.track = track FileFactory.register(self)
def __init__(self, path, artist_name, track_title): self.path = path self.track = TrackFactory.get(artist_name, track_title)