Create your own previews for Premiere Composer and Animation Composer
Steps to create your own previews for Premiere Composer and Animation Composer extensions from Mister Horse.
Mister Horse created two great extensions, Animation Composer and Premiere Composer, for Adobe After Effects and Premiere Composer, respectively. Operationally and visually they are nearly identical, which is a nice consistency. They have a very good feature for keeping user libraries of assets with fast-loading previews. Perhaps my favorite feature is that when you select a folder in the extension's User Library section, it presents previews for not only the files in that folder but also all of the files in that folder's subfolders. You can navigate to any of the subfolders to see only the files in that subfolder (and all files in that subfolder's subfolders).
They are very good extensions, but there are limitations. Probably due to licensing costs, Mister Horse changed the User Library's preview renderer to create previews for only H.264 files, and to keep the extensions nimble and quick the previews only present 10 frames max across the duration of the video. In a professional postproduction workflow, we get every flavor of camera, codec and color space under the sun, so the extension is mostly useless for an actual user library. The solutions include: 1. Convert everything to H.264, or 2. Render your own previews. Converting everything to H.264 is impractical or unworkable for most post houses and a negative hit on quality, so that leaves rendering your own previews.
Req'd:
1. ffmpeg 8.0 or later (download portable (currently version 8.0.1) from https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z )
note: the path to the folder containing "ffmpeg.exe" must be added to the "Path" Environmental Variable (see Path instruction (step 3))
2. Python (currently 3.12)
- search Microsoft Store for Python 3.12 or later and install
- note: install from Microsoft Store will need Windows 11 App settings modification:
- Go to Settings → Apps → Advanced app settings → App Execution Aliases.
- Toggle Off all Python (look in subtitle, too), including:
- IDLE (Python 3.12) > idle.exe
- IDLE (Python 3.12) > idle3.exe
- IDLE (Python 3.12) > idle3.12.exe
- Python 3.12 > python.exe
- Python 3.12 > python3.exe
- Python 3.12 > python3.12.exe
- Python 3.12 (Windowed) > pythonw.exe
- Python 3.12 (Windowed) > pythonw3.exe
- Python 3.12 (Windowed) > pythonw3.12.exe
- App Installer > python.exe
- App Installer > python3.exe
- pip (Python 3.12) > pip.exe
- pip (Python 3.12) > pip3.exe
- pip (Python 3.12) > pip3.12.exe
- then toggle ON only the one you want (usually)
- Python 3.12 > python.exe
- Python 3.12 > python3.exe
- this is due to a (currently) known issue with the Microsoft Store version of Python being sneaky about the "Stubs" it creates in the C:\Users\ folder.
- or, download and install the official version of Python from python.org/downloads
- note: install of the official version will need the path to the folder containing "python.exe" (or python3.12.exe) to be added to the "Path" Environmental Variable
3. Edit the Path variable in Environmental Variables
- Open Windows 11 Settings -> System -> About (at bottom of the list), click the "Advanced system settings" tab in the horizontal row of “Related links” in the middle of the page to open the System Properties window
- Click "Environmental Variables..." in the lower right corner to open the Environmental Variables window
- In the top section titled "User Variables for [you]", double click on "Path" in the left column to open its editing window, "Edit environmental variable”, which gives a list of paths currently defined. Leave this window alone for a minute.
- Open File Explorer and navigate to the location of ffmpeg.exe – <ctrl><c> copy the path to ffmpeg.exe from the address bar of File Explorer and return to the "Edit environmental variable” that we left alone in step 3
- Click the “New…” button to the right of the list of paths
- That will highlight the next available blank row in the list, <ctrl><v> Paste the path we copied in step 4
- e.g. My ffmpeg is right where I download it, so I copy/pasted (no quotes) "E:\Downloads\Programs\Video Encoding\ffmpeg-8.0.1-full_build\bin"
- Since I have the Microsoft Store version of Python installed, I do not need to add Python to the "Path" variable (but I did need to change the App Execution Aliases in Win11 settings). If you install the official version instead of the Microsoft Store version then repeat steps 4 through 7 but paste the path to the installed location of python3.12.exe, or python.exe.
They both require the same amount of work to set up, so the choice is yours. The Microsoft Store version doesn’t send you through the Administrator Rights headache, which is why I went with that one.
4. Run the Script
- See below
Is it worth the effort?
Obviously, I think so, or I wouldn't have spent an entire day writing and debugging these scripts, but it depends on your workflow. I use these extensions as tools for both editing and digital asset management, and these scripts extend the functionality of the Mister Horse extensions to non-H.264 files. The extensions provide quick access to previews of files without needing to import them and without leaving the Adobe workspace. Scanning large client b-roll libraries, sound effects, stock footage, etc. and then dragging and dropping into the timeline (also imports the file) is a breeze. Double clicking the preview loads the full rez clip into the source monitor but doesn’t import the clip into the project until you insert any portion of the clip into the timeline. That’s probably my most used feature because it also puts the clip on the full screen Playback and client monitors (Mercury Transmit).
On the digital asset management side, I can render previews for every file on a backup drive and store them on that drive before putting it on the shelf. Then, if a few files from the backup drive need to be restored, I simply mount the drive and add the needed job/project folder(s) back to the user library in the extension. The extension finds the previews, and they are instantly available. I use 10TB drives for back-up, so rather than 1 monster directory of previews, I create them the same way I back everything up – by project folder. When the project folder is sent to the backup, the project root folder will contain a “_Mister Horse Previews” folder which contains previews for only the video files in that project and that project’s subfolders.
But it's only 10 frames
10-frame previews are surprisingly helpful for single shots, but they fall short for longer sequences and edited source files. Animation Composer for After Effects uses a tiled PNG (1 column × 10 rows, 320 px × 1800 px) that allows scrubbing through the 10 frames. Premiere Composer, uses WebP files for previews. WebP offers more flexibility because in addition to static tiled WebP images, Premiere Composer unoficially supports animated WebP files (similar to GIFs), enabling previews that include every nth frame across the entire video. My script captures three frames per second and plays them back at the same rate. The key difference: tiled previews allow scrubbing but don’t play, while animated WebP previews loop smoothly but cannot be scrubbed. For assessing pacing and duration, the animated option is far more representative. Both tiled and animated webp previews in Premiere Composer will load the full rez media in the source monitor with a double click on the preview. In the source monitor you can play and scrub the real thing..
Which version - tiled or animated webp?
I use the animated webp during active projects and for long-standing libraries. When the gig is moved to offline storage I can choose to replace them with tiled versions. Both scripts for creating the tiled versions of png and WebP work very quickly compared to my previous versions. The efficiency comes from avoiding multiple decodes—10 times from the beginning—to select the 10 frames to use in the preview. This saves considerable time, especially on longer clips.
Animated WebP previews take longer to create but require only one decode using an fps=3 parameter, making them practical for comprehensive previews.
How fast?
Here’s a performance comparison for generating previews in PNG (tiled) and animated WebP formats. From 1,568 source files across 28 subfolders—a mix of .mxf, .mov, and .mp4 formats—totaling 414 GB. The process produced 3,112 preview files (≈2.15 GB) in the correct directory structures
. Twelve files failed in each batch due to bad encodes or corruption.
- PNG tiled previews: 66 minutes, slightly over 1 GB of storage
- Animated WebP previews: 3.5 hours, slightly under 1 GB of storage
Interestingly, static PNG previews consume more space than animated WebP files. Each format is generated by a slightly different script. The test system is an Intel 7960X, 16 core/32 thread @2.8 MHz/Turbo boost to 4.1 MHz, 128 GB DDR4-3600, and Asus TUF RTX-4090 GPU (PCIE 3.0 x8) on an Asus WS X299 Sage motherboard. Files were served up from, and saved to, an internal Raid 0 of (4) 2TB 970EVO M.2 nVME’s.
Where are the files stored?
The scripts store the preview files in a sub folder of the same directory from which the script is run. The subfolder is named “_Mister Horse Previews”. The script looks in the directory from which it is run for video files, including in all subdirectories, processes them, and writes the result in the subfolder named “_Mister Horse Previews”. The script also mirrors the source’s folder structure in the previews folder which insures proper linking to the media. More details about running the script are below.
Running the Script?
For Mister Horse extensions to recognize previews, they must reside in a subfolder named _Mister Horse Previews within the same directory as the User Library folder. For example, if you add:
"H:\Stock Footage"
as a User Librayin Animation Composer or Premiere Composer, previews should be stored in:
"H:\Stock Footage\_Mister Horse Previews"
The script creates this folder, if needed, and then it mirrors the original folder hierarchies of the source file inside the _Mister Horse Previews directory and saves the preview there. For instance, the preview for:
"H:\Stock Footage\Aerials\POVs\pov_cockpit.mxf"
would be located at:
"H:\Stock Footage\_Mister Horse Previews\Aerials\POVs\pov_cockpit.mxf.webp"
Note the insert location of the Mister Horse Previews folder, the mirroring of the source file’s folder hierarchy, and the addition of the .webp file extension at the end. This ensures all nested subfolders and files are properly linked and previews remain organized. The scripts handle all of that. I just wanted to explain what was going on when the scripts are run.
But you need to run the script from the proper location. Expanding on the example, here is how to create previews for all of the video files in the H:\Stock Footage folder, including subfolders:
- Copy the files: “png_scrub_pv.py”, “webp_scrub_pv.py”, and “webp_anim_pv.bat” to “H:\Stock Footage” (get the files below)
- Open a cmd prompt window and change to the drive and directory of the source files, in our case, at the C:\> prompt we would type H: <enter>
- The prompt changes to H:\>, and we type, cd “H:\Stock Footage” note: we need the quote marks around H:\Stock Footage because of the space between Stock and Footage in the folder name.
- The prompt changes to H:\Stock Footage>
Finally, run the script for the desired flavor of preview –
- For scrubbable 10 frame png previews for use in Animation Composer (AE), type:
python png_scrub_ppv.py
- For scrubbable 10 frame webp previews for use in Premiere Composer (PrPro), type:
python webp_scrub_pv.py
- For animated webp previews of the same length as the clip (@3 fps) for use in Premiere Composer (PrPro), type:
webp_anim_pv.bat
On average the tiled preview files took approx. 2.5 seconds each, and the animated preview files took approx. 8 seconds each.
And here are the scripts:
Script 1: "png_scrub_pv.py"
– copy the block of text below, paste it in notepad and save as “png_scrub_pv.py” .
# png_scrub_pv.py
# 1×10 tiled PNG preview generator for Animation Composer
# Requires: Python 3.8+ and FFmpeg in PATH
# Tested on Windows 11 + 4×970 EVO RAID-0
import os
import time
import subprocess
from pathlib import Path
# ==================== CONFIG ====================
SOURCE_ROOT = Path.cwd() # Change if needed: Path("H:/__Stock Footage__")
DEST_ROOT = SOURCE_ROOT / "_Mister Horse Previews" # Previews saved under source root in this subfolder
NUM_FRAMES = 10
SCALE_FILTER = "scale='min(320,iw*180/ih)':'min(180,ih*320/iw)':flags=lanczos"
VIDEO_EXTS = {'.mp4','.mov','.mxf','.mkv','.avi','.mpg','.mpeg','.m2ts','.mts','.ts','.vob','.m4v','.webm','.flv','.ogv'}
# ================================================
def get_duration(video_path):
cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'csv=p=0', str(video_path)]
try:
return float(subprocess.run(cmd, capture_output=True, text=True, check=True).stdout.strip())
except:
return 0
def extract_frame(video_path, ts, out_path):
cmd = [
'ffmpeg', '-hide_banner', '-loglevel', 'error',
'-ss', f"{ts:.6f}", '-i', str(video_path),
'-vf', SCALE_FILTER, '-frames:v', '1', '-y', str(out_path)
]
subprocess.run(cmd, check=False)
def create_tiled_png(temp_dir, output_png):
files = sorted(temp_dir.glob("frame_*.png"))
n = len(files)
if n == 0:
return False
concat = temp_dir / "concat.txt"
with open(concat, "w", encoding="utf-8") as f:
for p in files:
f.write(f"file '{p.as_posix()}'\n")
f.write("duration 1\n")
if files:
f.write(f"file '{files[-1].as_posix()}'\n")
cmd = [
'ffmpeg', '-hide_banner', '-loglevel', 'error',
'-f', 'concat', '-safe', '0', '-i', str(concat),
'-vf', f"tile=1x{n}:margin=0:padding=0:color=black",
'-frames:v', '1', '-y', str(output_png)
]
try:
subprocess.run(cmd, check=True)
return True
finally:
concat.unlink(missing_ok=True)
def process_video(video_path, out_dir):
duration = get_duration(video_path)
if duration < 0.5:
print(f" Skipping {video_path.name} – too short")
return
interval = duration / (NUM_FRAMES - 1)
temp_dir = out_dir / f"{video_path.stem}_temps"
temp_dir.mkdir(exist_ok=True)
# Launch all 10 extractions
for i in range(NUM_FRAMES):
raw_ts = i * interval
ts = min(raw_ts, duration - 0.101) if i == NUM_FRAMES - 1 else raw_ts
png = temp_dir / f"frame_{i:02d}.png"
extract_frame(video_path, ts, png)
print(f" Frame {i+1:2}/10 at {ts:6.2f}s → launched")
# Wait for all frames to land
deadline = time.time() + 15
while time.time() < deadline:
ready = [f for f in temp_dir.glob("frame_*.png") if f.stat().st_size > 2000]
if len(ready) == NUM_FRAMES:
print(" All 10 frames ready → tiling PNG")
break
print(f" Waiting… {len(ready)}/10", end="\r")
time.sleep(0.05)
# Create tiled PNG
final = out_dir / f"{video_path.stem}{video_path.suffix}.png"
if create_tiled_png(temp_dir, final):
print(f" → Created {final.name}")
else:
print(f" Failed to create {final.name}")
# Cleanup
for f in temp_dir.iterdir():
f.unlink(missing_ok=True)
try:
temp_dir.rmdir()
except:
pass
def main():
processed = 0
print("1×10 tiled PNG preview generator for Animation Composer")
print(f"Source : {SOURCE_ROOT}")
print(f"Output : {DEST_ROOT}\n")
for root, _, files in os.walk(SOURCE_ROOT):
rel = Path(root).relative_to(SOURCE_ROOT)
out_dir = DEST_ROOT / rel
out_dir.mkdir(parents=True, exist_ok=True)
for file in files:
if Path(file).suffix.lower() in VIDEO_EXTS:
vp = Path(root) / file
print(f"\nProcessing {vp.name}")
process_video(vp, out_dir)
processed += 1
print(f"\nFINISHED – {processed} tiled PNG previews created.")
if __name__ == "__main__":
main()
Script 2 - "webp_scrub_pv.py"
– copy the block of text below, paste it in notepad and save as “webp_scrub_pv.py”.
# webp_scrub_pv.py
# 1×10 tiled WebP preview generator for Premiere Composer
# Requires: Python 3.8+ and FFmpeg in PATH
# Tested on Windows 11 + 4×970 EVO RAID-0
import os
import time
import subprocess
from pathlib import Path
# ==================== CONFIG ====================
SOURCE_ROOT = Path.cwd() # Change if needed: Path("H:/__Stock Footage__")
DEST_ROOT = SOURCE_ROOT / "_Mister Horse Previews" # Previews saved under source root in this subfolder
NUM_FRAMES = 10
SCALE_FILTER = "scale='min(320,iw*180/ih)':'min(180,ih*320/iw)':flags=lanczos"
VIDEO_EXTS = {'.mp4','.mov','.mxf','.mkv','.avi','.mpg','.mpeg','.m2ts','.mts','.ts','.vob','.m4v','.webm','.flv','.ogv'}
# ================================================
def get_duration(video_path):
cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'csv=p=0', str(video_path)]
try:
return float(subprocess.run(cmd, capture_output=True, text=True, check=True).stdout.strip())
except:
return 0
def extract_frame(video_path, ts, out_path):
cmd = [
'ffmpeg', '-hide_banner', '-loglevel', 'error',
'-ss', f"{ts:.6f}", '-i', str(video_path),
'-vf', SCALE_FILTER, '-frames:v', '1', '-y', str(out_path)
]
subprocess.run(cmd, check=False)
def create_tiled_webp(temp_dir, output_webp):
files = sorted(temp_dir.glob("frame_*.png"))
n = len(files)
if n == 0:
return False
concat = temp_dir / "concat.txt"
with open(concat, "w", encoding="utf-8") as f:
for p in files:
f.write(f"file '{p.as_posix()}'\n")
f.write("duration 1\n")
if files:
f.write(f"file '{files[-1].as_posix()}'\n")
cmd = [
'ffmpeg', '-hide_banner', '-loglevel', 'error',
'-f', 'concat', '-safe', '0', '-i', str(concat),
'-vf', f"tile=1x{n}:margin=0:padding=0:color=black",
'-c:v', 'libwebp', '-quality', '85', '-compression_level', '6',
'-frames:v', '1', '-y', str(output_webp)
]
try:
subprocess.run(cmd, check=True)
return True
finally:
concat.unlink(missing_ok=True)
def process_video(video_path, out_dir):
duration = get_duration(video_path)
if duration < 0.5:
print(f" Skipping {video_path.name} – too short")
return
interval = duration / (NUM_FRAMES - 1)
temp_dir = out_dir / f"{video_path.stem}_temps"
temp_dir.mkdir(exist_ok=True)
# Launch all 10 extractions
for i in range(NUM_FRAMES):
raw_ts = i * interval
ts = min(raw_ts, duration - 0.101) if i == NUM_FRAMES - 1 else raw_ts
png = temp_dir / f"frame_{i:02d}.png"
extract_frame(video_path, ts, png)
print(f" Frame {i+1:2}/10 at {ts:6.2f}s → launched")
# Wait for all frames to land
deadline = time.time() + 15
while time.time() < deadline:
ready = [f for f in temp_dir.glob("frame_*.png") if f.stat().st_size > 2000]
if len(ready) == NUM_FRAMES:
print(" All 10 frames ready → tiling WebP")
break
print(f" Waiting… {len(ready)}/10", end="\r")
time.sleep(0.05)
# Create tiled WebP
final = out_dir / f"{video_path.stem}{video_path.suffix}.webp"
if create_tiled_webp(temp_dir, final):
print(f" → Created {final.name}")
else:
print(f" Failed to create {final.name}")
# Cleanup
for f in temp_dir.iterdir():
f.unlink(missing_ok=True)
try:
temp_dir.rmdir()
except:
pass
def main():
processed = 0
print("1×10 tiled WebP preview generator for Animation Composer")
print(f"Source : {SOURCE_ROOT}")
print(f"Output : {DEST_ROOT}\n")
for root, _, files in os.walk(SOURCE_ROOT):
rel = Path(root).relative_to(SOURCE_ROOT)
out_dir = DEST_ROOT / rel
out_dir.mkdir(parents=True, exist_ok=True)
for file in files:
if Path(file).suffix.lower() in VIDEO_EXTS:
vp = Path(root) / file
print(f"\nProcessing {vp.name}")
process_video(vp, out_dir)
processed += 1
print(f"\nFINISHED – {processed} tiled WebP previews created.")
if __name__ == "__main__":
main()
Script 3 - "webp_anim_pv.bat"
– copy the block of text below, paste it in notepad and save as “webp_anim_pv.bat”.
@2846721 off
setlocal EnableDelayedExpansion
rem === CONFIG ===
rem Automatically uses the folder where this .bat lives (or where you run it from)
set "SourceRoot=%~dp0"
set "PreviewFolder=_Mister Horse Previews"
rem === All common video extensions (case-insensitive) ===
set "Exts=*.mp4 *.mov *.mxf *.mkv *.avi *.mpg *.mpeg *.m2ts *.mts *.ts *.vob *.m4v *.webm *.flv *.ogv"
echo.
echo Mister Horse Animated WebP Preview Generator
echo Source folder : %SourceRoot%
echo Preview folder: %PreviewFolder% (created automatically)
echo.
for /R "%SourceRoot%" %%F in (%Exts%) do (
echo Processing "%%~nxF"
rem Build relative path and output directory
set "RelPath=%%~dpF"
set "RelPath=!RelPath:%SourceRoot%=!"
set "OutDir=%SourceRoot%%PreviewFolder%!RelPath!"
rem Create output directory if needed
if not exist "!OutDir!" mkdir "!OutDir!" >nul
rem Generate animated WebP (your perfect settings)
ffmpeg -hide_banner -loglevel error -i "%%F" ^
-vf "fps=3,scale='min(320,iw*180/ih)':'min(180,ih*320/iw)':flags=lanczos" ^
-c:v libwebp ^
-lossless 0 ^
-compression_level 6 ^
-quality 80 ^
-loop 0 ^
-preset picture ^
-an -vsync 0 ^
-y "!OutDir!%%~nF%%~xF.webp"
)
echo.
echo FINISHED - All animated WebP previews created in "_Mister Horse Previews"
echo.
pause
Have fun!