Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -5,12 +5,39 @@ from PIL import Image
|
|
5 |
import tempfile
|
6 |
import PIL.Image
|
7 |
import subprocess
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
app = Flask(__name__)
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
# Configuration de l'API Gemini
|
13 |
token = os.environ.get("TOKEN")
|
|
|
|
|
|
|
|
|
14 |
genai.configure(api_key=token)
|
15 |
|
16 |
generation_config = {
|
@@ -25,14 +52,11 @@ safety_settings = [
|
|
25 |
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
|
26 |
]
|
27 |
|
28 |
-
|
29 |
@app.route('/')
|
30 |
def generale():
|
|
|
31 |
return render_template("generale.html")
|
32 |
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
def upload_and_process_file(file_path):
|
37 |
"""Upload et traite un fichier avec l'API Gemini avec gestion des erreurs améliorée"""
|
38 |
max_retries = 3
|
@@ -40,19 +64,21 @@ def upload_and_process_file(file_path):
|
|
40 |
|
41 |
for attempt in range(max_retries):
|
42 |
try:
|
43 |
-
|
44 |
|
45 |
# Vérification du fichier
|
46 |
if not os.path.exists(file_path):
|
|
|
47 |
raise FileNotFoundError(f"Le fichier {file_path} n'existe pas")
|
48 |
|
49 |
file_size = os.path.getsize(file_path)
|
50 |
if file_size == 0:
|
|
|
51 |
raise ValueError(f"Le fichier {file_path} est vide")
|
52 |
|
53 |
# Upload du fichier
|
54 |
uploaded_file = genai.upload_file(path=file_path)
|
55 |
-
|
56 |
|
57 |
# Attente du traitement
|
58 |
timeout = 300 # 5 minutes
|
@@ -60,61 +86,56 @@ def upload_and_process_file(file_path):
|
|
60 |
|
61 |
while uploaded_file.state.name == "PROCESSING":
|
62 |
if time.time() - start_time > timeout:
|
|
|
63 |
raise TimeoutError("Timeout pendant le traitement du fichier")
|
64 |
|
65 |
-
|
66 |
time.sleep(10)
|
67 |
uploaded_file = genai.get_file(uploaded_file.name)
|
68 |
|
69 |
if uploaded_file.state.name == "FAILED":
|
|
|
70 |
raise ValueError(f"Échec du traitement: {uploaded_file.state.name}")
|
71 |
|
72 |
-
|
73 |
return uploaded_file
|
74 |
|
75 |
except ssl.SSLError as e:
|
76 |
-
|
77 |
if attempt < max_retries - 1:
|
78 |
time.sleep(retry_delay * (attempt + 1))
|
79 |
else:
|
80 |
raise
|
81 |
|
82 |
except Exception as e:
|
83 |
-
|
84 |
if attempt < max_retries - 1:
|
85 |
time.sleep(retry_delay * (attempt + 1))
|
86 |
else:
|
87 |
raise
|
88 |
-
|
89 |
-
|
90 |
|
91 |
def is_youtube_url(url):
|
92 |
"""Vérifie si l'URL est une URL YouTube"""
|
93 |
parsed = urlparse(url)
|
94 |
return any(domain in parsed.netloc for domain in ['youtube.com', 'youtu.be'])
|
95 |
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
def download_youtube_video(url):
|
100 |
"""Télécharge une vidéo YouTube en utilisant subprocess avec youtube-dl"""
|
101 |
try:
|
102 |
with tempfile.TemporaryDirectory() as temp_dir:
|
103 |
output_template = os.path.join(temp_dir, '%(title)s.%(ext)s')
|
104 |
|
105 |
-
# Commande youtube-dl avec options
|
106 |
command = [
|
107 |
'youtube-dl',
|
108 |
-
'--format', 'best[filesize<
|
109 |
'--quiet',
|
110 |
'--no-warnings',
|
111 |
'--output', output_template,
|
112 |
url
|
113 |
]
|
114 |
|
115 |
-
|
116 |
|
117 |
-
# Exécution de la commande
|
118 |
process = subprocess.Popen(
|
119 |
command,
|
120 |
stdout=subprocess.PIPE,
|
@@ -122,42 +143,43 @@ def download_youtube_video(url):
|
|
122 |
universal_newlines=True
|
123 |
)
|
124 |
|
125 |
-
# Attente et récupération du résultat
|
126 |
stdout, stderr = process.communicate()
|
127 |
|
128 |
if process.returncode != 0:
|
|
|
129 |
raise Exception(f"Erreur youtube-dl: {stderr}")
|
130 |
|
131 |
-
# Recherche du fichier téléchargé dans le dossier temporaire
|
132 |
downloaded_files = os.listdir(temp_dir)
|
133 |
if not downloaded_files:
|
|
|
134 |
raise FileNotFoundError("Aucun fichier n'a été téléchargé")
|
135 |
|
136 |
video_path = os.path.join(temp_dir, downloaded_files[0])
|
137 |
|
138 |
-
# Copie vers un fichier temporaire permanent
|
139 |
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(video_path)[1])
|
140 |
with open(video_path, 'rb') as f:
|
141 |
temp_file.write(f.read())
|
142 |
|
143 |
-
|
144 |
return temp_file.name
|
145 |
|
146 |
except Exception as e:
|
147 |
-
|
148 |
return None
|
149 |
|
150 |
def telecharger_pdf(url):
|
151 |
"""Télécharge un PDF et retourne le chemin du fichier"""
|
152 |
try:
|
|
|
153 |
response = requests.get(url)
|
154 |
response.raise_for_status()
|
155 |
|
156 |
with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
|
157 |
temp_file.write(response.content)
|
|
|
158 |
return temp_file.name
|
159 |
except Exception as e:
|
160 |
-
|
161 |
return None
|
162 |
|
163 |
def allowed_file(filename):
|
@@ -167,11 +189,15 @@ def allowed_file(filename):
|
|
167 |
|
168 |
@app.route('/submit', methods=['POST'])
|
169 |
def submit_question():
|
|
|
170 |
question = request.form.get('question')
|
171 |
urls = request.form.getlist('urls')
|
172 |
files = request.files.getlist('files')
|
173 |
|
174 |
-
|
|
|
|
|
|
|
175 |
content = [question]
|
176 |
temp_files = []
|
177 |
|
@@ -179,51 +205,62 @@ def submit_question():
|
|
179 |
# Traitement des fichiers uploadés
|
180 |
for file in files:
|
181 |
if file and allowed_file(file.filename):
|
|
|
182 |
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as temp_file:
|
183 |
file.save(temp_file.name)
|
184 |
temp_files.append(temp_file.name)
|
185 |
if file.filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
|
186 |
content.append(PIL.Image.open(temp_file.name))
|
|
|
187 |
else:
|
188 |
uploaded_file = upload_and_process_file(temp_file.name)
|
189 |
content.append(uploaded_file)
|
|
|
190 |
|
191 |
# Traitement des URLs
|
192 |
for url in urls:
|
193 |
-
|
194 |
if is_youtube_url(url):
|
195 |
video_path = download_youtube_video(url)
|
196 |
if video_path:
|
197 |
temp_files.append(video_path)
|
198 |
uploaded_file = upload_and_process_file(video_path)
|
199 |
content.append(uploaded_file)
|
|
|
200 |
elif url.lower().endswith('.pdf'):
|
201 |
pdf_path = telecharger_pdf(url)
|
202 |
if pdf_path:
|
203 |
temp_files.append(pdf_path)
|
204 |
uploaded_file = upload_and_process_file(pdf_path)
|
205 |
content.append(uploaded_file)
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
|
210 |
# Génération de contenu avec Gemini
|
|
|
211 |
model = genai.GenerativeModel(
|
212 |
model_name="models/gemini-2.0-flash-exp",
|
213 |
safety_settings=safety_settings,
|
214 |
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."
|
215 |
)
|
|
|
|
|
216 |
response = model.generate_content(content, request_options={"timeout": 600})
|
|
|
|
|
217 |
return jsonify({"response": response.text})
|
218 |
|
219 |
except Exception as e:
|
|
|
220 |
return jsonify({"error": str(e)}), 500
|
221 |
finally:
|
222 |
# Nettoyage des fichiers temporaires
|
223 |
for temp_file in temp_files:
|
224 |
try:
|
225 |
os.unlink(temp_file)
|
|
|
226 |
except Exception as e:
|
227 |
-
|
228 |
-
|
229 |
|
|
|
|
|
|
|
|
5 |
import tempfile
|
6 |
import PIL.Image
|
7 |
import subprocess
|
8 |
+
import logging
|
9 |
+
from urllib.parse import urlparse
|
10 |
+
import requests
|
11 |
+
import time
|
12 |
+
import ssl
|
13 |
+
from logging.handlers import RotatingFileHandler
|
14 |
|
15 |
app = Flask(__name__)
|
16 |
|
17 |
+
# Configuration des logs
|
18 |
+
log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
19 |
+
log_file = 'app.log'
|
20 |
+
|
21 |
+
# Handler pour fichier avec rotation
|
22 |
+
file_handler = RotatingFileHandler(log_file, maxBytes=10485760, backupCount=5) # 10MB par fichier, max 5 fichiers
|
23 |
+
file_handler.setFormatter(log_formatter)
|
24 |
+
|
25 |
+
# Handler pour console
|
26 |
+
console_handler = logging.StreamHandler()
|
27 |
+
console_handler.setFormatter(log_formatter)
|
28 |
+
|
29 |
+
# Configuration du logger principal
|
30 |
+
logger = logging.getLogger(__name__)
|
31 |
+
logger.setLevel(logging.INFO)
|
32 |
+
logger.addHandler(file_handler)
|
33 |
+
logger.addHandler(console_handler)
|
34 |
+
|
35 |
# Configuration de l'API Gemini
|
36 |
token = os.environ.get("TOKEN")
|
37 |
+
if not token:
|
38 |
+
logger.error("Token API non trouvé dans les variables d'environnement")
|
39 |
+
raise ValueError("Token API manquant")
|
40 |
+
|
41 |
genai.configure(api_key=token)
|
42 |
|
43 |
generation_config = {
|
|
|
52 |
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
|
53 |
]
|
54 |
|
|
|
55 |
@app.route('/')
|
56 |
def generale():
|
57 |
+
logger.info("Accès à la page principale")
|
58 |
return render_template("generale.html")
|
59 |
|
|
|
|
|
|
|
60 |
def upload_and_process_file(file_path):
|
61 |
"""Upload et traite un fichier avec l'API Gemini avec gestion des erreurs améliorée"""
|
62 |
max_retries = 3
|
|
|
64 |
|
65 |
for attempt in range(max_retries):
|
66 |
try:
|
67 |
+
logger.info(f"Tentative d'upload {attempt + 1}/{max_retries} pour {file_path}")
|
68 |
|
69 |
# Vérification du fichier
|
70 |
if not os.path.exists(file_path):
|
71 |
+
logger.error(f"Fichier non trouvé: {file_path}")
|
72 |
raise FileNotFoundError(f"Le fichier {file_path} n'existe pas")
|
73 |
|
74 |
file_size = os.path.getsize(file_path)
|
75 |
if file_size == 0:
|
76 |
+
logger.error(f"Fichier vide: {file_path}")
|
77 |
raise ValueError(f"Le fichier {file_path} est vide")
|
78 |
|
79 |
# Upload du fichier
|
80 |
uploaded_file = genai.upload_file(path=file_path)
|
81 |
+
logger.info(f"Upload réussi: {uploaded_file.uri}")
|
82 |
|
83 |
# Attente du traitement
|
84 |
timeout = 300 # 5 minutes
|
|
|
86 |
|
87 |
while uploaded_file.state.name == "PROCESSING":
|
88 |
if time.time() - start_time > timeout:
|
89 |
+
logger.error("Timeout pendant le traitement du fichier")
|
90 |
raise TimeoutError("Timeout pendant le traitement du fichier")
|
91 |
|
92 |
+
logger.debug(f"En attente du traitement... Temps écoulé: {int(time.time() - start_time)}s")
|
93 |
time.sleep(10)
|
94 |
uploaded_file = genai.get_file(uploaded_file.name)
|
95 |
|
96 |
if uploaded_file.state.name == "FAILED":
|
97 |
+
logger.error(f"Échec du traitement: {uploaded_file.state.name}")
|
98 |
raise ValueError(f"Échec du traitement: {uploaded_file.state.name}")
|
99 |
|
100 |
+
logger.info(f"Traitement terminé avec succès: {uploaded_file.uri}")
|
101 |
return uploaded_file
|
102 |
|
103 |
except ssl.SSLError as e:
|
104 |
+
logger.error(f"Erreur SSL lors de l'upload (tentative {attempt + 1}): {e}")
|
105 |
if attempt < max_retries - 1:
|
106 |
time.sleep(retry_delay * (attempt + 1))
|
107 |
else:
|
108 |
raise
|
109 |
|
110 |
except Exception as e:
|
111 |
+
logger.error(f"Erreur lors de l'upload (tentative {attempt + 1}): {e}")
|
112 |
if attempt < max_retries - 1:
|
113 |
time.sleep(retry_delay * (attempt + 1))
|
114 |
else:
|
115 |
raise
|
|
|
|
|
116 |
|
117 |
def is_youtube_url(url):
|
118 |
"""Vérifie si l'URL est une URL YouTube"""
|
119 |
parsed = urlparse(url)
|
120 |
return any(domain in parsed.netloc for domain in ['youtube.com', 'youtu.be'])
|
121 |
|
|
|
|
|
|
|
122 |
def download_youtube_video(url):
|
123 |
"""Télécharge une vidéo YouTube en utilisant subprocess avec youtube-dl"""
|
124 |
try:
|
125 |
with tempfile.TemporaryDirectory() as temp_dir:
|
126 |
output_template = os.path.join(temp_dir, '%(title)s.%(ext)s')
|
127 |
|
|
|
128 |
command = [
|
129 |
'youtube-dl',
|
130 |
+
'--format', 'best[filesize<50M]',
|
131 |
'--quiet',
|
132 |
'--no-warnings',
|
133 |
'--output', output_template,
|
134 |
url
|
135 |
]
|
136 |
|
137 |
+
logger.info(f"Début du téléchargement de la vidéo: {url}")
|
138 |
|
|
|
139 |
process = subprocess.Popen(
|
140 |
command,
|
141 |
stdout=subprocess.PIPE,
|
|
|
143 |
universal_newlines=True
|
144 |
)
|
145 |
|
|
|
146 |
stdout, stderr = process.communicate()
|
147 |
|
148 |
if process.returncode != 0:
|
149 |
+
logger.error(f"Erreur youtube-dl: {stderr}")
|
150 |
raise Exception(f"Erreur youtube-dl: {stderr}")
|
151 |
|
|
|
152 |
downloaded_files = os.listdir(temp_dir)
|
153 |
if not downloaded_files:
|
154 |
+
logger.error("Aucun fichier n'a été téléchargé")
|
155 |
raise FileNotFoundError("Aucun fichier n'a été téléchargé")
|
156 |
|
157 |
video_path = os.path.join(temp_dir, downloaded_files[0])
|
158 |
|
|
|
159 |
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(video_path)[1])
|
160 |
with open(video_path, 'rb') as f:
|
161 |
temp_file.write(f.read())
|
162 |
|
163 |
+
logger.info(f"Vidéo téléchargée avec succès: {temp_file.name}")
|
164 |
return temp_file.name
|
165 |
|
166 |
except Exception as e:
|
167 |
+
logger.error(f"Erreur lors du téléchargement de la vidéo: {e}")
|
168 |
return None
|
169 |
|
170 |
def telecharger_pdf(url):
|
171 |
"""Télécharge un PDF et retourne le chemin du fichier"""
|
172 |
try:
|
173 |
+
logger.info(f"Début du téléchargement du PDF: {url}")
|
174 |
response = requests.get(url)
|
175 |
response.raise_for_status()
|
176 |
|
177 |
with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
|
178 |
temp_file.write(response.content)
|
179 |
+
logger.info(f"PDF téléchargé avec succès: {temp_file.name}")
|
180 |
return temp_file.name
|
181 |
except Exception as e:
|
182 |
+
logger.error(f"Erreur lors du téléchargement du PDF: {e}")
|
183 |
return None
|
184 |
|
185 |
def allowed_file(filename):
|
|
|
189 |
|
190 |
@app.route('/submit', methods=['POST'])
|
191 |
def submit_question():
|
192 |
+
logger.info("Nouvelle soumission reçue")
|
193 |
question = request.form.get('question')
|
194 |
urls = request.form.getlist('urls')
|
195 |
files = request.files.getlist('files')
|
196 |
|
197 |
+
logger.info(f"Question reçue: {question}")
|
198 |
+
logger.info(f"URLs reçues: {urls}")
|
199 |
+
logger.info(f"Nombre de fichiers reçus: {len(files)}")
|
200 |
+
|
201 |
content = [question]
|
202 |
temp_files = []
|
203 |
|
|
|
205 |
# Traitement des fichiers uploadés
|
206 |
for file in files:
|
207 |
if file and allowed_file(file.filename):
|
208 |
+
logger.info(f"Traitement du fichier: {file.filename}")
|
209 |
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as temp_file:
|
210 |
file.save(temp_file.name)
|
211 |
temp_files.append(temp_file.name)
|
212 |
if file.filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
|
213 |
content.append(PIL.Image.open(temp_file.name))
|
214 |
+
logger.info(f"Image ajoutée au contenu: {file.filename}")
|
215 |
else:
|
216 |
uploaded_file = upload_and_process_file(temp_file.name)
|
217 |
content.append(uploaded_file)
|
218 |
+
logger.info(f"Fichier uploadé et ajouté au contenu: {file.filename}")
|
219 |
|
220 |
# Traitement des URLs
|
221 |
for url in urls:
|
222 |
+
logger.info(f"Traitement de l'URL: {url}")
|
223 |
if is_youtube_url(url):
|
224 |
video_path = download_youtube_video(url)
|
225 |
if video_path:
|
226 |
temp_files.append(video_path)
|
227 |
uploaded_file = upload_and_process_file(video_path)
|
228 |
content.append(uploaded_file)
|
229 |
+
logger.info(f"Vidéo YouTube traitée et ajoutée au contenu: {url}")
|
230 |
elif url.lower().endswith('.pdf'):
|
231 |
pdf_path = telecharger_pdf(url)
|
232 |
if pdf_path:
|
233 |
temp_files.append(pdf_path)
|
234 |
uploaded_file = upload_and_process_file(pdf_path)
|
235 |
content.append(uploaded_file)
|
236 |
+
logger.info(f"PDF téléchargé et ajouté au contenu: {url}")
|
|
|
|
|
237 |
|
238 |
# Génération de contenu avec Gemini
|
239 |
+
logger.info("Initialisation du modèle Gemini")
|
240 |
model = genai.GenerativeModel(
|
241 |
model_name="models/gemini-2.0-flash-exp",
|
242 |
safety_settings=safety_settings,
|
243 |
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."
|
244 |
)
|
245 |
+
|
246 |
+
logger.info("Génération de la réponse")
|
247 |
response = model.generate_content(content, request_options={"timeout": 600})
|
248 |
+
logger.info("Réponse générée avec succès")
|
249 |
+
|
250 |
return jsonify({"response": response.text})
|
251 |
|
252 |
except Exception as e:
|
253 |
+
logger.error(f"Erreur lors du traitement de la requête: {e}", exc_info=True)
|
254 |
return jsonify({"error": str(e)}), 500
|
255 |
finally:
|
256 |
# Nettoyage des fichiers temporaires
|
257 |
for temp_file in temp_files:
|
258 |
try:
|
259 |
os.unlink(temp_file)
|
260 |
+
logger.debug(f"Fichier temporaire supprimé: {temp_file}")
|
261 |
except Exception as e:
|
262 |
+
logger.error(f"Erreur lors de la suppression du fichier temporaire {temp_file}: {e}")
|
|
|
263 |
|
264 |
+
if __name__ == '__main__':
|
265 |
+
logger.info("Démarrage de l'application")
|
266 |
+
app.run()
|