Skip to content

Instantly share code, notes, and snippets.

@kizernis
Created December 4, 2025 16:41
Show Gist options
  • Select an option

  • Save kizernis/a46d8bd3a4eb56ed2d4917767fe86f7f to your computer and use it in GitHub Desktop.

Select an option

Save kizernis/a46d8bd3a4eb56ed2d4917767fe86f7f to your computer and use it in GitHub Desktop.
from pathlib import Path
import subprocess
from tqdm import tqdm
import re
# ────────────────── SETTINGS ──────────────────
root_input = Path(r"D:\Downloads\Breaking Bad") # where your current seasons are
root_output = Path(r"F:\Breaking Bad") # where Goblin copies will be created
PROGRESS_STEPS = 5 # 1 = every 1 % (slowest but smoothest
# 5 = every 5 % (default, very nice)
# 10 = every 10 % (fastest updates)
# ─────────────────────────────────────────
root_output.mkdir(parents=True, exist_ok=True) # create if missing
tasks = []
for gob in root_input.rglob("Goblin"):
if gob.is_dir():
for ac3 in gob.glob("*.[aA][cC]3"):
mkv = gob.parent / (ac3.stem + ".mkv")
if mkv.is_file():
# preserve full folder structure in output
rel = mkv.parent.relative_to(root_input)
output_mkv = root_output / rel / mkv.name
tasks.append((mkv, ac3, output_mkv))
print(f"Found {len(tasks)} episodes → creating copies with Goblin audio in:\n {root_output}\n")
for mkv, ac3, output_mkv in tqdm(tasks, desc="Overall", unit="file", colour="cyan", leave=True):
output_mkv.parent.mkdir(parents=True, exist_ok=True)
temp = output_mkv.with_suffix(".temp.mkv")
cmd = [
"ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
"-stats_period", "0.05", "-stats",
"-i", str(mkv), "-i", str(ac3),
"-map", "0:v:0", "-map", "1:a:0",
"-map", "-0:a", "-map", "-0:s", "-map", "-0:d", "-map", "-0:t",
"-c", "copy",
"-copyts", "-avoid_negative_ts", "make_zero",
str(temp)
]
duration = float(subprocess.check_output([
"ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", str(mkv)
]).decode().strip() or 1)
proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.DEVNULL, text=True, bufsize=1)
with tqdm(total=100, desc=output_mkv.stem.ljust(60)[:60], leave=False,
bar_format="{l_bar}{bar} {percentage:3.0f}%") as pbar:
last = -1
line = ""
while True:
ch = proc.stderr.read(1)
if not ch and proc.poll() is not None:
break
if ch in '\r\n':
if "time=" in line:
if m := re.search(r"time=(\d{2,}:\d{2}:\d{2}\.\d{2})", line):
t = m.group(1)
h, m_, s = t.split(":")
secs = int(h)*3600 + int(m_)*60 + float(s)
percent = int(secs / duration * 100 + 0.999)
if percent // PROGRESS_STEPS > last // PROGRESS_STEPS:
pbar.n = (percent // PROGRESS_STEPS) * PROGRESS_STEPS
pbar.refresh()
last = percent
line = ""
else:
line += ch
pbar.n = 100
pbar.refresh()
proc.wait()
temp.rename(output_mkv) # final file in the new location
print(f"\nALL DONE! Goblin releases are waiting for you in:\n {root_output}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment