Skip to content

Commit

Permalink
Merge pull request #1478 from jdholtz/115-compatibility
Browse files Browse the repository at this point in the history
Add support for downloading Chromedriver versions 115+
  • Loading branch information
ultrafunkamsterdam authored Aug 25, 2023
2 parents a415e40 + 29551bd commit 01417d0
Showing 1 changed file with 74 additions and 24 deletions.
98 changes: 74 additions & 24 deletions undetected_chromedriver/patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

from distutils.version import LooseVersion
import io
import json
import logging
import os
import pathlib
import platform
import random
import re
import shutil
Expand All @@ -24,21 +26,9 @@

class Patcher(object):
lock = Lock()
url_repo = "https://chromedriver.storage.googleapis.com"
zip_name = "chromedriver_%s.zip"
exe_name = "chromedriver%s"

platform = sys.platform
if platform.endswith("win32"):
zip_name %= "win32"
exe_name %= ".exe"
if platform.endswith(("linux", "linux2")):
zip_name %= "linux64"
exe_name %= ""
if platform.endswith("darwin"):
zip_name %= "mac64"
exe_name %= ""

if platform.endswith("win32"):
d = "~/appdata/roaming/undetected_chromedriver"
elif "LAMBDA_TASK_ROOT" in os.environ:
Expand Down Expand Up @@ -72,6 +62,10 @@ def __init__(
prefix = "undetected"
self.user_multi_procs = user_multi_procs

self.is_old_chromedriver = version_main and version_main <= 114
# Needs to be called before self.exe_name is accessed
self._set_platform_name()

if not os.path.exists(self.data_path):
os.makedirs(self.data_path, exist_ok=True)

Expand All @@ -97,9 +91,33 @@ def __init__(
self._custom_exe_path = True
self.executable_path = executable_path

# Set the correct repository to download the Chromedriver from
if self.is_old_chromedriver:
self.url_repo = "https://chromedriver.storage.googleapis.com"
else:
self.url_repo = "https://googlechromelabs.github.io/chrome-for-testing"

self.version_main = version_main
self.version_full = None

def _set_platform_name(self):
"""
Set the platform and exe name based on the platform undetected_chromedriver is running on
in order to download the correct chromedriver.
"""
if self.platform.endswith("win32"):
self.platform_name = "win32"
self.exe_name %= ".exe"
if self.platform.endswith(("linux", "linux2")):
self.platform_name = "linux64"
self.exe_name %= ""
if self.platform.endswith("darwin"):
if self.is_old_chromedriver:
self.platform_name = "mac64"
else:
self.platform_name = "mac-x64"
self.exe_name %= ""

def auto(self, executable_path=None, force=False, version_main=None, _=None):
"""
Expand Down Expand Up @@ -217,12 +235,32 @@ def fetch_release_number(self):
:return: version string
:rtype: LooseVersion
"""
path = "/latest_release"
if self.version_main:
path += f"_{self.version_main}"
path = path.upper()
# Endpoint for old versions of Chromedriver (114 and below)
if self.is_old_chromedriver:
path = f"/latest_release_{self.version_main}"
path = path.upper()
logger.debug("getting release number from %s" % path)
return LooseVersion(urlopen(self.url_repo + path).read().decode())

# Endpoint for new versions of Chromedriver (115+)
if not self.version_main:
# Fetch the latest version
path = "/last-known-good-versions-with-downloads.json"
logger.debug("getting release number from %s" % path)
with urlopen(self.url_repo + path) as conn:
response = conn.read().decode()

last_versions = json.loads(response)
return LooseVersion(last_versions["channels"]["Stable"]["version"])

# Fetch the latest minor version of the major version provided
path = "/latest-versions-per-milestone-with-downloads.json"
logger.debug("getting release number from %s" % path)
return LooseVersion(urlopen(self.url_repo + path).read().decode())
with urlopen(self.url_repo + path) as conn:
response = conn.read().decode()

major_versions = json.loads(response)
return LooseVersion(major_versions["milestones"][str(self.version_main)]["version"])

def parse_exe_version(self):
with io.open(self.executable_path, "rb") as f:
Expand All @@ -237,17 +275,29 @@ def fetch_package(self):
:return: path to downloaded file
"""
u = "%s/%s/%s" % (self.url_repo, self.version_full.vstring, self.zip_name)
logger.debug("downloading from %s" % u)
# return urlretrieve(u, filename=self.data_path)[0]
return urlretrieve(u)[0]
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)
else:
zip_name = zip_name.replace("_", "-", 1)
download_url = "https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/%s/%s/%s"
download_url %= (self.version_full.vstring, self.platform_name, zip_name)

logger.debug("downloading from %s" % download_url)
return urlretrieve(download_url)[0]

def unzip_package(self, fp):
"""
Does what it says
:return: path to unpacked executable
"""
exe_path = self.exe_name
if not self.is_old_chromedriver:
# The new chromedriver unzips into its own folder
zip_name = f"chromedriver-{self.platform_name}"
exe_path = os.path.join(zip_name, self.exe_name)

logger.debug("unzipping %s" % fp)
try:
os.unlink(self.zip_path)
Expand All @@ -256,10 +306,10 @@ def unzip_package(self, fp):

os.makedirs(self.zip_path, mode=0o755, exist_ok=True)
with zipfile.ZipFile(fp, mode="r") as zf:
zf.extract(self.exe_name, self.zip_path)
os.rename(os.path.join(self.zip_path, self.exe_name), self.executable_path)
zf.extractall(self.zip_path)
os.rename(os.path.join(self.zip_path, exe_path), self.executable_path)
os.remove(fp)
os.rmdir(self.zip_path)
shutil.rmtree(self.zip_path)
os.chmod(self.executable_path, 0o755)
return self.executable_path

Expand Down

0 comments on commit 01417d0

Please sign in to comment.