Skill: mimose101/xiaomi-touchscreen-alarm-clock-video-production (version 1.1.0)

--- name: bilibili-video-download description: B站视频下载裁剪压缩工具。下载bilibili视频、裁剪去除边框、压缩到指定大小时使用。支持b23.tv短链和BV号。别名：闹钟视频下载。 --- # 闹钟视频下载 ## 依赖 - yt-dlp: `py -m pip install yt-dlp` - ffmpeg/ffprobe: 需在PATH中 ## 工作流 ### 1. 下载720P视频 固定下载720P分辨率视频（格式ID: 30064+30280），文件名使用BV号： ``` py -m yt_dlp -f "30064+30280" -o "PATH/%(id)s.%(ext)s" --merge-output-format mp4 URL ``` 说明: 30064为720P视频流,30280为音频流。使用 `%(id)s` 自动提取BV号作为文件名，避免多个视频下载时文件名冲突。 ### 2. 裁剪边框（可选） 使用 `--crop` 参数指定裁剪区域，默认裁剪参数 `792:600:432:56`（去除B站边框）： ``` ffmpeg -y -i IN -vf "crop=792:600:432:56" -c:v libx264 -crf 18 -c:a copy OUT ``` **720P固定裁剪参数:** `crop=792:600:432:56` 如无需裁剪，传入 `--no-crop`。 ### 3. 剪切视频（可选，默认去除前后各10秒） **步骤1: 去除前10秒** ``` ffmpeg -y -i IN -ss 00:00:10 -c:v libx264 -crf 18 -c:a copy TEMP ``` **步骤2: 获取剩余视频时长** ``` ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 TEMP ``` **步骤3: 去除后10秒（总时长-10）** ``` ffmpeg -y -i TEMP -t (duration-10) -c:v libx264 -crf 18 -c:a copy OUT ``` 说明: 固定去除片头10秒和片尾10秒，总时长减少20秒。如无需剪切，传入 `--no-trim`。 ### 4. 压缩到目标大小 **目标：压缩到10MB以内** **步骤1: 获取视频时长** ``` ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 INPUT_VIDEO ``` **步骤2: 计算动态码率** 使用Python计算码率参数（以目标10MB为例）： ```python target_MB = 10 duration = 217.345783 # 从步骤1获取的实际时长 vbr = max(int((target_MB * 0.9 * 8 * 1024) / duration - 64), 100) # 视频码率 maxrate = int(vbr * 1.25) # 最大码率 bufsize = int(vbr * 2.5) # 缓冲大小 # 示例输出: vbr=275k, maxrate=344k, bufsize=688k ``` 计算公式： - 视频码率: `vbr = max(int((target_MB * 0.9 * 8 * 1024) / duration - 64), 100)` - 最大码率: `maxrate = int(vbr * 1.25)` - 缓冲大小: `bufsize = int(vbr * 2.5)` **步骤3: 执行压缩** ``` ffmpeg -y -i INPUT_VIDEO -c:v libx264 -b:v 275k -maxrate 344k -bufsize 688k -c:a aac -b:a 64k -ar 44100 OUTPUT_VIDEO ``` **验证压缩结果:** ``` ffprobe -v error -show_entries format=size -of default=noprint_wrappers=1:nokey=1 OUTPUT_VIDEO ``` **实际案例参考:** - 输入视频: 792x600, 时长217秒, 约22.4MB - 压缩参数: vbr=275k, maxrate=344k, bufsize=688k - 输出结果: 约9.0MB, 成功压缩到10MB以内 ### 5. 一键脚本 ``` py scripts/download_and_process.py URL [选项] ``` **必选参数:** - `url` — B站视频URL（支持BV号、b23.tv短链） **可选参数:** | 参数 | 默认值 | 说明 | |------|--------|------| | `--format_id` | `30064+30280` | 下载格式ID | | `--crop` | `792:600:432:56` | 裁剪参数（宽:高:左:上） | | `--no-crop` | — | 跳过裁剪步骤 | | `--no-trim` | — | 跳过去头尾步骤（默认去除前后各10秒） | | `--max_size_mb` | `10` | 目标文件大小（MB） | | `--filename` | 自动从URL提取 | 输出文件名（不含扩展名） | | `--output_dir` | `Documents` | 输出目录 | **使用示例:** ```bash # 默认处理（下载→裁剪→去头尾→压缩） py scripts/download_and_process.py "https://www.bilibili.com/video/BV1m24y1d7xw" # 不裁剪，直接去头尾并压缩 py scripts/download_and_process.py "https://www.bilibili.com/video/BV1m24y1d7xw" --no-crop # 不去头尾，只裁剪并压缩 py scripts/download_and_process.py "https://www.bilibili.com/video/BV1m24y1d7xw" --no-trim # 完全不裁剪也不去头尾 py scripts/download_and_process.py "https://www.bilibili.com/video/BV1m24y1d7xw" --no-crop --no-trim # 自定义裁剪区域 py scripts/download_and_process.py "https://www.bilibili.com/video/BV1m24y1d7xw" --crop "800:600:240:60" # 压缩到20MB以内 py scripts/download_and_process.py "https://www.bilibili.com/video/BV1m24y1d7xw" --max_size_mb 20 ``` 脚本自动依次执行：下载 → [裁剪] → [去头去尾] → 压缩到目标大小（中括号为可选步骤） ## 文件名约定 **所有视频文件必须以BV号命名，禁止使用固定文件名如 `bilibili_video.mp4`。** - 下载时: `-o "PATH/%(id)s.%(ext)s"` → 生成 `BV1N14y1Y7uN.mp4` - 中间文件: `{bvid}_raw.mp4`, `{bvid}_cropped.mp4`, `{bvid}_trimmed.mp4`, `{bvid}_trim_start.mp4` - 最终输出: `{bvid}.mp4` 这样每个视频都有独立文件名，不会相互覆盖。

