auto backup 2026-07-06 18:00

This commit is contained in:
2026-07-06 18:00:08 +08:00
parent d276ce0cb6
commit 58ba51fdcb
8 changed files with 209 additions and 3 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
"operator.read",
"operator.write"
],
"updatedAtMs": 1782957219692
"updatedAtMs": 1783328657006
}
}
}
+1 -1
View File
@@ -1 +1 @@
{"schema":"openclaw.skill-workshop.proposals-manifest.v1","updatedAt":"2026-07-01T23:52:24.428Z","proposals":[{"id":"jellyfin-nfo-builder-20260701-6434352e0d","kind":"create","status":"pending","title":"Create jellyfin-nfo-builder","description":"为剧集整理统一文件名、生成缺失的 Jellyfin .nfo 剧集元数据","skillName":"jellyfin-nfo-builder","skillKey":"jellyfin-nfo-builder","createdAt":"2026-07-01T23:52:24.406Z","updatedAt":"2026-07-01T23:52:24.406Z","scanState":"clean"},{"id":"weekly-report-g5-20260626-7595273f1d","kind":"create","status":"applied","title":"Create weekly-report-g5","description":"维云智造G5周报生成:整理每日工作→多段邮件(纯文本+HTML含签名图)→存入钉邮草稿箱","skillName":"weekly-report-g5","skillKey":"weekly-report-g5","createdAt":"2026-06-26T00:46:22.478Z","updatedAt":"2026-06-26T00:46:35.812Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-0fc374efd7","kind":"update","status":"pending","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T03:23:27.062Z","updatedAt":"2026-06-24T03:23:27.062Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-cdbeb219a3","kind":"update","status":"applied","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T03:03:28.166Z","updatedAt":"2026-06-24T03:03:30.915Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-a441798af9","kind":"create","status":"applied","title":"Create calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T02:29:02.278Z","updatedAt":"2026-06-24T02:29:27.227Z","scanState":"clean"},{"id":"calibre-title-cleaner-20260624-3760b64e8b","kind":"create","status":"applied","title":"Create calibre-title-cleaner","description":"清洗 Calibre 书库中损坏的 txt 文档书名(HTML标签/长文本/乱码/路径超限修复)","skillName":"calibre-title-cleaner","skillKey":"calibre-title-cleaner","createdAt":"2026-06-24T01:10:56.296Z","updatedAt":"2026-06-24T01:13:39.670Z","scanState":"clean"},{"id":"dianping-review-20260611-d402abec10","kind":"create","status":"pending","title":"Create dianping-review","description":"通过大众点评搜索查询餐厅、老店、美食推荐和评论","skillName":"dianping-review","skillKey":"dianping-review","createdAt":"2026-06-11T23:50:15.731Z","updatedAt":"2026-06-11T23:50:15.731Z","scanState":"clean"}]}
{"schema":"openclaw.skill-workshop.proposals-manifest.v1","updatedAt":"2026-07-06T09:04:18.526Z","proposals":[{"id":"f-download-sorter-20260706-902cc0d5aa","kind":"create","status":"applied","title":"Create f-download-sorter","description":"整理 /mnt/f 下下载的视频文件:扫描→清垃圾→按规则归档到7个大类目录","skillName":"f-download-sorter","skillKey":"f-download-sorter","createdAt":"2026-07-06T09:03:54.941Z","updatedAt":"2026-07-06T09:04:18.513Z","scanState":"clean"},{"id":"jellyfin-nfo-builder-20260701-6434352e0d","kind":"create","status":"pending","title":"Create jellyfin-nfo-builder","description":"为剧集整理统一文件名、生成缺失的 Jellyfin .nfo 剧集元数据","skillName":"jellyfin-nfo-builder","skillKey":"jellyfin-nfo-builder","createdAt":"2026-07-01T23:52:24.406Z","updatedAt":"2026-07-01T23:52:24.406Z","scanState":"clean"},{"id":"weekly-report-g5-20260626-7595273f1d","kind":"create","status":"applied","title":"Create weekly-report-g5","description":"维云智造G5周报生成:整理每日工作→多段邮件(纯文本+HTML含签名图)→存入钉邮草稿箱","skillName":"weekly-report-g5","skillKey":"weekly-report-g5","createdAt":"2026-06-26T00:46:22.478Z","updatedAt":"2026-06-26T00:46:35.812Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-0fc374efd7","kind":"update","status":"pending","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T03:23:27.062Z","updatedAt":"2026-06-24T03:23:27.062Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-cdbeb219a3","kind":"update","status":"applied","title":"Update calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T03:03:28.166Z","updatedAt":"2026-06-24T03:03:30.915Z","scanState":"clean"},{"id":"calibre-cleaner-20260624-a441798af9","kind":"create","status":"applied","title":"Create calibre-cleaner","description":"清洗 Calibre 书库:修复损坏书名/提取作者/检测编码损坏、小文件的批量删除","skillName":"calibre-cleaner","skillKey":"calibre-cleaner","createdAt":"2026-06-24T02:29:02.278Z","updatedAt":"2026-06-24T02:29:27.227Z","scanState":"clean"},{"id":"calibre-title-cleaner-20260624-3760b64e8b","kind":"create","status":"applied","title":"Create calibre-title-cleaner","description":"清洗 Calibre 书库中损坏的 txt 文档书名(HTML标签/长文本/乱码/路径超限修复)","skillName":"calibre-title-cleaner","skillKey":"calibre-title-cleaner","createdAt":"2026-06-24T01:10:56.296Z","updatedAt":"2026-06-24T01:13:39.670Z","scanState":"clean"},{"id":"dianping-review-20260611-d402abec10","kind":"create","status":"pending","title":"Create dianping-review","description":"通过大众点评搜索查询餐厅、老店、美食推荐和评论","skillName":"dianping-review","skillKey":"dianping-review","createdAt":"2026-06-11T23:50:15.731Z","updatedAt":"2026-06-11T23:50:15.731Z","scanState":"clean"}]}
@@ -0,0 +1,64 @@
---
name: "f-download-sorter"
description: "整理 /mnt/f 下下载的视频文件:扫描→清垃圾→按规则归档到7个大类目录"
status: proposal
version: "v1"
date: "2026-07-06T09:03:54.941Z"
---
# /mnt/f 下载文件批量归档整理
## 用途
`/mnt/f/collections``/mnt/f/video` 里堆了大量下载的视频文件时,用此 Skill 批量整理:**清理垃圾 → 按内容类型归档到 `/mnt/f` 下的大类目录**。
## 触发条件
- 用户说"整理 /mnt/f 的视频"、"归档 collections"、"下载文件太多了帮忙理一下"等
- 或者用户显式调用此 skill
## 流程
### Step 1: 扫描并统计
运行 `/home/yangxuan/.nvm/versions/node/v25.2.0/lib/node_modules/openclaw/skills/f-download-sorter/scripts/scan_and_clean.py`
- 遍历 `/mnt/f/collections``/mnt/f/video` 下所有条目
- 输出每个条目的:名称、大小、视频文件数
### Step 2: 清理垃圾
同上脚本自动清理以下两类:
- **空壳目录**:目录总大小 < 1MB 且无视频文件(通常是推广 HTML/URL 文件)
- **小视频残片**:整条目录总大小 < 50MB 且视频内容极少
### Step 3: 按规则归类归档
运行 `/home/yangxuan/.nvm/versions/node/v25.2.0/lib/node_modules/openclaw/skills/f-download-sorter/scripts/classify_and_move.py`
分类规则(按文件名关键词匹配,优先级从上到下):
| 规则 | 关键词 | 目标目录 |
|------|--------|----------|
| 过那条巷子 | "过那条巷子" | `/mnt/f/过那条巷子/` |
| 2048合集 | "2048" | `/mnt/f/2048合集/` |
| AI短剧 | "AI短剧"/"AI自制"/"AI视频"/"美丽新世界"/"少年阿宾"/"NTR寝取"/"嫂子"/"魔女小姨子"/"桃运圣医"/"我和老师的约定" | `/mnt/f/AI短剧/` |
| FC2福利姬 | "FC2"/"Comatozze"/"Dainty"/"KAKAE"/"lananlanan"/"P站" | `/mnt/f/FC2福利姬/` |
| 种子合集 | "BT-btt" | `/mnt/f/合集种子/` |
| 国产露脸 | "19岁"/"冯帆"/"momocats"/"露脸才是王道"/"香蕉" | `/mnt/f/国产露脸/` |
| 自拍泄漏 | 剩余所有含"泄密"/"自拍"/"露脸"/"自慰"/"原相机"/"情侣"/"校花"/"嫩妹"/"福利姬"/"裸聊"/"绿帽"/"良家"/"约炮"/"狗哥"/"土豪"/"原档"/"维度"/"桃子"/"娇娇"/"学妹"/"反差"等关键词 | `/mnt/f/自拍泄漏/` |
| 其他无法归类 | 以上都不匹配 | `/mnt/f/AI短剧/`(兜底) |
**特殊规则:**
- `重生之征服郭伯母``/mnt/f/video/重生之征服郭伯母`)已有 Jellyfin NFO 元数据,**不动**
### Step 4: 清理空目录
移动完毕后,删除 `/mnt/f/collections``/mnt/f/video` 目录(如已为空)。
### Step 5: 报告结果
输出最终 `/mnt/f` 下 7 个大类目录的结构和各目录大小。
## 注意事项
- **WSL `/mnt/` 跨文件系统扫描很慢**,如果条目数极大(>500),优先在 Windows 原生 Python 中执行扫描/移动
- 所有操作都是**同一分区内重命名(mv)**,不复制数据,瞬间完成
- 不会打开/预览视频内容,仅基于文件名关键词分类
- 删除前会经用户确认
## 脚本
两个脚本位于 skill 目录的 `scripts/` 下:
- `scripts/scan_and_clean.py` — 扫描+清理垃圾
- `scripts/classify_and_move.py` — 分类归档移动
@@ -0,0 +1 @@
{"schema":"openclaw.skill-workshop.proposal.v1","id":"f-download-sorter-20260706-902cc0d5aa","kind":"create","status":"applied","title":"Create f-download-sorter","description":"整理 /mnt/f 下下载的视频文件:扫描→清垃圾→按规则归档到7个大类目录","createdAt":"2026-07-06T09:03:54.941Z","updatedAt":"2026-07-06T09:04:18.513Z","createdBy":"skill-workshop","origin":{"agentId":"storage","sessionKey":"agent:storage:main","runId":"ead41134-1fbd-4c3a-92b3-6ac9ff66c881","messageId":"ead41134-1fbd-4c3a-92b3-6ac9ff66c881"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"6bb936a1321fbabce7a95b719c325be86c0a50005faa4e56bce40a058bdc9360","target":{"skillName":"f-download-sorter","skillKey":"f-download-sorter","skillDir":"/home/yangxuan/.openclaw/workspace/storage/skills/f-download-sorter","skillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/f-download-sorter/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-07-06T09:04:18.504Z","critical":0,"warn":0,"info":0,"findings":[]},"appliedAt":"2026-07-06T09:04:18.513Z"}
@@ -0,0 +1 @@
{"schema":"openclaw.skill-workshop.rollback.v1","proposalId":"f-download-sorter-20260706-902cc0d5aa","writtenAt":"2026-07-06T09:04:18.505Z","targetSkillFile":"/home/yangxuan/.openclaw/workspace/storage/skills/f-download-sorter/SKILL.md","action":"create"}
@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""
按关键词规则分类归档 /mnt/f/collections + /mnt/f/video 到 /mnt/f 下的大类目录
"""
import os, shutil, json
SRC = ["/mnt/f/collections", "/mnt/f/video"]
BASE = "/mnt/f"
# 目标大类目录(自动创建)
CATEGORIES = {
"过那条巷子": ("过那条巷子", ["过那条巷子"]),
"2048合集": ("2048合集", ["2048"]),
"AI短剧": ("AI短剧", ["AI短剧","AI自制","AI视频","美丽新世界","少年阿宾","NTR寝取",
"嫂子","魔女小姨子","桃运圣医","我和老师的约定"]),
"FC2福利姬": ("FC2福利姬", ["FC2","Comatozze","Dainty","KAKAE","lananlanan","P站"]),
"合集种子": ("合集种子", ["BT-btt"]),
"国产露脸": ("国产露脸", ["19岁","冯帆","momocats","露脸才是王道","香蕉"]),
"自拍泄漏": ("自拍泄漏", ["泄密","自拍","自慰","原相机","情侣","校花","嫩妹","福利姬",
"裸聊","绿帽","良家","约炮","狗哥","土豪","原档","维度",
"桃子","娇娇","学妹","反差"]),
}
KEEP_AS_IS = {"重生之征服郭伯母"}
def classify(name):
"""返回目标目录名或 None"""
if name in KEEP_AS_IS:
return None
for cat_name, (dir_name, keywords) in CATEGORIES.items():
if any(k in name for k in keywords):
return os.path.join(BASE, dir_name)
return os.path.join(BASE, "AI短剧") # 兜底
moves = []
for base in SRC:
if not os.path.isdir(base):
continue
for entry in sorted(os.listdir(base)):
full = os.path.join(base, entry)
if not os.path.isdir(full):
continue
dst_base = classify(entry)
if dst_base is None:
print(f" ○ 不动: {entry}")
continue
dst = os.path.join(dst_base, entry)
if os.path.exists(dst):
i = 1
while os.path.exists(f"{dst}_{i}"): i += 1
dst = f"{dst}_{i}"
moves.append((full, dst))
# 创建目录
all_dirs = set(os.path.dirname(d) for _, d in moves)
for d in all_dirs:
os.makedirs(d, exist_ok=True)
# 执行移动
print(f"开始移动 {len(moves)} 个条目...")
for src, dst in moves:
try:
shutil.move(src, dst)
print(f"{os.path.basename(src)}")
except Exception as e:
print(f"{os.path.basename(src)} -> {e}")
# 清理空目录
for d in SRC:
if os.path.isdir(d) and not os.listdir(d):
os.rmdir(d)
print(f" ✓ 已删空目录: {d}")
# 报告
print("\n=== /mnt/f 最终结构 ===")
for d in sorted(os.listdir(BASE)):
full = os.path.join(BASE, d)
if os.path.isdir(full):
sz = 0
cnt = 0
for dp, dn, fn in os.walk(full):
for f in fn:
cnt += 1
try: sz += os.path.getsize(os.path.join(dp, f))
except: pass
print(f" {d}/ ({cnt} 文件, {sz/1024**3:.1f}GB)")
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""
扫一扫 + 清垃圾
遍历 /mnt/f/collections 和 /mnt/f/video,输出清单并清理空壳/小文件残片
"""
import os, shutil
SRC = ["/mnt/f/collections", "/mnt/f/video"]
MINSIZE = 50 * 1024 * 1024 # 50MB
video_exts = {'.mp4','.mkv','.avi','.mov','.wmv','.flv','.webm','.ts','.m4v','.rmvb'}
targets = []
print("=== 扫描 ===")
for base in SRC:
if not os.path.isdir(base):
continue
for entry in sorted(os.listdir(base)):
full = os.path.join(base, entry)
if not os.path.isdir(full):
continue
total_sz = 0
real_video = 0
try:
for dp, dn, fn in os.walk(full):
for f in fn:
fp = os.path.join(dp, f)
try:
total_sz += os.path.getsize(fp)
except: pass
if os.path.splitext(f)[1].lower() in video_exts:
real_video += 1
except: pass
action = ""
if total_sz < MINSIZE and real_video == 0:
action = "DELETE"
elif total_sz < MINSIZE and real_video > 0:
action = "DELETE"
else:
action = "KEEP"
print(f" [{action:>6}] {entry} ({total_sz/1024**2:.0f}MB, {real_video}视频)")
if action == "DELETE":
targets.append(full)
if targets:
print(f"\n待删除: {len(targets)}")
print("开始删除...")
for d in targets:
shutil.rmtree(d)
print(f" ✓ 已删: {os.path.basename(d)}")
else:
print("\n无需清理。")
@@ -1,2 +1,2 @@
openclaw-workspace-attestation:v1
2026-07-06T08:49:46.214Z
2026-07-06T09:51:32.683Z