| | from flask import Flask, request, jsonify, send_from_directory, render_template_string |
| | import uuid |
| | import os |
| | import base64 |
| | from datetime import datetime |
| |
|
| | app = Flask(__name__) |
| | app.config['UPLOAD_FOLDER'] = 'uploads' |
| | os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) |
| |
|
| | |
| | @app.route('/uploads/<filename>') |
| | def serve_uploaded_file(filename): |
| | try: |
| | return send_from_directory(app.config['UPLOAD_FOLDER'], filename) |
| | except FileNotFoundError: |
| | return "File not found", 404 |
| |
|
| | @app.route('/') |
| | def home(): |
| | return render_template_string(''' |
| | <!DOCTYPE html> |
| | <html> |
| | <head> |
| | <title>StikkApp</title> |
| | <style> |
| | .option { padding: 15px; margin: 10px; background: #4285f4; |
| | color: white; text-align: center; border-radius: 5px; } |
| | #camera-container { display: none; position: relative; max-width: 100%; } |
| | #camera-preview { width: 100%; transform: scaleX(-1); } |
| | #capture-btn { position: absolute; bottom: 20px; left: 50%; |
| | transform: translateX(-50%); background: red; |
| | width: 60px; height: 60px; border-radius: 50%; } |
| | #file-input { display: none; } |
| | </style> |
| | </head> |
| | <body> |
| | <h1>Scegli origine immagine</h1> |
| | |
| | <div class="option" onclick="startCamera()">Fotocamera</div> |
| | <div class="option" onclick="document.getElementById('file-input').click()">Galleria</div> |
| | <div class="option" onclick="pasteFromClipboard()">Clipboard</div> |
| | |
| | <div id="camera-container"> |
| | <video id="camera-preview" autoplay playsinline></video> |
| | <button id="capture-btn" onclick="capturePhoto()"></button> |
| | </div> |
| | |
| | <input type="file" id="file-input" accept="image/*"> |
| | |
| | <script> |
| | // Gestione fotocamera |
| | let cameraStream; |
| | |
| | async function startCamera() { |
| | try { |
| | cameraStream = await navigator.mediaDevices.getUserMedia({ |
| | video: { facingMode: 'environment' } |
| | }); |
| | document.getElementById('camera-container').style.display = 'block'; |
| | document.getElementById('camera-preview').srcObject = cameraStream; |
| | } catch (err) { |
| | alert("Errore fotocamera: " + err.message); |
| | } |
| | } |
| | |
| | function capturePhoto() { |
| | const video = document.getElementById('camera-preview'); |
| | const canvas = document.createElement('canvas'); |
| | canvas.width = video.videoWidth; |
| | canvas.height = video.videoHeight; |
| | const ctx = canvas.getContext('2d'); |
| | |
| | // Corregge l'orientamento per fotocamera posteriore |
| | ctx.translate(canvas.width, 0); |
| | ctx.scale(-1, 1); |
| | ctx.drawImage(video, 0, 0, canvas.width, canvas.height); |
| | |
| | // Converti in JPG di qualità |
| | canvas.toBlob(blob => { |
| | const reader = new FileReader(); |
| | reader.onload = () => sendImage(reader.result); |
| | reader.readAsDataURL(blob); |
| | }, 'image/jpeg', 0.85); |
| | } |
| | |
| | // Gestione galleria |
| | document.getElementById('file-input').addEventListener('change', function(e) { |
| | if (e.target.files[0]) { |
| | const reader = new FileReader(); |
| | reader.onload = (event) => sendImage(event.target.result); |
| | reader.readAsDataURL(e.target.files[0]); |
| | } |
| | }); |
| | |
| | // Gestione clipboard |
| | async function pasteFromClipboard() { |
| | try { |
| | const items = await navigator.clipboard.read(); |
| | for (const item of items) { |
| | for (const type of item.types) { |
| | if (type.startsWith('image/')) { |
| | const blob = await item.getType(type); |
| | const reader = new FileReader(); |
| | reader.onload = (event) => sendImage(event.target.result); |
| | reader.readAsDataURL(blob); |
| | return; |
| | } |
| | } |
| | } |
| | alert("Nessuna immagine trovata"); |
| | } catch (err) { |
| | alert("Errore clipboard: " + err.message); |
| | } |
| | } |
| | |
| | // Invio immagine al server |
| | function sendImage(dataUrl) { |
| | fetch('/upload', { |
| | method: 'POST', |
| | body: JSON.stringify({ image: dataUrl }), |
| | headers: { 'Content-Type': 'application/json' } |
| | }) |
| | .then(response => { |
| | if (!response.ok) throw new Error('Upload failed'); |
| | return response.json(); |
| | }) |
| | .then(data => { |
| | window.location.href = `/preview?img=${encodeURIComponent(data.image_url)}&t=${Date.now()}`; |
| | }) |
| | .catch(error => { |
| | console.error('Upload error:', error); |
| | alert('Errore durante l\'upload'); |
| | }); |
| | } |
| | </script> |
| | </body> |
| | </html> |
| | ''') |
| |
|
| | @app.route('/upload', methods=['POST']) |
| | def upload_image(): |
| | try: |
| | |
| | image_data = request.json['image'].split(',')[1] |
| | binary_data = base64.b64decode(image_data) |
| | |
| | |
| | unique_id = uuid.uuid4().hex |
| | filename = f"{unique_id}.jpg" |
| | filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) |
| | |
| | |
| | with open(filepath, 'wb') as f: |
| | f.write(binary_data) |
| | |
| | return jsonify({ |
| | 'image_url': f'/uploads/{filename}', |
| | 'unique_id': unique_id |
| | }) |
| | |
| | except Exception as e: |
| | return jsonify({'error': str(e)}), 500 |
| |
|
| | @app.route('/preview') |
| | def preview(): |
| | img_url = request.args.get('img', '') |
| | return render_template_string(''' |
| | <!DOCTYPE html> |
| | <html> |
| | <head> |
| | <title>Anteprima</title> |
| | <style> |
| | img { max-width: 100%; height: auto; } |
| | button { padding: 10px 20px; margin: 10px; font-size: 16px; } |
| | </style> |
| | </head> |
| | <body> |
| | <img src="{{ img_url }}" id="preview-img"> |
| | <div> |
| | <button onclick="analyze()">Analizza</button> |
| | <button onclick="window.location.href='/'">Annulla</button> |
| | </div> |
| | <script> |
| | function analyze() { |
| | const img = document.getElementById('preview-img'); |
| | const timestamp = new Date().getTime(); |
| | window.location.href = `/analyze?img=${encodeURIComponent("{{ img_url }}")}&t=${timestamp}`; |
| | } |
| | </script> |
| | </body> |
| | </html> |
| | ''', img_url=img_url) |
| |
|
| | @app.route('/analyze') |
| | def analyze(): |
| | img_url = request.args.get('img', '') |
| | return render_template_string(''' |
| | <!DOCTYPE html> |
| | <html> |
| | <head> |
| | <title>Analisi</title> |
| | <style> |
| | #container { position: relative; } |
| | #target-img { max-width: 100%; } |
| | #selection-box { |
| | position: absolute; |
| | border: 2px solid red; |
| | display: none; |
| | pointer-events: none; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| | <div id="container"> |
| | <img src="{{ img_url }}" id="target-img"> |
| | <div id="selection-box"></div> |
| | </div> |
| | |
| | <script> |
| | const img = document.getElementById('target-img'); |
| | const box = document.getElementById('selection-box'); |
| | |
| | img.addEventListener('click', (e) => { |
| | const rect = img.getBoundingClientRect(); |
| | const x = e.clientX - rect.left; |
| | const y = e.clientY - rect.top; |
| | |
| | // Mostra il quadrato di selezione |
| | box.style.left = `${x - 25}px`; |
| | box.style.top = `${y - 25}px`; |
| | box.style.width = '50px'; |
| | box.style.height = '50px'; |
| | box.style.display = 'block'; |
| | |
| | // Invia le coordinate al server |
| | fetch('/search', { |
| | method: 'POST', |
| | headers: { 'Content-Type': 'application/json' }, |
| | body: JSON.stringify({ |
| | img_url: "{{ img_url }}", |
| | x: x, |
| | y: y |
| | }) |
| | }).then(response => response.json()) |
| | .then(data => { |
| | // Gestisci la risposta qui |
| | console.log(data); |
| | }); |
| | }); |
| | </script> |
| | </body> |
| | </html> |
| | ''', img_url=img_url) |
| |
|
| | @app.route('/search', methods=['POST']) |
| | def search(): |
| | data = request.json |
| | |
| | return jsonify({ |
| | 'result': 'success', |
| | 'data': data |
| | }) |
| |
|
| | if __name__ == '__main__': |
| | app.run(host='0.0.0.0', port=7860, debug=True) |