Compare commits

...

42 Commits

Author SHA1 Message Date
ilike2burnthing
6faab19533 Bump version 3.4.0 (#1564) 2025-08-25 04:21:44 +01:00
ilike2burnthing
af0a7af757 Remove disable software rasterizer option for ARM builds 2025-08-25 04:17:22 +01:00
Alex Naidis
ff74b50b60 Modernize and upgrade application (#1540)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-08-25 03:55:06 +01:00
ilike2burnthing
3e51ac1188 Update README.md 2025-06-26 05:30:44 +01:00
ilike2burnthing
6627de4fa6 Bump version 3.3.25 (#1523) 2025-06-14 03:52:28 +01:00
ilike2burnthing
fe649255f2 Revert "Fix Chrome GL erros in ASUSTOR NAS"
This reverts commit 8316350b98.
2025-06-14 03:42:08 +01:00
dependabot[bot]
3e338fce2e Bump requests from 2.32.3 to 2.32.4 (#1516)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-11 02:48:00 +01:00
ilike2burnthing
3dd3e7559d u_c: remove apparent c&p typo
https://github.com/ultrafunkamsterdam/undetected-chromedriver/pull/1933
2025-06-06 08:04:00 +01:00
ilike2burnthing
f21c1d51bc Restore example service file. #1204 2025-06-04 23:13:00 +01:00
ilike2burnthing
957347f73a Bump version 3.3.24 (#1505) 2025-06-04 19:02:06 +01:00
ilike2burnthing
c55080b0ec Remove hidden character 2025-06-04 18:54:48 +01:00
ilike2burnthing
639bfca020 Bump version 3.3.23 (#1504) 2025-06-04 18:51:31 +01:00
ilike2burnthing
237694df76 Update base image to bookworm. resolves #1503 2025-06-04 18:44:17 +01:00
ilike2burnthing
6e5d6f1795 Bump version 3.3.22 (#1500) 2025-06-03 05:54:25 +01:00
ilike2burnthing
30804a86e5 Bump Chromium to v137 for build 2025-06-03 05:39:46 +01:00
ilike2burnthing
e0bdaf7745 Don't open devtools 2025-06-03 05:38:55 +01:00
ilike2burnthing
795365dbe4 Change from click to keys
credit to @sh4dowb - #1497
2025-06-03 05:38:06 +01:00
ilike2burnthing
ce5369dd41 Update bug_report.yml
fix accidental delete
2025-03-04 02:05:23 +00:00
ilike2burnthing
600b09d498 Update bug_report.yml 2025-03-04 02:04:29 +00:00
ilike2burnthing
d1f19405a1 Update README.md. closes #1267 2025-01-21 20:27:36 +00:00
ilike2burnthing
82a1366d34 Remove dead directory link from readme. resolve #1436 2025-01-20 17:37:14 +00:00
dependabot[bot]
a2fe9e7776 Bump waitress from 2.1.2 to 3.0.1 (#1418)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-29 01:53:34 +00:00
ilike2burnthing
6cc628df9e Disable search engine choice screen 2024-11-24 18:31:50 +00:00
Eduard Tykhoniuk
8b1851eeb1 Fix headless=false stalling 2024-11-24 18:30:35 +00:00
ilike2burnthing
54668a11e7 bug_report: lint fix again 2024-09-28 08:08:49 +01:00
ilike2burnthing
701d8fb4ff bug_report: change to input 2024-09-28 08:07:56 +01:00
ilike2burnthing
39a265ccb8 bug_report: lint fix 2024-09-28 07:58:14 +01:00
ilike2burnthing
e32b247014 bug_report: default=0 2024-09-28 07:57:11 +01:00
ilike2burnthing
0d8fe8fe50 bug_report: no booleans allowed 2024-09-28 07:51:57 +01:00
ilike2burnthing
718da3a36f bug_report: add 'no really' drop down
maybe this will help... 🙄
2024-09-28 07:50:11 +01:00
ilike2burnthing
a798561338 Bump requests version
*.0 was yanked
2024-07-30 02:38:13 +01:00
Bogdan
eb680efc90 Don't build docker images for PRs from forks (#1281) 2024-07-20 22:08:40 +03:00
ilike2burnthing
0f8f0bec25 revert and bump action version 2024-07-20 19:41:49 +01:00
ilike2burnthing
3d9bc5627b Change to GITHUB_TOKEN for GHRC login 2024-07-20 14:21:34 +01:00
ilike2burnthing
dd7eaee2e3 Bump requirements
resolves Dependabot alerts
2024-07-12 17:11:40 +01:00
ilike2burnthing
031177bbdb Bump version 3.3.21 (#1240) 2024-06-26 02:14:25 +01:00
Bogdan
a8644532a1 Escape values for generated form used in request.post (#1236)
and build docker images for PRs
2024-06-26 02:04:59 +01:00
ilike2burnthing
e96161c873 Add challenge selector to catch reloading page on non-English systems. resolves #1237 2024-06-25 22:32:06 +01:00
ilike2burnthing
5a1f25cd52 Bump version 3.3.20 (#1229) 2024-06-21 22:21:37 +01:00
tenettow
a2c0e4348e Update Cloudflare challenge and checkbox selectors (#1224) 2024-06-21 22:07:03 +01:00
ilike2burnthing
2ecf88895b Check not running in Docker before logging version_main error 2024-06-15 08:37:42 +01:00
ilike2burnthing
984368edb5 maxTimeout should always be int. resolves #1212 2024-06-15 05:41:45 +01:00
16 changed files with 199 additions and 136 deletions

View File

@@ -29,6 +29,13 @@ body:
options: options:
- label: I have read the Discussions - label: I have read the Discussions
required: true required: true
- type: input
attributes:
label: Have you ACTUALLY checked all these?
description: Please do not waste our time and yours; these checks are there for a reason, it is not just so you can tick boxes for fun. If you type <b>YES</b> and it is clear you did not or have put in no effort, your issue will be closed and locked without comment. If you type <b>NO</b> but still open this issue, you will be permanently blocked for timewasting.
placeholder: YES or NO
validations:
required: true
- type: textarea - type: textarea
attributes: attributes:
label: Environment label: Environment

View File

@@ -11,7 +11,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- -
name: Auto Tag name: Auto Tag
uses: Klemensas/action-autotag@stable uses: Klemensas/action-autotag@stable

View File

@@ -3,51 +3,65 @@ name: release-docker
on: on:
push: push:
tags: tags:
- 'v*.*.*' - "v*.*.*"
pull_request:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
build-docker-images: build-docker-images:
runs-on: ubuntu-22.04 if: ${{ !github.event.pull_request.head.repo.fork }}
runs-on: ubuntu-24.04
steps: steps:
- - name: Checkout
name: Checkout uses: actions/checkout@v4
uses: actions/checkout@v3
- - name: Downcase repo
name: Downcase repo
run: echo REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV run: echo REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
-
name: Docker meta - name: Docker meta
id: docker_meta id: docker_meta
uses: crazy-max/ghaction-docker-meta@v3 uses: docker/metadata-action@v5
with: with:
images: ${{ env.REPOSITORY }},ghcr.io/${{ env.REPOSITORY }} images: |
tag-sha: false ${{ env.REPOSITORY }},enable=${{ github.event_name != 'pull_request' }}
- ghcr.io/${{ env.REPOSITORY }}
name: Set up QEMU tags: |
uses: docker/setup-qemu-action@v2 type=semver,pattern={{version}},prefix=v
- type=ref,event=pr
name: Set up Docker Buildx flavor: |
uses: docker/setup-buildx-action@v2 latest=auto
-
name: Login to DockerHub - name: Set up QEMU
uses: docker/login-action@v2 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GH_PAT }} password: ${{ secrets.GH_PAT }}
-
name: Build and push - name: Build and push
uses: docker/build-push-action@v3 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
platforms: linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 platforms: linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8
push: ${{ github.event_name != 'pull_request' }} push: true
tags: ${{ steps.docker_meta.outputs.tags }} tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }} labels: ${{ steps.docker_meta.outputs.labels }}

View File

@@ -3,15 +3,15 @@ name: release
on: on:
push: push:
tags: tags:
- 'v*.*.*' - "v*.*.*"
jobs: jobs:
create-release: create-release:
name: Create release name: Create release
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 # get all commits, branches and tags (required for the changelog) fetch-depth: 0 # get all commits, branches and tags (required for the changelog)
@@ -39,27 +39,27 @@ jobs:
build-linux-package: build-linux-package:
name: Build Linux binary name: Build Linux binary
needs: create-release needs: create-release
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 # get all commits, branches and tags (required for the changelog) fetch-depth: 0 # get all commits, branches and tags (required for the changelog)
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.11' python-version: "3.13"
- name: Build artifacts - name: Build artifacts
run: | run: |
python -m pip install -r requirements.txt python -m pip install -r requirements.txt
python -m pip install pyinstaller==5.13.0 python -m pip install pyinstaller==6.14.2
cd src cd src
python build_package.py python build_package.py
- name: Upload release artifacts - name: Upload release artifacts
uses: alexellis/upload-assets@0.4.0 uses: alexellis/upload-assets@0.4.1
env: env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }} GITHUB_TOKEN: ${{ secrets.GH_PAT }}
with: with:
@@ -71,24 +71,24 @@ jobs:
runs-on: windows-2022 runs-on: windows-2022
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
fetch-depth: 0 # get all commits, branches and tags (required for the changelog) fetch-depth: 0 # get all commits, branches and tags (required for the changelog)
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.11' python-version: "3.13"
- name: Build artifacts - name: Build artifacts
run: | run: |
python -m pip install -r requirements.txt python -m pip install -r requirements.txt
python -m pip install pyinstaller==5.13.0 python -m pip install pyinstaller==6.14.2
cd src cd src
python build_package.py python build_package.py
- name: Upload release artifacts - name: Upload release artifacts
uses: alexellis/upload-assets@0.4.0 uses: alexellis/upload-assets@0.4.1
env: env:
GITHUB_TOKEN: ${{ secrets.GH_PAT }} GITHUB_TOKEN: ${{ secrets.GH_PAT }}
with: with:

View File

@@ -1,5 +1,37 @@
# Changelog # Changelog
## v3.4.0 (2025/08/25)
* Modernize and upgrade application. Thanks @TheCrazyLex
* Remove disable software rasterizer option for ARM builds. Thanks @smrodman83
## v3.3.25 (2025/06/14)
* Remove `use-gl` argument. Thanks @qwerty12
* u_c: remove apparent c&p typo. Thanks @ok3721
* Bump requirements
## v3.3.24 (2025/06/04)
* Remove hidden character
## v3.3.23 (2025/06/04)
* Update base image to bookworm. Thanks @rwjack
## v3.3.22 (2025/06/03)
* Disable search engine choice screen
* Fix headless=false stalling. Thanks @MAKMED1337
* Change from click to keys. Thanks @sh4dowb
* Don't open devtools
* Bump Chromium to v137 for build
* Bump requirements
## v3.3.21 (2024/06/26)
* Add challenge selector to catch reloading page on non-English systems
* Escape values for generated form used in request.post. Thanks @mynameisbogdan
## v3.3.20 (2024/06/21)
* maxTimeout should always be int
* Check not running in Docker before logging version_main error
* Update Cloudflare challenge and checkbox selectors. Thanks @tenettow & @21hsmw
## v3.3.19 (2024/05/23) ## v3.3.19 (2024/05/23)
* Fix occasional headless issue on Linux when set to "false". Thanks @21hsmw * Fix occasional headless issue on Linux when set to "false". Thanks @21hsmw

View File

@@ -1,4 +1,4 @@
FROM python:3.11-slim-bullseye as builder FROM python:3.13-slim-bookworm as builder
# Build dummy packages to skip installing them and their dependencies # Build dummy packages to skip installing them and their dependencies
RUN apt-get update \ RUN apt-get update \
@@ -12,7 +12,7 @@ RUN apt-get update \
&& equivs-build adwaita-icon-theme \ && equivs-build adwaita-icon-theme \
&& mv adwaita-icon-theme_*.deb /adwaita-icon-theme.deb && mv adwaita-icon-theme_*.deb /adwaita-icon-theme.deb
FROM python:3.11-slim-bullseye FROM python:3.13-slim-bookworm
# Copy dummy packages # Copy dummy packages
COPY --from=builder /*.deb / COPY --from=builder /*.deb /
@@ -62,17 +62,17 @@ ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/usr/local/bin/python", "-u", "/app/flaresolverr.py"] CMD ["/usr/local/bin/python", "-u", "/app/flaresolverr.py"]
# Local build # Local build
# docker build -t ngosang/flaresolverr:3.3.19 . # docker build -t ngosang/flaresolverr:3.4.0 .
# docker run -p 8191:8191 ngosang/flaresolverr:3.3.19 # docker run -p 8191:8191 ngosang/flaresolverr:3.4.0
# Multi-arch build # Multi-arch build
# docker run --rm --privileged multiarch/qemu-user-static --reset -p yes # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# docker buildx create --use # docker buildx create --use
# docker buildx build -t ngosang/flaresolverr:3.3.19 --platform linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 . # docker buildx build -t ngosang/flaresolverr:3.4.0 --platform linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 .
# add --push to publish in DockerHub # add --push to publish in DockerHub
# Test multi-arch build # Test multi-arch build
# docker run --rm --privileged multiarch/qemu-user-static --reset -p yes # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# docker buildx create --use # docker buildx create --use
# docker buildx build -t ngosang/flaresolverr:3.3.19 --platform linux/arm/v7 --load . # docker buildx build -t ngosang/flaresolverr:3.4.0 --platform linux/arm/v7 --load .
# docker run -p 8191:8191 --platform linux/arm/v7 ngosang/flaresolverr:3.3.19 # docker run -p 8191:8191 --platform linux/arm/v7 ngosang/flaresolverr:3.4.0

View File

@@ -77,7 +77,7 @@ This is the recommended way for Windows users.
> **Warning** > **Warning**
> Installing from source code only works for x64 architecture. For other architectures see Docker images. > Installing from source code only works for x64 architecture. For other architectures see Docker images.
* Install [Python 3.11](https://www.python.org/downloads/). * Install [Python 3.13](https://www.python.org/downloads/).
* Install [Chrome](https://www.google.com/intl/en_us/chrome/) (all OS) or [Chromium](https://www.chromium.org/getting-involved/download-chromium/) (just Linux, it doesn't work in Windows) web browser. * Install [Chrome](https://www.google.com/intl/en_us/chrome/) (all OS) or [Chromium](https://www.chromium.org/getting-involved/download-chromium/) (just Linux, it doesn't work in Windows) web browser.
* (Only in Linux) Install [Xvfb](https://en.wikipedia.org/wiki/Xvfb) package. * (Only in Linux) Install [Xvfb](https://en.wikipedia.org/wiki/Xvfb) package.
* (Only in macOS) Install [XQuartz](https://www.xquartz.org/) package. * (Only in macOS) Install [XQuartz](https://www.xquartz.org/) package.
@@ -87,10 +87,10 @@ This is the recommended way for Windows users.
### From source code (FreeBSD/TrueNAS CORE) ### From source code (FreeBSD/TrueNAS CORE)
* Run `pkg install chromium python39 py39-pip xorg-vfbserver` command to install the required dependencies. * Run `pkg install chromium python313 py313-pip xorg-vfbserver` command to install the required dependencies.
* Clone this repository and open a shell in that path. * Clone this repository and open a shell in that path.
* Run `python3.9 -m pip install -r requirements.txt` command to install FlareSolverr dependencies. * Run `python3.13 -m pip install -r requirements.txt` command to install FlareSolverr dependencies.
* Run `python3.9 src/flaresolverr.py` command to start FlareSolverr. * Run `python3.13 src/flaresolverr.py` command to start FlareSolverr.
### Systemd service ### Systemd service
@@ -269,7 +269,6 @@ This is the same as `request.get` but it takes one more param:
| TZ | UTC | Timezone used in the logs and the web browser. Example: `TZ=Europe/London`. | | TZ | UTC | Timezone used in the logs and the web browser. Example: `TZ=Europe/London`. |
| LANG | none | Language used in the web browser. Example: `LANG=en_GB`. | | LANG | none | Language used in the web browser. Example: `LANG=en_GB`. |
| HEADLESS | true | Only for debugging. To run the web browser in headless mode or visible. | | HEADLESS | true | Only for debugging. To run the web browser in headless mode or visible. |
| BROWSER_TIMEOUT | 40000 | If you are experiencing errors/timeouts because your system is slow, you can try to increase this value. Remember to increase the `maxTimeout` parameter too. |
| TEST_URL | https://www.google.com | FlareSolverr makes a request on start to make sure the web browser is working. You can change that URL if it is blocked in your country. | | TEST_URL | https://www.google.com | FlareSolverr makes a request on start to make sure the web browser is working. You can change that URL if it is blocked in your country. |
| PORT | 8191 | Listening port. You don't need to change this if you are running on Docker. | | PORT | 8191 | Listening port. You don't need to change this if you are running on Docker. |
| HOST | 0.0.0.0 | Listening interface. You don't need to change this if you are running on Docker. | | HOST | 0.0.0.0 | Listening interface. You don't need to change this if you are running on Docker. |
@@ -317,7 +316,7 @@ solve a captcha.
If this is the case, FlareSolverr will return the error `Captcha detected but no automatic solver is configured.` If this is the case, FlareSolverr will return the error `Captcha detected but no automatic solver is configured.`
FlareSolverr can be customized to solve the CAPTCHA automatically by setting the environment variable `CAPTCHA_SOLVER` FlareSolverr can be customized to solve the CAPTCHA automatically by setting the environment variable `CAPTCHA_SOLVER`
to the file name of one of the adapters inside the [/captcha](src/captcha) directory. to the file name of one of the adapters inside the `/captcha` directory.
## Related projects ## Related projects

19
flaresolverr.service Normal file
View File

@@ -0,0 +1,19 @@
[Unit]
Description=FlareSolverr
After=network.target
[Service]
SyslogIdentifier=flaresolverr
Restart=always
RestartSec=5
Type=simple
User=flaresolverr
Group=flaresolverr
Environment="LOG_LEVEL=info"
Environment="CAPTCHA_SOLVER=none"
WorkingDirectory=/opt/flaresolverr
ExecStart=/opt/flaresolverr/flaresolverr
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target

View File

@@ -1,6 +1,6 @@
{ {
"name": "flaresolverr", "name": "flaresolverr",
"version": "3.3.19", "version": "3.4.0",
"description": "Proxy server to bypass Cloudflare protection", "description": "Proxy server to bypass Cloudflare protection",
"author": "Diego Heras (ngosang / ngosang@hotmail.es)", "author": "Diego Heras (ngosang / ngosang@hotmail.es)",
"license": "MIT" "license": "MIT"

View File

@@ -1,13 +1,14 @@
bottle==0.12.25 bottle==0.13.4
waitress==2.1.2 waitress==3.0.2
selenium==4.15.2 selenium==4.34.2
func-timeout==4.3.5 func-timeout==4.3.5
prometheus-client==0.17.1 prometheus-client==0.22.1
# required by undetected_chromedriver # required by undetected_chromedriver
requests==2.31.0 requests==2.32.4
certifi==2023.7.22 certifi==2025.7.9
websockets==11.0.3 websockets==15.0.1
packaging==25.0
# only required for linux and macos # only required for linux and macos
xvfbwrapper==0.2.9; platform_system != "Windows" xvfbwrapper==0.2.13; platform_system != "Windows"
# only required for windows # only required for windows
pefile==2023.2.7; platform_system == "Windows" pefile==2024.8.26; platform_system == "Windows"

View File

@@ -25,7 +25,7 @@ def clean_files():
def download_chromium(): def download_chromium():
# https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/ # https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/
revision = "1260008" if os.name == 'nt' else '1260015' revision = "1465706" if os.name == 'nt' else '1465706'
arch = 'Win_x64' if os.name == 'nt' else 'Linux_x64' arch = 'Win_x64' if os.name == 'nt' else 'Linux_x64'
dl_file = 'chrome-win' if os.name == 'nt' else 'chrome-linux' dl_file = 'chrome-win' if os.name == 'nt' else 'chrome-linux'
dl_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, 'dist_chrome') dl_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, 'dist_chrome')

View File

@@ -3,12 +3,14 @@ import platform
import sys import sys
import time import time
from datetime import timedelta from datetime import timedelta
from urllib.parse import unquote from html import escape
from urllib.parse import unquote, quote
from func_timeout import FunctionTimedOut, func_timeout from func_timeout import FunctionTimedOut, func_timeout
from selenium.common import TimeoutException from selenium.common import TimeoutException
from selenium.webdriver.chrome.webdriver import WebDriver from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.expected_conditions import ( from selenium.webdriver.support.expected_conditions import (
presence_of_element_located, staleness_of, title_is) presence_of_element_located, staleness_of, title_is)
from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.action_chains import ActionChains
@@ -40,7 +42,7 @@ CHALLENGE_TITLES = [
] ]
CHALLENGE_SELECTORS = [ CHALLENGE_SELECTORS = [
# Cloudflare # Cloudflare
'#cf-challenge-running', '.ray_id', '.attack-box', '#cf-please-wait', '#challenge-spinner', '#trk_jschal_js', '#cf-challenge-running', '.ray_id', '.attack-box', '#cf-please-wait', '#challenge-spinner', '#trk_jschal_js', '#turnstile-wrapper', '.lds-ring',
# Custom CloudFlare for EbookParadijs, Film-Paleis, MuziekFabriek and Puur-Hollands # Custom CloudFlare for EbookParadijs, Film-Paleis, MuziekFabriek and Puur-Hollands
'td.info #js_info', 'td.info #js_info',
# Fairlane / pararius.com # Fairlane / pararius.com
@@ -119,7 +121,7 @@ def _controller_v1_handler(req: V1RequestBase) -> V1ResponseBase:
logging.warning("Request parameter 'userAgent' was removed in FlareSolverr v2.") logging.warning("Request parameter 'userAgent' was removed in FlareSolverr v2.")
# set default values # set default values
if req.maxTimeout is None or req.maxTimeout < 1: if req.maxTimeout is None or int(req.maxTimeout) < 1:
req.maxTimeout = 60000 req.maxTimeout = 60000
# execute the command # execute the command
@@ -220,7 +222,7 @@ def _cmd_sessions_destroy(req: V1RequestBase) -> V1ResponseBase:
def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT: def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT:
timeout = req.maxTimeout / 1000 timeout = int(req.maxTimeout) / 1000
driver = None driver = None
try: try:
if req.session: if req.session:
@@ -254,17 +256,8 @@ def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT:
def click_verify(driver: WebDriver): def click_verify(driver: WebDriver):
try: try:
logging.debug("Try to find the Cloudflare verify checkbox...") logging.debug("Try to find the Cloudflare verify checkbox...")
iframe = driver.find_element(By.XPATH, "//iframe[starts-with(@id, 'cf-chl-widget-')]")
driver.switch_to.frame(iframe)
checkbox = driver.find_element(
by=By.XPATH,
value='//*[@id="challenge-stage"]/div/label/input',
)
if checkbox:
actions = ActionChains(driver) actions = ActionChains(driver)
actions.move_to_element_with_offset(checkbox, 5, 7) actions.pause(5).send_keys(Keys.TAB).pause(1).send_keys(Keys.SPACE).perform()
actions.click(checkbox)
actions.perform()
logging.debug("Cloudflare verify checkbox found and clicked!") logging.debug("Cloudflare verify checkbox found and clicked!")
except Exception: except Exception:
logging.debug("Cloudflare verify checkbox not found on the page.") logging.debug("Cloudflare verify checkbox not found on the page.")
@@ -289,22 +282,6 @@ def click_verify(driver: WebDriver):
time.sleep(2) time.sleep(2)
def get_correct_window(driver: WebDriver) -> WebDriver:
if len(driver.window_handles) > 1:
for window_handle in driver.window_handles:
driver.switch_to.window(window_handle)
current_url = driver.current_url
if not current_url.startswith("devtools://devtools"):
return driver
return driver
def access_page(driver: WebDriver, url: str) -> None:
driver.get(url)
driver.start_session()
driver.start_session() # required to bypass Cloudflare
def _evil_logic(req: V1RequestBase, driver: WebDriver, method: str) -> ChallengeResolutionT: def _evil_logic(req: V1RequestBase, driver: WebDriver, method: str) -> ChallengeResolutionT:
res = ChallengeResolutionT({}) res = ChallengeResolutionT({})
res.status = STATUS_OK res.status = STATUS_OK
@@ -316,8 +293,7 @@ def _evil_logic(req: V1RequestBase, driver: WebDriver, method: str) -> Challenge
if method == 'POST': if method == 'POST':
_post_request(req, driver) _post_request(req, driver)
else: else:
access_page(driver, req.url) driver.get(req.url)
driver = get_correct_window(driver)
# set cookies if required # set cookies if required
if req.cookies is not None and len(req.cookies) > 0: if req.cookies is not None and len(req.cookies) > 0:
@@ -329,8 +305,7 @@ def _evil_logic(req: V1RequestBase, driver: WebDriver, method: str) -> Challenge
if method == 'POST': if method == 'POST':
_post_request(req, driver) _post_request(req, driver)
else: else:
access_page(driver, req.url) driver.get(req.url)
driver = get_correct_window(driver)
# wait for the page # wait for the page
if utils.get_config_log_html(): if utils.get_config_log_html():
@@ -439,7 +414,7 @@ def _post_request(req: V1RequestBase, driver: WebDriver):
value = unquote(parts[1]) value = unquote(parts[1])
except Exception: except Exception:
value = parts[1] value = parts[1]
post_form += f'<input type="text" name="{name}" value="{value}"><br>' post_form += f'<input type="text" name="{escape(quote(name))}" value="{escape(quote(value))}"><br>'
post_form += '</form>' post_form += '</form>'
html_content = f""" html_content = f"""
<!DOCTYPE html> <!DOCTYPE html>
@@ -449,6 +424,4 @@ def _post_request(req: V1RequestBase, driver: WebDriver):
<script>document.getElementById('hackForm').submit();</script> <script>document.getElementById('hackForm').submit();</script>
</body> </body>
</html>""" </html>"""
driver.get("data:text/html;charset=utf-8," + html_content) driver.get("data:text/html;charset=utf-8,{html_content}".format(html_content=html_content))
driver.start_session()
driver.start_session() # required to bypass Cloudflare

View File

@@ -471,7 +471,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
self.patcher.executable_path self.patcher.executable_path
) )
super(Chrome, self).__init__( super().__init__(
service=service, service=service,
options=options, options=options,
keep_alive=keep_alive, keep_alive=keep_alive,
@@ -507,8 +507,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
"Page.addScriptToEvaluateOnNewDocument", "Page.addScriptToEvaluateOnNewDocument",
{ {
"source": """ "source": """
Object.defineProperty(window, "navigator", {
Object.defineProperty(window, "navigator", { Object.defineProperty(window, "navigator", {
value: new Proxy(navigator, { value: new Proxy(navigator, {
has: (target, key) => (key === "webdriver" ? false : key in target), has: (target, key) => (key === "webdriver" ? false : key in target),
@@ -729,10 +727,8 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
def start_session(self, capabilities=None, browser_profile=None): def start_session(self, capabilities=None, browser_profile=None):
if not capabilities: if not capabilities:
capabilities = self.options.to_capabilities() capabilities = self.options.to_capabilities()
super(selenium.webdriver.chrome.webdriver.WebDriver, self).start_session( super().start_session(capabilities)
capabilities # super(Chrome, self).start_session(capabilities, browser_profile) # Original explicit call commented out
)
# super(Chrome, self).start_session(capabilities, browser_profile)
def find_elements_recursive(self, by, value): def find_elements_recursive(self, by, value):
""" """

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# this module is part of undetected_chromedriver # this module is part of undetected_chromedriver
from distutils.version import LooseVersion from packaging.version import Version as LooseVersion
import io import io
import json import json
import logging import logging
@@ -12,6 +12,7 @@ import random
import re import re
import shutil import shutil
import string import string
import subprocess
import sys import sys
import time import time
from urllib.request import urlopen from urllib.request import urlopen
@@ -68,8 +69,10 @@ class Patcher(object):
# check if version_main_int is less than or equal to e.g 114 # check if version_main_int is less than or equal to e.g 114
self.is_old_chromedriver = version_main and version_main_int <= 114 self.is_old_chromedriver = version_main and version_main_int <= 114
except (ValueError,TypeError): except (ValueError,TypeError):
# If the conversion fails, print an error message # Check not running inside Docker
print("version_main cannot be converted to an integer") if not os.path.exists("/app/chromedriver"):
# If the conversion fails, log an error message
logging.info("version_main cannot be converted to an integer")
# Set self.is_old_chromedriver to False if the conversion fails # Set self.is_old_chromedriver to False if the conversion fails
self.is_old_chromedriver = False self.is_old_chromedriver = False
@@ -220,7 +223,7 @@ class Patcher(object):
pass pass
release = self.fetch_release_number() release = self.fetch_release_number()
self.version_main = release.version[0] self.version_main = release.major
self.version_full = release self.version_full = release
self.unzip_package(self.fetch_package()) self.unzip_package(self.fetch_package())
@@ -325,11 +328,11 @@ class Patcher(object):
""" """
zip_name = f"chromedriver_{self.platform_name}.zip" zip_name = f"chromedriver_{self.platform_name}.zip"
if self.is_old_chromedriver: if self.is_old_chromedriver:
download_url = "%s/%s/%s" % (self.url_repo, self.version_full.vstring, zip_name) download_url = "%s/%s/%s" % (self.url_repo, str(self.version_full), zip_name)
else: else:
zip_name = zip_name.replace("_", "-", 1) zip_name = zip_name.replace("_", "-", 1)
download_url = "https://storage.googleapis.com/chrome-for-testing-public/%s/%s/%s" download_url = "https://storage.googleapis.com/chrome-for-testing-public/%s/%s/%s"
download_url %= (self.version_full.vstring, self.platform_name, zip_name) download_url %= (str(self.version_full), self.platform_name, zip_name)
logger.debug("downloading from %s" % download_url) logger.debug("downloading from %s" % download_url)
return urlretrieve(download_url)[0] return urlretrieve(download_url)[0]
@@ -371,10 +374,31 @@ class Patcher(object):
""" """
exe_name = os.path.basename(exe_name) exe_name = os.path.basename(exe_name)
if IS_POSIX: if IS_POSIX:
r = os.system("kill -f -9 $(pidof %s)" % exe_name) # Using shell=True for pidof, consider a more robust pid finding method if issues arise.
# pgrep can be an alternative: ["pgrep", "-f", exe_name]
# Or psutil if adding a dependency is acceptable.
command = f"pidof {exe_name}"
try:
result = subprocess.run(command, shell=True, capture_output=True, text=True, check=True)
pids = result.stdout.strip().split()
if pids:
subprocess.run(["kill", "-9"] + pids, check=False) # Changed from -f -9 to -9 as -f is not standard for kill
return True
return False # No PIDs found
except subprocess.CalledProcessError: # pidof returns 1 if no process found
return False # No process found
except Exception as e:
logger.debug(f"Error killing process on POSIX: {e}")
return False
else: else:
r = os.system("taskkill /f /im %s" % exe_name) try:
return not r # TASKKILL /F /IM chromedriver.exe
result = subprocess.run(["taskkill", "/f", "/im", exe_name], check=False, capture_output=True)
# taskkill returns 0 if process was killed, 128 if not found.
return result.returncode == 0
except Exception as e:
logger.debug(f"Error killing process on Windows: {e}")
return False
@staticmethod @staticmethod
def gen_random_cdc(): def gen_random_cdc():

View File

@@ -1,11 +1,12 @@
import json import json
import logging import logging
import os import os
import platform
import re import re
import shutil import shutil
import urllib.parse
import tempfile
import sys import sys
import tempfile
import urllib.parse
from selenium.webdriver.chrome.webdriver import WebDriver from selenium.webdriver.chrome.webdriver import WebDriver
import undetected_chromedriver as uc import undetected_chromedriver as uc
@@ -129,21 +130,18 @@ def get_webdriver(proxy: dict = None) -> WebDriver:
options = uc.ChromeOptions() options = uc.ChromeOptions()
options.add_argument('--no-sandbox') options.add_argument('--no-sandbox')
options.add_argument('--window-size=1920,1080') options.add_argument('--window-size=1920,1080')
options.add_argument('--disable-search-engine-choice-screen')
# todo: this param shows a warning in chrome head-full # todo: this param shows a warning in chrome head-full
options.add_argument('--disable-setuid-sandbox') options.add_argument('--disable-setuid-sandbox')
options.add_argument('--disable-dev-shm-usage') options.add_argument('--disable-dev-shm-usage')
# this option removes the zygote sandbox (it seems that the resolution is a bit faster) # this option removes the zygote sandbox (it seems that the resolution is a bit faster)
options.add_argument('--no-zygote') options.add_argument('--no-zygote')
# attempt to fix Docker ARM32 build # attempt to fix Docker ARM32 build
IS_ARMARCH = platform.machine().startswith(('arm', 'aarch'))
if IS_ARMARCH:
options.add_argument('--disable-gpu-sandbox') options.add_argument('--disable-gpu-sandbox')
options.add_argument('--disable-software-rasterizer')
options.add_argument('--ignore-certificate-errors') options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors') options.add_argument('--ignore-ssl-errors')
# fix GL errors in ASUSTOR NAS
# https://github.com/FlareSolverr/FlareSolverr/issues/782
# https://github.com/microsoft/vscode/issues/127800#issuecomment-873342069
# https://peter.sh/experiments/chromium-command-line-switches/#use-gl
options.add_argument('--use-gl=swiftshader')
language = os.environ.get('LANG', None) language = os.environ.get('LANG', None)
if language is not None: if language is not None:
@@ -173,8 +171,6 @@ def get_webdriver(proxy: dict = None) -> WebDriver:
# For normal headless mode: # For normal headless mode:
# options.add_argument('--headless') # options.add_argument('--headless')
options.add_argument("--auto-open-devtools-for-tabs")
# if we are inside the Docker container, we avoid downloading the driver # if we are inside the Docker container, we avoid downloading the driver
driver_exe_path = None driver_exe_path = None
version_main = None version_main = None
@@ -197,6 +193,8 @@ def get_webdriver(proxy: dict = None) -> WebDriver:
windows_headless=windows_headless, headless=get_config_headless()) windows_headless=windows_headless, headless=get_config_headless())
except Exception as e: except Exception as e:
logging.error("Error starting Chrome: %s" % e) logging.error("Error starting Chrome: %s" % e)
# No point in continuing if we cannot retrieve the driver
raise e
# save the patched driver to avoid re-downloads # save the patched driver to avoid re-downloads
if driver_exe_path is None: if driver_exe_path is None:

View File

@@ -1 +1 @@
WebTest==3.0.0 WebTest==3.0.6