Last active
August 18, 2023 03:27
-
-
Save awbacker/62473baa4e4a662ec3fb8d3198f672e9 to your computer and use it in GitHub Desktop.
Starter script for purging a YT music library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# > python3.11 -m venv .venv | |
# > source .venv/bin/activate | |
# > pip install ytmusicapi | |
# Run the following command to generate the auth token file (oauth.json): | |
# > ytmusicapi oauth | |
import logging | |
import ytmusicapi | |
logging.basicConfig(level=logging.DEBUG, format="[%(levelname)-5s] %(message)s", handlers=[logging.StreamHandler()]) | |
yt_client = ytmusicapi.YTMusic("oauth.json") | |
def purge_songs(): | |
logging.info("FETCHING SONGS (This may take a while)") | |
songs = yt_client.get_library_songs( | |
limit=None, | |
order="a_to_z", | |
) | |
logging.debug(f" * {len(songs)} fetched" if songs else " * No songs found") | |
for start, songs_chunk in chunky(songs, size=5): | |
logging.info("Submitting Batch of Songs") | |
tokens = [s["feedbackTokens"]["add"] for s in songs_chunk] | |
resp = yt_client.edit_song_library_status(tokens) | |
feedback = [r["isProcessed"] for r in resp["feedbackResponses"]] | |
for i, (song, ok) in enumerate(zip(songs_chunk, feedback)): | |
song_change_log(i + start + 1, ok, song, start) | |
# Likes do not work quite right and I'm not sure why | |
# - There may be a different way to remove them that is not the edit_song_library_status? | |
# - The YT web ui calls a specific endpoint to remove the likes | |
def purge_likes(): | |
logging.info("FETCHING LIKES (This may take a while)") | |
likes = yt_client.get_liked_songs(limit=None) | |
tracks = likes["tracks"] | |
logging.debug(f" * {len(tracks)} fetched" if tracks else " * No likes found") | |
for start, tracks_chunk in chunky(tracks, size=5): | |
logging.info("Submitting Batch of Likes") | |
tokens = [s["feedbackTokens"]["remove"] for s in tracks_chunk] | |
resp = yt_client.edit_song_library_status(feedbackTokens=tokens) | |
feedback = [r["isProcessed"] for r in resp["feedbackResponses"]] | |
for i, (song, ok) in enumerate(zip(tracks_chunk, feedback)): | |
song_change_log(i + start + 1, ok, song, start) | |
def song_change_log(i, is_ok, song, start): | |
title = song.get("title", "<no title>") | |
artist = song.get("artists", [{}])[0].get("name", "<no artist>") | |
logging.log( | |
logging.INFO if is_ok else logging.ERROR, | |
f"({i: 4}) Remove: {title!r} ({artist})", | |
) | |
def chunky(items, size=20): | |
for start in range(0, len(items), size): | |
yield start, items[start: start + size] | |
if __name__ == "__main__": | |
purge_songs() | |
# purge_likes() # see function comment |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment