Exemplo n.º 1
0
    def run(self, args, config):
        from ytmusicapi.ytmusic import YTMusic

        filepath = input(
            "Enter the path where you want to save auth.json [default=current dir]: "
        )
        if not filepath:
            filepath = os.getcwd()
        path = Path(filepath + "/auth.json")
        print('Using "' + str(path) + '"')
        if path.exists():
            print("File already exists!")
            return 1
        print(
            "Open Youtube Music, open developer tools (F12), go to Network tab,"
        )
        print(
            'right click on a POST request and choose "Copy request headers".')
        print("Then paste (CTRL+SHIFT+V) them here and press CTRL+D.")
        try:
            print(YTMusic.setup(filepath=str(path)))
        except Exception:
            logger.exception("YTMusic setup failed")
            return 1
        print("Authentication JSON data saved to {}".format(str(path)))
        print("")
        print("Update your mopidy.conf to reflect the new auth file:")
        print("   [ytmusic]")
        print("   enabled=true")
        print("   auth_json=" + str(path))
        return 0
Exemplo n.º 2
0
 def _get_auto_playlists(self):
     try:
         logger.debug("YTMusic loading auto playlists")
         response = self.api._send_request("browse", {})
         tab = nav(response, SINGLE_COLUMN_TAB)
         browse = parse_auto_playlists(nav(tab, SECTION_LIST))
         if "continuations" in tab["sectionListRenderer"]:
             request_func = lambda additionalParams: self.api._send_request(
                 "browse", {}, additionalParams)
             parse_func = lambda contents: parse_auto_playlists(contents)
             browse.extend(
                 get_continuations(
                     tab["sectionListRenderer"],
                     "sectionListContinuation",
                     100,
                     request_func,
                     parse_func,
                 ))
         # Delete empty sections
         for i in range(len(browse) - 1, 0, -1):
             if len(browse[i]["items"]) == 0:
                 browse.pop(i)
         logger.info("YTMusic loaded %d auto playlists sections",
                     len(browse))
         self.library.ytbrowse = browse
     except Exception:
         logger.exception("YTMusic failed to load auto playlists")
     return None
Exemplo n.º 3
0
 def delete(self, uri):
     logger.debug('YTMusic deleting playlist "%s"', uri)
     bId = parse_uri(uri)
     try:
         self.backend.api.delete_playlist(bId)
         return True
     except Exception:
         logger.exception("YTMusic failed to delete playlist")
         return False
Exemplo n.º 4
0
 def delete(self, uri):
     logger.info("YTMusic deleting playlist \"%s\"", uri)
     id_, upload = parse_uri(uri)
     try:
         API.delete_playlist(id_)
         return True
     except Exception:
         logger.exception("YTMusic failed to delete playlist")
         return False
Exemplo n.º 5
0
 def lookup(self, uri):
     id_, upload = parse_uri(uri)
     logger.info("YTMusic looking up playlist \"%s\"", id_)
     try:
         pls = API.get_playlist(id_, limit=100)
     except Exception:
         logger.exception("YTMusic playlist lookup failed")
         pls = None
     if pls:
         tracks = []
         if "tracks" in pls:
             for track in pls["tracks"]:
                 duration = track["duration"].split(":")
                 artists = [Artist(
                     uri=f"ytm:artist?id={a['id']}&upload=false",
                     name=a["name"],
                     sortname=a["name"],
                     musicbrainz_id="",
                 ) for a in track["artists"]]
                 if track["album"]:
                     album = Album(
                         uri=f"ytm:album?id={track['album']['id']}&upload=false",
                         name=track["album"]["name"],
                         artists=artists,
                         num_tracks=None,
                         num_discs=None,
                         date="1999",
                         musicbrainz_id="",
                     )
                 else:
                     album = None
                 tracks.append(Track(
                     uri=f"ytm:video?id={track['videoId']}",
                     name=track["title"],
                     artists=artists,
                     album=album,
                     composers=[],
                     performers=[],
                     genre="",
                     track_no=None,
                     disc_no=None,
                     date="1999",
                     length=(int(duration[0]) * 60 * 1000) + (int(duration[1]) * 1000),
                     bitrate=0,
                     comment="",
                     musicbrainz_id="",
                     last_modified=None,
                 ))
         for track in tracks:
             tid, tupload = parse_uri(track.uri)
             TRACKS[tid] = track
         return Playlist(
             uri=f"ytm:playlist?id={pls['id']}",
             name=pls["title"],
             tracks=tracks,
             last_modified=None,
         )
Exemplo n.º 6
0
 def get_items(self, uri):
     bId = parse_uri(uri)
     logger.debug('YTMusic getting playlist items for "%s"', bId)
     try:
         pls = self.backend.api.get_playlist(
             bId, limit=self.backend.playlist_item_limit)
     except Exception:
         logger.exception("YTMusic failed getting playlist items")
         pls = None
     if pls:
         tracks = self.backend.library.playlistToTracks(pls)
         return [Ref.track(uri=t.uri, name=t.name) for t in tracks]
     return None
Exemplo n.º 7
0
 def as_list(self):
     logger.info("YTMusic getting user playlists")
     refs = []
     try:
         playlists = API.get_library_playlists(limit=100)
     except Exception:
         logger.exception("YTMusic failed getting a list of playlists")
         playlists = []
     for pls in playlists:
         refs.append(Ref.playlist(
             uri=f"ytm:playlist?id={pls['playlistId']}", name=pls["title"],
         ))
     return refs
Exemplo n.º 8
0
 def get_items(self, uri):
     id_, upload = parse_uri(uri)
     logger.info("YTMusic getting playlist items for \"%s\"", id_)
     try:
         pls = API.get_playlist(id_, limit=100)
     except Exception:
         logger.exception("YTMusic failed getting playlist items")
         pls = None
     if pls:
         refs = []
         if "tracks" in pls:
             for track in pls["tracks"]:
                 refs.append(Ref.track(uri=f"ytm:video?id={track['videoId']}", name=track["title"]))
                 duration = track["duration"].split(":")
                 artists = [Artist(
                     uri=f"ytm:artist?id={a['id']}&upload=false",
                     name=a["name"],
                     sortname=a["name"],
                     musicbrainz_id="",
                 ) for a in track["artists"]]
                 if track["album"]:
                     album = Album(
                         uri=f"ytm:album?id={track['album']['id']}&upload=false",
                         name=track["album"]["name"],
                         artists=artists,
                         num_tracks=None,
                         num_discs=None,
                         date="1999",
                         musicbrainz_id="",
                     )
                 else:
                     album = None
                 TRACKS[track["videoId"]] = Track(
                     uri=f"ytm:video?id={track['videoId']}",
                     name=track["title"],
                     artists=artists,
                     album=album,
                     composers=[],
                     performers=[],
                     genre="",
                     track_no=None,
                     disc_no=None,
                     date="1999",
                     length=(int(duration[0]) * 60 * 1000) + (int(duration[1]) * 1000),
                     bitrate=0,
                     comment="",
                     musicbrainz_id="",
                     last_modified=None,
                 )
         return refs
     return None
