Files
workbuddy_skills/video-capture/scripts/fetch_video.py
T

85 lines
3.3 KiB
Python
Raw Normal View History

#!/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()