diff --git a/docs/install/README.md b/docs/install/README.md index 21219d3904..8e56613ac0 100644 --- a/docs/install/README.md +++ b/docs/install/README.md @@ -446,6 +446,10 @@ RSSHub 支持使用访问密钥 / 码,白名单和黑名单三种方式进行 - `PIXIV_PASSWORD`: Pixiv 密码 + - `PIXIV_BYPASS_CDN`: 绕过 Pixiv 前置的 Cloudflare CDN, 使用`PIXIV_BYPASS_HOSTNAME`指示的 IP 地址访问 Pixiv API, 可以解决因 Cloudflare 机器人验证导致的登录失败问题,默认关闭,设置 true 或 1 开启 + + - `PIXIV_BYPASS_HOSTNAME`: Pixiv 源站的主机名或 IP 地址,主机名会被解析为 IPv4 地址,默认为`public-api.secure.pixiv.net`;仅在`PIXIV_BYPASS_CDN`开启时生效 + - pixiv fanbox 用于获取付费内容 - `FANBOX_SESSION_ID`: 对应 cookies 中的`FANBOXSESSID`。 diff --git a/lib/config.js b/lib/config.js index c9759849be..4a3b17c958 100644 --- a/lib/config.js +++ b/lib/config.js @@ -55,6 +55,8 @@ const calculateValue = () => { pixiv: { username: envs.PIXIV_USERNAME, password: envs.PIXIV_PASSWORD, + bypassCdn: envs.PIXIV_BYPASS_CDN !== '0' && envs.PIXIV_BYPASS_CDN !== 'false', + bypassCdnHostname: envs.PIXIV_BYPASS_HOSTNAME || 'public-api.secure.pixiv.net', }, fanbox: { session: envs.FANBOX_SESSION_ID, diff --git a/lib/routes/pixiv/api/getBookmarks.js b/lib/routes/pixiv/api/getBookmarks.js index a446f8e3bd..c23a8a2bb2 100644 --- a/lib/routes/pixiv/api/getBookmarks.js +++ b/lib/routes/pixiv/api/getBookmarks.js @@ -1,4 +1,4 @@ -const got = require('@/utils/got'); +const got = require('../pixiv-got'); const maskHeader = require('../constants').maskHeader; const queryString = require('query-string'); diff --git a/lib/routes/pixiv/api/getIllustFollows.js b/lib/routes/pixiv/api/getIllustFollows.js index d73908ca53..b2ae54f261 100644 --- a/lib/routes/pixiv/api/getIllustFollows.js +++ b/lib/routes/pixiv/api/getIllustFollows.js @@ -1,4 +1,4 @@ -const got = require('@/utils/got'); +const got = require('../pixiv-got'); const maskHeader = require('../constants').maskHeader; const queryString = require('query-string'); diff --git a/lib/routes/pixiv/api/getIllusts.js b/lib/routes/pixiv/api/getIllusts.js index cd764d9c73..6a112bd05c 100644 --- a/lib/routes/pixiv/api/getIllusts.js +++ b/lib/routes/pixiv/api/getIllusts.js @@ -1,4 +1,4 @@ -const got = require('@/utils/got'); +const got = require('../pixiv-got'); const maskHeader = require('../constants').maskHeader; const queryString = require('query-string'); diff --git a/lib/routes/pixiv/api/getRanking.js b/lib/routes/pixiv/api/getRanking.js index da1c48dae3..5addad53e1 100644 --- a/lib/routes/pixiv/api/getRanking.js +++ b/lib/routes/pixiv/api/getRanking.js @@ -1,4 +1,4 @@ -const got = require('@/utils/got'); +const got = require('../pixiv-got'); const maskHeader = require('../constants').maskHeader; const assert = require('assert'); const queryString = require('query-string'); diff --git a/lib/routes/pixiv/api/getUserDetail.js b/lib/routes/pixiv/api/getUserDetail.js index 7c750adbb4..eb26413461 100644 --- a/lib/routes/pixiv/api/getUserDetail.js +++ b/lib/routes/pixiv/api/getUserDetail.js @@ -1,4 +1,4 @@ -const got = require('@/utils/got'); +const got = require('../pixiv-got'); const maskHeader = require('../constants').maskHeader; const queryString = require('query-string'); diff --git a/lib/routes/pixiv/api/searchIllust.js b/lib/routes/pixiv/api/searchIllust.js index affb418a81..738a9f5996 100644 --- a/lib/routes/pixiv/api/searchIllust.js +++ b/lib/routes/pixiv/api/searchIllust.js @@ -1,4 +1,4 @@ -const got = require('@/utils/got'); +const got = require('../pixiv-got'); const maskHeader = require('../constants').maskHeader; const queryString = require('query-string'); diff --git a/lib/routes/pixiv/api/searchPopularIllust.js b/lib/routes/pixiv/api/searchPopularIllust.js index ded3f1bbe4..430e5516d7 100644 --- a/lib/routes/pixiv/api/searchPopularIllust.js +++ b/lib/routes/pixiv/api/searchPopularIllust.js @@ -1,4 +1,4 @@ -const got = require('@/utils/got'); +const got = require('../pixiv-got'); const maskHeader = require('../constants').maskHeader; const queryString = require('query-string'); diff --git a/lib/routes/pixiv/pixiv-got.js b/lib/routes/pixiv/pixiv-got.js new file mode 100644 index 0000000000..b624d4f4ca --- /dev/null +++ b/lib/routes/pixiv/pixiv-got.js @@ -0,0 +1,42 @@ +const dnsPromises = require('dns').promises; +const ipRegex = require('ip-regex'); +const got = require('@/utils/got'); +const logger = require('@/utils/logger'); +const pixivConfig = require('@/config').value.pixiv; + +const pixivGot = got.extend({ + hooks: { + beforeRequest: [ + async (options) => { + if (!pixivConfig.bypassCdn) { + return; + } + let hostname = null; + const isIP = ipRegex({ exact: true }).test(pixivConfig.bypassCdnHostname); + if (isIP) { + hostname = pixivConfig.bypassCdnHostname; + } else { + try { + const addresses = await dnsPromises.resolve4(pixivConfig.bypassCdnHostname); + if (addresses.length) { + hostname = addresses[Math.floor(addresses.length * Math.random())]; + } else { + logger.warn(`No IPv4 address resolved from ${pixivConfig.bypassCdnHostname}`); + } + } catch (e) { + logger.error(`Failed to resolve ${pixivConfig.bypassCdnHostname}`); + } + } + if (hostname) { + options.headers = { + ...options.headers, + host: options.url.hostname, + }; + options.url.hostname = hostname; + } + }, + ], + }, +}); + +module.exports = pixivGot; diff --git a/lib/routes/pixiv/token.js b/lib/routes/pixiv/token.js index d8ec96af6e..b1edad3e2a 100644 --- a/lib/routes/pixiv/token.js +++ b/lib/routes/pixiv/token.js @@ -1,7 +1,7 @@ const config = require('@/config').value; const logger = require('@/utils/logger'); const wait = require('@/utils/wait'); -const got = require('@/utils/got'); +const got = require('./pixiv-got'); const maskHeader = require('./constants').maskHeader; const md5 = require('@/utils/md5'); diff --git a/package.json b/package.json index 2f900de80e..bf247cd809 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "hooman": "1.2.6", "https-proxy-agent": "5.0.0", "iconv-lite": "0.6.2", + "ip-regex": "4.2.0", "is-localhost-ip": "1.4.0", "jsdom": "16.4.0", "json-bigint": "1.0.0", diff --git a/yarn.lock b/yarn.lock index 032d52b207..c9cbdd188a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6525,6 +6525,11 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ip-regex@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.2.0.tgz#a03f5eb661d9a154e3973a03de8b23dd0ad6892e" + integrity sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A== + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"