Merge pull request #6 from ThePhaseless/chromium

Chromium
This commit is contained in:
Jakub Orchowski 2024-10-18 18:35:05 +02:00 committed by GitHub
commit 0fa7209311
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 633 additions and 933 deletions

View File

@ -1,8 +1,27 @@
FROM mcr.microsoft.com/devcontainers/python:1-3.12-bullseye
FROM python:3.12
# Inspired by https://github.com/Hudrolax/uc-docker-alpine/
# Install build dependencies
RUN apt update && apt upgrade -y && apt install -y\
curl \
wget \
unzip \
gnupg \
bash \
stow
# Install dependencies
RUN apt install -y \
xvfb \
x11vnc \
fluxbox \
xterm \
git \
ca-certificates \
pipx \
chromium
USER vscode
RUN pipx install poetry
RUN poetry config virtualenvs.in-project true
RUN sudo apt update && sudo apt upgrade -y
RUN sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
RUN sudo apt install -y ./google-chrome-stable_current_amd64.deb
ENV DISPLAY=:0
# RUN poetry config virtualenvs.in-project true

View File

@ -6,7 +6,12 @@
"build": {
"dockerfile": "Dockerfile"
},
"runArgs": ["-p", "8181:8191"],
"runArgs": [
"-p",
"8181:8191",
"--cap-add",
"SYS_ADMIN"
],
"customizations": {
"vscode": {
"extensions": [
@ -19,15 +24,13 @@
}
}
},
"postStartCommand": "poetry install",
"postStartCommand": "./entrypoint.sh",
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [6080],
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
"ghcr.io/devcontainers/features/desktop-lite:1": {}
}
"forwardPorts": [
5900
]
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "pip3 install --user -r requirements.txt",
// Configure tool-specific properties.

View File

