80 lines
2.3 KiB
Python
80 lines
2.3 KiB
Python
import argparse
|
|
import asyncio
|
|
from pathlib import Path
|
|
|
|
from ..core.config import THUMBS_DIR, YT_DIR
|
|
|
|
|
|
async def run (cmd: list[str]) -> tuple[int, bytes]:
|
|
proc = await asyncio.create_subprocess_exec(
|
|
*cmd,
|
|
stdout=asyncio.subprocess.DEVNULL,
|
|
stderr=asyncio.subprocess.PIPE,
|
|
)
|
|
|
|
_, err = await proc.communicate()
|
|
return proc.returncode, err
|
|
|
|
async def extract_cover(video: Path, out: Path, sem: asyncio.Semaphore) -> bool:
|
|
if ".Trash" in str(video) or any(part.startswith('.') for part in video.parts):
|
|
return False
|
|
|
|
if ".temp." in video.name or ".f" in video.name and video.suffix == ".webm":
|
|
return False
|
|
|
|
async with sem:
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
|
if out.exists() and out.stat().st_size > 0:
|
|
return True
|
|
|
|
code, err = await run([
|
|
"ffmpeg", "-y", "-loglevel", "error",
|
|
"-i", str(video),
|
|
"-map", "0:v:1?",
|
|
"-c", "copy",
|
|
str(out),
|
|
])
|
|
|
|
if code != 0:
|
|
code, err = await run([
|
|
"ffmpeg", "-y", "-loglevel", "error",
|
|
"-i", str(video),
|
|
"-map", "0:v:0?",
|
|
"-c", "copy",
|
|
str(out),
|
|
])
|
|
if code != 0:
|
|
print(f"FAIL {video.name}: {err.decode()[:300]}")
|
|
return False
|
|
|
|
return True
|
|
|
|
async def main(workers: int):
|
|
SEM = asyncio.Semaphore(workers)
|
|
videos = [p for p in YT_DIR.rglob("*") if p.is_file() and p.suffix.lower() in {".mp4", ".mkv", ".webm"}]
|
|
print(f"videos: {len(videos)}")
|
|
|
|
tasks = []
|
|
for video in videos:
|
|
rel = video.relative_to(YT_DIR)
|
|
out = THUMBS_DIR / rel.with_suffix(".jpg")
|
|
tasks.append(extract_cover(video, out, SEM))
|
|
|
|
results = await asyncio.gather(*tasks)
|
|
ok = sum(1 for r in results if r)
|
|
print(f"done: {ok}/{len(results)}")
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(description="Extract video previews")
|
|
parser.add_argument(
|
|
"-w", "--workers",
|
|
type=int,
|
|
default=8,
|
|
help="number of parallel ffmpeg processes (default: 8)",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = parse_args()
|
|
asyncio.run(main(args.workers)) |