示例#1
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
示例#2
0
 def _refresh_youtube_player(self):
     t0 = time.time()
     url = self._get_youtube_player()
     if url is not None:
         if self.playback.Youtube_Player_URL != url:
             self.playback.update_cipher(playerurl=url)
         t = time.time() - t0
         logger.debug("YTMusic Player URL refreshed in %.2fs", t)
示例#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
示例#4
0
 def scrobble_track(self, bId):
     # Called through YTMusicScrobbleListener
     # Let YTMusic know we're playing this track so it will be added to our history.
     endpoint = "https://www.youtube.com/get_video_info"
     params = {
         "video_id": bId,
         "hl": self.api.language,
         "el": "detailpage",
         "c": "WEB_REMIX",
         "cver": "0.1",
     }
     response = requests.get(endpoint,
                             params,
                             headers=self.api.headers,
                             proxies=self.api.proxies)
     text = parse_qs(response.text)
     player_response = json.loads(text["player_response"][0])
     trackurl = re.sub(
         r"plid=",
         "list=",
         player_response["playbackTracking"]["videostatsPlaybackUrl"]
         ["baseUrl"],
     )
     CPN_ALPHABET = (
         "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_")
     params = {
         "cpn":
         "".join((CPN_ALPHABET[random.randint(0, 256) & 63]
                  for _ in range(0, 16))),
         "referrer":
         "https://music.youtube.com",
         "cbr":
         text["cbr"][0],
         "cbrver":
         text["cbrver"][0],
         "c":
         text["c"][0],
         "cver":
         text["cver"][0],
         "cos":
         text["cos"][0],
         "cosver":
         text["cosver"][0],
         "cr":
         text["cr"][0],
         "ver":
         2,
     }
     tr = requests.get(
         trackurl,
         params=params,
         headers=self.api.headers,
         proxies=self.api.proxies,
     )
     logger.debug("%d code from '%s'", tr.status_code, tr.url)
示例#5
0
 def artistToTracks(self, artist):
     if ("songs" in artist and "browseId" in artist["songs"]
             and artist["songs"]["browseId"] is not None):
         res = self.backend.api.get_playlist(
             artist["songs"]["browseId"],
             limit=self.backend.playlist_item_limit,
         )
         tracks = self.playlistToTracks(res)
         logger.debug("YTMusic found %d tracks for %s", len(tracks),
                      artist["name"])
         return tracks
     return None
示例#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
示例#7
0
    def translate_uri(self, uri):
        logger.debug('YTMusic PlaybackProvider.translate_uri "%s"', uri)

        if "ytmusic:track:" not in uri:
            return None

        try:
            bId = uri.split(":")[2]
            self.last_id = bId
            return self._get_track(bId)
        except Exception as e:
            logger.error('translate_uri error "%s"', str(e))
            return None
示例#8
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
示例#9
0
def get_video(id_):
    uri = f"https://music.youtube.com/watch?v={id_}"
    vid = YDL.extract_info(
        url=uri,
        download=False,
        ie_key=None,
        extra_info={},
        process=True,
        force_generic_extractor=False,
    )
    for fmt in vid["formats"]:
        if fmt["ext"] == "m4a":
            logger.debug("YTMusic Stream URI %s", fmt["url"])
            return fmt["url"]
    return None
示例#10
0
 def as_list(self):
     logger.debug("YTMusic getting user playlists")
     refs = []
     try:
         playlists = self.backend.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"ytmusic:playlist:{pls['playlistId']}",
                 name=pls["title"],
             ))
     return refs
示例#11
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
示例#12
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,
         )
示例#13
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
示例#14
0
    def track_playback_ended(self, tl_track, time_position):
        if self.scrobbling:
            track = tl_track.track
            if track.uri.startswith("ytmusic:"):
                duration = track.length and track.length // 1000 or 0
                time_position = time_position // 1000

                if time_position < duration // 2 and time_position < 120:
                    logger.debug(
                        "Track not played long enough too scrobble. (50% or 120s)"
                    )
                    return

                bId = track.uri.split(":")[2]
                logger.debug("Scrobbling: %s", bId)
                listener.send(
                    YTMusicScrobbleListener,
                    "scrobble_track",
                    bId=bId,
                )