Exemplo n.º 9
0
 def save(self, playlist):
     bId = parse_uri(playlist.uri)
     logger.debug('YTMusic saving playlist "%s" "%s"', playlist.name, bId)
     try:
         pls = self.backend.api.get_playlist(
             bId, limit=self.backend.playlist_item_limit)
     except Exception:
         logger.exception("YTMusic saving playlist failed")
         return None
     oldIds = set([t["videoId"] for t in pls["tracks"]])
     newIds = set([parse_uri(p.uri)[0] for p in playlist.tracks])
     common = oldIds & newIds
     remove = oldIds ^ common
     add = newIds ^ common
     if len(remove):
         logger.debug('YTMusic removing items "%s" from playlist', remove)
         try:
             videos = [t for t in pls["tracks"] if t["videoId"] in remove]
             self.backend.api.remove_playlist_items(bId, videos)
         except Exception:
             logger.exception("YTMusic failed removing items from playlist")
     if len(add):
         logger.debug('YTMusic adding items "%s" to playlist', add)
         try:
             self.backend.api.add_playlist_items(bId, list(add))
         except Exception:
             logger.exception("YTMusic failed adding items to playlist")
     if pls["title"] != playlist.name:
         logger.debug('Renaming playlist to "%s"', playlist.name)
         try:
             self.backend.api.edit_playlist(bId, title=playlist.name)
         except Exception:
             logger.exception("YTMusic failed renaming playlist")
     return playlist
Exemplo n.º 10
0
 def save(self, playlist):
     id_, upload = parse_uri(playlist.uri)
     logger.info("YTMusic saving playlist \"%s\" \"%s\"", playlist.name,
                 id_)
     try:
         pls = API.get_playlist(id_, limit=100)
     except Exception:
         logger.exception("YTMusic saving playlist failed")
         return None
     oldIds = set([t["videoId"] for t in pls["tracks"]])
     newIds = set([parse_uri(p.uri)[0] for p in playlist.tracks])
     common = oldIds & newIds
     remove = oldIds ^ common
     add = newIds ^ common
     if len(remove):
         logger.debug("YTMusic removing items \"%s\" from playlist", remove)
         try:
             videos = [t for t in pls["tracks"] if t["videoId"] in remove]
             API.remove_playlist_items(id_, videos)
         except Exception:
             logger.exception("YTMusic failed removing items from playlist")
     if len(add):
         logger.debug("YTMusic adding items \"%s\" to playlist", add)
         try:
             API.add_playlist_items(id_, list(add))
         except Exception:
             logger.exception("YTMusic failed adding items to playlist")
     if pls["title"] != playlist.name:
         logger.debug("Renaming playlist to \"%s\"", playlist.name)
         try:
             API.edit_playlist(id_, title=playlist.name)
         except Exception:
             logger.exception("YTMusic failed renaming playlist")
     return playlist
Exemplo n.º 11
0
 def search(self, query=None, uris=None, exact=False):
     logger.info("YTMusic searching for %s", query)
     tracks = []
     if "any" in query:
         try:
             res = API.search(" ".join(query["any"]), filter=None)
             tracks.extend(parseSearch(res))
             if (exact):
                 for track in tracks:
                     for q in query["any"]:
                         q = q.casefold()
                         if q != track.name.casefold():
                             tracks.remove(track)
                         if q == track.album.name.casefold():
                             tracks.remove(track)
                         for artist in track.artists:
                             if q == artist.name.casefold():
                                 tracks.remove(track)
         except Exception:
             logger.exception("YTMusic search failed for query \"%s\"", " ".join(query["any"]))
     elif "track_name" in query:
         try:
             res = API.search(" ".join(query["track_name"]), filter="songs")
             if exact:
                 tracks.extend(parseSearch(res, "track", query["track_name"]))
             else:
                 tracks.extend(parseSearch(res))
         except Exception:
             logger.exception("YTMusic search failed for query \"title\"=\"%s\"", " ".join(query["track_name"]))
     elif "albumartist" in query or "artist" in query:
         q1 = ("albumartist" in query and query["albumartist"]) or []
         q2 = ("artist" in query and query["artist"]) or []
         try:
             res = API.search(" ".join(q1 + q2), filter="artists")
             if exact:
                 tracks.extend(parseSearch(res, "artist", q1 + q2))
             else:
                 tracks.extend(parseSearch(res))
         except Exception:
             logger.exception("YTMusic search failed for query \"artist\"=\"%s\"", " ".join(q1 + q2))
     elif "album" in query:
         try:
             res = API.search(" ".join(query["album"]), filter="albums")
             if exact:
                 tracks.extend(parseSearch(res, "album", query["album"]))
             else:
                 tracks.extend(parseSearch(res))
         except Exception:
             logger.exception("YTMusic search failed for query \"album\"=\"%s\"", " ".join(query["album"]))
     else:
         logger.info("YTMusic skipping search, unsupported field types \"%s\"", " ".join(query.keys()))
         return None
     logger.info("YTMusic search returned %d results", len(tracks))
     return SearchResult(
         uri="",
         tracks=tracks,
         artists=None,
         albums=None,
     )
Exemplo n.º 12
0
 def lookup(self, uri):
     bId, upload = parse_uri(uri)
     if upload:
         if uri.startswith("ytmusic:album:"):
             try:
                 res = self.backend.api.get_library_upload_album(bId)
                 tracks = self.albumToTracks(res, bId)
                 return tracks
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for album "%s"', bId)
         elif uri.startswith("ytmusic:artist:"):
             try:
                 res = self.backend.api.get_library_upload_artist(bId)
                 tracks = self.artistToTracks(res)
                 return tracks
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for artist "%s"', bId)
     else:
         if uri.startswith("ytmusic:album:"):
             try:
                 res = self.backend.api.get_album(bId)
                 tracks = self.albumToTracks(res, bId)
                 return tracks
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for album "%s"', bId)
         elif uri.startswith("ytmusic:artist:"):
             try:
                 res = self.backend.api.get_artist(bId)
                 tracks = self.artistToTracks(res)
                 return tracks
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for artist "%s"', bId)
         elif uri.startswith("ytmusic:playlist:"):
             try:
                 res = self.backend.api.get_playlist(
                     bId, limit=self.backend.playlist_item_limit)
                 tracks = self.playlistToTracks(res)
                 return tracks
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for playlist "%s"', bId)
     if (bId) in self.TRACKS:
         return [self.TRACKS[bId]]
     return []
Exemplo n.º 13
0
 def lookup(self, uri):
     bId = parse_uri(uri)
     logger.debug('YTMusic looking up playlist "%s"', bId)
     try:
         pls = self.backend.api.get_playlist(
             bId, limit=self.backend.playlist_item_limit)
     except Exception:
         logger.exception("YTMusic playlist lookup failed")
         pls = None
     if pls:
         tracks = self.backend.library.playlistToTracks(pls)
         return Playlist(
             uri=f"ytmusic:playlist:{pls['id']}",
             name=pls["title"],
             tracks=tracks,
             last_modified=None,
         )
Exemplo n.º 14
0
 def create(self, name):
     logger.info("YTMusic creating playlist \"%s\"", name)
     try:
         id_ = API.create_playlist(name, "")
     except Exception:
         logger.exception("YTMusic playlist creation failed")
         id_ = None
     if id_:
         uri = f"ytm:playlist?id={id_}"
         logger.info("YTMusic created playlist \"%s\"", uri)
         return Playlist(
             uri=uri,
             name=name,
             tracks=[],
             last_modified=None,
         )
     return None
Exemplo n.º 15
0
 def create(self, name):
     logger.debug('YTMusic creating playlist "%s"', name)
     try:
         bId = self.backend.api.create_playlist(name, "")
     except Exception:
         logger.exception("YTMusic playlist creation failed")
         bId = None
     if bId:
         uri = f"ytmusic:playlist:{bId}"
         logger.debug('YTMusic created playlist "%s"', uri)
         return Playlist(
             uri=uri,
             name=name,
             tracks=[],
             last_modified=None,
         )
     return None
Exemplo n.º 16
0
 def _get_youtube_player(self):
     # Refresh our js player URL so YDL can decode the signature correctly.
     try:
         response = requests.get(
             "https://music.youtube.com",
             headers=self.api.headers,
             proxies=self.api.proxies,
         )
         m = re.search(r'jsUrl"\s*:\s*"([^"]+)"', response.text)
         if m:
             url = m.group(1)
             logger.debug("YTMusic updated player URL to %s", url)
             return url
         else:
             logger.error("YTMusic unable to extract player URL.")
             return None
     except Exception:
         logger.exception("YTMusic failed to refresh player URL.")
     return None
Exemplo n.º 17
0
    def run(self, args, config):
        from ytmusicapi.ytmusic import YTMusic

        path = config["ytmusic"]["auth_json"]
        if not path:
            logger.error("auth_json path not defined in config")
            return 1
        print('Updating credentials in  "' + str(path) + '"')
        print(
            "Open Youtube Music, open developer tools (F12), go to Network tab,"
        )
        print(
            'right click on a POST request and choose "Copy request headers".')
        print("Then paste (CTRL+SHIFT+V) them here and press CTRL+D.")
        try:
            print(YTMusic.setup(filepath=str(path)))
        except Exception:
            logger.exception("YTMusic setup failed")
            return 1
        print("Authentication JSON data saved to {}".format(str(path)))
        return 0
Exemplo n.º 18
0
    def run(self, args, config):
        from ytmusicapi.ytmusic import YTMusic

        filepath = input("Enter the path where you want to save auth.json:")
        path = Path(filepath)
        if (path.exists()):
            print("File already exists!")
            return 1
        print(
            "Open Youtube Music, open developer tools (F12), go to Network tab,"
        )
        print(
            "right click on a POST request and choose \"Copy request headers\"."
        )
        print("Then paste (CTRL+SHIFT+V) them here and press CTRL+D.")
        try:
            print(YTMusic.setup(filepath=str(path)))
        except Exception:
            logger.exception("YTMusic setup failed")
            return 1
        print("auth.json saved to {}".format(str(path)))
        return 0
Exemplo n.º 19
0
 def get_distinct(self, field, query=None):
     ret = set()
     if field == "artist" or field == "albumartist":
         # try:
         #     uploads = self.backend.api.get_library_upload_artists(limit=100)
         # except Exception:
         #     logger.exception("YTMusic failed getting uploaded artists")
         #     uploads = []
         #     pass
         try:
             library = self.backend.api.get_library_artists(
                 limit=self.backend.playlist_item_limit)
         except Exception:
             logger.exception("YTMusic failed getting artists from library")
             library = []
             pass
         # for a in uploads:
         #     ret.add(a["artist"])
         for a in library:
             ret.add(a["artist"])
     # elif field == "album":
     #     try:
     #         uploads = self.backend.api.get_library_upload_albums(limit=self.backend.playlist_item_limit)
     #     except Exception:
     #         logger.exception("YTMusic failed getting uploaded albums")
     #         uploads = []
     #         pass
     #     try:
     #         library = self.backend.api.get_library_albums(limit=self.backend.playlist_item_limit)
     #     except Exception:
     #         logger.exception("YTMusic failed getting albums from library")
     #         library = []
     #         pass
     #     for a in uploads:
     #         ret.add(a["title"])
     #     for a in library:
     #         ret.add(a["title"])
     return ret
Exemplo n.º 20
0
    def as_list(self):
        logger.info("YTMusic getting user playlists")
        refs = []

        # playlist with songs similar to the last played
        refs.append(
            Ref.playlist(
                uri=f"ytm:playlist?id=watch",
                name="Similar to last played",
            ))

        # system playlists
        try:
            playlists = API.get_library_playlists(limit=100)
        except Exception:
            logger.exception("YTMusic failed getting a list of playlists")
            playlists = []
        for pls in playlists:
            refs.append(
                Ref.playlist(
                    uri=f"ytm:playlist?id={pls['playlistId']}",
                    name=pls["title"],
                ))
        return refs
