@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.
- package/README.md +24 -0
- package/bin/stream.mjs +89 -0
- package/package.json +34 -0
- package/resources/app/dist-panel/panel-boards.js +6 -0
- package/resources/app/dist-panel/panel-detail.css +1 -0
- package/resources/app/dist-panel/panel-detail.js +2141 -0
- package/resources/app/dist-panel/panel-manage.css +1 -0
- package/resources/app/dist-panel/panel-manage.js +2246 -0
- package/resources/app/dist-panel/panel-movie.css +1 -0
- package/resources/app/dist-panel/panel-movie.js +2268 -0
- package/resources/app/dist-panel/panel-research.css +1 -0
- package/resources/app/dist-panel/panel-research.js +316 -0
- package/resources/app/dist-panel/panel.css +1 -0
- package/resources/app/dist-panel/panel.js +463 -0
- package/resources/dsh-engine-lock/package-lock.json +9032 -0
- package/resources/extension/.output/chrome-mv3/assets/popup-C2NUZZQW.css +2 -0
- package/resources/extension/.output/chrome-mv3/background.js +1 -0
- package/resources/extension/.output/chrome-mv3/chunks/popup-BeBts9v2.js +50 -0
- package/resources/extension/.output/chrome-mv3/icon/128.png +0 -0
- package/resources/extension/.output/chrome-mv3/icon/16.png +0 -0
- package/resources/extension/.output/chrome-mv3/icon/32.png +0 -0
- package/resources/extension/.output/chrome-mv3/icon/48.png +0 -0
- package/resources/extension/.output/chrome-mv3/icon/96.png +0 -0
- package/resources/extension/.output/chrome-mv3/manifest.json +1 -0
- package/resources/extension/.output/chrome-mv3/popup.html +21 -0
- package/resources/packages/1lou/1lou-search.recipe.json +102 -0
- package/resources/packages/1lou/README.md +7 -0
- package/resources/packages/1lou/package.json +15 -0
- package/resources/packages/Douyin_TikTok_Download_API/README.md +23 -0
- package/resources/packages/Douyin_TikTok_Download_API/activate.ts +14 -0
- package/resources/packages/Douyin_TikTok_Download_API/adapter/adapter.ts +321 -0
- package/resources/packages/Douyin_TikTok_Download_API/adapter/danmaku.ts +21 -0
- package/resources/packages/Douyin_TikTok_Download_API/adapter/executor.ts +75 -0
- package/resources/packages/Douyin_TikTok_Download_API/adapter/normalize.ts +20 -0
- package/resources/packages/Douyin_TikTok_Download_API/manifests.yaml +521 -0
- package/resources/packages/Douyin_TikTok_Download_API/package.json +32 -0
- package/resources/packages/alist/README.md +14 -0
- package/resources/packages/alist/activate.ts +12 -0
- package/resources/packages/alist/adapter.ts +74 -0
- package/resources/packages/alist/manifests.yaml +76 -0
- package/resources/packages/alist/mounts.ts +120 -0
- package/resources/packages/alist/package.json +24 -0
- package/resources/packages/alist/presets.ts +53 -0
- package/resources/packages/alist/provision.ts +84 -0
- package/resources/packages/baidu/baidu-share.recipe.json +93 -0
- package/resources/packages/baidu/package.json +16 -0
- package/resources/packages/baidu-search/baidu-search.recipe.json +129 -0
- package/resources/packages/baidu-search/package.json +25 -0
- package/resources/packages/brave/brave-search.recipe.json +129 -0
- package/resources/packages/brave/package.json +25 -0
- package/resources/packages/browser/README.md +3 -0
- package/resources/packages/browser/adapter.ts +44 -0
- package/resources/packages/browser/manifests.yaml +20 -0
- package/resources/packages/browser/package.json +13 -0
- package/resources/packages/bt0/README.md +7 -0
- package/resources/packages/bt0/bt0-search.recipe.json +69 -0
- package/resources/packages/bt0/bt0-tlist.recipe.json +74 -0
- package/resources/packages/bt0/package.json +15 -0
- package/resources/packages/btbtla/README.md +6 -0
- package/resources/packages/btbtla/btbtla-detail.recipe.json +76 -0
- package/resources/packages/btbtla/btbtla-search.recipe.json +74 -0
- package/resources/packages/btbtla/package.json +15 -0
- package/resources/packages/builtin/README.md +8 -0
- package/resources/packages/builtin/manifests.yaml +487 -0
- package/resources/packages/builtin/package.json +14 -0
- package/resources/packages/douyin/README.md +39 -0
- package/resources/packages/douyin/douyin-collection.recipe.json +127 -0
- package/resources/packages/douyin/douyin-search.recipe.json +163 -0
- package/resources/packages/douyin/package.json +16 -0
- package/resources/packages/firecrawl/firecrawl-create-key.recipe.json +81 -0
- package/resources/packages/firecrawl/firecrawl-read-key.recipe.json +56 -0
- package/resources/packages/firecrawl/package.json +16 -0
- package/resources/packages/google/google-search.recipe.json +127 -0
- package/resources/packages/google/package.json +24 -0
- package/resources/packages/groq/groq-create-key.recipe.json +67 -0
- package/resources/packages/groq/package.json +16 -0
- package/resources/packages/huggingface/README.md +7 -0
- package/resources/packages/huggingface/huggingface-spaces.recipe.json +73 -0
- package/resources/packages/huggingface/package.json +15 -0
- package/resources/packages/imdb/imdb-chart.recipe.json +75 -0
- package/resources/packages/imdb/package.json +15 -0
- package/resources/packages/index.ts +15 -0
- package/resources/packages/iqiyi/README.md +10 -0
- package/resources/packages/iqiyi/iqiyi-cn-album.recipe.json +80 -0
- package/resources/packages/iqiyi/iqiyi-cn-search.recipe.json +71 -0
- package/resources/packages/iqiyi/package.json +15 -0
- package/resources/packages/lizhi/README.md +35 -0
- package/resources/packages/lizhi/lizhi-user.recipe.json +80 -0
- package/resources/packages/lizhi/package.json +15 -0
- package/resources/packages/manmanbuy/README.md +16 -0
- package/resources/packages/manmanbuy/manmanbuy-search.recipe.json +115 -0
- package/resources/packages/manmanbuy/package.json +15 -0
- package/resources/packages/mineru/README.md +38 -0
- package/resources/packages/mineru/package.json +28 -0
- package/resources/packages/pansou/README.md +14 -0
- package/resources/packages/pansou/activate.ts +11 -0
- package/resources/packages/pansou/adapter.ts +94 -0
- package/resources/packages/pansou/manifests.yaml +50 -0
- package/resources/packages/pansou/package.json +27 -0
- package/resources/packages/qq/package.json +15 -0
- package/resources/packages/qq/qq-send.recipe.json +104 -0
- package/resources/packages/quark/package.json +16 -0
- package/resources/packages/quark/quark-share.recipe.json +40 -0
- package/resources/packages/reddit/package.json +15 -0
- package/resources/packages/reddit/reddit-sub.recipe.json +77 -0
- package/resources/packages/replay/README.md +7 -0
- package/resources/packages/replay/package.json +13 -0
- package/resources/packages/rsshub/README.md +16 -0
- package/resources/packages/rsshub/manifests.yaml +107 -0
- package/resources/packages/rsshub/package.json +21 -0
- package/resources/packages/telegram/package.json +15 -0
- package/resources/packages/telegram/telegram-search.recipe.json +125 -0
- package/resources/packages/tencent/README.md +11 -0
- package/resources/packages/tencent/package.json +15 -0
- package/resources/packages/tencent/tencent-cn-episode.recipe.json +98 -0
- package/resources/packages/toubiec/README.md +38 -0
- package/resources/packages/toubiec/package.json +15 -0
- package/resources/packages/toubiec/toubiec-download.recipe.json +58 -0
- package/resources/packages/toubiec/toubiec-playlist.recipe.json +49 -0
- package/resources/packages/toubiec/toubiec-search.recipe.json +80 -0
- package/resources/packages/voiceprint/README.md +31 -0
- package/resources/packages/voiceprint/package.json +36 -0
- package/resources/packages/wikipedia/package.json +15 -0
- package/resources/packages/wikipedia/wikipedia-award-list.recipe.json +102 -0
- package/resources/packages/xhs/README.md +32 -0
- package/resources/packages/xhs/package.json +21 -0
- package/resources/packages/xhs/xhs-detail.recipe.json +129 -0
- package/resources/packages/xhs/xhs-home.recipe.json +144 -0
- package/resources/packages/xhs/xhs-like.recipe.json +96 -0
- package/resources/packages/xhs/xhs-search.recipe.json +138 -0
- package/resources/packages/xueqiu/README.md +9 -0
- package/resources/packages/xueqiu/package.json +16 -0
- package/resources/packages/xueqiu/xueqiu-detail.recipe.json +92 -0
- package/resources/packages/xueqiu/xueqiu-user.recipe.json +75 -0
- package/resources/packages/zhipu/package.json +16 -0
- package/resources/packages/zhipu/zhipu-create-key.recipe.json +74 -0
- package/resources/packages/zuna/README.md +31 -0
- package/resources/packages/zuna/package.json +15 -0
- package/resources/packages/zuna/zuna-album.recipe.json +46 -0
- package/resources/packages/zuna/zuna-download.recipe.json +53 -0
- package/resources/packages/zuna/zuna-playlist.recipe.json +46 -0
- package/resources/packages/zuna/zuna-search.recipe.json +96 -0
- package/resources/server.mjs +238876 -0
- 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,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
|
+
}
|