Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

RetroConvert

Converts a folder of video into 640x480 SD MP4 for FieldStation42 on a Raspberry Pi driving a composite CRT.

Point it at a folder and walk away.

cd ~/Desktop/RetroConvert
./retro-convert.sh "/Volumes/SD/Next Wave"

You get a live progress bar with a running count, rate and ETA:

  [############..................]  41%  59/144  ok:59 fail:0  2.6/min  eta 32m  free 178GB

Common runs

What you want Command
Convert a card, free space as it goes ./retro-convert.sh /Volumes/SD
Same, but stay usable while you work ./retro-convert.sh /Volumes/SD --jobs 4 --nice 19
Full speed, machine left alone ./retro-convert.sh /Volumes/SD --jobs 12
Keep originals, write elsewhere ./retro-convert.sh ~/Desktop/Show --out ~/Desktop/done --keep-sources
Just tell me what you'd do ./retro-convert.sh /Volumes/SD --dry-run

--help lists everything.

Always start with --dry-run on an unfamiliar folder. It prints the counts and changes nothing.


What it does, and what it refuses to do

  • Skips anything already 640x480 or smaller. Re-encoding those costs a generation of quality and saves nothing. In one wave this was 375 of 519 files left untouched.
  • Never upscales. A 320x240 source stays 320x240.
  • Fills the tube. Widescreen is scaled to cover 640x480 then cropped, not letterboxed — a CRT is 4:3 and black bars waste a third of the picture. Extreme aspects are padded instead.
  • Deletes a source only after its output passes every check: duration within 2%, ≤640x480, even dimensions, no upscale. Anything that fails keeps its source and is listed by name at the end.
  • Never deletes an unreadable source. Broken files are reported for you to re-copy.
  • Resumable. Ctrl-C is safe; re-run the same command and finished files are skipped.
  • Keeps the Mac awake for the whole run, and releases it on exit.
  • Sweeps ._* sidecars at the end. FieldStation42 globs *.mp4 and would otherwise catalogue every sidecar as an episode.

Encoder settings are fixed and deliberate: libx264, CRF 23, main/3.1, -preset veryfast, yuv420p, -g 60, AAC stereo 128k 48kHz, subtitles and data streams dropped. CRF not fixed bitrate — a fixed bitrate inflated files. Never VideoToolbox — no throughput gain, and concurrent jobs die with -22 Could not open encoder.


Run logs

Each run writes runs/<timestamp>/:

File Contents
status.tsv one line per file: OK/FAIL, sizes, reason
failed.txt just the failures, with why
skipped.txt what was left alone, and why (already SD / broken)
build.log raw ffmpeg errors

If ffmpeg goes missing

The script checks ~/.local/bin first, then PATH, and prints install instructions if neither has it. It has vanished twice before — once with a deleted Desktop folder, once when /tmp was purged — which is why it now lives in ~/.local/bin.


Notes for whoever edits this next

Several things in the script look like they could be simplified. They can't. Each one cost real time to find:

  • wait -n does not work here. macOS ships bash 3.2; wait -n arrived in 4.3. In 3.2 it fails instantly and does not block, so the parallelism gate silently stops holding and every job launches at once — 190 concurrent ffmpegs, observed. The jobs -r count gate is deliberate.
  • -threads must sit in the output options. Before -i it only limits the decoder; x264 then free-runs across every core and a few concurrent jobs make the machine unusable.
  • No "if the encode grew, remux instead" fallback. A remux copies the video stream untouched, so for an oversized source it returns the original oversized frame — smaller on disk, unplayable on the Pi. 56 short clips came back at full width that way before this was removed.
  • The upscale check tests the picture, not the frame. A letterboxed output is a 640x480 frame holding smaller content; comparing frame height to source height falsely failed two correct 720x400 encodes.
  • An unreadable probe must fail loudly. Shell numeric tests against an empty string pass, so treating a failed probe as "fine" means the upscale check never ran at all.
  • Parse dimensions with cut, not ${d%x*}. Some files make ffprobe emit a trailing delimiter (3840x2160x); the naive form reads that as junk and silently skips a real 4K file.
  • Sweep ._* after the copy, not just during. COPYFILE_DISABLE=1 plus an rsync --exclude is not enough — rsync preserves xattrs and exFAT materialises them as sidecars at the destination.
  • Kill the whole process tree. Killing the driver leaves ffmpeg children reparented to PID 1, still burning CPU and still holding disk blocks for files you already deleted.

Double-click version

RetroConvert.command converts the folder it sits in, plus every subfolder — so a shows folder with Season 01/, Season 02/… is handled in one go.

  1. Copy RetroConvert.command and retro-convert.sh into the folder (or the root of the SD card).
  2. Double-click RetroConvert.command.
  3. It scans, shows you the counts, and waits for you to type yes. Anything else cancels without touching a file.

It looks for retro-convert.sh beside itself first, then falls back to ~/Desktop/RetroConvert/, so copying just the .command also works as long as this folder stays put.

The confirmation prompt is deliberate: double-clicking is easy to do by accident, and the default behaviour deletes each original once its replacement verifies.

About

Batch-convert video to 480p SD MP4 for FieldStation42 on a CRT. Never upscales, verifies before deleting sources, resumable.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages