Skip to main content
D

dsh-bili-jar

debuger-114514/dsh-bili-jar

A whale-maid with a jar, sitting in the corner of your DSH window. She goes exploring on Bilibili for whatever you are into, ranks by like, coin and favourite rate rather than view count, and brings back pickled snacks that suit your taste. When a term runs dry she takes another route. Five picks go into the jar, and she tells you what each video is about. News-type axes keep only the last two weeks; game guides and music have no limit, and each axis decides for itself.

Install

dsh plugin --profile web add github:debuger-114514/dsh-bili-jar

README

电子榨菜罐 · dsh-bili-jar

鲸鱼娘带着一个罐子,守在右下角。按照你的兴趣,她会去 B 站探险,按赞币藏率排, 给你找一些符合口味的电子榨菜。

她平时睡着。点一下罐子,她就出门找,回来把五条装进罐子,再和你说说这些视频讲了什么。

出门找 → 满载而归 面板

面板图是用插件自己那套 CSS 渲染的,不是屏摄 —— 真机上长得一样,只是多了窗口背景。

先看三行:

  • 需要 dsh ≥ 0.1.7;装完要重启一次(插件在 bundle 层组装)。
  • 需要 你自己的 DeepSeek API Key。一轮「换一批」大约 ¥0.004–0.01。
  • 时政这类只留最近两周,游戏攻略和音乐不限时间 —— 每根轴她自己定。

它干什么

  • 按你填的兴趣轴(deepseek / 时政 / 历史 / 随便什么)去搜 B 站
  • 按互动率(赞、币、藏、弹幕除以播放)排好坏,而不是播放量
  • 结果并进一个池子,跨天累积;今天没搜到的好东西,明天被别的词带出来还在
  • 最后把前 24 条交给模型,让它挑 5 条,每条写一句理由
  • 理由只说内容:讲了什么、怎么讲的、适合什么时候看。数据用来挑,不写进理由
  • 点开卡片能小窗播放;不感兴趣的可以 👍/👎(攒够 20 条开始影响打分)
  • 点击标题、「打开」或「小窗」即记为已观看,从推荐、关注和待看中移除,小红点同步减少(只统计推荐列表)。「历史」页保留最近 10 条,重复观看移到最前,重启后仍保留;历史超过 10 条时移除最早记录,当前片单仍不会补回已看视频。

模型没配或调用失败时退回纯规则挑片:每根轴先挑一条相似度 ≥0.85 的,同轴最多 2 条、同 UP 最多 1 条。片单不会因为没模型就空着。

一轮都经过什么

搜     每根轴 3 个词(其中 1 个按小时轮换),4 根轴约 9 次搜索,回来 180 条
补货   某根轴凑不够 3 条时,换两条通道再找:按时长带重搜 + 从种子扩散(相关推荐)
并池   和上一轮的池子合并,同一个 BV 只更新数据
筛     时长、播放门槛、充电专属、直播回放、日期窗口、贴不上任何轴 —— 全砍
打分   赞/币/藏/弹幕的率 0.39 + 贴轴 0.30 + 新鲜度 0.10 + 小众 tag 0.06 + 字幕 0.06 + 播放 0.05 + 多轴 0.04
补查   前 12 名回查真实投币数(搜索接口给的是旧的),重新打分
策展   只把前 24 名交给模型,让它挑 5 条、写理由

为什么只给模型 24 条:给整个池子也能挑,但一次调用会贵十倍,挑得还未必更好。

日期:每根轴自己决定

同一个窗口不可能同时适合「时政」和「空洞骑士攻略」—— 前者 14 天就过期,后者 2023 年的攻略今天照样有用。

所以每根轴有一个 freshness:

日期窗口时效分权重
news14 天(全局可改)0.10
evergreen不限0.02

怎么定的:不看条数,看合格条目的年龄中位数。超过 90 天就算常青内容。

实测七个冷门词,分得很干净:丝之歌 361 天、空洞骑士 2052 天、vtuber 2385 天(都是 evergreen);时政 13 天、深蓝 26 天、术力口 5 天、kards 5 天(都是 news)。中间几乎没有模糊地带。

条数会跟着搜索结果抖(同一个词两次跑,命中数会变),中位数不会 —— 所以拿它当判据。

新轴默认 news,跑一轮后自动看:如果它凑不够 3 条、而且合格条目普遍很老,就在同一轮里改判成 evergreen,日志记一行原因。设置页每根轴旁边有个两态小开关,点了就是手填,手填之后不再被自动覆盖。

冷门轴为什么搜不到

搜「空洞骑士」时,B 站 的前 3 页被续作「丝之歌」的新内容占满,五年前的好视频排不进来 —— 有一条 33 分钟、108 万播放的攻略要翻到第 4 页才有。

所以凑不够 3 条时会走两条补充通道:

  1. 按时长带重搜:B 站 搜索支持 duration 参数(1=<10 分 / 2=10–30 / 3=30–60 / 4=>60 分),一页 20 条全落在想要的时长里,比混着搜再筛掉强得多
  2. 种子扩散:/x/web-interface/archive/related?bvid= 是按内容相似度给的,不受新内容淹没。从任意 3 条同主题视频出发(不需要它们合格),去重后能捞回几十条,其中就有搜不到的那条

两条通道都是纯 HTTP,不花模型钱。

小众 tag 分:一条视频的 tag 在池子里越罕见,这一项越高(取最罕见的三个 tag 平均)。目的是别让「必剪创作 / 高能」这种大标签把长尾内容挤下去。

预设

装好就有,不用配:

类型自带
兴趣轴30 根:游戏 / 音乐 / 舞蹈 / 动画 / 知识 / 科技 / 影视 / 娱乐 / 生活 / 美食 / 运动 / 汽车 / 时尚 / 动物 / 鬼畜 / 纪录片 / 搞笑 / 学习 / 手工 / 摄影 / 历史 / 哲学 / 心理学 / 编程 / 数学 / 物理 / 军事 / 财经 / 职场 / 育儿
子簇68 个,比如「游戏 → 沙盒游戏 / 游戏推荐 / 独立游戏」
语气三套:鲸鱼女仆 / 正经简报 / 损友吐槽
头像五张:睡觉 / 看电视 / 探险 / 哦鲸鲸 / 走路(唯一会动的一张)

清单在 docs/presets.md。设置页点「+ 游戏」就把整根轴连子簇加进来。

轴树会长。 如果预设里没有你要的,或者不够细,点「看建议」:

  1. 从你池子里的 tag 挖候选(DF + 两两共现 PMI;跟谁都不特别的高频词会被当泛标签丢掉)
  2. 模型再过一遍:判是不是真细分方向、是不是别的轴的词、起个能当标签用的名字、说一句为什么
  3. 你点一下才写进自己的树(存在 ~/.dsh/bili-jar/axis-tree.json,不碰出厂值)

实跑过的例子:历史 挖出「人文历史档案馆 / 人文 / 美国」,模型全否了,理由是「UP 主品牌不是分类」「太泛」「属于时政轴」;deepseek 留了三个,把 ai编程 改成 AI 编程,把 claude 当作别轴的词丢掉。

记账

三层:单次 / 今天 / 累计。按 DeepSeek 官方规则算钱 —— 北京时间工作日 09:00–12:00、14:00–18:00 是峰时,其余(含周末和法定节假日)半价。

元 / 百万 token缓存命中未命中输出
峰时0.0428
谷时0.0214

历史记录按每条自己的时间戳重算,改规则会把旧账一起修正。

装

桌面版的 desktop profile 不让 CLI 装插件,只能手改依赖:

// ~/.dsh/profiles/desktop/package.json
{
  "dependencies": { "dsh-bili-jar": "link:D:/你的路径/电子榨菜罐" },
  "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "dsh-bili-jar"] } }
}
cd ~/.dsh/profiles/desktop && pnpm install

其他 profile:

dsh plugin --profile web add dsh-bili-jar

装完重启 dsh,再刷新页面。

更新

npm 上的最新版看这里:https://www.npmjs.com/package/dsh-bili-jar

# 走 npm 装的
pnpm up dsh-bili-jar@latest

# 走 GitHub 装的(会重新拉默认分支)
pnpm up dsh-bili-jar

# 桌面版(依赖写死在 profile 里时)
# 把 package.json 里的版本改成 ^0.2.5,或者直接写 @latest,再 pnpm install

装完重启 dsh,然后 Ctrl+Shift+R 硬刷新页面 —— 客户端脚本会被浏览器缓存,光点刷新不够。

怎么确认版本对不对:开一次面板,设置 页底部有版本号;或者直接看接口:

curl -s http://127.0.0.1:你的端口/api/bili-jar/ping | head -c 120

第一次用

  1. 点罐子 → 设置 → 填 API Key(DeepSeek 官方)
  2. 改兴趣轴。默认那四根是我的口味,换成你的
  3. 回「推荐」,点换一批

B 站登录是可选的,只用来取 AI 字幕。二维码是插件自己画的(lib/qr.mjs,零依赖),凭证落在自己的目录里。

数据都在哪

~/.dsh/bili-jar/

文件内容
config.json面板里的设置 + 语气预设(含明文 API Key)
feed.json / pool.json当前片单 / 池子
reasons.json攒下来的推荐理由,关掉再开还能用
usage.json每次调用的 token 和费用
feedback.jsonl / later.json赞踩 / 待看
history.json最近 10 条观看记录,最新在前
credential.jsonB 站登录,只有你自己扫码才有
STATE.md人话摘要:现在什么配置、这一批推了什么、花了多少

安全

做过一次检查,结论都写在这:

  • 只监听 127.0.0.1(DSH 的 webServer 本来就这么绑),局域网访问不到
  • 跨站请求一律拒:带 Sec-Fetch-Site: cross-site,或者 Origin 不是本机的,直接 403。避免你开着插件逛网页时,被某个页面偷偷改配置或触发付费调用
  • API Key 明文存在 config.json,但所有 HTTP 接口只返回 sk-••••后四位,/debug 也一样
  • B 站 cookie 存在 credential.json,只在你自己扫码后写入;POSIX 下用 0600
  • 不读别的插件的凭证,除非你在设置里显式打开「复用刷B站的登录」(默认关)
  • 不收集遥测,不往作者的任何服务器发数据。出站只有两处:bilibili.com 和你配的模型 API
  • 小窗播放是 player.bilibili.com 的 iframe,等于在页面里跑 B 站的播放器

已知的限制

  • 时政区很多视频没有 tag,只能靠标题里的实体词判断。「时政」这个词在 B 站被考公内容占着,真·国际时政得用实体词搜(见 docs/tag-tree.md)
  • 赞踩的学习很粗:只看 UP 和 tag,不看内容本身
  • 越往后捞越靠近池子边缘。「更多电子榨菜」按互动率排队,不像首推那样经过模型
  • 预设树里有些子簇的词不干净(带 UP 名或活动名),个别大区(科技 / 音乐)只挖出 1–2 个
  • 没有端到端测试,只有 tools/ 里几个自检脚本

结构

文件干什么
index.jshost 半端:配置、HTTP 路由、池子落盘、策展接线
client.js浏览器半端:罐子 + 面板
lib/bili.mjsB 站客户端:WBI 签名 / 搜索 / 详情 / 粉丝 / 投稿 / 字幕
lib/pool.mjs候选池:轴匹配、硬过滤、打分、按轴轮转
lib/tree.mjs轴树:内置 + 用户自己长的枝;子簇挖掘
lib/axis-presets.mjs30 根预设轴(工具生成,别手改)
lib/tree-review.mjs让模型审子簇建议
lib/curator.mjs让模型挑片并写理由
lib/reasons.mjs写理由(API 直连 → 宿主模型兜底),带磁盘缓存
lib/usage.mjstoken 记账 + 峰谷计价
lib/qr.mjs零依赖 QR 编码器
lib/statefile.mjs生成 STATE.md
lib/hostdeps.mjs从宿主树里取 @deepseek-ai/*

工具

node tools/why.mjs BV1xxxxx        # 这条为什么没进罐子?逐关诊断
node tools/feed.mjs 10000 0        # 跑一次建池,打印片单
node tools/tree-suggest.mjs 历史    # 从池子里挖子簇建议
node tools/tree-review.mjs deepseek # 规则 + 模型审一遍(--apply 才写盘)
node tools/qr-check.mjs            # QR 编码器与 Python qrcode 逐位比对
node tools/affinity-check.mjs      # 赞踩加权的单测
node tools/pack-check.mjs          # 发版前自检
python tools/cutout.py in.png out.png   # 抠掉画在图里的棋盘格
python tools/sprites.py                 # 从设定表切走路帧

发版

pnpm check          # 自检
npm version patch
git push --follow-tags

推 tag 会触发 .github/workflows/publish.yml 发 npm。细节在 docs/publish.md。

更新日志

0.2.6

  • 介绍重写:主语从「一个罐子」换成「鲸鱼娘带着一个罐子」
  • 首页换成面板实拍 + 走路动图;仓库里加了 screenshots.json,市场详情页会自动抓这两张

0.2.5

  • 头像多一张「走路」:拿原来那套两帧走路做的,页头 AI 的头像会动
  • 装罐时列表底下那个小人也会迈步(同一套两帧),跟廊道里出门找的不是同一个
  • README 顶图换成新的「出门找 → 满载而归」

0.2.4

  • 每根轴自己决定看不看老内容(freshness):按合格条目的年龄中位数判定,超过 90 天就算常青,设置里点一下就能改
  • 冷门轴补货:按时长带重搜 + 从「相关推荐」做种子扩散。搜不到的老片能捞回来(实测「空洞骑士」从 0 条到 19 条)
  • 加载动画改成「出门找 → 满载而归」:12 帧、底部廊道、一步一步走,角度和步幅都调小了
  • 刷新时不再清空旧片单:旧卡片留在原位,走路动画表示正在更新

0.2.x

  • 预设轴树 30 根 / 68 个子簇;子簇可以从池子里长,模型审过再采纳
  • 前端重做:插画面板、装罐时走路、列表底部的小人、四档头像
  • 赞踩参与打分;小窗播放;语气预设支持导入 .md
  • 修:峰谷按本机时区算(成本算成一半)、/explain 的 ReferenceError、/debug 泄露 key、explainOn 死开关、换头像不刷新页头、走路帧被裁掉背包、设置页打不开(漏声明)、头像上传被 64KB 限制挡住
  • 安全:拒跨站请求、响应加 nosniff、凭证文件 0600

0.1.0 第一版:按轴搜索、互动率打分、累积池、策展、扫码登录、记账。

许可

代码 MIT。插画(assets/jar-*.png / av-*.png / walk-*.png)由作者用豆包生成,随仓库提供,只用于这个插件的界面。「哦鲸鲸」那档头像取自 dsh-whale-widget(MIT),详见 NOTICE。

Related plugins