Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/docker-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ jobs:
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: .
build-args: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0 # also test bundling Chromium
# also test bundling Chromium
build-args: |
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=0
CLOAKBROWSER_SKIP_DOWNLOAD=0
load: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/issue-command.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ jobs:
cache: 'pnpm'

- name: Install dependencies (pnpm)
run: pnpm i && pnpm rb && pnpm exec playwright install chromium
run: pnpm i && pnpm rb && pnpm exec cloakbrowser install

- name: Fetch affected routes
id: fetch-route
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Install dependencies (pnpm)
run: pnpm i
- name: Run postinstall script for dependencies
run: pnpm rb && pnpm exec playwright install chromium
run: pnpm rb && pnpm exec cloakbrowser install
- name: Build routes
run: pnpm build
- name: Build worker routes
Expand Down Expand Up @@ -91,7 +91,7 @@ jobs:
run: pnpm build
- name: Install bundled Chromium
if: ${{ matrix.chromium.dependency == '' }}
run: pnpm exec playwright install chromium
run: pnpm exec cloakbrowser install
- name: Install Chromium
if: ${{ matrix.chromium.dependency != '' }}
# 'chromium-browser' from Ubuntu APT repo is a dummy package. Its version (85.0.4183.83) means
Expand Down
64 changes: 35 additions & 29 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ WORKDIR /ver
COPY ./package.json /app/
RUN \
set -ex && \
grep -Po '(?<="playwright": ")[^\s"]*(?=")' /app/package.json | tee /ver/.playwright_version && \
grep -Po '(?<="cloakbrowser": ")[^\s"]*(?=")' /app/package.json | tee /ver/.cloakbrowser_version && \
grep -Po '(?<="@vercel/nft": ")[^\s"]*(?=")' /app/package.json | tee /ver/.nft_version && \
grep -Po '(?<="fs-extra": ")[^\s"]*(?=")' /app/package.json | tee /ver/.fs_extra_version

Expand Down Expand Up @@ -88,29 +88,32 @@ FROM node:24-bookworm-slim AS chromium-downloader
# Yeah, downloading Chromium never needs those dependencies below.

WORKDIR /app
COPY --from=dep-version-parser /ver/.playwright_version /app/.playwright_version
COPY --from=dep-version-parser /ver/.cloakbrowser_version /app/.cloakbrowser_version

