diff --git a/README.md b/README.md
index 8e0a31b..d126c2a 100644
--- a/README.md
+++ b/README.md
@@ -1,679 +1,259 @@
-
+# DouyinLiveRecorder 转播版
-## 💡简介
-[](https://www.python.org/downloads/release/python-3116/)
-[](https://github.com/ihmily/DouyinLiveRecorder)
-[](https://hub.docker.com/r/ihmily/douyin-live-recorder/tags)
-
-[](https://github.com/ihmily/DouyinLiveRecorder/releases/latest)
-[](https://github.com/ihmily/DouyinLiveRecorder/releases/latest)
+[](https://www.python.org/downloads/)
+[](https://github.com/ihmily/DouyinLiveRecorder)
+[](LICENSE)
-一款**简易**的可循环值守的直播录制工具,基于FFmpeg实现多平台直播源录制,支持自定义配置录制以及直播状态推送。
+基于 [DouyinLiveRecorder](https://github.com/ihmily/DouyinLiveRecorder) 的**转播增强版**,将原本的录制下载功能扩展为**实时推流转播**,支持抖音/TikTok 等直播推送到 YouTube、HDMI 等目标。
-
+## 与原版的主要区别
-## 😺已支持平台
+| 功能 | 原版 DouyinLiveRecorder | 本转播版 |
+|------|-------------------------|----------|
+| 核心用途 | 录制直播到本地文件 | 实时转播到 YouTube / HDMI |
+| 抖音 → YouTube | 不支持 | ✅ 支持,含 AI 人声分离防版权 |
+| 抖音 → HDMI/SRS | 不支持 | ✅ 支持(TikTok 模式) |
+| 录制到本地 | ✅ 支持 | ✅ 保留支持 |
+| 平台支持 | 40+ 平台 | 继承全部平台 |
-- [x] 抖音
-- [x] TikTok
-- [x] 快手
-- [x] 虎牙
-- [x] 斗鱼
-- [x] YY
-- [x] B站
-- [x] 小红书
-- [x] bigo
-- [x] blued
-- [x] SOOP(原AfreecaTV)
-- [x] 网易cc
-- [x] 千度热播
-- [x] PandaTV
-- [x] 猫耳FM
-- [x] Look直播
-- [x] WinkTV
-- [x] TTingLive(原Flextv)
-- [x] PopkonTV
-- [x] TwitCasting
-- [x] 百度直播
-- [x] 微博直播
-- [x] 酷狗直播
-- [x] TwitchTV
-- [x] LiveMe
-- [x] 花椒直播
-- [x] 流星直播
-- [x] ShowRoom
-- [x] Acfun
-- [x] 映客直播
-- [x] 音播直播
-- [x] 知乎直播
-- [x] CHZZK
-- [x] 嗨秀直播
-- [x] vv星球直播
-- [x] 17Live
-- [x] 浪Live
-- [x] 畅聊直播
-- [x] 飘飘直播
-- [x] 六间房直播
-- [x] 乐嗨直播
-- [x] 花猫直播
-- [x] Shopee
-- [x] Youtube
-- [x] 淘宝
-- [x] 京东
-- [x] Faceit
-- [x] 咪咕
-- [x] 连接直播
-- [x] 来秀直播
-- [x] Picarto
-- [ ] 更多平台正在更新中
+---
-
+## 功能特性
-## 🎈项目结构
+### 转播模式
+
+- **YouTube 转播**:抖音直播 → YouTube Live,支持 AI 人声分离(Demucs)降低音乐版权识别
+- **TikTok/HDMI 转播**:抖音直播 → SRS 推流 → HDMI 采集卡或 OBS
+- **本地录制**:保留原版全部录制能力
+
+### 防版权识别(YouTube 专用)
+
+- **Demucs AI 人声分离**:只保留人声,去除背景音乐,从源头避免 Content ID 匹配
+- **NVIDIA GPU 加速**:自动检测 CUDA,优先使用 GPU 加速 Demucs
+- **无 GPU 回退**:无显卡时自动回退到 CPU,不报错
+- **可配置开关**:可在 `config/youtube.ini` 中关闭 Demucs,使用原始音频
+
+### 循环值守与多直播间
+
+- 自动轮询直播间状态
+- 主播下播后自动停止推流,并快速重检
+- 支持多直播间 URL 配置,单路推流时自动切换
+- 直播状态推送(钉钉、微信、Telegram 等)
+
+---
+
+## 项目结构
```
.
-└── DouyinLiveRecorder/
- ├── /config -> (config record)
- ├── /logs -> (save runing log file)
- ├── /backup_config -> (backup file)
- ├── /douyinliverecorder -> (package)
- ├── initializer.py-> (check and install nodejs)
- ├── spider.py-> (get live data)
- ├── stream.py-> (get live stream address)
- ├── utils.py -> (contains utility functions)
- ├── logger.py -> (logger handdle)
- ├── room.py -> (get room info)
- ├── ab_sign.py-> (generate dy token)
- ├── /javascript -> (some decrypt code)
- ├── main.py -> (main file)
- ├── ffmpeg_install.py -> (ffmpeg install script)
- ├── demo.py -> (call package test demo)
- ├── msg_push.py -> (send live status update message)
- ├── ffmpeg.exe -> (record video)
- ├── index.html -> (play m3u8 and flv video)
- ├── requirements.txt -> (library dependencies)
- ├── docker-compose.yaml -> (Container Orchestration File)
- ├── Dockerfile -> (Application Build Recipe)
- ├── StopRecording.vbs -> (stop recording script on Windows)
- ...
+├── main.py # 主入口(本地录制 + SRS 转播)
+├── youtube.py # YouTube 转播入口
+├── tiktok.py # TikTok/HDMI 转播入口
+├── douyin_youtube_ffplay.py # 抖音 → YouTube 推流(含 Demucs 管道)
+├── douyin_srs_ffplay.py # 抖音 → SRS/HDMI 推流
+├── web.py # Web 配置界面
+├── config/
+│ ├── config.ini # 通用配置
+│ ├── youtube.ini # YouTube 推流配置(stream key、Demucs 开关)
+│ └── URL_config.ini # 直播间 URL 列表
+├── src/ # 爬虫、流解析等核心逻辑
+├── backup/ # 备份与辅助模块
+├── requirements.txt
+├── pyproject.toml
+└── README.md
```
-
+---
-## 🌱使用说明
+## 快速开始
-- 对于只想使用录制软件的小白用户,进入[Releases](https://github.com/ihmily/DouyinLiveRecorder/releases) 中下载最新发布的 zip压缩包即可,里面有打包好的录制软件。(有些电脑可能会报毒,直接忽略即可,如果下载时被浏览器屏蔽,请更换浏览器下载)
+### 1. 环境要求
-- 压缩包解压后,在 `config` 文件夹内的 `URL_config.ini` 中添加录制直播间地址,一行一个直播间地址。如果要自定义配置录制,可以修改`config.ini` 文件,推荐将录制格式修改为`ts`。
-- 以上步骤都做好后,就可以运行`DouyinLiveRecorder.exe` 程序进行录制了。录制的视频文件保存在同目录下的 `downloads` 文件夹内。
+- **Python** >= 3.10
+- **FFmpeg**(需支持 libx264、h264_nvenc)
+- **可选**:NVIDIA GPU + CUDA(用于 Demucs 加速)
-- 另外,如果需要录制TikTok、AfreecaTV等海外平台,请在配置文件中设置开启代理并添加proxy_addr链接 如:`127.0.0.1:7890` (这只是示例地址,具体根据实际填写)。
+### 2. 安装依赖
-- 假如`URL_config.ini`文件中添加的直播间地址,有个别直播间暂时不想录制又不想移除链接,可以在对应直播间的链接开头加上`#`,那么将停止该直播间的监测以及录制。
+```bash
+# 克隆或下载项目
+cd douyinyoutube
-- 软件默认录制清晰度为 `原画` ,如果要单独设置某个直播间的录制画质,可以在添加直播间地址时前面加上画质即可,如`超清,https://live.douyin.com/745964462470` 记得中间要有`,` 分隔。
+# 创建虚拟环境(推荐)
+python -m venv .venv
+# Windows:
+.venv\Scripts\activate
+# Ubuntu/Linux:
+source .venv/bin/activate
-- 如果要长时间挂着软件循环监测直播,最好循环时间设置长一点(咱也不差没录制到的那几分钟),避免因请求频繁导致被官方封禁IP 。
-
-- 要停止直播录制,Windows平台可执行StopRecording.vbs脚本文件,或者在录制界面使用 `Ctrl+C ` 组合键中断录制,若要停止其中某个直播间的录制,可在`URL_config.ini`文件中的地址前加#,会自动停止对应直播间的录制并正常保存已录制的视频。
-- 最后,欢迎右上角给本项目一个star,同时也非常乐意大家提交pr。
-
-
-
-直播间链接示例:
+# 安装基础依赖
+pip install -r requirements.txt
+# 若使用 YouTube 转播 + Demucs 防版权,需额外安装:
+pip install -r requirements-youtube.txt
+# GPU 版本(NVIDIA)需先安装 CUDA 版 PyTorch:
+# pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu118
```
-抖音:
+
+### 3. 配置
+
+#### YouTube 转播
+
+1. 编辑 `config/youtube.ini`:
+
+```ini
+[youtube]
+# YouTube 直播 stream key(从 YouTube 直播控制台获取)
+key = xxxx-xxxx-xxxx-xxxx
+
+# 是否启用 Demucs 人声分离(防版权识别)
+# 是 = 启用,否 = 使用原始音频
+enable_demucs = 是
+
+# Demucs 是否优先使用 GPU(有 NVIDIA 显卡时)
+# 是 = 优先 GPU,否 = 强制 CPU
+demucs_use_gpu = 是
+```
+
+2. 编辑 `config/URL_config.ini`,添加抖音直播间地址:
+
+```ini
https://live.douyin.com/745964462470
-https://v.douyin.com/iQFeBnt/
-https://live.douyin.com/yall1102 (链接+抖音号)
-https://v.douyin.com/CeiU5cbX (主播主页地址)
-
-TikTok:
-https://www.tiktok.com/@pearlgaga88/live
-
-快手:
-https://live.kuaishou.com/u/yall1102
-
-虎牙:
-https://www.huya.com/52333
-
-斗鱼:
-https://www.douyu.com/3637778?dyshid=
-https://www.douyu.com/topic/wzDBLS6?rid=4921614&dyshid=
-
-YY:
-https://www.yy.com/22490906/22490906
-
-B站:
-https://live.bilibili.com/320
-
-小红书(直播间分享地址):
-http://xhslink.com/xpJpfM
-
-bigo直播:
-https://www.bigo.tv/cn/716418802
-
-buled直播:
-https://app.blued.cn/live?id=Mp6G2R
-
-SOOP:
-https://play.sooplive.co.kr/sw7love
-
-网易cc:
-https://cc.163.com/583946984
-
-千度热播:
-https://qiandurebo.com/web/video.php?roomnumber=33333
-
-PandaTV:
-https://www.pandalive.co.kr/live/play/bara0109
-
-猫耳FM:
-https://fm.missevan.com/live/868895007
-
-Look直播:
-https://look.163.com/live?id=65108820&position=3
-
-WinkTV:
-https://www.winktv.co.kr/live/play/anjer1004
-
-FlexTV(TTinglive)::
-https://www.flextv.co.kr/channels/593127/live
-
-PopkonTV:
-https://www.popkontv.com/live/view?castId=wjfal007&partnerCode=P-00117
-https://www.popkontv.com/channel/notices?mcid=wjfal007&mcPartnerCode=P-00117
-
-TwitCasting:
-https://twitcasting.tv/c:uonq
-
-百度直播:
-https://live.baidu.com/m/media/pclive/pchome/live.html?room_id=9175031377&tab_category
-
-微博直播:
-https://weibo.com/l/wblive/p/show/1022:2321325026370190442592
-
-酷狗直播:
-https://fanxing2.kugou.com/50428671?refer=2177&sourceFrom=
-
-TwitchTV:
-https://www.twitch.tv/gamerbee
-
-LiveMe:
-https://www.liveme.com/zh/v/17141543493018047815/index.html
-
-花椒直播:
-https://www.huajiao.com/l/345096174
-
-流星直播:
-https://www.7u66.com/100960
-
-ShowRoom:
-https://www.showroom-live.com/room/profile?room_id=480206 (主播主页地址)
-
-Acfun:
-https://live.acfun.cn/live/179922
-
-映客直播:
-https://www.inke.cn/liveroom/index.html?uid=22954469&id=1720860391070904
-
-音播直播:
-https://live.ybw1666.com/800002949
-
-知乎直播:
-https://www.zhihu.com/people/ac3a467005c5d20381a82230101308e9 (主播主页地址)
-
-CHZZK:
-https://chzzk.naver.com/live/458f6ec20b034f49e0fc6d03921646d2
-
-嗨秀直播:
-https://www.haixiutv.com/6095106
-
-VV星球直播:
-https://h5webcdn-pro.vvxqiu.com//activity/videoShare/videoShare.html?h5Server=https://h5p.vvxqiu.com&roomId=LP115924473&platformId=vvstar
-
-17Live:
-https://17.live/en/live/6302408
-
-浪Live:
-https://www.lang.live/en-US/room/3349463
-
-畅聊直播:
-https://live.tlclw.com/106188
-
-飘飘直播:
-https://m.pp.weimipopo.com/live/preview.html?uid=91648673&anchorUid=91625862&app=plpl
-
-六间房直播:
-https://v.6.cn/634435
-
-乐嗨直播:
-https://www.lehaitv.com/8059096
-
-花猫直播:
-https://h.catshow168.com/live/preview.html?uid=19066357&anchorUid=18895331
-
-Shopee:
-https://sg.shp.ee/GmpXeuf?uid=1006401066&session=802458
-
-Youtube:
-https://www.youtube.com/watch?v=cS6zS5hi1w0
-
-淘宝(需cookie):
-https://tbzb.taobao.com/live?liveId=532359023188
-https://m.tb.cn/h.TWp0HTd
-
-京东:
-https://3.cn/28MLBy-E
-
-Faceit:
-https://www.faceit.com/zh/players/Compl1/stream
-
-连接直播:
-https://show.lailianjie.com/10000258
-
-咪咕直播:
-https://www.miguvideo.com/p/live/120000541321
-
-来秀直播:
-https://www.imkktv.com/h5/share/video.html?uid=1845195&roomId=1710496
-
-Picarto:
-https://www.picarto.tv/cuteavalanche
+https://live.douyin.com/xxx,主播名
```
-
+3. 在 `config/config.ini` 中将 `视频保存格式` 设为 `ts`(触发 YouTube 推流逻辑)
-## 🎃源码运行
-使用源码运行,可参考下面的步骤。
+#### TikTok/HDMI 转播
-1.首先拉取或手动下载本仓库项目代码
+- 使用 `tiktok.py` 入口,配置 SRS 推流地址
+- 详见 `tiktok/` 目录说明
+
+### 4. 运行
```bash
-git clone https://github.com/ihmily/DouyinLiveRecorder.git
+# YouTube 转播
+python youtube.py
+
+# 或使用 uv
+uv run youtube.py
```
-2.进入项目文件夹,安装依赖
+---
+
+## 详细配置说明
+
+### config/youtube.ini
+
+| 配置项 | 说明 | 默认值 |
+|--------|------|--------|
+| key | YouTube 直播 stream key | 必填 |
+| enable_demucs | 是否启用 Demucs 人声分离 | 是 |
+| demucs_use_gpu | 是否优先使用 NVIDIA GPU | 是 |
+
+### config/config.ini
+
+- **视频保存格式**:设为 `ts` 时,youtube.py 会走 YouTube 推流逻辑,而非本地录制
+- **循环时间**:轮询间隔(秒)
+- **代理**:海外平台需配置代理
+
+### URL_config.ini 格式
+
+```
+# 单行一个直播间
+https://live.douyin.com/745964462470
+
+# 可加画质和备注
+超清,https://live.douyin.com/xxx
+https://live.douyin.com/yyy,主播名
+
+# 注释掉不监测
+# https://live.douyin.com/zzz
+```
+
+---
+
+## Demucs 防版权说明
+
+### 原理
+
+- 使用 [Demucs](https://github.com/facebookresearch/demucs) 对混音进行人声分离
+- 只保留人声轨道推送到 YouTube,背景音乐被物理移除
+- Content ID 无法匹配已移除的音乐
+
+### 延迟与性能
+
+- **延迟**:约 4–8 秒(与 chunk 大小相关)
+- **GPU**:有 NVIDIA 显卡时自动使用 CUDA 加速
+- **CPU**:无 GPU 时自动回退,不影响运行
+
+### 回退机制
+
+1. **Demucs 未安装**:自动使用原始 FFmpeg 推流
+2. **GPU 不可用**:自动使用 CPU 运行 Demucs
+3. **enable_demucs = 否**:直接使用原始音频
+
+---
+
+## 平台兼容性
+
+| 平台 | 录制 | YouTube 转播 | Demucs |
+|------|------|--------------|--------|
+| Windows | ✅ | ✅ | ✅(CPU/GPU) |
+| Ubuntu Server | ✅ | ✅ | ✅(优先) |
+| 其他 Linux | ✅ | ✅ | ✅ |
+
+---
+
+## Web 配置界面
```bash
-cd DouyinLiveRecorder
+python web.py
```
-> [!TIP]
-> - 不论你是否已安装 **Python>=3.10** 环境, 都推荐使用 [**uv**](https://github.com/astral-sh/uv) 运行, 因为它可以自动管理虚拟环境和方便地管理 **Python** 版本, **不过这完全是可选的**
-> 使用以下命令安装
-> ```bash
-> # 在 macOS 和 Linux 上安装 uv
-> curl -LsSf https://astral.sh/uv/install.sh | sh
-> ```
-> ```powershell
-> # 在 Windows 上安装 uv
-> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
-> ```
-> - 如果安装依赖速度太慢, 你可以考虑使用国内 pip 镜像源:
-> 在 `pip` 命令使用 `-i` 参数指定, 如 `pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple`
-> 或者在 `uv` 命令 `--index` 选项指定, 如 `uv sync --index https://pypi.tuna.tsinghua.edu.cn/simple`
+- 浏览器访问配置页面
+- 支持编辑 `youtube.ini`、`URL_config.ini`
+- 选择 youtube / tiktok / obs 等模式
-
+---
- 如果已安装 Python>=3.10 环境
+## 常见问题
- - :white_check_mark: 在虚拟环境中安装 (推荐)
-
- 1. 创建虚拟环境
-
- - 使用系统已安装的 Python, 不使用 uv
-
- ```bash
- python -m venv .venv
- ```
-
- - 使用 uv, 默认使用系统 Python, 你可以添加 `--python` 选项指定 Python 版本而不使用系统 Python [uv官方文档](https://docs.astral.sh/uv/concepts/python-versions/)
-
- ```bash
- uv venv
- ```
-
- 2. 在终端激活虚拟环境 (在未安装 uv 或你想要手动激活虚拟环境时执行, 若已安装 uv, 可以跳过这一步, uv 会自动激活并使用虚拟环境)
-
- **Bash** 中
- ```bash
- source .venv/Scripts/activate
- ```
-
- **Powershell** 中
- ```powershell
- .venv\Scripts\activate.ps1
- ```
-
- **Windows CMD** 中
- ```bat
- .venv\Scripts\activate.bat
- ```
-
- 3. 安装依赖
-
- ```bash
- # 使用 pip (若安装太慢或失败, 可使用 `-i` 指定镜像源)
- pip3 install -U pip && pip3 install -r requirements.txt
- # 或者使用 uv (可使用 `--index` 指定镜像源)
- uv sync
- # 或者
- uv pip sync requirements.txt
- ```
-
- - :x: 在系统 Python 环境中安装 (不推荐)
-
- ```bash
- pip3 install -U pip && pip3 install -r requirements.txt
- ```
-
-
-
-
-
- 如果未安装 Python>=3.10 环境
-
- 你可以使用 [**uv**](https://github.com/astral-sh/uv) 安装依赖
-
- ```bash
- # uv 将使用 3.10 及以上的最新 python 发行版自动创建并使用虚拟环境, 可使用 --python 选项指定 python 版本, 参见 https://docs.astral.sh/uv/reference/cli/#uv-sync--python 和 https://docs.astral.sh/uv/reference/cli/#uv-pip-sync--python
- uv sync
- # 或
- uv pip sync requirements.txt
- ```
-
-
-
-3.安装[FFmpeg](https://ffmpeg.org/download.html#build-linux),如果是Windows系统,这一步可跳过。对于Linux系统,执行以下命令安装
-
-CentOS执行
+### 1. Demucs 安装失败
```bash
-yum install epel-release
-yum install ffmpeg
+# 仅 CPU
+pip install demucs torch torchaudio
+
+# 使用国内镜像
+pip install demucs torch torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple
```
-Ubuntu则执行
+### 2. 无 NVIDIA 显卡
-```bash
-apt update
-apt install ffmpeg
-```
+- 程序会自动使用 CPU 运行 Demucs
+- 可在 `youtube.ini` 中设置 `demucs_use_gpu = 否` 以跳过 GPU 检测
-macOS 执行
+### 3. 推流中断
-**如果已经安装 Homebrew 请跳过这一步**
+- 检查网络与代理
+- 检查 YouTube stream key 是否有效
+- 查看控制台日志定位错误
-```bash
-/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-```
+### 4. 抖音风控
-```bash
-brew install ffmpeg
-```
+- 适当增大循环时间
+- 确保 Cookie 有效(录制抖音需配置)
-4.运行程序
+---
-```python
-python main.py
+## 致谢
-```
-或
+- 原项目:[ihmily/DouyinLiveRecorder](https://github.com/ihmily/DouyinLiveRecorder)
+- 人声分离:[facebookresearch/demucs](https://github.com/facebookresearch/demucs)
-```bash
-uv run main.py
-```
+---
-其中Linux系统请使用`python3 main.py` 运行。
+## 许可证
-
-## 🐋容器运行
-
-在运行命令之前,请确保您的机器上安装了 [Docker](https://docs.docker.com/get-docker/) 和 [Docker Compose](https://docs.docker.com/compose/install/)
-
-1.快速启动
-
-最简单方法是运行项目中的 [docker-compose.yaml](https://github.com/ihmily/DouyinLiveRecorder/blob/main/docker-compose.yaml) 文件,只需简单执行以下命令:
-
-```bash
-docker-compose up
-```
-
-可选 `-d` 在后台运行。
-
-
-
-2.构建镜像(可选)
-
-如果你只想简单的运行程序,则不需要做这一步。Docker镜像仓库中代码版本可能不是最新的,如果要运行本仓库主分支最新代码,可以本地自定义构建,通过修改 [docker-compose.yaml](https://github.com/ihmily/DouyinLiveRecorder/blob/main/docker-compose.yaml) 文件,如将镜像名修改为 `douyin-live-recorder:latest`,并取消 `# build: .` 注释,然后再执行
-
-```bash
-docker build -t douyin-live-recorder:latest .
-docker-compose up
-```
-
-或者直接使用下面命令进行构建并启动
-
-```bash
-docker-compose -f docker-compose.yaml up
-```
-
-
-
-3.停止容器实例
-
-```bash
-docker-compose stop
-```
-
-
-
-4.注意事项
-
-①在docker容器内运行本程序之前,请先在配置文件中添加要录制的直播间地址。
-
-②在容器内时,如果手动中断容器运行停止录制,会导致正在录制的视频文件损坏!
-
-**无论哪种运行方式,为避免手动中断或者异常中断导致录制的视频文件损坏的情况,推荐使用 `ts` 格式保存**。
-
-
-
-## 🤖相关项目
-
-- StreamCap: https://github.com/ihmily/StreamCap
-- streamget: https://github.com/ihmily/streamget
-
-
-
-## ❤️贡献者
-
- [](https://github.com/ihmily)
-[](https://github.com/iridescentGray)
-[](https://github.com/annidy)
-[](https://github.com/wwkk2580)
-[](https://github.com/missuo)
-
-
-
-
-[](https://github.com/justdoiting)
-[](https://github.com/dhbxs)
-[](https://github.com/wujiyu115)
-[](https://github.com/zhanghao333)
-
-
- [](https://github.com/HoratioShaw)
-[](https://github.com/nov30th)
-[](https://github.com/727155455)
-[](https://github.com/nixingshiguang)
-[](https://github.com/1411430556)
-[](https://github.com/Ovear)
-
-
-## ⏳提交日志
-
-- 20251024
- - 修复抖音风控无法获取数据问题
-
- - 新增soop.com录制支持
-
- - 修复bigo录制
-
-- 20250127
- - 新增淘宝、京东、faceit直播录制
- - 修复小红书直播流录制以及转码问题
- - 修复畅聊、VV星球、flexTV直播录制
- - 修复批量微信直播推送
- - 新增email发送ssl和port配置
- - 新增强制转h264配置
- - 更新ffmpeg版本
- - 重构包为异步函数!
-
-- 20241130
- - 新增shopee、youtube直播录制
- - 新增支持自定义m3u8、flv地址录制
- - 新增自定义执行脚本,支持python、bat、bash等
- - 修复YY直播、花椒直播和小红书直播录制
- - 修复b站标题获取错误
- - 修复log日志错误
-- 20241030
- - 新增嗨秀直播、vv星球直播、17Live、浪Live、SOOP、畅聊直播(原时光直播)、飘飘直播、六间房直播、乐嗨直播、花猫直播等10个平台直播录制
- - 修复小红书直播录制,支持小红书作者主页地址录制直播
- - 新增支持ntfy消息推送,以及新增支持批量推送多个地址(逗号分隔多个推送地址)
- - 修复Liveme直播录制、twitch直播录制
- - 新增Windows平台一键停止录制VB脚本程序
-- 20241005
- - 新增邮箱和Bark推送
- - 新增直播注释停止录制
- - 优化分段录制
- - 重构部分代码
-- 20240928
- - 新增知乎直播、CHZZK直播录制
- - 修复音播直播录制
-- 20240903
- - 新增抖音双屏录制、音播直播录制
- - 修复PandaTV、bigo直播录制
-- 20240713
- - 新增映客直播录制
-- 20240705
- - 新增时光直播录制
-- 20240701
- - 修复虎牙直播录制2分钟断流问题
- - 新增自定义直播推送内容
-- 20240621
- - 新增Acfun、ShowRoom直播录制
- - 修复微博录制、新增直播源线路
- - 修复斗鱼直播60帧录制
- - 修复酷狗直播录制
- - 修复TikTok部分无法解析直播源
- - 修复抖音无法录制连麦直播
-- 20240510
- - 修复部分虎牙直播间录制错误
-- 20240508
- - 修复花椒直播录制
- - 更改文件路径解析方式 [@kaine1973](https://github.com/kaine1973)
-- 20240506
- - 修复抖音录制画质解析bug
- - 修复虎牙录制 60帧最高画质问题
- - 新增流星直播录制
-- 20240427
- - 新增LiveMe、花椒直播录制
-- 20240425
- - 新增TwitchTV直播录制
-- 20240424
- - 新增酷狗直播录制、优化PopkonTV直播录制
-- 20240423
- - 新增百度直播录制、微博直播录制
- - 修复斗鱼录制直播回放的问题
- - 新增直播源地址显示以及输出到日志文件设置
-- 20240311
- - 修复海外平台录制bug,增加画质选择,增强录制稳定性
- - 修复虎牙录制bug (虎牙`一起看`频道 有特殊限制,有时无法录制)
-- 20240309
- - 修复虎牙直播、小红书直播和B站直播录制
- - 新增5个直播平台录制,包括winktv、flextv、look、popkontv、twitcasting
- - 新增部分海外平台账号密码配置,实现自动登录并更新配置文件中的cookie
- - 新增自定义配置需要使用代理录制的平台
- - 新增只推送开播消息不进行录制设置
- - 修复了一些bug
-- 20240209
- - 优化AfreecaTV录制,新增账号密码登录获取cookie以及持久保存
- - 修复了小红书直播因官方更新直播域名,导致无法录制直播的问题
- - 修复了更新URL配置文件的bug
- - 最后,祝大家新年快乐!
-
-点击展开更多提交日志
-
-- 20240129
- - 新增猫耳FM直播录制
-- 20240127
- - 新增千度热播直播录制、新增pandaTV(韩国)直播录制
- - 新增telegram直播状态消息推送,修复了某些bug
- - 新增自定义设置不同直播间的录制画质(即每个直播间录制画质可不同)
- - 修改录制视频保存路径为 `downloads` 文件夹,并且分平台进行保存。
-- 20240114
- - 新增网易cc直播录制,优化ffmpeg参数,修改AfreecaTV输入直播地址格式
- - 修改日志记录器 @[iridescentGray](https://github.com/iridescentGray)
-- 20240102
- - 修复Linux上运行,新增docker配置文件
-- 20231210
- - 修复录制分段bug,修复bigo录制检测bug
- - 新增自定义修改录制主播名
- - 新增AfreecaTV直播录制,修复某些可能会发生的bug
-- 20231207
- - 新增blued直播录制,修复YY直播录制,新增直播结束消息推送
-- 20231206
- - 新增bigo直播录制
-- 20231203
- - 新增小红书直播录制(全网首发),目前小红书官方没有切换清晰度功能,因此直播录制也只有默认画质
- - 小红书录制暂时无法循环监测,每次主播开启直播,都要重新获取一次链接
- - 获取链接的方式为 将直播间转发到微信,在微信中打开后,复制页面的链接。
-- 20231030
- - 本次更新只是进行修复,没时间新增功能。
- - 欢迎各位大佬提pr 帮忙更新维护
-- 20230930
- - 新增抖音从接口获取直播流,增强稳定性
- - 修改快手获取直播流的方式,改用从官方接口获取
- - 祝大家中秋节快乐!
-- 20230919
- - 修复了快手版本更新后录制出错的问题,增加了其自动获取cookie(~~稳定性未知~~)
- - 修复了TikTok显示正在直播但不进行录制的问题
-- 20230907
- - 修复了因抖音官方更新了版本导致的录制出错以及短链接转换出错
- - 修复B站无法录制原画视频的bug
- - 修改了配置文件字段,新增各平台自定义设置Cookie
-- 20230903
- - 修复了TikTok录制时报644无法录制的问题
- - 新增直播状态推送到钉钉和微信的功能,如有需要请看 [设置推送教程](https://d04vqdiqwr3.feishu.cn/docx/XFPwdDDvfobbzlxhmMYcvouynDh?from=from_copylink)
- - 最近比较忙,其他问题有时间再更新
-- 20230816
- - 修复斗鱼直播(官方更新了字段)和快手直播录制出错的问题
-- 20230814
- - 新增B站直播录制
- - 写了一个在线播放M3U8和FLV视频的网页源码,打开即可食用
-- 20230812
- - 新增YY直播录制
-- 20230808
- - 修复主播重新开播无法再次录制的问题
-- 20230807
- - 新增了斗鱼直播录制
- - 修复显示录制完成之后会重新开始录制的问题
-- 20230805
- - 新增了虎牙直播录制,其暂时只能用flv视频流进行录制
- - Web API 新增了快手和虎牙这两个平台的直播流解析(TikTok要代理)
-- 20230804
- - 新增了快手直播录制,优化了部分代码
- - 上传了一个自动化获取抖音直播间页面Cookie的代码,可以用于录制
-- 20230803
- - 通宵更新
- - 新增了国际版抖音TikTok的直播录制,去除冗余 简化了部分代码
-- 20230724
- - 新增了一个通过抖音直播间地址获取直播视频流链接的API接口,上传即可用
-
-
-
-## 有问题可以提issue, 我会在这里持续添加更多直播平台的录制 欢迎Star
-####
+MIT License
diff --git a/config/youtube.ini b/config/youtube.ini
index 9d34b07..7efb7ff 100644
--- a/config/youtube.ini
+++ b/config/youtube.ini
@@ -1,3 +1,11 @@
[youtube]
# 只写 stream key(推荐方式)
-key = x04z-564w-aks7-embw-30y4
\ No newline at end of file
+key = x04z-564w-aks7-embw-30y4
+
+# 是否启用 Demucs 人声分离(防 YouTube Content ID 版权识别)
+# 是 = 启用,只保留人声;否 = 使用原始音频
+enable_demucs = 是
+
+# Demucs 是否优先使用 NVIDIA GPU 加速
+# 是 = 有显卡则用 GPU;否 = 强制 CPU;无显卡时自动回退到 CPU
+demucs_use_gpu = 是
\ No newline at end of file
diff --git a/demucs_pipeline.py b/demucs_pipeline.py
new file mode 100644
index 0000000..ce72e2b
--- /dev/null
+++ b/demucs_pipeline.py
@@ -0,0 +1,387 @@
+# demucs_pipeline.py
+# Demucs 人声分离管道:FFmpeg 拉流 → Demucs 分离人声 → FFmpeg 推流
+# 兼容 Windows / Ubuntu,支持 NVIDIA GPU 加速与 CPU 回退
+"""
+Demucs 音频管道模块
+用于将混音分离为人声,推送到 YouTube 时避免 Content ID 识别背景音乐。
+"""
+import os
+import sys
+import socket
+import threading
+import subprocess
+import queue
+import time
+import platform
+
+# 可选依赖:Demucs 和 PyTorch
+DEMUCS_AVAILABLE = False
+DEMUCS_MODEL = None
+TORCH_AVAILABLE = False
+
+try:
+ import torch
+ TORCH_AVAILABLE = True
+except ImportError:
+ pass
+
+try:
+ from demucs.pretrained import get_model
+ from demucs.apply import apply_model
+ DEMUCS_AVAILABLE = True
+except ImportError:
+ pass
+
+
+def _check_demucs_available():
+ """检查 Demucs 是否可用"""
+ return DEMUCS_AVAILABLE and TORCH_AVAILABLE
+
+
+def _load_demucs_model(use_gpu: bool):
+ """加载 Demucs 模型,支持 GPU/CPU 回退"""
+ global DEMUCS_MODEL
+ if not _check_demucs_available():
+ return None
+ try:
+ # htdemucs 较轻量,适合实时;htdemucs_ft 质量更好但更慢
+ model_name = "htdemucs"
+ device = "cpu"
+ if use_gpu and TORCH_AVAILABLE and torch.cuda.is_available():
+ try:
+ device = "cuda"
+ except Exception:
+ device = "cpu"
+ model = get_model(model_name)
+ model.eval()
+ return model.to(device)
+ except Exception as e:
+ print(f"[WARN] Demucs 模型加载失败,回退原始音频: {e}")
+ return None
+
+
+# Demucs 模型采样率
+DEMUCS_SAMPLE_RATE = 44100
+DEMUCS_CHANNELS = 2
+# 每次处理的音频长度(秒),影响延迟与质量
+DEMUCS_CHUNK_SEC = 2.0
+# 字节 per sample (s16le: 2 bytes)
+BYTES_PER_SAMPLE = 2
+
+
+def _demucs_separate_vocals(model, audio_chunk, device):
+ """对音频块进行人声分离,返回人声轨道"""
+ import torch
+ # audio_chunk: numpy or tensor, shape (channels, samples)
+ if hasattr(audio_chunk, 'numpy'):
+ wav = audio_chunk
+ else:
+ import numpy as np
+ wav = torch.from_numpy(audio_chunk).float()
+ # Demucs 期望 (batch, channels, samples)
+ if wav.dim() == 2:
+ wav = wav.unsqueeze(0)
+ with torch.no_grad():
+ out = apply_model(model, wav, device=device, progress=False, overlap=0.25)
+ # out: (batch, sources, channels, samples)
+ # sources: [drums, bass, other, vocals]
+ vocals = out[0, 3] # 取 vocals
+ return vocals.cpu().numpy()
+
+
+def _run_demucs_worker(audio_read_pipe, audio_write_socket, model, device, stop_event):
+ """Demucs 工作线程:从 FFmpeg 读音频,分离人声,写入 socket"""
+ import numpy as np
+ chunk_samples = int(DEMUCS_SAMPLE_RATE * DEMUCS_CHUNK_SEC * DEMUCS_CHANNELS)
+ chunk_bytes = chunk_samples * BYTES_PER_SAMPLE
+ buf = b""
+
+ def read_audio(size):
+ if hasattr(audio_read_pipe, 'recv'):
+ return audio_read_pipe.recv(size)
+ return audio_read_pipe.read(size)
+
+ try:
+ while not stop_event.is_set():
+ try:
+ data = read_audio(65536)
+ if not data:
+ break
+ buf += data
+ except (socket.error, OSError, ValueError):
+ break
+ while len(buf) >= chunk_bytes:
+ chunk = buf[:chunk_bytes]
+ buf = buf[chunk_bytes:]
+ # s16le -> float
+ arr = np.frombuffer(chunk, dtype=np.int16)
+ arr = arr.reshape(-1, DEMUCS_CHANNELS).T.astype(np.float32) / 32768.0
+ try:
+ vocals = _demucs_separate_vocals(model, arr, device)
+ # float -> s16le
+ out = (vocals * 32768).astype(np.int16).tobytes()
+ try:
+ audio_write_socket.sendall(out)
+ except (socket.error, OSError):
+ return
+ except Exception as e:
+ print(f"[WARN] Demucs 处理异常: {e}")
+ # 回退:直接透传原始音频
+ try:
+ audio_write_socket.sendall(chunk)
+ except (socket.error, OSError):
+ return
+ # 剩余 buffer
+ if buf and audio_write_socket:
+ try:
+ arr = np.frombuffer(buf, dtype=np.int16)
+ arr = arr.reshape(-1, DEMUCS_CHANNELS).T.astype(np.float32) / 32768.0
+ vocals = _demucs_separate_vocals(model, arr, device)
+ out = (vocals * 32768).astype(np.int16).tobytes()
+ audio_write_socket.sendall(out)
+ except Exception:
+ pass
+ except Exception as e:
+ print(f"[WARN] Demucs worker 异常: {e}")
+ finally:
+ try:
+ audio_write_socket.shutdown(socket.SHUT_WR)
+ audio_write_socket.close()
+ except Exception:
+ pass
+
+
+def run_demucs_pipeline(
+ real_url: str,
+ youtube_rtmp: str,
+ douyin_headers: str,
+ nvenc_params: dict,
+ end_keywords: list,
+ stderr_queue_timeout: float,
+ no_frame_timeout: int,
+ start_check_after: int,
+ stale_output_seconds: int,
+ stream_ended_exception,
+ cleanup_proc_fn,
+ stderr_reader_fn,
+):
+ """
+ 运行 Demucs 管道:拉流 → 人声分离 → 推流
+ 若主播下播应停止则 raise stream_ended_exception
+ """
+
+ # 检查 Demucs
+ use_gpu = nvenc_params.get("demucs_use_gpu", True)
+ model = _load_demucs_model(use_gpu)
+ if model is None:
+ raise RuntimeError("Demucs 模型不可用,将回退原始推流")
+
+ device = next(model.parameters()).device
+ device_str = "cuda" if "cuda" in str(device) else "cpu"
+ print(f"[INFO] Demucs 人声分离已启用 ({device_str})")
+
+ # TCP 服务器用于音频(跨平台)
+ server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ server.bind(("127.0.0.1", 0))
+ port = server.getsockname()[1]
+ server.listen(1)
+
+ tcp_audio_url = f"tcp://127.0.0.1:{port}"
+ stop_event = threading.Event()
+ conn = [None] # 用 list 以便在闭包中修改
+
+ def accept_conn():
+ server.settimeout(120)
+ try:
+ c, _ = server.accept()
+ conn[0] = c
+ except Exception:
+ pass
+ finally:
+ try:
+ server.close()
+ except Exception:
+ pass
+
+ accept_thread = threading.Thread(target=accept_conn, daemon=True)
+ accept_thread.start()
+
+ # FFmpeg 拉流(视频)
+ ffmpeg_v_cmd = [
+ "ffmpeg", "-y", "-loglevel", "info", "-nostdin", "-re",
+ "-stats", "-stats_period", "1",
+ "-rw_timeout", "30000000",
+ "-reconnect", "1", "-reconnect_at_eof", "1", "-reconnect_streamed", "1",
+ "-reconnect_delay_max", "5",
+ "-fflags", "+genpts+discardcorrupt+nobuffer+flush_packets",
+ "-err_detect", "ignore_err",
+ "-headers", douyin_headers,
+ "-i", real_url,
+ "-map", "0:v", "-c:v", "copy", "-f", "nut", "pipe:1",
+ ]
+
+ # FFmpeg 拉流(音频)
+ ffmpeg_a_cmd = [
+ "ffmpeg", "-y", "-loglevel", "error", "-nostdin", "-re",
+ "-rw_timeout", "30000000",
+ "-reconnect", "1", "-reconnect_at_eof", "1", "-reconnect_streamed", "1",
+ "-headers", douyin_headers,
+ "-i", real_url,
+ "-map", "0:a", "-f", "s16le", "-ac", str(DEMUCS_CHANNELS),
+ "-ar", str(DEMUCS_SAMPLE_RATE), "pipe:1",
+ ]
+
+ # FFmpeg 推流(视频 stdin + 音频 tcp)
+ vf = "fps=30,scale=720:1280:force_original_aspect_ratio=decrease:flags=lanczos,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black"
+ ffmpeg_out_cmd = [
+ "ffmpeg", "-y", "-loglevel", "info", "-nostdin",
+ "-f", "nut", "-i", "pipe:0",
+ "-f", "s16le", "-ac", str(DEMUCS_CHANNELS), "-ar", str(DEMUCS_SAMPLE_RATE),
+ "-i", tcp_audio_url,
+ "-map", "0:v", "-map", "1:a",
+ "-vf", vf,
+ "-c:v", nvenc_params.get("codec_v", "libx264"),
+ "-preset", nvenc_params.get("preset_v", "fast"),
+ "-profile:v", "high",
+ "-b:v", "2600k", "-maxrate", "3000k", "-bufsize", "5200k",
+ "-vsync", "cfr", "-g", "60", "-keyint_min", "60", "-r", "30",
+ "-bf", "0", "-sc_threshold", "0",
+ "-x264-params", "force-cfr=1:scenecut=0:open_gop=0:sync-lookahead=0",
+ "-pix_fmt", "yuv420p",
+ "-c:a", "aac", "-b:a", "128k", "-ar", "48000", "-ac", "2",
+ "-af", "aresample=async=1:first_pts=0",
+ "-flags", "+global_header", "-flvflags", "no_duration_filesize",
+ "-f", "flv", youtube_rtmp,
+ ]
+ if nvenc_params.get("tune_param"):
+ idx = ffmpeg_out_cmd.index("-profile:v")
+ for p in reversed(nvenc_params["tune_param"]):
+ ffmpeg_out_cmd.insert(idx, p)
+
+ proc_v = None
+ proc_a = None
+ proc_out = None
+ demucs_thread = None
+
+ try:
+ proc_v = subprocess.Popen(
+ ffmpeg_v_cmd,
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ bufsize=1024 * 1024,
+ )
+ proc_a = subprocess.Popen(
+ ffmpeg_a_cmd,
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL,
+ bufsize=1024 * 1024,
+ )
+ proc_out = subprocess.Popen(
+ ffmpeg_out_cmd,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ bufsize=1024 * 1024,
+ )
+
+ # 等待 TCP 连接(FFmpeg_out 会连接)
+ accept_thread.join(timeout=15)
+ if conn[0] is None:
+ raise RuntimeError("Demucs 管道: 音频 TCP 连接超时")
+
+ # 视频转发线程
+ def copy_video():
+ try:
+ while proc_v.poll() is None and proc_out.poll() is None:
+ chunk = proc_v.stdout.read(1024 * 256)
+ if not chunk:
+ break
+ proc_out.stdin.write(chunk)
+ except Exception:
+ pass
+ finally:
+ try:
+ proc_out.stdin.close()
+ except Exception:
+ pass
+
+ copy_t = threading.Thread(target=copy_video, daemon=True)
+ copy_t.start()
+
+ # Demucs 工作线程(从 proc_a 读,处理后写 conn)
+ # proc_a 输出是 stdout,conn 是 socket。需要把 proc_a.stdout 的数据通过 socket 发到 FFmpeg。
+ # FFmpeg_out 的第二个输入是 tcp,已经连接了 conn。所以我们从 proc_a 读,Demucs 处理,写 conn。
+ # 但 proc_a.stdout 是 pipe,conn 是 socket。我们需要一个线程读 proc_a 并写 conn。
+ # 注意:proc_a 输出到 stdout,我们读。conn 是 FFmpeg 作为 client 连上来的,所以 FFmpeg 在读,我们写 conn。
+ demucs_thread = threading.Thread(
+ target=_run_demucs_worker,
+ args=(proc_a.stdout, conn[0], model, device, stop_event),
+ daemon=True,
+ )
+ demucs_thread.start()
+
+ # 监控 proc_v 的 stderr(与源连接,用于检测下播)
+ stderr_q = queue.Queue()
+ reader_t = threading.Thread(target=stderr_reader_fn, args=(proc_v, stderr_q))
+ reader_t.daemon = True
+ reader_t.start()
+
+ start_time = last_frame_time = time.time()
+ proc_v.last_out_ts = time.time()
+
+ while True:
+ try:
+ line = stderr_q.get(timeout=stderr_queue_timeout)
+ except queue.Empty:
+ if time.time() - getattr(proc_v, "last_out_ts", time.time()) > stale_output_seconds:
+ print("[WARN] 长时间无任何日志,强制重启进程")
+ break
+ continue
+
+ if line is None:
+ if proc_v.poll() is not None:
+ break
+ continue
+
+ line = line.strip()
+ if line.startswith("__ERR__READER__"):
+ print(f"[ERROR] stderr reader 异常: {line}")
+ continue
+
+ if line:
+ proc_v.last_out_ts = time.time()
+ print(f"[FFmpeg] {line}")
+ line_lower = line.lower()
+ if any(kw in line_lower for kw in end_keywords):
+ print("[ERROR] 检测到主播真下播或被明确拒绝,停止推流")
+ raise stream_ended_exception()
+ if "frame=" in line_lower or "fps=" in line_lower:
+ last_frame_time = time.time()
+ if time.time() - start_time > start_check_after:
+ if time.time() - last_frame_time > no_frame_timeout:
+ print(f"[WARN] 连续 {no_frame_timeout}s 无新帧,判定主播已下播")
+ raise stream_ended_exception()
+
+ except stream_ended_exception:
+ raise
+ except Exception as e:
+ print(f"[ERROR] Demucs 管道异常: {e}")
+ import traceback
+ traceback.print_exc()
+ finally:
+ stop_event.set()
+ cleanup_proc_fn(proc_v)
+ cleanup_proc_fn(proc_a)
+ cleanup_proc_fn(proc_out)
+ try:
+ if conn[0]:
+ conn[0].close()
+ except Exception:
+ pass
+ try:
+ server.close()
+ except Exception:
+ pass
diff --git a/douyin_youtube_ffplay.py b/douyin_youtube_ffplay.py
index 9f492ac..cc44373 100644
--- a/douyin_youtube_ffplay.py
+++ b/douyin_youtube_ffplay.py
@@ -11,6 +11,17 @@ import re
import os
import configparser
+# Demucs 管道(可选,用于防版权)
+try:
+ from demucs_pipeline import (
+ _check_demucs_available,
+ run_demucs_pipeline,
+ )
+ _DEMUCS_MODULE_AVAILABLE = True
+except ImportError:
+ _DEMUCS_MODULE_AVAILABLE = False
+ _check_demucs_available = lambda: False
+
class StreamEnded(Exception):
"""主播真实下播的专用异常"""
@@ -85,19 +96,30 @@ def start_douyin_youtube_ffplay(
default_key = "qxvb-r47b-r5ju-6ud3-6k7z" # 备用默认 key
youtube_rtmp = rtmp_base + default_key # 默认值
+ enable_demucs = False
+ demucs_use_gpu = True
if os.path.exists(config_file):
config = configparser.ConfigParser()
config.read(config_file, encoding="utf-8")
if config.has_section("youtube"):
for key_name, value in config.items("youtube"):
- value = value.strip()
- if value:
- if value.startswith("rtmp://"):
- youtube_rtmp = value
- else:
- youtube_rtmp = rtmp_base + value
+ key_lower = key_name.strip().lower()
+ val = value.strip()
+ if key_lower == "enable_demucs":
+ enable_demucs = val.lower() in ("1", "true", "yes", "是")
+ elif key_lower == "demucs_use_gpu":
+ demucs_use_gpu = val.lower() not in ("0", "false", "no", "否")
+ elif val and key_lower in ("key", "stream_key", "streamkey"):
+ youtube_rtmp = val if val.startswith("rtmp://") else rtmp_base + val
print(f"[INFO] 从 {config_file} 读取 YouTube stream key: {youtube_rtmp}")
- break # 只取第一个有效 key,保持单路推流(不加多路逻辑)
+ # 兼容:若上面未匹配到 key,取第一个非配置项的值作为 stream key
+ if youtube_rtmp == rtmp_base + default_key:
+ for k, v in config.items("youtube"):
+ if k.strip().lower() not in ("enable_demucs", "demucs_use_gpu") and v.strip():
+ v = v.strip()
+ youtube_rtmp = v if v.startswith("rtmp://") else rtmp_base + v
+ print(f"[INFO] 从 {config_file} 读取 YouTube stream key: {youtube_rtmp}")
+ break
# ====================== 常量配置 ======================
MAX_RETRY_DELAY = 90
@@ -151,6 +173,14 @@ def start_douyin_youtube_ffplay(
except Exception as e:
print(f"[WARN] NVENC 检测失败,回退软件编码: {e}")
+ nvenc_params = {
+ "codec_v": codec_v,
+ "preset_v": preset_v,
+ "tune_param": tune_param,
+ "demucs_use_gpu": demucs_use_gpu,
+ }
+ use_demucs = enable_demucs and _DEMUCS_MODULE_AVAILABLE and _check_demucs_available()
+
# 720p (最原始)
ffmpeg_command = [
"ffmpeg", "-y", "-loglevel", "info", "-nostdin", "-re",
@@ -435,20 +465,45 @@ def start_douyin_youtube_ffplay(
stderr_q = queue.Queue()
print(f"[INFO][{datetime.datetime.now():%H:%M:%S}] 启动 YouTube 推流 → {stream_title}")
- proc = subprocess.Popen(
- ffmpeg_command,
- stdin=subprocess.DEVNULL,
- stdout=subprocess.DEVNULL,
- stderr=subprocess.PIPE,
- text=True,
- bufsize=1,
- universal_newlines=True
- )
- proc.last_out_ts = time.time()
- reader_t = threading.Thread(target=_stderr_reader_thread, args=(proc, stderr_q))
- reader_t.daemon = True
- reader_t.start()
+ proc = None
+ if use_demucs:
+ try:
+ run_demucs_pipeline(
+ real_url=real_url,
+ youtube_rtmp=youtube_rtmp,
+ douyin_headers=douyin_headers,
+ nvenc_params=nvenc_params,
+ end_keywords=END_KEYWORDS,
+ stderr_queue_timeout=STDERR_QUEUE_TIMEOUT,
+ no_frame_timeout=NO_FRAME_TIMEOUT,
+ start_check_after=START_CHECK_AFTER,
+ stale_output_seconds=STALE_OUTPUT_SECONDS,
+ stream_ended_exception=StreamEnded,
+ cleanup_proc_fn=cleanup_proc,
+ stderr_reader_fn=_stderr_reader_thread,
+ )
+ except StreamEnded:
+ raise
+ except Exception as e:
+ print(f"[WARN] Demucs 管道异常,回退原始推流: {e}")
+ use_demucs = False
+
+ if not use_demucs:
+ proc = subprocess.Popen(
+ ffmpeg_command,
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ text=True,
+ bufsize=1,
+ universal_newlines=True
+ )
+ proc.last_out_ts = time.time()
+
+ reader_t = threading.Thread(target=_stderr_reader_thread, args=(proc, stderr_q))
+ reader_t.daemon = True
+ reader_t.start()
start_time = last_frame_time = time.time()
diff --git a/requirements-youtube.txt b/requirements-youtube.txt
new file mode 100644
index 0000000..cd82fb8
--- /dev/null
+++ b/requirements-youtube.txt
@@ -0,0 +1,12 @@
+# YouTube 转播 + Demucs 人声分离(防版权)所需依赖
+# 在 requirements.txt 基础上额外安装:
+# pip install -r requirements.txt -r requirements-youtube.txt
+
+# CPU 版本
+demucs>=4.0.0
+torch>=2.0.0
+torchaudio>=2.0.0
+
+# 若使用 NVIDIA GPU,请安装 CUDA 版 PyTorch 后覆盖:
+pip uninstall torch torchaudio
+pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu118
diff --git a/requirements.txt b/requirements.txt
index 18f105b..58ced78 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,4 +4,11 @@ pycryptodome>=3.20.0
distro>=1.9.0
tqdm>=4.67.1
httpx[http2]>=0.28.1
-PyExecJS>=1.5.1
\ No newline at end of file
+PyExecJS>=1.5.1
+
+# 可选:YouTube 转播时 Demucs 人声分离(防版权识别)
+# pip install demucs torch torchaudio
+# GPU 版本: pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu118
+# demucs>=4.0.0
+# torch>=2.0.0
+# torchaudio>=2.0.0
\ No newline at end of file