mirror of
https://github.com/Zippland/Snap-Solver.git
synced 2026-01-19 09:41:15 +08:00
181 lines
5.4 KiB
Python
181 lines
5.4 KiB
Python
from flask import Flask, jsonify, render_template, request
|
|
from flask_socketio import SocketIO
|
|
import pyautogui
|
|
import base64
|
|
from io import BytesIO
|
|
import socket
|
|
from threading import Thread
|
|
import pystray
|
|
from PIL import Image, ImageDraw
|
|
import pyperclip
|
|
from models import ModelFactory
|
|
|
|
app = Flask(__name__)
|
|
socketio = SocketIO(app, cors_allowed_origins="*")
|
|
|
|
def get_local_ip():
|
|
try:
|
|
# Get local IP address
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
s.connect(("8.8.8.8", 80))
|
|
ip = s.getsockname()[0]
|
|
s.close()
|
|
return ip
|
|
except Exception:
|
|
return "127.0.0.1"
|
|
|
|
def create_tray_icon():
|
|
# Create a simple icon (a colored circle)
|
|
icon_size = 64
|
|
icon_image = Image.new('RGB', (icon_size, icon_size), color='white')
|
|
draw = ImageDraw.Draw(icon_image)
|
|
draw.ellipse([4, 4, icon_size-4, icon_size-4], fill='#2196F3') # Using the primary color from our CSS
|
|
|
|
# Get server URL
|
|
ip_address = get_local_ip()
|
|
server_url = f"http://{ip_address}:5000"
|
|
|
|
# Create menu
|
|
menu = pystray.Menu(
|
|
pystray.MenuItem(server_url, lambda icon, item: None, enabled=False),
|
|
pystray.MenuItem("Exit", lambda icon, item: icon.stop())
|
|
)
|
|
|
|
# Create icon
|
|
icon = pystray.Icon(
|
|
"SnapSolver",
|
|
icon_image,
|
|
"Snap Solver",
|
|
menu
|
|
)
|
|
|
|
return icon
|
|
|
|
@app.route('/')
|
|
def index():
|
|
local_ip = get_local_ip()
|
|
return render_template('index.html', local_ip=local_ip)
|
|
|
|
@socketio.on('connect')
|
|
def handle_connect():
|
|
print('Client connected')
|
|
|
|
@socketio.on('disconnect')
|
|
def handle_disconnect():
|
|
print('Client disconnected')
|
|
|
|
def stream_model_response(response_generator, sid):
|
|
"""Stream model responses to the client"""
|
|
try:
|
|
print("Starting response streaming...")
|
|
|
|
# Send initial status
|
|
socketio.emit('claude_response', {
|
|
'status': 'started',
|
|
'content': ''
|
|
}, room=sid)
|
|
print("Sent initial status to client")
|
|
|
|
# Stream responses
|
|
for response in response_generator:
|
|
socketio.emit('claude_response', response, room=sid)
|
|
|
|
except Exception as e:
|
|
error_msg = f"Streaming error: {str(e)}"
|
|
print(error_msg)
|
|
socketio.emit('claude_response', {
|
|
'status': 'error',
|
|
'error': error_msg
|
|
}, room=sid)
|
|
|
|
@socketio.on('request_screenshot')
|
|
def handle_screenshot_request():
|
|
try:
|
|
# Capture the screen
|
|
screenshot = pyautogui.screenshot()
|
|
|
|
# Convert the image to base64 string
|
|
buffered = BytesIO()
|
|
screenshot.save(buffered, format="PNG")
|
|
img_str = base64.b64encode(buffered.getvalue()).decode()
|
|
|
|
# Emit the screenshot back to the client
|
|
socketio.emit('screenshot_response', {
|
|
'success': True,
|
|
'image': img_str
|
|
})
|
|
except Exception as e:
|
|
socketio.emit('screenshot_response', {
|
|
'success': False,
|
|
'error': str(e)
|
|
})
|
|
|
|
@socketio.on('analyze_image')
|
|
def handle_image_analysis(data):
|
|
try:
|
|
print("Starting image analysis...")
|
|
settings = data['settings']
|
|
image_data = data['image'] # Base64 encoded image
|
|
|
|
# Validate required settings
|
|
if not settings.get('apiKey'):
|
|
raise ValueError("API key is required")
|
|
|
|
print("Using API key:", settings['apiKey'][:6] + "..." if settings.get('apiKey') else "None")
|
|
print("Selected model:", settings.get('model', 'claude-3-5-sonnet-20241022'))
|
|
|
|
# Configure proxy settings if enabled
|
|
proxies = None
|
|
if settings.get('proxyEnabled', False):
|
|
proxy_host = settings.get('proxyHost', '127.0.0.1')
|
|
proxy_port = settings.get('proxyPort', '4780')
|
|
proxies = {
|
|
'http': f'http://{proxy_host}:{proxy_port}',
|
|
'https': f'http://{proxy_host}:{proxy_port}'
|
|
}
|
|
|
|
try:
|
|
# Create model instance using factory
|
|
model = ModelFactory.create_model(
|
|
model_name=settings.get('model', 'claude-3-5-sonnet-20241022'),
|
|
api_key=settings['apiKey'],
|
|
temperature=float(settings.get('temperature', 0.7)),
|
|
system_prompt=settings.get('systemPrompt')
|
|
)
|
|
|
|
# Start streaming in a separate thread
|
|
Thread(
|
|
target=stream_model_response,
|
|
args=(model.analyze_image(image_data, proxies), request.sid)
|
|
).start()
|
|
|
|
except Exception as e:
|
|
socketio.emit('claude_response', {
|
|
'status': 'error',
|
|
'error': f'API error: {str(e)}'
|
|
}, room=request.sid)
|
|
|
|
except Exception as e:
|
|
print(f"Analysis error: {str(e)}")
|
|
socketio.emit('claude_response', {
|
|
'status': 'error',
|
|
'error': f'Analysis error: {str(e)}'
|
|
}, room=request.sid)
|
|
|
|
def run_tray():
|
|
icon = create_tray_icon()
|
|
icon.run()
|
|
|
|
if __name__ == '__main__':
|
|
local_ip = get_local_ip()
|
|
print(f"Local IP Address: {local_ip}")
|
|
print(f"Connect from your mobile device using: {local_ip}:5000")
|
|
|
|
# Run system tray icon in a separate thread
|
|
tray_thread = Thread(target=run_tray)
|
|
tray_thread.daemon = True
|
|
tray_thread.start()
|
|
|
|
# Run Flask in the main thread without debug mode
|
|
socketio.run(app, host='0.0.0.0', port=5000, allow_unsafe_werkzeug=True)
|