Skip to content

Instantly share code, notes, and snippets.

@VIRUXE
Created June 24, 2025 20:10
Show Gist options
  • Save VIRUXE/a72a615da212f6adbc6e8f4145e6232e to your computer and use it in GitHub Desktop.
Save VIRUXE/a72a615da212f6adbc6e8f4145e6232e to your computer and use it in GitHub Desktop.
python3 gta5-mods_download.py <URL> [-o <output_directory>]
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import argparse
import time
import os
from selenium.webdriver.chrome.service import Service
# ------------------------------
# Command-line interface
# ------------------------------
parser = argparse.ArgumentParser(description="Download a mod file from GTA5-Mods with Selenium (head-less).")
parser.add_argument("url", help="The GTA5-Mods mod page or direct download page URL")
# Optional download directory (defaults to the working directory)
parser.add_argument(
"-o",
"--output",
default=os.getcwd(),
help="Directory where the file will be saved (defaults to the current working directory)",
)
args = parser.parse_args()
# Resolve and, if necessary, create the target download directory
download_dir = os.path.abspath(args.output)
os.makedirs(download_dir, exist_ok=True)
# Configure Chrome to run in headless mode for non-GUI environments
options = webdriver.ChromeOptions()
# NOTE: '--headless=new' uses the new headless implementation available in recent Chrome versions.
# Fall back to '--headless' if you encounter compatibility issues on older releases.
options.add_argument("--headless=new")
# Optionally disable GPU and infobars for additional stability
options.add_argument("--disable-gpu")
# Configure automatic download location without prompting.
options.add_experimental_option("prefs", {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"safebrowsing.enabled": True,
})
options.add_argument("--disable-infobars")
# Reduce Chrome log noise that appears on STDERR ("blink.mojom.WidgetHost" etc.)
options.add_argument("--log-level=3") # 0 = INFO, 3 = FATAL
options.add_argument("--disable-logging")
# Remove automation & logging switches that can trigger extra output
options.add_experimental_option("excludeSwitches", ["enable-logging", "enable-automation"])
# --- Linux / root-user stability tweaks --------------------------------------
# When running Chrome in head-less mode on Linux (especially as the root user),
# the default user-data directory can become locked by another stray Chrome
# process, triggering the dreaded:
# "session not created: probably user data directory is already in use"
# We therefore force Chrome to use an isolated, per-process directory under
# /tmp which will be removed automatically by the OS on reboot. We also pass
# the "--no-sandbox" and "--disable-dev-shm-usage" flags to avoid sandbox/
# shared-memory pitfalls that frequently appear in containerised or root
# environments.
unique_user_data_dir = f"/tmp/selenium_chrome_profile_{os.getpid()}"
os.makedirs(unique_user_data_dir, exist_ok=True)
options.add_argument(f"--user-data-dir={unique_user_data_dir}")
options.add_argument("--no-sandbox") # Required when running as root
options.add_argument("--disable-dev-shm-usage") # Mitigate /dev/shm size limits
# Create a Service that discards ChromeDriver's own logs
service = Service(log_path=os.devnull)
# Initialise the driver with the silent service
driver = webdriver.Chrome(service=service, options=options)
try:
# Navigate to URL
driver.get(args.url)
print("Navigated to the provided URL.")
# Attempt to accept cookie/consent banner if present
try:
accept_button = WebDriverWait(driver, 5).until(
EC.element_to_be_clickable(
(By.XPATH, "//div[contains(@class,'amc-focus-first') and normalize-space(text())='Accept']")
)
)
accept_button.click()
print("Clicked the 'Accept' consent button.")
except TimeoutException:
# Banner not present; continue normally
pass
# Define download button XPath (language-agnostic)
download_button_xpath = "//a[contains(@class, 'btn-download') and .//span[contains(@class, 'fa-download')]]"
# Check if on download page or mod page
current_url = driver.current_url
if "/download/" in current_url:
print("Detected download page. Clicking the download button.")
download_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, download_button_xpath)))
download_button.click()
else:
print("Detected mod page. Clicking the first download button.")
first_download_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, download_button_xpath)))
first_download_button.click()
# Wait for download page
WebDriverWait(driver, 20).until(EC.url_contains("/download/"))
print("Navigated to the download page.")
# Click second download button
print("Clicking the second download button.")
second_download_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, download_button_xpath)))
second_download_button.click()
# Wait for download to start
time.sleep(5)
print("Download initiated. Check your default download folder.")
finally:
driver.quit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment