diff --git a/directory_scanner.py b/directory_scanner.py
index 3b47a21..ba6a5cf 100644
--- a/directory_scanner.py
+++ b/directory_scanner.py
@@ -317,6 +317,8 @@ def _load_existing_manifest_items(directory_path):
# =============================================================================
IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".bmp"}
+VIDEO_EXTENSIONS = {".mp4", ".webm", ".mov", ".m4v"}
+MEDIA_EXTENSIONS = IMAGE_EXTENSIONS | VIDEO_EXTENSIONS
def scan_directory_for_images(directory_path, max_items=5000, view_subfolder=""):
@@ -356,7 +358,7 @@ def scan_directory_for_images(directory_path, max_items=5000, view_subfolder="")
for entry in os.scandir(scan_dir):
if entry.is_file():
ext = os.path.splitext(entry.name)[1].lower()
- if ext in IMAGE_EXTENSIONS:
+ if ext in MEDIA_EXTENSIONS:
image_files.append(entry.path)
except PermissionError:
raise ValueError(f"Permission denied: {directory_path}")
@@ -407,9 +409,11 @@ def scan_directory_for_images(directory_path, max_items=5000, view_subfolder="")
def _process_single_image(file_path, directory_path, view_subfolder=""):
"""
- Process a single image file into a manifest-compatible item dict.
+ Process a single image or video file into a manifest-compatible item dict.
"""
filename = os.path.basename(file_path)
+ ext = os.path.splitext(filename)[1].lower()
+ is_video = ext in VIDEO_EXTENSIONS
# Generate unique ID (matching existing manifest pattern)
ts = int(time.time() * 100000) + random.randint(0, 1000)
@@ -418,6 +422,32 @@ def _process_single_image(file_path, directory_path, view_subfolder=""):
from urllib.parse import quote
file_url = f"/view?filename={quote(filename, safe='')}&type=output&subfolder={quote(view_subfolder, safe='/')}"
+ if is_video:
+ # Videos: browser will update dimensions on loadedmetadata — use 16:9 default
+ return {
+ "id": ts,
+ "file": file_url,
+ "media_type": "video",
+ "width": 1280,
+ "height": 720,
+ "seed": 0,
+ "steps": 0,
+ "cfg": 0,
+ "sampler": "video",
+ "scheduler": "video",
+ "positive": "",
+ "negative": "",
+ "denoise": 1,
+ "model": "Video",
+ "lora": "None",
+ "clip_skip": -1,
+ "duration": 0,
+ "batch_idx": 0,
+ "rejected": False,
+ "favorited": False,
+ "source_file": filename,
+ }
+
# Get image dimensions from PIL
try:
with Image.open(file_path) as img:
@@ -440,6 +470,7 @@ def _process_single_image(file_path, directory_path, view_subfolder=""):
item = {
"id": ts,
"file": file_url,
+ "media_type": "image",
"width": parsed.get("width") or width,
"height": parsed.get("height") or height,
"seed": parsed.get("seed", 0),
diff --git a/resources/logic_ui.js b/resources/logic_ui.js
index 4a298b4..6cb2456 100644
--- a/resources/logic_ui.js
+++ b/resources/logic_ui.js
@@ -1249,14 +1249,24 @@ function createCard(d) {
const aspectRatio = (d.width && d.height) ? (d.height / d.width) : 1;
const paddingBottom = (aspectRatio * 100).toFixed(2);
+ // Detect video vs image and prepare variants
+ const isVideo = d.media_type === 'video';
+ const mediaElement = isVideo
+ ? ``
+ : ``;
+ const reviseBtn = isVideo ? '' : ``;
+ const upscaleBtn = isVideo ? '' : ``;
+ const videoBadge = isVideo ? '