pixivflow 2.16.1 → 2.18.0
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_EN.md → README.en.md} +8 -6
- package/README.md +8 -6
- package/config/examples/README.md +218 -0
- package/config/examples/multi-tag-or-limit.zh.json +49 -0
- package/config/examples/specific-download.example.json +173 -0
- package/config/examples/standalone.config.example.json +167 -0
- package/config/examples/standalone.config.novel-chinese.json +124 -0
- package/config/examples/standalone.config.ranking.json +135 -0
- package/config/examples/standalone.config.simple.json +52 -0
- package/config/examples/yesterday-popular-novel.zh.json +48 -0
- package/config/examples/yesterday-ranking-illustration.json +43 -0
- package/dist/auth/PixivAuth.d.ts +12 -0
- package/dist/auth/PixivAuth.d.ts.map +1 -1
- package/dist/auth/PixivAuth.js +26 -0
- package/dist/auth/PixivAuth.js.map +1 -1
- package/dist/batch/executionResult.d.ts +85 -0
- package/dist/batch/executionResult.d.ts.map +1 -0
- package/dist/batch/executionResult.js +102 -0
- package/dist/batch/executionResult.js.map +1 -0
- package/dist/batch/runDiagnostics.d.ts +48 -0
- package/dist/batch/runDiagnostics.d.ts.map +1 -0
- package/dist/batch/runDiagnostics.js +103 -0
- package/dist/batch/runDiagnostics.js.map +1 -0
- package/dist/commands/Command.d.ts +5 -0
- package/dist/commands/Command.d.ts.map +1 -1
- package/dist/commands/Command.js +7 -0
- package/dist/commands/Command.js.map +1 -1
- package/dist/commands/DiagnoseEgressCommand.d.ts +98 -0
- package/dist/commands/DiagnoseEgressCommand.d.ts.map +1 -0
- package/dist/commands/DiagnoseEgressCommand.js +327 -0
- package/dist/commands/DiagnoseEgressCommand.js.map +1 -0
- package/dist/commands/ExecuteSlotCommand.d.ts +37 -0
- package/dist/commands/ExecuteSlotCommand.d.ts.map +1 -0
- package/dist/commands/ExecuteSlotCommand.js +267 -0
- package/dist/commands/ExecuteSlotCommand.js.map +1 -0
- package/dist/commands/MaintainCommand.d.ts.map +1 -1
- package/dist/commands/MaintainCommand.js +10 -15
- package/dist/commands/MaintainCommand.js.map +1 -1
- package/dist/commands/index.d.ts +3 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +7 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/scheduler-runtime.d.ts +37 -1
- package/dist/commands/scheduler-runtime.d.ts.map +1 -1
- package/dist/commands/scheduler-runtime.js +52 -19
- package/dist/commands/scheduler-runtime.js.map +1 -1
- package/dist/commands/types.d.ts +6 -0
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/config/types.d.ts +33 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/validation.d.ts +14 -1
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +109 -33
- package/dist/config/validation.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/delivery/DeliveryDispatcher.d.ts.map +1 -1
- package/dist/delivery/DeliveryDispatcher.js +7 -0
- package/dist/delivery/DeliveryDispatcher.js.map +1 -1
- package/dist/delivery/HttpMultipartDelivery.d.ts +12 -0
- package/dist/delivery/HttpMultipartDelivery.d.ts.map +1 -1
- package/dist/delivery/HttpMultipartDelivery.js +89 -70
- package/dist/delivery/HttpMultipartDelivery.js.map +1 -1
- package/dist/delivery/LegacyOutboxMigration.js +1 -1
- package/dist/delivery/LegacyOutboxMigration.js.map +1 -1
- package/dist/delivery/TelegramReviewDelivery.d.ts +49 -0
- package/dist/delivery/TelegramReviewDelivery.d.ts.map +1 -0
- package/dist/delivery/TelegramReviewDelivery.js +307 -0
- package/dist/delivery/TelegramReviewDelivery.js.map +1 -0
- package/dist/download/DownloadManager.d.ts +10 -0
- package/dist/download/DownloadManager.d.ts.map +1 -1
- package/dist/download/DownloadManager.js +21 -0
- package/dist/download/DownloadManager.js.map +1 -1
- package/dist/download/plan/DownloadPlanner.d.ts +10 -0
- package/dist/download/plan/DownloadPlanner.d.ts.map +1 -1
- package/dist/download/plan/DownloadPlanner.js +19 -0
- package/dist/download/plan/DownloadPlanner.js.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/notification/NotificationPolicy.d.ts.map +1 -1
- package/dist/notification/NotificationPolicy.js +4 -1
- package/dist/notification/NotificationPolicy.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/pixiv-token-getter-adapter.d.ts +27 -7
- package/dist/pixiv-token-getter-adapter.d.ts.map +1 -1
- package/dist/pixiv-token-getter-adapter.js +44 -27
- package/dist/pixiv-token-getter-adapter.js.map +1 -1
- package/dist/puppeteer-login-adapter/auth-code.d.ts +1 -1
- package/dist/puppeteer-login-adapter/auth-code.d.ts.map +1 -1
- package/dist/puppeteer-login-adapter/browser-launch.d.ts +21 -0
- package/dist/puppeteer-login-adapter/browser-launch.d.ts.map +1 -0
- package/dist/puppeteer-login-adapter/browser-launch.js +46 -0
- package/dist/puppeteer-login-adapter/browser-launch.js.map +1 -0
- package/dist/puppeteer-login-adapter/environment.js +2 -2
- package/dist/puppeteer-login-adapter/environment.js.map +1 -1
- package/dist/puppeteer-login-adapter/login-headless.d.ts.map +1 -1
- package/dist/puppeteer-login-adapter/login-headless.js +2 -5
- package/dist/puppeteer-login-adapter/login-headless.js.map +1 -1
- package/dist/puppeteer-login-adapter/login-interactive.d.ts.map +1 -1
- package/dist/puppeteer-login-adapter/login-interactive.js +2 -5
- package/dist/puppeteer-login-adapter/login-interactive.js.map +1 -1
- package/dist/storage/Database.d.ts +2 -0
- package/dist/storage/Database.d.ts.map +1 -1
- package/dist/storage/Database.js +7 -6
- package/dist/storage/Database.js.map +1 -1
- package/dist/storage/DatabaseMigration.d.ts +2 -2
- package/dist/storage/DatabaseMigration.d.ts.map +1 -1
- package/dist/storage/DatabaseMigration.js.map +1 -1
- package/dist/storage/drivers/NodeSqliteDriver.d.ts +14 -0
- package/dist/storage/drivers/NodeSqliteDriver.d.ts.map +1 -0
- package/dist/storage/drivers/NodeSqliteDriver.js +123 -0
- package/dist/storage/drivers/NodeSqliteDriver.js.map +1 -0
- package/dist/storage/drivers/SqliteDriver.d.ts +55 -0
- package/dist/storage/drivers/SqliteDriver.d.ts.map +1 -0
- package/dist/storage/drivers/SqliteDriver.js +21 -0
- package/dist/storage/drivers/SqliteDriver.js.map +1 -0
- package/dist/storage/repositories/BaseRepository.d.ts +4 -4
- package/dist/storage/repositories/BaseRepository.d.ts.map +1 -1
- package/dist/storage/repositories/BaseRepository.js.map +1 -1
- package/dist/storage/repositories/DownloadRepository.d.ts +2 -1
- package/dist/storage/repositories/DownloadRepository.d.ts.map +1 -1
- package/dist/storage/repositories/DownloadRepository.js.map +1 -1
- package/dist/storage/repositories/RateLimitStateRepository.d.ts +1 -1
- package/dist/storage/repositories/RateLimitStateRepository.js +1 -1
- package/dist/utils/concurrency.d.ts.map +1 -1
- package/dist/utils/concurrency.js +9 -2
- package/dist/utils/concurrency.js.map +1 -1
- package/dist/utils/config-validator-unified.d.ts.map +1 -1
- package/dist/utils/config-validator-unified.js +15 -1
- package/dist/utils/config-validator-unified.js.map +1 -1
- package/dist/utils/token-maintenance.d.ts +0 -4
- package/dist/utils/token-maintenance.d.ts.map +1 -1
- package/dist/utils/token-maintenance.js +5 -25
- package/dist/utils/token-maintenance.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/webui/package.json +1 -1
- package/node_modules/@redtidev/pixiv-client/README.en.md +105 -0
- package/node_modules/@redtidev/pixiv-client/README.md +67 -73
- package/node_modules/@redtidev/pixiv-client/dist/rate-limit/RateLimitGate.d.ts +1 -1
- package/node_modules/@redtidev/pixiv-client/dist/rate-limit/RateLimitGate.js +1 -1
- package/node_modules/@redtidev/pixiv-client/src/rate-limit/RateLimitGate.ts +1 -1
- package/package.json +7 -8
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "PixivFlow 完整配置示例 - 包含所有可用选项和详细说明",
|
|
3
|
+
"_usage": "使用方法:复制此文件为 standalone.config.json 并修改 refreshToken",
|
|
4
|
+
"_note": "所有以 _ 开头的字段都是注释说明,不会影响程序运行",
|
|
5
|
+
|
|
6
|
+
"logLevel": "info",
|
|
7
|
+
"_logLevel_note": "日志级别: debug(详细)| info(信息)| warn(警告)| error(错误)",
|
|
8
|
+
|
|
9
|
+
"pixiv": {
|
|
10
|
+
"_comment": "Pixiv API 认证配置(必需)",
|
|
11
|
+
"clientId": "MOBrBDS8blbauoSck0ZfDbtuzpyT",
|
|
12
|
+
"clientSecret": "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj",
|
|
13
|
+
"deviceToken": "pixiv",
|
|
14
|
+
"refreshToken": "YOUR_REFRESH_TOKEN",
|
|
15
|
+
"_refreshToken_note": "重要:请将 YOUR_REFRESH_TOKEN 替换为您的实际 refresh token。运行 pixivflow login 可自动获取",
|
|
16
|
+
"userAgent": "PixivAndroidApp/5.0.234 (Android 11; Pixel 6)"
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
"network": {
|
|
20
|
+
"_comment": "网络配置(可选,有默认值)",
|
|
21
|
+
"timeoutMs": 30000,
|
|
22
|
+
"_timeoutMs_note": "请求超时时间(毫秒),默认 30000(30秒)",
|
|
23
|
+
"retries": 3,
|
|
24
|
+
"_retries_note": "失败重试次数,默认 3",
|
|
25
|
+
"retryDelay": 1000,
|
|
26
|
+
"_retryDelay_note": "重试延迟(毫秒),默认 1000(1秒)",
|
|
27
|
+
"proxy": {
|
|
28
|
+
"_comment": "代理配置(可选,如需使用代理请启用)",
|
|
29
|
+
"enabled": false,
|
|
30
|
+
"host": "127.0.0.1",
|
|
31
|
+
"port": 7890,
|
|
32
|
+
"protocol": "http",
|
|
33
|
+
"_protocol_note": "支持: http | https | socks4 | socks5",
|
|
34
|
+
"username": "",
|
|
35
|
+
"password": ""
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
"storage": {
|
|
40
|
+
"_comment": "存储配置(可选,有默认值)",
|
|
41
|
+
"databasePath": "./data/pixiv-downloader.db",
|
|
42
|
+
"_databasePath_note": "SQLite 数据库文件路径,用于记录下载历史",
|
|
43
|
+
"downloadDirectory": "./downloads",
|
|
44
|
+
"_downloadDirectory_note": "下载根目录",
|
|
45
|
+
"illustrationDirectory": "./downloads/illustrations",
|
|
46
|
+
"_illustrationDirectory_note": "插画保存目录(相对于 downloadDirectory 或绝对路径)",
|
|
47
|
+
"novelDirectory": "./downloads/novels",
|
|
48
|
+
"_novelDirectory_note": "小说保存目录(相对于 downloadDirectory 或绝对路径)",
|
|
49
|
+
"illustrationOrganization": "byDownloadDate",
|
|
50
|
+
"_illustrationOrganization_note": "插画目录组织方式: flat(扁平)| byAuthor(按作者)| byTag(按标签)| byDate(按作品创建日期-月份)| byDay(按作品创建日期-天)| byDownloadDate(按下载日期-月份,推荐)| byDownloadDay(按下载日期-天)| byAuthorAndTag(按作者和标签)| byDateAndAuthor(按作品创建日期和作者)| byDownloadDateAndAuthor(按下载日期和作者)",
|
|
51
|
+
"novelOrganization": "byDownloadDate",
|
|
52
|
+
"_novelOrganization_note": "小说目录组织方式,选项同 illustrationOrganization"
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
"scheduler": {
|
|
56
|
+
"_comment": "定时任务配置(可选)",
|
|
57
|
+
"enabled": false,
|
|
58
|
+
"_enabled_note": "是否启用定时任务,默认 false",
|
|
59
|
+
"cron": "0 3 * * *",
|
|
60
|
+
"_cron_note": "Cron 表达式,默认每天凌晨3点执行。示例:'0 */6 * * *' 表示每6小时执行一次",
|
|
61
|
+
"timezone": "Asia/Shanghai",
|
|
62
|
+
"_timezone_note": "时区,默认 Asia/Shanghai",
|
|
63
|
+
"maxExecutions": null,
|
|
64
|
+
"_maxExecutions_note": "最大执行次数,null 表示无限制",
|
|
65
|
+
"minInterval": null,
|
|
66
|
+
"_minInterval_note": "最小执行间隔(毫秒),null 表示无限制",
|
|
67
|
+
"timeout": null,
|
|
68
|
+
"_timeout_note": "单次任务超时时间(毫秒),null 表示无超时",
|
|
69
|
+
"maxConsecutiveFailures": null,
|
|
70
|
+
"_maxConsecutiveFailures_note": "最大连续失败次数,超过后停止调度器,null 表示无限制",
|
|
71
|
+
"failureRetryDelay": null,
|
|
72
|
+
"_failureRetryDelay_note": "失败后重试延迟(毫秒),null 表示无延迟"
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
"download": {
|
|
76
|
+
"_comment": "下载配置(可选,有默认值)",
|
|
77
|
+
"concurrency": 3,
|
|
78
|
+
"_concurrency_note": "最大并发下载数,默认 3,建议范围 1-10。数值越大下载越快,但可能触发速率限制",
|
|
79
|
+
"requestDelay": 500,
|
|
80
|
+
"_requestDelay_note": "API 请求之间的最小延迟(毫秒),用于避免速率限制,默认 500(0.5秒)",
|
|
81
|
+
"dynamicConcurrency": true,
|
|
82
|
+
"_dynamicConcurrency_note": "是否启用动态并发调整。当检测到速率限制(429错误)时,自动降低并发数,默认 true(推荐)",
|
|
83
|
+
"minConcurrency": 1,
|
|
84
|
+
"_minConcurrency_note": "动态调整时的最小并发数,不会低于此值,默认 1",
|
|
85
|
+
"maxRetries": 3,
|
|
86
|
+
"_maxRetries_note": "每个下载的最大重试次数,默认 3",
|
|
87
|
+
"retryDelay": 2000,
|
|
88
|
+
"_retryDelay_note": "重试延迟(毫秒),默认 2000(2秒)",
|
|
89
|
+
"timeout": 60000,
|
|
90
|
+
"_timeout_note": "下载超时时间(毫秒),默认 60000(60秒)"
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
"targets": [
|
|
94
|
+
{
|
|
95
|
+
"_comment": "下载目标配置(必需,至少一个)",
|
|
96
|
+
"_example1": "示例1: 搜索模式 - 下载包含指定标签的插画",
|
|
97
|
+
"type": "illustration",
|
|
98
|
+
"_type_note": "类型: illustration(插画)| novel(小说)",
|
|
99
|
+
"tag": "風景",
|
|
100
|
+
"_tag_note": "搜索标签,必填。多个标签用空格分隔,如:'原神 風景'",
|
|
101
|
+
"tagRelation": "and",
|
|
102
|
+
"_tagRelation_note": "标签关系: and(默认,必须同时包含所有标签)| or(包含任意一个标签即可)",
|
|
103
|
+
"limit": 20,
|
|
104
|
+
"_limit_note": "每次运行下载数量限制,建议范围 1-1000",
|
|
105
|
+
"searchTarget": "partial_match_for_tags",
|
|
106
|
+
"_searchTarget_note": "搜索模式: partial_match_for_tags(部分匹配,推荐)| exact_match_for_tags(精确匹配)| title_and_caption(标题和说明)",
|
|
107
|
+
"sort": "date_desc",
|
|
108
|
+
"_sort_note": "排序方式: date_desc(按日期降序,最新优先)| date_asc(按日期升序)| popular_desc(按收藏数降序,最热门优先)",
|
|
109
|
+
"restrict": "public",
|
|
110
|
+
"_restrict_note": "限制: public(公开)| private(私有)",
|
|
111
|
+
"mode": "search",
|
|
112
|
+
"_mode_note": "下载模式: search(搜索,默认)| ranking(排行榜)",
|
|
113
|
+
"minBookmarks": 100,
|
|
114
|
+
"_minBookmarks_note": "可选,最小收藏数过滤,只下载收藏数大于等于此值的作品",
|
|
115
|
+
"startDate": null,
|
|
116
|
+
"_startDate_note": "可选,开始日期过滤(YYYY-MM-DD 格式),只下载此日期之后的作品",
|
|
117
|
+
"endDate": null,
|
|
118
|
+
"_endDate_note": "可选,结束日期过滤(YYYY-MM-DD 格式),只下载此日期之前的作品",
|
|
119
|
+
"random": false,
|
|
120
|
+
"_random_note": "是否随机选择,true 表示从搜索结果中随机选择"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"_example2": "示例2: 排行榜模式 - 下载今日排行榜作品",
|
|
124
|
+
"type": "illustration",
|
|
125
|
+
"tag": "ranking",
|
|
126
|
+
"limit": 50,
|
|
127
|
+
"mode": "ranking",
|
|
128
|
+
"rankingMode": "day",
|
|
129
|
+
"_rankingMode_note": "排行榜模式: day(日榜)| week(周榜)| month(月榜)| day_male(男性向日榜)| day_female(女性向日榜)| day_ai(AI作品日榜)| week_original(原创周榜)| week_rookie(新人周榜)| day_r18(R18日榜)| day_male_r18(男性向R18日榜)| day_female_r18(女性向R18日榜)",
|
|
130
|
+
"rankingDate": null,
|
|
131
|
+
"_rankingDate_note": "排行榜日期(YYYY-MM-DD 格式),null 表示今天,也可使用 'YESTERDAY' 占位符",
|
|
132
|
+
"filterTag": null,
|
|
133
|
+
"_filterTag_note": "过滤标签(可选),从排名结果中筛选包含此标签的作品,null 表示不过滤"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"_example3": "示例3: 小说下载 - 下载指定标签的小说",
|
|
137
|
+
"type": "novel",
|
|
138
|
+
"tag": "オリジナル",
|
|
139
|
+
"limit": 10,
|
|
140
|
+
"searchTarget": "partial_match_for_tags",
|
|
141
|
+
"mode": "search",
|
|
142
|
+
"languageFilter": null,
|
|
143
|
+
"_languageFilter_note": "语言过滤: chinese(仅中文)| non-chinese(非中文)| null(不过滤)",
|
|
144
|
+
"detectLanguage": true,
|
|
145
|
+
"_detectLanguage_note": "是否启用语言检测和记录,默认 true"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"_example4": "示例4: 下载指定小说系列",
|
|
149
|
+
"type": "novel",
|
|
150
|
+
"tag": "series",
|
|
151
|
+
"seriesId": 14690617,
|
|
152
|
+
"_seriesId_note": "小说系列ID,从URL https://www.pixiv.net/novel/series/14690617 中获取",
|
|
153
|
+
"limit": null,
|
|
154
|
+
"_limit_note": "系列下载时 limit 通常设为 null(下载全部)"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"_example5": "示例5: 下载单个小说",
|
|
158
|
+
"type": "novel",
|
|
159
|
+
"tag": "single",
|
|
160
|
+
"novelId": 26132156,
|
|
161
|
+
"_novelId_note": "小说ID,从URL https://www.pixiv.net/novel/show.php?id=26132156 中获取"
|
|
162
|
+
}
|
|
163
|
+
],
|
|
164
|
+
|
|
165
|
+
"initialDelay": 0,
|
|
166
|
+
"_initialDelay_note": "启动延迟(毫秒),用于测试或延迟启动,默认 0"
|
|
167
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "PixivFlow 中文小说下载配置示例",
|
|
3
|
+
"_usage": "使用方法:",
|
|
4
|
+
"_usage1": " 1. 复制此文件: cp config/standalone.config.novel-chinese.json config/standalone.config.json",
|
|
5
|
+
"_usage2": " 2. 修改 refreshToken(运行 pixivflow login 获取)",
|
|
6
|
+
"_usage3": " 3. 根据需要修改 targets 数组中的标签",
|
|
7
|
+
"_usage4": " 4. 运行下载: pixivflow download",
|
|
8
|
+
|
|
9
|
+
"logLevel": "info",
|
|
10
|
+
"pixiv": {
|
|
11
|
+
"clientId": "MOBrBDS8blbauoSck0ZfDbtuzpyT",
|
|
12
|
+
"clientSecret": "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj",
|
|
13
|
+
"deviceToken": "pixiv",
|
|
14
|
+
"refreshToken": "YOUR_REFRESH_TOKEN",
|
|
15
|
+
"userAgent": "PixivAndroidApp/5.0.234 (Android 11; Pixel 6)"
|
|
16
|
+
},
|
|
17
|
+
"network": {
|
|
18
|
+
"timeoutMs": 30000,
|
|
19
|
+
"retries": 3,
|
|
20
|
+
"retryDelay": 1000,
|
|
21
|
+
"proxy": {
|
|
22
|
+
"enabled": false,
|
|
23
|
+
"host": "127.0.0.1",
|
|
24
|
+
"port": 7890,
|
|
25
|
+
"protocol": "http",
|
|
26
|
+
"username": "",
|
|
27
|
+
"password": ""
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"storage": {
|
|
31
|
+
"databasePath": "./data/pixiv-downloader.db",
|
|
32
|
+
"downloadDirectory": "./downloads",
|
|
33
|
+
"illustrationDirectory": "./downloads/illustrations",
|
|
34
|
+
"novelDirectory": "./downloads/novels",
|
|
35
|
+
"illustrationOrganization": "byDownloadDate",
|
|
36
|
+
"novelOrganization": "byDownloadDate"
|
|
37
|
+
},
|
|
38
|
+
"scheduler": {
|
|
39
|
+
"enabled": false
|
|
40
|
+
},
|
|
41
|
+
"download": {
|
|
42
|
+
"concurrency": 2,
|
|
43
|
+
"requestDelay": 1000,
|
|
44
|
+
"dynamicConcurrency": true,
|
|
45
|
+
"minConcurrency": 1,
|
|
46
|
+
"maxRetries": 3,
|
|
47
|
+
"retryDelay": 2000,
|
|
48
|
+
"timeout": 60000
|
|
49
|
+
},
|
|
50
|
+
"targets": [
|
|
51
|
+
{
|
|
52
|
+
"_comment": "示例1: 下载包含'オリジナル'标签的中文小说,按热门程度排序",
|
|
53
|
+
"type": "novel",
|
|
54
|
+
"tag": "オリジナル",
|
|
55
|
+
"limit": 20,
|
|
56
|
+
"mode": "search",
|
|
57
|
+
"searchTarget": "partial_match_for_tags",
|
|
58
|
+
"sort": "popular_desc",
|
|
59
|
+
"languageFilter": "chinese",
|
|
60
|
+
"detectLanguage": true
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"_comment": "示例2: 下载最近7天发布的中文小说(使用时间范围过滤)",
|
|
64
|
+
"type": "novel",
|
|
65
|
+
"tag": "オリジナル",
|
|
66
|
+
"limit": 30,
|
|
67
|
+
"mode": "search",
|
|
68
|
+
"searchTarget": "partial_match_for_tags",
|
|
69
|
+
"sort": "date_desc",
|
|
70
|
+
"startDate": "LAST_7_DAYS",
|
|
71
|
+
"_startDate_note": "使用 'LAST_7_DAYS' 占位符表示最近7天,程序会自动计算日期",
|
|
72
|
+
"endDate": "YESTERDAY",
|
|
73
|
+
"_endDate_note": "使用 'YESTERDAY' 占位符表示昨天",
|
|
74
|
+
"languageFilter": "chinese",
|
|
75
|
+
"detectLanguage": true
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"_comment": "示例3: 下载指定日期范围内的中文小说",
|
|
79
|
+
"type": "novel",
|
|
80
|
+
"tag": "オリジナル",
|
|
81
|
+
"limit": 50,
|
|
82
|
+
"mode": "search",
|
|
83
|
+
"searchTarget": "partial_match_for_tags",
|
|
84
|
+
"sort": "popular_desc",
|
|
85
|
+
"startDate": "2024-01-01",
|
|
86
|
+
"_startDate_note": "指定开始日期(YYYY-MM-DD 格式)",
|
|
87
|
+
"endDate": "2024-01-31",
|
|
88
|
+
"_endDate_note": "指定结束日期(YYYY-MM-DD 格式)",
|
|
89
|
+
"languageFilter": "chinese",
|
|
90
|
+
"detectLanguage": true
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"_comment": "示例4: 下载非中文小说(日语、英语等)",
|
|
94
|
+
"type": "novel",
|
|
95
|
+
"tag": "オリジナル",
|
|
96
|
+
"limit": 20,
|
|
97
|
+
"mode": "search",
|
|
98
|
+
"searchTarget": "partial_match_for_tags",
|
|
99
|
+
"sort": "popular_desc",
|
|
100
|
+
"languageFilter": "non-chinese",
|
|
101
|
+
"detectLanguage": true
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"_comment": "示例5: 下载所有语言的小说(不过滤语言)",
|
|
105
|
+
"type": "novel",
|
|
106
|
+
"tag": "オリジナル",
|
|
107
|
+
"limit": 20,
|
|
108
|
+
"mode": "search",
|
|
109
|
+
"searchTarget": "partial_match_for_tags",
|
|
110
|
+
"sort": "popular_desc",
|
|
111
|
+
"detectLanguage": true,
|
|
112
|
+
"_note": "不设置 languageFilter 或设为 null 表示不过滤语言"
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"_说明": {
|
|
116
|
+
"languageFilter": "语言过滤选项: chinese(仅中文)| non-chinese(非中文)| null(不过滤,下载所有语言)",
|
|
117
|
+
"detectLanguage": "是否启用语言检测和记录,默认 true。启用后会在日志中显示检测到的语言",
|
|
118
|
+
"startDate": "开始日期过滤,格式:YYYY-MM-DD 或使用占位符 'LAST_7_DAYS'(最近7天)",
|
|
119
|
+
"endDate": "结束日期过滤,格式:YYYY-MM-DD 或使用占位符 'YESTERDAY'(昨天)",
|
|
120
|
+
"sort": "排序方式: popular_desc(按收藏数降序,最热门优先)| date_desc(按日期降序,最新优先)| date_asc(按日期升序)",
|
|
121
|
+
"limit": "下载数量限制",
|
|
122
|
+
"tag": "搜索标签,可以修改为你感兴趣的标签"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "PixivFlow 排行榜下载配置示例",
|
|
3
|
+
"_usage": "使用方法:",
|
|
4
|
+
"_usage1": " 1. 复制此文件: cp config/standalone.config.ranking.json config/standalone.config.json",
|
|
5
|
+
"_usage2": " 2. 修改 refreshToken(运行 pixivflow login 获取)",
|
|
6
|
+
"_usage3": " 3. 根据需要修改 targets 数组中的配置",
|
|
7
|
+
"_usage4": " 4. 运行下载: pixivflow download",
|
|
8
|
+
|
|
9
|
+
"logLevel": "info",
|
|
10
|
+
"pixiv": {
|
|
11
|
+
"clientId": "MOBrBDS8blbauoSck0ZfDbtuzpyT",
|
|
12
|
+
"clientSecret": "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj",
|
|
13
|
+
"deviceToken": "pixiv",
|
|
14
|
+
"refreshToken": "YOUR_REFRESH_TOKEN",
|
|
15
|
+
"userAgent": "PixivAndroidApp/5.0.234 (Android 11; Pixel 6)"
|
|
16
|
+
},
|
|
17
|
+
"network": {
|
|
18
|
+
"timeoutMs": 30000,
|
|
19
|
+
"retries": 3,
|
|
20
|
+
"retryDelay": 1000
|
|
21
|
+
},
|
|
22
|
+
"storage": {
|
|
23
|
+
"databasePath": "./data/pixiv-downloader.db",
|
|
24
|
+
"downloadDirectory": "./downloads",
|
|
25
|
+
"illustrationDirectory": "./downloads/illustrations",
|
|
26
|
+
"novelDirectory": "./downloads/novels",
|
|
27
|
+
"illustrationOrganization": "byDownloadDate",
|
|
28
|
+
"novelOrganization": "byDownloadDate"
|
|
29
|
+
},
|
|
30
|
+
"scheduler": {
|
|
31
|
+
"enabled": false
|
|
32
|
+
},
|
|
33
|
+
"download": {
|
|
34
|
+
"concurrency": 3,
|
|
35
|
+
"requestDelay": 500,
|
|
36
|
+
"dynamicConcurrency": true,
|
|
37
|
+
"minConcurrency": 1,
|
|
38
|
+
"maxRetries": 3,
|
|
39
|
+
"retryDelay": 2000,
|
|
40
|
+
"timeout": 60000
|
|
41
|
+
},
|
|
42
|
+
"targets": [
|
|
43
|
+
{
|
|
44
|
+
"_comment": "示例1: 下载今日日榜前50名(不筛选标签)",
|
|
45
|
+
"type": "illustration",
|
|
46
|
+
"tag": "ranking-day",
|
|
47
|
+
"limit": 50,
|
|
48
|
+
"mode": "ranking",
|
|
49
|
+
"rankingMode": "day"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"_comment": "示例2: 从今日日榜中筛选包含'風景'标签的作品,下载前20个",
|
|
53
|
+
"type": "illustration",
|
|
54
|
+
"tag": "風景",
|
|
55
|
+
"limit": 20,
|
|
56
|
+
"mode": "ranking",
|
|
57
|
+
"rankingMode": "day",
|
|
58
|
+
"filterTag": "風景"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"_comment": "示例3: 下载昨日日榜前30名",
|
|
62
|
+
"type": "illustration",
|
|
63
|
+
"tag": "ranking-yesterday",
|
|
64
|
+
"limit": 30,
|
|
65
|
+
"mode": "ranking",
|
|
66
|
+
"rankingMode": "day",
|
|
67
|
+
"rankingDate": "YESTERDAY"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"_comment": "示例4: 下载本周周榜前100名",
|
|
71
|
+
"type": "illustration",
|
|
72
|
+
"tag": "ranking-week",
|
|
73
|
+
"limit": 100,
|
|
74
|
+
"mode": "ranking",
|
|
75
|
+
"rankingMode": "week"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"_comment": "示例5: 下载男性向日榜前30名",
|
|
79
|
+
"type": "illustration",
|
|
80
|
+
"tag": "ranking-male",
|
|
81
|
+
"limit": 30,
|
|
82
|
+
"mode": "ranking",
|
|
83
|
+
"rankingMode": "day_male"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"_comment": "示例6: 下载女性向日榜前30名",
|
|
87
|
+
"type": "illustration",
|
|
88
|
+
"tag": "ranking-female",
|
|
89
|
+
"limit": 30,
|
|
90
|
+
"mode": "ranking",
|
|
91
|
+
"rankingMode": "day_female"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"_comment": "示例7: 下载AI作品日榜前50名",
|
|
95
|
+
"type": "illustration",
|
|
96
|
+
"tag": "ranking-ai",
|
|
97
|
+
"limit": 50,
|
|
98
|
+
"mode": "ranking",
|
|
99
|
+
"rankingMode": "day_ai"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"_comment": "示例8: 下载原创周榜前50名",
|
|
103
|
+
"type": "illustration",
|
|
104
|
+
"tag": "ranking-original",
|
|
105
|
+
"limit": 50,
|
|
106
|
+
"mode": "ranking",
|
|
107
|
+
"rankingMode": "week_original"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"_comment": "示例9: 下载新人周榜前30名",
|
|
111
|
+
"type": "illustration",
|
|
112
|
+
"tag": "ranking-rookie",
|
|
113
|
+
"limit": 30,
|
|
114
|
+
"mode": "ranking",
|
|
115
|
+
"rankingMode": "week_rookie"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"_comment": "示例10: 从本周周榜中筛选包含'オリジナル'标签的小说,下载前10个",
|
|
119
|
+
"type": "novel",
|
|
120
|
+
"tag": "オリジナル",
|
|
121
|
+
"limit": 10,
|
|
122
|
+
"mode": "ranking",
|
|
123
|
+
"rankingMode": "week",
|
|
124
|
+
"filterTag": "オリジナル"
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"_说明": {
|
|
128
|
+
"mode": "ranking - 使用排行榜模式下载(可选:search - 搜索模式,默认)",
|
|
129
|
+
"rankingMode": "排行榜类型: day(日榜)| week(周榜)| month(月榜)| day_male(男性向日榜)| day_female(女性向日榜)| day_ai(AI作品日榜)| week_original(原创周榜)| week_rookie(新人周榜)| day_r18(R18日榜)| day_male_r18(男性向R18日榜)| day_female_r18(女性向R18日榜)",
|
|
130
|
+
"rankingDate": "排行榜日期,格式:YYYY-MM-DD(如 '2024-01-15'),或使用 'YESTERDAY' 占位符表示昨天,不填则使用今天",
|
|
131
|
+
"filterTag": "可选,从排名结果中筛选包含此标签的作品。不填则下载排名中的所有作品",
|
|
132
|
+
"limit": "最终下载的数量限制",
|
|
133
|
+
"tag": "用于日志记录和文件组织的标签名称,可以自定义"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "PixivFlow 简化配置 - 快速开始使用",
|
|
3
|
+
"_usage": "使用方法:",
|
|
4
|
+
"_usage1": " 1. 运行 pixivflow login 获取 refreshToken",
|
|
5
|
+
"_usage2": " 2. 将下面的 YOUR_REFRESH_TOKEN 替换为登录后获取的 token",
|
|
6
|
+
"_usage3": " 3. 根据需要修改 targets 数组中的标签和数量",
|
|
7
|
+
"_usage4": " 4. 运行 pixivflow download 开始下载",
|
|
8
|
+
|
|
9
|
+
"logLevel": "info",
|
|
10
|
+
"pixiv": {
|
|
11
|
+
"clientId": "MOBrBDS8blbauoSck0ZfDbtuzpyT",
|
|
12
|
+
"clientSecret": "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj",
|
|
13
|
+
"deviceToken": "pixiv",
|
|
14
|
+
"refreshToken": "YOUR_REFRESH_TOKEN",
|
|
15
|
+
"userAgent": "PixivAndroidApp/5.0.234 (Android 11; Pixel 6)"
|
|
16
|
+
},
|
|
17
|
+
"network": {
|
|
18
|
+
"timeoutMs": 30000,
|
|
19
|
+
"retries": 3,
|
|
20
|
+
"retryDelay": 1000
|
|
21
|
+
},
|
|
22
|
+
"storage": {
|
|
23
|
+
"databasePath": "./data/pixiv-downloader.db",
|
|
24
|
+
"downloadDirectory": "./downloads",
|
|
25
|
+
"illustrationDirectory": "./downloads/illustrations",
|
|
26
|
+
"novelDirectory": "./downloads/novels",
|
|
27
|
+
"illustrationOrganization": "byDownloadDate",
|
|
28
|
+
"novelOrganization": "byDownloadDate"
|
|
29
|
+
},
|
|
30
|
+
"scheduler": {
|
|
31
|
+
"enabled": false
|
|
32
|
+
},
|
|
33
|
+
"targets": [
|
|
34
|
+
{
|
|
35
|
+
"_comment": "示例:下载包含'風景'标签的插画,每次下载20张",
|
|
36
|
+
"type": "illustration",
|
|
37
|
+
"tag": "風景",
|
|
38
|
+
"limit": 20,
|
|
39
|
+
"searchTarget": "partial_match_for_tags",
|
|
40
|
+
"sort": "date_desc"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"download": {
|
|
44
|
+
"concurrency": 3,
|
|
45
|
+
"requestDelay": 500,
|
|
46
|
+
"dynamicConcurrency": true,
|
|
47
|
+
"minConcurrency": 1,
|
|
48
|
+
"maxRetries": 3,
|
|
49
|
+
"retryDelay": 2000,
|
|
50
|
+
"timeout": 60000
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"logLevel": "info",
|
|
3
|
+
"pixiv": {
|
|
4
|
+
"clientId": "MOBrBDS8blbauoSck0ZfDbtuzpyT",
|
|
5
|
+
"clientSecret": "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj",
|
|
6
|
+
"deviceToken": "pixiv",
|
|
7
|
+
"refreshToken": "YOUR_REFRESH_TOKEN",
|
|
8
|
+
"userAgent": "PixivAndroidApp/5.0.234 (Android 11; Pixel 6)"
|
|
9
|
+
},
|
|
10
|
+
"network": {
|
|
11
|
+
"timeoutMs": 30000,
|
|
12
|
+
"retries": 3,
|
|
13
|
+
"retryDelay": 1000
|
|
14
|
+
},
|
|
15
|
+
"storage": {
|
|
16
|
+
"databasePath": "./data/pixiv-downloader.db",
|
|
17
|
+
"downloadDirectory": "./downloads"
|
|
18
|
+
},
|
|
19
|
+
"scheduler": {
|
|
20
|
+
"enabled": false,
|
|
21
|
+
"cron": "0 3 * * *",
|
|
22
|
+
"timezone": "Asia/Shanghai"
|
|
23
|
+
},
|
|
24
|
+
"download": {
|
|
25
|
+
"concurrency": 3,
|
|
26
|
+
"requestDelay": 500,
|
|
27
|
+
"dynamicConcurrency": true,
|
|
28
|
+
"minConcurrency": 1,
|
|
29
|
+
"maxRetries": 3,
|
|
30
|
+
"retryDelay": 2000,
|
|
31
|
+
"timeout": 60000
|
|
32
|
+
},
|
|
33
|
+
"targets": [
|
|
34
|
+
{
|
|
35
|
+
"type": "novel",
|
|
36
|
+
"mode": "search",
|
|
37
|
+
"tag": "オリジナル",
|
|
38
|
+
"searchTarget": "partial_match_for_tags",
|
|
39
|
+
"sort": "popular_desc",
|
|
40
|
+
"limit": 10,
|
|
41
|
+
"startDate": "YESTERDAY",
|
|
42
|
+
"endDate": "YESTERDAY",
|
|
43
|
+
"languageFilter": "chinese",
|
|
44
|
+
"detectLanguage": true
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"initialDelay": 0
|
|
48
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"logLevel": "info",
|
|
3
|
+
"pixiv": {
|
|
4
|
+
"clientId": "MOBrBDS8blbauoSck0ZfDbtuzpyT",
|
|
5
|
+
"clientSecret": "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj",
|
|
6
|
+
"deviceToken": "pixiv",
|
|
7
|
+
"refreshToken": "YOUR_REFRESH_TOKEN",
|
|
8
|
+
"userAgent": "PixivAndroidApp/5.0.234 (Android 11; Pixel 6)"
|
|
9
|
+
},
|
|
10
|
+
"network": {
|
|
11
|
+
"timeoutMs": 30000,
|
|
12
|
+
"retries": 3,
|
|
13
|
+
"retryDelay": 1000
|
|
14
|
+
},
|
|
15
|
+
"storage": {
|
|
16
|
+
"databasePath": "./data/pixiv-downloader.db",
|
|
17
|
+
"downloadDirectory": "./downloads"
|
|
18
|
+
},
|
|
19
|
+
"scheduler": {
|
|
20
|
+
"enabled": false,
|
|
21
|
+
"cron": "0 3 * * *",
|
|
22
|
+
"timezone": "Asia/Shanghai"
|
|
23
|
+
},
|
|
24
|
+
"download": {
|
|
25
|
+
"concurrency": 3,
|
|
26
|
+
"requestDelay": 500,
|
|
27
|
+
"dynamicConcurrency": true,
|
|
28
|
+
"minConcurrency": 1,
|
|
29
|
+
"maxRetries": 3,
|
|
30
|
+
"retryDelay": 2000,
|
|
31
|
+
"timeout": 60000
|
|
32
|
+
},
|
|
33
|
+
"targets": [
|
|
34
|
+
{
|
|
35
|
+
"type": "illustration",
|
|
36
|
+
"mode": "ranking",
|
|
37
|
+
"rankingMode": "day",
|
|
38
|
+
"rankingDate": "YESTERDAY",
|
|
39
|
+
"limit": 10
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"initialDelay": 0
|
|
43
|
+
}
|
package/dist/auth/PixivAuth.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { NetworkConfig, PixivCredentialConfig } from '../config';
|
|
2
2
|
import { Database } from '../storage/Database';
|
|
3
|
+
/**
|
|
4
|
+
* Read-only auth: never exchange a refresh token, never persist one.
|
|
5
|
+
*
|
|
6
|
+
* Two processes can share one refresh token, but Pixiv's rotation behaviour on
|
|
7
|
+
* `grant_type=refresh_token` is a server detail a client cannot prove. If it
|
|
8
|
+
* rotates, a second process that refreshes can invalidate the first one's token —
|
|
9
|
+
* and a long-running daemon never re-reads the token from disk, so it cannot
|
|
10
|
+
* recover without a restart. In this mode the only accepted credential is an
|
|
11
|
+
* access token already present in the local cache; anything else fails loudly
|
|
12
|
+
* instead of silently rotating a credential someone else is using.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isAuthReadOnly(): boolean;
|
|
3
15
|
export declare class PixivAuth {
|
|
4
16
|
private readonly credentials;
|
|
5
17
|
private readonly network;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PixivAuth.d.ts","sourceRoot":"","sources":["../../src/auth/PixivAuth.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,EAAoB,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAmBjE,qBAAa,SAAS;IAIlB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAL3B,OAAO,CAAC,UAAU,CAAC,CAAS;gBAGT,WAAW,EAAE,qBAAqB,EAClC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EACnC,UAAU,CAAC,EAAE,MAAM;IAKR,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"PixivAuth.d.ts","sourceRoot":"","sources":["../../src/auth/PixivAuth.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,EAAoB,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAmBjE;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAGxC;AAED,qBAAa,SAAS;IAIlB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAL3B,OAAO,CAAC,UAAU,CAAC,CAAS;gBAGT,WAAW,EAAE,qBAAqB,EAClC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,QAAQ,EACnC,UAAU,CAAC,EAAE,MAAM;IAKR,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAiB9C;;;OAGG;IACG,2BAA2B,IAAI,OAAO,CAAC,MAAM,CAAC;YAKtC,kBAAkB;IAoJhC,OAAO,CAAC,kBAAkB;CAI3B"}
|
package/dist/auth/PixivAuth.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.PixivAuth = void 0;
|
|
37
|
+
exports.isAuthReadOnly = isAuthReadOnly;
|
|
37
38
|
const node_crypto_1 = require("node:crypto");
|
|
38
39
|
const promises_1 = require("node:timers/promises");
|
|
39
40
|
const logger_1 = require("../logger");
|
|
@@ -41,6 +42,21 @@ const login_helper_1 = require("../utils/login-helper");
|
|
|
41
42
|
const errors_1 = require("../utils/errors");
|
|
42
43
|
const TOKEN_CACHE_KEY = 'pixiv_access_token';
|
|
43
44
|
const REFRESH_TOKEN_CACHE_KEY = 'pixiv_refresh_token';
|
|
45
|
+
/**
|
|
46
|
+
* Read-only auth: never exchange a refresh token, never persist one.
|
|
47
|
+
*
|
|
48
|
+
* Two processes can share one refresh token, but Pixiv's rotation behaviour on
|
|
49
|
+
* `grant_type=refresh_token` is a server detail a client cannot prove. If it
|
|
50
|
+
* rotates, a second process that refreshes can invalidate the first one's token —
|
|
51
|
+
* and a long-running daemon never re-reads the token from disk, so it cannot
|
|
52
|
+
* recover without a restart. In this mode the only accepted credential is an
|
|
53
|
+
* access token already present in the local cache; anything else fails loudly
|
|
54
|
+
* instead of silently rotating a credential someone else is using.
|
|
55
|
+
*/
|
|
56
|
+
function isAuthReadOnly() {
|
|
57
|
+
const value = process.env.PIXIV_AUTH_READONLY?.trim().toLowerCase();
|
|
58
|
+
return value === '1' || value === 'true' || value === 'yes';
|
|
59
|
+
}
|
|
44
60
|
class PixivAuth {
|
|
45
61
|
credentials;
|
|
46
62
|
network;
|
|
@@ -57,6 +73,10 @@ class PixivAuth {
|
|
|
57
73
|
if (cached && cached.accessToken && cached.expiresAt > Date.now() + 60_000) {
|
|
58
74
|
return cached.accessToken;
|
|
59
75
|
}
|
|
76
|
+
if (isAuthReadOnly()) {
|
|
77
|
+
throw new errors_1.AuthenticationError('Read-only auth (PIXIV_AUTH_READONLY) refuses to refresh: no usable access token is cached. ' +
|
|
78
|
+
'Seed the access-token cache with a token that is still valid, or run with its own credential.');
|
|
79
|
+
}
|
|
60
80
|
const refreshed = await this.refreshAccessToken();
|
|
61
81
|
return refreshed.accessToken;
|
|
62
82
|
}
|
|
@@ -69,6 +89,12 @@ class PixivAuth {
|
|
|
69
89
|
return stored.accessToken;
|
|
70
90
|
}
|
|
71
91
|
async refreshAccessToken() {
|
|
92
|
+
// Single guard for every refresh caller: the persistence block below writes a
|
|
93
|
+
// rotated token to the database, a file and the config, in this process only.
|
|
94
|
+
if (isAuthReadOnly()) {
|
|
95
|
+
throw new errors_1.AuthenticationError('Read-only auth (PIXIV_AUTH_READONLY) must never call the token endpoint: ' +
|
|
96
|
+
'refreshing may rotate a refresh token another process is using.');
|
|
97
|
+
}
|
|
72
98
|
const url = 'https://oauth.secure.pixiv.net/auth/token';
|
|
73
99
|
let lastError;
|
|
74
100
|
for (let attempt = 0; attempt < (this.network.retries ?? 3); attempt++) {
|