@ -31,6 +31,13 @@ jobs:
# with sigstore/fulcio when running outside of PRs.
id-token: write
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- name: Checkout repository
uses: actions/checkout@v4
@ -81,13 +88,19 @@ jobs:
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Upload error logs
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: error-screenshots
path: ./errors/*
# Sign the resulting Docker image digest except on PRs.
# This will only write to the public Rekor transparency log when the Docker
# repository is public to avoid leaking data. If you would like to publish

View File

@ -1,4 +1,37 @@
FROM python:3.12
FROM python:3.12-alpine
# Inspired by https://github.com/Hudrolax/uc-docker-alpine/
# Install build dependencies
RUN apk update && apk upgrade && \
apk add --no-cache --virtual .build-deps \
alpine-sdk \
curl \
wget \
unzip \
gnupg
# Install dependencies
RUN apk add --no-cache \
xvfb \
x11vnc \
fluxbox \
xterm \
libffi-dev \
openssl-dev \
zlib-dev \
bzip2-dev \
readline-dev \
git \
nss \
freetype \
freetype-dev \
harfbuzz \
ca-certificates \
ttf-freefont \
pipx \
chromium \
chromium-chromedriver
WORKDIR /app
EXPOSE 8191
@ -11,26 +44,15 @@ ENV \
PYTHONDONTWRITEBYTECODE=1 \
# do not ask any interactive question
POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_IN_PROJECT=true
POETRY_VIRTUALENVS_IN_PROJECT=true \
DISPLAY=:0
RUN apt update && apt upgrade -y
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
RUN apt install -y --no-install-recommends --no-install-suggests ./google-chrome-stable_current_amd64.deb && rm ./google-chrome-stable_current_amd64.deb
RUN apt install pipx -y
RUN pipx ensurepath
RUN pipx install poetry
ENV PATH="/root/.local/bin:$PATH"
COPY pyproject.toml poetry.lock ./
RUN poetry install
ENV INSTALL_NOVNC=false
COPY novnc.sh .
RUN ./novnc.sh
ENV DISPLAY=:1.0
COPY fix_nodriver.py ./
RUN . /app/.venv/bin/activate && python fix_nodriver.py
COPY . .
RUN /usr/local/share/desktop-init.sh && poetry run pytest
CMD /usr/local/share/desktop-init.sh && . /app/.venv/bin/activate && python main.py
CMD ["./entrypoint.sh", "&&", ".", "/app/.venv/bin/activate", "&&", "python", "main.py"]

View File

@ -4,7 +4,6 @@ An alternative to [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) a
> [!IMPORTANT]
> Currenly, due to [bug in nodriver](https://github.com/ultrafunkamsterdam/undetected-chromedriver/issues/1954), if you want to run this project ouside of prebuild container, you have to run `python fix_nodriver.py` after creating venv to patch the library.
> [!NOTE]
> Thanks to FastAPI implementation, now you can also see the API documentation at `/docs` or `/` (redirect to `/docs`) endpoints.
@ -15,14 +14,16 @@ Long story short, I created it in like 3 days, so if you get any bugs/hangs etc.
I focus maily on Cloudflare, which is tested daily, any other anti-bot challenges should pass out of the box, but if any issues, please report these providers with an example website ❤️
## Troubleshooting
1. Clone repo to the host that has the container has issues on.
2. Using vscode and `SSH extention`, connect to the host and open repo in it.
3. Download `devcontainers` extention and reopen repo in container (with `CTRL + SHIFT + P` -> `Reopen in devcontainer`)
4. Open forwarded port from `Ports` tab in your browser to see emulated display
5. Check if `chrome` works by running in VNCs terminal command `chrome --no-sandbox`
6. If chrome works, run API by pressing F5 in vscode
7. In Prowlarr (or target client) change port byparrs port to `8181` instead of `8191` (Port opened by and pointing to devcontainer)
8. Check if everything works by testing byparr and observing VNC in browser
4. Write down the port in `Ports` tab in vscode
5. Open <https://novnc.com/noVNC/vnc.html> in your pc's browser and using settings on left under websocket, set host to `localhost` nad port to the port you wrote down
6. Check if `chromium` works by running in VNCs terminal command `chromium --no-sandbox`
7. If chrome works, run API by pressing F5 in vscode
8. In Prowlarr (or target client) change port byparr's port to `8181` instead of `8191` (Port opened by and pointing to devcontainer)
9. Check if everything works by testing byparr and observing VNC in browser
## Usage

15
entrypoint.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
rm -f /tmp/.X0-lock
# Run Xvfb on dispaly 0.
Xvfb :0 -screen 0 1280x720x16 &
# Run fluxbox windows manager on display 0.
fluxbox -display :0 &
# Run x11vnc on display 0
x11vnc -display :0 -forever -ncache 10 &
# Add delay
sleep 5

View File

@ -5,11 +5,13 @@ from __future__ import annotations
import logging
import os
from pathlib import Path
from platform import python_version
env_path = os.getenv("VIRTUAL_ENV")
if env_path is None:
env_path = Path(os.__file__).parent.parent.parent.as_posix()
nodriver_path = Path(env_path + "/lib/python3.12/site-packages/nodriver/cdp/network.py")
python_version = python_version().split(".")[0:2]
nodriver_path = Path(env_path + f"/lib/python{'.'.join(python_version)}/site-packages/nodriver/cdp/network.py")
if not nodriver_path.exists():
msg = f"{nodriver_path} not found"
raise FileNotFoundError(msg)

10
main.py
View File

@ -6,7 +6,7 @@ import time
import uvicorn
import uvicorn.config
from fastapi import FastAPI
from fastapi import FastAPI, HTTPException
from fastapi.responses import RedirectResponse
from src.models.requests import LinkRequest, LinkResponse
@ -36,8 +36,12 @@ async def read_item(request: LinkRequest):
timeout = request.maxTimeout
if timeout == 0:
timeout = None
challenged = await asyncio.wait_for(bypass_cloudflare(page), timeout=timeout)
try:
challenged = await asyncio.wait_for(bypass_cloudflare(page), timeout=timeout)
except Exception as e:
logger.error(await page.get_content())
logger.fatal("Element is a string, please report this to Byparr dev")
raise HTTPException(detail="Couldn't bypass", status_code=408) from e
logger.info(f"Got webpage: {request.url}")

429
novnc.sh
View File

@ -1,429 +0,0 @@
#!/usr/bin/env bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/desktop-lite.md
# Maintainer: The VS Code and Codespaces Teams
NOVNC_VERSION="${NOVNCVERSION:-"1.2.0"}" # TODO: Add in a 'latest' auto-detect and swap name to 'version'
VNC_PASSWORD=${PASSWORD:-"vscode"}
if [ "$VNC_PASSWORD" = "noPassword" ]; then
unset VNC_PASSWORD
fi
NOVNC_PORT="${WEBPORT:-6080}"
VNC_PORT="${VNCPORT:-5901}"
INSTALL_NOVNC="${INSTALL_NOVNC:-"true"}"
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
WEBSOCKETIFY_VERSION=0.10.0
package_list="
tigervnc-standalone-server \
tigervnc-common \
fluxbox \
dbus-x11 \
x11-utils \
x11-xserver-utils \
xdg-utils \
fbautostart \
at-spi2-core \
eterm \
gnome-keyring \
libx11-dev \
libxkbfile-dev \
libsecret-1-dev \
libgbm-dev \
libnotify4 \
libnss3 \
libxss1 \
fonts-wqy-microhei \
fonts-droid-fallback \
htop \
ncdu \
curl \
ca-certificates\
unzip \
nano \
locales"
# Packages to attempt to install if essential tools are missing (ie: vncpasswd).
# This is useful, at least, for Ubuntu 22.04 (jammy)
package_list_additional="
tigervnc-tools"
set -e
# Clean up
rm -rf /var/lib/apt/lists/*
if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi
# Determine the appropriate non-root user
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
USERNAME=""
POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
if id -u ${CURRENT_USER} > /dev/null 2>&1; then
USERNAME=${CURRENT_USER}
break
fi
done
if [ "${USERNAME}" = "" ]; then
USERNAME=root
fi
elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root
fi
# Add default Fluxbox config files if none are already present
fluxbox_apps="$(cat \
<< 'EOF'
[transient] (role=GtkFileChooserDialog)
[Dimensions] {70% 70%}
[Position] (CENTER) {0 0}
[end]
EOF
)"
fluxbox_init="$(cat \
<< 'EOF'
session.configVersion: 13
session.menuFile: ~/.fluxbox/menu
session.keyFile: ~/.fluxbox/keys
session.styleFile: /usr/share/fluxbox/styles/qnx-photon
session.screen0.workspaces: 1
session.screen0.workspacewarping: false
session.screen0.toolbar.widthPercent: 100
session.screen0.strftimeFormat: %a %l:%M %p
session.screen0.toolbar.tools: RootMenu, clock, iconbar, systemtray
session.screen0.workspaceNames: One,
EOF
)"
fluxbox_menu="$(cat \
<< 'EOF'
[begin] ( Application Menu )
[exec] (File Manager) { nautilus ~ } <>
[exec] (Text Editor) { mousepad } <>
[exec] (Terminal) { tilix -w ~ -e $(readlink -f /proc/$$/exe) -il } <>
[exec] (Web Browser) { x-www-browser --disable-dev-shm-usage } <>
[submenu] (System) {}
[exec] (Set Resolution) { tilix -t "Set Resolution" -e bash /usr/local/bin/set-resolution } <>
[exec] (Edit Application Menu) { mousepad ~/.fluxbox/menu } <>
[exec] (Passwords and Keys) { seahorse } <>
[exec] (Top Processes) { tilix -t "Top" -e htop } <>
[exec] (Disk Utilization) { tilix -t "Disk Utilization" -e ncdu / } <>
[exec] (Editres) {editres} <>
[exec] (Xfontsel) {xfontsel} <>
[exec] (Xkill) {xkill} <>
[exec] (Xrefresh) {xrefresh} <>
[end]
[config] (Configuration)
[workspaces] (Workspaces)
[end]
EOF
)"
# Copy config files if the don't already exist
copy_fluxbox_config() {
local target_dir="$1"
mkdir -p "${target_dir}/.fluxbox"
touch "${target_dir}/.Xmodmap"
if [ ! -e "${target_dir}/.fluxbox/apps" ]; then
echo "${fluxbox_apps}" > "${target_dir}/.fluxbox/apps"
fi
if [ ! -e "${target_dir}/.fluxbox/init" ]; then
echo "${fluxbox_init}" > "${target_dir}/.fluxbox/init"
fi
if [ ! -e "${target_dir}/.fluxbox/menu" ]; then
echo "${fluxbox_menu}" > "${target_dir}/.fluxbox/menu"
fi
}
apt_get_update()
{
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..."
apt-get update -y
fi
}
# Checks if packages are installed and installs them if not
check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update
apt-get -y install --no-install-recommends "$@"
fi
}
##########################
# Install starts here #
##########################
# Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive
apt_get_update
# On older Ubuntu, Tilix is in a PPA. on Debian stretch its in backports.
# if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then
# . /etc/os-release
# if [ "${ID}" = "ubuntu" ]; then
# check_packages apt-transport-https software-properties-common
# add-apt-repository -y ppa:webupd8team/terminix
# elif [ "${VERSION_CODENAME}" = "stretch" ]; then
# echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/stretch-backports.list
# fi
# apt-get update
# if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then
# echo "(!) WARNING: Tilix not available on ${ID} ${VERSION_CODENAME} architecture $(uname -m). Skipping."
# else
# package_list="${package_list} tilix"
# fi
# else
# package_list="${package_list} tilix"
# fi
# Install X11, fluxbox and VS Code dependencies
check_packages ${package_list}
# if Ubuntu-24.04, noble(numbat) found, then will install libasound2-dev instead of libasound2.
# this change is temporary, https://packages.ubuntu.com/noble/libasound2 will switch to libasound2 once it is available for Ubuntu-24.04, noble(numbat)
# . /etc/os-release
# if [ "${ID}" = "ubuntu" ] && [ "${VERSION_CODENAME}" = "noble" ]; then
# echo "Ubuntu 24.04, Noble(Numbat) detected. Installing libasound2-dev package..."
# check_packages "libasound2-dev"
# else
# check_packages "libasound2"
# fi
# On newer versions of Ubuntu (22.04),
# we need an additional package that isn't provided in earlier versions
if ! type vncpasswd > /dev/null 2>&1; then
check_packages ${package_list_additional}
fi
# Install Emoji font if available in distro - Available in Debian 10+, Ubuntu 18.04+
# if dpkg-query -W fonts-noto-color-emoji > /dev/null 2>&1 && ! dpkg -s fonts-noto-color-emoji > /dev/null 2>&1; then
# apt-get -y install --no-install-recommends fonts-noto-color-emoji
# fi
# Check at least one locale exists
if ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
fi
# Install the Cascadia Code fonts - https://github.com/microsoft/cascadia-code
# if [ ! -d "/usr/share/fonts/truetype/cascadia" ]; then
# curl -sSL https://github.com/microsoft/cascadia-code/releases/download/v2008.25/CascadiaCode-2008.25.zip -o /tmp/cascadia-fonts.zip
# unzip /tmp/cascadia-fonts.zip -d /tmp/cascadia-fonts
# mkdir -p /usr/share/fonts/truetype/cascadia
# mv /tmp/cascadia-fonts/ttf/* /usr/share/fonts/truetype/cascadia/
# rm -rf /tmp/cascadia-fonts.zip /tmp/cascadia-fonts
# fi
# Install noVNC
if [ "${INSTALL_NOVNC}" = "true" ] && [ ! -d "/usr/local/novnc" ]; then
mkdir -p /usr/local/novnc
curl -sSL https://github.com/novnc/noVNC/archive/v${NOVNC_VERSION}.zip -o /tmp/novnc-install.zip
unzip /tmp/novnc-install.zip -d /usr/local/novnc
cp /usr/local/novnc/noVNC-${NOVNC_VERSION}/vnc.html /usr/local/novnc/noVNC-${NOVNC_VERSION}/index.html
curl -sSL https://github.com/novnc/websockify/archive/v${WEBSOCKETIFY_VERSION}.zip -o /tmp/websockify-install.zip
unzip /tmp/websockify-install.zip -d /usr/local/novnc
ln -s /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION} /usr/local/novnc/noVNC-${NOVNC_VERSION}/utils/websockify
rm -f /tmp/websockify-install.zip /tmp/novnc-install.zip
# Install noVNC dependencies and use them.
check_packages python3-minimal python3-numpy
sed -i -E 's/^python /python3 /' /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION}/run
fi
# Set up folders for scripts and init files
mkdir -p /var/run/dbus /usr/local/etc/vscode-dev-containers/
# Script to change resolution of desktop
cat << EOF > /usr/local/bin/set-resolution
#!/bin/bash
RESOLUTION=\${1:-\${VNC_RESOLUTION:-1920x1080}}
DPI=\${2:-\${VNC_DPI:-96}}
IGNORE_ERROR=\${3:-"false"}
if [ -z "\$1" ]; then
echo -e "**Current Settings **\n"
xrandr
echo -n -e "\nEnter new resolution (WIDTHxHEIGHT, blank for \${RESOLUTION}, Ctrl+C to abort).\n> "
read NEW_RES
if [ "\${NEW_RES}" != "" ]; then
RESOLUTION=\${NEW_RES}
fi
if ! echo "\${RESOLUTION}" | grep -E '[0-9]+x[0-9]+' > /dev/null; then
echo -e "\nInvalid resolution format!\n"
exit 1
fi
if [ -z "\$2" ]; then
echo -n -e "\nEnter new DPI (blank for \${DPI}, Ctrl+C to abort).\n> "
read NEW_DPI
if [ "\${NEW_DPI}" != "" ]; then
DPI=\${NEW_DPI}
fi
fi
fi
xrandr --fb \${RESOLUTION} --dpi \${DPI} > /dev/null 2>&1
if [ \$? -ne 0 ] && [ "\${IGNORE_ERROR}" != "true" ]; then
echo -e "\nFAILED TO SET RESOLUTION!\n"
exit 1
fi
echo -e "\nSuccess!\n"
EOF
# Container ENTRYPOINT script
cat << EOF > /usr/local/share/desktop-init.sh
#!/bin/bash
user_name="${USERNAME}"
group_name="$(id -gn ${USERNAME})"
LOG=/tmp/container-init.log
export DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-"autolaunch:"}"
export DISPLAY="${DISPLAY:-:1}"
export VNC_RESOLUTION="${VNC_RESOLUTION:-1440x768x16}"
export LANG="${LANG:-"en_US.UTF-8"}"
export LANGUAGE="${LANGUAGE:-"en_US.UTF-8"}"
# Execute the command it not already running
startInBackgroundIfNotRunning()
{
log "Starting \$1."
echo -e "\n** \$(date) **" | sudoIf tee -a /tmp/\$1.log > /dev/null
if ! pgrep -x \$1 > /dev/null; then
keepRunningInBackground "\$@"
while ! pgrep -x \$1 > /dev/null; do
sleep 1
done
log "\$1 started."
else
echo "\$1 is already running." | sudoIf tee -a /tmp/\$1.log > /dev/null
log "\$1 is already running."
fi
}
# Keep command running in background
keepRunningInBackground()
{
(\$2 bash -c "while :; do echo [\\\$(date)] Process started.; \$3; echo [\\\$(date)] Process exited!; sleep 5; done 2>&1" | sudoIf tee -a /tmp/\$1.log > /dev/null & echo "\$!" | sudoIf tee /tmp/\$1.pid > /dev/null)
}
# Use sudo to run as root when required
sudoIf()
{
if [ "\$(id -u)" -ne 0 ]; then
sudo "\$@"
else
"\$@"
fi
}
# Use sudo to run as non-root user if not already running
sudoUserIf()
{
if [ "\$(id -u)" -eq 0 ] && [ "\${user_name}" != "root" ]; then
sudo -u \${user_name} "\$@"
else
"\$@"
fi
}
# Log messages
log()
{
echo -e "[\$(date)] \$@" | sudoIf tee -a \$LOG > /dev/null
}
log "** SCRIPT START **"
# Start dbus.
log 'Running "/etc/init.d/dbus start".'
if [ -f "/var/run/dbus/pid" ] && ! pgrep -x dbus-daemon > /dev/null; then
sudoIf rm -f /var/run/dbus/pid
fi
sudoIf /etc/init.d/dbus start 2>&1 | sudoIf tee -a /tmp/dbus-daemon-system.log > /dev/null
while ! pgrep -x dbus-daemon > /dev/null; do
sleep 1
done
# Startup tigervnc server and fluxbox
sudoIf rm -rf /tmp/.X11-unix /tmp/.X*-lock
mkdir -p /tmp/.X11-unix
sudoIf chmod 1777 /tmp/.X11-unix
sudoIf chown root:\${group_name} /tmp/.X11-unix
if [ "\$(echo "\${VNC_RESOLUTION}" | tr -cd 'x' | wc -c)" = "1" ]; then VNC_RESOLUTION=\${VNC_RESOLUTION}x16; fi
screen_geometry="\${VNC_RESOLUTION%*x*}"
screen_depth="\${VNC_RESOLUTION##*x}"
# Check if VNC_PASSWORD is set and use the appropriate command
common_options="tigervncserver \${DISPLAY} -geometry \${screen_geometry} -depth \${screen_depth} -rfbport ${VNC_PORT} -dpi \${VNC_DPI:-96} -localhost -desktop fluxbox -fg"
if [ -n "\${VNC_PASSWORD+x}" ]; then
startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "\${common_options} -passwd /usr/local/etc/vscode-dev-containers/vnc-passwd"
else
startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "\${common_options} -SecurityTypes None"
fi
# Spin up noVNC if installed and not running.
if [ -d "/usr/local/novnc" ] && [ "\$(ps -ef | grep /usr/local/novnc/noVNC*/utils/launch.sh | grep -v grep)" = "" ]; then
keepRunningInBackground "noVNC" sudoIf "/usr/local/novnc/noVNC*/utils/launch.sh --listen ${NOVNC_PORT} --vnc localhost:${VNC_PORT}"
log "noVNC started."
else
log "noVNC is already running or not installed."
fi
# Run whatever was passed in
log "Executing \"\$@\"."
exec "\$@"
log "** SCRIPT EXIT **"
EOF
if [ -n "${VNC_PASSWORD+x}" ]; then
echo "${VNC_PASSWORD}" | vncpasswd -f > /usr/local/etc/vscode-dev-containers/vnc-passwd
fi
chmod +x /usr/local/share/desktop-init.sh /usr/local/bin/set-resolution
# Set up fluxbox config
copy_fluxbox_config "/root"
if [ "${USERNAME}" != "root" ]; then
copy_fluxbox_config "/home/${USERNAME}"
chown -R ${USERNAME} /home/${USERNAME}/.Xmodmap /home/${USERNAME}/.fluxbox
fi
# Clean up
rm -rf /var/lib/apt/lists/*
# Determine the message based on whether VNC_PASSWORD is set
if [ -n "${VNC_PASSWORD+x}" ]; then
PASSWORD_MESSAGE="In both cases, use the password \"${VNC_PASSWORD}\" when connecting"
else
PASSWORD_MESSAGE="In both cases, no password is required."
fi
# Display the message
cat << EOF
You now have a working desktop! Connect to in one of the following ways:
- Forward port ${NOVNC_PORT} and use a web browser to start the noVNC client (recommended)
- Forward port ${VNC_PORT} using VS Code client and connect using a VNC Viewer
${PASSWORD_MESSAGE}
(*) Done!
EOF

912
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -48,3 +48,6 @@ ignore = [
]
select = ["ALL"]
extend-safe-fixes = ["D415"]
[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function"

3
pytest.sh Executable file
View File

@ -0,0 +1,3 @@
if [ $(arch) = "x86_64" ]; then
./entrypoint.sh && . ./.venv/bin/activate && poetry run pytest
fi

View File

@ -23,8 +23,9 @@ async def new_browser():
Any exceptions that may occur during the creation of the browser instance.
"""
config: webdriver.Config = webdriver.Config()
config.sandbox = False
config: webdriver.Config = webdriver.Config(
browser_executable_path="/usr/bin/chromium", sandbox=True
)
config.add_argument(f"--load-extension={','.join(downloaded_extentions)}")
return await webdriver.start(config=config)
@ -65,24 +66,26 @@ async def bypass_cloudflare(page: webdriver.Tab):
if not challenged:
logger.info("Found challenge")
challenged = True
loaded = False
try:
elem = await page.find(
"Verify you are human by completing the action below.",
timeout=3,
)
# If challenge solves by itself
elem = await page.find("lds-ring", timeout=3)
parent = elem.parent
if not isinstance(parent, Element) or parent.attributes is None:
continue
for attr in parent.attributes:
if attr == "display: none; visibility: hidden;":
loaded = True
except asyncio.TimeoutError:
if page.target.title not in CHALLENGE_TITLES:
return challenged
if elem is None:
logger.debug("Couldn't find the title, trying other method...")
continue
if not isinstance(elem, Element):
logger.fatal("Element is a string, please report this to Byparr dev")
raise InvalidElementError
logger.debug("Challenge loaded")
else:
if not loaded:
logger.debug("Challenge still loading")
continue
await page
logger.debug("Couldn't find the title, trying other method...")
elem = await page.find("input")
elem = elem.parent
# Get the element containing the shadow root
@ -92,10 +95,12 @@ async def bypass_cloudflare(page: webdriver.Tab):
if isinstance(inner_elem, Element):
logger.debug("Clicking element")
await inner_elem.mouse_click()
await asyncio.sleep(3)
else:
logger.warning(
"Element is a string, please report this to Byparr dev"
) # I really hope this never happens
logger.warning(inner_elem)
else:
logger.warning("Coulnd't find checkbox, trying again...")

View File

@ -77,7 +77,12 @@ def download_extentions():
logger.error(f"Error downloading {extention_name}, using local copy")
downloaded_extentions.append(path.as_posix())
continue
zip_file = requests.get(extention.browser_download_url, timeout=10)
try:
zip_file = requests.get(extention.browser_download_url, timeout=10)
except UnboundLocalError as e:
logger.error(f"Error downloading {extention_name}, skipping")
logger.error(e)
continue
Path(EXTENTIONS_PATH).mkdir(exist_ok=True)
with ZipFile(io.BytesIO(zip_file.content)) as zip_obj:
zip_obj.extractall(f"{EXTENTIONS_PATH}/{extention_name}")

View File

@ -1,4 +1,5 @@
from http import HTTPStatus
from time import sleep
import pytest
from starlette.testclient import TestClient
@ -11,17 +12,20 @@ client = TestClient(app)
test_websites = [
"https://ext.to/",
"https://btmet.com/",
# "https://extratorrent.st/", # github is blocking these
# "https://idope.se/", # github is blocking these
"https://extratorrent.st/", # github is blocking these
"https://idope.se/", # github is blocking these
"https://www.ygg.re/",
]
@pytest.mark.parametrize("website", test_websites)
def test_bypass(website: str):
sleep(3)
response = client.post(
"/v1",
json=LinkRequest(
url=website, maxTimeout=60 * len(test_websites), cmd="request.get"
).model_dump(),
json=LinkRequest(url=website, maxTimeout=30, cmd="request.get").model_dump(),
)
if response.status_code == HTTPStatus.TOO_MANY_REQUESTS:
# if rate limited
assert True
assert response.status_code == HTTPStatus.OK