提示

返回博客列表

关注 up 主的新视频自动下载、自动入库——用 RSS + yt-dlp 打造全自动内容订阅管线

关注 up 主的新视频自动下载、自动入库——用 RSS + yt-dlp 打造全自动内容订阅管线

你关注了十几个 YouTube 频道和 B 站 up 主,每次都要手动去各个平台检查有没有更新,有更新了再手动下载。能不能像 RSS 订阅博客一样,订阅视频频道——有新视频自动下载、自动归档到 Jellyfin、自动发通知告诉你?答案是能,而且不需要自己写爬虫。这套方案的核心是 RSSHub(把任意内容源转成 RSS)+ yt-dlp 订阅模式 + Jellyfin 自动入库 + 钉钉/微信通知。四个开源工具串联,零代码实现全自动。

TL;DR:全自动链路:RSSHub 生成频道的 RSS 地址 → yt-dlp 读取 RSS,检测到新视频自动下载 → --exec 参数下载后自动移动到 Jellyfin 媒体目录 → 触发 Jellyfin 刷新 → 钉钉 Webhook 通知。核心命令:yt-dlp --download-archive archive.txt --exec "python notify.py %(id)s" "RSS_URL"。配合 cron 或 GitHub Actions 定时执行,全程无人值守。

目录

一、RSSHub:把任何平台变成 RSS

1.1 RSSHub 是什么

RSSHub 是一个开源项目,口号是"万物皆可 RSS"。它把几百个网站和平台的内容转成标准 RSS 格式:

YouTube 频道 → RSS
B 站 up 主  → RSS
抖音用户    → RSS
知乎专栏    → RSS
微博用户    → RSS
GitHub Releases → RSS
... 总计 300+ 源

1.2 部署 RSSHub

# Docker 部署(推荐)
docker run -d \
  --name rsshub \
  -p 1200:1200 \
  -e CACHE_EXPIRE=3600 \
  diygod/rsshub:latest

# 访问 http://你的IP:1200

1.3 生成 RSS 地址

YouTube 频道:
  http://rsshub:1200/youtube/channel/UCxxx
  (UCxxx 是频道的 Channel ID,在频道页面的 URL 里)

B 站 up 主:
  http://rsshub:1200/bilibili/user/video/123456
  (123456 是 up 主的 UID)

B 站番剧:
  http://rsshub:1200/bilibili/bangumi/media/28231846

抖音用户:
  http://rsshub:1200/douyin/user/xxxxx

Twitter 用户:
  http://rsshub:1200/twitter/user/username

获取 YouTube Channel ID

# 方法 1:从频道页面 URL
# https://www.youtube.com/@Fireship → 查看页面源码 → 搜索 "channelId"

# 方法 2:用 yt-dlp
yt-dlp --print channel_id "https://www.youtube.com/@Fireship"

# 方法 3:在线工具
# https://commentpicker.com/youtube-channel-id.php

1.4 验证 RSS

# 确认 RSS 能正常访问
curl http://rsshub:1200/youtube/channel/UCxxx | head -50

输出应该是一个标准 RSS XML,包含视频标题、链接、发布时间等信息。

二、yt-dlp 的 RSS 订阅模式

2.1 基础用法

yt-dlp 可以直接读取 RSS 作为输入源。它会把 RSS 中的每个条目当作一个视频链接处理:

# 下载 RSS 中所有未下载过的视频
yt-dlp \
  --download-archive archive.txt \
  --output "/downloads/%(uploader)s/%(title)s.%(ext)s" \
  "http://rsshub:1200/youtube/channel/UCxxx"

关键参数

参数 作用
--download-archive archive.txt 记录已下载的视频 ID,下次跳过
--playlist-end 5 只处理最近 5 条(限制首次运行时的下载量)
--dateafter 20260101 只下载此日期之后的视频
--match-filter "duration < 3600" 只下载时长 < 1 小时的视频

2.2 多频道订阅配置

#!/bin/bash
# download_all.sh - 定时执行此脚本