示例#15
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
示例#16
0
 def scrobble_track(self, bId):
     # Called through YTMusicScrobbleListener
     # Let YTMusic know we're playing this track so it will be added to our history.
     player_response = self.api._send_request(
         "player",
         {
             "playbackContext": {
                 "contentPlaybackContext": {"signatureTimestamp": 18766}
             },
             "video_id": bId,
         },
     )
     trackurl = re.sub(
         r"plid=",
         "list=",
         player_response["playbackTracking"]["videostatsPlaybackUrl"][
             "baseUrl"
         ],
     )
     CPN_ALPHABET = (
         "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
     )
     params = {
         "cpn": "".join(
             (
                 CPN_ALPHABET[random.randint(0, 256) & 63]
                 for _ in range(0, 16)
             )
         ),
         "referrer": "https://music.youtube.com",
         "ver": 2,
     }
     tr = requests.get(
         trackurl,
         params=params,
         headers=self.api.headers,
         proxies=self.api.proxies,
     )
     logger.debug("%d code from '%s'", tr.status_code, tr.url)
示例#17
0
 def _refresh_youtube_player(self):
     t0 = time.time()
     self.playback.Youtube_Player_URL = self._get_youtube_player()
     t = time.time() - t0
     logger.debug("YTMusic Player URL refreshed in %.2fs", t)
示例#18
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 []
示例#19
0
 def get_images(self, uris):
     ret = {}
     for uri in uris:
         images = []
         comp = uri.split(":")
         if len(comp) == 3:
             logger.debug("YTMusic getting images for %s", uri)
             func, bId = comp[1:3]
             if bId not in self.IMAGES:
                 try:
                     if func == "artist":
                         data = self.backend.api.get_artist(bId)
                         images = self.addThumbnails(bId, data, False)
                     elif func == "album":
                         data = self.backend.api.get_album(bId)
                         images = self.addThumbnails(bId, data)
                     elif func == "playlist":
                         data = self.backend.api.get_playlist(bId)
                         images = self.addThumbnails(bId, data, False)
                     elif func == "track":
                         if (bId in self.TRACKS
                                 and self.TRACKS[bId].album is not None and
                                 self.TRACKS[bId].album.uri is not None):
                             album, upload = parse_uri(
                                 self.TRACKS[bId].album.uri)
                             if upload:
                                 data = self.backend.api.get_library_upload_album(
                                     album)
                             else:
                                 data = self.backend.api.get_album(album)
                             images = self.addThumbnails(bId, data)
                 except Exception:
                     logger.error("YTMusic unable to get image url for %s",
                                  uri)
             else:
                 images = self.IMAGES[bId]
         elif len(comp) == 4 and comp[3] == "upload":
             logger.debug("YTMusic getting images for %s", uri)
             func, bId = comp[1:3]
             if bId not in self.IMAGES:
                 try:
                     if func == "artist":
                         data = self.backend.api.get_library_upload_artist(
                             bId)
                         images = self.addThumbnails(bId, data, False)
                     elif func == "album":
                         data = self.backend.api.get_library_upload_album(
                             bId)
                         images = self.addThumbnails(bId, data)
                     elif func == "track":
                         if (bId in self.TRACKS
                                 and self.TRACKS[bId].album is not None and
                                 self.TRACKS[bId].album.uri is not None):
                             album, upload = parse_uri(
                                 self.TRACKS[bId].album.uri)
                             if upload:
                                 data = self.backend.api.get_library_upload_album(
                                     album)
                             else:
                                 data = self.backend.api.get_album(album)
                             images = self.addThumbnails(bId, data)
                 except Exception:
                     logger.error(
                         "YTMusic unable to get image url for uploaded %s",
                         uri,
                     )
             else:
                 images = self.IMAGES[bId]
         ret[uri] = images
         logger.debug("YTMusic found %d image urls for %s", len(images),
                      uri)
     return ret
示例#20
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
示例#21
0
 def _get_track(self, bId):
     streams = self.backend.api.get_streaming_data(bId)
     playstr = None
     url = None
     if self.backend.stream_preference:
         # Try to find stream by our preference order.
         tags = {}
         if "adaptiveFormats" in streams:
             for stream in streams["adaptiveFormats"]:
                 tags[str(stream["itag"])] = stream
         elif "dashManifestUrl" in streams:
             # Grab the dashmanifest XML and parse out the streams from it
             dash = requests.get(streams["dashManifestUrl"])
             formats = re.findall(
                 r'<Representation id="(\d+)" .*? bandwidth="(\d+)".*?BaseURL>(.*?)</BaseURL',
                 dash.text,
             )
             for stream in formats:
                 tags[stream[0]] = {
                     "url": stream[2],
                     "audioQuality": "ITAG_" + stream[0],
                     "bitrate": int(stream[1]),
                 }
         for i, p in enumerate(self.backend.stream_preference, start=1):
             if str(p) in tags:
                 playstr = tags[str(p)]
                 logger.debug("Found #%d preference stream %s", i, str(p))
                 break
     if playstr is None:
         # Couldn't find our preference, let's try something else:
         if "adaptiveFormats" in streams:
             # Try to find the highest quality stream.  We want "AUDIO_QUALITY_HIGH", barring
             # that we find the highest bitrate audio/mp4 stream, after that we sort through the
             # garbage.
             bitrate = 0
             crap = {}
             worse = {}
             for stream in streams["adaptiveFormats"]:
                 if ("audioQuality" in stream and stream["audioQuality"]
                         == "AUDIO_QUALITY_HIGH"):
                     playstr = stream
                     break
                 if (stream["mimeType"].startswith("audio/mp4")
                         and stream["bitrate"] > bitrate):
                     bitrate = stream["bitrate"]
                     playstr = stream
                 elif stream["mimeType"].startswith("audio"):
                     crap[stream["bitrate"]] = stream
                 else:
                     worse[stream["bitrate"]] = stream
             if playstr is None:
                 # sigh.
                 if len(crap):
                     playstr = crap[sorted(list(crap.keys()))[-1]]
                     if "audioQuality" not in playstr:
                         playstr["audioQuality"] = "AUDIO_QUALITY_GARBAGE"
                 elif len(worse):
                     playstr = worse[sorted(list(worse.keys()))[-1]]
                     if "audioQuality" not in playstr:
                         playstr["audioQuality"] = "AUDIO_QUALITY_FECES"
         elif "formats" in streams:
             # Great, we're really left with the dregs of quality.
             playstr = streams["formats"][0]
             if "audioQuality" not in playstr:
                 playstr["audioQuality"] = "AUDIO_QUALITY_404"
         else:
             logger.error(
                 "No streams found for %s. Falling back to youtube-dl.",
                 bId)
     if playstr is not None:
         # Use Youtube-DL's Info Extractor to decode the signature.
         if "signatureCipher" in playstr:
             sc = parse_qs(playstr["signatureCipher"])
             sig = self.YoutubeIE._decrypt_signature(
                 sc["s"][0],
                 bId,
                 self.Youtube_Player_URL,
             )
             url = sc["url"][0] + "&sig=" + sig + "&ratebypass=yes"
         elif "url" in playstr:
             url = playstr["url"]
         else:
             logger.error("Unable to get URL from stream for %s", bId)
             return None
         logger.info(
             "YTMusic Found %s stream with %d bitrate for %s",
             playstr["audioQuality"],
             playstr["bitrate"],
             bId,
         )
     if url is not None:
         if (self.backend.verify_track_url
                 and requests.head(url).status_code == 403):
             # It's forbidden. Likely because the player url changed and we
             # decoded the signature incorrectly.
             # Refresh the player, log an error, and send back none.
             logger.error(
                 "YTMusic found forbidden URL. Updating player URL now.")
             self.backend._youtube_player_refresh_timer.now()
         else:
             # Return the decoded youtube url to mopidy for playback.
             logger.debug("YTMusic found %s", url)
             return url
     return None
示例#22
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),
     )