fix: 本地上传的视频被拉黑后无法恢复 - #89
Merged
nianzhibai merged 2 commits intoAug 14, 2026
Merged
Conversation
Hiding a video from the front end deletes its row and writes a blacklist tombstone. Local uploads were then classified as unrestorable, so the admin UI offered no way back and the video was gone for good even though its file was still on disk: the local-upload drive returns ErrNotSupported from List, so no scan or crawl can ever rediscover it. Add a fourth restore policy, "direct", for sources whose file this application retains but cannot enumerate. Removing such a tombstone rebuilds the catalog row on the spot from the restore payload the tombstone already stores, so title, author, tags and description survive. Derived assets were deleted with the original row, so thumbnail, preview and transcode state are reset to pending for the generation workers to rebuild. Because this is the only restore path that writes a row back unconditionally, it first asks the caller to confirm the retained source file still exists; the other policies rely on a rescan that simply finds nothing when the file is gone. The policy checks are reordered so a deleted source and a deduplicated row still resolve to "none" regardless of the drive, leaving scan and crawler sources untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Restore retained local-upload sources directly, simplify the cancellation confirmation, and remove the redundant local-upload badge.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
前台点「不再展示」会删掉视频记录并写一条黑名单墓碑(
OnHideVideo→deleteVideo(ctx, id, false))。对本地上传的视频,这个操作不可逆:deletedVideoRestorePolicy把drive_id == "local-upload"判为none,后台黑名单页面因此连「取消拉黑」按钮都不渲染,只显示「不可自动恢复」deleteSource=false,源文件其实还在 uploads 目录里localupload.Driver.List()直接返回drives.ErrNotSupported(internal/drives/localupload/driver.go:32),这个盘无法枚举,没有任何扫盘或爬取能重新发现它;maintainLocalUploadFileNames也只遍历已入库的视频,不扫描目录结果是:文件占着磁盘,视频永远回不来,用户在后台没有任何补救手段,只能重新上传一遍。
改动
新增第四种恢复策略
direct,适用于「源文件由本应用保留、但无法枚举」的来源(目前只有本地上传)。取消拉黑时当场重建记录,而不是去等一场永远不会到来的扫盘。恢复是无损的:
DeleteVideoWithTombstoneOptions本来就把完整的 Video JSON 存进了restore_payload(catalog.go:1128),所以标题、作者、标签、简介、时长都能还原。派生资源(封面、预览、转码产物)在拉黑时已被物理删除,因此重置为pending交给生成 worker 重建 —— 保留原路径会让记录指向不存在的文件。因为 direct 是唯一无条件写回记录的路径,恢复前会先让调用方确认保留下来的源文件仍然存在(
RemoveDeletedVideoWithSourceCheck)。其他策略靠「扫不到就不入库」天然安全,不需要这层校验,也不会触发它。实现上参考了爬虫已有的
RestoreRequestedVideos——「保留文件 + 墓碑 → 重新入库」这套流程本来就跑通了,本地上传只是缺了对应的那一半。兼容性
策略判定的顺序做了调整,把「源文件已删」和「重复文件」提到来源判断之前:
所以行为发生变化的只有「本地上传 + 源文件还在 + 非重复」这一种组合,也就是这个 bug 本身。源文件已删的本地上传、被去重删掉的本地上传、普通网盘、爬虫来源,行为全部不变,这三条都有测试锁定。
restorePolicy全项目只有两个消费点(ListDeletedVideos展示、RemoveDeletedVideo分支判断);扫盘拦截、去重、备份恢复、源文件删除队列都是直接查 SQL,不读这个字段。RemoveDeletedVideo(ctx, id)的签名保持不变,11 个既有调用点一个都没改。测试
后端新增/更新 6 个用例(
internal/catalog+internal/api):directnone(优先级回归)none(优先级回归)restore_payload为空时的兜底(标题从文件名还原)前端新增 1 个用例(
tests/adminResponsive.test.ts),481 个前端测试全绿,tsc --noEmit干净。在纯上游代码基(8cda4a5)上验证过:
go build ./...通过,internal/catalog与黑名单相关测试全部通过。另外跑了全量回归对比(上游基线 vs 本改动),失败集合零新增。🤖 Generated with Claude Code
Follow-up 修正
Review 后补充了系统级完整性修复(
8d09e75):direct恢复改为在同一事务中写回视频、标签关系并删除 tombstone,同时兼容旧版恢复 payload,避免留下部分恢复状态。skipped处理,避免误删恢复后的源文件。skipped计数。