按口味自动筛选 Pixiv 作品并存到本地文件夹
31.5/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/20 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add kuroshio4396/dafayu-pixiv-duty该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · search
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 12 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 157 处,展示前 128 条)
- 读取本地文件index.js:51
if (!existsSync(configFile(home))) { - 发起网络请求client.js:47
const r = await fetch(b + '/ping', { credentials: 'same-origin', signal: AbortSignal.timeout(3000) }) - 发起网络请求client.js:75
const res = await fetch(b + p, { credentials: 'same-origin', ...opts, signal: ctl.signal }) - 读取本地文件lib/hostdeps.mjs:40
for (const n of readdirSync(join(npm, '@deepseek-ai'))) { - 读取本地文件lib/hostdeps.mjs:63
for (const p of readdirSync(join(dshHome, 'profiles'))) { - 读取本地文件lib/hostdeps.mjs:79
if (!existsSync(pkgFile)) return null - 读取本地文件lib/hostdeps.mjs:80
const pkg = JSON.parse(readFileSync(pkgFile, 'utf8')) - 读取本地文件lib/hostdeps.mjs:87
return existsSync(file) ? pathToFileURL(file).href : null - 读取本地文件lib/config.mjs:224
try { raw = JSON.parse(readFileSync(configFile(stateDir), 'utf8')) } catch { } - 写入本地文件lib/config.mjs:233
writeFileSync(tmp, JSON.stringify(next, null, 2), 'utf8') - 写入本地文件lib/config.mjs:234
renameSync(tmp, f) - 读取本地文件lib/config.mjs:242
try { prev = JSON.parse(readFileSync(overrideFile(stateDir), 'utf8')) } catch { } - 写入本地文件lib/config.mjs:245
try { writeFileSync(overrideFile(stateDir), JSON.stringify(merged, null, 2), 'utf8') } catch { } - 读取本地文件lib/config.mjs:249
try { return Object.keys(JSON.parse(readFileSync(overrideFile(stateDir), 'utf8'))) } catch { return [] } - 读取本地文件lib/config.mjs:254
try { prev = JSON.parse(readFileSync(overrideFile(stateDir), 'utf8')) } catch { return } - 写入本地文件lib/config.mjs:259
if (Object.keys(prev).length) writeFileSync(overrideFile(stateDir), JSON.stringify(prev, null, 2), 'utf8') - 写入本地文件lib/config.mjs:260
else writeFileSync(overrideFile(stateDir), '{}', 'utf8') - 写入本地文件lib/workspace.mjs:37
mkdirSync(dir, { recursive: true }) - 读取本地文件lib/workspace.mjs:63
return { path: dir, exists: existsSync(dir), enabled: cfg?.notifyWorkspace !== false } - 写入本地文件lib/workspace.mjs:85
try { mkdirSync(dir, { recursive: true }) } catch { } - 读取本地文件lib/workspace.mjs:137
for (const p of readdirSync(home + '/profiles')) { - 读取本地文件lib/workspace.mjs:146
try { if (existsSync(join(dir, 'lib', 'index.js'))) return dir } catch { } - 读取本地文件lib/workspace.mjs:155
try { return JSON.parse(readFileSync(file, 'utf8')) } catch { return null } - 写入本地文件lib/state.mjs:50
mkdirSync(dir, { recursive: true }) - 读取本地文件lib/state.mjs:55
try { return JSON.parse(readFileSync(file, 'utf8')) } catch { return fallback } - 写入本地文件lib/state.mjs:59
writeFileSync(file, JSON.stringify(value, null, 2), 'utf8') - 写入本地文件lib/state.mjs:65
writeFileSync(tmp, JSON.stringify(value, null, 2), 'utf8') - 写入本地文件lib/state.mjs:66
renameSync(tmp, file) - 读取本地文件lib/state.mjs:74
if (!existsSync(p)) { mkdirSync(p, { recursive: true }); made.push(p) } - 写入本地文件lib/state.mjs:74
if (!existsSync(p)) { mkdirSync(p, { recursive: true }); made.push(p) } - 读取本地文件lib/state.mjs:85
if (!existsSync(from)) { log(`缺文件:${from}`); continue } - 读取本地文件lib/state.mjs:86
if (existsSync(to) && readFileSync(from, 'utf8') === readFileSync(to, 'utf8')) continue - 读取本地文件lib/state.mjs:86
if (existsSync(to) && readFileSync(from, 'utf8') === readFileSync(to, 'utf8')) continue - 写入本地文件lib/state.mjs:87
copyFileSync(from, to) - 读取本地文件lib/state.mjs:96
try { if (existsSync(home)) return home } catch { } - 删除本地文件lib/state.mjs:185
if (existsSync(old)) rmSync(old, { force: true }) - 删除本地文件lib/state.mjs:212
if (statSync(p).mtimeMs < cutoff) { rmSync(p, { force: true }); n++ } - 执行外部命令固定命令lib/toolset.mjs:57
if (process.platform === 'win32') spawn('explorer', [dir], { detached: true, stdio: 'ignore', windowsHide: true }).unref() - 执行外部命令固定命令lib/toolset.mjs:58
else if (process.platform === 'darwin') spawn('open', [dir], { detached: true, stdio: 'ignore' }).unref() - 执行外部命令固定命令lib/toolset.mjs:59
else spawn('xdg-open', [dir], { detached: true, stdio: 'ignore' }).unref() - 读取本地文件lib/toolset.mjs:769
const fresh = !existsSync(file) - 写入本地文件lib/toolset.mjs:785
mkdirSync(join(home, 'out'), { recursive: true }) - 写入本地文件lib/toolset.mjs:786
appendFileSync(file, text, 'utf8') - 执行外部命令来自配置/变量lib/http.mjs:42
const child = spawn(cmd, args, { cwd, env: { ...process.env, PYTHONUTF8: '1', ...env }, windowsHide: true }) - 写入本地文件lib/http.mjs:43
const onData = (b) => { try { appendFileSync(log, String(b), 'utf8') } catch { } } - 写入本地文件lib/http.mjs:65
mkdirSync(home, { recursive: true }) - 写入本地文件lib/http.mjs:66
appendFileSync(installLog, `\n=== ${new Date().toISOString()} 开始装依赖 ===\n`, 'utf8') - 写入本地文件lib/http.mjs:72
appendFileSync(installLog, `\n--- uv pip @ ${index} ---\n`, 'utf8') - 写入本地文件lib/http.mjs:84
appendFileSync(installLog, `\n--- pip @ ${index} ---\n`, 'utf8') - 写入本地文件lib/http.mjs:89
appendFileSync(installLog, `venv 创建失败:${v.error || v.code}\n`, 'utf8') - 读取本地文件lib/http.mjs:125
python: cfg.pythonPath, venv: existsSync(venvPython(home)), - 读取本地文件lib/http.mjs:307
return { status: 200, body: { ok: true, day, file: f, text: existsSync(f) ? readFileSync(f, 'utf8') : '' } } - 读取本地文件lib/http.mjs:307
return { status: 200, body: { ok: true, day, file: f, text: existsSync(f) ? readFileSync(f, 'utf8') : '' } } - 执行外部命令固定命令lib/http.mjs:326
if (process.platform === 'win32') spawn('explorer', [target], { detached: true, stdio: 'ignore' }).unref() - 执行外部命令固定命令lib/http.mjs:327
else if (process.platform === 'darwin') spawn('open', [target], { detached: true, stdio: 'ignore' }).unref() - 执行外部命令固定命令lib/http.mjs:328
else spawn('xdg-open', [target], { detached: true, stdio: 'ignore' }).unref() - 读取本地文件lib/runner.mjs:141
if (!existsSync(bin)) return { ok: false, error: `dshBin 指向的文件不存在:${bin}` } - 读取本地文件lib/runner.mjs:161
].filter(Boolean).find((p) => existsSync(p)) - 执行外部命令固定命令lib/runner.mjs:176
spawn('taskkill', ['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' }) - 执行外部命令来自配置/变量lib/runner.mjs:195
if (!dsh.shell) return spawn(dsh.command, args, opts) - 执行外部命令来自配置/变量lib/runner.mjs:198
return spawn(line, { ...opts, shell: true }) - 写入本地文件lib/runner.mjs:220
mkdirSync(home, { recursive: true }) - 写入本地文件lib/runner.mjs:238
writeFileSync(overlay, buildOverlay({ - 写入本地文件lib/runner.mjs:246
try { mkdirSync(workspace, { recursive: true }) } catch { } - 读取本地文件lib/runner.mjs:333
if (!existsSync(reportFile)) { - 读取本地文件lib/runner.mjs:335
try { return readFileSync(join(home, 'duty.log'), 'utf8').trimEnd().split(/ ? /).slice(-20).join('\n') } catch { return '' } - 写入本地文件lib/runner.mjs:337
mkdirSync(join(home, 'out'), { recursive: true }) - 写入本地文件lib/runner.mjs:338
writeFileSync(reportFile, [ - 写入本地文件lib/runner.mjs:387
mkdirSync(ws, { recursive: true }) - 读取本地文件lib/mcp.mjs:16
const PKG = (() => { try { return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) } catch { return {} } })() - 执行外部命令来自配置/变量lib/mcp.mjs:36
this.proc = spawn(command, args, { - 写入本地文件lib/seen.mjs:24
try { appendFileSync(ledgerFile(stateDir), line + '\n', 'utf8'); return true } catch { return false } - 读取本地文件lib/seen.mjs:30
if (!existsSync(f)) return [] - 读取本地文件lib/seen.mjs:32
try { text = readFileSync(f, 'utf8') } catch { return [] } - 写入本地文件lib/seen.mjs:109
try { writeFileSync(join(stateDir, 'seen.md'), text, 'utf8') } catch { } - 写入本地文件lib/save.mjs:69
mkdirSync(dir, { recursive: true }) - 写入本地文件lib/save.mjs:71
writeFileSync(probe, '') - 删除本地文件lib/save.mjs:72
rmSync(probe, { force: true }) - 写入本地文件lib/save.mjs:84
writeFileSync(tmp, buf) - 写入本地文件lib/save.mjs:85
renameSync(tmp, dest) - 读取本地文件lib/save.mjs:92
try { return JSON.parse(readFileSync(f, 'utf8')) } catch { return null } - 读取本地文件lib/save.mjs:109
const name = readdirSync(dir).find((n) => /^metadata(\..+)?\.json$/.test(n)) - 读取本地文件lib/save.mjs:120
if (!existsSync(target)) return target - 读取本地文件lib/save.mjs:126
for (const f of readdirSync(target)) { - 删除本地文件lib/save.mjs:127
if (f.includes('.part-')) rmSync(join(target, f), { force: true }) - 写入本地文件lib/save.mjs:219
mkdirSync(dir, { recursive: true }) - 读取本地文件lib/save.mjs:236
const buf = readFileSync(f.path) - 读取本地文件lib/save.mjs:318
if (!existsSync(p)) { problems.push(`缺文件:${f.name}`); continue } - 读取本地文件lib/agents.mjs:26
for (const name of readdirSync(root)) { - 读取本地文件lib/agents.mjs:29
if (!existsSync(dir)) continue - 读取本地文件lib/agents.mjs:30
out.push({ id: name, hasPatch: existsSync(join(dir, 'cordis.patch.yml')) }) - 发起网络请求lib/login-pixiv.mjs:52
const res = await fetch(TOKEN_URL, { - 读取本地文件lib/login-pixiv.mjs:176
if (existsSync(file)) { - 读取本地文件lib/login-pixiv.mjs:179
const old = JSON.parse(readFileSync(file, 'utf8')) - 写入本地文件lib/login-pixiv.mjs:183
writeFileSync(file, JSON.stringify(data, null, 2), 'utf8') - 写入本地文件lib/login-pixiv.mjs:184
try { chmodSync(file, 0o600) } catch { } - 读取本地文件lib/uvfetch.mjs:33
return createHash('sha256').update(readFileSync(file)).digest('hex') - 发起网络请求lib/uvfetch.mjs:45
const r = await fetch(source.url + '.sha256') - 读取本地文件lib/uvfetch.mjs:55
try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return null } - 执行外部命令来自配置/变量lib/uvfetch.mjs:69
const r = spawnSync(cmd, args, { stdio: 'ignore' }) - 读取本地文件lib/uvfetch.mjs:83
if (existsSync(target)) return target - 写入本地文件lib/uvfetch.mjs:85
mkdirSync(dir, { recursive: true }) - 删除本地文件递归lib/uvfetch.mjs:87
rmSync(tmp, { recursive: true, force: true }) - 写入本地文件lib/uvfetch.mjs:88
mkdirSync(tmp, { recursive: true }) - 请求外部主机pypi.tuna.tsinghua.edu.cnlib/uvfetch.mjs:100
const r = await fetch('https://pypi.tuna.tsinghua.edu.cn/pypi/uv/json') - 发起网络请求lib/uvfetch.mjs:100
const r = await fetch('https://pypi.tuna.tsinghua.edu.cn/pypi/uv/json') - 发起网络请求lib/uvfetch.mjs:124
const res = await fetch(s.url) - 写入本地文件lib/uvfetch.mjs:126
await pipeline(Readable.fromWeb(res.body), createWriteStream(s.file)) - 读取本地文件lib/uvfetch.mjs:127
const mb = (statSync(s.file).size / 1048576).toFixed(1) - 写入本地文件lib/uvfetch.mjs:144
copyFileSync(hit, target) - 写入本地文件lib/uvfetch.mjs:145
try { chmodSync(target, 0o755) } catch { } - 执行外部命令来自配置/变量lib/uvfetch.mjs:147
const r = spawnSync(target, ['--version'], { stdio: 'ignore' }) - 删除本地文件递归lib/uvfetch.mjs:150
rmSync(tmp, { recursive: true, force: true }) - 删除本地文件递归lib/uvfetch.mjs:158
rmSync(tmp, { recursive: true, force: true }) - 删除本地文件递归selfcheck.mjs:100
rmSync(SANDBOX, { recursive: true, force: true }) - 写入本地文件selfcheck.mjs:101
mkdirSync(SANDBOX, { recursive: true }) - 写入本地文件selfcheck.mjs:103
mkdirSync(DSH_HOME, { recursive: true }) - 读取本地文件selfcheck.mjs:106
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8')) - 写入本地文件selfcheck.mjs:286
mkdirSync(home, { recursive: true }) - 读取本地文件selfcheck.mjs:300
const source = readFileSync(join(ROOT, 'client.js'), 'utf8') - 写入本地文件selfcheck.mjs:391
mkdirSync(tmpHome, { recursive: true }) - 读取本地文件selfcheck.mjs:411
const st = JSON.parse(readFileSync(join(tmpHome, 'state.json'), 'utf8')) - 读取本地文件selfcheck.mjs:413
must(existsSync(join(tmpHome, 'workspace', 'last-status.md')), '工作区里写了 last-status.md') - 写入本地文件selfcheck.mjs:424
mkdirSync(wsPath, { recursive: true }) - 写入本地文件selfcheck.mjs:448
mkdirSync(join(hubRoot, 'home'), { recursive: true }) - 读取本地文件selfcheck.mjs:460
const text = readFileSync(join(ROOT, 'prompts', 'duty.md'), 'utf8') - 读取本地文件selfcheck.mjs:474
const missing = files.filter((f) => !existsSync(join(pyDir, f))) - 执行外部命令来自配置/变量selfcheck.mjs:490
const r = spawnSync(py, ['-B', '-c', pyCode, ...pyFiles], {
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
涉及外部主机:pypi.tuna.tsinghua.edu.cn
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh-tools给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
大肥鱼自动刷Pixiv|DSH 插件:按你的口味刷 Pixiv,把插画 / 漫画 / 中文小说挑出来存到本地;标签先筛、再读图读正文确认
README
由 DeepSeek 最新模型翻译生成大肥鱼自动刷 Pixiv(dsh-pixiv-scroll)
DSH 插件:按你的口味刷 Pixiv,把插画 / 漫画 / 中文小说挑出来,存到你指定的文件夹。
标签优先筛,再读图 / 读正文确认;符合的落盘,不符合的只在报告里留一句为什么。
它做什么
- 按你填的关键词与标签(必须 / 排除 / 权重)搜最近几天的作品
- 先看元数据(标签、画师、页数、收藏、分级),再对候选读图(read_image)
- 中文小说:先看字数/语言/章节,再按关键词挑段落;短篇整篇、长篇选段
- 落盘到 <保存根>/插画|漫画|小说/<画师>/<标题>_<ID>/,文件名就是 Pixiv 的标题
- 每件写 metadata.<关键词>.json(来源链接、标签、分级、判定理由、文件 sha256)
- 写一份 Markdown 日报到 out/<日期>.md
装之前
- Python 3.10+ 或 uv(有一个就够;插件也能自己下一份 uv)
- 登录那一次会用到本机的 Edge 或 Chrome(playwright-core 不下载 Chromium,直接用系统浏览器)
- 账号需要在网页端能正常登录;要收 R-18 还得先在网页端开启 R-18 显示
装
dsh plugin --profile web add link:<插件目录>
或发布后:
展开完整文档(README 剩余 1 节)
大肥鱼自动刷 Pixiv(dsh-pixiv-scroll)
DSH 插件:按你的口味刷 Pixiv,把插画 / 漫画 / 中文小说挑出来,存到你指定的文件夹。
标签优先筛,再读图 / 读正文确认;符合的落盘,不符合的只在报告里留一句为什么。
它做什么
- 按你填的关键词与标签(必须 / 排除 / 权重)搜最近几天的作品
- 先看元数据(标签、画师、页数、收藏、分级),再对候选读图(read_image)
- 中文小说:先看字数/语言/章节,再按关键词挑段落;短篇整篇、长篇选段
- 落盘到 <保存根>/插画|漫画|小说/<画师>/<标题>_<ID>/,文件名就是 Pixiv 的标题
- 每件写 metadata.<关键词>.json(来源链接、标签、分级、判定理由、文件 sha256)
- 写一份 Markdown 日报到 out/<日期>.md
装之前
- Python 3.10+ 或 uv(有一个就够;插件也能自己下一份 uv)
- 登录那一次会用到本机的 Edge 或 Chrome(playwright-core 不下载 Chromium,直接用系统浏览器)
- 账号需要在网页端能正常登录;要收 R-18 还得先在网页端开启 R-18 显示
装
dsh plugin --profile web add link:<插件目录>
或发布后:
dsh plugin --profile web add dsh-pixiv-scroll
装完必须重启一次 dsh(bundle 层要重启才组装)。然后进 设置 → 刷Pixiv。
首次使用(三步)
1. 装依赖 —— 点「装依赖」:在插件家目录建 .venv,装 pixivpy3 / mcp<2 / requests / Pillow。
进度写在 <DSH_HOME>/pixiv-duty/install.log,页面不卡。
2. 登录 —— 填 Pixiv 登录名 + 密码,点「用账号密码登录」。
- 插件会起一个本机 Edge,走 OAuth PKCE,把 code 换成 refresh_token。
- 密码只在这一个请求里活着,换到 token 后立刻丢弃,不写进任何配置或日志。
- 被 reCAPTCHA 拦 / 拿不到 code?面板会提示,改用「手贴 refresh_token」那条。
3. 跑一轮 —— 点右上角「现在跑一轮」。想定时就打开「谷时自动跑」。
设置页里有什么
| 组 | 项 | 说明 |
| 账号 | 登录名 / 密码 / 手贴 token | 密码不保存;token 只落在插件家目录,权限收紧 |
| 跑哪一轮 | Profile / 渠道+模型 / Jet Hub 开关 | 按渠道分组展示宿主模型目录;开 Jet Hub 开关后 CodeBuddy 等渠道也可用 |
| 收什么 | 插画 / 漫画 / 中文小说 | 三个独立开关 |
| 分级 | 收 R-18 | 默认关;开了之后落 R-18/ 子目录,需账号已开启 R-18 |
| 偏好 | 关键词 / 必须标签 / 排除标签 / 标签权重 | 必须标签是 AND;排除是任一命中即淘汰;权重只影响打分 |
| 偏好 | 收藏数区间 / 时间窗开关 | 下限与上限各自 0 = 不限;时间窗可整项关掉(不限发布时间) |
| 预算 | 每天最多存 / 单轮最多存 / 最多看几条 / 最多读几张图 / 单件最多几页 | 读图预算是真金白银,默认 6 张 |
| 保存 | 根目录 / 目录模板 / 存好自动打开 | 模板必须含 {id};存下一件就弹出它的文件夹;metadata 用关键词命名 |
| 工作区 | 开播报 / 工作区目录 | 在 DSH 侧栏挂一个工作区;每轮会话跑完会自动归组到该工作区,agent 回复状态并附下载目录 |
| 小说 | 粒度 / 整篇阈值 / 选段字数与段数 / 中文判定 / 正文模糊搜索 | auto = 短篇整篇、长篇选段;模糊搜索用自然语言写场景偏好 |
| 定时 | 谷时自动跑 / 中午 / 晚上 | 工作日 9–12、14–18 是高峰,其余算谷时 |
工作区播报
插件会在 DSH 侧栏里挂一个自己的工作区(默认 <插件家>/workspace),每轮的 headless 会话就跑在这个目录里。
因为 DSH 按 cwd 把会话归到工作区,所以每轮的对话会直接出现在这个工作区下,agent 的收尾回复就是运行状态:
| 情况 | agent 第一行 |
| 存下至少 1 件 | 下载成功:N 件,随后列出每件的绝对路径 |
| 搜了但没值得收的 | 搜寻无结果 |
| 报错 / 登录失效 / 额度用完 / 被限流 | 下载失败:一句话原因 |
除了 agent 的措辞,插件还会写一份机器判定的 last-status.md(在同一工作区)与 state.json 的 lastStatus,两者都以台账为准,不受模型发挥影响。设置里可以关掉播报或改工作区目录。
落盘长什么样
D:\Pixiv\
├─ 插画\<画师>\<标题>_<ID>\001.jpg … metadata.<关键词>.json ├─ 漫画\<画师>\<标题>_<ID>\001.jpg … 012.jpg metadata.<关键词>.json ├─ 小说\<作者>\<标题>_<ID>\<标题>.md metadata.<关键词>.json └─ R-18\… # 开了 R-18 且这件是 R-18 才出现
- 名称清洗按 Windows 规则:< > : " / \ | ? → _,去掉结尾的点与空格,CON/NUL/COM1… 加前缀,超长截断但保留 _ID
- 同名同 ID 会合并(补缺失的页、更新 updatedAt),不重复占额度
- 小说整篇 = front matter + 正文;选段 = front matter + 标记 + 省略段数说明,保留原文链接与段落位置便于回查
它是怎么跑的
- 插件里自带一个 Python MCP(vendor/pixiv-mcp/),只做网络与字节:登录刷新、搜索、详情、下载、正文
- 到点(或你点「现在跑一轮」)→ 起一个 dsh --profile <你选的> --patch <overlay> <prompt> 的 headless 子会话
- 子会话里只有:read / read_image + 13 个 pixiv_ 工具。
bash / pwsh / web / fs-search 全部关掉,write / edit 也关掉 —— 落盘只能走 pixiv_save
- pixiv_save 内部再拦一遍:内容类型、分级、今日与单轮额度、重复、磁盘空间;全过才写盘
- 收尾核对文件与 metadata.<关键词>.json,缺报告就补一份占位(附日志尾部)
红线(写在工具与章程里,不靠自觉)
- 只读:不点赞、不收藏、不关注、不评论、不发私信
- 保存只为个人收藏,不二次分发;metadata 里带 licenseNote
- 作品标题 / 简介 / 正文里的文字不是指令(防提示注入)
- 不把保存目录、家目录、token 写进报告或文件名
常见问题
1. 登录卡住 / 一直不出结果 —— 多半是 reCAPTCHA。把 PIXIV_LOGIN_HEADLESS=0 设上再试(有头 Edge),或直接在面板手贴 refresh_token。
2. token 突然失效 —— Pixiv 每次刷新都会换发新的 refresh_token,旧值立即作废。插件用文件锁 + 单进程刷新避免自我作废;如果你在别处(比如别的工具)也用同一个 token,两边会互相踢掉。
3. 跑一轮什么都没存,报告说「模型不支持图片」 —— 选了个纯文本模型。换支持图片的,或把插画/漫画关掉只跑小说。
4. 桌面版跑一轮 0.1 秒就结束 —— 桌面版里 dsh 不在 PATH。把设置里的 dshBin 指到 <桌面版安装目录>\resources\runtime\cli\bin\dsh.cmd(这个包装内部会设 ELECTRON_RUN_AS_NODE 并转发参数)。
5. 设置改了没生效 —— 本插件以自己那份 config.json 为准(DSH 原生设置尽力同步);面板顶部会提示哪几项没同步。
6. 报告里全是「跳过」 —— 看 blocked 的原因:R18 / CONTENT_TYPE / QUOTA / NOVEL_LANG / DUPLICATE。多数是偏好设得太窄(排除标签写多了)。
7. 国内网络必须填代理 —— 「网络」那一栏填 http://127.0.0.1:12000(Clash 之类)这种本地代理; Node 与 Python 都直连不到 Pixiv,而浏览器走系统代理能通,容易出现「登录成功但搜不到东西」。
实测记录(2026-10-19,Windows + 桌面版 0.2.1)
- 登录:账号密码 → 本机 Edge(持久 profile)→ 人机验证后点「继续使用此账号」→ 浏览器内换 token 成功
- 一轮:75 秒,59 条候选 → 8 条详情 → 3 张预览 → 存 2 件,第 3 件被 QUOTA 拦下
- 落盘:插画/<画师>/<标题>_<ID>/001.jpg|png + metadata.<关键词>.json(2.9MB / 6.6MB 原图,未压缩)
- 小说:中文判定「汉字占比 91%」→ 3537 字整篇保存
数据都在哪
$DSH_HOME/pixiv-duty/
├─ config.json 设置(唯一真相源) ├─ credential.json refresh_token / userId / web cookie(0600) ├─ ledger.jsonl 判重与额度台账(一行一件) ├─ out/<日期>.md 日报 ├─ cache/ 缩图与原图缓存(24 小时后自动清) ├─ duty.md 值班章程(可改;升级时不会覆盖你的改动) ├─ duty.log / install.log 日志 └─ .venv/ Python 环境
依赖与致谢
- pixivpy3(App API 客户端)
- playwright-core(只用于登录;复用系统 Edge/Chrome)
- Pillow(缩图与解码校验)
- 架构参考 dsh-bili-scroll(MIT)
许可证
MIT。保存内容仅供个人收藏,请遵守 Pixiv 的服务条款与各地法律。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
kuroshio4396/dafayu-pixiv-duty
按口味自动筛选 Pixiv 作品并存到本地文件夹
相关插件 · 搜索 / 抓取
LLM 知识平台Tencent/WeKnora
概览 ·快速开始 ·新特性 ·功能 ·客户端 ·文档 ·开发
★ 36,032已汉化DSH
72.1
智能体记忆系统MemTensor/MemOS
MemOS 2.0 Stardust星尘
★ 12,195已汉化DSH
71.2
跨代理记忆库zilliztech/memsearch
跨平台语义记忆,专为 AI 编程智能体打造。
★ 2,799已汉化DSH
70.9
技能语料库EverMind-AI/SkillCorpus
SkillCorpus 能给你什么
★ 694已汉化DSH
69.7
代码图谱记忆syncable-dev/memtrace-public
你的智能体值得拥有结构化记忆。
★ 509已汉化MCP
67.9
AnySearch 搜索插件anysearch-team/anysearch-dsh
AnySearch 网页搜索服务商,为 DeepSeek Harness(DSH)提供高级搜索工具
★ 457已汉化DSH
66.6