Spaces:
Running
Running
Commit
·
36fd3e9
1
Parent(s):
b0a85e5
Bug-Fixed
Browse files- Dockerfile +22 -3
- worker.py +77 -19
Dockerfile
CHANGED
|
@@ -8,7 +8,12 @@ ENV TZ=Etc/UTC
|
|
| 8 |
ENV HOME=/home/appuser
|
| 9 |
ENV PYTHONUSERBASE=/home/appuser/.local
|
| 10 |
|
| 11 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 13 |
wget \
|
| 14 |
gnupg \
|
|
@@ -24,11 +29,22 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
| 24 |
libgtk-3-0 \
|
| 25 |
libgbm1 \
|
| 26 |
fonts-liberation \
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
&& rm -rf /var/lib/apt/lists/*
|
| 28 |
|
| 29 |
-
# Create unprivileged user
|
| 30 |
RUN addgroup --system appuser && adduser --system --ingroup appuser appuser
|
| 31 |
-
RUN mkdir -p /home/appuser/.local
|
|
|
|
|
|
|
| 32 |
|
| 33 |
USER appuser
|
| 34 |
|
|
@@ -39,6 +55,9 @@ ENV PATH="/home/appuser/.local/bin:${PATH}"
|
|
| 39 |
|
| 40 |
COPY --chown=appuser:appuser . .
|
| 41 |
|
|
|
|
|
|
|
|
|
|
| 42 |
EXPOSE 7860
|
| 43 |
|
| 44 |
CMD ["python", "server.py"]
|
|
|
|
| 8 |
ENV HOME=/home/appuser
|
| 9 |
ENV PYTHONUSERBASE=/home/appuser/.local
|
| 10 |
|
| 11 |
+
# HuggingFace Spaces specific optimizations
|
| 12 |
+
ENV CHROME_BIN=/usr/bin/chromium
|
| 13 |
+
ENV CHROMEDRIVER_PATH=/usr/bin/chromedriver
|
| 14 |
+
ENV DISPLAY=:99
|
| 15 |
+
|
| 16 |
+
# System deps + Chromium + ChromeDriver + essential libs for HF Spaces
|
| 17 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 18 |
wget \
|
| 19 |
gnupg \
|
|
|
|
| 29 |
libgtk-3-0 \
|
| 30 |
libgbm1 \
|
| 31 |
fonts-liberation \
|
| 32 |
+
libappindicator3-1 \
|
| 33 |
+
libasound2 \
|
| 34 |
+
libdbus-glib-1-2 \
|
| 35 |
+
libdrm2 \
|
| 36 |
+
libxcomposite1 \
|
| 37 |
+
libxdamage1 \
|
| 38 |
+
libxrandr2 \
|
| 39 |
+
xvfb \
|
| 40 |
+
procps \
|
| 41 |
&& rm -rf /var/lib/apt/lists/*
|
| 42 |
|
| 43 |
+
# Create unprivileged user with proper permissions
|
| 44 |
RUN addgroup --system appuser && adduser --system --ingroup appuser appuser
|
| 45 |
+
RUN mkdir -p /home/appuser/.local /tmp/chrome-temp && \
|
| 46 |
+
chown -R appuser:appuser /app /home/appuser /tmp/chrome-temp && \
|
| 47 |
+
chmod 755 /tmp/chrome-temp
|
| 48 |
|
| 49 |
USER appuser
|
| 50 |
|
|
|
|
| 55 |
|
| 56 |
COPY --chown=appuser:appuser . .
|
| 57 |
|
| 58 |
+
# Create config directory if needed
|
| 59 |
+
RUN mkdir -p config
|
| 60 |
+
|
| 61 |
EXPOSE 7860
|
| 62 |
|
| 63 |
CMD ["python", "server.py"]
|
worker.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
| 1 |
# worker.py
|
| 2 |
import time
|
| 3 |
import threading
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
from selenium import webdriver
|
| 6 |
from selenium.webdriver.chrome.service import Service as ChromeService
|
|
@@ -18,42 +22,83 @@ class QuantumBot:
|
|
| 18 |
self.driver = None
|
| 19 |
self.DEFAULT_TIMEOUT = 30
|
| 20 |
self.termination_event = threading.Event()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
def initialize_driver(self):
|
| 23 |
try:
|
| 24 |
self.micro_status("Initializing headless browser...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
options = ChromeOptions()
|
| 26 |
-
|
| 27 |
-
#
|
| 28 |
-
options.
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
# Stable/headless startup flags
|
| 35 |
-
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
|
| 36 |
-
options.add_argument(f"--user-agent={user_agent}")
|
| 37 |
options.add_argument("--headless=new")
|
| 38 |
-
options.add_argument("--window-size=1920,1080")
|
| 39 |
-
options.add_argument("--no-first-run")
|
| 40 |
-
options.add_argument("--no-default-browser-check")
|
| 41 |
options.add_argument("--no-sandbox")
|
| 42 |
options.add_argument("--disable-dev-shm-usage")
|
| 43 |
options.add_argument("--disable-gpu")
|
| 44 |
-
options.add_argument("--disable-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
service = ChromeService(executable_path="/usr/bin/chromedriver")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
self.driver = webdriver.Chrome(service=service, options=options)
|
| 48 |
-
|
| 49 |
# Reduce webdriver detection
|
| 50 |
self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
|
| 51 |
'source': "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
|
| 52 |
})
|
|
|
|
| 53 |
return True, None
|
|
|
|
| 54 |
except Exception as e:
|
| 55 |
error_message = f"Message: {str(e)}"
|
| 56 |
print(f"CRITICAL ERROR in WebDriver Initialization: {error_message}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
return False, error_message
|
| 58 |
|
| 59 |
def micro_status(self, message):
|
|
@@ -196,6 +241,19 @@ class QuantumBot:
|
|
| 196 |
return 'Error'
|
| 197 |
|
| 198 |
def shutdown(self):
|
| 199 |
-
|
| 200 |
-
self.driver
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
print("[Bot] Chrome session closed.")
|
|
|
|
| 1 |
# worker.py
|
| 2 |
import time
|
| 3 |
import threading
|
| 4 |
+
import tempfile
|
| 5 |
+
import shutil
|
| 6 |
+
import os
|
| 7 |
+
import subprocess
|
| 8 |
|
| 9 |
from selenium import webdriver
|
| 10 |
from selenium.webdriver.chrome.service import Service as ChromeService
|
|
|
|
| 22 |
self.driver = None
|
| 23 |
self.DEFAULT_TIMEOUT = 30
|
| 24 |
self.termination_event = threading.Event()
|
| 25 |
+
self.temp_user_dir = None
|
| 26 |
+
|
| 27 |
+
def _kill_existing_chrome_processes(self):
|
| 28 |
+
"""Kill any existing Chrome processes that might be locking directories"""
|
| 29 |
+
try:
|
| 30 |
+
subprocess.run(['pkill', '-f', 'chrome'], capture_output=True, text=True)
|
| 31 |
+
subprocess.run(['pkill', '-f', 'chromium'], capture_output=True, text=True)
|
| 32 |
+
time.sleep(2)
|
| 33 |
+
except Exception:
|
| 34 |
+
pass
|
| 35 |
|
| 36 |
def initialize_driver(self):
|
| 37 |
try:
|
| 38 |
self.micro_status("Initializing headless browser...")
|
| 39 |
+
|
| 40 |
+
# Kill any existing Chrome processes first
|
| 41 |
+
self._kill_existing_chrome_processes()
|
| 42 |
+
|
| 43 |
+
# Create a completely isolated temporary directory for this session
|
| 44 |
+
self.temp_user_dir = tempfile.mkdtemp(prefix="hf-chrome-", dir="/tmp")
|
| 45 |
+
os.chmod(self.temp_user_dir, 0o755)
|
| 46 |
+
|
| 47 |
options = ChromeOptions()
|
| 48 |
+
|
| 49 |
+
# Force Chrome to use our isolated temporary directory
|
| 50 |
+
options.add_argument(f"--user-data-dir={self.temp_user_dir}")
|
| 51 |
+
options.add_argument(f"--data-path={self.temp_user_dir}")
|
| 52 |
+
options.add_argument(f"--disk-cache-dir={self.temp_user_dir}/cache")
|
| 53 |
+
|
| 54 |
+
# HuggingFace Spaces specific optimizations
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
options.add_argument("--headless=new")
|
|
|
|
|
|
|
|
|
|
| 56 |
options.add_argument("--no-sandbox")
|
| 57 |
options.add_argument("--disable-dev-shm-usage")
|
| 58 |
options.add_argument("--disable-gpu")
|
| 59 |
+
options.add_argument("--disable-software-rasterizer")
|
| 60 |
+
options.add_argument("--disable-background-timer-throttling")
|
| 61 |
+
options.add_argument("--disable-backgrounding-occluded-windows")
|
| 62 |
+
options.add_argument("--disable-renderer-backgrounding")
|
| 63 |
+
options.add_argument("--disable-features=TranslateUI,BlinkGenPropertyTrees")
|
| 64 |
+
options.add_argument("--remote-debugging-port=0")
|
| 65 |
+
options.add_argument("--no-first-run")
|
| 66 |
+
options.add_argument("--no-default-browser-check")
|
| 67 |
+
options.add_argument("--disable-extensions")
|
| 68 |
+
options.add_argument("--disable-plugins")
|
| 69 |
+
options.add_argument("--disable-images")
|
| 70 |
+
options.add_argument("--disable-javascript")
|
| 71 |
+
options.add_argument("--window-size=1920,1080")
|
| 72 |
+
|
| 73 |
+
# Set user agent
|
| 74 |
+
user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
| 75 |
+
options.add_argument(f"--user-agent={user_agent}")
|
| 76 |
+
|
| 77 |
+
# Set binary location for HF Spaces
|
| 78 |
+
options.binary_location = "/usr/bin/chromium"
|
| 79 |
+
|
| 80 |
service = ChromeService(executable_path="/usr/bin/chromedriver")
|
| 81 |
+
|
| 82 |
+
# Set service args for additional stability
|
| 83 |
+
service.service_args = ["--verbose", "--whitelisted-ips="]
|
| 84 |
+
|
| 85 |
self.driver = webdriver.Chrome(service=service, options=options)
|
| 86 |
+
|
| 87 |
# Reduce webdriver detection
|
| 88 |
self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
|
| 89 |
'source': "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
|
| 90 |
})
|
| 91 |
+
|
| 92 |
return True, None
|
| 93 |
+
|
| 94 |
except Exception as e:
|
| 95 |
error_message = f"Message: {str(e)}"
|
| 96 |
print(f"CRITICAL ERROR in WebDriver Initialization: {error_message}")
|
| 97 |
+
|
| 98 |
+
# Cleanup on failure
|
| 99 |
+
if self.temp_user_dir and os.path.exists(self.temp_user_dir):
|
| 100 |
+
shutil.rmtree(self.temp_user_dir, ignore_errors=True)
|
| 101 |
+
|
| 102 |
return False, error_message
|
| 103 |
|
| 104 |
def micro_status(self, message):
|
|
|
|
| 241 |
return 'Error'
|
| 242 |
|
| 243 |
def shutdown(self):
|
| 244 |
+
try:
|
| 245 |
+
if self.driver:
|
| 246 |
+
self.driver.quit()
|
| 247 |
+
|
| 248 |
+
# Kill any remaining Chrome processes
|
| 249 |
+
self._kill_existing_chrome_processes()
|
| 250 |
+
|
| 251 |
+
# Clean up temporary directory
|
| 252 |
+
if self.temp_user_dir and os.path.exists(self.temp_user_dir):
|
| 253 |
+
shutil.rmtree(self.temp_user_dir, ignore_errors=True)
|
| 254 |
+
print(f"[Bot] Cleaned up temporary Chrome profile: {self.temp_user_dir}")
|
| 255 |
+
|
| 256 |
+
except Exception as e:
|
| 257 |
+
print(f"[Bot] Error during shutdown: {e}")
|
| 258 |
+
|
| 259 |
print("[Bot] Chrome session closed.")
|