Exemplo n.º 21
0
 def browse(self, uri):
     if not uri:
         return []
     logger.debug('YTMusic browsing uri "%s"', uri)
     if uri == "ytmusic:root":
         dirs = []
         if self.backend.auth:
             dirs += [
                 Ref.directory(uri="ytmusic:artist", name="Artists"),
                 Ref.directory(uri="ytmusic:album", name="Albums"),
             ]
             if self.backend.liked_songs:
                 dirs.append(
                     Ref.directory(uri="ytmusic:liked", name="Liked Songs"))
             if self.backend.history:
                 dirs.append(
                     Ref.directory(uri="ytmusic:history",
                                   name="Recently Played"))
             if self.backend.subscribed_artist_limit:
                 dirs.append(
                     Ref.directory(uri="ytmusic:subscriptions",
                                   name="Subscriptions"))
         dirs.append(
             Ref.directory(uri="ytmusic:watch",
                           name="Similar to last played"))
         if self.backend.mood_genre:
             dirs.append(
                 Ref.directory(uri="ytmusic:mood",
                               name="Mood and Genre Playlists"))
         if self.backend._auto_playlist_refresh_rate:
             dirs.append(
                 Ref.directory(uri="ytmusic:auto", name="Auto Playlists"))
         return dirs
     elif (uri == "ytmusic:subscriptions"
           and self.backend.subscribed_artist_limit):
         try:
             subs = self.backend.api.get_library_subscriptions(
                 limit=self.backend.subscribed_artist_limit)
             logger.debug("YTMusic found %d artists in subscriptions",
                          len(subs))
             return [
                 Ref.artist(uri=f"ytmusic:artist:{a['browseId']}",
                            name=a["artist"]) for a in subs
             ]
         except Exception:
             logger.exception(
                 "YTMusic failed getting artists from subscriptions")
     elif uri == "ytmusic:artist":
         try:
             library_artists = [
                 Ref.artist(uri=f"ytmusic:artist:{a['browseId']}",
                            name=a["artist"])
                 for a in self.backend.api.get_library_artists(limit=100)
             ]
             logger.debug("YTMusic found %d artists in library",
                          len(library_artists))
         except Exception:
             logger.exception("YTMusic failed getting artists from library")
             library_artists = []
         if self.backend.auth:
             try:
                 upload_artists = [
                     Ref.artist(
                         uri=f"ytmusic:artist:{a['browseId']}:upload",
                         name=a["artist"],
                     ) for a in self.backend.api.get_library_upload_artists(
                         limit=100)
                 ]
                 logger.debug("YTMusic found %d uploaded artists",
                              len(upload_artists))
             except Exception:
                 logger.exception("YTMusic failed getting uploaded artists")
                 upload_artists = []
         else:
             upload_artists = []
         return library_artists + upload_artists
     elif uri == "ytmusic:album":
         try:
             library_albums = [
                 Ref.album(uri=f"ytmusic:album:{a['browseId']}",
                           name=a["title"])
                 for a in self.backend.api.get_library_albums(limit=100)
             ]
             logger.debug("YTMusic found %d albums in library",
                          len(library_albums))
         except Exception:
             logger.exception("YTMusic failed getting albums from library")
             library_albums = []
         if self.backend.auth:
             try:
                 upload_albums = [
                     Ref.album(
                         uri=f"ytmusic:album:{a['browseId']}:upload",
                         name=a["title"],
                     ) for a in self.backend.api.get_library_upload_albums(
                         limit=100)
                 ]
                 logger.debug("YTMusic found %d uploaded albums",
                              len(upload_albums))
             except Exception:
                 logger.exception("YTMusic failed getting uploaded albums")
                 upload_albums = []
         else:
             upload_albums = []
         return library_albums + upload_albums
     elif uri == "ytmusic:liked":
         try:
             res = self.backend.api.get_liked_songs(
                 limit=self.backend.playlist_item_limit)
             tracks = self.playlistToTracks(res)
             logger.debug("YTMusic found %d liked songs",
                          len(res["tracks"]))
             return [Ref.track(uri=t.uri, name=t.name) for t in tracks]
         except Exception:
             logger.exception("YTMusic failed getting liked songs")
     elif uri == "ytmusic:history":
         try:
             res = self.backend.api.get_history()
             tracks = self.playlistToTracks({"tracks": res})
             logger.debug("YTMusic found %d songs from recent history",
                          len(res))
             return [Ref.track(uri=t.uri, name=t.name) for t in tracks]
         except Exception:
             logger.exception("YTMusic failed getting listening history")
     elif uri == "ytmusic:watch":
         try:
             playback = self.backend.playback
             if playback.last_id is not None:
                 track_id = playback.last_id
             elif self.backend.auth:
                 hist = self.backend.api.get_history()
                 track_id = hist[0]["videoId"]
             if track_id:
                 res = self.backend.api.get_watch_playlist(
                     track_id, limit=self.backend.playlist_item_limit)
                 if "tracks" in res:
                     logger.debug(
                         'YTMusic found %d watch songs for "%s"',
                         len(res["tracks"]),
                         track_id,
                     )
                     res["tracks"].pop(0)
                     tracks = self.playlistToTracks(res)
                     return [
                         Ref.track(uri=t.uri, name=t.name) for t in tracks
                     ]
         except Exception:
             logger.exception("YTMusic failed getting watch songs")
     elif uri == "ytmusic:mood":
         try:
             logger.debug("YTMusic loading mood/genre playlists")
             moods = {}
             response = self.backend.api._send_request(
                 "browse", {"browseId": "FEmusic_moods_and_genres"})
             for sect in nav(response, SINGLE_COLUMN_TAB + SECTION_LIST):
                 for cat in nav(sect, ["gridRenderer", "items"]):
                     title = nav(
                         cat,
                         [
                             "musicNavigationButtonRenderer",
                             "buttonText",
                             "runs",
                             0,
                             "text",
                         ],
                     ).strip()
                     endpnt = nav(
                         cat,
                         [
                             "musicNavigationButtonRenderer",
                             "clickCommand",
                             "browseEndpoint",
                             "browseId",
                         ],
                     )
                     params = nav(
                         cat,
                         [
                             "musicNavigationButtonRenderer",
                             "clickCommand",
                             "browseEndpoint",
                             "params",
                         ],
                     )
                     moods[title] = {
                         "name": title,
                         "uri": "ytmusic:mood:" + params + ":" + endpnt,
                     }
             return [
                 Ref.directory(uri=moods[a]["uri"], name=moods[a]["name"])
                 for a in sorted(moods.keys())
             ]
         except Exception:
             logger.exception("YTMusic failed to load mood/genre playlists")
     elif uri.startswith("ytmusic:mood:"):
         try:
             ret = []
             _, _, params, endpnt = uri.split(":")
             response = self.backend.api._send_request(
                 "browse", {
                     "browseId": endpnt,
                     "params": params
                 })
             for sect in nav(response, SINGLE_COLUMN_TAB + SECTION_LIST):
                 key = []
                 if "gridRenderer" in sect:
                     key = ["gridRenderer", "items"]
                 elif "musicCarouselShelfRenderer" in sect:
                     key = ["musicCarouselShelfRenderer", "contents"]
                 elif "musicImmersiveCarouselShelfRenderer" in sect:
                     key = [
                         "musicImmersiveCarouselShelfRenderer",
                         "contents",
                     ]
                 if len(key):
                     for item in nav(sect, key):
                         title = nav(item, ["musicTwoRowItemRenderer"] +
                                     TITLE_TEXT).strip()
                         #                           if 'subtitle' in item['musicTwoRowItemRenderer']:
                         #                               title += ' ('
                         #                               for st in item['musicTwoRowItemRenderer']['subtitle']['runs']:
                         #                                   title += st['text']
                         #                               title += ')'
                         brId = nav(
                             item,
                             ["musicTwoRowItemRenderer"] +
                             NAVIGATION_BROWSE_ID,
                         )
                         ret.append(
                             Ref.playlist(uri=f"ytmusic:playlist:{brId}",
                                          name=title))
             return ret
         except Exception:
             logger.exception(
                 'YTMusic failed getting mood/genre playlist "%s"', uri)
     elif uri == "ytmusic:auto" and self.backend._auto_playlist_refresh_rate:
         try:
             return [
                 Ref.directory(uri=a["uri"], name=a["name"])
                 for a in self.ytbrowse
             ]
         except Exception:
             logger.exception("YTMusic failed getting auto playlists")
     elif (uri.startswith("ytmusic:auto:")
           and self.backend._auto_playlist_refresh_rate):
         try:
             for a in self.ytbrowse:
                 if a["uri"] == uri:
                     ret = []
                     for i in a["items"]:
                         if i["type"] == "playlist":
                             ret.append(
                                 Ref.playlist(uri=i["uri"], name=i["name"]))
                             logger.debug("playlist: %s - %s", i["name"],
                                          i["uri"])
                         elif i["type"] == "artist":
                             ret.append(
                                 Ref.artist(uri=i["uri"], name=i["name"]))
                             logger.debug("artist: %s - %s", i["name"],
                                          i["uri"])
                         elif i["type"] == "album":
                             ret.append(
                                 Ref.album(uri=i["uri"], name=i["name"]))
                             logger.debug("album: %s - %s", i["name"],
                                          i["uri"])
                     return ret
         except Exception:
             logger.exception('YTMusic failed getting auto playlist "%s"',
                              uri)
     elif uri.startswith("ytmusic:artist:"):
         bId, upload = parse_uri(uri)
         if upload:
             try:
                 res = self.backend.api.get_library_upload_artist(bId)
                 tracks = self.uploadArtistToTracks(res)
                 logger.debug(
                     'YTMusic found %d songs for uploaded artist "%s"',
                     len(res),
                     res[0]["artist"][0]["name"],
                 )
                 return [Ref.track(uri=t.uri, name=t.name) for t in tracks]
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for uploaded artist "%s"',
                     bId,
                 )
         else:
             try:
                 res = self.backend.api.get_artist(bId)
                 tracks = self.artistToTracks(res)
                 logger.debug(
                     'YTMusic found %d songs for artist "%s" in library',
                     len(res["songs"]),
                     res["name"],
                 )
                 return [Ref.track(uri=t.uri, name=t.name) for t in tracks]
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for artist "%s"', bId)
     elif uri.startswith("ytmusic:album:"):
         bId, upload = parse_uri(uri)
         if upload:
             try:
                 res = self.backend.api.get_library_upload_album(bId)
                 tracks = self.uploadAlbumToTracks(res, bId)
                 logger.debug(
                     'YTMusic found %d songs for uploaded album "%s"',
                     len(res["tracks"]),
                     res["title"],
                 )
                 return [Ref.track(uri=t.uri, name=t.name) for t in tracks]
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for uploaded album "%s"',
                     bId,
                 )
         else:
             try:
                 res = self.backend.api.get_album(bId)
                 tracks = self.albumToTracks(res, bId)
                 logger.debug(
                     'YTMusic found %d songs for album "%s" in library',
                     len(res["tracks"]),
                     res["title"],
                 )
                 return [Ref.track(uri=t.uri, name=t.name) for t in tracks]
             except Exception:
                 logger.exception(
                     'YTMusic failed getting tracks for album "%s"', bId)
     elif uri.startswith("ytmusic:playlist:"):
         bId, upload = parse_uri(uri)
         try:
             res = self.backend.api.get_playlist(
                 bId, limit=self.backend.playlist_item_limit)
             tracks = self.playlistToTracks(res)
             return [Ref.track(uri=t.uri, name=t.name) for t in tracks]
         except Exception:
             logger.exception(
                 "YTMusic failed to get tracks from playlist '%s'", bId)
     return []
Exemplo n.º 22
0
    def get_items(self, uri):
        id_, upload = parse_uri(uri)
        tracks = None

        try:
            if id_ == 'watch':
                playback = self.backend.playback
                if playback.last_id is not None:
                    track_id = playback.last_id
                    logger.info(
                        "YTMusic getting watch playlist items for \"%s\"",
                        track_id)
                    tracks = API.get_watch_playlist(track_id, limit=100)
            else:
                logger.info("YTMusic getting playlist items for \"%s\"", id_)
                pls = API.get_playlist(id_, limit=100)
                if "tracks" in pls:
                    tracks = pls["tracks"]
        except Exception:
            logger.exception("YTMusic failed getting playlist items")

        if tracks:
            refs = []
            for track in tracks:
                refs.append(
                    Ref.track(uri=f"ytm:video?id={track['videoId']}",
                              name=track["title"]))
                duration = (track['duration'] if 'duration' in track else
                            track['length']).split(":")
                if 'artists' in track:
                    artists = [
                        Artist(
                            uri=f"ytm:artist?id={a['id']}&upload=false",
                            name=a["name"],
                            sortname=a["name"],
                            musicbrainz_id="",
                        ) for a in track["artists"]
                    ]
                elif 'byline' in track:
                    artists = [
                        Artist(
                            name=track["byline"],
                            sortname=track["byline"],
                            musicbrainz_id="",
                        )
                    ]
                else:
                    artists = None

                if 'album' in track:
                    album = Album(
                        uri=f"ytm:album?id={track['album']['id']}&upload=false",
                        name=track["album"]["name"],
                        artists=artists,
                        num_tracks=None,
                        num_discs=None,
                        date="1999",
                        musicbrainz_id="",
                    )
                else:
                    album = None

                TRACKS[track["videoId"]] = Track(
                    uri=f"ytm:video?id={track['videoId']}",
                    name=track["title"],
                    artists=artists,
                    album=album,
                    composers=[],
                    performers=[],
                    genre="",
                    track_no=None,
                    disc_no=None,
                    date="1999",
                    length=(int(duration[0]) * 60 * 1000) +
                    (int(duration[1]) * 1000),
                    bitrate=0,
                    comment="",
                    musicbrainz_id="",
                    last_modified=None,
                )
            return refs
        return None
Exemplo n.º 23
0
 def browse(self, uri):
     logger.info("YTMusic browsing uri \"%s\"", uri)
     if uri == "ytm:root":
         return [
             Ref.directory(uri="ytm:artist", name="Artists"),
             Ref.directory(uri="ytm:album", name="Albums"),
             Ref.directory(uri="ytm:liked", name="Liked Songs"),
         ]
     elif uri == "ytm:artist":
         try:
             library_artists = [
                 Ref.artist(
                     uri=f"ytm:artist?id={a['browseId']}&upload=false",
                     name=a["artist"])
                 for a in API.get_library_artists(limit=100)
             ]
             logger.info("YTMusic found %d artists in library",
                         len(library_artists))
         except Exception:
             logger.exception("YTMusic failed getting artists from library")
             library_artists = []
         # try:
         #     upload_artists = [
         #         Ref.artist(uri=f"ytm:artist?id={a['browseId']}&upload=true", name=a["artist"])
         #         for a in API.get_library_upload_artists(limit=100)
         #     ]
         #     logger.info("YTMusic found %d uploaded artists", len(upload_artists))
         # except Exception:
         #     logger.exception("YTMusic failed getting uploaded artists")
         #     upload_artists = []
         return library_artists  # + upload_artists
     elif uri == "ytm:album":
         try:
             library_albums = [
                 Ref.album(uri=f"ytm:album?id={a['browseId']}&upload=false",
                           name=a["title"])
                 for a in API.get_library_albums(limit=100)
             ]
             logger.info("YTMusic found %d albums in library",
                         len(library_albums))
         except Exception:
             logger.exception("YTMusic failed getting albums from library")
             library_albums = []
         # try:
         #     upload_albums = [
         #         Ref.album(uri=f"ytm:album?id={a['browseId']}&upload=true", name=a["title"])
         #         for a in API.get_library_upload_albums(limit=100)
         #     ]
         #     logger.info("YTMusic found %d uploaded albums", len(upload_albums))
         # except Exception:
         #     logger.exception("YTMusic failed getting uploaded albums")
         #     upload_albums = []
         return library_albums  # + upload_albums
     elif uri == "ytm:liked":
         try:
             res = API.get_liked_songs(limit=100)
             logger.info("YTMusic found %d liked songs", len(res["tracks"]))
             playlistToTracks(res)
             return [
                 Ref.track(uri=f"ytm:video?id={t['videoId']}",
                           name=t["title"])
                 for t in ("tracks" in res and res["tracks"]) or []
             ]
         except Exception:
             logger.exception("YTMusic failed getting liked songs")
     elif uri.startswith("ytm:artist?"):
         id_, upload = parse_uri(uri)
         # if upload:
         #     try:
         #         res = API.get_library_upload_artist(id_)
         #         uploadArtistToTracks(res)
         #         return [
         #             Ref.track(uri=f"ytm:album?id={t['videoId']}", name=t["title"])
         #             for t in res
         #         ]
         #         logger.info("YTMusic found %d songs for uploaded artist \"%s\"", len(res), res[0]["artist"]["name"])
         #     except Exception:
         #         logger.exception("YTMusic failed getting tracks for uploaded artist \"%s\"", id_)
         # else:
         try:
             res = API.get_artist(id_)
             logger.info(
                 "YTMusic found %d songs for artist \"%s\" in library",
                 len(res["songs"]), res["name"])
             artistToTracks(res)
             return [
                 Ref.track(uri=f"ytm:video?id={t['videoId']}",
                           name=t["title"])
                 for t in ("songs" in res and "results" in res["songs"]
                           and res["songs"]["results"]) or []
             ]
         except Exception:
             logger.exception(
                 "YTMusic failed getting tracks for artist \"%s\"", id_)
     elif uri.startswith("ytm:album?"):
         id_, upload = parse_uri(uri)
         # if upload:
         #     try:
         #         res = API.get_library_upload_album(id_)
         #         uploadAlbumToTracks(res, id_)
         #         return [
         #             Ref.track(uri=f"ytm:video?id={t['videoId']}", name=t["title"])
         #             for t in ("tracks" in res and res["tracks"]) or []
         #         ]
         #         logger.info("YTMusic found %d songs for uploaded album \"%s\"", len(res["tracks"]), res["title"])
         #     except Exception:
         #         logger.exception("YTMusic failed getting tracks for uploaded album \"%s\"", id_)
         # else:
         try:
             res = API.get_album(id_)
             logger.info(
                 "YTMusic found %d songs for album \"%s\" in library",
                 len(res["tracks"]), res["title"])
             albumToTracks(res, id_)
             return [
                 Ref.track(uri=f"ytm:video?id={t['videoId']}",
                           name=t["title"])
                 for t in ("tracks" in res and res["tracks"]) or []
             ]
         except Exception:
             logger.exception(
                 "YTMusic failed getting tracks for album \"%s\"", id_)
     return []