ARCHIVE_FILE="/downloads/archive.txt"

# 频道列表
declare -A CHANNELS=(
  ["YouTube-Fireship"]="http://rsshub:1200/youtube/channel/UCsBjURrPoezykLs9EqgamOA"
  ["YouTube-ThePrimeagen"]="http://rsshub:1200/youtube/channel/UC8ENHE5xdFSwx71u3fDH5Xw"
  ["B站-阮一峰"]="http://rsshub:1200/bilibili/user/video/123456"
  ["B站-科技美学"]="http://rsshub:1200/bilibili/user/video/789012"
)

for name in "${!CHANNELS[@]}"; do
  url="${CHANNELS[$name]}"
  echo "=== 检查: $name ==="

  yt-dlp \
    --download-archive "$ARCHIVE_FILE" \
    --playlist-end 3 \
    --output "/downloads/$name/%(upload_date)s - %(title).100s.%(ext)s" \
    --format "bestvideo[height<=1080]+bestaudio/best[height<=1080]" \
    --merge-output-format mp4 \
    --sleep-interval 2 \
    --no-overwrites \
    --exec "python /scripts/notify.py %(id)s '%(title)s' '$name'" \
    "$url"
done

echo "全部检查完成"

2.3 防止首次运行下载太多

首次运行如果不加限制,yt-dlp 会尝试下载 RSS 中的所有历史视频(可能有几百个)。限制方法:

# 方法 1:只下载最近 3 个
--playlist-end 3

# 方法 2:只下载某个日期之后的
--dateafter 20260101

# 方法 3:先手动把已有的视频 ID 加入 archive.txt
# 格式:youtube VIDEO_ID(每行一个)

三、下载后自动入库 Jellyfin

3.1 下载时直接放到 Jellyfin 目录

最简单的方式:把 yt-dlp 的输出目录直接设为 Jellyfin 的媒体库目录:

# Jellyfin 媒体目录结构
/media/
├── YouTube/
│   ├── Fireship/
│   └── ThePrimeagen/
└── Bilibili/
    └── 阮一峰/

# yt-dlp 输出模板
--output "/media/YouTube/Fireship/%(title)s.%(ext)s"

3.2 下载后自动触发 Jellyfin 扫描

#!/usr/bin/env python3
# notify.py - yt-dlp 的 --exec 脚本

import sys
import requests
import os

JELLYFIN_URL = os.getenv('JELLYFIN_URL', 'http://localhost:8096')
JELLYFIN_API_KEY = os.getenv('JELLYFIN_API_KEY')
DINGTALK_WEBHOOK = os.getenv('DINGTALK_WEBHOOK')


def notify_jellyfin_refresh():
    """通知 Jellyfin 扫描新文件"""
    headers = {'X-MediaBrowser-Token': JELLYFIN_API_KEY}
    resp = requests.post(
        f'{JELLYFIN_URL}/Library/Refresh',
        headers=headers
    )
    if resp.status_code == 204:
        print("Jellyfin 扫描已触发")
    else:
        print(f"Jellyfin 扫描失败: {resp.status_code}")


def notify_dingtalk(title, content):
    """发送钉钉通知"""
    if not DINGTALK_WEBHOOK:
        return

    requests.post(DINGTALK_WEBHOOK, json={
        'msgtype': 'markdown',
        'markdown': {
            'title': title,
            'text': f'## {title}\n\n{content}'
        }
    })


if __name__ == '__main__':
    # yt-dlp 的 --exec 会传入 %(id)s %(title)s
    video_id = sys.argv[1] if len(sys.argv) > 1 else '未知'
    title = sys.argv[2] if len(sys.argv) > 2 else '未知视频'
    channel = sys.argv[3] if len(sys.argv) > 3 else '未知频道'

    # 触发 Jellyfin 扫描
    notify_jellyfin_refresh()

    # 发送通知
    notify_dingtalk(
        f'新视频下载完成',
        f'- **频道**: {channel}\n'
        f'- **标题**: {title}\n'
        f'- **ID**: {video_id}\n'
        f'- 已自动归档到 Jellyfin,打开即可观看'
    )

    print(f"通知已发送: {title}")

