From 465ef03e5c312fb5bafdc20662637d922b6093ab Mon Sep 17 00:00:00 2001 From: alsi-lawr Date: Sun, 12 Jul 2026 18:44:50 +0100 Subject: [PATCH 1/2] feat: support webp as a first-class output format --- .gitattributes | 1 + README.md | 1 + command.go | 2 ++ examples/demo.tape | 1 + examples/fixtures/all.tape | 1 + examples/neofetch/README.md | 1 + examples/neofetch/neofetch.tape | 1 + ffmpeg.go | 15 +++++++++++++++ lexer/lexer_test.go | 2 ++ main.go | 2 ++ man.go | 4 ++-- parser/parser_test.go | 1 + serve.go | 6 +++++- vhs.go | 1 + video.go | 15 ++++++++++++--- 15 files changed, 48 insertions(+), 6 deletions(-) diff --git a/.gitattributes b/.gitattributes index 7fdaaf4e..b6df76f8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ **/*.gif filter=lfs diff=lfs merge=lfs -text **/*.mp4 filter=lfs diff=lfs merge=lfs -text **/*.webm filter=lfs diff=lfs merge=lfs -text +**/*.webp filter=lfs diff=lfs merge=lfs -text **/*.png filter=lfs diff=lfs merge=lfs -text *.tape linguist-language=elixir themes*.json linguist-generated diff --git a/README.md b/README.md index 8e3384ae..b2868130 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,7 @@ will render them to the respective locations. Output out.gif Output out.mp4 Output out.webm +Output out.webp Output frames/ # a directory of frames as a PNG sequence ``` diff --git a/command.go b/command.go index 4e5b0d06..d55bbcbe 100644 --- a/command.go +++ b/command.go @@ -414,6 +414,8 @@ func ExecuteOutput(c parser.Command, v *VHS) error { v.Options.Video.Output.Frames = c.Args case ".webm": v.Options.Video.Output.WebM = c.Args + case ".webp": + v.Options.Video.Output.WebP = c.Args default: v.Options.Video.Output.GIF = c.Args } diff --git a/examples/demo.tape b/examples/demo.tape index 46edfd04..ea0f4bd5 100644 --- a/examples/demo.tape +++ b/examples/demo.tape @@ -4,6 +4,7 @@ # Output .gif Create a GIF output at the given # Output .mp4 Create an MP4 output at the given # Output .webm Create a WebM output at the given +# Output .webp Create a WebP output at the given # # Require: # Require Ensure a program is on the $PATH to proceed diff --git a/examples/fixtures/all.tape b/examples/fixtures/all.tape index c72be570..06360877 100644 --- a/examples/fixtures/all.tape +++ b/examples/fixtures/all.tape @@ -4,6 +4,7 @@ Output examples/fixtures/all.gif Output examples/fixtures/all.mp4 Output examples/fixtures/all.webm +Output examples/fixtures/all.webp # Settings: Set Shell "fish" diff --git a/examples/neofetch/README.md b/examples/neofetch/README.md index d3aa7281..f1a92c9c 100644 --- a/examples/neofetch/README.md +++ b/examples/neofetch/README.md @@ -12,6 +12,7 @@ Output examples/neofetch/neofetch.gif Output examples/neofetch/neofetch.mp4 Output examples/neofetch/neofetch.webm +Output examples/neofetch/neofetch.webp Output examples/neofetch/frames/ Set TypingSpeed 75ms diff --git a/examples/neofetch/neofetch.tape b/examples/neofetch/neofetch.tape index ff0e4b3f..2198b3e2 100644 --- a/examples/neofetch/neofetch.tape +++ b/examples/neofetch/neofetch.tape @@ -7,6 +7,7 @@ Output examples/neofetch/neofetch.gif Output examples/neofetch/neofetch.mp4 Output examples/neofetch/neofetch.webm +Output examples/neofetch/neofetch.webp Output examples/neofetch/frames/ Set TypingSpeed 75ms diff --git a/ffmpeg.go b/ffmpeg.go index d35823af..edbe5710 100644 --- a/ffmpeg.go +++ b/ffmpeg.go @@ -318,6 +318,21 @@ func (sb *StreamBuilder) WithWebm() *StreamBuilder { return sb } +// WithWebP adds animated WebP stream configuration. +func (sb *StreamBuilder) WithWebP() *StreamBuilder { + sb.args = append(sb.args, + "-vcodec", "libwebp_anim", + "-pix_fmt", "yuva420p", + "-quality", "100", + "-compression_level", "3", + "-lossless", "0", + "-loop", "0", + "-an", + ) + + return sb +} + // Build returns streams for using with ffmepg. func (sb *StreamBuilder) Build() []string { return sb.args diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go index fd637a72..8d8bd9e2 100644 --- a/lexer/lexer_test.go +++ b/lexer/lexer_test.go @@ -163,6 +163,8 @@ func TestLexTapeFile(t *testing.T) { {token.STRING, "examples/fixtures/all.mp4"}, {token.OUTPUT, "Output"}, {token.STRING, "examples/fixtures/all.webm"}, + {token.OUTPUT, "Output"}, + {token.STRING, "examples/fixtures/all.webp"}, {token.COMMENT, " Settings:"}, {token.SET, "Set"}, {token.SHELL, "Shell"}, diff --git a/main.go b/main.go index 4e319bb5..6fc9d1ef 100644 --- a/main.go +++ b/main.go @@ -106,6 +106,8 @@ var ( v.Options.Video.Output.GIF = output } else if strings.HasSuffix(output, webm) { v.Options.Video.Output.WebM = output + } else if strings.HasSuffix(output, webp) { + v.Options.Video.Output.WebP = output } else if strings.HasSuffix(output, mp4) { v.Options.Video.Output.MP4 = output } diff --git a/man.go b/man.go index 816343b2..02dd830c 100644 --- a/man.go +++ b/man.go @@ -24,7 +24,7 @@ A tape file is a script made up of commands describing what actions to perform i The following is a list of all possible commands in VHS: -* %Output% .(gif|webm|mp4) +* %Output% .(gif|webm|webp|mp4) * %Require% * %Set% * %Sleep%