Exemplo n.º 24
0
def parseSearch(results, field=None, queries=[]):
    tracks = set()
    for result in results:
        if result["resultType"] == "song":
            if field == "track" and not any(
                    q.casefold() == result["title"].casefold()
                    for q in queries):
                continue
            try:
                length = [int(i) for i in result["duration"].split(":")]
            except ValueError:
                length = [0, 0]
            track = Track(
                uri=f"ytm:video?id={result['videoId']}",
                name=result["title"],
                artists=[
                    Artist(
                        uri=f"ytm:artist?id={a['id']}&upload=false",
                        name=a["name"],
                        sortname=a["name"],
                        musicbrainz_id="",
                    ) for a in result["artists"]
                ],
                album=Album(
                    uri=f"ytm:album?id={result['album']['id']}&upload=false",
                    name=result["album"]["name"],
                    artists=[
                        Artist(
                            uri=f"ytm:artist?id={a['id']}&upload=false",
                            name=a["name"],
                            sortname=a["name"],
                            musicbrainz_id="",
                        ) for a in result["artists"]
                    ],
                    num_tracks=None,
                    num_discs=None,
                    date="1999",
                    musicbrainz_id="",
                ) if "album" in result else None,
                composers=[],
                performers=[],
                genre="",
                track_no=None,
                disc_no=None,
                date="1999",
                length=(length[0] * 60 * 1000) + (length[1] * 1000),
                bitrate=0,
                comment="",
                musicbrainz_id="",
                last_modified=None,
            )
            tracks.add(track)
        elif result["resultType"] == "album":
            if field == "album" and not any(
                    q.casefold() == result["title"].casefold()
                    for q in queries):
                continue
            try:
                album = API.get_album(result["browseId"])
                artists = [
                    Artist(
                        uri="",
                        name=result["artist"],
                        sortname=result["artist"],
                        musicbrainz_id="",
                    )
                ]
                albumObj = Album(
                    uri=f"ytm:album?id={result['browseId']}&upload=false",
                    name=album["title"],
                    artists=artists,
                    num_tracks=int(album["trackCount"])
                    if str(album["trackCount"]).isnumeric() else None,
                    num_discs=None,
                    date=
                    f"{album['releaseDate']['year']}-{album['releaseDate']['month']}-{album['releaseDate']['day']}",
                    musicbrainz_id="",
                )
                if "tracks" in album:
                    for song in album["tracks"]:
                        track = Track(
                            uri=f"ytm:video?id={song['videoId']}",
                            name=song["title"],
                            artists=artists,
                            album=albumObj,
                            composers=[],
                            performers=[],
                            genre="",
                            track_no=int(song["index"])
                            if str(song["index"]).isnumeric() else None,
                            disc_no=None,
                            date=albumObj.date,
                            length=int(song["lengthMs"])
                            if str(song["lengthMs"]).isnumeric() else None,
                            bitrate=0,
                            comment="",
                            musicbrainz_id="",
                            last_modified=None,
                        )
                        tracks.add(track)
            except Exception:
                logger.exception("YTMusic failed parsing album %s",
                                 result["title"])
        elif result["resultType"] == "artist":
            if field == "artist" and not any(
                    q.casefold() == result["artist"].casefold()
                    for q in queries):
                continue
            try:
                artist = API.get_artist(result["browseId"])
                artists = [
                    Artist(
                        uri=f"ytm:artist?id={result['browseId']}&upload=false",
                        name=artist["name"],
                        sortname=artist["name"],
                        musicbrainz_id="",
                    )
                ]
                albums = API.get_artist_albums(result["browseId"],
                                               artist["albums"]["params"])
                for album in albums:
                    if field == "album" and not any(
                            q.casefold() == album["title"].casefold()
                            for q in queries):
                        continue
                    songs = API.get_album(album["browseId"])
                    albumObj = Album(
                        uri=f"ytm:album?id={album['browseId']}&upload=false",
                        name=album["title"],
                        artists=artists,
                        num_tracks=int(songs["trackCount"])
                        if str(songs["trackCount"]).isnumeric() else None,
                        num_discs=None,
                        date=
                        f"{songs['releaseDate']['year']}-{songs['releaseDate']['month']}-{songs['releaseDate']['day']}",
                        musicbrainz_id="",
                    )
                    if "tracks" in songs:
                        for song in songs["tracks"]:
                            track = Track(
                                uri=f"ytm:video?id={song['videoId']}",
                                name=song["title"],
                                artists=artists,
                                album=albumObj,
                                composers=[],
                                performers=[],
                                genre="",
                                track_no=int(song["index"])
                                if str(song["index"]).isnumeric() else None,
                                disc_no=None,
                                date=albumObj.date,
                                length=int(song["lengthMs"])
                                if str(song["lengthMs"]).isnumeric() else None,
                                bitrate=0,
                                comment="",
                                musicbrainz_id="",
                                last_modified=None,
                            )
                            tracks.add(track)
            except Exception:
                logger.exception("YTMusic failed parsing artist %s",
                                 result["artist"])
    tracks = list(tracks)
    for track in tracks:
        id_, upload = parse_uri(track.uri)
        TRACKS[id_] = track
    return tracks
Exemplo n.º 25
0
 def search(self, query=None, uris=None, exact=False):
     results = []
     logger.debug("YTMusic searching for %s", query)
     if "any" in query:
         try:
             res = self.backend.api.search(" ".join(query["any"]),
                                           filter=None)
             results = self.parseSearch(res)
         except Exception:
             logger.exception(
                 'YTMusic search failed for query "any"="%s"',
                 " ".join(query["any"]),
             )
     elif "track_name" in query:
         try:
             res = self.backend.api.search(" ".join(query["track_name"]),
                                           filter="songs")
             if exact:
                 results = self.parseSearch(res, "track",
                                            query["track_name"])
             else:
                 results = self.parseSearch(res)
         except Exception:
             logger.exception(
                 'YTMusic search failed for query "title"="%s"',
                 " ".join(query["track_name"]),
             )
     elif "albumartist" in query or "artist" in query:
         q1 = ("albumartist" in query and query["albumartist"]) or []
         q2 = ("artist" in query and query["artist"]) or []
         try:
             res = self.backend.api.search(" ".join(q1 + q2),
                                           filter="artists")
             if exact:
                 results = self.parseSearch(res, "artist", q1 + q2)
             else:
                 results = self.parseSearch(res)
         except Exception:
             logger.exception(
                 'YTMusic search failed for query "artist"="%s"',
                 " ".join(q1 + q2),
             )
     elif "album" in query:
         try:
             res = self.backend.api.search(" ".join(query["album"]),
                                           filter="albums")
             if exact:
                 results = self.parseSearch(res, "album", query["album"])
             else:
                 results = self.parseSearch(res)
         except Exception:
             logger.exception(
                 'YTMusic search failed for query "album"="%s"',
                 " ".join(query["album"]),
             )
     else:
         logger.debug(
             'YTMusic skipping search, unsupported field types "%s"',
             " ".join(query.keys()),
         )
         return None
     return results
