Created
January 24, 2025 07:56
-
-
Save FlyingFathead/13cfe86196794f7f32c6530bfcc700b9 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#!/usr/bin/env python3 | |
# this is a simple script that allows you to use yt-dlp to get an audio file | |
# and recode it (if necessary) to fit within target size limitations. | |
import sys | |
import os | |
import subprocess | |
# ---------------------- | |
# CONFIG | |
# ---------------------- | |
DEFAULT_TARGET_MB = 20 | |
MIN_BITRATE = 8 # kbps | |
MAX_BITRATE = 320 # kbps | |
def hz_line(): | |
""" Print a line of dashes across the terminal width (or 80 fallback). """ | |
try: | |
columns = int(os.environ.get("COLUMNS", 0)) | |
if columns <= 0: | |
columns = int(subprocess.check_output(["tput", "cols"]).strip()) | |
except: | |
columns = 80 | |
print("-" * columns, flush=True) | |
def run_live(cmd): | |
""" Run a command so its stdout/stderr prints live. Return exit code. """ | |
p = subprocess.Popen(cmd) | |
p.communicate() | |
return p.returncode | |
def run_capture(cmd): | |
""" Run a command capturing all output (stdout/stderr). Return (stdout, stderr, code). """ | |
r = subprocess.run(cmd, capture_output=True, text=True) | |
return r.stdout.strip(), r.stderr.strip(), r.returncode | |
def file_size(path): | |
return os.path.getsize(path) | |
def usage(): | |
print(f"Usage: {os.path.basename(sys.argv[0])} <YOUTUBE_URL> [TARGET_MB]", flush=True) | |
print(f"Default TARGET_MB={DEFAULT_TARGET_MB}", flush=True) | |
def main(): | |
print("Starting yousqueeze single-pass script...", flush=True) | |
hz_line() | |
# --------------------- | |
# Step 0: Parse Args | |
# --------------------- | |
if len(sys.argv) < 2: | |
usage() | |
sys.exit(1) | |
url = sys.argv[1] | |
target_mb = float(sys.argv[2]) if len(sys.argv) > 2 else DEFAULT_TARGET_MB | |
target_bytes = int(target_mb * 1024 * 1024) | |
print(f"Target = {target_mb} MB => {target_bytes} bytes", flush=True) | |
hz_line() | |
# --------------------- | |
# Step 1: Fetch Title | |
# --------------------- | |
print("STEP 1: GET VIDEO TITLE (for naming)", flush=True) | |
hz_line() | |
title_cmd = ["yt-dlp", "--print", "title", "--skip-download", url] | |
out, err, code = run_capture(title_cmd) | |
if code != 0 or not out: | |
print("Could not retrieve title. Using fallback 'untitled_video'.", flush=True) | |
final_title = "untitled_video" | |
else: | |
final_title = out.strip() | |
final_mp3_name = f"{final_title}_recode_{int(target_mb)}.mp3" | |
print(f"Video title => {final_title}", flush=True) | |
hz_line() | |
# --------------------- | |
# Step 2: Download MP3 | |
# --------------------- | |
print("STEP 2: DOWNLOAD AS MP3", flush=True) | |
hz_line() | |
if os.path.exists("temp.mp3"): | |
os.remove("temp.mp3") | |
dl_cmd = [ | |
"yt-dlp", | |
"--extract-audio", | |
"--audio-format", "mp3", | |
"-f", "bestaudio", | |
"-o", "temp.%(ext)s", | |
url | |
] | |
print(f"Running: {' '.join(dl_cmd)}", flush=True) | |
rcode = run_live(dl_cmd) | |
if rcode != 0: | |
print("yt-dlp failed!", flush=True) | |
sys.exit(1) | |
if not os.path.exists("temp.mp3"): | |
print("No temp.mp3 created—something went wrong.", flush=True) | |
sys.exit(1) | |
size_now = file_size("temp.mp3") | |
print(f"Downloaded MP3 size => {size_now} bytes (~{size_now/(1024*1024):.2f} MB)", flush=True) | |
hz_line() | |
# --------------------- | |
# Step 3: Check If Under Target | |
# --------------------- | |
if size_now <= target_bytes: | |
print("Already under target => just rename & done!", flush=True) | |
if os.path.exists(final_mp3_name): | |
os.remove(final_mp3_name) | |
os.rename("temp.mp3", final_mp3_name) | |
print(f"Final => {final_mp3_name}", flush=True) | |
sys.exit(0) | |
# --------------------- | |
# Step 4: Single-Pass Squeeze | |
# --------------------- | |
print("STEP 4: SINGLE-PASS RE-ENCODE TO REACH TARGET", flush=True) | |
hz_line() | |
# 4a) get duration | |
ffprobe_cmd = [ | |
"ffprobe", | |
"-v", "error", | |
"-show_entries", "format=duration", | |
"-of", "default=noprint_wrappers=1:nokey=1", | |
"-i", "temp.mp3" | |
] | |
dur_out, dur_err, dur_code = run_capture(ffprobe_cmd) | |
if dur_code != 0 or not dur_out: | |
print("Could not get duration from ffprobe! Bailing out.", flush=True) | |
sys.exit(1) | |
duration_sec = float(dur_out.strip()) | |
print(f"Duration => {duration_sec:.2f} seconds", flush=True) | |
# 4b) approximate needed kbps | |
# total_bits_needed = target_bytes * 8 | |
# bits_per_second = total_bits_needed / duration_sec | |
# kbps = bits_per_second / 1000 | |
needed_kbps = int((target_bytes * 8) / (duration_sec * 1000)) | |
if needed_kbps < MIN_BITRATE: | |
needed_kbps = MIN_BITRATE | |
if needed_kbps > MAX_BITRATE: | |
needed_kbps = MAX_BITRATE | |
print(f"Estimated needed bitrate => {needed_kbps} kbps", flush=True) | |
hz_line() | |
# 4c) re-encode at that bitrate | |
if os.path.exists(final_mp3_name): | |
os.remove(final_mp3_name) | |
ff_cmd = [ | |
"ffmpeg", | |
"-y", | |
"-v", "info", | |
"-i", "temp.mp3", | |
"-vn", | |
"-c:a", "libmp3lame", | |
"-b:a", f"{needed_kbps}k", | |
"-f", "mp3", | |
final_mp3_name | |
] | |
print(f"Encoding once at {needed_kbps} kbps...", flush=True) | |
run_live(ff_cmd) | |
if not os.path.exists(final_mp3_name): | |
print("No final MP3 produced! Something went wrong with FFmpeg!", flush=True) | |
sys.exit(1) | |
final_size = file_size(final_mp3_name) | |
print(f"Final size => {final_size} bytes (~{final_size/(1024*1024):.2f} MB).", flush=True) | |
# Cleanup temp | |
if os.path.exists("temp.mp3"): | |
os.remove("temp.mp3") | |
print(f"Done => {final_mp3_name}", flush=True) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment