sigma67 / ytmusicapi

Unofficial API for YouTube Music
https://ytmusicapi.readthedocs.io
MIT License
1.76k stars 209 forks source link

yt.get_song does not work for uploaded songs #64

Closed xplorr closed 3 years ago

xplorr commented 4 years ago

I try to get the dict info from an uploaded song, but for all uploaded songids I try, I get the same error. Here is the content of my test python script isolating the problem:

from ytmusicapi import YTMusic
ytmusic = YTMusic('headers_auth.json')
song = ytmusic.get_song("kLXRb5ah7Fo")
print(song)

And this is the error it generates no matter what uploaded songid I specify:

Traceback (most recent call last):
  File "F:\Develop\test.py", line 3, in <module>
    song = ytmusic.get_song("kLXRb5ah7Fo")
  File "F:\Python38.32\lib\site-packages\ytmusicapi\mixins\browsing.py", line 535, in get_song
    song_meta['streamingData'] = player_response['streamingData']
KeyError: 'streamingData'

What is the reason? ps. all my other calls creating playlists, adding songs to playlists etc. work perfectly ps. for public songs, the call works ok ps. i need this function to remove songs from an existing playlist (YTMusic.remove_playlist_items)

sigma67 commented 4 years ago

This method was not intended for uploaded songs, so I'm not surprised it yields an error. I'll have to look into it to see if it's possible to get metadata for a single uploaded song somehow.

As for your actual use case, I'm not sure why you'd need this call to remove uploaded items from a playlist. The proper way to this is to first call get_playlist, then filter out the items you want to remove, and then call remove_playlist_items with a List of Dicts, where each Dict contains a videoId and a setVideoId from the playlist you're targeting.

xplorr commented 4 years ago

I need it because for my WinAmp/WACUP sync tool WinaYo, I create a local "database" of songsids and playlistids to be able to sync my local playlists with Youtube Music. So I already have all the songids of all playlists (to do SQL queries). Because I want to reduce the number of ytmusicapi calls, I use this information iso calling get_playlist again.

Besides, all other functions (I tested so far) of ytmusicapi also work correctly with uploaded songids, so consequently I expected that yt.get_song would also work for uploaded songs. So, would be very glad if it worked.

sigma67 commented 4 years ago

Makes sense so far, but to be able to remove entries from the playlist again you also need to store the setVideoId for each playlist entry in your database. The song id (videoId) is not enough. I'm not sure how get_song would help you achieve that goal?

Nevertheless, I'll do some research on retrieving metadata for uploaded songs.

xplorr commented 4 years ago

Because I avoid/don't allow double songid entries in playlists.

sigma67 commented 4 years ago

The get_video_info API used by get_song does not work with uploaded songs.

The metadata you see in the web API is retrieved from the next endpoint used by get_watch_playlist, which is quite unstructured and has no annotations for the text (i.e. if it's artist, title etc.). See the below example. So I'm not sure if it's possible to support uploaded songs properly. Perhaps we could add a metadata key to the response of get_watch_playlist.

  "metadataScreen": {
    "sectionListRenderer": {
      "contents": [
        {
          "itemSectionRenderer": {
            "contents": [
              {
                "musicWatchMetadataRenderer": {
                  "title": {
                    "runs": [
                      {
                        "text": "A Different Way (Tritonal Remix)"
                      }
                    ]
                  },
                  "byline": {
                    "runs": [
                      {
                        "text": "DJ Snake ft Lauv",
                        "navigationEndpoint": {
                          "clickTrackingParams": "CAQQkGsYACITCNHtl_y4xesCFcOOfAodn8UC4w==",
                          "browseEndpoint": {
                            "browseId": "FEmusic_library_privately_owned_artist_detaila_po_CICr2crg7OWpchIQZGogc25ha2UgZnQgbGF1dg",
                            "browseEndpointContextSupportedConfigs": {
                              "browseEndpointContextMusicConfig": {
                                "pageType": "MUSIC_PAGE_TYPE_UNKNOWN"
                              }
                            }
                          }
                        }
                      },
                      {
                        "text": " • "
                      },
                      {
                        "text": "A Different Way (Tritonal Remix)",
                        "navigationEndpoint": {
                          "clickTrackingParams": "CAQQkGsYACITCNHtl_y4xesCFcOOfAodn8UC4w==",
                          "browseEndpoint": {
                            "browseId": "FEmusic_library_privately_owned_release_detailb_po_CICr2crg7OWpchIcZGlmZmVyZW50IHdheSB0cml0b25hbCByZW1peBoQZGogc25ha2UgZnQgbGF1diIDZ3Bt",
                            "browseEndpointContextSupportedConfigs": {
                              "browseEndpointContextMusicConfig": {
                                "pageType": "MUSIC_PAGE_TYPE_ALBUM"
                              }
                            }
                          }
                        }
                      }
                    ]
                  },
                  "secondaryByline": {
                    "runs": [
                      {
                        "text": "3 views"
                      },
                      {
                        "text": " • "
                      },
                      {
                        "text": "1 like"
                      }
                    ]
                  },
                  "secondaryTitle": {
                    "runs": [
                      {
                        "text": "A Different Way (Tritonal Remix)"
                      }
                    ]
                  },
                  "trackingParams": "CAQQkGsYACITCNHtl_y4xesCFcOOfAodn8UC4w==",
                  "albumName": {
                    "runs": [
                      {
                        "text": "A Different Way (Tritonal Remix)"
                      }
                    ]
                  }
                }
              }
            ],
            "trackingParams": "CAMQuy8YACITCNHtl_y4xesCFcOOfAodn8UC4w=="
          }
        }
      ],
      "trackingParams": "CAIQui8iEwjR7Zf8uMXrAhXDjnwKHZ_FAuM="
    }
  }
xplorr commented 4 years ago

That would be a real pity if the function get_song() does not work with uploaded songs.

sigma67 commented 3 years ago

I believe get_watch_playlist should now return sufficient information (first track of returned tracks).