跳到主要内容

从旧文件结构迁移到媒体池

媒体池让 lani 保留导入原件及历史版本,并安全替换媒体库中的视频和字幕。新版会自动登记已有媒体;升级前需要让下载目录、媒体池与媒体库在 lani 内可读写且能够硬链接,或者明确选择跨卷复制。

先核对现状​

记录 lani、Jellyfin、qBittorrent 的容器挂载,以及媒体库位置、下载默认目录、分类目录和已有种子的保存位置。保存配置、容器启动参数、数据库备份及 Jellyfin/qBittorrent 的小体积配置备份。它们用于恢复关联和设置,与复制整库媒体不同;调整挂载或同文件系统移动通常不需要额外复制数 TB 文件。

以下是常见旧布局及推荐调整。外部应用的容器路径尽量保持原样,只有 lani 改为挂载共同父目录:

用途宿主机目录外部应用容器内路径(保持)lani 旧路径lani 新路径
下载/srv/media/downloadsqBittorrent /downloads/downloads 单独挂载/data/downloads
媒体库/srv/media/libraryJellyfin /media/library 单独挂载/data/library
媒体池/srv/media/pool无需对外部应用挂载无/data/pool

lani 挂载 /srv/media:/data;Jellyfin 仍挂载 /srv/media/library:/media,qBittorrent 仍挂载 /srv/media/downloads:/downloads。在同一块磁盘上分别挂载 /downloads 和 /library 也可能无法跨挂载点硬链接,不能只凭磁盘型号或路径前缀判断。新版启动会用自建临时文件探测媒体池与媒体库间的硬链接,不修改用户媒体;下载目录在实际下载导入时检查。

对应配置示例(保留其余已有配置):

mediaPool:
root: /data/pool
painfullyAcceptCrossDeviceCopy: false
jellyfin:
pathMapping:
- from: /media
to: /data/library
downloadClient:
qbittorrent:
pathMapping:
- from: /downloads
to: /data/downloads

删除旧 lani.moveStrategy。当前配置校验会拒绝 lani 下未声明的旧键;删除该键,保留 publicHost。旧 copyOnly、moveOnly 等值不再适用。路径映射的 from 是外部应用实际报告的位置,to 是 lani 内能访问同一文件的位置,映射不能代替挂载。

情况一:只需调整挂载​

  1. 暂停受影响种子的下载及文件操作,停止旧 lani;更改挂载时停止对应容器。不要运行两个 lani 实例写同一媒体库。
  2. 保留下载器及 Jellyfin 的容器内路径,按上表将共同父目录挂入 lani,配置媒体池目录及映射。目录应对实际运行用户可读写,避免使用符号链接;无需递归改变所有媒体所有权。
  3. 重启外部应用,确认 Jellyfin 仍能读取原媒体,qBittorrent 报告的保存位置与原来一致。没有改变文件内容或外部路径时不需要重新下载、重新添加媒体库。
  4. 正常启动新版 lani。预检失败会报告具体路径、权限或 EXDEV 原因;停止 lani,修正挂载/权限后再启动。可以恢复旧挂载和配置,但数据库已升级时,不要直接启动不兼容的旧版本,应先恢复相应数据库备份。

情况二:同文件系统调整目录​

例如旧目录是 /srv/downloads、/srv/library,希望改为 /srv/media/downloads、/srv/media/library。先用宿主机挂载信息(例如 findmnt -T 路径)核对文件系统,不要把“同一块磁盘”视为“同一文件系统”。

  1. 备份小体积配置和数据库,记录源目录大小、文件数量和若干媒体样本。暂停下载、媒体库扫描及其他写入,停止相关容器。
  2. 确认目标目录不存在且空间/权限正确。在同一文件系统内使用目录移动,例如 mv /srv/downloads /srv/media/downloads。同文件系统 rename 通常只改目录项,无需复制全部内容;若工具提示跨设备或开始复制,先停止并按下一节重新规划。
  3. 将外部容器的挂载源改到新宿主机目录,保持 /downloads、/media 等容器路径。更新 lani 的共同挂载和映射。
  4. 检查文件数量、大小与样本播放,核对下载器仍识别已有文件后恢复服务。失败时停止写入,按记录将目录移回并恢复挂载;不要删除任一尚未确认归属的目录,也不要让两个目录同时被自动扫描。

