Spaces:
Sleeping
Sleeping
File size: 13,872 Bytes
446664e 6a0ca7e b3f9d19 3733555 446664e 3733555 446664e 3733555 446664e 3733555 446664e 5ec64d2 446664e 3733555 5ec64d2 446664e 3733555 446664e 5ec64d2 446664e 3733555 446664e 5ec64d2 446664e 6407653 3733555 446664e 6407653 446664e 5ec64d2 6407653 3733555 6407653 3733555 446664e 6407653 446664e 6407653 3733555 6407653 3733555 6407653 446664e 3733555 446664e 5ec64d2 446664e 3733555 446664e 3dc384c 446664e bea9f70 446664e b24ee2b 446664e b3f9d19 5ec64d2 b24ee2b b3f9d19 8170733 3733555 b3f9d19 b24ee2b 2451ccd b3f9d19 2451ccd b3f9d19 bea9f70 b24ee2b bea9f70 b24ee2b b3f9d19 b24ee2b b3f9d19 b24ee2b b3f9d19 3733555 b3f9d19 b24ee2b b3f9d19 3733555 b3f9d19 5ec64d2 446664e 3733555 446664e 3733555 446664e 3733555 446664e 3733555 446664e 3733555 446664e 5ec64d2 3733555 446664e 5ec64d2 446664e 3733555 446664e 3733555 446664e 6407653 446664e 3733555 446664e 3733555 446664e 6407653 446664e 3733555 446664e 6407653 446664e 3733555 8bc9850 446664e 3733555 446664e 47ad7bc 446664e 8bc9850 3733555 6407653 3733555 446664e 3733555 446664e 3733555 446664e 3733555 6407653 3733555 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
from flask import Flask, render_template, request, jsonify
import google.generativeai as genai
import os
from PIL import Image
import tempfile
import PIL.Image
import subprocess
import logging
from urllib.parse import urlparse
import requests
import time
import ssl
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
# Configuration des logs
log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log_file = 'app.log'
# Handler pour fichier avec rotation
file_handler = RotatingFileHandler(log_file, maxBytes=10485760, backupCount=5) # 10MB par fichier, max 5 fichiers
file_handler.setFormatter(log_formatter)
# Handler pour console
console_handler = logging.StreamHandler()
console_handler.setFormatter(log_formatter)
# Configuration du logger principal
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# Configuration de l'API Gemini
token = os.environ.get("TOKEN")
if not token:
logger.error("Token API non trouvé dans les variables d'environnement")
raise ValueError("Token API manquant")
genai.configure(api_key=token)
generation_config = {
"temperature": 1,
"max_output_tokens": 8192,
}
safety_settings = [
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
]
@app.route('/')
def generale():
logger.info("Accès à la page principale")
return render_template("generale.html")
def upload_and_process_file(file_path):
"""Upload et traite un fichier avec l'API Gemini avec gestion des erreurs améliorée"""
max_retries = 3
retry_delay = 2 # secondes
for attempt in range(max_retries):
try:
logger.info(f"Tentative d'upload {attempt + 1}/{max_retries} pour {file_path}")
# Vérification du fichier
if not os.path.exists(file_path):
logger.error(f"Fichier non trouvé: {file_path}")
raise FileNotFoundError(f"Le fichier {file_path} n'existe pas")
file_size = os.path.getsize(file_path)
if file_size == 0:
logger.error(f"Fichier vide: {file_path}")
raise ValueError(f"Le fichier {file_path} est vide")
# Upload du fichier
uploaded_file = genai.upload_file(path=file_path)
logger.info(f"Upload réussi: {uploaded_file.uri}")
# Attente du traitement
timeout = 300 # 5 minutes
start_time = time.time()
while uploaded_file.state.name == "PROCESSING":
if time.time() - start_time > timeout:
logger.error("Timeout pendant le traitement du fichier")
raise TimeoutError("Timeout pendant le traitement du fichier")
logger.debug(f"En attente du traitement... Temps écoulé: {int(time.time() - start_time)}s")
time.sleep(10)
uploaded_file = genai.get_file(uploaded_file.name)
if uploaded_file.state.name == "FAILED":
logger.error(f"Échec du traitement: {uploaded_file.state.name}")
raise ValueError(f"Échec du traitement: {uploaded_file.state.name}")
logger.info(f"Traitement terminé avec succès: {uploaded_file.uri}")
return uploaded_file
except ssl.SSLError as e:
logger.error(f"Erreur SSL lors de l'upload (tentative {attempt + 1}): {e}")
if attempt < max_retries - 1:
time.sleep(retry_delay * (attempt + 1))
else:
raise
except Exception as e:
logger.error(f"Erreur lors de l'upload (tentative {attempt + 1}): {e}")
if attempt < max_retries - 1:
time.sleep(retry_delay * (attempt + 1))
else:
raise
def is_youtube_url(url):
"""Vérifie si l'URL est une URL YouTube"""
parsed = urlparse(url)
return any(domain in parsed.netloc for domain in ['youtube.com', 'youtu.be'])
def download_youtube_video(url):
"""Télécharge une vidéo YouTube en utilisant yt-dlp avec une configuration adaptée aux conteneurs"""
try:
with tempfile.TemporaryDirectory() as temp_dir:
output_template = os.path.join(temp_dir, '%(title)s.%(ext)s')
# Configuration optimisée pour environnement conteneurisé
command = [
'yt-dlp',
'--format', 'best[filesize<50M]',
'--quiet',
'--no-warnings',
'--output', output_template,
'--extract-audio', # Tenter d'abord l'extraction audio si la vidéo échoue
'--format-sort', 'res,ext:mp4:m4a', # Prioriser les formats plus légers
'--user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'--referer', 'https://www.youtube.com/',
'--geo-bypass', # Contourner les restrictions géographiques
'--no-check-certificates', # Ignorer les erreurs de certificat
'--force-ipv4', # Forcer IPv4 pour plus de stabilité
'--ignore-errors', # Continuer en cas d'erreurs mineures
'--no-playlist', # Éviter le téléchargement de playlists
'--extractor-retries', '3',
'--file-access-retries', '3',
'--fragment-retries', '3',
'--retry-sleep', '5',
'--sleep-requests','1.5',
'--min-sleep-interval','60',
'--max-sleep-interval','90',
url
]
logger.info(f"Début du, téléchargement de la vidéo: {url}")
try:
# Vérifier la version de yt-dlp
version_process = subprocess.run(['yt-dlp', '--version'],
capture_output=True,
text=True,
check=True)
logger.info(f"Version de yt-dlp: {version_process.stdout.strip()}")
except subprocess.SubprocessError as e:
logger.error(f"Erreur lors de la vérification de yt-dlp: {e}")
raise RuntimeError("yt-dlp n'est pas correctement installé")
# Premier essai avec la configuration standard
process = subprocess.run(
command,
capture_output=True,
text=True
)
if process.returncode != 0:
logger.warning(f"Premier essai échoué: {process.stderr}")
logger.info("Tentative avec configuration alternative...")
# Configuration alternative
alternative_command = command.copy()
alternative_command.extend([
'--format', 'worst', # Utiliser la qualité la plus basse
'--prefer-insecure', # Préférer les connexions non-sécurisées si nécessaire
'--socket-timeout', '30',
'--external-downloader', 'aria2c', # Utiliser aria2c comme downloader alternatif
'--external-downloader-args', 'aria2c:"--min-split-size=1M --max-connection-per-server=16 --max-concurrent-downloads=16 --split=16"'
])
process = subprocess.run(
alternative_command,
capture_output=True,
text=True
)
if process.returncode != 0:
logger.error(f"Échec du téléchargement après tentatives alternatives: {process.stderr}")
raise Exception(f"Échec du téléchargement: {process.stderr}")
downloaded_files = os.listdir(temp_dir)
if not downloaded_files:
logger.error("Aucun fichier n'a été téléchargé")
raise FileNotFoundError("Aucun fichier n'a été téléchargé")
video_path = os.path.join(temp_dir, downloaded_files[0])
# Vérifier la taille du fichier
file_size = os.path.getsize(video_path)
if file_size == 0:
raise ValueError("Le fichier téléchargé est vide")
logger.info(f"Fichier téléchargé: {video_path} ({file_size} bytes)")
# Copier le fichier vers un emplacement temporaire
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(video_path)[1])
with open(video_path, 'rb') as f:
temp_file.write(f.read())
logger.info(f"Vidéo téléchargée avec succès: {temp_file.name}")
return temp_file.name
except Exception as e:
logger.error(f"Erreur lors du téléchargement de la vidéo: {e}")
return None
def telecharger_pdf(url):
"""Télécharge un PDF et retourne le chemin du fichier"""
try:
logger.info(f"Début du téléchargement du PDF: {url}")
response = requests.get(url)
response.raise_for_status()
with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
temp_file.write(response.content)
logger.info(f"PDF téléchargé avec succès: {temp_file.name}")
return temp_file.name
except Exception as e:
logger.error(f"Erreur lors du téléchargement du PDF: {e}")
return None
def allowed_file(filename):
"""Vérifie si l'extension du fichier est autorisée"""
ALLOWED_EXTENSIONS = {'pdf', 'png', 'jpg', 'jpeg', 'gif', 'mp4', 'mov', 'avi'}
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/submit', methods=['POST'])
def submit_question():
logger.info("Nouvelle soumission reçue")
question = request.form.get('question')
urls = request.form.getlist('urls')
files = request.files.getlist('files')
logger.info(f"Question reçue: {question}")
logger.info(f"URLs reçues: {urls}")
logger.info(f"Nombre de fichiers reçus: {len(files)}")
content = [question]
temp_files = []
try:
# Traitement des fichiers uploadés
for file in files:
if file and allowed_file(file.filename):
logger.info(f"Traitement du fichier: {file.filename}")
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as temp_file:
file.save(temp_file.name)
temp_files.append(temp_file.name)
if file.filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
content.append(PIL.Image.open(temp_file.name))
logger.info(f"Image ajoutée au contenu: {file.filename}")
else:
uploaded_file = upload_and_process_file(temp_file.name)
content.append(uploaded_file)
logger.info(f"Fichier uploadé et ajouté au contenu: {file.filename}")
# Traitement des URLs
for url in urls:
logger.info(f"Traitement de l'URL: {url}")
if is_youtube_url(url):
video_path = download_youtube_video(url)
if video_path:
temp_files.append(video_path)
uploaded_file = upload_and_process_file(video_path)
content.append(uploaded_file)
logger.info(f"Vidéo YouTube traitée et ajoutée au contenu: {url}")
elif url.lower().endswith('.pdf'):
pdf_path = telecharger_pdf(url)
if pdf_path:
temp_files.append(pdf_path)
uploaded_file = upload_and_process_file(pdf_path)
content.append(uploaded_file)
logger.info(f"PDF téléchargé et ajouté au contenu: {url}")
# Génération de contenu avec Gemini
logger.info("Initialisation du modèle Gemini")
model = genai.GenerativeModel(
model_name="models/gemini-2.0-flash",
safety_settings=safety_settings,
system_instruction="Tu es un assistant intelligent. ton but est d'assister au mieux que tu peux. tu as été créé par Aenir et tu t'appelles Mariam."
)
logger.info("Génération de la réponse")
response = model.generate_content(content, request_options={"timeout": 600})
logger.info("Réponse générée avec succès")
return jsonify({"response": response.text})
except Exception as e:
logger.error(f"Erreur lors du traitement de la requête: {e}", exc_info=True)
return jsonify({"error": str(e)}), 500
finally:
# Nettoyage des fichiers temporaires
for temp_file in temp_files:
try:
os.unlink(temp_file)
logger.debug(f"Fichier temporaire supprimé: {temp_file}")
except Exception as e:
logger.error(f"Erreur lors de la suppression du fichier temporaire {temp_file}: {e}")
if __name__ == '__main__':
logger.info("Démarrage de l'application")
app.run() |