四、钉钉/微信/Telegram 通知推送

4.1 钉钉机器人

# 添加钉钉机器人:
# 钉钉群 → 群设置 → 智能群助手 → 添加机器人 → 自定义 → 获取 Webhook URL

DINGTALK_URL = 'https://oapi.dingtalk.com/robot/send?access_token=xxx'

def send_dingtalk(title, text):
    import requests
    requests.post(DINGTALK_URL, json={
        'msgtype': 'markdown',
        'markdown': {
            'title': title,
            'text': f'## {title}\n\n{text}\n\n---\n自动下载管线'
        }
    })

4.2 Telegram Bot

# 创建 Bot:和 @BotFather 对话 → /newbot → 获取 Token
# 获取 Chat ID:给 Bot 发消息后访问
# https://api.telegram.org/bot<TOKEN>/getUpdates

def send_telegram(bot_token, chat_id, text):
    import requests
    requests.post(
        f'https://api.telegram.org/bot{bot_token}/sendMessage',
        json={'chat_id': chat_id, 'text': text, 'parse_mode': 'Markdown'}
    )

4.3 微信公众号(Server 酱)

# 注册:https://sct.ftqq.com/ → 获取 SendKey

def send_wechat(send_key, title, content):
    import requests
    requests.post(
        f'https://sctapi.ftqq.com/{send_key}.send',
        data={'title': title, 'desp': content}
    )

五、整条管线的串联脚本

#!/bin/bash
# pipeline.sh - 完整的自动下载管线
# 放到 crontab 中:0 */4 * * * /scripts/pipeline.sh

set -e

LOG_FILE="/var/log/video-pipeline.log"
ARCHIVE_FILE="/data/archive.txt"
DOWNLOAD_DIR="/data/downloads"
NOTIFY_SCRIPT="/scripts/notify.py"

echo "=== $(date) 开始执行 ===" >> "$LOG_FILE"

# 频道配置文件(每行:频道名|RSS地址)
CHANNELS_FILE="/scripts/channels.txt"

