补充说明
本教程介绍 fan-video-tr 从源码编译、发布 GitHub Release 到目标服务器一键安装、升级的完整流程,适用于个人设备视频转码自建发布链路。
项目:fan-video-tr|视频转码工具(基于 FFmpeg,单二进制 + 原生前端内嵌,无账号密码的内网工具) 仓库:https://github.com/meimolihan/fan-video-tr 编译发布主机:fnOS(Debian,本机 Go/make 环境齐全) 部署测试主机:Debian Linux(amd64/arm64) 运行环境:Go 1.25+(
go.mod指定go 1.25.0)
重要规则
- GitHub 不允许直接覆盖已发布 Release,正式环境版本号单向递增;内测可删除重建同名 tag。
- install.sh 使用
releases/latest/download,脚本不需要硬编码版本号。- 编译机器 ≠ 业务运行机器;fnOS 只做编译发布,业务跑在目标主机。
- 前端为原生 HTML/CSS/JS(
internal/embedded/web),已通过go:embed内嵌二进制,无 Node/React 构建步骤;改前端必须重新编译二进制。- 发布脚本的 TAG 必须带
v前缀(vX.Y.Z),例如./scripts/build-and-push.sh v1.0.1;传1.0.1会被正则^v[0-9]+\.[0-9]+\.[0-9]+$直接拒绝。- 发布脚本会校验工作区无未提交改动、且本地 main 与远端一致,发版前先
git add/git pull。- 二进制通过外部命令调用
ffmpeg/ffprobe(不是静态内嵌 ffmpeg 库),目标机需安装;容器镜像内已apk add ffmpeg。- 二进制为纯 Go(
CGO_ENABLED=0),不含 cgo 依赖,amd64/arm64 产物行为一致。
一、编译发布机(fnOS)环境准备
1.1 安装依赖
apt update
apt install git curl build-essential
Go 1.25+(本机 /usr/local/go/bin/go,确保 go version 正常),Make,python3(发布脚本用它改写 internal/version/version.go)。
本机跑服务调试时另需 ffmpeg(目标机由 install.sh 自动安装):
apt install -y ffmpeg
1.2 安装 gh 命令行工具并授权 GitHub
gh auth login
选择 GitHub.com → SSH → 网页授权登录,务必勾选 repo 权限。
触发发布流水线还需要 workflow 权限(gh workflow run 会用到)。
校验授权结果
gh auth status
输出必须包含:Token scopes: admin:public_key, gist, read:org, repo, workflow
1.3 拉取源码
git clone git@github.com:meimolihan/fan-video-tr.git
cd fan-video-tr
go mod download
项目目录结构
fan-video-tr/
├── scripts/install.sh # 远程一键安装脚本(systemd,无 systemd 回退后台运行)
├── scripts/uninstall.sh # 卸载脚本
├── scripts/build-and-push.sh # 升版+打tag+推代码+触发 release.yml
├── scripts/build-release.sh # 本地交叉编译 dist/(amd64+arm64+sha256+脚本)
├── internal/version/version.go # 版本号(唯一来源,ldflags 注入)
├── internal/embedded/web/ # 前端(原生 HTML/CSS/JS,go:embed 内嵌)
├── internal/{config,ffmpeg,handler,logger,service}/ # 后端
├── main.go / server.go # 入口与 Web 服务
├── cli_ui.go / cli_style.go # CLI 子命令与终端样式
├── status_cmd.go / service_cmd.go / uninstall_cmd.go # status/start|stop|restart/uninstall
├── Makefile # build/build-all/run/test/docker/docker-push/install/uninstall
├── Dockerfile # 多阶段构建,运行镜像内置 ffmpeg
├── docker/entrypoint.sh # 容器入口:权限自愈 + su-exec 降权到 fvt
├── docker-compose.yml # 本地一键 Compose 部署
├── config.example.yaml # 配置示例(FVT_ 环境变量映射)
└── .github/workflows/ # ci.yaml(push/PR)+ release.yml(workflow_dispatch 发版)
二、配置一键安装脚本
确认 scripts/install.sh 头部常量(无需改动,默认已正确定义):
APP_NAME="fan-video-tr"
DEFAULT_PORT=8790
DEFAULT_INSTALL_DIR="/var/lib/fan-video-tr"
RECORD_FILE="/etc/fan-video-tr.conf"
SERVICE_FILE="/etc/systemd/system/fan-video-tr.service"
WRAPPER_FILE="/usr/local/bin/fan-video-tr"
Release 下载地址与流水线产物命名必须一致(fan-video-tr_linux_amd64 / fan-video-tr_linux_arm64):
REL_URL="https://github.com/meimolihan/fan-video-tr/releases/latest/download/fan-video-tr_linux_${REL_ARCH}"
提交推送至 main 分支
git add scripts/install.sh
git commit -m "chore: update install script"
git push origin main
三、构建与发布
3.1 本地构建与自测
make vet # go vet ./...
make test # go vet + go test(含 internal/ffmpeg、internal/service 单测)
make build # 产物 bin/fan-video-tr,版本号/提交号/构建时间自动注入 ldflags
make run # 本地启动(-port 8790 -media 当前目录 -data ./data)
本地运行验证
./bin/fan-video-tr -port 8790 -data ./data -media /vol2/1000/downloads/fan-video-tr
curl -s http://127.0.0.1:8790/api/health
curl -s http://127.0.0.1:8790/api/capabilities # 确认软件编码族与硬件加速自检结果
3.2 一键发布(推荐,CI 出包)
./scripts/build-and-push.sh v1.0.1 --yes -m "本次新增 xxx"
内部流程:校验 TAG 格式 → CNB 同名仓库检测/创建(可选)→ 校验工作区干净 → python3 改写 internal/version/version.go → 生成 RELEASE_NOTES.md → git commit + 打 tag v1.0.1 → git push origin main + git push origin v1.0.1 → gh workflow run release.yml -f tag=v1.0.1 --ref v1.0.1 → 轮询并美化输出流水线状态。
3.3 Release 流水线(GitHub Actions)
release.yml(仅 workflow_dispatch 触发,日常 push/PR 不会触发发版)按顺序执行:
build-binaries:ubuntu-24.04构建fan-video-tr_linux_amd64/fan-video-tr_linux_arm64+sha256sums.txt(先校验 tag 格式与version.go一致性)publish-release:创建/更新 GitHub Release 并上传上述附件(说明取RELEASE_NOTES.md)build-docker:buildx 构建linux/amd64,linux/arm64镜像并推送- Docker Hub:
mobufan/fan-video-tr:v1.0.1、mobufan/fan-video-tr:latest - GHCR:
ghcr.io/meimolihan/fan-video-tr:v1.0.1、ghcr.io/meimolihan/fan-video-tr:1.0.1
- Docker Hub:
sync-cnb:同步镜像仓库到cnb.cool/meimolihan/fan-video-tr(continue-on-error: true,失败不阻塞 Release)
所需 Secrets:DOCKERHUB_USERNAME、DOCKERHUB_TOKEN、CNB_ACCESS_TOKEN(未配置则跳过 CNB 同步)。
3.4 本地交叉编译 Release 产物(离线/自建发布)
bash scripts/build-release.sh # amd64 + arm64
bash scripts/build-release.sh amd64 # 仅单架构
产物(dist/):
dist/
├── fan-video-tr_linux_amd64
├── fan-video-tr_linux_arm64
├── sha256sums.txt
├── install.sh
├── uninstall.sh
├── config.example.yaml
└── RELEASE_NOTES.md
或手工等价命令:
export VERSION=1.0.1
PKG=github.com/meimolihan/fan-video-tr
rm -rf dist && mkdir -p dist
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w -X ${PKG}/internal/version.Version=${VERSION}" -o dist/fan-video-tr_linux_amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "-s -w -X ${PKG}/internal/version.Version=${VERSION}" -o dist/fan-video-tr_linux_arm64 .
sha256sum dist/fan-video-tr_linux_* > dist/sha256sums.txt
3.5 创建 GitHub Release 并上传二进制(离线模式)
VERSION=1.0.1
gh release create "v${VERSION}" \
dist/fan-video-tr_linux_amd64 \
dist/fan-video-tr_linux_arm64 \
dist/sha256sums.txt \
--title "v${VERSION}" \
--notes "fan-video-tr - Local Video Transcode Tool"
校验 Release
访问:https://github.com/meimolihan/fan-video-tr/releases/tag/v1.0.1
- 状态:Published,不是 Draft 草稿
- Assets 内必须存在
fan-video-tr_linux_amd64与fan-video-tr_linux_arm64,不要只看源码 zip/tar.gz。
测试下载链接有效性
curl -IL https://github.com/meimolihan/fan-video-tr/releases/latest/download/fan-video-tr_linux_amd64
返回 302 Found 正常;返回 404 代表附件缺失或没有已发布 Release。
四、本地构建 Docker 镜像
make docker # docker build -t mobufan/fan-video-tr:$(VERSION) -t ...:latest .
make docker-push # buildx --platform linux/amd64,linux/arm64 --push
或手工多架构构建
docker buildx build --platform linux/amd64,linux/arm64 \
--build-arg FVT_VERSION=1.0.1 \
--build-arg GOPROXY=https://goproxy.cn,direct \
-t mobufan/fan-video-tr:1.0.1 -t mobufan/fan-video-tr:latest \
--push .
镜像要点:golang:1.25-alpine 构建 → alpine:3.20 运行,apk add ffmpeg su-exec tzdata ca-certificates,内置 fvt(uid 1000) 用户、VOLUME /data、EXPOSE 8790、HEALTHCHECK /api/health,入口 docker/entrypoint.sh 负责卷权限自愈并 su-exec fvt 降权启动。
五、目标服务器远程一键安装
目标服务器需要 root 权限,已安装 curl。
5.1 预检查国内网络
curl -sSL https://raw.githubusercontent.com/meimolihan/fan-video-tr/main/scripts/install.sh
输出 shell 脚本内容 = 网络正常;卡住超时需要网络代理。 二进制下载走 GitHub Release,脚本已内置
ghfast.top/ghproxy.net/gh.xxooo.cf/githubproxy.cc逐个回退。
5.2 执行一键安装
bash -c "$(curl -sSL https://raw.githubusercontent.com/meimolihan/fan-video-tr/main/scripts/install.sh)"
交互步骤:
请输入监听端口 [默认: 8790]:回车使用默认,或填写自定义端口请输入视频浏览根目录 [可选,留空浏览全盘]:可指定媒体目录(如/vol2/1000/downloads/fan-video-tr)- 脚本检测/安装 ffmpeg,下载二进制(或使用本地
-b产物),写入/usr/local/bin/fan-video-tr软链 - 写入安装记录
/etc/fan-video-tr.conf,生成 systemd 单元并enable+restart - 输出 Web 访问地址;无账号密码(内网工具)
静默安装
bash -c "$(curl -sSL https://raw.githubusercontent.com/meimolihan/fan-video-tr/main/scripts/install.sh)" -p 8790 -d /var/lib/fan-video-tr -s /vol2/1000/downloads/fan-video-tr -y
非 systemd 环境(容器/受限环境)脚本自动回退为
nohup后台运行,日志写入<数据目录>/fan-video-tr.log,重启后不会自动恢复。
部署后运维命令
systemctl status fan-video-tr
journalctl -u fan-video-tr -f
systemctl restart fan-video-tr
fan-video-tr status # PID / 端口 / 内存 / 健康检查
fan-video-tr --version
访问示例:http://10.10.10.251:8790(无账号密码)
六、一键卸载
bash -c "$(curl -sSL https://raw.githubusercontent.com/meimolihan/fan-video-tr/main/scripts/uninstall.sh)"
交互:
是否清除数据目录 ... ?默认保留,-y --purge可一并清除(不可恢复)
也可直接用二进制自带子命令:sudo fan-video-tr uninstall -y --purge
卸载完成:systemd 服务移除、二进制与 /usr/local/bin 软链删除、安装记录清除。
七、新版本发布两种模式
模式 1:递增新版本(正式环境,推荐)
不要复用旧 tag,版本号向上迭代,例如 v1.0.1 → v1.0.2
cd /vol1/1000/GitHub/fan-video-tr
./scripts/build-and-push.sh v1.0.2 --yes
目标服务器升级,直接重跑安装脚本,自动拉取 latest,覆盖二进制,systemd 自动重启
bash -c "$(curl -sSL https://raw.githubusercontent.com/meimolihan/fan-video-tr/main/scripts/install.sh)"
fan-video-tr --version
模式 2:删除重建同名 Release(仅限内测,无线上用户)
⚠️ 已有服务器依赖该版本绝对禁止执行,会造成旧版本下载 404。
cd /vol1/1000/GitHub/fan-video-tr
# 删除github release
gh release delete v1.0.1 --yes
# 删除本地与远端git tag
git tag -d v1.0.1
git push origin --delete v1.0.1
# 重新编译并重建release上传附件
export VERSION=1.0.1
PKG=github.com/meimolihan/fan-video-tr
rm -rf dist && mkdir -p dist
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w -X ${PKG}/internal/version.Version=${VERSION}" -o dist/fan-video-tr_linux_amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "-s -w -X ${PKG}/internal/version.Version=${VERSION}" -o dist/fan-video-tr_linux_arm64 .
gh release create "v${VERSION}" \
dist/fan-video-tr_linux_amd64 \
dist/fan-video-tr_linux_arm64 \
dist/sha256sums.txt \
--title "v${VERSION}" \
--notes "fan-video-tr - Local Video Transcode Tool"
八、故障排查表
| 现象 | 排查方案 |
|---|---|
| 发布脚本报「TAG 必须形如 vX.Y.Z」 | 补 v 前缀:./scripts/build-and-push.sh v1.0.1 |
| 发布脚本报「工作区存在未提交改动」 | git add/git commit 或 git stash 后重试 |
| 发布脚本报「本地 main 与远端不一致」 | git pull --rebase 后重试 |
gh workflow run 失败 |
gh auth login 重新授权,确保 token 含 workflow 权限;tag 已推送远端 |
| 流水线报「version.go 与 tag 不一致」 | 走 build-and-push.sh 升版发布,不要手动只打 tag |
| 下载二进制返回 404 | 检查 Release 是否 Published,Assets 文件名严格匹配 fan-video-tr_linux_{arch} |
| 下载内容被识别为非 ELF | install.sh 校验魔数 7f454c46,说明下到的不是二进制(多为代理返回的 HTML) |
| raw.githubusercontent.com 卡住超时 | 国内网络限制,配置代理;install.sh 已内置 GitHub 加速镜像回退 |
| 服务启动失败 | journalctl -u fan-video-tr -n 100;检查 ffmpeg 是否安装、端口占用、目录权限 |
| 界面硬件加速全部不可用 | 容器需映射 /dev/dri(VAAPI 还要 video/render 组),或设置 FVT_FFMPEG_ACCEL=none |
| AV1 + 双遍编码明确报错 | 本机 libsvtav1 仅单遍;用 libaom-av1 或改用单遍/码率模式 |
| 转码任务一直排队不执行 | FVT_APP_WORKER 并发数为 1,确认无其他转码占用;查看日志是否 FFmpeg 报错 |
编译机本机执行 fan-video-tr: command not found |
编译机没走 install.sh;测试运行用完整路径 ./bin/fan-video-tr |
| 流水线 sync-cnb 报错 | CNB 仓库未创建或 token 权限不足,属尽力而为同步,不影响 Release/Docker |
九、前端 UI 修改流程
前端为原生 HTML/CSS/JS(internal/embedded/web/),无 Node 构建,修改后直接重新编译内嵌即可:
- 修改
internal/embedded/web/index.html/css/style.css/js/app.js/favicon.svg - 重新构建:
make build(改前端后必须重新编译,否则运行的仍是旧页面) - 重新走发布流程:
./scripts/build-and-push.sh v1.0.3 --yes触发流水线 - 目标服务器重跑一键安装脚本升级生效(或本地
make install)
若需要覆盖内嵌资源,也可在配置里指定外部前端目录:
app:
web_dir: "/opt/fan-video-tr/web" # 留空使用二进制内嵌资源
favicon: "" # 留空依次回退 <data_dir>/favicon.svg -> 内置图标
十、极简速查复制块
发布新版本速记
cd /vol1/1000/GitHub/fan-video-tr
git pull --rebase
./scripts/build-and-push.sh v1.0.2 --yes # 自动:升版 version.go → 写 RELEASE_NOTES → commit → push main → tag → 触发 release.yml
# 或分步(离线模式):
# make test && make build
# git add . && git commit -m "chore: bump version to v1.0.2"
# git push origin main && git tag -a v1.0.2 -m v1.0.2 && git push origin v1.0.2
# gh workflow run release.yml -f tag=v1.0.2 --ref v1.0.2
export VERSION=1.0.2
PKG=github.com/meimolihan/fan-video-tr
rm -rf dist && mkdir -p dist
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w -X ${PKG}/internal/version.Version=${VERSION}" -o dist/fan-video-tr_linux_amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "-s -w -X ${PKG}/internal/version.Version=${VERSION}" -o dist/fan-video-tr_linux_arm64 .
gh release create "v${VERSION}" dist/fan-video-tr_linux_amd64 dist/fan-video-tr_linux_arm64 --title "v${VERSION}" --notes "更新说明"
# 目标服务器升级
bash -c "$(curl -sSL https://raw.githubusercontent.com/meimolihan/fan-video-tr/main/scripts/install.sh)" -y
fan-video-tr status && fan-video-tr --version