ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

自托管服务实战(9):自建媒体与下载服务

自托管服务实战(9):自建媒体与下载服务

上一篇建立了更新与告警门禁。本篇加入 Jellyfin 与下载任务这类大容量、高 I/O、可能持续占用 CPU 或核显的负载,并通过目录、权限和资源预算把它们与密码库、代理等核心服务隔离。

一、先设计媒体流转路径

目录至少分为下载临时区、完成区、媒体库和应用配置。下载器只写临时区与完成区,整理任务在校验完成后把文件移动到媒体库,Jellyfin 对媒体库只读。若下载区和媒体库位于同一文件系统,原子移动几乎立即完成;跨文件系统会变成复制加删除,期间占用双倍空间并产生半成品风险。

能直接播放就不转码。直播放由客户端解码,服务器只传输文件;转封装只改变容器,成本较低;视频转码会持续消耗 CPU 或硬件编码单元。选型要用真实客户端、字幕和码率测试,不能只看影片分辨率。硬件转码时仅映射需要的渲染设备,并确认容器用户属于正确组,不要为了省事使用特权模式。

媒体元数据、观看进度与用户配置是小而重要的状态,应定期备份;可从合法来源重新取得的大媒体文件可采用不同保留等级。下载内容必须遵守来源许可与当地法律。下载器的 Web 界面不应直接公开到公网,默认密码必须更换,完成脚本也不能拥有整个宿主文件系统权限。

下面的程序根据客户端能力判断直播放、转封装或转码,并汇总并发转码预算。规则简化但完整可运行,适合改成上线前的设备兼容性清单。

fromdataclassesimportdataclass@dataclass(frozen=True)classSession:name:strvideo:straudio:strcontainer:strclient_video:set[str]client_audio:set[str]client_container:set[str]sessions=[Session("电视","h264","aac","mp4",{"h264","hevc"},{"aac"},{"mp4","mkv"}),Session("浏览器","hevc","aac","mkv",{"h264"},{"aac"},{"mp4"}),Session("平板","h264","aac","mkv",{"h264"},{"aac"},{"mp4"}),]transcodes=0foriteminsessions:ifitem.videonotinitem.client_videooritem.audionotinitem.client_audio:mode="transcode"transcodes+=1elifitem.containernotinitem.client_container:mode="remux"else:mode="direct-play"print(f"{item.name}: mode={mode}")capacity=2print(f"transcodes={transcodes}")print(f"capacity_gate={'PASS'iftranscodes<=capacityelse'FAIL'}")

运行输出:

电视: mode=direct-play 浏览器: mode=transcode 平板: mode=remux transcodes=1 capacity_gate=PASS

二、用水位和限额保护核心服务

下载任务会制造持续写入、校验读取和大量连接。为容器设置 CPU、内存和 I/O 优先级,限制并发下载与上传速度,并给系统盘、下载盘、媒体盘分别告警。保留空间不能只看百分比:10TB 盘剩 5% 仍有 500GB,小系统盘剩 5% 可能已经无法写日志;应同时使用绝对容量和预测耗尽时间。

媒体扫描安排在低峰,并避免每次启动全库重扫。缩略图和转码缓存是可重建数据,可设置容量上限;配置数据库与观看进度进入备份。下载未完成文件使用专用后缀或目录,整理器只处理完成且校验通过的项目,避免 Jellyfin 索引到半个文件。

以下程序模拟空间水位与整理决策。只有状态完成、摘要正确且目标盘保留安全余量时才移动;所有输入明确,便于把规则迁移到实际自动化脚本。

fromdataclassesimportdataclass@dataclass(frozen=True)classDownload:name:strsize_gb:intcomplete:boolchecksum_ok:booldownloads=[Download("family-video.mp4",18,True,True),Download("archive.iso",42,False,False),Download("licensed-film.mkv",36,True,True),]free_gb=120reserve_gb=50moved=[]foritemindownloads:enough_space=free_gb-item.size_gb>=reserve_gb eligible=item.completeanditem.checksum_okandenough_spaceprint(f"{item.name}: eligible={eligible}free_before={free_gb}GB")ifeligible:free_gb-=item.size_gb moved.append(item.name)print(f"moved={len(moved)}")print(f"free_after={free_gb}GB")print(f"storage_gate={'PASS'iffree_gb>=reserve_gbelse'FAIL'}")

运行输出:

family-video.mp4: eligible=True free_before=120GB archive.iso: eligible=False free_before=102GB licensed-film.mkv: eligible=True free_before=102GB moved=2 free_after=66GB storage_gate=PASS

三、上线和迁移验收

先导入少量样本,覆盖 H.264、HEVC、不同音轨、内嵌与外挂字幕。分别在电视、浏览器和移动网络测试,记录是否直放、首帧时间、服务端负载和带宽。再进行两路并发测试,确认密码库和代理延迟没有明显恶化。若资源争用,先限制转码并发和扫描时间,不要用无限重启掩盖 OOM。

迁移媒体库时保持原目录结构和稳定挂载路径,可以减少重新匹配。先备份应用配置,在新实例只读挂载媒体并完成扫描,对比库数量、用户和观看进度,最后切换客户端。旧实例停止写入后保留短期回退,避免两个服务同时修改元数据。

可迁移的核心是“临时—完成—只读媒体”的单向数据流、真实客户端兼容矩阵和资源水位门禁。下一篇把前九篇资产整合成家庭私有云的服务目录、依赖图与灾难恢复顺序。

参考来源

  • Jellyfin:容器安装
  • Jellyfin:硬件加速
  • Docker:资源约束

👍 觉得有用就点个赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。

🚀 本文属于《自托管服务实战》系列,持续更新,关注不迷路。

📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。

返回列表