Compare commits

...

53 Commits

Author SHA1 Message Date
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
21hsmw
6c1d78cb84
Fix occasional headless issue on Linux when set to "false" (#1199)
* Fix occasional headless issue on Linux when set to "false"

- Add a variable containing the current platform
- Check if the platform is "nt" (Windows) before closing the driver

* Update CHANGELOG.md

---------

Co-authored-by: ilike2burnthing <59480337+ilike2burnthing@users.noreply.github.com>
2024-05-24 17:33:46 +01:00
ilike2burnthing
5a2c61601e
Fix Chrome v124+ not closing on Windows. resolves #1161 (#1193) 2024-05-20 00:52:55 +01:00
ilike2burnthing
c304da2964
Update README.md 2024-04-22 23:30:52 +01:00
ilike2burnthing
b811412699
Fix LANG ENV for Linux. #1036 2024-04-20 03:41:53 +01:00
Ross Patterson
0bb8de144f
Add Compose V2 command to readme (#1154)
Co-authored-by: root@library.moodysalon.net <root@library.moodysalon.net>
Co-authored-by: ilike2burnthing <59480337+ilike2burnthing@users.noreply.github.com>
2024-04-18 05:07:06 +01:00
ilike2burnthing
38166dfaa0
Bump version 3.3.17 (#1147) 2024-04-09 20:31:21 +01:00
ilike2burnthing
8dea0ed017
Fix file descriptor leak in service on quit(). resolves #983
credit: @zkulis - https://github.com/ultrafunkamsterdam/undetected-chromedriver/pull/1812
2024-04-09 20:27:42 +01:00
francisco-lafe
20cd2944a7
Update README.md (#1127)
Co-authored-by: ilike2burnthing <59480337+ilike2burnthing@users.noreply.github.com>
2024-03-19 18:05:11 +00:00
ilike2burnthing
fd773e5909
Bump version 3.3.16 (#1105) 2024-02-28 21:38:53 +00:00
Justin Kromlinger
35c7bff3c8
Use headless configuration properly (#1104) 2024-02-28 20:36:38 +00:00
Raphaël
afdc1c7a8e
Add FreeBSD support (#1054) 2024-02-28 02:45:04 +00:00
ilike2burnthing
0bc7a4498c
Update README.md 2024-02-23 05:01:00 +00:00
Xewdy
c5a5f6d65e
Add platform specifiers to dependencies (#877) 2024-02-21 21:14:25 +00:00
ilike2burnthing
aaf29be8e1
Bump version 3.3.15 (#1088) 2024-02-20 23:48:23 +00:00
Tadas Gedgaudas
800866d033
Fix looping challenges. #1036 (#1065)
Co-authored-by: Tadas Gedgaudas <tg@infrahub.io>
Co-authored-by: ilike2burnthing <59480337+ilike2burnthing@users.noreply.github.com>
2024-02-20 23:41:02 +00:00
ilike2burnthing
043f18b231
Bump UC version to 3.5.5 2024-02-17 19:28:06 +00:00
ilike2burnthing
d21a332519
Hotfix 2 - bad Chromium build, instances failed to terminate (#1072) 2024-02-17 05:53:45 +00:00
ilike2burnthing
3ca6d08f41
Hotfix for Linux build - some Chrome files no longer exist (#1071) 2024-02-17 01:15:32 +00:00
16 changed files with 268 additions and 117 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

@ -4,50 +4,64 @@ 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:
if: ${{ !github.event.pull_request.head.repo.fork }}
runs-on: ubuntu-22.04 runs-on: ubuntu-22.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

@ -1,5 +1,58 @@
# Changelog # Changelog
## 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)
* Fix occasional headless issue on Linux when set to "false". Thanks @21hsmw
## v3.3.18 (2024/05/20)
* Fix LANG ENV for Linux
* Fix Chrome v124+ not closing on Windows. Thanks @RileyXX
## v3.3.17 (2024/04/09)
* Fix file descriptor leak in service on quit(). Thanks @zkulis
## v3.3.16 (2024/02/28)
* Fix of the subprocess.STARTUPINFO() call. Thanks @ceconelo
* Add FreeBSD support. Thanks @Asthowen
* Use headless configuration properly. Thanks @hashworks
## v3.3.15 (2024/02/20)
* Fix looping challenges
## v3.3.14-hotfix2 (2024/02/17)
* Hotfix 2 - bad Chromium build, instances failed to terminate
## v3.3.14-hotfix (2024/02/17)
* Hotfix for Linux build - some Chrome files no longer exist
## v3.3.14 (2024/02/17) ## v3.3.14 (2024/02/17)
* Update Chrome downloads. Thanks @opemvbs * Update Chrome downloads. Thanks @opemvbs

View File

@ -1,4 +1,4 @@
FROM python:3.11-slim-bullseye as builder FROM python:3.11-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.11-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.14 . # docker build -t ngosang/flaresolverr:3.3.24 .
# docker run -p 8191:8191 ngosang/flaresolverr:3.3.14 # docker run -p 8191:8191 ngosang/flaresolverr:3.3.24
# 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.14 --platform linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 . # docker buildx build -t ngosang/flaresolverr:3.3.24 --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.14 --platform linux/arm/v7 --load . # docker buildx build -t ngosang/flaresolverr:3.3.24 --platform linux/arm/v7 --load .
# docker run -p 8191:8191 --platform linux/arm/v7 ngosang/flaresolverr:3.3.14 # docker run -p 8191:8191 --platform linux/arm/v7 ngosang/flaresolverr:3.3.24

View File

@ -45,7 +45,8 @@ Supported architectures are:
| ARM32 | linux/arm/v7 | | ARM32 | linux/arm/v7 |
| ARM64 | linux/arm64 | | ARM64 | linux/arm64 |
We provide a `docker-compose.yml` configuration file. Clone this repository and execute `docker-compose up -d` to start We provide a `docker-compose.yml` configuration file. Clone this repository and execute
`docker-compose up -d` _(Compose V1)_ or `docker compose up -d` _(Compose V2)_ to start
the container. the container.
If you prefer the `docker cli` execute the following command. If you prefer the `docker cli` execute the following command.
@ -78,11 +79,19 @@ This is the recommended way for Windows users.
* Install [Python 3.11](https://www.python.org/downloads/). * Install [Python 3.11](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 / macOS) 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.
* Clone this repository and open a shell in that path. * Clone this repository and open a shell in that path.
* Run `pip install -r requirements.txt` command to install FlareSolverr dependencies. * Run `pip install -r requirements.txt` command to install FlareSolverr dependencies.
* Run `python src/flaresolverr.py` command to start FlareSolverr. * Run `python src/flaresolverr.py` command to start FlareSolverr.
### From source code (FreeBSD/TrueNAS CORE)
* Run `pkg install chromium python311 py311-pip xorg-vfbserver` command to install the required dependencies.
* Clone this repository and open a shell in that path.
* Run `python3.11 -m pip install -r requirements.txt` command to install FlareSolverr dependencies.
* Run `python3.11 src/flaresolverr.py` command to start FlareSolverr.
### Systemd service ### Systemd service
We provide an example Systemd unit file `flaresolverr.service` as reference. You have to modify the file to suit your needs: paths, user and environment variables. We provide an example Systemd unit file `flaresolverr.service` as reference. You have to modify the file to suit your needs: paths, user and environment variables.
@ -95,7 +104,7 @@ curl -L -X POST 'http://localhost:8191/v1' \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
--data-raw '{ --data-raw '{
"cmd": "request.get", "cmd": "request.get",
"url":"http://www.google.com/", "url": "http://www.google.com/",
"maxTimeout": 60000 "maxTimeout": 60000
}' }'
``` ```
@ -269,8 +278,8 @@ This is the same as `request.get` but it takes one more param:
Environment variables are set differently depending on the operating system. Some examples: Environment variables are set differently depending on the operating system. Some examples:
* Docker: Take a look at the Docker section in this document. Environment variables can be set in the `docker-compose.yml` file or in the Docker CLI command. * Docker: Take a look at the Docker section in this document. Environment variables can be set in the `docker-compose.yml` file or in the Docker CLI command.
* Linux: Run `export LOG_LEVEL=debug` and then start FlareSolverr in the same shell. * Linux: Run `export LOG_LEVEL=debug` and then run `flaresolverr` in the same shell.
* Windows: Open `cmd.exe`, run `set LOG_LEVEL=debug` and then start FlareSolverr in the same shell. * Windows: Open `cmd.exe`, run `set LOG_LEVEL=debug` and then run `flaresolverr.exe` in the same shell.
## Prometheus exporter ## Prometheus exporter
@ -308,7 +317,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.14", "version": "3.3.24",
"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,13 @@
bottle==0.12.25 bottle==0.12.25
waitress==2.1.2 waitress==3.0.1
selenium==4.15.2 selenium==4.15.2
func-timeout==4.3.5 func-timeout==4.3.5
prometheus-client==0.17.1 prometheus-client==0.17.1
# required by undetected_chromedriver # required by undetected_chromedriver
requests==2.31.0 requests==2.32.3
certifi==2023.7.22 certifi==2024.07.04
websockets==11.0.3 websockets==11.0.3
# only required for linux # only required for linux and macos
xvfbwrapper==0.2.9 xvfbwrapper==0.2.9; platform_system != "Windows"
# only required for windows # only required for windows
pefile==2023.2.7 pefile==2023.2.7; 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 = "1250005" if os.name == 'nt' else '1250006' revision = "1453032" if os.name == 'nt' else '1453031'
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')
@ -59,8 +59,7 @@ def download_chromium():
# Give executable permissions for *nix # Give executable permissions for *nix
# file * | grep executable | cut -d: -f1 # file * | grep executable | cut -d: -f1
print("Giving executable permissions...") print("Giving executable permissions...")
execs = ['chrome', 'chrome_crashpad_handler', 'chrome_sandbox', 'chrome-wrapper', 'nacl_helper', execs = ['chrome', 'chrome_crashpad_handler', 'chrome_sandbox', 'chrome-wrapper', 'xdg-mime', 'xdg-settings']
'nacl_helper_bootstrap', 'nacl_irt_x86_64.nexe', 'xdg-mime', 'xdg-settings']
for exec_file in execs: for exec_file in execs:
exec_path = os.path.join(chrome_path, exec_file) exec_path = os.path.join(chrome_path, exec_file)
os.chmod(exec_path, 0o755) os.chmod(exec_path, 0o755)

View File

@ -101,6 +101,9 @@ if __name__ == "__main__":
logging.info(f'FlareSolverr {utils.get_flaresolverr_version()}') logging.info(f'FlareSolverr {utils.get_flaresolverr_version()}')
logging.debug('Debug log enabled') logging.debug('Debug log enabled')
# Get current OS for global variable
utils.get_current_platform()
# test browser installation # test browser installation
flaresolverr_service.test_browser_installation() flaresolverr_service.test_browser_installation()

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:
@ -245,6 +247,8 @@ def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT:
raise Exception('Error solving the challenge. ' + str(e).replace('\n', '\\n')) raise Exception('Error solving the challenge. ' + str(e).replace('\n', '\\n'))
finally: finally:
if not req.session and driver is not None: if not req.session and driver is not None:
if utils.PLATFORM_VERSION == "nt":
driver.close()
driver.quit() driver.quit()
logging.debug('A used instance of webdriver has been destroyed') logging.debug('A used instance of webdriver has been destroyed')
@ -252,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.")
@ -292,15 +287,13 @@ def _evil_logic(req: V1RequestBase, driver: WebDriver, method: str) -> Challenge
res.status = STATUS_OK res.status = STATUS_OK
res.message = "" res.message = ""
# navigate to the page # navigate to the page
logging.debug(f'Navigating to... {req.url}') logging.debug(f'Navigating to... {req.url}')
driver.get(req.url)
driver.start_session() # required to bypass Cloudflare
if method == 'POST': if method == 'POST':
_post_request(req, driver) _post_request(req, driver)
else: else:
driver.get(req.url) driver.get(req.url)
driver.start_session() # required to bypass Cloudflare
# 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:
@ -313,7 +306,6 @@ def _evil_logic(req: V1RequestBase, driver: WebDriver, method: str) -> Challenge
_post_request(req, driver) _post_request(req, driver)
else: else:
driver.get(req.url) driver.get(req.url)
driver.start_session() # required to bypass Cloudflare
# wait for the page # wait for the page
if utils.get_config_log_html(): if utils.get_config_log_html():
@ -422,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>
@ -432,5 +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() # required to bypass Cloudflare

View File

@ -66,6 +66,8 @@ class SessionsStorage:
return False return False
session = self.sessions.pop(session_id) session = self.sessions.pop(session_id)
if utils.PLATFORM_VERSION == "nt":
session.driver.close()
session.driver.quit() session.driver.quit()
return True return True

View File

@ -17,7 +17,7 @@ by UltrafunkAmsterdam (https://github.com/ultrafunkamsterdam)
from __future__ import annotations from __future__ import annotations
__version__ = "3.5.4" __version__ = "3.5.5"
import json import json
import logging import logging
@ -451,8 +451,10 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
options.binary_location, *options.arguments options.binary_location, *options.arguments
) )
else: else:
startupinfo = subprocess.STARTUPINFO() startupinfo = None
if os.name == 'nt' and windows_headless: if os.name == 'nt' and windows_headless:
# STARTUPINFO() is Windows only
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
browser = subprocess.Popen( browser = subprocess.Popen(
[options.binary_location, *options.arguments], [options.binary_location, *options.arguments],
@ -505,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),
@ -769,7 +769,9 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
def quit(self): def quit(self):
try: try:
self.service.stop()
self.service.process.kill() self.service.process.kill()
self.command_executor.close()
self.service.process.wait(5) self.service.process.wait(5)
logger.debug("webdriver process ended") logger.debug("webdriver process ended")
except (AttributeError, RuntimeError, OSError): except (AttributeError, RuntimeError, OSError):
@ -784,15 +786,6 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver):
logger.debug("gracefully closed browser") logger.debug("gracefully closed browser")
except Exception as e: # noqa except Exception as e: # noqa
pass pass
# Force kill Chrome process in Windows
# https://github.com/FlareSolverr/FlareSolverr/issues/772
if os.name == 'nt':
try:
subprocess.call(['taskkill', '/f', '/pid', str(self.browser_pid)],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except Exception:
pass
if ( if (
hasattr(self, "keep_user_data_dir") hasattr(self, "keep_user_data_dir")
and hasattr(self, "user_data_dir") and hasattr(self, "user_data_dir")

View File

@ -2,6 +2,7 @@ import asyncio
from collections.abc import Mapping from collections.abc import Mapping
from collections.abc import Sequence from collections.abc import Sequence
from functools import wraps from functools import wraps
import os
import logging import logging
import threading import threading
import time import time
@ -187,4 +188,6 @@ def test():
time.sleep(10) time.sleep(10)
if os.name == "nt":
driver.close()
driver.quit() driver.quit()

View File

@ -21,7 +21,7 @@ from multiprocessing import Lock
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
IS_POSIX = sys.platform.startswith(("darwin", "cygwin", "linux", "linux2")) IS_POSIX = sys.platform.startswith(("darwin", "cygwin", "linux", "linux2", "freebsd"))
class Patcher(object): class Patcher(object):
@ -68,8 +68,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
@ -80,6 +82,11 @@ class Patcher(object):
os.makedirs(self.data_path, exist_ok=True) os.makedirs(self.data_path, exist_ok=True)
if not executable_path: if not executable_path:
if sys.platform.startswith("freebsd"):
self.executable_path = os.path.join(
self.data_path, self.exe_name
)
else:
self.executable_path = os.path.join( self.executable_path = os.path.join(
self.data_path, "_".join([prefix, self.exe_name]) self.data_path, "_".join([prefix, self.exe_name])
) )
@ -127,6 +134,9 @@ class Patcher(object):
else: else:
self.platform_name = "mac-x64" self.platform_name = "mac-x64"
self.exe_name %= "" self.exe_name %= ""
if self.platform.startswith("freebsd"):
self.platform_name = "freebsd"
self.exe_name %= ""
def auto(self, executable_path=None, force=False, version_main=None, _=None): def auto(self, executable_path=None, force=False, version_main=None, _=None):
""" """
@ -166,6 +176,35 @@ class Patcher(object):
if force is True: if force is True:
self.force = force self.force = force
if self.platform_name == "freebsd":
chromedriver_path = shutil.which("chromedriver")
if not os.path.isfile(chromedriver_path) or not os.access(chromedriver_path, os.X_OK):
logging.error("Chromedriver not installed!")
return
version_path = os.path.join(os.path.dirname(self.executable_path), "version.txt")
process = os.popen(f'"{chromedriver_path}" --version')
chromedriver_version = process.read().split(' ')[1].split(' ')[0]
process.close()
current_version = None
if os.path.isfile(version_path) or os.access(version_path, os.X_OK):
with open(version_path, 'r') as f:
current_version = f.read()
if current_version != chromedriver_version:
logging.info("Copying chromedriver executable...")
shutil.copy(chromedriver_path, self.executable_path)
os.chmod(self.executable_path, 0o755)
with open(version_path, 'w') as f:
f.write(chromedriver_version)
logging.info("Chromedriver executable copied!")
else:
try: try:
os.unlink(self.executable_path) os.unlink(self.executable_path)
except PermissionError: except PermissionError:
@ -186,6 +225,7 @@ class Patcher(object):
self.version_main = release.version[0] self.version_main = release.version[0]
self.version_full = release self.version_full = release
self.unzip_package(self.fetch_package()) self.unzip_package(self.fetch_package())
return self.patch() return self.patch()
def driver_binary_in_use(self, path: str = None) -> bool: def driver_binary_in_use(self, path: str = None) -> bool:

View File

@ -1,15 +1,18 @@
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 sys
import tempfile 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
FLARESOLVERR_VERSION = None FLARESOLVERR_VERSION = None
PLATFORM_VERSION = None
CHROME_EXE_PATH = None CHROME_EXE_PATH = None
CHROME_MAJOR_VERSION = None CHROME_MAJOR_VERSION = None
USER_AGENT = None USER_AGENT = None
@ -37,6 +40,13 @@ def get_flaresolverr_version() -> str:
FLARESOLVERR_VERSION = json.loads(f.read())['version'] FLARESOLVERR_VERSION = json.loads(f.read())['version']
return FLARESOLVERR_VERSION return FLARESOLVERR_VERSION
def get_current_platform() -> str:
global PLATFORM_VERSION
if PLATFORM_VERSION is not None:
return PLATFORM_VERSION
PLATFORM_VERSION = os.name
return PLATFORM_VERSION
def create_proxy_extension(proxy: dict) -> str: def create_proxy_extension(proxy: dict) -> str:
parsed_url = urllib.parse.urlparse(proxy['url']) parsed_url = urllib.parse.urlparse(proxy['url'])
@ -120,12 +130,15 @@ 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('--disable-software-rasterizer')
options.add_argument('--ignore-certificate-errors') options.add_argument('--ignore-certificate-errors')
@ -138,7 +151,7 @@ def get_webdriver(proxy: dict = None) -> WebDriver:
language = os.environ.get('LANG', None) language = os.environ.get('LANG', None)
if language is not None: if language is not None:
options.add_argument('--lang=%s' % language) options.add_argument('--accept-lang=%s' % language)
# Fix for Chrome 117 | https://github.com/FlareSolverr/FlareSolverr/issues/910 # Fix for Chrome 117 | https://github.com/FlareSolverr/FlareSolverr/issues/910
if USER_AGENT is not None: if USER_AGENT is not None:
@ -180,9 +193,12 @@ def get_webdriver(proxy: dict = None) -> WebDriver:
# downloads and patches the chromedriver # downloads and patches the chromedriver
# if we don't set driver_executable_path it downloads, patches, and deletes the driver each time # if we don't set driver_executable_path it downloads, patches, and deletes the driver each time
try:
driver = uc.Chrome(options=options, browser_executable_path=browser_executable_path, driver = uc.Chrome(options=options, browser_executable_path=browser_executable_path,
driver_executable_path=driver_exe_path, version_main=version_main, driver_executable_path=driver_exe_path, version_main=version_main,
windows_headless=windows_headless, headless=windows_headless) windows_headless=windows_headless, headless=get_config_headless())
except Exception as e:
logging.error("Error starting Chrome: %s" % 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:
@ -308,6 +324,8 @@ def get_user_agent(driver=None) -> str:
raise Exception("Error getting browser User-Agent. " + str(e)) raise Exception("Error getting browser User-Agent. " + str(e))
finally: finally:
if driver is not None: if driver is not None:
if PLATFORM_VERSION == "nt":
driver.close()
driver.quit() driver.quit()