由于前段想学习做切片视频,一方面担心用别人上传的 B站 录播会压缩画质,另一方面也没有每次蹲直播间打开电脑进行录播的条件,所以使用服务器进行自动化录制就显得很是必要。
而据了解 BILIUP 集自动化录制 + 投稿功能于一身,用着还算顺手。但是经实测,使用 BILIUP 进行 Twitch 平台的直播流录制时,很容易会拿到低清晰度的直播流,同时还经常出现在意料之外的地方进行分段,导致录制的质量很难保证。而使用 StreamCap 进行 Twitch 平台的直播流录制就不会出现类似情况,但是 StreamCap 是不带投稿模块的。
本文章展示的是使用 StreamCap 进行 Twitch 平台的直播流录制,同时通过 StreamCap 的自定义脚本执行命令调用 BILIUP 命令行进行自动投稿至 B站。同时也可以使用 BILIUP 进行 B站 的直播流录制和自动投稿。
一、服务器的选择
1.1、硬盘大小
根据平时使用 StreamCap 进行直播流录制的情况,一般1080P 60帧 6Mbps的直播流,连续录制两个小时所占用的存储空间约为5.2G。
手动计算为:
2h 的总字节数:6 * 1000kbps * 7200s = 432 * 10^5kb
转成KB:432 * 10^5kb / 8 = 54 * 10^5KB
转成GB:54 * 10^5KB / 1024^2 ≈ 5.15GB
那么,根据主播常播的时长,以及可能同时录制的直播间个数,就可以大致估算出需要的硬盘空间大小。
1-2个主播,建议至少选择20GB或以上规格的服务器。3-5个主播,建议至少选择50GB或以上规格的服务器。6-10个主播,建议至少选择100GB或以上规格的服务器。
注:有的主播的单次直播时长不是固定的,如果单个主播可能连续直播超过12h,那么至少需要12/2 * 5.2GB + 5GB盈余硬盘空间,即应选择40GB或以上硬盘规格的服务器。
1.2、带宽选择
需要自己实际测试,一般国内服务器带宽较小,带宽成本较高,不太建议做录播服务器。除非只录制1-2个直播间。
比如可能需要同时录制3个直播间,那么保险起见应选择(6Mbps * 3 = 18Mbps)20Mbps或以上带宽的服务器。
然而,vps服务器的带宽通常是共享的,实际上手测试出来的带宽往往低于标注的带宽,所以实际上还需要上手实测。
关于网络优化,录播服务器一般不用考虑对大陆是否有优化,主要因为优化线路成本相差比较大。下载录播视频到本地做切片时,如果线路真的很差(很难连接上或者跑不动),可以考虑用优化线路的机器拉,或换本地网络/vps线路再测试。对线路有要求的建议购买前在Bing或者Google上搜索相关测评再考虑是否入手。当然一般vps服务商也有提供退款政策,可提前了解。
1.3、核心和运行内存
只做录播服务器的话,不是很重要,根据前两点选择就好,一般都是溢出的。当然建议最低配置为1H1G。
1.4、商家
目前选择的是 CCS 家的 OUT OF STOCK SPECIALS - Holiday Special 4GB RAM,在网站https://colocross.ing/上筛选的,发贴时貌似已无货。目前选择的vps配置大致为 $20.99 USD / 3 vCPU / 4GB RAM / 60GB SSD / 40TB @ 1Gbps。测试同时录制2-3个直播流基本上没什么问题,偶尔可能网络不是很稳定。
商家和vps信息可在各种vps测评网站和论坛找到。由于本人没怎么使用过其它vps进行录播,所以不在此做推荐。
1.5、系统选择
个人习惯使用 Debian 系列,属于比较大众的服务器系统里面资源占用较少的。目前出的 Debian 13 即可,不必过多纠结。
1.6、录播项目的选择
- BILIUP:主要用来录制 B站 的主播,带
自动投稿。但在测试录制 Twitch 平台时经常出现低清晰度和多分段。可录弹幕,不过弹幕需要自己找工具转换和发送。 - StreamCap:主要用来录制 Twitch 的主播。缺点是本身
不带投稿模块,且存在开播延迟录制(约两三分钟),不带弹幕。
二、环境配置与部署
由于我第一次部署时,先部署 BILIUP 后部署 StreamCap ,没出现什么异常。但后面重装系统后无论怎么部署都会出现 StreamCap 不会自动检测开播的BUG,可能是两个项目的环境或依赖存在冲突吧。后面改用docker部署了,暂时没发现什么问题。主要步骤如下:
2.1、设置DNS为1.1.1.1 (可选)
修改DNS文件
echo -e "nameserver 1.1.1.1\nnameserver 8.8.8.8" > /etc/resolv.conf锁定该文件
chattr +i /etc/resolv.conf2.2、设置时区 (可选)
设置时区为上海
timedatectl set-timezone Asia/Shanghai验证
date2.3、开启 BBR 加速 (建议)
BBR 是 Google 开发的拥塞控制算法,对于 VPS 跨海访问的情况,能显著提升高丢包、高延迟环境下的速度。
修改内核参数
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf使配置生效
sysctl -p验证开启状态
sysctl net.ipv4.tcp_congestion_control2.4、更新系统包 (建议)
apt update && apt upgrade -y2.5、更改 SSH 默认端口 (建议)
编辑 /etc/ssh/sshd_config
nano /etc/ssh/sshd_config移动光标到行 #Port 22
手动去掉 # 后把端口改成 10000-60000 之间的高位端口,Ctrl+S保存,Ctrl+X退出
重启ssh
systemctl restart ssh2.6、安装基础依赖 (建议)
apt install curl wget git tar htop -y三、Docker 方式部署
3.1、安装 Docker
curl -fsSL https://get.docker.com | bash验证版本
docker --version
docker compose version3.2、创建 docker 总目录 (建议,方便管理)
创建 /opt/docker-apps 目录
mkdir -p /opt/docker-apps设置权限
chown -R $USER:$USER /opt/docker-apps3.3、编辑 docker-compose.yml (示例为 /opt/docker-apps 目录下)
进入 /opt/docker-apps 目录,编辑该目录下的 docker-compose.yml 文件
cd /opt/docker-apps
nano docker-compose.yml粘贴如下内容后Ctrl+S保存,Ctrl+X退出
services:
biliup:
image: ghcr.io/biliup/caution:latest
container_name: biliup
restart: unless-stopped
volumes:
- ./biliup:/opt
- ./biliup/backup:/opt/backup
- ./streamcap/downloads:/opt/downloads
ports:
- "19159:19159"
environment:
- TZ=Asia/Shanghai
streamcap:
image: ihmily/streamcap
container_name: streamcap
restart: unless-stopped
tty: true
ports:
- "6006:6006"
environment:
- PLATFORM=web
- HOST=0.0.0.0
- PORT=6006
- TZ=Asia/Shanghai
volumes:
- ./streamcap/.env:/app/.env
- ./streamcap/config:/app/config
- ./streamcap/downloads:/app/downloads
- ./streamcap/logs:/app/logs
- ./streamcap/locales:/app/locales
- ./streamcap/assets:/app/assets
- ./streamcap/upload.sh:/app/upload.sh # 自动投稿脚本
- /var/run/docker.sock:/var/run/docker.sock # 用于测试 docker 命令
- /usr/bin/docker:/usr/bin/docker # 挂载宿主机 docker 二进制
healthcheck:
test: ["CMD", "sh", "-c", "curl -f http://localhost:6006/about"]
interval: 30s
timeout: 10s
retries: 3
video_api:
image: ihmily/streamcap
container_name: video_api
restart: unless-stopped
command: python -m app.api.video_stream_service
ports:
- "6007:6007"
environment:
- TZ=Asia/Shanghai
- CUSTOM_VIDEO_ROOT_DIR=/app/downloads
volumes:
- ./streamcap/downloads:/app/downloads
- ./streamcap/.env:/app/.env
depends_on:
- streamcap
miniserve: # 新增的http服务端,用于在网页端快速下载录播/弹幕文件
image: svenstaro/miniserve
container_name: miniserve
restart: unless-stopped
command: -z -r /data # -z 启用压缩 -r 启用目录浏览
ports:
- "8080:8080" # 映射到宿主机 8080 端口,可按需修改
volumes:
- ./biliup/backup:/data # 暴露 biliup/backup 备份目录(也可换成 ./streamcap/downloads)
environment:
- TZ=Asia/Shanghai
networks:
default:
driver: bridge3.4、创建必须的文件
定位到 /opt/docker-apps 目录
cd /opt/docker-apps创建必须的空文件夹(防止报错)
mkdir -p biliup streamcap克隆StreamCap完整源码
git clone https://github.com/ihmily/StreamCap.git streamcap复制配置环境文件
cd streamcap
cp .env.example .env编辑配置环境文件,手动修改平台为网页端、后台对外暴露后Ctrl+S保存,Ctrl+X退出PLATFORM=webHOST=0.0.0.0
nano .env3.5、执行 docker 部署
# 定位到 /opt/docker-apps 目录
cd /opt/docker-apps
# 关闭并删除当前目录下所有容器和临时数据
docker compose down -v
# 后台重新启动所有服务(重新创建容器:本地无镜像则拉,有就用本地)
docker compose up -dBiliup 面板:http:// 你的 IP:19159
StreamCap 面板:http:// 你的 IP:6006
miniserve 面板:http:// 你的 IP:8080
四、常用命令
4.1、检查是否全部运行
docker ps4.2、拉取最新镜像
docker compose pull4.3、重启服务(用新镜像)
docker compose up -d4.4、只拉 StreamCap 镜像
docker compose pull streamcap4.5、只重启 StreamCap 和依赖它的 video_api
docker compose up -d streamcap4.6、清理掉不再用的旧镜像
docker image prune -f4.7、(已弃用)在 biliup 目录下创建 streamcap/downloads/Twitch 的快捷方式,以便于快速跳转 (示例主播:飯絲児ファンシ)
ln -s /opt/docker-apps/streamcap/downloads/Twitch/飯絲児ファンシ /opt/docker-apps/biliup/飯絲児ファンシ4.8、(已弃用)一键移动 streamcap 的 Twitch 主播录播文件到 biliup 目录下,以便于使用 biliup 网页端投稿和预览 (示例主播:飯絲児ファンシ)
mv /opt/docker-apps/streamcap/downloads/Twitch/飯絲児ファンシ/* /opt/docker-apps/biliup/4.9、(已弃用)一键删除 biliup 目录下指定的主播录播文件,以腾出空间 (示例主播:飯絲児ファンシ),删除前注意是否已经投稿/下载到本地
rm -f /opt/docker-apps/biliup/飯絲児ファンシ*.mp4五、常用 Biliup 投稿模板
5.1、模板名
根据实际情况自己修改
主播名【直播回放】5.2、投稿标题
自动投稿,无需修改
【%Y-%m-%d】【{streamer}】{title}手动投稿,除了日期会取当天,其它的不会自动识别,需要根据情况自己修改
【%Y-%m-%d】【主播名】标题5.3、转载说明
根据情况自己修改
飯絲児 Twitch 直播间:https://www.twitch.tv/fancy_ito5.4、标签
根据情况自己修改
直播回放,飯絲児ファンシ,fancy_ito,VTuber,虚拟主播,゙悅音幻絲5.5、简介
根据情况自己修改
【゙悅音幻絲】
个人空间:https://space.bilibili.com/19366645
直播间:https://live.bilibili.com/473323
平时回放在【饭丝儿IV岁啦w_录播姬】
https://space.bilibili.com/1186069249
关注饭丝喵谢谢喵~六、StreamCap 自动投稿的配置
6.1、在streamcap目录下新建 upload.sh 文件
跳转到/opt/docker-apps/streamcap目录并编辑upload.sh
cd /opt/docker-apps/streamcap
nano upload.sh粘贴如下内容后Ctrl+S保存,Ctrl+X退出
#!/bin/bash
# ============================================
# biliup 自动投稿脚本(完全配置化版)
# 所有参数均可由主播配置文件覆盖,未配置则自动生成。
# 新增:日志轮转(超过2MB自动切分,保留3个备份)
# ============================================
# --- 0. 初始化与日志设置 ---
STREAMER="$1"
STREAMER="${STREAMER#\"}"
STREAMER="${STREAMER%\"}"
STREAMER="${STREAMER#\'}"
STREAMER="${STREAMER%\'}"
PATH_TEMPLATE="$2"
PATH_TEMPLATE="${PATH_TEMPLATE#\"}"
PATH_TEMPLATE="${PATH_TEMPLATE%\"}"
PATH_TEMPLATE="${PATH_TEMPLATE#\'}"
PATH_TEMPLATE="${PATH_TEMPLATE%\'}"
FORMAT="$3"
SPLIT_FLAG="$4"
CONVERT_FLAG="$5"
LOG_FILE="/app/logs/upload.log"
CONFIG_DIR="/app/config"
LOCK_DIR="/tmp/streamcap_locks"
mkdir -p "$LOCK_DIR"
# --- 日志轮转函数 ---
rotate_log() {
local log_file="$1"
local max_size=$((2 * 1024 * 1024)) # 2MB
local keep_count=3
if [ ! -f "$log_file" ]; then
return
fi
# 获取文件大小(兼容 Linux 和 macOS)
local size
size=$(stat -c%s "$log_file" 2>/dev/null || stat -f%z "$log_file" 2>/dev/null)
if [ -z "$size" ] || [ "$size" -le "$max_size" ]; then
return
fi
# 轮转:移动旧日志并创建新文件
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
mv "$log_file" "${log_file}.${timestamp}"
touch "$log_file"
echo "$(date): 日志文件已轮转(大小超过 $((max_size/1024/1024))MB)" >> "$log_file"
# 清理旧备份,只保留最近 $keep_count 个
local backups
backups=($(ls -t "${log_file}."* 2>/dev/null))
if [ ${#backups[@]} -gt "$keep_count" ]; then
local to_delete=("${backups[@]:$keep_count}")
for f in "${to_delete[@]}"; do
rm -f "$f"
done
fi
}
# 在首次写日志前执行轮转检查
rotate_log "$LOG_FILE"
# 定义日志函数(在轮转之后)
log() {
echo "$(date): $*" >> "$LOG_FILE"
}
log "脚本被触发,主播: $STREAMER"
# 检查 docker 命令
if ! command -v docker &> /dev/null; then
log "docker 命令不可用,尝试安装 docker-cli ..."
if command -v apk &> /dev/null; then
apk add --no-cache docker-cli >> "$LOG_FILE" 2>&1
if ! command -v docker &> /dev/null; then
log "安装 docker-cli 失败,请手动安装。"
exit 1
fi
else
log "非 Alpine 系统,无法自动安装 docker-cli,请手动安装。"
exit 1
fi
fi
# --- 1. 解析真实文件路径 ---
resolve_real_path() {
local template_path="$1"
template_path="${template_path#\"}"
template_path="${template_path%\"}"
if [ -f "$template_path" ]; then
echo "$template_path"
return 0
fi
local pattern=$(echo "$template_path" | sed 's/%0[0-9]d/*/g')
if [[ "$pattern" != "$template_path" ]]; then
shopt -s nullglob
local matched_files=($pattern)
shopt -u nullglob
if [ ${#matched_files[@]} -gt 0 ]; then
local sorted_file=$(printf '%s\n' "${matched_files[@]}" | sort | head -n1)
echo "$sorted_file"
return 0
fi
fi
return 1
}
REAL_FILE_PATH=$(resolve_real_path "$PATH_TEMPLATE")
if [ -z "$REAL_FILE_PATH" ]; then
log "错误 - 无法获取有效的录制文件路径,脚本退出。传入值: '$PATH_TEMPLATE'"
exit 1
fi
log "处理文件: $REAL_FILE_PATH"
STREAMER_DIR=$(dirname "$REAL_FILE_PATH")
BASE_PREFIX=$(basename "$REAL_FILE_PATH" | sed 's/_[0-9][0-9][0-9]\.mp4$//' | sed 's/_%03d$//')
log "主播目录: $STREAMER_DIR, 文件前缀: $BASE_PREFIX"
# --- 提取直播日期 ---
LIVE_DATE=$(echo "$BASE_PREFIX" | grep -oP '\d{4}-\d{2}-\d{2}' | head -1)
if [ -z "$LIVE_DATE" ]; then
LIVE_DATE=$(date +%Y-%m-%d)
log "警告: 无法从文件名提取日期,使用当前日期 $LIVE_DATE"
else
log "提取直播日期: $LIVE_DATE"
fi
# --- 2. 会话锁机制 ---
get_session_id() {
local first_file_path="$1"
local dir_name=$(dirname "$first_file_path")
local first_file_name=$(basename "$first_file_path")
local session_start_time=$(echo "$first_file_name" | grep -oP '\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}' | head -1)
if [ -n "$session_start_time" ]; then
session_start_time=$(echo "$session_start_time" | sed 's/[-_]//g')
else
session_start_time=$(date +%Y%m%d)
fi
echo "${dir_name}_${session_start_time}_session" | sed 's|/|_|g'
}
SESSION_ID=$(get_session_id "$REAL_FILE_PATH")
LOCK_FILE="${LOCK_DIR}/${SESSION_ID}.lock"
if [ -f "$LOCK_FILE" ]; then
LOCK_MTIME=$(stat -c %Y "$LOCK_FILE" 2>/dev/null)
NOW=$(date +%s)
if [ -n "$LOCK_MTIME" ] && [ $((NOW - LOCK_MTIME)) -gt 1800 ]; then
log "发现过期锁文件(超过30分钟),强制删除: $LOCK_FILE"
rm -f "$LOCK_FILE"
fi
fi
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
log "已有投稿任务在执行(锁文件: $LOCK_FILE),本脚本退出。"
exit 0
fi
log "获取锁成功: $LOCK_FILE"
cleanup() {
flock -u 200 2>/dev/null
rm -f "$LOCK_FILE"
log "已释放锁并清理锁文件"
}
trap cleanup EXIT
# --- 3. 等待写入完成 ---
sleep 60
# --- 4. 收集同前缀的所有分段 ---
ALL_SEGMENTS=$(find "$STREAMER_DIR" -maxdepth 1 -name "${BASE_PREFIX}_*.mp4" 2>/dev/null | sort)
if [ -z "$ALL_SEGMENTS" ]; then
log "未找到任何可投稿的视频文件,退出。"
exit 1
fi
SEGMENT_FILES=$(echo "$ALL_SEGMENTS" | sort -u)
SEGMENT_COUNT=$(echo "$SEGMENT_FILES" | wc -l)
log "共找到 $SEGMENT_COUNT 个分段文件"
convert_path() { echo "$1" | sed 's|/app/downloads|/opt/downloads|'; }
# --- 5. 从日志提取直播信息(支持日志轮转,搜索 streamget*) ---
get_live_info_from_log() {
local streamer="$1"
local log_files=()
# 收集所有 streamget 开头的日志文件(包括轮转文件),按修改时间从新到旧排序
while IFS= read -r f; do
log_files+=("$f")
done < <(find /app/logs -maxdepth 1 -name "streamget*" -type f -printf '%T@ %p\n' 2>/dev/null | sort -rn | cut -d' ' -f2-)
# 如果没找到任何 streamget 文件,再尝试查找其他 .log 文件(兼容)
if [ ${#log_files[@]} -eq 0 ]; then
while IFS= read -r f; do
log_files+=("$f")
done < <(find /app/logs -name "*.log" -type f -printf '%T@ %p\n' 2>/dev/null | sort -rn | cut -d' ' -f2-)
fi
if [ ${#log_files[@]} -eq 0 ]; then
log "未找到任何 StreamCap 日志文件"
return 1
fi
# 转义主播名中的特殊字符
local escaped_streamer=$(echo "$streamer" | sed 's/[][\.*^$()+?{}|]/\\&/g')
# 遍历日志文件(从新到旧)
for lf in "${log_files[@]}"; do
# 从最新行开始读取,查找包含主播名且含有 title= 的记录
local raw_line=$(tac "$lf" 2>/dev/null | grep -m1 "anchor_name=['\"]$escaped_streamer" | grep "title=")
if [ -n "$raw_line" ]; then
local title=""; local live_url=""
if [[ "$raw_line" =~ title=\'([^\']+)\' ]]; then
title="${BASH_REMATCH[1]}"
elif [[ "$raw_line" =~ title=\"([^\"]+)\" ]]; then
title="${BASH_REMATCH[1]}"
fi
if [[ "$raw_line" =~ live_url=\'([^\']+)\' ]]; then
live_url="${BASH_REMATCH[1]}"
elif [[ "$raw_line" =~ live_url=\"([^\"]+)\" ]]; then
live_url="${BASH_REMATCH[1]}"
fi
if [ -n "$title" ] && [ -n "$live_url" ]; then
log "从日志文件 $lf 提取成功: title=$title, live_url=$live_url"
echo "${title}||${live_url}"
return 0
fi
fi
done
log "在所有日志文件中均未找到包含主播名 '$streamer' 的记录"
return 1
}
LIVE_INFO=$(get_live_info_from_log "$STREAMER")
if [ -n "$LIVE_INFO" ]; then
LIVE_TITLE="${LIVE_INFO%%||*}"
LIVE_URL="${LIVE_INFO##*||}"
else
LIVE_TITLE=""
LIVE_URL=""
fi
# --- 6. 加载主播配置(可覆盖标题、描述、来源等) ---
# 默认值
DEFAULT_TID="21"
DEFAULT_TAG="直播回放,Twitch,${STREAMER//,/、}"
DEFAULT_COPYRIGHT="2"
DEFAULT_LINE="txa"
DEFAULT_SOURCE_FORMAT="%s Twitch 直播间:%s"
# 先设置默认值
TID="$DEFAULT_TID"
TAG="$DEFAULT_TAG"
COPYRIGHT="$DEFAULT_COPYRIGHT"
LINE="$DEFAULT_LINE"
SOURCE_FORMAT="$DEFAULT_SOURCE_FORMAT"
# 配置文件可能定义的覆盖变量
TITLE=""
DESCRIPTION=""
SOURCE_INFO=""
# 加载配置文件(如果存在)
conf_file="${CONFIG_DIR}/${STREAMER}.conf"
if [ -f "$conf_file" ]; then
# shellcheck source=/dev/null
source "$conf_file"
log "加载主播 $STREAMER 专属配置"
fi
# --- 7. 自动生成未配置的参数 ---
# 标题:如果配置文件未设置 TITLE,则自动生成
if [ -z "$TITLE" ]; then
if [ -n "$LIVE_TITLE" ]; then
TITLE="【${LIVE_DATE}】【$STREAMER】$LIVE_TITLE"
else
TITLE="【${LIVE_DATE}】【$STREAMER】直播回放"
fi
fi
# --- 标题长度截断(B站限制80字符) ---
MAX_TITLE_LEN=80
if [ ${#TITLE} -gt $MAX_TITLE_LEN ]; then
# 保留固定部分 "【日期】【主播】",截断直播标题部分
FIXED_PART="【${LIVE_DATE}】【${STREAMER}】"
FIXED_LEN=${#FIXED_PART}
# 计算直播标题允许的最大长度(留出 "..." 的3个字符)
MAX_LIVE_TITLE_LEN=$((MAX_TITLE_LEN - FIXED_LEN - 3))
if [ $MAX_LIVE_TITLE_LEN -gt 0 ] && [ -n "$LIVE_TITLE" ]; then
# 如果原始标题中有直播标题,截断它
ORIGINAL_LIVE_TITLE="$LIVE_TITLE"
if [ ${#ORIGINAL_LIVE_TITLE} -gt $MAX_LIVE_TITLE_LEN ]; then
LIVE_TITLE="${ORIGINAL_LIVE_TITLE:0:$MAX_LIVE_TITLE_LEN}..."
fi
# 重新构建标题
TITLE="${FIXED_PART}${LIVE_TITLE}"
else
# 如果无法区分固定部分,则直接截断整个标题
TITLE="${TITLE:0:$MAX_TITLE_LEN}"
fi
log "警告:标题超过80字符,已截断至 ${#TITLE} 字符"
fi
# 描述:如果未设置 DESCRIPTION,则自动使用直播标题
if [ -z "$DESCRIPTION" ]; then
DESCRIPTION="$LIVE_TITLE"
fi
# 来源信息:如果未设置 SOURCE_INFO,则根据 SOURCE_FORMAT 生成
if [ -z "$SOURCE_INFO" ]; then
if [ -n "$LIVE_URL" ]; then
SOURCE_INFO=$(printf "$SOURCE_FORMAT" "$STREAMER" "$LIVE_URL")
else
SOURCE_INFO=$(printf "$SOURCE_FORMAT" "$STREAMER" "https://www.twitch.tv/${STREAMER}")
fi
fi
log "最终标题: $TITLE"
log "最终描述: $DESCRIPTION"
log "最终来源: $SOURCE_INFO"
# --- 8. 执行投稿 ---
cmd=(docker exec -w /opt biliup biliup upload)
while IFS= read -r line; do cmd+=("$(convert_path "$line")"); done <<< "$SEGMENT_FILES"
cmd+=(--title "$TITLE" --tid "$TID" --tag "$TAG" --copyright "$COPYRIGHT" --line "$LINE" --source "$SOURCE_INFO" --desc "$DESCRIPTION")
log "执行投稿: ${cmd[*]}"
"${cmd[@]}" >> "$LOG_FILE" 2>&1
UPLOAD_EXIT=$?
# --- 9. 清理 ---
if [ $UPLOAD_EXIT -eq 0 ]; then
log "投稿成功,清理文件。"
while IFS= read -r line; do rm -f "$line"; done <<< "$SEGMENT_FILES"
else
log "投稿失败,保留文件待处理。"
fi
exit $UPLOAD_EXIT6.2、StreamCap 网页端设置
打开StreamCap网页端,切换到设置,在录制设置项目下的自定义脚本执行命令处输入/app/upload.sh。
切换到录制,检查主播名是否为主播对外显示的昵称,默认添加的可能带直播间ID,如稻乙緹-iitifox,应改成稻乙緹。
6.3、将宿主机的 Docker 二进制文件挂载到容器内,实现Docker项目中调用Docker命令
执行which docker确认宿主机 Docker 路径为/usr/bin/docker
前面已在docker-compose.yml文件中streamcap服务的volumes中添加挂载- /usr/bin/docker:/usr/bin/docker # 挂载宿主机 docker 二进制
6.4、重启docker
重启后记得手动打开一次StreamCap网页端,不然StreamCap可能不会开始工作。
docker compose down
docker compose up -d6.5、验证docker命令是否可用
如果显示版本信息,则挂载成功。
docker exec -it streamcap docker --version6.6、upload.sh调用的主播投稿模板
在/opt/docker-apps/streamcap/config目录下新建主播名.conf
如(以主播飯絲児ファンシ为例)
切换到该配置目录
cd /opt/docker-apps/streamcap/config编辑主播飯絲児ファンシ配置文件,主播名根据需要自己修改
nano 飯絲児ファンシ.conf粘贴预先写好的内容,根据需要自己修改(一般只需要设置标签和简介,标题自动生成,来源如果生成的有问题也可以在配置文件固定写好,去掉前面的#注释符)
然后Ctrl+S保存,Ctrl+X退出
# 标题(若不写则自动生成)
# TITLE="【2026-06-21】【飯絲児ファンシ】直播录播"
# 分区
# TID="21"
# 标签
TAG="直播回放,飯絲児ファンシ,悅音幻絲,fancy_ito"
# 来源信息
# SOURCE_INFO="飯絲児ファンシ Twitch 直播间:https://www.twitch.tv/fancy_ito"
# 覆盖描述
DESCRIPTION="【゙悅音幻絲】
个人空间:https://space.bilibili.com/19366645
直播间:https://live.bilibili.com/473323
平时回放在【饭丝儿IV岁啦w_录播姬】
https://space.bilibili.com/1186069249
关注饭丝喵谢谢喵~"
# 线路
# LINE="txa"6.7、Biliup命令行登录
切换到/opt/docker-apps/目录,执行登录,未登录会找不到cookie文件导致投稿失败
cd /opt/docker-apps
docker exec -it biliup biliup login 


































































































































