Files
workbuddy_skills/video-capture/scripts/fetch_video.py
T
admin 051482168f video-capture:入仓(加入 .gitignore 白名单)—— 取视频 + 抽帧的全局技能
- 用户 2026-10-12 令「可以加入」。
- 依据:product-planning 第③段的交接说明已把它当**依赖**指向 ⇒ 仓里有引用、仓里就该有实体,
  ⛔ 否则从本仓还原到别的机器会拿到一个「指向装不出来的技能」的悬空引用。
- 收录 5 个:SKILL.md、scripts/extract_frames.py、scripts/fetch_video.py、
  references/视频抽帧与字幕规范.md、ATTRIBUTION.md。
- 目录内 env/ 与 logs/ 仍按本机配置规则挡在仓外(各机器自建)。
2026-10-12 09:56:59 +08:00

86 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
video-capture · 取视频(最小、合规集)
只做两件事,用于把「一个视频」落到本地待抽帧:
1) 本地文件:直接登记(--file)
2) 直链媒体 URL:下载(--url,需 .mp4/.mov/.webm/.m4v 等直链)
⛔ 不做:平台页内抓取(抖音/B站/YouTube 等页面解析)、⛔ 不调任何付费接口。
—— 如需平台视频,请先用浏览器工具把视频下到本地,再用 --file 传入(本工具不代抓)。
依赖:ffprobe(校验)+ Python 标准库。可选 curl(更快)。
用法:
python fetch_video.py --file D:/a.mp4 --out ./video
python fetch_video.py --url https://host/x.mp4 --out ./video
"""
import argparse
import json
import os
import shutil
import subprocess
import sys
import urllib.request
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/126.0 Safari/537.36"
def probe(path):
try:
r = subprocess.run(
["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-show_entries", "stream=codec_type,width,height", "-of", "json", path],
capture_output=True, text=True, encoding="utf-8", errors="replace")
j = json.loads(r.stdout or "{}")
dur = float((j.get("format") or {}).get("duration") or 0)
w = h = 0
for s in (j.get("streams") or []):
if s.get("codec_type") == "video":
w, h = int(s.get("width") or 0), int(s.get("height") or 0)
break
return {"duration": round(dur, 3), "resolution": f"{w}x{h}"}
except Exception:
return {}
def download(url, dest):
req = urllib.request.Request(url, headers={"User-Agent": UA, "Referer": url})
with urllib.request.urlopen(req, timeout=90) as r, open(dest, "wb") as f:
shutil.copyfileobj(r, f)
def main():
ap = argparse.ArgumentParser()
g = ap.add_mutually_exclusive_group(required=True)
g.add_argument("--file", help="本地视频路径")
g.add_argument("--url", help="直链媒体 URL(.mp4/.mov/.webm/.m4v)")
ap.add_argument("--out", default="video", help="输出目录")
args = ap.parse_args()
os.makedirs(args.out, exist_ok=True)
if args.file:
if not os.path.exists(args.file):
print(json.dumps({"ok": False, "error": f"找不到文件:{args.file}"}), file=sys.stderr)
sys.exit(2)
dest = os.path.join(args.out, os.path.basename(args.file))
if os.path.abspath(dest) != os.path.abspath(args.file):
shutil.copy2(args.file, dest)
else:
if not os.path.splitext(args.url.split("?")[0])[1].lower() in (".mp4", ".mov", ".webm", ".m4v", ".mkv"):
print(json.dumps({"ok": False, "error": "仅支持直链媒体 URL(.mp4/.mov/.webm/.m4v/.mkv);平台页内抓取不在本工具范围"}), file=sys.stderr)
sys.exit(2)
dest = os.path.join(args.out, "video" + os.path.splitext(args.url.split("?")[0])[1].lower())
try:
download(args.url, dest)
except Exception as e:
print(json.dumps({"ok": False, "error": f"下载失败:{e}"}), file=sys.stderr)
sys.exit(1)
meta = probe(dest)
print(json.dumps({"ok": True, "video": os.path.abspath(dest), **meta}, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()