拓冰建站拓冰建站
首页 / 资讯中心 / 正文

家庭影音中心LunaTV搭建实践:N100迷你主机、Docker Compose与硬链接全指南

1. 为什么我要自己搭一套「LunaTV」家庭影音中心说来也巧这个项目的起因并不是什么技术追求而是某天晚上我窝在沙发里想看电影结果在手机、平板、电视三个设备之间来回倒腾资源折腾了快二十分钟还没看成。当时我就意识到家里的影音资源管理已经彻底失控了——下载的资源散落在各个硬盘里有的存在老笔记本上有的在移动硬盘深处吃灰想看的时候根本不知道哪个设备上有哪个片子。这种资源越多反而越不方便的荒诞感让我决定认真搭一套统一的家庭影音中心。至于为什么叫LunaTV纯粹是因为家里那位喜欢月亮而且这套系统主要服务于晚上的观影场景月亮主题再合适不过。LunaTV本质上是一套以媒体管理和流媒体转码为核心的自托管影音服务跑在一台常年开机的迷你主机上所有的电影、剧集、纪录片都汇总到一个统一界面电视、手机、平板、电脑随时随地都能打开看。它能解决的事情很直接资源统一管理、自动刮削海报与简介、多端播放进度同步、以及打开就能看的流畅体验。这篇文章我不会只讲装个软件就好了这种废话而是把整套系统的选型逻辑、目录规划、容器编排、踩坑记录和调优经验都摊开讲。不管你是纯粹想解决家里电影观看问题的小白还是想认真搭建一套类似系统的技术爱好者这篇都值得从头看到尾。所有步骤我用的都是主流开源或免费方案成本上除了一台主机和一块硬盘之外没有额外支出。2. 硬件与底层系统的选型思路够用、静音、低功耗2.1 为什么不用旧电脑、NAS成品而是选迷你主机搭建LunaTV之前我认真对比过三套硬件方案旧笔记本、成品NAS、迷你主机。旧笔记本的问题在于功耗太高待机三四十瓦起步一年下来的电费都够买个小主机了而且风扇噪音在客厅里实在难以接受。成品NAS是好东西但市面上主流型号的CPU性能偏弱做转码时会力不从心价格还偏高。综合下来我选了一台N100处理器的迷你主机准系统不到一千块钱配上16GB内存和一块闲置的2.5寸SATA固态。这套配置有几个实实在在的好处N100的TDP只有6W整机待机功耗在10W左右一年电费不到五十块可以一年四季不关机性能做1080P和4K转码足够机身无风扇或低转速风扇设计放电视柜里几乎听不到声音。内存方面16GB对于跑Jellyfin、下载工具和几个辅助容器来说绰绰有余但如果你打算同时跑虚拟机和更多服务32GB会更宽裕。2.2 底层系统选型Debian是省心之选底层系统我在Ubuntu Server和Debian之间犹豫了一下最后选了Debian 12。原因很简单系统资源占用更低稳定性更好毕竟这类跑在家里的服务最怕的就是装完用两个月突然起不来了。如果你对Debian的命令行不熟悉Ubuntu Server其实也可以命令基本通用教程也多。系统装完之后我做了几个基础优化开启SSH方便远程管理设置了固定IP地址以及最重要的——把swap Swappiness调到10避免内存在日常运行中频繁换页。N100搭配固态盘跑这几个容器内存占用常驻在4GB左右16GB内存完全够用调整swappiness只是顺手优化。提示迷你主机装系统时记得在BIOS里开启断电恢复后自动启动AC Power Recovery选项这样家里意外断电再来电时主机会自己启动不用每次手动去按电源键。这个细节对无人值守的家用服务器非常重要。2.3 存储方案一块大容量机械盘做仓库系统盘和数据盘分开是必须遵守的底线。系统装在128GB的SATA固态上数据盘单独用一块4TB的机械硬盘挂载到/data目录。机械硬盘转速不高运行时噪音也小适合存影音这类顺序读取为主的大文件。硬盘挂载我使用了ext4文件系统挂载参数加了noatime,nofail。noatime可以减少不必要的磁盘写入延长寿命nofail保证开机时硬盘异常不影响系统整体启动。这里特别说一下如果你硬盘容量超过2TB分区表一定要用GPT而不是MBRMBR最大只支持2TB我见过不少人在这上面踩坑。3. 一个长期不乱的媒体库目录结构存储规划是一切的基石3.1 目录结构设计下载区与媒体库分开很多人搭影音服务容易忽略目录规划软件装好了就往一个目录里塞结果半年以后目录乱成一锅粥。我的规划原则是**下载区和媒体库彻底分离**下载区负责临时存储媒体库负责整理分类。/data/ ├── downloads/ # 下载临时区 │ ├── movies/ # 电影下载目录 │ ├── tv/ # 剧集下载目录 │ └── incomplete/ # 未下载完成 ├── media/ # 媒体库整理后的成品 │ ├── movies/ # 电影库 │ ├── tvshows/ # 剧集库 │ ├── anime/ # 动漫库可选 │ └── music/ # 音乐库可选 └── appdata/ # 应用配置目录 ├── jellyfin/ # Jellyfin数据 ├── qbittorrent/ # 下载器配置 └── prowlarr/ # 索引器配置这套结构的核心逻辑是下载工具写downloads媒体服务器读media。整理动作由程序自动完成不需要人为干预。appdata单独一个目录用来放各个应用的状态文件这样备份只需要打包这一个目录即可。3.2 硬链接与原子整理pt做种与媒体库共存的关键接着是最关键的环节——硬链接。玩过PT的人都知道下载完的资源需要继续做种但媒体服务器又需要把资源整理成规范的文件名和目录结构。如果直接移动文件做种就断了如果复制一份又白白浪费一倍硬盘空间。硬链接的妙处在于同一份数据可以有多个路径入口修改任意一个路径下的文件名或目录位置都不会影响另一个路径下的内容因为文件数据本身只有一份。Linux的ext4文件系统原生支持硬链接而我用的环境恰好满足硬链接的硬性条件——下载目录和媒体库必须在同一个文件系统内。上面的目录结构里/data/downloads和/data/media都在同一块硬盘的同一分区下天然满足条件。如果你的下载盘和媒体库盘是两块独立硬盘那硬链接就彻底用不了了。我使用硬链接的逻辑是下载工具下载完成后通过自动化脚本将文件转移到媒体库专门按Jellyfin的命名规范对文件进行重命名和整理。由于是硬链接下载目录里的原文件不受影响文件名和存储位置完全不变做种继续媒体库里则出现了一份整理后的入口Jellyfin读取起来完全正常。这样既保住了做种连接和分享率又不影响媒体库整洁磁盘空间也只占用一份。整个过程中只要不跨文件系统就不存在性能损耗文件数据连复制动作都没有发生。4. 核心服务编排Docker Compose一条命令拉起全部服务4.1 为什么用Docker Compose而不是直接在系统里装软件LunaTV的核心组件一共有四个Jellyfin媒体服务器、qBittorrent下载工具、Prowlarr索引器、Automatic系列自动订阅管理。如果直接在Debian里装这四套软件依赖冲突、配置隔离、升级管理都是麻烦事。用Docker Compose编排的好处是所有服务互相隔离升级互不影响配置统一管理重装系统后一条命令就能恢复全部服务。而且Compose文件本身就是一份基础设施即代码的配置文档每个服务的镜像版本、端口映射、目录挂载、环境变量都清清楚楚比人肉记在笔记里可靠多了。4.2 完整的Compose配置文件与逐段讲解下面是我当前在用的docker-compose.yml直接抄作业就能用但每个关键参数我都会讲清楚为什么这么设。version: 3.8 services: jellyfin: image: jellyfin/jellyfin:latest container_name: jellyfin restart: unless-stopped ports: - 8096:8096 volumes: - /data/appdata/jellyfin:/config - /data/media:/data/media - /data/downloads:/data/downloads devices: - /dev/dri:/dev/dri # Intel核显硬件转码 environment: - TZAsia/Shanghai - JELLYFIN_PublishedServerUrl192.168.1.100:8096 group_add: - 44 # video 组用于访问GPU stop_grace_period: 60s # 停止时给足时间保存数据库 qbittorrent: image: lscr.io/linuxserver/qbittorrent:latest container_name: qbittorrent restart: unless-stopped ports: - 8081:8081 - 6881:6881 - 6881:6881/udp volumes: - /data/appdata/qbittorrent:/config - /data/downloads:/data/downloads environment: - PUID1000 - PGID1000 - TZAsia/Shanghai - WEBUI_PORT8081 stop_grace_period: 60s prowlarr: image: lscr.io/linuxserver/prowlarr:latest container_name: prowlarr restart: unless-stopped ports: - 9696:9696 volumes: - /data/appdata/prowlarr:/config environment: - PUID1000 - PGID1000 - TZAsia/Shanghai radarr: # 电影自动化 image: lscr.io/linuxserver/radarr:latest container_name: radarr restart: unless-stopped ports: - 7878:7878 volumes: - /data/appdata/radarr:/config - /data/downloads:/data/downloads - /data/media:/data/media environment: - PUID1000 - PGID1000 - TZAsia/Shanghai sonarr: # 剧集自动化 image: lscr.io/linuxserver/sonarr:latest container_name: sonarr restart: unless-stopped ports: - 8989:8989 volumes: - /data/appdata/sonarr:/config - /data/downloads:/data/downloads - /data/media:/data/media environment: - PUID1000 - PGID1000 - TZAsia/Shanghai jellyseerr: # 全家统一申请入口 image: fallenbagel/jellyseerr:latest container_name: jellyseerr restart: unless-stopped ports: - 5055:5055 volumes: - /data/appdata/jellyseerr:/app/config environment: - TZAsia/Shanghai - LOG_LEVELinfo depends_on: - jellyfin - radarr - sonarr有几个参数我要专门拎出来说restart: unless-stopped除了手动停止容器在崩溃或主机重启后会自动拉起。家用服务最重要的就是不折腾这个参数保证开机后所有服务自动恢复运行。PUID1000和PGID1000linuxserver镜像默认以root运行会有安全风险所以它们设计成可以在指定用户权限下运行。这里1000是Debian安装时创建的第一个普通用户的UID确保容器内的文件操作权限和宿主机用户一致。这个值必须和你在/data目录上的实际用户UID一致否则容器里创建的文件在宿主机上会没有操作权限。可以运行id命令查看当前用户的UID和GID。stop_grace_period: 60sJellyfin和qBittorrent都有频繁的数据库写入停止时如果被强制杀掉可能造成数据库损坏。加长这个时间让容器在收到停止信号后有足够时间完成数据落盘。4.3 启动命令与服务间的关系梳理配置写好之后直接执行docker compose up -d等镜像拉取完毕所有容器就会自动启动。这里要提醒一下watchtower这类自动更新容器镜像的工具我建议先用着但保持保守态度——它确实方便但大版本更新偶尔会引入不兼容配置家里的服务一旦跑稳定了稳定优先于新鲜。整套系统启动之后的关系很简单Prowlarr负责从各个资源站抓取可用的索引Sonarr剧集和Radarr电影根据用户的观影需求或订阅规则在Prowlarr返回的结果里挑选合适的资源提交给qBittorrent执行下载下载完成后自动触发硬链接整理进媒体库Jellyfin识别到新文件后刮削元数据最终在电视端和手机端呈现出一个带着海报墙的私人影院。Jellyseerr则是给全家人用的前端申请界面不用让每个人接触这些后台工具家人只需要告诉它想看哪部片子后面的自动化链路会全部跑完。5. 元数据刮削与多端播放体验的差距全在这个环节5.1 刮削器的选择内置刮削仍是最省事的方案Jellyfin的元数据刮削能力在几代版本迭代之后已经非常成熟对中文资源的识别率也在稳步提升。很多教程会推荐额外安装tinyMediaManager之类的外部刮削工具但对于大部分普通家庭场景Jellyfin内置刮削器配合正确的命名规范已经完全够用。关键还是命名规范。很多人在这一步出问题不是软件不行而是文件名太乱导致软件认不出来。我的实践证明只要遵循以下规则识别率基本是百分之百电影 /data/media/movies/肖申克的救赎 (1994)/肖申克的救赎 (1994).mkv 剧集 /data/media/tvshows/绝命毒师 (2008)/Season 01/绝命毒师 S01E01.mkv电影用电影名 (年份)做文件夹里面放同名文件剧集用剧集名 (首播年份)做总目录第二层按Season 01分季目录文件命名包含SxxExx格式。只要文件名里带上英文括号和年份、集数信息Jellyfin的内置刮削器基本都能正确识别。提示文件名里的括号一定要用英文半角括号中文全角括号会导致刮削识别失败。这属于那种排查起来很隐蔽、知道以后又觉得很简单的问题。5.2 命名规范和自动分类的组合拳如果只是靠手动整理命名再好的规范都坚持不了多久。这时候Sonarr和Radarr的价值就完全体现出来了——它们不仅仅能自动下载还能在下载完成后自动重命名文件、移动到媒体库对应目录。以Sonarr为例在设置里配置好媒体库根目录为/data/media/tvshows下载目录关联到qBittorrent的/data/downloads/tv再设置好命名规则{Series Title} ({Year})/Season {season:00}/{Series Title} S{season:00}E{episode:00}下载完成后Sonarr会自动调用硬链接、按规则重命名、移动到对应季目录。文件名和目录结构永远保持规范Jellyfin识别成功率自然就高。作为不折腾主义者这种一次性配置长期自动运行的模式正是家用系统该有的姿态。5.3 硬件转码配置Intel核显的价值兑现时刻Jellyfin的硬件转码是体验的质变点。家里的智能电视原生播放能力参差不齐有的是老电视只支持H.264编码有的是新电视虽然解码能力强但遇到高规格的HDR10内容时仍有兼容性问题。转码的价值就是把Jellyfin服务器变成了一个万能解码器——无论终端设备是什么Jellyfin都会按需把视频转成终端能直接播放的格式。硬件转码配置在Jellyfin控制台的播放设置里勾选启用硬件解码Intel核显的设备还会出现QSV选项。注意前面的Compose配置里已经做了两件事把宿主机的/dev/dri设备映射进了容器将容器用户加入了video组。这两步缺一不可否则Jellyfin容器内根本访问不到核显。N100的核显同时支持QSV和OpenCL实测下来4K HEVC/HDR内容转1080P可以做到几乎实时占用率不到30%。这里有一个很多人忽略的小经验转码后的视频码率可以通过Jellyfin的播放设置里互联网流媒体比特率限制做上限控制配合家庭上行带宽设定个合理的值比如40Mbps既保证画质又让远程播放不卡顿。5.4 客户端选择电视、手机、平板的实际体验电视端如果电视是Android TV或基于它的国产智能系统直接装Jellyfin官方客户端就行。电视端支持海报墙展示、章节跳转、直通DTS和杜比全景声体验非常接近商业流媒体。如果电视系统装不了Jellyfin客户端还有Kodi搭配Jellyfin插件的方案不过配置复杂度会高一些需要调整Kodi的缓存设置才能流畅播放大码率文件。手机/平板端Jellyfin官方客户端在iOS和Android上都有支持外网登录、离线下载、投屏推送家里老人用起来也没问题。Web端不想装客户端的话浏览器直接访问http://主机IP:8096就能开看UI完全自适应备用方案里算是最省事的。播放环节还有一个提示如果同一部片子既看了4K版本又看了1080P版本Jellyfin的版本管理功能会自动把它们归到一个条目下播放时手动选择版本即可。配合播放进度同步功能在电视上看到一半关掉躺床上拿起手机接着放进度自动从刚才的位置继续。6. 运行半年后最想分享的五个排错记录与调优经验6.1 坑一硬链接偶发失败排查链路走了一遍系统跑了一个月之后某天突然发现新下载的剧集在媒体库文件夹里不见了但下载目录里文件还在qbittorrent显示做种正常。查了Sonarr的日志里面有failed to hard link报错。排查过程从检查文件系统确认ext4没问题到检查目标目录权限media目录权限为drwxr-xr-x、属主正确一步步缩窄到文件系统层面到底怎么了。最后用df -h命令一眼看出问题根分区空间只剩几百MBLinux文件系统发送硬链接请求时为新目录项分配空间失败。清理了Docker的日志文件和镜像缓存给根分区腾出几个GB空间后硬链接一切正常。教训是只要涉及文件写入的操作空间充足永远是最底层的保障没有例外。6.2 坑二Jellyfin数据库损坏表现是界面打不开但进程还在有一次家里意外断电来电后Jellyfin容器虽然自动起来了但网页一直转圈打不开看容器日志也没有明显报错。最后直接docker stop jellyfin再docker start jellyfinJellyfin恢复运行。这个事情说明一个问题SQLite数据库在意外断电时可能出现损坏而Jellyfin的启动过程加载不到数据库时容器进程不会崩溃只会卡在等待状态。所以看到进程在、但界面打不开第一反应应该是重启服务而不是重装。为了防患于未然我在配置目录里加了一个每天凌晨执行的SQLite备份任务用sqlite3 .backup命令把jellyfin.db备份到另一个目录保留最近7份。这样即使数据库损坏也能恢复最多一天的损失。6.3 坑三qBittorrent下载速度上不去居然是端口范围太小用默认设置下载一些冷门资源时速度极慢后来发现qBittorrent的监听端口只有一个6881而运营商的NAT设备对单端口连接数有限制。把监听端口改成连续范围比如6881-6889同时在路由器的端口转发和防火墙里放行这个范围下载速度有了非常明显的提升。如果你家里是光猫拨号路由器二级NAT需要在两级设备上都做端口转发或设置DMZ主机这个坑更隐蔽。6.4 坑四局域网播放一卡一卡的罪魁祸首是无线网络最开始电视用的是Wi-Fi连接播放原盘ISO资源时经常缓冲。查了Jellyfin的日志发现服务器转码正常、网络传输在100Mbps左右也不高最后换成有线网线直连路由器问题直接消失。这里要澄清一个常见误区5G Wi-Fi的信号标称速率再高实际传输大码率视频时的拥塞和抖动大得多。看原盘或者高码率4K资源固定设备一定要走有线。6.5 调优一Tautulli监控 观看统计数据驱动内容管理Tautulli是Jellyfin的配套监控工具可以记录每个用户每天看了什么、看了多久、在哪个设备上看的。我一开始以为这只是个统计面板实际用下来它的实时通知功能价值更大——当家人开始播放某部剧时能在手机上收到通知并附带剧集海报遇到字幕音轨异常时可以立刻知道是哪部片子、哪个文件出了问题。6.6 调优二定时维护脚本一个命令搞定空间管理和健康检查Docker容器跑久了日志文件会无限膨胀。我写了下面这个维护脚本每周日半夜自动执行#!/bin/bash # 清理日志和构建缓存 docker system prune -f --volumes # 删除超过30天的Jellyfin数据库备份 find /data/appdata/jellyfin/backups -name *.db -mtime 30 -delete # 检查硬盘空间和系统温度 df -h /data /var/log/lunatv-health.log # 容器健康状态检查 docker ps --format table {{.Names}}\t{{.Status}} /var/log/lunatv-health.logdocker system prune -f --volumes会清理所有停止运行的容器、无用的网络资源和悬空的镜像层但不会删除正在使用的数据卷所以可以放心用。脚本跑完看一眼lunatv-health.log整个系统的运行状态一目了然。7. 还能往哪走从能看到好看的三个进阶方向7.1 接入家庭语音助手喊一声就能开播LunaTV跑稳定之后我开始琢磨减少打开App-搜索-选择-播放的路径。语音是一条值得探索的方向通过对小爱/天猫精灵这类设备扬声器的桥接把语音发出的指令转成对Jellyfin和Jellyseerr的API调用。目前这类桥接方案在开源社区已有不少成熟项目虽然不同品牌的响应速度和中文识别效果参差不齐但帮我找一部诺兰的电影这类简单指令已经能跑通了。这块优化的核心价值在于让不怎么习惯智能设备的家人也能用最自然的方式享受系统。7.2 Redis缓存加速目录遍历影视库上千部时的质变我的媒体库大概存了三百多部电影和几十部剧集Jellyfin浏览首页时几乎无感。但把动漫、纪录片也加进来之后几千个条目的图库加载开始出现明显的卡顿尤其是首次进入某分类页面时。Jellyfin官方文档里提供了Redis作为目录浏览缓存的方案配置方式不算复杂在Compose里多加一个Redis服务并在Jellyfin的环境变量里指定CACHE__REDIS连接地址。我用这个方案之后海报墙的加载速度提升非常明显首屏几乎做到了秒开如果你也是重度收藏党这个优化值得做。7.3 家庭全员接入划分用户与权限比你想的更重要最后一个建议不是技术问题而是使用习惯。很多家庭影音系统搭建完成后可能一家人都用同一个账号。Jellyfin默认支多个用户每个用户独立开一个账号并不麻烦却能带来两个实质性好处一是观看记录和进度互不干扰孩子看过什么、看到第几集不会跟大人的混在一起二是Jellyfin支持家长控制功能可以按分级限制某个账号能访问的内容给孩子用的账号设置白名单后家长就不用担心了。我在实际使用中的体会是家庭自建影音系统最有成就感的那一刻不是配置全通、海报墙刷出来的瞬间而是系统跑了两三个月家里人已经完全习惯打开电视就能直接看这件事的日常感。LunaTV这个东西本质上不是技术项目而是给生活减少摩擦的一种基础设施。它不像很多折腾项目那样越搞越复杂反倒在跑稳定之后越来越隐形——真到被家人都遗忘技术存在的那一天这套系统才算是真正成功了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门