Exemplo n.º 26
0
 def parseSearch(self, results, field=None, queries=[]):
     tracks = set()
     salbums = set()
     sartists = set()
     for result in results:
         if result["resultType"] == "song":
             if field == "track" and not any(
                     q.casefold() == result["title"].casefold()
                     for q in queries):
                 continue
             if result["videoId"] in self.TRACKS:
                 tracks.add(self.TRACKS[result["videoId"]])
             else:
                 try:
                     length = [
                         int(i) for i in result["duration"].split(":")
                     ]
                 except ValueError:
                     length = [0, 0]
                 if result["videoId"] is None:
                     continue
                 if result["videoId"] not in self.TRACKS:
                     artists = []
                     for a in result["artists"]:
                         if a["id"] not in self.ARTISTS:
                             self.ARTISTS[a["id"]] = Artist(
                                 uri=f"ytmusic:artist:{a['id']}",
                                 name=a["name"],
                                 sortname=a["name"],
                                 musicbrainz_id="",
                             )
                         artists.append(self.ARTISTS[a["id"]])
                     album = None
                     if "album" in result:
                         if result["album"]["id"] not in self.ALBUMS:
                             self.ALBUMS[result["album"]["id"]] = Album(
                                 uri=
                                 f"ytmusic:album:{result['album']['id']}",
                                 name=result["album"]["name"],
                                 artists=artists,
                                 num_tracks=None,
                                 num_discs=None,
                                 date="0000",
                                 musicbrainz_id="",
                             )
                             album = self.ALBUMS[result["album"]["id"]]
                     self.TRACKS[result["videoId"]] = Track(
                         uri=f"ytmusic:track:{result['videoId']}",
                         name=result["title"],
                         artists=artists,
                         album=album,
                         composers=[],
                         performers=[],
                         genre="",
                         track_no=None,
                         disc_no=None,
                         date="0000",
                         length=(length[0] * 60 * 1000) +
                         (length[1] * 1000),
                         bitrate=0,
                         comment="",
                         musicbrainz_id="",
                         last_modified=None,
                     )
                 tracks.add(self.TRACKS[result["videoId"]])
         elif result["resultType"] == "album":
             if field == "album" and not any(
                     q.casefold() == result["title"].casefold()
                     for q in queries):
                 continue
             try:
                 if result["browseId"] not in self.ALBUMS:
                     date = result["year"]
                     self.ALBUMS[result["browseId"]] = Album(
                         uri=f"ytmusic:album:{result['browseId']}",
                         name=result["title"],
                         artists=[
                             Artist(
                                 uri="",
                                 name=result["artist"],
                                 sortname=result["artist"],
                                 musicbrainz_id="",
                             )
                         ],
                         num_tracks=None,
                         num_discs=None,
                         date=date,
                         musicbrainz_id="",
                     )
                 salbums.add(self.ALBUMS[result["browseId"]])
             except Exception:
                 logger.exception("YTMusic failed parsing album %s",
                                  result["title"])
         elif result["resultType"] == "artist":
             if field == "artist" and not any(
                     q.casefold() == result["artist"].casefold()
                     for q in queries):
                 continue
             try:
                 artistq = self.backend.api.get_artist(result["browseId"])
                 if result["browseId"] not in self.ARTISTS:
                     self.ARTISTS[result["browseId"]] = Artist(
                         uri=f"ytmusic:artist:{result['browseId']}",
                         name=artistq["name"],
                         sortname=artistq["name"],
                         musicbrainz_id="",
                     )
                 sartists.add(self.ARTISTS[result["browseId"]])
                 if "albums" in artistq:
                     if "params" in artistq["albums"]:
                         albums = self.backend.api.get_artist_albums(
                             artistq["channelId"],
                             artistq["albums"]["params"],
                         )
                         for album in albums:
                             if album["browseId"] not in self.ALBUMS:
                                 self.ALBUMS[album["browseId"]] = Album(
                                     uri=
                                     f"ytmusic:album:{album['browseId']}",
                                     name=album["title"],
                                     artists=[
                                         self.ARTISTS[result["browseId"]]
                                     ],
                                     date=album["year"],
                                     musicbrainz_id="",
                                 )
                             salbums.add(self.ALBUMS[album["browseId"]])
                     elif "results" in artistq["albums"]:
                         for album in artistq["albums"]["results"]:
                             if album["browseId"] not in self.ALBUMS:
                                 self.ALBUMS[album["browseId"]] = Album(
                                     uri=
                                     f"ytmusic:album:{album['browseId']}",
                                     name=album["title"],
                                     artists=[
                                         self.ARTISTS[result["browseId"]]
                                     ],
                                     date=album["year"],
                                     musicbrainz_id="",
                                 )
                             salbums.add(self.ALBUMS[album["browseId"]])
                 if "singles" in artistq and "results" in artistq["singles"]:
                     for single in artistq["singles"]["results"]:
                         if single["browseId"] not in self.ALBUMS:
                             self.ALBUMS[single["browseId"]] = Album(
                                 uri=f"ytmusic:album:{single['browseId']}",
                                 name=single["title"],
                                 artists=[self.ARTISTS[result["browseId"]]],
                                 date=single["year"],
                                 musicbrainz_id="",
                             )
                         salbums.add(self.ALBUMS[single["browseId"]])
                 if "songs" in artistq:
                     if "results" in artistq["songs"]:
                         for song in artistq["songs"]["results"]:
                             if song["videoId"] in self.TRACKS:
                                 tracks.add(self.TRACKS[song["videoId"]])
                             else:
                                 album = None
                                 if "album" in song:
                                     if (song["album"]["id"]
                                             not in self.ALBUMS):
                                         self.ALBUMS[
                                             song["album"]["id"]] = Album(
                                                 uri=
                                                 f"ytmusic:album:{song['album']['id']}",
                                                 name=song["album"]["name"],
                                                 artists=[
                                                     self.ARTISTS[
                                                         result["browseId"]]
                                                 ],
                                                 date="1999",
                                                 musicbrainz_id="",
                                             )
                                     album = self.ALBUMS[song["album"]
                                                         ["id"]]
                                 if song["videoId"] not in self.TRACKS:
                                     self.TRACKS[song["videoId"]] = Track(
                                         uri=
                                         f"ytmusic:track:{song['videoId']}",
                                         name=song["title"],
                                         artists=[
                                             self.ARTISTS[
                                                 result["browseId"]]
                                         ],
                                         album=album,
                                         composers=[],
                                         performers=[],
                                         genre="",
                                         track_no=None,
                                         disc_no=None,
                                         date="0000",
                                         length=None,
                                         bitrate=0,
                                         comment="",
                                         musicbrainz_id="",
                                         last_modified=None,
                                     )
                                 tracks.add(self.TRACKS[song["videoId"]])
             except Exception:
                 logger.exception("YTMusic failed parsing artist %s",
                                  result["artist"])
     tracks = list(tracks)
     for track in tracks:
         bId, _ = parse_uri(track.uri)
         self.TRACKS[bId] = track
     logger.debug(
         "YTMusic search returned %d results",
         len(tracks) + len(sartists) + len(salbums),
     )
     return SearchResult(
         uri="ytmusic:search",
         tracks=tracks,
         artists=list(sartists),
         albums=list(salbums),
     )