ARG TARGETPLATFORM
ARG USE_CHINA_NPM_REGISTRY=0
ARG PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
# The official recommended way to use Playwright on x86(_64) is to use the bundled browser.
ARG CLOAKBROWSER_SKIP_DOWNLOAD=1
# CloakBrowser publishes prebuilt patched Chromium for both linux/amd64 and linux/arm64,
ENV CLOAKBROWSER_CACHE_DIR=/app/node_modules/.cache/cloakbrowser
RUN \
set -ex ; \
if [ "$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD" = 0 ] && [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \
if [ "$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD" = 0 ]; then \
if [ "$USE_CHINA_NPM_REGISTRY" = 1 ]; then \
npm config set registry https://registry.npmmirror.com && \
yarn config set registry https://registry.npmmirror.com && \
pnpm config set registry https://registry.npmmirror.com ; \
fi; \
echo 'Downloading Chromium...' && \
echo 'Downloading CloakBrowser...' && \
unset PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD && \
export PLAYWRIGHT_BROWSERS_PATH=/app/node_modules/.cache/ms-playwright && \
corepack enable pnpm && \
pnpm --allow-build=playwright add playwright@$(cat /app/.playwright_version) --save-prod && \
pnpm rb && \
pnpm exec playwright install chromium ; \
pnpm add cloakbrowser@$(cat /app/.cloakbrowser_version) --save-prod && \
pnpm rb ; \
if [ "$CLOAKBROWSER_SKIP_DOWNLOAD" = 0 ]; then \
pnpm exec cloakbrowser install ; \
fi; \
else \
mkdir -p /app/node_modules/.cache/ms-playwright ; \
mkdir -p "$CLOAKBROWSER_CACHE_DIR" ; \
fi;

# ---------------------------------------------------------------------------------------------------------------------
Expand All @@ -121,48 +124,51 @@ LABEL org.opencontainers.image.authors="https://github.com/DIYgod/RSSHub"

ENV NODE_ENV=production
ENV TZ=Asia/Shanghai
ENV CLOAKBROWSER_AUTO_UPDATE=false
ENV CLOAKBROWSER_DOWNLOAD_URL=https://github.com/CloakHQ/cloakbrowser/releases/download

WORKDIR /app

# install deps first to avoid cache miss or disturbing buildkit to build concurrently
ARG TARGETPLATFORM
ARG PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
ARG CLOAKBROWSER_SKIP_DOWNLOAD=1
# https://playwright.dev/docs/docker#introduction
# https://www.debian.org/releases/bookworm/amd64/release-notes/ch-information.en.html#noteworthy-obsolete-packages
# On arm/arm64, install Chromium from the distribution repositories.
# CloakBrowser ships prebuilt patched Chromium for both linux/amd64 and linux/arm64
RUN \
set -ex && \
apt-get update && \
apt-get install -yq --no-install-recommends \
dumb-init git curl \
; \
if [ "$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD" = 0 ]; then \
if [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \
apt-get install -yq --no-install-recommends \
ca-certificates fonts-liberation wget xdg-utils \
libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libcairo2 libcups2 libdbus-1-3 libdrm2 \
libexpat1 libgbm1 libglib2.0-0 libnspr4 libnss3 libpango-1.0-0 libx11-6 libxcb1 libxcomposite1 \
libxdamage1 libxext6 libxfixes3 libxkbcommon0 libxrandr2 \
; \
else \
apt-get install -yq --no-install-recommends \
chromium \
&& \
echo "CHROMIUM_EXECUTABLE_PATH=$(which chromium)" | tee /app/.env ; \
fi; \
apt-get install -yq --no-install-recommends \
ca-certificates fonts-liberation wget xdg-utils \
libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libcairo2 libcups2 libdbus-1-3 libdrm2 \
libexpat1 libgbm1 libglib2.0-0 libnspr4 libnss3 libpango-1.0-0 libx11-6 libxcb1 libxcomposite1 \
libxdamage1 libxext6 libxfixes3 libxkbcommon0 libxrandr2 \
# https://github.com/CloakHQ/CloakBrowser/tree/main#font-setup-on-linux
fonts-noto-color-emoji fonts-freefont-ttf fonts-unifont \
fonts-ipafont-gothic fonts-wqy-zenhei fonts-tlwg-loma-otf \
; \
fi; \
rm -rf /var/lib/apt/lists/*

COPY --from=chromium-downloader /app/node_modules/.cache/ms-playwright /app/node_modules/.cache/ms-playwright
ENV CLOAKBROWSER_CACHE_DIR=/app/node_modules/.cache/cloakbrowser
COPY --from=chromium-downloader /app/node_modules/.cache/cloakbrowser /app/node_modules/.cache/cloakbrowser

RUN \
set -ex && \
if [ "$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD" = 0 ] && [ "$TARGETPLATFORM" = 'linux/amd64' ]; then \
if [ "$PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD" = 0 ] && [ "$CLOAKBROWSER_SKIP_DOWNLOAD" = 0 ]; then \
echo 'Verifying Chromium installation...' && \
_chrome_path=$(find /app/node_modules/.cache/ms-playwright/ -name chrome -xtype f -executable | head -n1) && \
echo "CHROMIUM_EXECUTABLE_PATH=$_chrome_path" | tee /app/.env && \
_chrome_path=$(find /app/node_modules/.cache/cloakbrowser/ -name chrome -xtype f -executable | head -n1) && \
if [ -z "$_chrome_path" ]; then \
echo "!!! CloakBrowser binary not found !!!" && \
exit 1 ; \
fi; \
if ldd "$_chrome_path" | grep "not found"; then \
echo "!!! Chromium has unmet shared libs !!!" && \
echo "!!! CloakBrowser has unmet shared libs !!!" && \
exit 1 ; \
else \
echo "Awesome! All shared libs are met!" ; \
Expand Down
11 changes: 5 additions & 6 deletions lib/bilibili-video-route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@ const destroy = vi.fn();
const getPlaywrightPage = vi.fn();
const goto = vi.fn();
const on = vi.fn();
const setCookie = vi.fn();
const setRequestInterception = vi.fn();
const pageRoute = vi.fn();
const waitForResponse = vi.fn();

const page = {
goto,
on,
setCookie,
setRequestInterception,
route: pageRoute,
waitForResponse,
};

Expand Down Expand Up @@ -66,8 +64,7 @@ describe('/bilibili/user/video/:uid', () => {
getPlaywrightPage.mockReset();
goto.mockReset();
on.mockReset();
setCookie.mockReset();
setRequestInterception.mockReset();
pageRoute.mockReset();
waitForResponse.mockReset();
});

Expand Down Expand Up @@ -109,6 +106,7 @@ describe('/bilibili/user/video/:uid', () => {
getPlaywrightPage.mockImplementation(async (_url, options) => {
await options.onBeforeLoad?.(page);
return {
context: {},
destroy,
page,
};
Expand Down Expand Up @@ -171,6 +169,7 @@ describe('/bilibili/user/video/:uid', () => {
getPlaywrightPage.mockImplementation(async (_url, options) => {
await options.onBeforeLoad?.(page);
return {
context: {},
destroy,
page,
};
Expand Down
5 changes: 5 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import app from '@/app';
import { config } from '@/config';
import { getLocalhostAddress } from '@/utils/common-utils';
import logger from '@/utils/logger';
import { checkChromium } from '@/utils/playwright-utils';

const port = config.connect.port;
const hostIPList = getLocalhostAddress();
Expand All @@ -30,6 +31,8 @@ if (config.enableCluster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}

void checkChromium();
} else {
logger.info(`Worker ${process.pid} is running`);
serve({
Expand Down Expand Up @@ -58,6 +61,8 @@ if (config.enableCluster) {
maxHeaderSize: 1024 * 32,
},
});

void checkChromium();
}

export default server;
12 changes: 6 additions & 6 deletions lib/routes/acs/journal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ async function handler(ctx) {

let title = '';

const browser = await playwright();
const context = await playwright();
const items = await cache.tryGet(
currentUrl,
async () => {
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', (request) => {
request.resourceType() === 'document' || request.resourceType() === 'script' ? request.continue() : request.abort();
const page = await context.newPage();
await page.route('**/*', (route) => {
const request = route.request();
request.resourceType() === 'document' || request.resourceType() === 'script' ? route.continue() : route.abort();
});
await page.goto(currentUrl, {
waitUntil: 'domcontentloaded',
Expand Down Expand Up @@ -76,7 +76,7 @@ async function handler(ctx) {
false
);

await browser.close();
await context.close();

return {
title,
Expand Down
6 changes: 3 additions & 3 deletions lib/routes/aip/journal-pupp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ const handler = async (ctx) => {
}

// use Playwright due to the obstacle by cloudflare challenge
const browser = await playwright();
const context = await playwright();

const { jrnlName, list } = await cache.tryGet(
jrnlUrl,
async () => {
const response = await playwrightGet(jrnlUrl, browser);
const response = await playwrightGet(jrnlUrl, context);
const $ = load(response);
const jrnlName = $('.header-journal-title').text();
const list = $('.card')
Expand Down Expand Up @@ -52,7 +52,7 @@ const handler = async (ctx) => {
false
);

await browser.close();
await context.close();

return {
title: jrnlName,
Expand Down
10 changes: 5 additions & 5 deletions lib/routes/aip/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { renderToString } from 'hono/jsx/dom/server';

const playwrightGet = async (url, browser) => {
const page = await browser.newPage();
const playwrightGet = async (url, context) => {
const page = await context.newPage();
// await page.setExtraHTTPHeaders({ referer: host });
await page.setRequestInterception(true);
page.on('request', (request) => {
request.resourceType() === 'document' ? request.continue() : request.abort();
await page.route('**/*', (route) => {
const request = route.request();
request.resourceType() === 'document' ? route.continue() : route.abort();
});
await page.goto(url, {
waitUntil: 'domcontentloaded',
Expand Down
12 changes: 6 additions & 6 deletions lib/routes/alternativeto/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ const baseURL = 'https://alternativeto.net';

const playwrightGet = (url, cache) =>
cache.tryGet(url, async () => {
const browser = await playwright();
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', (request) => {
request.resourceType() === 'document' ? request.continue() : request.abort();
const context = await playwright();
const page = await context.newPage();
await page.route('**/*', (route) => {
const request = route.request();
request.resourceType() === 'document' ? route.continue() : route.abort();
});
await page.goto(url, {
waitUntil: 'domcontentloaded',
});
const html = await page.evaluate(() => document.documentElement.innerHTML);
await browser.close();
await context.close();
return html;
});

Expand Down
12 changes: 6 additions & 6 deletions lib/routes/apkpure/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ async function handler(ctx) {
const baseUrl = 'https://apkpure.com';
const link = `${baseUrl}/${region}/${pkg}/versions`;

const browser = await playwright();
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', (request) => {
request.resourceType() === 'document' ? request.continue() : request.abort();
const context = await playwright();
const page = await context.newPage();
await page.route('**/*', (route) => {
const request = route.request();
request.resourceType() === 'document' ? route.continue() : route.abort();
});
logger.http(`Requesting ${link}`);
await page.goto(link, {
waitUntil: 'domcontentloaded',
});

const r = await page.evaluate(() => document.documentElement.innerHTML);
await browser.close();
await context.close();

const $ = load(r);
const img = new URL($('.ver-top img').attr('src'));
Expand Down
2 changes: 1 addition & 1 deletion lib/routes/bilibili/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const getCookie = (disableConfig = false) => {
waitForRequest = new Promise<string>((resolve) => {
page.on('requestfinished', async (request) => {
if (request.url() === 'https://api.bilibili.com/x/web-interface/nav') {
const cookies = await page.cookies();
const cookies = await page.context().cookies();
let cookieString = cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');
cookieString = cookieString.replace(/b_lsid=[0-9A-F]+_[0-9A-F]+/, `b_lsid=${utils.lsid()}`);
resolve(cookieString);
Expand Down
8 changes: 4 additions & 4 deletions lib/routes/bilibili/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ async function applyCookie(page: Page, cookie: string) {
.filter((item) => item !== undefined);

if (cookies.length > 0) {
await page.setCookie(...cookies);
await page.context().addCookies(cookies);
}
}

Expand Down Expand Up @@ -184,9 +184,9 @@ async function fetchVideoListFromBrowser(uid: string): Promise<VideoListData> {
await applyCookie(page, cookie);
}

await page.setRequestInterception(true);
page.on('request', (request) => {
allowedBrowserRequestTypes.has(request.resourceType()) ? request.continue() : request.abort();
await page.route('**/*', (route) => {
const request = route.request();
allowedBrowserRequestTypes.has(request.resourceType()) ? route.continue() : route.abort();
});
},
gotoConfig: { waitUntil: 'domcontentloaded' },
Expand Down
Loading