Local Video Generation With WAN

BagelMonster Now updated for DaSiWa's 6.1 Workflow!

If you're just here for the guide, skip to the Table of Contents.

A personal note to start, since I've been asked by a few people: most of my generations are posted on BeFeral instead of e6ai, and I will rarely post to e6ai in the future. If you're wondering where you can find other work of mine, BeFeral is the place. Note that my posts on there are generally not suitable for e6ai due to realistic-looking content and may contain non-Pokemon ferals. If that type of thing doesn't interest you, most of my posts there won't be to your liking.

BeFeral and e6ai are the only places I post, I do not post on Twitter/Telegram/Discord/4chan/any other social media service. Anyone on those services claiming to be me is not me, and I do not charge money for requests (I usually just ask you to help review/critique a future work in progress and tell me which generation was your favorite if you're OK with having it posted publicly). If you've paid money to an impersonator, sorry to say you've been scammed.

If a post of mine does not have the original images with metadata or prompts linked already and you want them, DM me on e6ai or post a comment and I'll be happy to provide them!

Shoutouts to /sdg/, /spt/, and /ff/ on /trash/!

Thanks to gridanon for reviewing this and providing suggestions!

Changes With The New Workflow

Tl;dr on changes with the new 6.1 workflow:

  • Models now go under unet, not checkpoints.
  • FLF2V is now a distinct mode instead of just a toggle. It still works the same way as before.
  • Names on some settings have been changed, and they've been moved around.
  • SD (576p) is now the default resolution, not SD Speed (480p). I recommend changing to SD Speed for testing generations.
  • A mode was added to allow combining 2 videos directly in ComfyUI (it will not trim frames though, so you may want to do all of it manually still)
  • You can add premade audio or generate it via a local model, if desired. This guide does not cover S2V or audio generation at all.

Models Needed

Software

A1111-Reforge - For image generation
ComfyUI - For video generation
ComfyUI Manager - For easier setup of ComfyUI.
FFMPEG - For frame extraction, video generation, video concatenation, video trimming, etc. If you're on Linux this is probably already installed for you, if you are on Windows you will need to download a prebuilt version, then add it to your PATH environment variable.

Image Models

3WolfMond-LastSDG
Kodak-K9

You will be using a combination of 3WolfMond and Kodak-K9 to generate images.

Video Models and Dependencies

DaSiWa's WAN 2.2 Lightning
Grab TastySin High and Low. The newer model, SyntheticSeduction, produces worse results from my experience.
Place the video models in ComfyUI/models/unet.

Text Encoder
Place this in ComfyUI/models/text_encoders.

WAN 2.1 VAE
Place this in ComfyUI/models/vae/WAN. Note that on Linux, the WAN directory name is case-sensitive and the workflow defaults to WAN being in all-caps here.

CLIP Vision
Place this in ComfyUI/models/clip_vision.

DaSiWa's ComfyUI Workflow
Grab the FastFidelity AIO one, at the time of writing this guide the version was 6.1. This workflow is meant to be used with his lightning models. This workflow may change and update in the future, but the major sections and fields this guide references should remain the same. I've uploaded a copy of the 6.1 workflow on catbox for those who want it.

Follow the instructions on the ComfyUI Manager Github page for installation. Once installed, it's time to set up ComfyUI!

ComfyUI Setup

Start ComfyUI.
Navigate to its web interface (by default, http://127.0.0.1:8188).
Open the ComfyUI workflow (File > Open > Find the workflow .json).
Image of ComfyUI Open Dialog

When you first open the workflow, you will get a notification about missing custom nodes. Luckily, ComfyUI Manager will help you install them. Near the top of your screen, you will see a new toolbar.
Image of ComfyUI Manager Toolbar

Click the big ComfyUI Manager button, you will get an overview screen.
Image of ComfyUI Manager Overview Screen

Click the button saying Install Missing Custom Nodes. A window will open with a list of the missing custom nodes for the workflow. Check all of them, and click the Install button. ComfyUI Manager will install all of the missing nodes (note that this may take several minutes). Once it's done, you'll get a popup prompting you to restart ComfyUI. Go ahead and restart it. Once it fully restarts, you'll probably get a second prompt asking to refresh your browser tab. Go ahead and refresh.

Conflicting Extensions

You may get a notification about a conflict between 2 GGUF extensions. Since we're using SafeTensors instead of GGUF, you can just ignore that warning.

Once none of the custom nodes are highlighted red, you can continue on.

Generate Base Images

In order to generate a video, you will need a high-quality base image. Use dimensions of 888 x 1152 (Width x Height). Older WAN workflows only supported generation on portrait-layout videos, if you used a landscape format image on them it was forcibly cropped down to a portrait layout. This guide will focus on portrait-layout generation, but the same principles can be applied for other layouts.

If you're going for a realistic look, I suggest using 3WolfMond-LastSDG to start and enabling Refiner to switch to Kodak-K9 at 80% (0.8). This can sometimes give better results anatomically.

If you're going for a maximum photorealistic look, just use Kodak-K9 for the entire generation.

Things To Look For

In general, try to keep parts you care about from being too close to the edges. Even if you tell the video model not to move the camera, it still will. If things go off-screen, the model will have to make something up when it comes back onscreen.
Expect some degradation over time with features in the image that may clip or otherwise interact with other features in the base image. This is ESPECIALLY true for longer videos with multiple segments, as features tend to go off screen and you get parts of them in last frame outputs, causing further degradation on future runs. Unfortunately, the only real solution is brute forcing a segment at a time until you get a good result.

Things to REALLY Look For

You will want to inpaint out any glaring artifacts. In particular, Kodak tends to mess up the eyes and will frequently leave little blocks of color or distortions in the image. You will want these gone, otherwise they will persist into the video. When inpainting, try both Kodak and 3WolfMond and see which gives you the better results.

The following is an example image generated using Kodak-K9, after inpainting:
Example Image After Inpainting

Once you have an image inpainted, you may notice there's a kind of grainy filter over it. This is a result of Kodak-K9's look and feel, trying to replicate photos. To get rid of the grainy filter, click the Extras tab. Go to Upscaler, drag in your image, select R-ESRGAN 4x+ for the upscaler, click Scale To, and input the same dimensions as the image you generated (888 x 1152). "Upscale" it and the grainy filter will be gone.

The following is that above image with the grainy filter removed:
Example Image with Grainy Filter Removed

Generate Video Segments

Once you have a finalized base image, it's time to generate a video with it!

Start ComfyUI.
Navigate to its web interface (by default, http://127.0.0.1:8188).
Open the ComfyUI workflow (File > Open > Find the workflow .json).
Image of ComfyUI Open Dialog
In the new workflow tab, you will be able to select your generation settings.

Image of ComfyUI Recommended Engine Settings

Engine Switch

Leave this on I2V (Safetensors).

Type Switch

Leave this on I2V. FLF2V will be covered later in this guide, for loops and longer videos.

Rendering Features I2V

NAG allows for you to use a negative prompt in your generation when using the 4-step WAN lightning models (you downloaded one of these already). You can enable NAG if you have a lot of VRAM, I have had very mixed results with it actually working.
If your PyTorch install and GPU combination supports SageAttention, feel free to turn it on for the performance gains. If you're not sure, just leave it turned off.

Image of ComfyUI Recommended Resolution and Features

Resolution

I recommend setting this to SD Speed (480p) while you get used to generation, you can always change it up later. Note that on my machine, a 3090 was only able to generate up to the HD setting (720p) before running out of VRAM on generation.

Post-Processing Features

Enable Interpolation and Upscale 2x, they don't take very long and will give you smoother motion and a better video resolution.
Enable Extract Last Frame.

Image of ComfyUI Backend Settings

Backend

This area is where you select which models to use. Click the model name text in I2V_High_Safetensors and select the High model you downloaded, then click the model name text in I2V_Low_Safetensors and select the Low model you downloaded. If you click and nothing happens, make sure you placed the models in the right ComfyUI/models/unet directory.
You can change your video length, any amount from 2-7 seconds will work. I don't recommend going below 2 seconds or above 7, you will get weird results.
If you want a fixed seed, change the control_after_generate setting to fixed. Otherwise, I recommend leaving it on randomize for a random seed each time.
For this model, you must leave CFG at 1.0.

Image of ComfyUI Video Prompts

Prompts

Next to the settings is where your positive prompt will go.

For your positive prompt, it's really up to you how detailed you want to go. For the purposes of this first example, we're using a very simple positive prompt: The man opens the box, revealing an engagement ring inside it.
Conversely, the second segment of the video (see the section below on making longer videos) has a fairly detailed prompt:
The Ninetales barks excitedly, then jumps up onto the man with all 9 of her tails wagging excitedly. She pushes her head under his chin and presses up as close to his body as possible, cuddling him. The man embraces her and pulls her close. The Ninetales does not climb back down or fall off of the man. Her many tails do not drag her back down. She remains pressed up close to him at the end of the video. The camera does not move.

Please note that the negative prompt will have no effect unless you enable NAG under Rendering Features I2V. Doing so will consume a LOT more VRAM. Note that with NAG enabled you may end up actually getting MORE of the behavior you're trying to avoid.

First-Frame-Image

Drag your base image here, it should then appear in the UI.
Image of ComfyUI First Frame Image

Generate!

Click the Run button in the upper left. After a few minutes, you should have a video under ComfyUI/output/video/<current date>.
Congratulations! Watch the generated video and make adjustments as desired.

Example video using the above generation settings

How Long Does This Thing Take?

On a single 3090, it takes around 5-6 minutes to generate the first 7 second video. Once the models are cached, this shortens to around 2.5 minutes.
On a single A4500, it takes roughly 6 minutes, then 3 minutes for subsequent generations.
In both cases, the models were being read off a NAS hard drive. Storing them on an SSD will probably improve things.

Improving Your Videos

I Want To Make Longer Videos!

Remember that Extract Last Frame option that we enabled above? In your video output folder, you will also have an image file which is the last frame of video.
Extract Last Frame output from the above generation

Take that image, drag it into ComfyUI, and generate another video (feel free to change up your prompt, LORAs, etc for the new video!). The new video will start from that last frame.

An example generation:
Positive prompt:

1
2
3
The Ninetales barks excitedly, then jumps up onto the man with all 9 of her tails wagging excitedly. She pushes her head under his chin and presses up as close to his body as possible, cuddling him. The man embraces her and pulls her close. The Ninetales does not climb back down or fall off of the man. Her many tails do not drag her back down. She remains pressed up close to him at the end of the video.

The camera does not move.

Part 2 Video, Continuing From Part 1 Above
As you can see in this Part 2, the attempts to keep the Ninetales in the video from jumping back down were only partly successful.

Once you have multiple video files, it's time to concatenate them:

Linux (ffmpeg_concat.sh):

#!/usr/bin/env bash

set -e

# Require at least one input file
if [ "$#" -lt 1 ]; then
  echo "Usage: $0 input1.webm input2.webm [input3.webm ...]"
  exit 1
fi

# Create a temporary file list
LIST_FILE="$(mktemp temp.XXXX)"
echo "$LIST_FILE"

# Ensure cleanup on exit
trap 'rm -f "$LIST_FILE"' EXIT

# Build the file list for ffmpeg
for FILE in "$@"; do
  if [ ! -f "$FILE" ]; then
    echo "Error: file not found: $FILE"
    exit 1
  fi
  BASENAME="${FILE%.*}"
  EXT="${FILE##*.}"
  # Escape single quotes for ffmpeg
  ESCAPED_FILE=${FILE//\'/\'\\\'\'}
  echo "file '$ESCAPED_FILE'" >> "$LIST_FILE"
done

# Concatenate videos
OUTPUT="${BASENAME}_combined.${EXT}"
ffmpeg -f concat -safe 0 -i "$LIST_FILE" -c copy "${OUTPUT}"

echo "Done! Output written to $OUTPUT"

Windows (ffmpeg_concat.bat):

@echo off
setlocal EnableDelayedExpansion

if "%~1"=="" (
    echo Usage: %0 input1.webm input2.webm [input3.webm ...]
    exit /b 1
)

REM Create a temporary file list
set LISTFILE=%TEMP%\ffmpeg_concat_%RANDOM%.txt

REM Track last file
set LASTFILE=

REM Build concat list and remember last file
(
    for %%F in (%*) do (
        echo file '%%~fF'
        set LASTFILE=%%~fF
    )
) > "%LISTFILE%"

REM Extract name and extension from last file
for %%L in ("%LASTFILE%") do (
    set BASENAME=%%~nL
    set EXT=%%~xL
    set DIR=%%~dpL
)

set OUTPUT=%DIR%%BASENAME%_combined%EXT%

REM Concatenate the videos
ffmpeg -f concat -safe 0 -i "%LISTFILE%" -c copy "%OUTPUT%"

REM Cleanup the temp list file
del "%LISTFILE%"

echo Output written to: %OUTPUT%

Part 1 + Part 2 Concatenated

My Extended Videos Stutter on Transition!

This is because the last frame of the previous video is repeated as the first frame of the next video, and motion will only start again after replaying that first frame. Trimming the first 2 frames (1 frame if you didn't enable Interpolation above) will make things smoother. Using ffmpeg:

Linux (ffmpeg_trim.sh):

#!/usr/bin/env bash

set -e

if [ "$#" -lt 1 ]; then
  echo "Usage: $0 video1.webm video2.webm [video3.webm ...]"
  exit 1
fi

for INPUT in "$@"; do
  if [ ! -f "$INPUT" ]; then
    echo "Error: file not found: $INPUT"
    exit 1
  fi

  BASENAME="${INPUT%.*}"
  EXT="${INPUT##*.}"
  OUTPUT="${BASENAME}_trimmed.${EXT}"

  echo "Trimming first 2 frames from: $INPUT"

  ffmpeg -y    -i "$INPUT"    -vf "trim=start_frame=2,setpts=PTS-STARTPTS"    "$OUTPUT"

  echo "Written: $OUTPUT"
done

Windows (ffmpeg_trim.bat):

@echo off
setlocal EnableExtensions EnableDelayedExpansion

REM Check for at least one argument
if "%~1"=="" (
    echo Usage: %~nx0 video1.webm video2.webm [video3.webm ...]
    exit /b 1
)

REM Loop over all arguments
for %%F in (%*) do (
    REM Check if file exists
    if not exist "%%F" (
        echo Error: file not found: %%F
        exit /b 1
    )

    REM Extract basename and extension
    set "INPUT=%%F"
    set "BASENAME=%%~dpnF"
    set "EXT=%%~xF"
    set "EXT=!EXT:~1!"
    set "OUTPUT=!BASENAME!_trimmed.!EXT!"

    echo Trimming first 2 frames from: %%F

    ffmpeg -y ^
        -i "%%F" ^
        -vf "trim=start_frame=2,setpts=PTS-STARTPTS" ^
        "!OUTPUT!"

    echo Written: !OUTPUT!
)

endlocal

Change the start_frame=2 to start_frame=1 if Interpolation wasn't enabled above.

I Get Degradation For Each Extension!

Unfortunately this is a simple reality of the video generation process. If things go off-screen or end up not being visible in the last frame, WAN has to create something new. Over time in a video this can compound, especially if last frame images include features being obscured or off-screen.

There are a couple ways to deal with this, depending on how complicated your video is:

You can just brute force things until you get a result you're happy with, if this is an acceptable approach to you.

If your video has a fixed camera, you can use FLF2V to "restore" quality back to the original state every now and then (I do it every 14 seconds of video). Under Type Switch, change to FLF2V. Drag your original starting image into the Last-Frame-Image section, then generate. Once you have things back to starting quality, you can disable FLF2V for another segment or 2.

Alternatively if your video is more complex, you can try the following: generate a segment and extract the last frame. Take that frame, and inpaint it until it's the quality you want. Then under Type Switch, change to FLF2V. Drag the inpainted frame into the Last-Frame-Image section, then generate. From there, you can disable FLF2V for another segment or 2.

If using the FLF2V approaches above, your video will likely "flash" a frame right before the transition because your original image will be higher quality than the video frames being generated. You can just trim this frame out (see the section on making loops below for the commands to do this). Note that it's the end frame of the video that did FLF2V, not the starting frame of that next segment. Don't trim the start of your next segment to keep everything smooth and consistent.

I Want To Create A Perfect Loop!

Under Type Switch, change to FLF2V. Under Post-Processing Features, enable Perfect Loop. Drag your first frame to the Last-Frame-Image section and generate! Note that this works for longer videos as well, just make your last segment end with the initial frame of the first segment. You may want to trim the last frame off the end video to make things smoother:
ffprobe -count_frames -show_entries stream=nb_read_frames -of csv=p=0 input_video.webm
This will give you the number of frames. You will need to subtract 2 (end_frame trimming doesn't take negative numbers) for the command below (1 if not using interpolation).
ffmpeg -i input_video.webm -vf "trim=end_frame=<frame count - 2>,setpts=PTS-STARTPTS" output_video.webm

I Want To Add Transformation/Something Complicated To My Video!

Remember that you can take your last video frame, inpaint it to look like what you want in the end, and then have WAN try to reach that end state.

Under Type Switch, change to FLF2V. Drag your desired end frame into the Last-Frame-Image section, then generate. WAN will attempt to reach that frame in the generation time allotted, you will want to play around with various durations until you get a result you like.

If you want a longer sequence, you can successively inpaint midpoint frames and generate many short segments using each frame as the last frame.

I Want Complicated Movement In My Video!

Depending on your desired movement, you may find it easier to generate many short segments and then concatenate them. For small movements a video length of 1 second will be OK.

Be very descriptive and keep trying until you get the movement you want for each segment.

For speed, switch Resolution to Preview and change control_after_generate to fixed.
Manually increment the seed per generation until you get the result you want, then restore the Resolution setting and generate.

I'm Not Getting Enough Movement In My Video!

I've found that being very explicit in what you want to see is the best way to get larger amounts of movement. Consider the following parts of a prompt:
The man is thrusting in and out of the female quickly.
vs
The man is thrusting in and out of the female quickly with long, deep strokes. For each thrust, he ends up as deep as possible inside the female before withdrawing to the maximum extent possible without losing penetration.

The second prompt results in significantly more movement.

I Want A Character To Stop Moving In My Video!

Unfortunately this is the one thing I haven't had success with, even trying regular WAN.

No matter how much I positive prompt characters remaining still and negative prompt movement, they still move. If you find something that works for this please message me on e6ai.

The Model Keeps Confusing Which Character Is Which!

I've found that using very generic, but differing, nouns for each character helps with this. For example, refer to a male character as the man and the female character as the female for a video that's M/F. Alternatively for that same scenario, the male paired with the woman would work.

I Want To Make a 3D Stereogram!

  1. Clone https://github.com/VisionDepth/VisionDepth3D (you do not need to download their installers or anything, though you can if you want. They're Windows only.)
    • You do not actually need conda to use this.
  2. Download required weights: See https://github.com/VisionDepth/VisionDepth3D/blob/Main-Stable/weights/WEIGHTS_README_PLACEHOLDER.md
  3. Place weights in VisionDepth3D/weights with the directory structure defined in that README file
  4. Open a terminal under the VisionDepth3D directory
    • python3 -m venv venv
    • source venv/bin/activate
    • pip install -r requirements.txt
    • pip install torchvision
    • python3 VisionDepth3D.py

In the UI:

  1. Use Depth Estimation to process the video
    • Select a model from the dropdown
    • Local model detection is finnicky - it will auto-download one for you if needed
    • Output to the VisionDepth3D directory
    • This should take only a couple minutes at the most.
  2. Use 3D Video Generator to generate the video
    • Input Video is the original video
    • Depth Map is the video created in the Depth Estimation step
    • Output Video is your desired output path
    • Open Encoder Settings and select Vertical aspect ratio (9:16), check the box to use ffmpeg, select an NVENC codec (I used H264 for maximum compatibility)
    • Open Processing Options, check the box for Preserve Original Aspect Ratio, UNCHECK the box for Floating Dynamic Window
    • Click the Generate 3D button to generate the video. This should also take only a couple minutes at the most.

If you get intruding black bars on your generated video, disable Floating Dynamic Window as noted above.
Generated videos with this program will have some anomalies on the left side of the right stereogram image due to the stereogram process. They may not be suitable for posting to e6ai, as I have had posts rejected for these anomalies.

I Have Multiple GPUs and Want To Speed Things Up!

There exist ComfyUI extensions such as ComfyUI-MultiGPU V2. These add custom nodes that allow you to select a particular CUDA device. Once installed, I recommend manually editing a copy of the workflow JSON to use these custom nodes instead of the default loader ones.

Unfortunately, I have not had success with these extensions. I tried a setup with 3x A4500, with the High model on one, Low model on another, and the third GPU using the text encoder/VAE/others. While the preview image looked promising, the actual video contents were just a black screen. I was able to confirm via GPU utilization that the models were being allocated on the appropriate device, so I don't know what happened. The 2 A4500s with the High and Low model were joined by NVLink, so I don't know if that affects things.

If you find a working setup for this please message me on e6ai.

I Want To Automate Video Generation!

ComfyUI has a pretty robust API you can use to drive everything. Set up the workflow how you would like to use it for generation, then click File > Export (API). You will get a minimal JSON file containing only the nodes in use and their settings as they were when you exported.

You can then programmatically alter the JSON fields as desired before submitting them to the web interface. Generate a Client ID (just a UUID) and connect via websocket to determine when a submitted job is completed.

I've noticed ComfyUI tends to leak memory when driven this way, as opposed to when driven via the web interface. It's possible I'm missing some necessary cleanup steps.
If using the 6.1 workflow, generate your own random number and replace the seed. Otherwise, whatever number was generated at the time of your workflow API export will be used as the seed for all generations, as the randomize/increment/etc features of the workflow modify the seed after its use (for the next generation) and don't affect the current generation.

Useful REST endpoints (see the ComfyUI docs for actual documentation on these):
/upload/image - Upload an image for use in a job.
/prompt - Queue a job, takes serialized API JSON and a client ID.
/history/<prompt id> - Get output for a completed job.
/api/manager/reboot - Reboot ComfyUI's server. Note that while this will release RAM, it will not release VRAM. Additionally, you will get no response so if your HTTP library expects one, you need to handle the resulting exception appropriately.

There's plenty of example code out there for driving these endpoints.

After Generating Videos, My Browser Is Slow/A1111 Is Laggy!

If you terminate the ComfyUI server but keep the browser tab open, it will constantly be trying to reconnect to ComfyUI. This can cause other tabs to lag significantly, this can be particularly noticeable with A1111. Close the ComfyUI tab and you'll see things go back to normal.

I'm Generating On a Phone/Using Grok or SORA or Another Service To Generate and Can't Run ffmpeg!

You can use an online ffmpeg service, right in your browser! https://ffmpeg-online.vercel.app/ provides an in-browser implementation of ffmpeg. There are also other services that do the same thing online. They may cause some decent battery drain and heat generation, so make sure you're not on low power and can set down your phone for a few minutes if needed.

To replicate the Extract Last Frame functionality in ComfyUI, construct the following input for these services:
ffmpeg -sseof -3 -i input_video.webm -vsync 0 -q:v 31 -update true last_frame.jpg

It'll extract every frame from the last 3 seconds of the video, overwriting last_frame.jpg every time so it's left with the last frame when it finishes running. You may be able to get away with seeking to the last second only (-sseof -1), but online sources I found indicated the last 3 seconds was most stable for general use.

Once you have that last frame, feed it into the next generation. If using a service that provides audio to your videos, I recommend stripping that audio:
ffmpeg -i input_video.webm -an -c:v copy output_video.webm

and then overlaying new audio of your choice on top (if desired) to the completed video:
ffmpeg -i input_video.webm -i input_audio.mp3 -c:v copy -map 0:v -map 1:a output_video_with_audio.webm

Otherwise, you'll get audio sync issues.

Give Me a Convenience Script To Trim and Combine My Segments!

These scripts will trim 2 frames from all but the first video, then concatenate all the segments together:

Linux (ffmpeg_trim_concat_segs.sh):

#!/usr/bin/env bash

set -e

if [ "$#" -lt 1 ]; then
  echo "Usage: $0 video1.mp4 video2.mp4 [video3.mp4 ...]"
  exit 1
fi

# Create a temporary file list
LIST_FILE="$(mktemp temp.XXXX)"
echo "$LIST_FILE"

# Ensure cleanup on exit
trap 'rm -f "$LIST_FILE"' EXIT

ESCAPED_FILE=${1//\'/\'\\\'\'}
echo "file '$ESCAPED_FILE'" >> "$LIST_FILE"

for INPUT in "${@:2}"; do
  if [ ! -f "$INPUT" ]; then
    echo "Error: file not found: $INPUT"
    exit 1
  fi

  BASENAME="${INPUT%.*}"
  EXT="${INPUT##*.}"
  OUTPUT="${BASENAME}_trimmed.${EXT}"
  ESCAPED_FILE=${OUTPUT//\'/\'\\\'\'}

  echo "Trimming first 2 frames from: $INPUT"

  ffmpeg -y    -i "$INPUT"    -vf "trim=start_frame=2,setpts=PTS-STARTPTS"    "$OUTPUT"

  echo "Written: $OUTPUT"
  echo "file '$OUTPUT'" >> "$LIST_FILE"
done

# Concatenate videos
OUTPUT="${BASENAME}_combined.${EXT}"
ffmpeg -f concat -safe 0 -i "$LIST_FILE" -c copy "${OUTPUT}"

echo "Done! Output written to $OUTPUT"

Windows (ffmpeg_trim_concat_segs.bat, converted using ChatGPT, untested, please contact me if this has issues):

@echo off
setlocal EnableDelayedExpansion

REM Require at least one argument
if "%~1"=="" (
    echo Usage: %~nx0 video1.mp4 video2.mp4 [video3.mp4 ...]
    exit /b 1
)

REM Create temporary list file
set LISTFILE=%TEMP%\ffmpeg_concat_%RANDOM%.txt
echo %LISTFILE%

REM Add first file to list (no trimming)
echo file '%~1' >> "%LISTFILE%"

REM Process remaining arguments
shift

:loop
if "%~1"=="" goto done_processing

if not exist "%~1" (
    echo Error: file not found: %~1
    exit /b 1
)

set "INPUT=%~1"
set "BASENAME=%~dpn1"
set "EXT=%~x1"
set "OUTPUT=%BASENAME%_trimmed%EXT%"

echo Trimming first 2 frames from: %INPUT%

ffmpeg -y ^
    -i "%INPUT%" ^
    -vf "trim=start_frame=2,setpts=PTS-STARTPTS" ^
    "%OUTPUT%"

if errorlevel 1 exit /b 1

echo Written: %OUTPUT%
echo file '%OUTPUT%' >> "%LISTFILE%"

shift
goto loop

:done_processing

REM Use last processed file name to build combined name
set "FINAL_BASE=%BASENAME%"
set "FINAL_EXT=%EXT%"
set "FINAL_OUTPUT=%FINAL_BASE%_combined%FINAL_EXT%"

ffmpeg -f concat -safe 0 -i "%LISTFILE%" -c copy "%FINAL_OUTPUT%"

if errorlevel 1 exit /b 1

echo Done! Output written to %FINAL_OUTPUT%

del %LISTFILE%

endlocal
Edit

Pub: 02 Feb 2026 00:35 UTC

Edit: 10 Mar 2026 00:00 UTC

Views: 507