From ff74b50b6039f9edcabfbc628e0d237edea564e0 Mon Sep 17 00:00:00 2001 From: Alex Naidis Date: Mon, 25 Aug 2025 04:55:06 +0200 Subject: [PATCH] Modernize and upgrade application (#1540) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .github/workflows/autotag.yml | 2 +- .github/workflows/release-docker.yml | 4 +-- .github/workflows/release.yml | 28 +++++++++---------- Dockerfile | 4 +-- README.md | 10 +++---- requirements.txt | 17 ++++++------ src/build_package.py | 2 +- src/undetected_chromedriver/__init__.py | 8 +++--- src/undetected_chromedriver/patcher.py | 36 ++++++++++++++++++++----- src/utils.py | 2 ++ test-requirements.txt | 2 +- 11 files changed, 69 insertions(+), 46 deletions(-) diff --git a/.github/workflows/autotag.yml b/.github/workflows/autotag.yml index 2363337..7c7c6f8 100644 --- a/.github/workflows/autotag.yml +++ b/.github/workflows/autotag.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Auto Tag uses: Klemensas/action-autotag@stable diff --git a/.github/workflows/release-docker.yml b/.github/workflows/release-docker.yml index 71fe5a1..aac4c8a 100644 --- a/.github/workflows/release-docker.yml +++ b/.github/workflows/release-docker.yml @@ -3,7 +3,7 @@ name: release-docker on: push: tags: - - 'v*.*.*' + - "v*.*.*" pull_request: branches: - master @@ -15,7 +15,7 @@ concurrency: jobs: build-docker-images: if: ${{ !github.event.pull_request.head.repo.fork }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9aab370..fd6f794 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,15 +3,15 @@ name: release on: push: tags: - - 'v*.*.*' + - "v*.*.*" jobs: create-release: name: Create release - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 # get all commits, branches and tags (required for the changelog) @@ -39,27 +39,27 @@ jobs: build-linux-package: name: Build Linux binary needs: create-release - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 # get all commits, branches and tags (required for the changelog) - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: "3.13" - name: Build artifacts run: | 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 python build_package.py - name: Upload release artifacts - uses: alexellis/upload-assets@0.4.0 + uses: alexellis/upload-assets@0.4.1 env: GITHUB_TOKEN: ${{ secrets.GH_PAT }} with: @@ -71,24 +71,24 @@ jobs: runs-on: windows-2022 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 # get all commits, branches and tags (required for the changelog) - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: "3.13" - name: Build artifacts run: | 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 python build_package.py - name: Upload release artifacts - uses: alexellis/upload-assets@0.4.0 + uses: alexellis/upload-assets@0.4.1 env: GITHUB_TOKEN: ${{ secrets.GH_PAT }} with: diff --git a/Dockerfile b/Dockerfile index e882e22..58d086d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim-bookworm as builder +FROM python:3.13-slim-bookworm as builder # Build dummy packages to skip installing them and their dependencies RUN apt-get update \ @@ -12,7 +12,7 @@ RUN apt-get update \ && equivs-build adwaita-icon-theme \ && mv adwaita-icon-theme_*.deb /adwaita-icon-theme.deb -FROM python:3.11-slim-bookworm +FROM python:3.13-slim-bookworm # Copy dummy packages COPY --from=builder /*.deb / diff --git a/README.md b/README.md index 78382ab..795886a 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ docker run -d \ ghcr.io/flaresolverr/flaresolverr:latest ``` -If your host OS is Debian, make sure `libseccomp2` version is 2.5.x. You can check the version with `sudo apt-cache policy libseccomp2` +If your host OS is Debian, make sure `libseccomp2` version is 2.5.x. You can check the version with `sudo apt-cache policy libseccomp2` and update the package with `sudo apt install libseccomp2=2.5.1-1~bpo10+1` or `sudo apt install libseccomp2=2.5.1-1+deb11u1`. Remember to restart the Docker daemon and the container after the update. @@ -77,7 +77,7 @@ This is the recommended way for Windows users. > **Warning** > 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. * (Only in Linux) Install [Xvfb](https://en.wikipedia.org/wiki/Xvfb) 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) -* Run `pkg install chromium python311 py311-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. -* Run `python3.11 -m pip install -r requirements.txt` command to install FlareSolverr dependencies. -* Run `python3.11 src/flaresolverr.py` command to start FlareSolverr. +* Run `python3.13 -m pip install -r requirements.txt` command to install FlareSolverr dependencies. +* Run `python3.13 src/flaresolverr.py` command to start FlareSolverr. ### Systemd service diff --git a/requirements.txt b/requirements.txt index 069dd92..cfa05f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,14 @@ -bottle==0.12.25 -waitress==3.0.1 -selenium==4.15.2 +bottle==0.13.4 +waitress==3.0.2 +selenium==4.34.2 func-timeout==4.3.5 -prometheus-client==0.17.1 +prometheus-client==0.22.1 # required by undetected_chromedriver requests==2.32.4 -certifi==2024.07.04 -websockets==11.0.3 +certifi==2025.7.9 +websockets==15.0.1 +packaging==25.0 # only required for linux and macos -xvfbwrapper==0.2.9; platform_system != "Windows" +xvfbwrapper==0.2.13; platform_system != "Windows" # only required for windows -pefile==2023.2.7; platform_system == "Windows" +pefile==2024.8.26; platform_system == "Windows" diff --git a/src/build_package.py b/src/build_package.py index 28a70b3..87e1e27 100644 --- a/src/build_package.py +++ b/src/build_package.py @@ -25,7 +25,7 @@ def clean_files(): def download_chromium(): # https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/ - revision = "1453032" if os.name == 'nt' else '1453031' + revision = "1465706" if os.name == 'nt' else '1465706' arch = 'Win_x64' if os.name == 'nt' else 'Linux_x64' 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') diff --git a/src/undetected_chromedriver/__init__.py b/src/undetected_chromedriver/__init__.py index dcf1a26..b78f60e 100644 --- a/src/undetected_chromedriver/__init__.py +++ b/src/undetected_chromedriver/__init__.py @@ -471,7 +471,7 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): self.patcher.executable_path ) - super(Chrome, self).__init__( + super().__init__( service=service, options=options, keep_alive=keep_alive, @@ -727,10 +727,8 @@ class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): def start_session(self, capabilities=None, browser_profile=None): if not capabilities: capabilities = self.options.to_capabilities() - super(selenium.webdriver.chrome.webdriver.WebDriver, self).start_session( - capabilities - ) - # super(Chrome, self).start_session(capabilities, browser_profile) + super().start_session(capabilities) + # super(Chrome, self).start_session(capabilities, browser_profile) # Original explicit call commented out def find_elements_recursive(self, by, value): """ diff --git a/src/undetected_chromedriver/patcher.py b/src/undetected_chromedriver/patcher.py index e8a0e96..7a60f76 100644 --- a/src/undetected_chromedriver/patcher.py +++ b/src/undetected_chromedriver/patcher.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # this module is part of undetected_chromedriver -from distutils.version import LooseVersion +from packaging.version import Version as LooseVersion import io import json import logging @@ -12,6 +12,7 @@ import random import re import shutil import string +import subprocess import sys import time from urllib.request import urlopen @@ -222,7 +223,7 @@ class Patcher(object): pass release = self.fetch_release_number() - self.version_main = release.version[0] + self.version_main = release.major self.version_full = release self.unzip_package(self.fetch_package()) @@ -327,11 +328,11 @@ class Patcher(object): """ zip_name = f"chromedriver_{self.platform_name}.zip" 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: zip_name = zip_name.replace("_", "-", 1) 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) return urlretrieve(download_url)[0] @@ -373,10 +374,31 @@ class Patcher(object): """ exe_name = os.path.basename(exe_name) 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: - r = os.system("taskkill /f /im %s" % exe_name) - return not r + try: + # 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 def gen_random_cdc(): diff --git a/src/utils.py b/src/utils.py index d73bf0a..5bb3af6 100644 --- a/src/utils.py +++ b/src/utils.py @@ -194,6 +194,8 @@ def get_webdriver(proxy: dict = None) -> WebDriver: windows_headless=windows_headless, headless=get_config_headless()) except Exception as 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 if driver_exe_path is None: diff --git a/test-requirements.txt b/test-requirements.txt index aeb254e..accbb16 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1 @@ -WebTest==3.0.0 +WebTest==3.0.6