def find_track(self, query: str, limit: int = 5) -> tuple[bool, list[Track]]: self._logger.debug(f"Looking for Spotify track matching '{query}'") cache_persist = self._downloader.cache_persisted used_cache = False tracks = [] # cache response and use cache if available key = f"{self.code}api query {query}" query_result = self._downloader.retrieve_object(cache_persist, key) if query_result is not None: used_cache = True query_result = query_result.get_value() else: query_status, query_result = self._client.query(query, limit) self._downloader.store_object(cache_persist, key, query_result) # stop if there are results if query_result and query_result["tracks"].get("items"): for song_hit in query_result["tracks"].get("items"): track = Track( origin_code=self.code, track_id=song_hit.get("id"), title=song_hit.get("name"), artists=[ artist.get("name") for artist in song_hit.get("artists") ], info=song_hit, query_strings=[], ) tracks.append(track) return used_cache, tracks
def get_playlist_tracks(self, limit: Optional[int] = None) -> List[Track]: # build dates for urls current_time = datetime.now(tz=self._time_zone) current_day = current_time.date() url = self.build_url( "triplej", date_from=current_day - timedelta(days=8), date_to=current_day - timedelta(days=1), ) # download track list tracks_data = self._downloader.download_json( self._downloader.cache_persisted, url) result = [] for index, item in enumerate(tracks_data["items"]): title = item["title"] track_id = item["arid"] original_artists = item["artists"] # get primary artist and featured artists artist = "" featuring = "" for raw_artist in original_artists: if raw_artist["type"] == "primary": artist = f'{artist}, {raw_artist["name"]}' elif raw_artist["type"] == "featured": featuring = f'{artist}, {raw_artist["name"]}' else: raise Exception( f"Unrecognised artist {raw_artist['type']}, {artist}, {raw_artist['name']}." ) artists = [artist.strip(", ")] for i in featuring.split(", "): featured = i.strip(", ") if featured and featured not in artists: artists.append(featured) # build track result.append( Track.create( self.code, track_id, title, artists, { "artists": artists, "source_id": track_id, "source_order": index + 1, "original_track": title, "original_artists": original_artists, }, )) self._logger.info(f"Retrieved {self.title} with {len(result)} tracks.") if limit is not None and 0 < limit < len(result): result = result[:limit] return result
def is_match(self, query_track: Track, result_track: Track) -> bool: query_new = Track.create( query_track.origin_code, query_track.track_id, query_track.title, query_track.artists, query_track.info, ) query_title = slugify(query_new.title) query_artist = slugify(query_new.artists[0]) query_featured = slugify(", ".join(query_new.artists[1:])) result_new = Track.create( result_track.origin_code, result_track.track_id, result_track.title, result_track.artists, result_track.info, ) result_title = slugify(result_new.title) result_artist = slugify(result_new.artists[0]) result_featured = slugify(", ".join(result_new.artists[1:])) match_title = query_title in result_title # match_artist = query_artist in result_artist match_featuring = query_featured in result_featured match_featuring2 = result_featured in query_featured equal_title = query_title == result_title equal_artist = query_artist == result_artist # equal_featuring = query_featured == result_featured has_featuring = query_featured and result_featured # these are the variations that are valid if equal_title and equal_artist and match_featuring and has_featuring: return True if match_title and equal_artist and match_featuring and not has_featuring: return True if (equal_title and equal_artist and has_featuring and (match_featuring or match_featuring2)): return True return False
def get_playlist_tracks(self, playlist_id: str, limit: Optional[int] = None) -> List[Track]: self._logger.info(f"Retrieving tracks for Spotify playlist.") status, content = self._client.get_playlist_tracks(playlist_id, limit) result = [] for item in content.get("items", []): track = item.get("track", {}) result.append( Track( origin_code=self.code, track_id=track.get("id"), title=track.get("name"), artists=[a.get("name") for a in track.get("artists")], info=track, query_strings=[], )) return result
def get_playlist_tracks(self, limit: Optional[int] = None) -> List[Track]: # get content url = self.build_url(api_key=self._api_key) # download track list tracks_data = self._downloader.download_json(self._downloader.cache_temp, url) result = [] for index, item in enumerate(tracks_data["tracks"]["track"]): result.append( Track.create( self.code, item.get("url"), item["name"], [item["artist"]["name"]], item, ) ) self._logger.info(f"Retrieved {self.title} with {len(result)} tracks.") if limit is not None and 0 < limit < len(result): result = result[:limit] return result
def get_playlist_tracks(self, limit: Optional[int] = None) -> List[Track]: # build dates for urls current_time = datetime.now(tz=self._time_zone) date_from = current_time - timedelta(days=7) date_to = current_time # download 4zzz programs programs = self._downloader.download_json(self._downloader.cache_temp, self._url) tracks = {} # programs for program in programs: if program.get("archived"): continue program_name = program["name"] # episodes episodes_url = f"{program['programRestUrl']}/episodes" episodes = self._downloader.download_json( self._downloader.cache_temp, episodes_url) for episode in episodes or []: episode_start = datetime.strptime( episode["start"], "%Y-%m-%d %H:%M:%S").replace(tzinfo=self._time_zone) episode_end = datetime.strptime( episode["end"], "%Y-%m-%d %H:%M:%S").replace(tzinfo=self._time_zone) if (date_from > episode_start or date_to < episode_start or date_from > episode_end or date_to < episode_end): continue # playlist tracks playlist_url = f"{episode['episodeRestUrl']}/playlists" playlist_raw = self._downloader.download_json( self._downloader.cache_persisted, playlist_url) for track in playlist_raw or []: track_type = track["type"] track_artist = track["artist"] track_title = track["title"] track_track = track["track"] if track_type != "track": raise Exception( f"Track type is expected to be 'track', but is {track_type}." ) if track_title != track_track: raise Exception( f"Title and track are expected to match, but do not: '{track_title}' != '{track_track}'" ) track_key = "-".join([ slugify(track_artist, delim="-", ascii=True).decode("utf-8"), slugify(track_track, delim="-", ascii=True).decode("utf-8"), ]) item = { "type": track_type, "id": track["id"], "artist": track_artist, "title": track_title, "track": track_track, "release": track["release"], "time": track["time"], "notes": track["notes"], "twitter": track["twitterHandle"], "is_australian": track["contentDescriptors"]["isAustralian"], "is_local": track["contentDescriptors"]["isLocal"], "is_female": track["contentDescriptors"]["isFemale"], "is_indigenous": track["contentDescriptors"]["isIndigenous"], "is_new": track["contentDescriptors"]["isNew"], "wikipedia": track["wikipedia"], "image": track["image"], "video": track["video"], "url": track["url"], "approximate_time": track["approximateTime"], "program_name": program_name, "episode_start": episode_start, } if track_key in tracks: tracks[track_key].append(item) else: tracks[track_key] = [item] # find the top 50 most played tracks most_played_tracks = sorted([(len(v), k, v) for k, v in tracks.items() if len(v) > 1], reverse=True)[:100] # build the source playlist tracks result = [] for index, most_played_track in enumerate(most_played_tracks): play_count = most_played_track[0] track_id = most_played_track[1] track_info = most_played_track[2] info = self._build_info(track_id, play_count, track_info) title = info.get("title") artists = [info.get("artist")] result.append( Track.create(self.code, track_id, title, artists, info)) self._logger.info(f"Retrieved {self.title} with {len(result)} tracks.") if limit is not None and 0 < limit < len(result): result = result[:limit] return result