feat: add counting bench, triage, and dataset modules
This commit includes major additions and updates to the frontend and backend architectures, introducing new dataset management, live counting features, batch processing, and triage logic. Includes new UI pages, components, and API routes.
This commit is contained in:
1 parent
8285400254
commit
5c7c122105
80 files changed
+20074
-1412
No files matched your search
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Transcode video archive files to browser-compatible H.264 MP4 format."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
VIDEO_EXTS = {".mp4", ".mkv", ".mov", ".avi", ".webm", ".m4v"}
|
||||
|
||||
def probe_codec(file_path: Path) -> str:
|
||||
cmd = [
|
||||
"ffprobe", "-v", "error", "-select_streams", "v:0",
|
||||
"-show_entries", "stream=codec_name",
|
||||
"-of", "default=noprint_wrappers=1:nokey=1", str(file_path)
|
||||
]
|
||||
res = subprocess.run(cmd, capture_output=True, text=True)
|
||||
return res.stdout.strip().lower()
|
||||
|
||||
def transcode_video(file_path: Path):
|
||||
tmp_path = file_path.with_name(f"_transcoded_{file_path.stem}.mp4")
|
||||
cmd = [
|
||||
"ffmpeg", "-hide_banner", "-loglevel", "error", "-y",
|
||||
"-i", str(file_path),
|
||||
"-c:v", "libx264", "-preset", "fast", "-crf", "22",
|
||||
"-c:a", "aac", "-b:a", "128k",
|
||||
"-movflags", "+faststart",
|
||||
str(tmp_path)
|
||||
]
|
||||
res = subprocess.run(cmd)
|
||||
if res.returncode == 0 and tmp_path.exists() and tmp_path.stat().st_size > 0:
|
||||
# Save as .mp4
|
||||
target_path = file_path.with_suffix(".mp4")
|
||||
if target_path != file_path and file_path.exists():
|
||||
file_path.unlink()
|
||||
shutil.move(str(tmp_path), str(target_path))
|
||||
print(f" ✅ Transcoded -> {target_path}")
|
||||
else:
|
||||
if tmp_path.exists():
|
||||
tmp_path.unlink()
|
||||
print(f" ❌ Failed to transcode {file_path}")
|
||||
|
||||
def process_archive(archive_dir: Path):
|
||||
if not archive_dir.exists():
|
||||
print(f"Archive directory {archive_dir} does not exist.")
|
||||
return
|
||||
|
||||
print(f"Scanning {archive_dir} for non-H.264 videos...")
|
||||
for root, _, files in os.walk(archive_dir):
|
||||
for file in sorted(files):
|
||||
file_path = Path(root) / file
|
||||
if file_path.suffix.lower() in VIDEO_EXTS and not file.startswith("_transcoded_"):
|
||||
codec = probe_codec(file_path)
|
||||
if not codec:
|
||||
print(f"⚠️ Could not probe codec or unreadable: {file_path}")
|
||||
continue
|
||||
if codec != "h264":
|
||||
print(f"🔄 Converting {file_path} (current codec: {codec})...")
|
||||
transcode_video(file_path)
|
||||
else:
|
||||
print(f" ✓ Already H.264: {file_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
target = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("data/archive")
|
||||
process_archive(target)
|
||||
Reference in new issue
Block a user