# Alist / OpenList 存储 设计与 MP 对照:`docs/design/storage-alist.md`。整理侧口径:`docs/runbook/organize.md`「Alist / OpenList 存储」。 ## 登记 设置 → 媒体库与目录 →「存储」→ 新增: | 字段 | 说明 | | --- | --- | | 地址 | Alist / OpenList 的访问地址(带端口、子路径),本工具进程要能访问到;容器部署时用容器网络里的地址 | | 根路径 | 本工具只用这个路径下的内容(如 `/115/影视`),缺省 `/` | | 认证方式 | 令牌(Alist 后台「设置 → 其他 → 令牌」,推荐)/用户名和密码(换临时令牌,约两天续一次,失效自动重登)/访客 | | 路径密码 | Alist「元信息」里给目录设了访问密码时填写 | | 高级 | 每秒请求数(缺省 5)、并发(4)、请求超时、传输无进度超时、等待 Alist 任务上限、重试次数、上传交给 Alist 后台任务(缺省关,同 MP)、目录监控深度(5)与按目录修改时间跳过(开) | 保存后点「测试连接」:显示版本、登录用户、根路径是否可访问、容量(驱动不给或令牌不是管理员时显示「未知」)。凭据加密保存在 `secret` 表(用途 `storage_credential`),接口与日志里都不出现明文;直链的 `sign` 参数进日志前脱敏。 之后: - **媒体库目录**:新增时「存储」选这个 Alist,路径填 Alist 内路径(目录要先在 Alist 里建好)。Alist 上不写标记文件,目录可访问即在线。 - **整理来源**:来源目录可以选文件管理里的「Alist 存储」根下的目录,触发方式只能手动或目录快照。 - **文件管理**:左栏「Alist 存储」组,每个启用的实例一个根。浏览、新建目录、改名、删除(进 Alist 上的 `.umt-trash`,回收站可还原)、移动 / 复制(Alist 内走 Alist 接口,本机 ↔ Alist 上传 / 下载)、上传、下载小文件、整理、刮削。「刷新」才让 Alist 刷新目录缓存(会穿透到网盘接口)。 运行状态 →「挂载根」里列出每个存储实例的连通性;令牌失效显示「令牌失效或没有权限」。 ## 行为要点 - 成功判定(用户决定,同 MP):网盘方向的上传、下载、复制、移动只看接口成功 + 目标存在,不比对大小和哈希;移动在此之后删源。本地 ↔ 本地仍走现有核验。 - 不覆盖:上传先查同名再传临时名 `<名>.uhd-partial` 后改名(Alist 的 put 会静默覆盖同名);复制 / 移动目标已存在报冲突;名字不同的复制 / 移动经 `.umt-stage-*` 暂存目录改名,失败清理。 - 等待:Alist 的复制(v4 一律)与跨驱动移动是后台任务,按任务状态轮询到完成(失败带任务的错误信息),老版本没有任务接口时等目标出现。 - 错误:「不存在」与连不上 / 鉴权 / 服务端错误分开;整理遇到后者按可重试 IO 退避,不当成源不存在。限流 / 风控(429、「too many」)自动退避。 - 下载:写 `<目标>.uhd-partial`,中断后 Range 续传,直链签名过期自动重新获取,成功后不覆盖地改名。 - ffprobe:远端文件不下载,对直链只读文件头;探测不到不阻塞整理。 ## 真机验收(临时 OpenList 容器) 用户不提供真实 Alist 时,按下面起一个 OpenList(本地存储驱动)验收。容器名带 `umt-alist-` 前缀,用完删掉。 ```sh W=$PWD/.local/openlist # 工作区的 .local(不进 git) mkdir -p $W/{data,store1,store2} && chmod -R 777 $W docker run -d --name umt-alist-it -p 127.0.0.1:55244:5244 \ -v $W/data:/opt/openlist/data -v $W/store1:/mnt/s1 -v $W/store2:/mnt/s2 openlistteam/openlist:latest docker exec umt-alist-it ./openlist admin set umtAdmin123 # 设管理员密码 B=http://127.0.0.1:55244 TOK=$(curl -s -X POST $B/api/auth/login -H 'Content-Type: application/json' \ -d '{"username":"admin","password":"umtAdmin123"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["token"])') # 两个本地存储驱动(/s1、/s2),用来测同驱动与跨驱动 for n in 1 2; do curl -s -X POST $B/api/admin/storage/create -H "Authorization: $TOK" -H 'Content-Type: application/json' \ -d "{\"mount_path\":\"/s$n\",\"driver\":\"Local\",\"addition\":\"{\\\"root_folder_path\\\":\\\"/mnt/s$n\\\",\\\"mkdir_perm\\\":\\\"777\\\"}\"}"; done # 永久令牌(设置里登记用) PERM=$(curl -s "$B/api/admin/setting/get?key=token" -H "Authorization: $TOK" | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["value"])') ``` 部署环境里本工具跑在容器中时,把 OpenList 接到同一个 docker 网络(`docker network connect umt-alist-it`),设置里的地址填 `http://umt-alist-it:5244`。 ### 1. 适配器往返(脚本,可对任意地址跑) ```sh deno run -A scripts/alist-e2e.ts --url $B --token "$PERM" --root /s1 --root2 /s2 # 或 --user admin --password umtAdmin123;退出码 0 = 全部通过 ``` 步骤:连接 → 建目录 → 上传(秒传头、临时名改名、进度)→ 同名不覆盖 → 同驱动复制并改名 → 移动 → 跨驱动复制 / 移动(任务轮询)→ 下载比对 sha256 → 不存在 ≠ 失败、容量 → 清理。 ### 2. 自动化真机测试 ```sh export UMT_ALIST_REAL_URL=$B UMT_ALIST_REAL_TOKEN="$PERM" UMT_ALIST_REAL_ROOT=/s1 UMT_ALIST_REAL_ROOT2=/s2 (cd packages/adapters && deno run -A npm:vitest run src/alist/alist.real.test.ts) (cd apps/server && DATABASE_URL=... deno run -A npm:vitest run test/storage/alist-organize.real.test.ts) ``` 后者在测试库上跑整理往返:本地下载目录 → 整理(复制)到 OpenList 上的媒体库 → 文件管理「移动到」本地媒体库(下载后删远端)→ OpenList 内整理(同实例 API 移动)。 ### 3. 界面验收(部署环境) 1. 设置 → 媒体库与目录 → 存储:新增「OpenList」,地址、令牌 `$PERM`,测试连接显示版本与用户。 2. 在 OpenList 里建好 `/s1/media/movies`;媒体库目录新增「OpenList 电影」,存储选 OpenList,路径 `/s1/media/movies`,管理方式「自动维护」。 3. 文件管理左栏出现「Alist 存储 → OpenList」:新建目录、上传一个小文件、改名、删除后到回收站还原、移动到另一个目录、复制到本机媒体库(下载)。 4. 整理矩阵(文件管理选文件 →「整理」,目标选对应媒体库目录,各做一次复制、一次移动): - 本机下载目录的视频 → OpenList 电影(上传;移动后本机源消失); - OpenList `/s1/incoming` 下的视频 → 本机媒体库(下载;移动后 OpenList 源消失); - OpenList `/s1/incoming` → OpenList 电影(Alist 内复制 / 移动)。 整理记录里每条为「成功」,目标路径显示「OpenList:/…」。 5. 刮削写远端:OpenList 电影里已整理的作品点「刷新元数据」,OpenList 目录里出现 `.nfo`、`poster`、`clearlogo.png`。 6. 目录监控:设置 → 整理来源新增,来源选「OpenList」根下的 `incoming`,目标 OpenList 电影,触发「目录快照」,间隔 60 秒;首扫建基线,之后往 `/mnt/s1/incoming`(或经 OpenList 上传)放一个新视频,两次扫描后出现整理记录。 7. 运行状态 → 挂载根里 OpenList 在线;在 OpenList 后台重置令牌后再看,显示「令牌失效或没有权限」。 ### 收尾 ```sh docker rm -f umt-alist-it && rm -rf .local/openlist ``` ## 排查 | 现象 | 原因 / 处理 | | --- | --- | | 测试连接「令牌失效或无权限」 | 令牌被重置或用户没权限;重新复制令牌,或改用账号密码 | | 测试连接「连不上」 | 地址不对、容器网络不通;在 app 容器里 `curl <地址>/api/public/settings` | | 测试连接「连不上:HTTP 502」 | 请求被出站代理拦了:Deno 按**主机名**匹配 `NO_PROXY`。内网 IP 与容器名 `alist` / `openlist` 已在缺省列表里;用别的容器名或域名时加进 `.env.deploy` 的 `UMT_NO_PROXY` 后重建 app | | 根路径不存在 | 根路径要用 Alist 里的完整路径(以挂载路径开头,如 `/115/影视`) | | 整理卡在「文件操作可重试失败,等待重试」 | Alist 连不上或限流;恢复后任务按退避自动重试,源文件不会被删 | | 跨网盘复制很慢 | Alist 后台任务在搬数据;在 Alist「任务」页看进度,等待上限在实例高级选项 | | 目录监控看不到深层新文件 | 超过监控深度,或网盘不更新目录修改时间:调大深度或关掉「按修改时间跳过」 | | 容量显示「未知」 | 驱动不给容量或令牌不是管理员(`/api/admin/storage/list`),不影响使用 |