Skip to content

Instantly share code, notes, and snippets.

@Yepoleb
Last active November 4, 2023 05:19
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Yepoleb/2122a671fc7793f6e303f1dbea95e372 to your computer and use it in GitHub Desktop.
Save Yepoleb/2122a671fc7793f6e303f1dbea95e372 to your computer and use it in GitHub Desktop.
Youtube Music downloader with automatic ID3 tagging. Uses mutagen and requests.
#!/usr/bin/env python3
# License: GPLv3
import re
import json
import shutil
import subprocess
import sys
import urllib.parse
import requests
import mutagen
import mutagen.id3
REFERER = "https://music.youtube.com/watch?v="
URL = "https://music.youtube.com/youtubei/v1/next?alt=json&key=AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0"
NEXT_TEMPLATE = {
"context": {
"client": {
"clientName": "WEB_REMIX",
"clientVersion": "0.1",
"hl": "en",
"gl": "AT",
"experimentIds": [],
"utcOffsetMinutes": 120,
"locationInfo": {
"locationPermissionAuthorizationStatus": "LOCATION_PERMISSION_AUTHORIZATION_STATUS_UNSUPPORTED"
},
"musicAppInfo": {
"musicActivityMasterSwitch": "MUSIC_ACTIVITY_MASTER_SWITCH_INDETERMINATE",
"musicLocationMasterSwitch": "MUSIC_LOCATION_MASTER_SWITCH_INDETERMINATE"
}
},
"capabilities": {},
"request": {
"internalExperimentFlags": [{
"key": "client_streamz_web_flush_count",
"value": "2"
}, {
"key": "music_web_show_player_bezel",
"value": "true"
}, {
"key": "polymer_simple_endpoint",
"value": "true"
}, {
"key": "music_web_service_worker_caching_strategy",
"value": ""
}, {
"key": "music_web_enable_signed_out_playlist_sharing",
"value": "true"
}, {
"key": "music_web_like_buttons_on_album_detail_page",
"value": "true"
}, {
"key": "music_web_enable_impressum",
"value": "true"
}, {
"key": "kevlar_enable_vimio_callback",
"value": "true"
}, {
"key": "ad_to_video_use_gel",
"value": "true"
}, {
"key": "music_show_buffering_spinner_in_player_bar",
"value": "true"
}, {
"key": "music_web_enable_prelive_bylines",
"value": "true"
}, {
"key": "kevlar_import_vimio_behavior",
"value": "true"
}, {
"key": "music_enable_see_all_expand_buttons",
"value": "true"
}, {
"key": "music_web_enable_scraper_metadata_for_detail_pages",
"value": "true"
}, {
"key": "music_web_enable_kav_prompt",
"value": "true"
}, {
"key": "music_web_enable_library_v2",
"value": "true"
}, {
"key": "music_enable_mix_free_tier_curated_playlists",
"value": "true"
}, {
"key": "web_logging_max_batch",
"value": "100"
}, {
"key": "music_display_empty_playlists",
"value": "true"
}, {
"key": "music_web_enable_rtl",
"value": "true"
}, {
"key": "music_enable_album_page_alternate_releases_shelf",
"value": "true"
}, {
"key": "kevlar_use_vimio_behavior",
"value": "true"
}, {
"key": "enable_video_list_browse_for_topical_mixes",
"value": "true"
}, {
"key": "music_web_playlist_id_in_responsive_list_play_button",
"value": "true"
}, {
"key": "music_web_confirm_add_existing_song_to_playlist",
"value": "true"
}, {
"key": "music_mobile_web_app_install_page",
"value": "true"
}, {
"key": "music_web_serve_app_for_scrapers",
"value": "true"
}, {
"key": "force_music_enable_outertube_tastebuilder_browse",
"value": "true"
}, {
"key": "music_web_enable_madison_account_support",
"value": "true"
}, {
"key": "music_web_session_check_interval_millis",
"value": "120000"
}, {
"key": "music_enable_share_for_search_albums",
"value": "true"
}, {
"key": "enable_web_music_player_error_message_renderer",
"value": "true"
}, {
"key": "music_web_fill_page_type_in_playlist_endpoints",
"value": "true"
}, {
"key": "music_web_handle_active_session_changes",
"value": "true"
}, {
"key": "flush_onbeforeunload",
"value": "true"
}, {
"key": "music_web_play_buttons_on_album_detail_page",
"value": "true"
}, {
"key": "music_enable_playlist_context_menu_share_endpoint",
"value": "true"
}, {
"key": "music_web_ignore_display_config_promotions",
"value": "true"
}, {
"key": "music_web_enable_start_radio_from_album",
"value": "true"
}, {
"key": "music_web_enable_prebuffering",
"value": "true"
}, {
"key": "music_library_shelf_item_fetch_count_override",
"value": "25"
}, {
"key": "web_gel_debounce_ms",
"value": "10000"
}, {
"key": "music_enable_start_radio_for_search_albums",
"value": "true"
}, {
"key": "music_web_track_focus_via_tab_key_in_carousels",
"value": "true"
}, {
"key": "enable_playability_filtering_in_entity_manager",
"value": "true"
}, {
"key": "music_web_enable_audio_only_playback",
"value": "true"
}, {
"key": "music_enable_improve_your_recommendations_setting",
"value": "true"
}, {
"key": "music_playlist_detail_use_country_restrictions_from_ytms",
"value": "true"
}, {
"key": "kevlar_attach_vimio_behavior",
"value": "true"
}, {
"key": "music_enable_responsive_list_item_mobile_web_treatment",
"value": "true"
}, {
"key": "client_streamz_web_flush_interval_seconds",
"value": "60"
}, {
"key": "music_web_autoplay_blocked_prompt",
"value": "true"
}, {
"key": "music_entitlement_subscription_reads",
"value": "true"
}, {
"key": "music_web_signup_canonical_base_urls",
"value": "true"
}, {
"key": "music_web_disable_home_background_fetch",
"value": "true"
}, {
"key": "attach_child_on_gel_web",
"value": "true"
}, {
"key": "music_web_enable_player_attestation",
"value": "true"
}, {
"key": "force_music_enable_outertube_artist_detail_channel_browse",
"value": "true"
}, {
"key": "music_web_send_initial_endpoint_for_landing_page_urls",
"value": "true"
}, {
"key": "music_enable_responsive_list_items_for_search",
"value": "true"
}, {
"key": "user_preference_collection_initial_browse_id",
"value": "FEmusic_tastebuilder"
}, {
"key": "music_web_app_non_teamfood",
"value": "true"
}, {
"key": "music_enable_album_entity_getbrowse",
"value": "true"
}, {
"key": "music_web_enable_scraper_metadata",
"value": "true"
}, {
"key": "music_enable_prelive_bylines_localization",
"value": "true"
}, {
"key": "music_enable_navigation_client_streamz",
"value": "true"
}, {
"key": "log_foreground_heartbeat_music_web",
"value": "true"
}, {
"key": "music_web_disable_mobile_miniplayer",
"value": "true"
}, {
"key": "music_enable_prelive_bylines",
"value": "true"
}, {
"key": "music_fill_text_endpoints_in_two_row_item_renderer",
"value": "true"
}, {
"key": "music_web_enable_corrected_tastebuilder_ordering",
"value": "true"
}, {
"key": "music_web_title_line_height",
"value": "1.2"
}, {
"key": "music_enable_you_there",
"value": "true"
}, {
"key": "music_web_enable_channel_alerts",
"value": "true"
}, {
"key": "video_to_ad_use_gel",
"value": "true"
}, {
"key": "music_web_red_subscriber_icon_border",
"value": "true"
}, {
"key": "music_web_send_initial_endpoint_for_playlist_urls",
"value": "true"
}, {
"key": "music_enable_nitrate_based_tastebuilder_onboarding_flow",
"value": "true"
}, {
"key": "enable_client_streamz_web",
"value": "true"
}, {
"key": "polymer2_not_shady_build",
"value": "true"
}, {
"key": "enable_premium_voluntary_pause",
"value": "true"
}, {
"key": "music_web_teamfood_dogfood_logos",
"value": "true"
}, {
"key": "music_web_hide_carousel_buttons_on_non_hover_devices",
"value": "true"
}, {
"key": "kevlar_enable_vimio_logging",
"value": "true"
}, {
"key": "debug_forced_promo_id",
"value": ""
}, {
"key": "music_web_enable_album_details_more_button",
"value": "true"
}, {
"key": "music_web_body_line_height",
"value": "1.4"
}],
"sessionIndex": 0
},
"user": {
"enableSafetyMode": False
}
},
"enablePersistentPlaylistPanel": True,
"tunerSettingValue": "AUTOMIX_SETTING_NORMAL",
"videoId": "VIDEO_ID",
"isAudioOnly": True
}
if len(sys.argv) > 1:
video_id = sys.argv[1]
else:
video_id = input("Video ID or URL: ")
if len(video_id) != 11:
match = re.search("v=([\\w\\-_]+)", video_id)
if match is None:
print("Could not parse video id from URL")
exit(1)
video_id = match.group(1)
session = requests.Session()
session.headers["User-Agent"] = USER_AGENT
next_req = NEXT_TEMPLATE.copy()
next_req["videoId"] = video_id
resp = session.post(
URL,
data=json.dumps(next_req),
headers={"Content-Type": "application/json", "Referer": REFERER + video_id})
resp_json = resp.json()
# ~ with open("resp_cache.json", "w") as f:
# ~ json.dump(resp_json, f, indent=2)
# ~ with open("resp_cache.json", "r") as f:
# ~ resp_json = json.load(f)
section_list = resp_json["contents"] \
["singleColumnMusicWatchNextResultsRenderer"]["metadataScreen"] \
["sectionListRenderer"]["contents"]
for section in section_list:
if "musicWatchMetadataRenderer" in section["itemSectionRenderer"]["contents"][0]:
watch_meta = section["itemSectionRenderer"]["contents"][0]["musicWatchMetadataRenderer"]
title = watch_meta["title"]["runs"][0]["text"]
second_row = [""]
for run in watch_meta["byline"]["runs"]:
text = run["text"]
if text == " • ":
second_row.append("")
else:
second_row[-1] += text
artist = second_row[0]
album = second_row[1]
year = second_row[2]
playlist = resp_json["contents"]["singleColumnMusicWatchNextResultsRenderer"] \
["playlist"]["playlistPanelRenderer"]["contents"]
for video in playlist:
video_renderer = video["playlistPanelVideoRenderer"]
if not video_renderer["selected"]:
continue
thumbnail1 = video_renderer["thumbnail"]["thumbnails"][0]["url"]
image_base = thumbnail1.split("=")[0]
cover_url = image_base + "=w1000-h1000-l90-rj"
print("Title:", title)
print("Artist:", artist)
print("Album:", album)
print("Year:", year)
print("Cover:", cover_url)
cover = requests.get(cover_url).content
filename = "/tmp/youtubedl_{}.%(ext)s".format(video_id)
filename_mp3 = "/tmp/youtubedl_{}.mp3".format(video_id)
subprocess.run([
"youtube-dl", "-f", "bestaudio", "-x", "--audio-format", "mp3",
"--audio-quality", "0", "-o", filename, video_id])
mp3 = mutagen.File(filename_mp3)
mp3.tags.add(mutagen.id3.TIT2(text=[title]))
mp3.tags.add(mutagen.id3.TPE1(text=[artist]))
mp3.tags.add(mutagen.id3.TALB(text=[album]))
mp3.tags.add(mutagen.id3.TDRC(text=[year]))
mp3.tags.add(mutagen.id3.APIC(
mime="image/jpeg", type=mutagen.id3.PictureType.COVER_FRONT,
desc="Cover", data=cover))
mp3.save()
final_name = (artist + " - " + title + ".mp3").replace("/", "_")
shutil.move(filename_mp3, final_name)
print("Renamed to", final_name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment