Snapshot: full project state
This commit is contained in:
87
vision.py
Normal file
87
vision.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""Screenshot & Vision Analysis — takes screenshots and classifies with local LLM."""
|
||||
import base64, json, subprocess, tempfile, os, urllib.request, time
|
||||
|
||||
OLLAMA_URL = "http://10.30.20.186:11434"
|
||||
VISION_MODEL = "minicpm-v4.5:8b" # Best local vision model available
|
||||
|
||||
def take_screenshot(url, width=1280, height=900):
|
||||
"""Take a screenshot of a URL using Playwright."""
|
||||
try:
|
||||
from playwright.sync_api import sync_playwright
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True)
|
||||
page = browser.new_page(viewport={'width': width, 'height': height})
|
||||
page.goto(url, wait_until='networkidle', timeout=15000)
|
||||
time.sleep(1) # Let page settle
|
||||
screenshot = page.screenshot(full_page=False)
|
||||
browser.close()
|
||||
return screenshot
|
||||
except ImportError:
|
||||
# Fallback: use macos screencapture via Safari (less reliable)
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Screenshot error for {url}: {e}")
|
||||
return None
|
||||
|
||||
def screenshot_to_base64(screenshot_bytes):
|
||||
"""Convert screenshot bytes to base64 for Ollama."""
|
||||
return base64.b64encode(screenshot_bytes).decode('utf-8')
|
||||
|
||||
def analyze_screenshot(url, screenshot_bytes, processor_name):
|
||||
"""Analyze a screenshot using local vision model to describe the site."""
|
||||
if not screenshot_bytes:
|
||||
return generate_placeholder_description(url, processor_name)
|
||||
|
||||
img_b64 = screenshot_to_base64(screenshot_bytes)
|
||||
|
||||
prompt = f"""Look at this screenshot of a website. The site likely uses {processor_name} as a payment processor.
|
||||
Describe this site in 2-3 sentences covering:
|
||||
1. What type of site is this? (e-commerce, gambling, SaaS, adult, CBD, firearms, subscription, crypto, etc.)
|
||||
2. What does it sell or offer?
|
||||
3. What's the overall vibe/quality level?
|
||||
|
||||
Respond with just the description, no prefixes."""
|
||||
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
f"{OLLAMA_URL}/api/generate",
|
||||
data=json.dumps({
|
||||
"model": VISION_MODEL,
|
||||
"prompt": prompt,
|
||||
"images": [img_b64],
|
||||
"stream": False,
|
||||
"options": {"temperature": 0.3, "num_predict": 150}
|
||||
}).encode(),
|
||||
headers={"Content-Type": "application/json"}
|
||||
)
|
||||
resp = urllib.request.urlopen(req, timeout=30)
|
||||
data = json.loads(resp.read())
|
||||
return data.get('response', '').strip()
|
||||
except Exception as e:
|
||||
print(f"Vision analysis error: {e}")
|
||||
return generate_placeholder_description(url, processor_name)
|
||||
|
||||
def generate_placeholder_description(url, processor_name):
|
||||
"""Generate a description when vision isn't available."""
|
||||
domain = urllib.parse.urlparse(url).netloc.replace('www.', '')
|
||||
return f"Website at {domain}. Uses {processor_name} for payment processing. Visit the site to learn more about their products and services."
|
||||
|
||||
def batch_analyze(sites, processor_name, max_screenshots=5):
|
||||
"""Analyze multiple sites with screenshots."""
|
||||
results = []
|
||||
for i, site in enumerate(sites[:max_screenshots]):
|
||||
url = site.get('url', '')
|
||||
if not url:
|
||||
continue
|
||||
|
||||
print(f" Screenshot {i+1}/{min(len(sites), max_screenshots)}: {site.get('domain','?')}")
|
||||
screenshot = take_screenshot(url)
|
||||
description = analyze_screenshot(url, screenshot, processor_name)
|
||||
|
||||
results.append({
|
||||
**site,
|
||||
'description': description,
|
||||
'screenshot_available': screenshot is not None,
|
||||
'screenshot_base64': screenshot_to_base64(screenshot) if screenshot else None
|
||||
})
|
||||
return results
|
||||
Reference in New Issue
Block a user