From cc1432889a2bbdf45ee0438118f0bea86275fe3b Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 18 Nov 2025 12:29:30 -0500 Subject: [PATCH] Can now use variants to produce things. --- Makefile | 3 ++ config.json | 98 +++++++++++---------------------------- config/settings.go | 14 ++++-- main.go | 113 +++++++++++++++++++++++---------------------- 4 files changed, 97 insertions(+), 131 deletions(-) diff --git a/Makefile b/Makefile index bfe8560..6938c2f 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,6 @@ build: install: build sudo cp vidcrunch /usr/local/bin/ + +clean: + rm -f dash_test/*.webm dash_test/*.mpd diff --git a/config.json b/config.json index 61b3213..03d8c32 100644 --- a/config.json +++ b/config.json @@ -1,12 +1,34 @@ [ { "Format": "webm", - "Scale": "1920:1080", - "Resize": false, - "VideoBitrate": 900, - "AudioCodec": "none", - "VideoCodec": "libvpx-vp9", - "CleanFilename": false, + "Variants": [ + { + "Scale": "1920:1080", + "Resize": false, + "VideoBitrate": 900, + "AudioCodec": "none", + "VideoCodec": "libvpx-vp9" + }, + { + "Scale": "1280:720", + "Resize": true, + "VideoBitrate": 600, + "AudioCodec": "none", + "VideoCodec": "libvpx-vp9" + }, + { + "Scale": "640:360", + "Resize": true, + "VideoBitrate": 300, + "AudioCodec": "none", + "VideoCodec": "libvpx-vp9" + }, + { + "AudioCodec": "libopus", + "AudioBitrate": 192, + "VideoCodec": "none" + } + ], "CRF": 30, "FPS": 30, "Input": "test_video.mp4", @@ -24,69 +46,5 @@ "-frame-parallel", "1", "-tune-content", "1" ] - }, - { - "Format": "webm", - "Scale": "1280:720", - "Resize": false, - "VideoBitrate": 600, - "AudioCodec": "none", - "VideoCodec": "libvpx-vp9", - "CleanFilename": false, - "CRF": 30, - "FPS": 30, - "Input": "test_video.mp4", - "OutDir": "dash_test", - "Passes": 1, - "Dash": true, - "Extras": [ - "-deadline", "realtime", - "-row-mt", "1", - "-cpu-used", "8", - "-g", "150", - "-keyint_min", "150", - "-tile-columns", "4", - "-tile-rows", "2", - "-frame-parallel", "1", - "-tune-content", "1" - ] - }, - { - "Format": "webm", - "Scale": "640:360", - "Resize": false, - "VideoBitrate": 300, - "AudioCodec": "none", - "VideoCodec": "libvpx-vp9", - "CleanFilename": false, - "CRF": 30, - "FPS": 30, - "Input": "test_video.mp4", - "OutDir": "dash_test", - "Passes": 1, - "Dash": true, - "Extras": [ - "-deadline", "realtime", - "-row-mt", "1", - "-cpu-used", "8", - "-g", "150", - "-keyint_min", "150", - "-tile-columns", "4", - "-tile-rows", "2", - "-frame-parallel", "1", - "-tune-content", "1" - ] - }, - { - "Format": "webm", - "AudioBitrate": 192, - "AudioCodec": "libopus", - "VideoCodec": "none", - "CleanFilename": false, - "CRF": 30, - "FPS": 30, - "Input": "test_video.mp4", - "OutDir": "dash_test", - "Passes": 1 } ] diff --git a/config/settings.go b/config/settings.go index 499330b..d107cd6 100644 --- a/config/settings.go +++ b/config/settings.go @@ -7,23 +7,27 @@ import ( "encoding/json" ) -type VideoOpts struct { - Format string +type CodecOpts struct { Scale string Resize bool VideoBitrate int VideoCodec string AudioBitrate int AudioCodec string + // used internally, shouldn't set int json + Target string +} + +type EncodeOpts struct { + Format string + Variants []CodecOpts Preset string - CleanFilename bool Dash bool CRF int FPS int Tune string Input string OutDir string - FakeResult string Test int TestStart int Passes int @@ -36,7 +40,7 @@ type Settings struct { Serve string Port string ConfigPath string - Encodings []VideoOpts + Encodings []EncodeOpts } func ParseFlags(c *Settings) { diff --git a/main.go b/main.go index a015466..3f3726e 100644 --- a/main.go +++ b/main.go @@ -17,33 +17,36 @@ import ( "iter" ) -func AudioOnly(encoding config.VideoOpts) bool { - return encoding.Scale == "" && encoding.VideoCodec == "none" +func AudioOnly(codec config.CodecOpts) bool { + return codec.Scale == "" && codec.VideoCodec == "none" } -func VideoOnly(encoding config.VideoOpts) bool { - return encoding.AudioCodec == "none" +func VideoOnly(codec config.CodecOpts) bool { + return codec.AudioCodec == "none" } -func ModFile(fname string, encoding config.VideoOpts) string { - cleaned := filepath.Clean(fname) +func SetCodecTarget(codec *config.CodecOpts, path string, encoding config.EncodeOpts) { + base := filepath.Base(path) + target := filepath.Join(encoding.OutDir, base) + + cleaned := filepath.Clean(target) dir, file := filepath.Split(cleaned) ext := filepath.Ext(file) base, found := strings.CutSuffix(file, ext) if !found { panic("no extension found?!") } - if AudioOnly(encoding) { + if AudioOnly(*codec) { renamed := fmt.Sprint(base, ".audio.", encoding.Format) - return filepath.Join(dir, renamed) + codec.Target = filepath.Join(dir, renamed) } else { - dim := strings.Replace(encoding.Scale, ":", ".", 1) + dim := strings.Replace(codec.Scale, ":", ".", 1) renamed := fmt.Sprint(base, ".", dim, ".", encoding.Format) - return filepath.Join(dir, renamed) + codec.Target = filepath.Join(dir, renamed) } } -func Run(encoding config.VideoOpts, pass int, pid int, input string, output string) { +func Run(encoding config.EncodeOpts, codec config.CodecOpts, pass int, pid int, input string, output string) { encode := fluentffmpeg.NewCommand("") extras := []string{ @@ -60,18 +63,18 @@ func Run(encoding config.VideoOpts, pass int, pid int, input string, output stri extras = append(extras, "-preset", encoding.Preset) } - if encoding.Resize { + if codec.Resize { extras = append(extras, - "-vf", fmt.Sprintf("scale=%s:flags=lanczos", encoding.Scale), - "-aspect", encoding.Scale) + "-vf", fmt.Sprintf("scale=%s:flags=lanczos", codec.Scale), + "-aspect", codec.Scale) } - if pass != encoding.Passes || VideoOnly(encoding) { + if pass != encoding.Passes || VideoOnly(codec) { extras = append(extras, "-an") } else { - encode.AudioCodec(encoding.AudioCodec) + encode.AudioCodec(codec.AudioCodec) extras = append(extras, - "-b:a", fmt.Sprint(encoding.AudioBitrate * 1024)) + "-b:a", fmt.Sprint(codec.AudioBitrate * 1024)) } @@ -80,11 +83,11 @@ func Run(encoding config.VideoOpts, pass int, pid int, input string, output stri extras = append(extras, "-t", fmt.Sprint(encoding.Test)) } - if encoding.VideoCodec == "none" { + if codec.VideoCodec == "none" { extras = append(extras, "-vn") } else { - encode.VideoCodec(encoding.VideoCodec). - VideoBitRate(encoding.VideoBitrate * 1024). + encode.VideoCodec(codec.VideoCodec). + VideoBitRate(codec.VideoBitrate * 1024). FrameRate(encoding.FPS). ConstantRateFactor(encoding.CRF) } @@ -118,12 +121,28 @@ func DevNull() string { } } -func RenderFile(encoding config.VideoOpts, pid int, path string, target string) { - for i := 1; i < encoding.Passes; i++ { - Run(encoding, i, pid, path, DevNull()) +func RenderFile(encoding config.EncodeOpts, pid int, path string, force bool) { + for i := 0; i < len(encoding.Variants); i++ { + codec := &encoding.Variants[i] + + SetCodecTarget(codec, path, encoding) + + _, err := os.Stat(codec.Target) + + if err == nil && !force { + fmt.Println("^^^ SKIP", path, "->", codec.Target) + return + } + + fmt.Println("--- PATH", path, "->", codec.Target) + for i := 1; i < encoding.Passes; i++ { + Run(encoding, *codec, i, pid, path, DevNull()) + } + + Run(encoding, *codec, encoding.Passes, pid, path, codec.Target) } - Run(encoding, encoding.Passes, pid, path, target) + SaveMPD(encoding) } func StrSeq(up_to int) iter.Seq[string] { @@ -134,18 +153,18 @@ func StrSeq(up_to int) iter.Seq[string] { } } -func SaveMPD(settings config.Settings) { +func SaveMPD(encoding config.EncodeOpts) { args := make([]string, 0, 10) var the_audio string // force it to overwrite args = append(args, "-y") - for _, encoding := range settings.Encodings { - if VideoOnly(encoding) { - args = append(args, "-f", "webm_dash_manifest", "-i", encoding.FakeResult) - } else if AudioOnly(encoding) { - the_audio = encoding.FakeResult + for _, codec := range encoding.Variants { + if VideoOnly(codec) { + args = append(args, "-f", "webm_dash_manifest", "-i", codec.Target) + } else if AudioOnly(codec) { + the_audio = codec.Target } } @@ -154,7 +173,7 @@ func SaveMPD(settings config.Settings) { // create map for each args = append(args, "-c", "copy") - for i := 0; i < len(settings.Encodings); i++ { + for i := 0; i < len(encoding.Variants); i++ { args = append(args, "-map", strconv.Itoa(i)) } @@ -162,15 +181,15 @@ func SaveMPD(settings config.Settings) { // id=0 is videos, id=1 is audio args = append(args, "-f", "webm_dash_manifest", "-adaptation_sets") - da_ints := slices.Collect(StrSeq(len(settings.Encodings) - 1)) + da_ints := slices.Collect(StrSeq(len(encoding.Variants) - 1)) video_set := strings.Join(da_ints, ",") adapt_set := fmt.Sprintf("id=0,streams=%s id=1,streams=%d", - video_set, len(settings.Encodings) - 1) + video_set, len(encoding.Variants) - 1) args = append(args, adapt_set) - args = append(args, filepath.Join(settings.Encodings[0].OutDir, "manifest.mpd")) + args = append(args, filepath.Join(encoding.OutDir, "manifest.mpd")) fmt.Println("\n\n\n\nARGS", args) @@ -181,29 +200,14 @@ func SaveMPD(settings config.Settings) { log.Fatal(err) } -func RenderToDir(encoding config.VideoOpts, force bool) string { +func RenderToDir(encoding config.EncodeOpts, force bool) { matches, err := filepath.Glob(encoding.Input) + if err != nil { log.Fatalf("%v", err) } - target := "" for _, path := range matches { - base := filepath.Base(path) - target = filepath.Join(encoding.OutDir, base) - target = ModFile(target, encoding) - - _, err := os.Stat(target) - - if err != nil || force { - fmt.Println("--- PATH", path, "->", target) - RenderFile(encoding, rand.Int(), path, target) - } else { - fmt.Println("^^^ SKIP", path, "->", target) - } + RenderFile(encoding, rand.Int(), path, force) } - - if target == "" { log.Fatal("fuck!") } - - return target } func main() { @@ -224,13 +228,10 @@ func main() { encoding := &settings.Encodings[i]; if encoding.OutDir != "" { - encoding.FakeResult = RenderToDir(*encoding, settings.Force) - fmt.Println("ENCODING result", encoding.FakeResult, "len=", len(settings.Encodings)) + RenderToDir(*encoding, settings.Force) } else { log.Fatal("config file needs either Output or OutDir") } } - - SaveMPD(settings) } }