Skip to content

Instantly share code, notes, and snippets.

@ShayBox
Created May 7, 2026 07:27
Show Gist options
  • Select an option

  • Save ShayBox/699bbd9e4afb12204952747fd017fc90 to your computer and use it in GitHub Desktop.

Select an option

Save ShayBox/699bbd9e4afb12204952747fd017fc90 to your computer and use it in GitHub Desktop.
Osu Most Played Downloader | Some vibe coded shit that abuses multiple APIs
import requests
import time
import os
import re
from tqdm import tqdm
# Try APIs in order. Use {} for beatmapset_id formatting.
DOWNLOAD_APIS = [
"https://api.nerinyan.moe/d/{}",
"https://catboy.best/d/{}",
]
def sanitize_filename(name: str) -> str:
# Remove characters invalid in Windows filenames: \ / : * ? " < > |
return re.sub(r'[\\/*?:"<>|]', '', name)
def prompt_yes_no(prompt: str, default: str = "n") -> bool:
while True:
choice = input(f"{prompt} (y/n) [default: {default}]: ").strip().lower()
if choice == '':
choice = default
if choice in ['y', 'n']:
return choice == 'y'
print("Please enter 'y' or 'n'.")
def build_params(options):
# API query parameters
params = {}
for key in ['nohitsound', 'nostoryboard', 'nobg', 'novideo']:
params[key] = 'true' if options.get(key, False) else 'false'
return params
def download_from_url(url, filename, headers, params):
try:
with requests.get(url, headers=headers, params=params, stream=True, timeout=30) as response:
if response.status_code == 404:
return False, "404 Not Found"
if response.status_code != 200:
return False, f"HTTP {response.status_code}"
total_size = int(response.headers.get('content-length', 0))
with open(filename, "wb") as f, tqdm(
total=total_size,
unit='B',
unit_scale=True,
unit_divisor=1024,
desc=filename,
ascii=True
) as bar:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
bar.update(len(chunk))
return True, None
except Exception as e:
return False, str(e)
def download_single_beatmap(beatmapset_id, song_title, options):
headers = {
"Accept": "application/x-osu-beatmap-archive",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 OPR/108.0.0.0",
}
params = build_params(options)
filename = f"{beatmapset_id} - {sanitize_filename(song_title)}.osz"
# Skip if file already exists
if os.path.exists(filename):
print(f"Skipping {filename} (already exists)")
return True
last_error = None
for api_template in DOWNLOAD_APIS:
url = api_template.format(beatmapset_id)
print(f"Trying: {url}")
success, error = download_from_url(url, filename, headers, params)
if success:
print(f"Downloaded: {filename}")
return True
last_error = error
print(f"Failed with {url}: {error}")
# If this API 404s, try the next one
# If it fails for another reason, still try the next one
time.sleep(1)
print(f"All APIs failed for {beatmapset_id} - {song_title}: {last_error}")
return False
def download_beatmaps(beatmaps: list[dict], options):
failed_downloads = []
total = len(beatmaps)
for i, beatmap in enumerate(beatmaps, 1):
beatmapset = beatmap.get("beatmapset") or beatmap.get("beatmap")
if not beatmapset:
print(f"Skipping beatmap with missing beatmapset data at index {i}")
failed_downloads.append(None)
continue
beatmapset_id = beatmapset.get("id") or beatmapset.get("beatmapset_id")
song_title = beatmapset.get("title", "Unknown Title")
if not beatmapset_id:
print(f"Skipping beatmap with missing id at index {i}")
failed_downloads.append(None)
continue
print(f"Downloading ({i}/{total}): {beatmapset_id} - {song_title}")
success = download_single_beatmap(beatmapset_id, song_title, options)
if not success:
failed_downloads.append(beatmapset_id)
time.sleep(1) # Rate limit delay
if failed_downloads:
with open("failed_downloads.txt", "w") as f:
for fail in failed_downloads:
if fail:
f.write(str(fail) + "\n")
print("Failed downloads saved to failed_downloads.txt")
def retrieve_most_played_beatmaps(user_id: str, limit: int, offset: int = 0) -> list[dict]:
beatmaps = []
per_page = 10
for current_offset in range(offset, offset + limit, per_page):
try:
url = f"https://osu.ppy.sh/users/{user_id}/beatmapsets/most_played?limit={per_page}&offset={current_offset}"
response = requests.get(url, timeout=30)
if response.status_code == 429:
print(f"Rate limited at offset {current_offset}. Waiting 10 seconds...")
time.sleep(10)
continue
elif response.status_code != 200:
print(f"Skipping offset {current_offset} (status code: {response.status_code})")
continue
data = response.json()
if not isinstance(data, list):
print(f"Unexpected data structure at offset {current_offset}, skipping")
continue
beatmaps.extend(data)
print(f"Fetched {len(data)} beatmaps at offset {current_offset}")
time.sleep(1)
except Exception as e:
print(f"Error fetching beatmaps at offset {current_offset}: {e}")
return beatmaps
def main():
print("osu! Beatmap Downloader by API")
user_id = input("Enter your osu! user ID: ").strip()
while not user_id.isdigit():
user_id = input("Invalid input. Please enter numeric osu! user ID: ").strip()
try:
limit = int(input("How many beatmaps to download? (e.g. 100): ").strip())
except:
limit = 10
print("Invalid input. Defaulting to 10 beatmaps.")
try:
offset = int(input("Start from offset? (0 for beginning): ").strip())
except:
offset = 0
print("Invalid input. Starting from offset 0.")
print("\nSelect download options (y/n):")
options = {}
options['nohitsound'] = prompt_yes_no("NoHitsound", "n")
options['nostoryboard'] = prompt_yes_no("NoStoryboard", "n")
options['nobg'] = prompt_yes_no("noBg", "n")
options['novideo'] = prompt_yes_no("noVideo", "y")
beatmaps = retrieve_most_played_beatmaps(user_id, limit, offset)
if not beatmaps:
print("No beatmaps found. Exiting.")
return
download_beatmaps(beatmaps, options)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment