@streamapp/stream 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/README.md +24 -0
  2. package/bin/stream.mjs +89 -0
  3. package/package.json +34 -0
  4. package/resources/app/dist-panel/panel-boards.js +6 -0
  5. package/resources/app/dist-panel/panel-detail.css +1 -0
  6. package/resources/app/dist-panel/panel-detail.js +2141 -0
  7. package/resources/app/dist-panel/panel-manage.css +1 -0
  8. package/resources/app/dist-panel/panel-manage.js +2246 -0
  9. package/resources/app/dist-panel/panel-movie.css +1 -0
  10. package/resources/app/dist-panel/panel-movie.js +2268 -0
  11. package/resources/app/dist-panel/panel-research.css +1 -0
  12. package/resources/app/dist-panel/panel-research.js +316 -0
  13. package/resources/app/dist-panel/panel.css +1 -0
  14. package/resources/app/dist-panel/panel.js +463 -0
  15. package/resources/dsh-engine-lock/package-lock.json +9032 -0
  16. package/resources/extension/.output/chrome-mv3/assets/popup-C2NUZZQW.css +2 -0
  17. package/resources/extension/.output/chrome-mv3/background.js +1 -0
  18. package/resources/extension/.output/chrome-mv3/chunks/popup-BeBts9v2.js +50 -0
  19. package/resources/extension/.output/chrome-mv3/icon/128.png +0 -0
  20. package/resources/extension/.output/chrome-mv3/icon/16.png +0 -0
  21. package/resources/extension/.output/chrome-mv3/icon/32.png +0 -0
  22. package/resources/extension/.output/chrome-mv3/icon/48.png +0 -0
  23. package/resources/extension/.output/chrome-mv3/icon/96.png +0 -0
  24. package/resources/extension/.output/chrome-mv3/manifest.json +1 -0
  25. package/resources/extension/.output/chrome-mv3/popup.html +21 -0
  26. package/resources/packages/1lou/1lou-search.recipe.json +102 -0
  27. package/resources/packages/1lou/README.md +7 -0
  28. package/resources/packages/1lou/package.json +15 -0
  29. package/resources/packages/Douyin_TikTok_Download_API/README.md +23 -0
  30. package/resources/packages/Douyin_TikTok_Download_API/activate.ts +14 -0
  31. package/resources/packages/Douyin_TikTok_Download_API/adapter/adapter.ts +321 -0
  32. package/resources/packages/Douyin_TikTok_Download_API/adapter/danmaku.ts +21 -0
  33. package/resources/packages/Douyin_TikTok_Download_API/adapter/executor.ts +75 -0
  34. package/resources/packages/Douyin_TikTok_Download_API/adapter/normalize.ts +20 -0
  35. package/resources/packages/Douyin_TikTok_Download_API/manifests.yaml +521 -0
  36. package/resources/packages/Douyin_TikTok_Download_API/package.json +32 -0
  37. package/resources/packages/alist/README.md +14 -0
  38. package/resources/packages/alist/activate.ts +12 -0
  39. package/resources/packages/alist/adapter.ts +74 -0
  40. package/resources/packages/alist/manifests.yaml +76 -0
  41. package/resources/packages/alist/mounts.ts +120 -0
  42. package/resources/packages/alist/package.json +24 -0
  43. package/resources/packages/alist/presets.ts +53 -0
  44. package/resources/packages/alist/provision.ts +84 -0
  45. package/resources/packages/baidu/baidu-share.recipe.json +93 -0
  46. package/resources/packages/baidu/package.json +16 -0
  47. package/resources/packages/baidu-search/baidu-search.recipe.json +129 -0
  48. package/resources/packages/baidu-search/package.json +25 -0
  49. package/resources/packages/brave/brave-search.recipe.json +129 -0
  50. package/resources/packages/brave/package.json +25 -0
  51. package/resources/packages/browser/README.md +3 -0
  52. package/resources/packages/browser/adapter.ts +44 -0
  53. package/resources/packages/browser/manifests.yaml +20 -0
  54. package/resources/packages/browser/package.json +13 -0
  55. package/resources/packages/bt0/README.md +7 -0
  56. package/resources/packages/bt0/bt0-search.recipe.json +69 -0
  57. package/resources/packages/bt0/bt0-tlist.recipe.json +74 -0
  58. package/resources/packages/bt0/package.json +15 -0
  59. package/resources/packages/btbtla/README.md +6 -0
  60. package/resources/packages/btbtla/btbtla-detail.recipe.json +76 -0
  61. package/resources/packages/btbtla/btbtla-search.recipe.json +74 -0
  62. package/resources/packages/btbtla/package.json +15 -0
  63. package/resources/packages/builtin/README.md +8 -0
  64. package/resources/packages/builtin/manifests.yaml +487 -0
  65. package/resources/packages/builtin/package.json +14 -0
  66. package/resources/packages/douyin/README.md +39 -0
  67. package/resources/packages/douyin/douyin-collection.recipe.json +127 -0
  68. package/resources/packages/douyin/douyin-search.recipe.json +163 -0
  69. package/resources/packages/douyin/package.json +16 -0
  70. package/resources/packages/firecrawl/firecrawl-create-key.recipe.json +81 -0
  71. package/resources/packages/firecrawl/firecrawl-read-key.recipe.json +56 -0
  72. package/resources/packages/firecrawl/package.json +16 -0
  73. package/resources/packages/google/google-search.recipe.json +127 -0
  74. package/resources/packages/google/package.json +24 -0
  75. package/resources/packages/groq/groq-create-key.recipe.json +67 -0
  76. package/resources/packages/groq/package.json +16 -0
  77. package/resources/packages/huggingface/README.md +7 -0
  78. package/resources/packages/huggingface/huggingface-spaces.recipe.json +73 -0
  79. package/resources/packages/huggingface/package.json +15 -0
  80. package/resources/packages/imdb/imdb-chart.recipe.json +75 -0
  81. package/resources/packages/imdb/package.json +15 -0
  82. package/resources/packages/index.ts +15 -0
  83. package/resources/packages/iqiyi/README.md +10 -0
  84. package/resources/packages/iqiyi/iqiyi-cn-album.recipe.json +80 -0
  85. package/resources/packages/iqiyi/iqiyi-cn-search.recipe.json +71 -0
  86. package/resources/packages/iqiyi/package.json +15 -0
  87. package/resources/packages/lizhi/README.md +35 -0
  88. package/resources/packages/lizhi/lizhi-user.recipe.json +80 -0
  89. package/resources/packages/lizhi/package.json +15 -0
  90. package/resources/packages/manmanbuy/README.md +16 -0
  91. package/resources/packages/manmanbuy/manmanbuy-search.recipe.json +115 -0
  92. package/resources/packages/manmanbuy/package.json +15 -0
  93. package/resources/packages/mineru/README.md +38 -0
  94. package/resources/packages/mineru/package.json +28 -0
  95. package/resources/packages/pansou/README.md +14 -0
  96. package/resources/packages/pansou/activate.ts +11 -0
  97. package/resources/packages/pansou/adapter.ts +94 -0
  98. package/resources/packages/pansou/manifests.yaml +50 -0
  99. package/resources/packages/pansou/package.json +27 -0
  100. package/resources/packages/qq/package.json +15 -0
  101. package/resources/packages/qq/qq-send.recipe.json +104 -0
  102. package/resources/packages/quark/package.json +16 -0
  103. package/resources/packages/quark/quark-share.recipe.json +40 -0
  104. package/resources/packages/reddit/package.json +15 -0
  105. package/resources/packages/reddit/reddit-sub.recipe.json +77 -0
  106. package/resources/packages/replay/README.md +7 -0
  107. package/resources/packages/replay/package.json +13 -0
  108. package/resources/packages/rsshub/README.md +16 -0
  109. package/resources/packages/rsshub/manifests.yaml +107 -0
  110. package/resources/packages/rsshub/package.json +21 -0
  111. package/resources/packages/telegram/package.json +15 -0
  112. package/resources/packages/telegram/telegram-search.recipe.json +125 -0
  113. package/resources/packages/tencent/README.md +11 -0
  114. package/resources/packages/tencent/package.json +15 -0
  115. package/resources/packages/tencent/tencent-cn-episode.recipe.json +98 -0
  116. package/resources/packages/toubiec/README.md +38 -0
  117. package/resources/packages/toubiec/package.json +15 -0
  118. package/resources/packages/toubiec/toubiec-download.recipe.json +58 -0
  119. package/resources/packages/toubiec/toubiec-playlist.recipe.json +49 -0
  120. package/resources/packages/toubiec/toubiec-search.recipe.json +80 -0
  121. package/resources/packages/voiceprint/README.md +31 -0
  122. package/resources/packages/voiceprint/package.json +36 -0
  123. package/resources/packages/wikipedia/package.json +15 -0
  124. package/resources/packages/wikipedia/wikipedia-award-list.recipe.json +102 -0
  125. package/resources/packages/xhs/README.md +32 -0
  126. package/resources/packages/xhs/package.json +21 -0
  127. package/resources/packages/xhs/xhs-detail.recipe.json +129 -0
  128. package/resources/packages/xhs/xhs-home.recipe.json +144 -0
  129. package/resources/packages/xhs/xhs-like.recipe.json +96 -0
  130. package/resources/packages/xhs/xhs-search.recipe.json +138 -0
  131. package/resources/packages/xueqiu/README.md +9 -0
  132. package/resources/packages/xueqiu/package.json +16 -0
  133. package/resources/packages/xueqiu/xueqiu-detail.recipe.json +92 -0
  134. package/resources/packages/xueqiu/xueqiu-user.recipe.json +75 -0
  135. package/resources/packages/zhipu/package.json +16 -0
  136. package/resources/packages/zhipu/zhipu-create-key.recipe.json +74 -0
  137. package/resources/packages/zuna/README.md +31 -0
  138. package/resources/packages/zuna/package.json +15 -0
  139. package/resources/packages/zuna/zuna-album.recipe.json +46 -0
  140. package/resources/packages/zuna/zuna-download.recipe.json +53 -0
  141. package/resources/packages/zuna/zuna-playlist.recipe.json +46 -0
  142. package/resources/packages/zuna/zuna-search.recipe.json +96 -0
  143. package/resources/server.mjs +238876 -0
  144. package/resources/sidequest.jobs.js +1 -0
@@ -0,0 +1,77 @@
1
+ {
2
+ "version": 2,
3
+ "kind": "fetch",
4
+ "sourceId": "reddit-sub",
5
+ "cookieDomain": "reddit.com",
6
+ "entryUrl": "https://www.reddit.com/r/{subreddit}/",
7
+ "entryWait": "domcontentloaded",
8
+ "request": {
9
+ "url": "https://www.reddit.com/r/{subreddit}/hot.json?limit=25&after={cursor}",
10
+ "method": "GET",
11
+ "headers": {
12
+ "accept": "application/json"
13
+ }
14
+ },
15
+ "pagination": {
16
+ "mode": "cursor",
17
+ "itemsAt": "data.children",
18
+ "cursorFrom": "data.after",
19
+ "cursorParam": "after",
20
+ "hasMore": "data.after",
21
+ "maxPages": 3
22
+ },
23
+ "assert": [
24
+ {
25
+ "path": "data.children",
26
+ "desc": "hot.json 返回 Listing data.children 数组 —— 登录墙 / IP 封锁 / 端点已变"
27
+ }
28
+ ],
29
+ "mapping": {
30
+ "guid": "data.id",
31
+ "title": "data.title",
32
+ "link": "https://www.reddit.com{data.permalink}",
33
+ "author": "data.author",
34
+ "like_count": "data.score",
35
+ "comment_count": "data.num_comments",
36
+ "pubDate": "data.created_utc",
37
+ "description": "data.selftext"
38
+ },
39
+ "meta": {
40
+ "type": "post",
41
+ "description": "Reddit — subreddit 热门帖流",
42
+ "topics": [
43
+ "reddit",
44
+ "subreddit",
45
+ "discussion",
46
+ "社区"
47
+ ],
48
+ "categories": [
49
+ "social-media"
50
+ ],
51
+ "facility": {
52
+ "key": "reddit",
53
+ "label": "Reddit"
54
+ },
55
+ "capabilities": [
56
+ "timeline"
57
+ ],
58
+ "key_param": "subreddit",
59
+ "priority": 80,
60
+ "discoverable": true,
61
+ "cadence_hint_seconds": 1800,
62
+ "auth": {
63
+ "type": "none"
64
+ },
65
+ "homepage": "reddit.com",
66
+ "params_schema": {
67
+ "subreddit": {
68
+ "type": "string",
69
+ "required": true,
70
+ "description": "subreddit 名称(如 selfhosted、LocalLLaMA)"
71
+ }
72
+ },
73
+ "radar": [
74
+ "reddit.com/r/:subreddit"
75
+ ]
76
+ }
77
+ }
@@ -0,0 +1,7 @@
1
+ # Recipe
2
+
3
+ 登录态浏览器的 recipe 兜底采集引擎。
4
+
5
+ 无独立 facility:签名 / 登录门禁站点(小红书、雪球…)的 recipe 源都归在这个插件下。
6
+ 源来自 `recipes/` 包,这里只补插件级 name/description(manifest 是元数据唯一的家)。
7
+ 无 backend → 进程内(用户自己的 Chrome + ext-cdp 中继),不生成容器。
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "@streamapp/replay",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "stream": {
7
+ "id": "replay",
8
+ "name": "Recipe",
9
+ "tagline": "按 recipe 在你登录态的浏览器里采集",
10
+ "description": "把签名或登录门禁、直连与 RSSHub 都取不到的站点写成一份 recipe,在你自己登录态的浏览器里照着跑——页面自签请求、DOM 抓取,或调用站点自身的请求客户端。采集阶梯的最后一档兜底,运行期零 token。",
11
+ "normalizer": "generic"
12
+ }
13
+ }
@@ -0,0 +1,16 @@
1
+ # RSSHub
2
+
3
+ Upstream tagline: "Everything is RSSible".
4
+
5
+ `required: true` —— 核心插件:长尾 RSS 目录的唯一来源,产品离了它就没内容。UI 上开关锁定为开、
6
+ 不可关闭。
7
+
8
+ ## Runtime form
9
+
10
+ RSSHub runs in a WORKER THREAD, not a container and not in-process. It's the one plugin whose fetch
11
+ logic is foreign heavyweight code (3000+ routes, a global-fetch monkeypatch, on-first-hit route
12
+ compilation) — a worker gives it its own globals + event loop so none of that leaks into Stream,
13
+ while keeping cookie hot-reload a postMessage away. Wiring: `src/rsshub-adapter.ts` →
14
+ `src/rsshub-client.ts` (spawns) → `src/rsshub-worker.ts` (harness runs RSSHub). No `backend:` block:
15
+ there is no container. This runtime isn't a declared descriptor field yet (RSSHub is its sole user;
16
+ generalising a `worker:` schema for one occupant would be premature) — see rsshub-static-catalog.
@@ -0,0 +1,107 @@
1
+ # bilibili-dynamic / bilibili-following retired 2026-07-04 — the RSSHub catalog twins
2
+ # (rsshub:bilibili/user/dynamic/*, rsshub:bilibili/followings/dynamic/*) now derive
3
+ # cookie auth from features.requireConfig via NS_COOKIE_DOMAIN in src/rsshub-catalog.ts.
4
+ - id: btbtla-detail-lazy
5
+ adapter: rsshub
6
+ description: BTBTLA | 指定剧名 — 影视剧种子搜索(lazy 模式:不在搜索时解析磁力,点开单条时由 resolve.ts 直连 /tdown/ 现解,所以搜索很快)。
7
+ topics: [影视, 电影, 电视剧, 剧集, btbtla, bt, 种子, magnet, 下载, video, tv, movie]
8
+ categories: [video]
9
+ example_queries:
10
+ - btbtla 搜 雍正王朝
11
+ - 找一下 庆余年 的种子
12
+ - 上载新生 下载
13
+ capabilities: [search]
14
+ auth:
15
+ type: none
16
+ route: /btbtla/detail/:name/lazy
17
+ cadence_hint_seconds: 1800
18
+ params_schema:
19
+ name:
20
+ type: string
21
+ required: true
22
+ # lizhi-user / xiaoyuzhou-podcast retired 2026-07-04 — the podcast-feed Provider row
23
+ # now expands to the RSSHub catalog twins (rsshub:lizhi/user/:id,
24
+ # rsshub:xiaoyuzhou/podcast/:id) via {mode:'auto', matches} on their radar patterns.
25
+ - id: rsshub-raw
26
+ adapter: rsshub
27
+ description: Raw RSSHub route passthrough for ad-hoc fetches (escape hatch).
28
+ topics: []
29
+ example_queries: []
30
+ capabilities: [timeline]
31
+ auth:
32
+ type: none
33
+ discoverable: false
34
+ cadence_hint_seconds: 3600
35
+ params_schema:
36
+ route:
37
+ type: string
38
+ required: true
39
+
40
+ # —— 影视频道(video variant)榜单源 ——
41
+ # 固定 route(无占位符)+ normalizer: movie —— movie normalizer 按 id 分派解析
42
+ # rating/year/genre(见 src/content/movie.ts)。是 default-video 频道 7 条 stream 的成员。
43
+ - id: movie-douban-playing
44
+ adapter: rsshub
45
+ normalizer: movie
46
+ mode: collection
47
+ description: 豆瓣电影 — 正在热映
48
+ topics: [影视, 电影, 豆瓣, 热映, movie]
49
+ categories: [video]
50
+ capabilities: [timeline]
51
+ facility: { key: douban, label: 豆瓣 }
52
+ auth:
53
+ type: none
54
+ route: /douban/movie/playing
55
+ cadence_hint_seconds: 21600
56
+ - id: movie-douban-weekly
57
+ adapter: rsshub
58
+ normalizer: movie
59
+ mode: collection
60
+ description: 豆瓣电影 — 一周口碑榜
61
+ topics: [影视, 电影, 豆瓣, 口碑, 榜单, movie]
62
+ categories: [video]
63
+ capabilities: [timeline]
64
+ facility: { key: douban, label: 豆瓣 }
65
+ auth:
66
+ type: none
67
+ route: /douban/movie/weekly
68
+ cadence_hint_seconds: 21600
69
+ - id: movie-tmdb-trend-movie
70
+ adapter: rsshub
71
+ normalizer: movie
72
+ mode: collection
73
+ description: TMDB — 本周热门电影趋势
74
+ topics: [影视, 电影, tmdb, trending, movie]
75
+ categories: [video]
76
+ capabilities: [timeline]
77
+ facility: { key: themoviedb, label: TMDB }
78
+ auth:
79
+ type: none
80
+ route: /themoviedb/trending/movie/week/zh-CN
81
+ cadence_hint_seconds: 21600
82
+ - id: movie-tmdb-trend-tv
83
+ adapter: rsshub
84
+ normalizer: movie
85
+ mode: collection
86
+ description: TMDB — 本周热门剧集趋势
87
+ topics: [影视, 剧集, tmdb, trending, tv]
88
+ categories: [video]
89
+ capabilities: [timeline]
90
+ facility: { key: themoviedb, label: TMDB }
91
+ auth:
92
+ type: none
93
+ route: /themoviedb/trending/tv/week/zh-CN
94
+ cadence_hint_seconds: 21600
95
+ - id: movie-imdb-popular
96
+ adapter: rsshub
97
+ normalizer: movie
98
+ mode: collection
99
+ description: IMDb — 最热门电影
100
+ topics: [影视, 电影, imdb, popular, movie]
101
+ categories: [video]
102
+ capabilities: [timeline]
103
+ facility: { key: imdb, label: IMDb }
104
+ auth:
105
+ type: none
106
+ route: /imdb/chart/moviemeter
107
+ cadence_hint_seconds: 21600
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@streamapp/rsshub",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "stream": {
7
+ "id": "rsshub",
8
+ "required": true,
9
+ "name": "RSSHub",
10
+ "tagline": "世界上最大的 RSS 网络",
11
+ "description": "网易云、Twitter、Instagram、Telegram、哔哩哔哩、知乎、微博、GitHub、YouTube 等网站和应用的信息源。",
12
+ "homepage": "https://rsshub.app",
13
+ "repository": "https://github.com/DIYgod/RSSHub",
14
+ "docsUrl": "https://docs.rsshub.app",
15
+ "normalizer": "rsshub",
16
+ "sourceGrouping": {
17
+ "enabled": true,
18
+ "resolver": "adapter.groupByNamespace"
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@streamapp/telegram",
3
+ "version": "1.0.0",
4
+ "author": "stream",
5
+ "keywords": [
6
+ "stream-recipe"
7
+ ],
8
+ "stream": {
9
+ "type": "recipe",
10
+ "name": "Telegram",
11
+ "tagline": "在指定频道内搜关键词",
12
+ "facility": "telegram",
13
+ "schemaVersion": 1
14
+ }
15
+ }
@@ -0,0 +1,125 @@
1
+ {
2
+ "version": 7,
3
+ "kind": "desktop",
4
+ "sourceId": "telegram-search",
5
+ "app": {
6
+ "process": "Telegram.exe",
7
+ "windowClass": "MainWindow"
8
+ },
9
+ "steps": [
10
+ {
11
+ "kind": "invoke",
12
+ "query": {
13
+ "role": "Button",
14
+ "name": "取消搜索"
15
+ },
16
+ "optional": true
17
+ },
18
+ {
19
+ "kind": "wait",
20
+ "ms": 400
21
+ },
22
+ {
23
+ "kind": "invoke",
24
+ "query": {
25
+ "role": "ListItem",
26
+ "nameContains": "{channel}"
27
+ }
28
+ },
29
+ {
30
+ "kind": "wait",
31
+ "ms": 800
32
+ },
33
+ {
34
+ "kind": "invoke",
35
+ "query": {
36
+ "role": "Button",
37
+ "name": "搜索消息"
38
+ }
39
+ },
40
+ {
41
+ "kind": "type",
42
+ "text": "{q}",
43
+ "query": {
44
+ "role": "Edit",
45
+ "name": "搜索",
46
+ "className": "class Ui::InputField::Inner"
47
+ }
48
+ },
49
+ {
50
+ "kind": "wait",
51
+ "ms": 2000
52
+ },
53
+ {
54
+ "kind": "invoke",
55
+ "query": {
56
+ "role": "ListItem",
57
+ "nameContains": "频道, {channel}"
58
+ }
59
+ },
60
+ {
61
+ "kind": "wait",
62
+ "ms": 2500
63
+ }
64
+ ],
65
+ "observer": {
66
+ "itemQuery": {
67
+ "role": "ListItem",
68
+ "nameContains": "{q}"
69
+ },
70
+ "fields": {
71
+ "content": {
72
+ "read": "name"
73
+ }
74
+ },
75
+ "dedupeBy": "content"
76
+ },
77
+ "map": {
78
+ "content": {
79
+ "from": "content",
80
+ "match": "名称:[\\s\\S]+"
81
+ },
82
+ "title": {
83
+ "from": "content",
84
+ "match": "名称:(.+)"
85
+ },
86
+ "description": {
87
+ "from": "content",
88
+ "match": "描述:([\\s\\S]*?)(?:\\n\\n|$)"
89
+ },
90
+ "link": {
91
+ "from": "content",
92
+ "match": "https?://[^\\s]+"
93
+ }
94
+ },
95
+ "read": {
96
+ "dedupeBy": "link",
97
+ "targetCount": 30
98
+ },
99
+ "meta": {
100
+ "description": "在指定的 Telegram 频道里搜关键词:先复位(上一轮可能停在搜索模式)→ 从左栏会话列表按名字点进频道 → 频道内搜索 → 点第一条命中让主列表跳过去 → 从主列表读完整正文(搜索结果本身只有摘要,网盘链接被截断了)。桌面 recipe(a11y 词汇,Windows UIA 后端)。打字走 ValuePattern 写进输入框(不经键盘、不需要前台、锁屏也生效,已实测),所以 `type` 带 `query`——**必须指到里层的 `Ui::InputField::Inner`**,写外层容器 `Ui::InputField` 是收不下的。仍有一处会浮到前台:invoke「搜索消息」时 Telegram 自己 activate 窗口(不是 Stream 抢的)。实测见 2026-08-04-desktop-background-typing-design.md。",
101
+ "normalizer": "telegram",
102
+ "_why_member_timeout": "桌面采集不是网络请求:点开窗口、等界面渲染、逐步点进去,一趟约 12s。按流式路统一的 15s 量会贴着边被砍(而被砍的表现只是「这个源永远搜不到」,不报错)。按源申报 25s,只慢自己这一格。上限必须小于流式路的整体预算 DEFAULT_BUDGET_MS,否则预算先到,配多少都不生效。",
103
+ "member_timeout_ms": 25000,
104
+ "capabilities": [
105
+ "search"
106
+ ],
107
+ "key_param": "q",
108
+ "cadence_hint_seconds": 3600,
109
+ "discoverable": false,
110
+ "_why_pick_in": "只在 Provider 成员面挑得到,不在频道订阅面——按频道搜的一条腿",
111
+ "pick_in": ["provider"],
112
+ "params_schema": {
113
+ "q": {
114
+ "type": "string",
115
+ "required": true,
116
+ "description": "搜索词"
117
+ },
118
+ "channel": {
119
+ "type": "string",
120
+ "required": true,
121
+ "description": "要搜哪个频道(左栏会话列表里的名字,包含匹配即可)。必填:不指定就等于「搜你此刻恰好开着的那个会话」,而定时轮和聚合调用都指望不上这一点。"
122
+ }
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,11 @@
1
+
2
+ ## 说明(迁自 manifests.yaml 注释)
3
+
4
+ 腾讯视频(v.qq.com)— 纯 http recipe(pbaccess.video.qq.com)。
5
+ episode:GetPageData 走 page_id:vsite_episode_list,明文 JSON、无签名,UA/Origin 均非必需(不同于
6
+ 该站 search 端点——那个端点缺这两个头会被拒 20607,此处不需要)。page_size=100 通常一页拿全;
7
+ compute.decode 把 module_list_datas 摊平成 items[],并把 module_params.has_next(字符串
8
+ "true"/"false")转成真 boolean 喂 pagination.hasMore——不转的话原始字符串 "false" 在 JS 里是
9
+ truthy,increment 引擎 `!getPath(...)` 那条 hasMore 判断会被绕过去,永远不停。page_num 超出真实
10
+ 页数时服务器会绕回第 0 页而不是给空数组,所以必须靠这个 boolean 兜底停,不能只指望"空数组=
11
+ 自然结束"那条内置规则。
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@streamapp/tencent",
3
+ "version": "1.0.0",
4
+ "author": "stream",
5
+ "keywords": [
6
+ "stream-recipe"
7
+ ],
8
+ "stream": {
9
+ "type": "recipe",
10
+ "name": "腾讯视频",
11
+ "tagline": "剧集 / 综艺的分集列表",
12
+ "facility": "tencent",
13
+ "schemaVersion": 1
14
+ }
15
+ }
@@ -0,0 +1,98 @@
1
+ {
2
+ "version": 1,
3
+ "kind": "http",
4
+ "sourceId": "tencent-cn-episode",
5
+ "request": {
6
+ "url": "https://pbaccess.video.qq.com/trpc.universal_backend_service.page_server_rpc.PageServer/GetPageData?video_appid=3000010&vplatform=2&vversion_name=8.2.95",
7
+ "method": "POST",
8
+ "headers": {
9
+ "content-type": "application/json",
10
+ "referer": "https://v.qq.com/x/cover/{cid}.html"
11
+ },
12
+ "body": "{\"page_params\":{\"req_from\":\"web_vsite\",\"page_id\":\"vsite_episode_list\",\"page_type\":\"detail_operation\",\"id_type\":\"1\",\"page_size\":\"100\",\"cid\":\"{cid}\",\"vid\":\"\",\"lid\":\"\",\"page_num\":\"{page_num}\",\"page_context\":\"\",\"detail_page_type\":\"1\",\"req_type\":\"6\",\"req_from_second_type\":\"\",\"year\":\"\",\"tab_type\":\"\",\"chapter_name\":\"\",\"is_nocopyright\":\"false\"},\"has_cache\":1}"
13
+ },
14
+ "compute": {
15
+ "capabilities": [],
16
+ "prefetch": [
17
+ {
18
+ "as": "coverPage",
19
+ "parse": "text",
20
+ "request": {
21
+ "url": "https://m.v.qq.com/x/m/play?cid={cid}",
22
+ "method": "GET",
23
+ "headers": {
24
+ "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 MicroMessenger/8.0.0"
25
+ }
26
+ }
27
+ }
28
+ ],
29
+ "decode": "(() => { const mld = (input.body && input.body.data && input.body.data.module_list_datas) || []; const mod = mld[0] && mld[0].module_datas && mld[0].module_datas[0]; const items = (mod && mod.item_data_lists && mod.item_data_lists.item_datas) || []; const hasNext = !!(mod && mod.module_params && mod.module_params.has_next === 'true'); const m = /itemprop=\"image\" content=\"([^\"]*)\"/.exec((input.pre && input.pre.coverPage) || ''); const cover = m ? 'https:' + m[1] : undefined; const withCover = cover ? items.map((it) => Object.assign({}, it, { seriesCover: cover })) : items; return { items: withCover, hasNext }; })()"
30
+ },
31
+ "pagination": {
32
+ "mode": "increment",
33
+ "param": "page_num",
34
+ "start": 0,
35
+ "step": 1,
36
+ "itemsAt": "items",
37
+ "hasMore": "hasNext",
38
+ "maxPages": 5
39
+ },
40
+ "assert": [
41
+ {
42
+ "path": "items",
43
+ "desc": "tencent vsite_episode_list flattened"
44
+ }
45
+ ],
46
+ "mapping": {
47
+ "guid": "item_id",
48
+ "title": "item_params.title",
49
+ "link": "https://v.qq.com/x/cover/{item_params.cid}/{item_params.vid}.html",
50
+ "image": "item_params.image_url",
51
+ "author_avatar": "seriesCover",
52
+ "description": "item_params.video_subtitle",
53
+ "pubDate": "item_params.publish_date"
54
+ },
55
+ "meta": {
56
+ "normalizer": "rsshub",
57
+ "type": "post",
58
+ "description": "腾讯视频 — 剧集/综艺分集",
59
+ "topics": [
60
+ "腾讯视频",
61
+ "tencent",
62
+ "v.qq.com",
63
+ "影视",
64
+ "综艺",
65
+ "剧集",
66
+ "分集",
67
+ "tv"
68
+ ],
69
+ "categories": [
70
+ "multimedia"
71
+ ],
72
+ "facility": {
73
+ "key": "tencent",
74
+ "label": "腾讯视频"
75
+ },
76
+ "capabilities": [
77
+ "timeline"
78
+ ],
79
+ "key_param": "cid",
80
+ "priority": 80,
81
+ "discoverable": true,
82
+ "cadence_hint_seconds": 86400,
83
+ "auth": {
84
+ "type": "none"
85
+ },
86
+ "homepage": "v.qq.com",
87
+ "params_schema": {
88
+ "cid": {
89
+ "type": "string",
90
+ "required": true,
91
+ "description": "剧集/专辑 cid(取自 v.qq.com/x/cover/{cid}/... 页面 URL)"
92
+ }
93
+ },
94
+ "radar": [
95
+ "v.qq.com/x/cover/:cid"
96
+ ]
97
+ }
98
+ }
@@ -0,0 +1,38 @@
1
+ ## 说明
2
+
3
+ nextmusic.toubiec.cn — 另一个第三方网易云解析站(公开入口是 wyapi.toubiec.cn)。和 zuna 不同,
4
+ 它**不签名也不加密**:全是明文 JSON POST,所以这几份都是纯 http recipe,compute 只用来拼 body。
5
+
6
+ ## 三份 recipe
7
+
8
+ | recipe | 干什么 | 谁在用 |
9
+ |---|---|---|
10
+ | `toubiec-search` | 关键词 → 歌曲 | `music-search` 行的并发成员(与 `zuna-search` 并列) |
11
+ | `toubiec-download` | song id → 可播放/可下载地址 | `netease-track` 梯子首档(radar `music.163.com/song`) |
12
+ | `toubiec-playlist` | 歌单 id → 整份曲目名单 | radar `music.163.com/playlist` |
13
+
14
+ 搜索与歌单的 item link 回到 `music.163.com/song?id=`——文件地址在播放/下载那一刻由
15
+ `netease-track` 现解。
16
+
17
+ ## 每个请求都必须带 `ip`
18
+
19
+ 上游校验的是**调用方自报的公网 IP**:少了这个字段一律回 `400 当前非法提交参数`,空串回
20
+ `参数 ip 不能为空`。所以每份 recipe 都先 prefetch 它自己的 `/api/ip`,再由 `sign` 把值拼进 body
21
+ (它的前端就是这么做的)。
22
+
23
+ 实测边界(2026-08-25):`timestamp` 反过来**根本不校验**(传 `1` 也过),`ip` 只校验非空
24
+ (`0.0.0.0` 也收)——**没有签名可逆**,只是两个明文字段。
25
+
26
+ ## `toubiec-download` 的元数据来自网易云自己
27
+
28
+ `getSongUrl` 只回地址、码率、大小,不带歌名歌手专辑封面;而下游要拿这些写进音频文件的 ID3
29
+ 标签。所以这份 recipe 另外 prefetch 一次 `music.163.com/api/song/detail`(免鉴权,且它本来就是
30
+ 这些字段的权威出处),在 decode 里并进结果。
31
+
32
+ 不用上游的 `getSongInfo` 是有原因的:它同样要 `ip`,而 prefetch 之间取不到彼此的结果
33
+ (`http-fetch.ts` 的 prefetch 只吃 `params`),拿不到真实 IP 就只能编一个。
34
+
35
+ ## 专辑:上游自己坏了,没有对应 recipe
36
+
37
+ `getAlbum` 恒回 `code -462 Failed to fetch album`(四个不同专辑 id 全同),它前端的解析类型
38
+ 也只剩「单曲」一档。等它修好再补。
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@streamapp/toubiec",
3
+ "version": "1.0.0",
4
+ "author": "stream",
5
+ "keywords": [
6
+ "stream-recipe"
7
+ ],
8
+ "stream": {
9
+ "type": "recipe",
10
+ "name": "Toubiec",
11
+ "tagline": "搜歌 + 解析网易云直链",
12
+ "facility": "toubiec",
13
+ "schemaVersion": 1
14
+ }
15
+ }
@@ -0,0 +1,58 @@
1
+ {
2
+ "version": 1,
3
+ "kind": "http",
4
+ "sourceId": "toubiec-download",
5
+ "request": {
6
+ "url": "https://nextmusic.toubiec.cn/api/getSongUrl",
7
+ "method": "POST",
8
+ "headers": { "content-type": "application/json" }
9
+ },
10
+ "compute": {
11
+ "capabilities": [],
12
+ "prefetch": [
13
+ { "as": "ipResp", "request": { "url": "https://nextmusic.toubiec.cn/api/ip", "method": "GET" } },
14
+ { "as": "detail", "request": { "url": "https://music.163.com/api/song/detail?ids=%5B{id}%5D", "method": "GET" } }
15
+ ],
16
+ "sign": "(() => { const ip = (input.pre.ipResp && input.pre.ipResp.data && input.pre.ipResp.data.ip) || '0.0.0.0'; return { body: JSON.stringify({ id: String(input.params.id), level: input.params.level || 'lossless', ip, timestamp: input.now }) }; })()",
17
+ "decode": "(() => { const d = input.body && input.body.data; if (!d || !d.url) return { song: [] }; const s = ((input.pre.detail && input.pre.detail.songs) || [])[0] || {}; const ext = (String(d.url).split('?')[0].match(/\\.([a-z0-9]+)$/i) || [])[1] || 'mp3'; return { song: [{ url: d.url, format: ext, bitrate: d.br, size: d.size, level: d.level, name: s.name || String(input.params.id), artist: (s.artists || []).map((a) => a.name).filter(Boolean).join('/'), album: (s.album && s.album.name) || '', cover: (s.album && s.album.picUrl) || '' }] }; })()"
18
+ },
19
+ "pagination": { "mode": "increment", "param": "page", "start": 1, "step": 1, "itemsAt": "song", "maxPages": 1 },
20
+ "assert": [{ "path": "song", "desc": "toubiec getSongUrl wrapped as a one-element array (empty = 这首解不出来,让梯子下一档去试)" }],
21
+ "mapping": {
22
+ "guid": "url",
23
+ "title": "name",
24
+ "author": "artist",
25
+ "album": "album",
26
+ "link": "https://music.163.com/song?id={id}",
27
+ "enclosure_url": "url",
28
+ "enclosure_type": "format",
29
+ "format": "format",
30
+ "image": "cover"
31
+ },
32
+ "meta": {
33
+ "normalizer": "rsshub",
34
+ "type": "post",
35
+ "title": "单曲下载",
36
+ "description": "toubiec 网易云解析 — 单曲下载(song id → 可播放/可下载地址)",
37
+ "topics": ["toubiec", "网易云", "音乐", "下载", "无损", "music", "netease"],
38
+ "categories": ["music"],
39
+ "facility": { "key": "toubiec", "label": "toubiec" },
40
+ "capabilities": ["timeline"],
41
+ "key_param": "id",
42
+ "priority": 80,
43
+ "discoverable": true,
44
+ "cadence_hint_seconds": 3600,
45
+ "auth": { "type": "none" },
46
+ "homepage": "wyapi.toubiec.cn",
47
+ "params_schema": {
48
+ "id": { "type": "string", "required": true, "description": "网易云歌曲 ID" },
49
+ "level": {
50
+ "type": "string",
51
+ "required": false,
52
+ "default": "lossless",
53
+ "description": "音质:standard/exhigh/lossless/hires/jyeffect/sky/jymaster(上游给不出时自动降档)"
54
+ }
55
+ },
56
+ "radar": ["music.163.com/song"]
57
+ }
58
+ }