path: scripts/download_and_process.py
content: """B站视频下载裁剪压缩脚本"""
import argparse, subprocess, os, sys, shutil, re

def extract_bvid(url):
    """从B站URL中提取BV号"""
    m = re.search(r'BV[a-zA-Z0-9]+', url)
    return m.group(0) if m else None

def get_duration(fp):
    r = subprocess.run(
        ["ffprobe", "-v", "error", "-show_entries", "format=duration",
         "-of", "default=noprint_wrappers=1:nokey=1", fp],
        capture_output=True, text=True
    )
    return float(r.stdout.strip())

def size_mb(fp):
    return os.path.getsize(fp) / (1024 * 1024)

def download(url, out, fmt=None):
    cmd = [sys.executable, "-m", "yt_dlp", "-o", out, "--merge-output-format", "mp4"]
    if fmt:
        cmd.extend(["-f", fmt])
    cmd.append(url)
    subprocess.run(cmd)

def crop(inp, out, params):
    subprocess.run([
        "ffmpeg", "-y", "-i", inp,
        "-vf", f"crop={params}",
        "-c:v", "libx264", "-crf", "18", "-c:a", "copy", out
    ])

def trim(inp, out, trim_start=10, trim_end=10):
    """去除片头trim_start秒和片尾trim_end秒"""
    ext = os.path.splitext(out)[1] or ".mp4"
    temp = out.replace(ext, f"_temp{ext}")
    # 去除前n秒
    subprocess.run([
        "ffmpeg", "-y", "-i", inp,
        "-ss", f"00:00:{trim_start:02d}",
        "-c:v", "libx264", "-crf", "18", "-c:a", "copy", temp
    ])
    # 获取剩余时长
    dur = get_duration(temp)
    # 去除后n秒
    target = dur - trim_end
    if target > 0:
        subprocess.run([
            "ffmpeg", "-y", "-i", temp,
            "-t", str(target),
            "-c:v", "libx264", "-crf", "18", "-c:a", "copy", out
        ])
        os.remove(temp)
    else:
        os.rename(temp, out)

def compress(inp, out, max_mb):
    if size_mb(inp) <= max_mb:
        if inp != out:
            shutil.copy2(inp, out)
        return
    dur = get_duration(inp)
    vbr = max(int((max_mb * 0.9 * 8 * 1024) / dur - 64), 100)
    subprocess.run([
        "ffmpeg", "-y", "-i", inp,
        "-c:v", "libx264",
        "-b:v", f"{vbr}k",
        "-maxrate", f"{int(vbr * 1.25)}k",
        "-bufsize", f"{int(vbr * 2.5)}k",
        "-c:a", "aac",
        "-b:a", "64k",
        "-ar", "44100",
        out
    ])

def main():
    p = argparse.ArgumentParser(description="B站视频下载、裁剪、去头尾、压缩")
    p.add_argument("url", help="B站视频URL（支持BV号、b23.tv短链）")
    p.add_argument("--format_id", default="30064+30280",
                   help="下载格式ID (默认: 30064+30280)")
    p.add_argument("--crop",
                   default="792:600:432:56",
                   help="裁剪参数 宽:高:左:上 (默认: 792:600:432:56)")
    p.add_argument("--no-crop", action="store_true",
                   help="跳过裁剪步骤")
    p.add_argument("--no-trim", action="store_true",
                   help="跳过去头尾步骤（默认去除前后各10秒）")
    p.add_argument("--max_size_mb", type=float, default=10,
                   help="目标文件大小 MB (默认: 10)")
    p.add_argument("--filename", default=None,
                   help="输出文件名（不含扩展名），默认自动从URL提取BV号")
    p.add_argument("--output_dir",
                   default=os.path.join(os.path.expanduser("~"), "Documents"),
                   help="输出目录 (默认: ~/Documents)")
    a = p.parse_args()

    bvid = extract_bvid(a.url) or a.filename
    if not bvid:
        print("Error: Cannot extract BV id from URL and no --filename given")
        sys.exit(1)

    os.makedirs(a.output_dir, exist_ok=True)

    # 1. 下载
    raw = os.path.join(a.output_dir, f"{bvid}_raw.mp4")
    print(f"[1/4] 下载中...")
    download(a.url, raw, a.format_id)

    cur = raw

    # 2. 裁剪（可选）
    if a.no_crop:
        print("[2/4] 跳过裁剪")
    else:
        crp = os.path.join(a.output_dir, f"{bvid}_cropped.mp4")
        print(f"[2/4] 裁剪中 (crop={a.crop})...")
        crop(cur, crp, a.crop)
        cur = crp

    # 3. 去头尾（可选）
    if a.no_trim:
        print("[3/4] 跳过去头尾")
    else:
        trimmed = os.path.join(a.output_dir, f"{bvid}_trimmed.mp4")
        print("[3/4] 去除片头片尾各10秒...")
        trim(cur, trimmed)
        cur = trimmed

    # 4. 压缩
    final = os.path.join(a.output_dir, f"{bvid}.mp4")
    print(f"[4/4] 压缩到 {a.max_size_mb}MB 以内...")
    compress(cur, final, a.max_size_mb)

    # 清理中间文件
    for t in [raw,
              os.path.join(a.output_dir, f"{bvid}_cropped.mp4"),
              os.path.join(a.output_dir, f"{bvid}_trimmed.mp4")]:
        if os.path.exists(t) and t != final:
            os.remove(t)

    print(f"Done: {final} ({size_mb(final):.2f}MB)")

if __name__ == "__main__":
    main()

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)