auto backup 2026-07-06 18:00
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
"operator.read",
|
||||
"operator.write"
|
||||
],
|
||||
"updatedAtMs": 1782957219692
|
||||
"updatedAtMs": 1783328657006
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"}
|
||||
+86
@@ -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)")
|
||||
+54
@@ -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
-1
@@ -1,2 +1,2 @@
|
||||
openclaw-workspace-attestation:v1
|
||||
2026-07-06T08:49:46.214Z
|
||||
2026-07-06T09:51:32.683Z
|
||||
|
||||
Reference in New Issue
Block a user