情况三:真正跨磁盘或文件系统​

可以保留现有布局并显式开启:

mediaPool:
root: /pool
painfullyAcceptCrossDeviceCopy: true

只有硬链接返回跨卷错误时才复制。现有媒体不移动,但入池和发布可能各增加一份副本,历史版本持续占用空间,首次登记可能耗时较长;权限错误不会因开启复制而被忽略。应按媒体量和保留版本估算空间,完成前不要删除下载源或旧媒体。

如希望迁到可硬链接的新布局:

  1. 暂停相关读写,保存配置和数据库备份,确认目标空间足够。跨文件系统必须实际读取和写入全部内容,耗时取决于媒体量和磁盘速度。
  2. 将下载目录和媒体库分别复制到独立、尚未被应用扫描的目标目录,例如 rsync -aH --info=progress2 /old/downloads/ /new/media/downloads/ 和 rsync -aH --info=progress2 /old/library/ /new/media/library/。按实际位置替换两个源目录,若源端分处不同文件系统,需要为两份内容都预留空间。路径末尾斜杠影响复制布局,执行前核对;不要加入删除源文件或自动删除目标的选项。
  3. 保持两个源目录不再写入,分别核对文件数量、大小及内容;执行 rsync -aHnci /old/downloads/ /new/media/downloads/ 和 rsync -aHnci /old/library/ /new/media/library/。两条命令均成功退出且没有输出,才表示没有待同步差异;有输出时逐项核对并同步列出的内容或属性变化。校验本身也会读取完整数据。失败时修正并续传,源文件仍可用于回退。
  4. 两份目录均校验通过后,创建 /new/media/pool,将 lani 挂载改为 /new/media:/data,qBittorrent 改为 /new/media/downloads:/downloads,Jellyfin 改为 /new/media/library:/media,保持两个外部应用的容器内路径不变,并使用上面的 lani 路径映射。进行样本播放和下载器文件检查;失败时停止写入并恢复原挂载与配置。确认新布局稳定后再安排源文件的清理;不要为了腾空间提前删除唯一完整副本。

必须改变外部应用路径时​

优先改宿主机挂载,保留 Jellyfin/qBittorrent 的容器内路径。确实需要改变时,先暂停受影响下载和扫描,记录原路径与新路径对应关系。使用 qBittorrent 的“设置位置”调整已有种子的保存目录,核对该操作会移动文件还是采用已复制内容,再执行校验;不要删除种子及数据后重新添加。默认及分类保存目录也需要更新。

Jellyfin 媒体库位置和 lani 路径映射应同步调整。路径改变可能导致 Jellyfin 重新识别并生成新条目 ID,不能承诺原有识别关系一定保留。先对少量样本验证并保存可回退的配置/数据库;若原有条目失联,停止后续迁移并恢复旧路径及对应备份,确认关联恢复后再处理。不要在路径暂时不可用时执行清理媒体库或删除失联条目。

启动与验收​

一体机启动流程会先执行数据库迁移。独立部署应由部署流程先完成数据库迁移,再启动新版 lani 服务;仅更新应用镜像不会自动补齐数据库结构。随后 lani 运行有 30 秒边界的预检。预检与中断操作恢复成功后即开放服务,lani 在内部后台逐集登记已关联的视频和外挂字幕;不需要手工运行回填 CLI。原文件、播放关联和历史任务保留,相同内容出现在不同剧集或路径时各自登记为独立文件。

登记期间无关剧集可正常下载和导入,查询及原有播放继续可用。尚未登记的历史剧集暂缓替换,其所在季度暂缓改名和删除;对应登记完成后可重试,无需等待全库完成。日志会显示 Media registration: 已完成/总数,也可通过 GraphQL 查询:

query {
mediaRegistrationProgress { state completed total reason }
}

ready 表示登记完成且写入已开放。若进程中断,重新启动会根据持久化的文件/版本记录继续,不重复生成已准备的文件。无法继续的故障会记录原因、关闭服务并非零退出;修复具体目录、文件或外部服务问题后重启,不能通过删除恢复记录来强行跳过。

确认日志完成、原剧集仍可播放、字幕正确、下载器没有重新下载,再尝试一集导入和替换。发现异常时暂停相关写入并保留日志、数据库备份和原文件;回退应用版本时同步考虑数据库结构,不能仅切换镜像标签。