while IFS='|' read -r name url; do
  # 跳过注释和空行
  [[ "$name" =~ ^#.*$ ]] && continue
  [[ -z "$name" ]] && continue

  echo "检查: $name" >> "$LOG_FILE"

  yt-dlp \
    --download-archive "$ARCHIVE_FILE" \
    --playlist-end 3 \
    --output "$DOWNLOAD_DIR/$name/%(upload_date)s - %(title).100s.%(ext)s" \
    --format "bestvideo[height<=1080]+bestaudio/best[height<=1080]" \
    --merge-output-format mp4 \
    --sleep-interval 2 \
    --no-overwrites \
    --exec "python $NOTIFY_SCRIPT %(id)s '%(title)s' '$name'" \
    "$url" >> "$LOG_FILE" 2>&1

done < "$CHANNELS_FILE"

echo "=== $(date) 执行完成 ===" >> "$LOG_FILE"
# channels.txt 示例
YouTube-Fireship|http://rsshub:1200/youtube/channel/UCsBjURrPoezykLs9EqgamOA
YouTube-ThePrimeagen|http://rsshub:1200/youtube/channel/UC8ENHE5xdFSwx71u3fDH5Xw
B站-阮一峰|http://rsshub:1200/bilibili/user/video/123456
B站-科技美学|http://rsshub:1200/bilibili/user/video/789012

Crontab 定时

# 每 4 小时检查一次
0 */4 * * * /bin/bash /scripts/pipeline.sh

# 或者每天早 8 点和晚 8 点
0 8,20 * * * /bin/bash /scripts/pipeline.sh

六、部署到 NAS / VPS / GitHub Actions

6.1 Docker Compose 全家桶

# docker-compose.yml
version: '3.8'

services:
  rsshub:
    image: diygod/rsshub:latest
    container_name: rsshub
    restart: unless-stopped
    ports:
      - "1200:1200"
    environment:
      - CACHE_EXPIRE=3600

  downloader:
    build: ./downloader
    container_name: video-downloader
    restart: unless-stopped
    volumes:
      - ./downloads:/downloads
      - ./archive.txt:/archive.txt
      - ./scripts:/scripts
      - ./channels.txt:/channels.txt
    environment:
      - JELLYFIN_URL=http://jellyfin:8096
      - JELLYFIN_API_KEY=${JELLYFIN_API_KEY}
      - DINGTALK_WEBHOOK=${DINGTALK_WEBHOOK}
    # 不自动启动,由 cron 容器触发

  cron:
    image: alpine:latest
    container_name: video-cron
    restart: unless-stopped
    volumes:
      - ./scripts/crontab:/etc/crontabs/root
    command: crond -f -l 2

6.2 GitHub Actions 方案

如果不想维护服务器,用 GitHub Actions(详见 GitHub Actions 自动化下载文章):

name: RSS 视频下载
on:
  schedule:
    - cron: '0 */6 * * *'
  workflow_dispatch:

jobs:
  download:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: 安装 yt-dlp
        run: pip install yt-dlp
      - name: 下载新视频
        run: bash scripts/pipeline.sh
      - name: 上传到 Releases
        uses: softprops/action-gh-release@v2
        with:
          files: downloads/*.mp4

七、进阶:下载后自动 AI 生成字幕

在前面管线的基础上,加上 Whisper 自动生成字幕:

# 在 notify.py 中添加
def auto_generate_subtitle(video_path):
    """下载后自动生成字幕"""
    from faster_whisper import WhisperModel

    model = WhisperModel("medium", device="cuda", compute_type="float16")

    # 提取音频
    audio_path = video_path.replace('.mp4', '.wav')
    subprocess.run([
        'ffmpeg', '-i', video_path, '-vn',
        '-acodec', 'pcm_s16le', '-ar', '16000', '-ac', '1',
        audio_path, '-y'
    ], capture_output=True)

    # 识别
    segments, _ = model.transcribe(audio_path, beam_size=5)
    srt_path = video_path.replace('.mp4', '.srt')

    with open(srt_path, 'w', encoding='utf-8') as f:
        for i, seg in enumerate(segments, 1):
            start = format_srt_time(seg.start)
            end = format_srt_time(seg.end)
            f.write(f"{i}\n{start} --> {end}\n{seg.text.strip()}\n\n")

    os.remove(audio_path)
    print(f"字幕已生成: {srt_path}")

完整链路变成:

RSS 检测到新视频
  → yt-dlp 下载
  → FFmpeg 提取音频
  → Whisper 生成字幕
  → 移动到 Jellyfin 目录
  → Jellyfin 自动扫描
  → 钉钉推送通知(含字幕生成状态)

八、合规与温馨提示

  • RSSHub 是合法的开源工具,但使用它爬取内容需遵守目标平台的 robots.txt 和 ToS
  • 自动下载管线仅用于个人学习研究和下载已获授权的内容
  • 定时高频访问平台 API 可能触发反爬机制,请合理设置检查间隔(建议 >= 2 小时)
  • 更多讨论见 下载视频算侵权吗?聊聊个人备份与版权的那条线

这套管线我跑了快一年,最大的感受是:搭建的时候折腾一天,之后每天节省 20 分钟。 现在我的手机每天早上会收到一条钉钉消息:"昨晚下载了 3 个新视频,已归档到 Jellyfin"。打开 Jellyfin,最新的内容就在首页等着我。这种"信息主动来找你"的体验,用过之后就再也回不去手动刷网页的日子了。

本文由 VidDown 技术博客原创发布。VidDown 桌面客户端内置了订阅管理功能——添加关注的频道后,自动检测更新并一键下载。访问 VidDown 了解更多。

想亲手试试?用 VidDown 一键解析下载

粘贴视频链接即可解析,多平台支持、网页端即用;下载桌面客户端解锁海外平台本地解析,开通会员更享不限次下载。

顶部