[GH-ISSUE #379] [Question] How to mass-edit playlist #299

Closed
opened 2026-02-27 22:09:04 +03:00 by kerem · 2 comments
Owner

Originally created by @Bluscream on GitHub (Apr 14, 2023).
Original GitHub issue: https://github.com/sigma67/ytmusicapi/issues/379

I tried working this out with my good friend ChatGPT but we're unable to satisfy ytmusicapi;

def reverse_youtube_music_playlist(playlist_id):
    if "music.youtube.com/playlist" in playlist_id:
        # Extract the playlist id from the link
        playlist_id = playlist_id.split("list=")[1].split("&")[0]
    # get the playlist information
    playlist = ytm.get_playlist(playlist_id)
    playlist_title = playlist['title']
    playlist_description = playlist['description']
    # get the list of tracks in the playlist
    playlist_tracks = playlist['tracks']
    track_ids = [track for track in playlist_tracks]
    # reverse the order of the track ids
    reversed_track_ids = track_ids[::-1]
    # remove all tracks from the playlist
    ytm.remove_playlist_items(playlist_id, track_ids)
    # add the tracks back to the playlist in reverse order
    ytm.add_playlist_items(playlist_id, reversed_track_ids)
    # update the playlist title and description
    ytm.edit_playlist(playlist_id, title=playlist_title, description=playlist_description)
    print(f"Playlist '{playlist_title}' (ID: {playlist_id}) has been reversed!")
reverse_youtube_music_playlist("https://music.youtube.com/playlist?list=PLZcTzTcUhr8VIoyKfycoLAs3AbTlNUt2X&feature=share")

results in:

Traceback (most recent call last):
  File "C:\Python311\Lib\runpy.py", line 198, in _run_module_as_main
    return _run_code(code, main_globals, None,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\runpy.py", line 88, in _run_code
    exec(code, run_globals)
  File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy\__main__.py", line 39, in <module>
    cli.main()
  File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 430, in main
    run()
  File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 284, in run_file       
    runpy.run_path(target, run_name="__main__")
  File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 321, in run_path
    return _run_module_code(code, init_globals, run_name,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 135, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 124, in _run_code
    exec(code, run_globals)
  File "P:\Python\youtube-music\main.py", line 92, in <module>
    reverse_youtube_music_playlist("PLZcTzTcUhr8U5f3OvaFczQR2y6QwxOuCs")
  File "P:\Python\youtube-music\main.py", line 82, in reverse_youtube_music_playlist
    ytm.add_playlist_items(new_playlist_id, [track])
  File "C:\Python311\Lib\site-packages\ytmusicapi\mixins\playlists.py", line 319, in add_playlist_items
    response = self._send_request(endpoint, body)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\site-packages\ytmusicapi\ytmusic.py", line 144, in _send_request
    raise Exception(message + error)
Exception: Server returned HTTP 400: Bad Request.
Invalid value at 'actions[0]' (added_video_id), Starting an object on a scalar field

I have seen the moveItem argument in the edit_playlist method, but that's even more confusing to me and ChatGPT

Originally created by @Bluscream on GitHub (Apr 14, 2023). Original GitHub issue: https://github.com/sigma67/ytmusicapi/issues/379 I tried working this out with my good friend ChatGPT but we're unable to satisfy ytmusicapi; ```py def reverse_youtube_music_playlist(playlist_id): if "music.youtube.com/playlist" in playlist_id: # Extract the playlist id from the link playlist_id = playlist_id.split("list=")[1].split("&")[0] # get the playlist information playlist = ytm.get_playlist(playlist_id) playlist_title = playlist['title'] playlist_description = playlist['description'] # get the list of tracks in the playlist playlist_tracks = playlist['tracks'] track_ids = [track for track in playlist_tracks] # reverse the order of the track ids reversed_track_ids = track_ids[::-1] # remove all tracks from the playlist ytm.remove_playlist_items(playlist_id, track_ids) # add the tracks back to the playlist in reverse order ytm.add_playlist_items(playlist_id, reversed_track_ids) # update the playlist title and description ytm.edit_playlist(playlist_id, title=playlist_title, description=playlist_description) print(f"Playlist '{playlist_title}' (ID: {playlist_id}) has been reversed!") reverse_youtube_music_playlist("https://music.youtube.com/playlist?list=PLZcTzTcUhr8VIoyKfycoLAs3AbTlNUt2X&feature=share") ``` results in: ``` Traceback (most recent call last): File "C:\Python311\Lib\runpy.py", line 198, in _run_module_as_main return _run_code(code, main_globals, None, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Python311\Lib\runpy.py", line 88, in _run_code exec(code, run_globals) File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy\__main__.py", line 39, in <module> cli.main() File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 430, in main run() File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher/../..\debugpy/..\debugpy\server\cli.py", line 284, in run_file runpy.run_path(target, run_name="__main__") File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 321, in run_path return _run_module_code(code, init_globals, run_name, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 135, in _run_module_code _run_code(code, mod_globals, init_globals, File "c:\Users\blusc\.vscode\extensions\ms-python.python-2023.6.0\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 124, in _run_code exec(code, run_globals) File "P:\Python\youtube-music\main.py", line 92, in <module> reverse_youtube_music_playlist("PLZcTzTcUhr8U5f3OvaFczQR2y6QwxOuCs") File "P:\Python\youtube-music\main.py", line 82, in reverse_youtube_music_playlist ytm.add_playlist_items(new_playlist_id, [track]) File "C:\Python311\Lib\site-packages\ytmusicapi\mixins\playlists.py", line 319, in add_playlist_items response = self._send_request(endpoint, body) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Python311\Lib\site-packages\ytmusicapi\ytmusic.py", line 144, in _send_request raise Exception(message + error) Exception: Server returned HTTP 400: Bad Request. Invalid value at 'actions[0]' (added_video_id), Starting an object on a scalar field ``` I have seen the `moveItem` argument in the edit_playlist method, but that's even more confusing to me **and** ChatGPT
kerem closed this issue 2026-02-27 22:09:05 +03:00
Author
Owner

@Bluscream commented on GitHub (Apr 14, 2023):

After a lot of fiddling i came up with a solution that works, atleast. Idk if it's good tho:

from urllib.parse import urlparse, parse_qs
def s(l: dict): return f"'{l['title']}' ({l['id']}) [{len(l['tracks'])}]"
def get_playlist(playlist_id):
    if "music.youtube.com/playlist" in playlist_id:
        parsed_url = urlparse(playlist_id)
        playlist_id = parse_qs(parsed_url.query)['list'][0]
    playlist = ytm.get_playlist(playlist_id)
    if 'tracks' not in playlist:
        print(f"Playlist {s(playlist)} does not have any tracks.")
        return 
    for i, track in enumerate(playlist["tracks"]):
        if not "videoId" in track: print(f"videoId not found in #{i} {track['title']}")
        if not "setVideoId" in track: print(f"setVideoId not found in #{i} {track['title']}")
    return playlist
def clear_playlist(playlist):
    print(f"Removing tracks from {s(playlist)}...")
    ytm.remove_playlist_items(playlist["id"], playlist['tracks'])
def reverse_playlist(playlist):
    reversed_ids = [t["videoId"] for t in playlist['tracks']]
    reversed_ids.reverse()
    clear_playlist(playlist)
    ytm.add_playlist_items(playlistId=playlist["id"], videoIds=reversed_ids)
    new_description = f'{playlist["description"] or ""}\nReversed using ytmusicapi at {datetime.now()}'
    print(f"Updating playlist description to:\n{new_description}")
    ytm.edit_playlist(playlist["id"], description=new_description)
    print(f"Playlist {s(playlist)} has been reversed!")

playlist = get_playlist("https://music.youtube.com/playlist?list=PLZcTzTcUhr8U5f3OvaFczQR2y6QwxOuCs&feature=share")
reverse_playlist(playlist)
<!-- gh-comment-id:1509100155 --> @Bluscream commented on GitHub (Apr 14, 2023): After a lot of fiddling i came up with a solution that works, atleast. Idk if it's good tho: ```py from urllib.parse import urlparse, parse_qs def s(l: dict): return f"'{l['title']}' ({l['id']}) [{len(l['tracks'])}]" def get_playlist(playlist_id): if "music.youtube.com/playlist" in playlist_id: parsed_url = urlparse(playlist_id) playlist_id = parse_qs(parsed_url.query)['list'][0] playlist = ytm.get_playlist(playlist_id) if 'tracks' not in playlist: print(f"Playlist {s(playlist)} does not have any tracks.") return for i, track in enumerate(playlist["tracks"]): if not "videoId" in track: print(f"videoId not found in #{i} {track['title']}") if not "setVideoId" in track: print(f"setVideoId not found in #{i} {track['title']}") return playlist def clear_playlist(playlist): print(f"Removing tracks from {s(playlist)}...") ytm.remove_playlist_items(playlist["id"], playlist['tracks']) def reverse_playlist(playlist): reversed_ids = [t["videoId"] for t in playlist['tracks']] reversed_ids.reverse() clear_playlist(playlist) ytm.add_playlist_items(playlistId=playlist["id"], videoIds=reversed_ids) new_description = f'{playlist["description"] or ""}\nReversed using ytmusicapi at {datetime.now()}' print(f"Updating playlist description to:\n{new_description}") ytm.edit_playlist(playlist["id"], description=new_description) print(f"Playlist {s(playlist)} has been reversed!") playlist = get_playlist("https://music.youtube.com/playlist?list=PLZcTzTcUhr8U5f3OvaFczQR2y6QwxOuCs&feature=share") reverse_playlist(playlist) ```
Author
Owner

@sigma67 commented on GitHub (Apr 14, 2023):

This is not a place for questions. In any case, follow the issue template.

https://github.com/sigma67/ytmusicapi/discussions

<!-- gh-comment-id:1509119162 --> @sigma67 commented on GitHub (Apr 14, 2023): This is not a place for questions. In any case, follow the issue template. https://github.com/sigma67/ytmusicapi/discussions
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/ytmusicapi#299
No description provided.