Update app.py
Browse files
app.py
CHANGED
@@ -12,7 +12,6 @@ app = Flask(__name__)
|
|
12 |
app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'une_cle_secrete_par_defaut_pour_dev')
|
13 |
|
14 |
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://Podcast_owner:npg_gFdMDLO9lVa0@ep-delicate-surf-a4v7wopn-pooler.us-east-1.aws.neon.tech/Podcast?sslmode=require'
|
15 |
-
|
16 |
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
17 |
|
18 |
db = SQLAlchemy(app)
|
@@ -35,6 +34,16 @@ class Podcast(db.Model):
|
|
35 |
def __repr__(self):
|
36 |
return f'<Podcast {self.name}>'
|
37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
@app.route('/')
|
39 |
def index():
|
40 |
try:
|
@@ -122,27 +131,29 @@ def play_podcast_route(podcast_id):
|
|
122 |
return jsonify({'audio_url': audio_url})
|
123 |
else:
|
124 |
logger.warning(f"Fichier cache {podcast.filename_cache} pour podcast {podcast.id} non trouvé. Re-téléchargement.")
|
125 |
-
podcast.filename_cache = None
|
126 |
|
127 |
final_cached_filepath = None
|
128 |
try:
|
129 |
parsed_url = urlparse(podcast.url)
|
130 |
path_parts = os.path.splitext(parsed_url.path)
|
131 |
-
extension = path_parts[1] if path_parts[1] else '.audio'
|
132 |
|
133 |
-
base_filename = str(podcast.id)
|
134 |
|
135 |
logger.info(f"Téléchargement de {podcast.url} pour le podcast ID {podcast.id}")
|
136 |
-
|
|
|
137 |
response.raise_for_status()
|
138 |
|
|
|
139 |
content_type = response.headers.get('Content-Type')
|
140 |
if content_type:
|
141 |
if 'mpeg' in content_type: extension = '.mp3'
|
142 |
elif 'ogg' in content_type: extension = '.ogg'
|
143 |
elif 'wav' in content_type: extension = '.wav'
|
144 |
elif 'aac' in content_type: extension = '.aac'
|
145 |
-
elif 'mp4' in content_type: extension = '.m4a'
|
146 |
|
147 |
cached_filename_with_ext = f"{base_filename}{extension}"
|
148 |
final_cached_filepath = os.path.join(AUDIO_CACHE_DIR, cached_filename_with_ext)
|
@@ -163,18 +174,21 @@ def play_podcast_route(podcast_id):
|
|
163 |
return jsonify({'error': 'Le téléchargement du podcast a pris trop de temps.'}), 504
|
164 |
except requests.exceptions.RequestException as e:
|
165 |
logger.error(f"Erreur de téléchargement pour {podcast.url}: {e}")
|
|
|
|
|
|
|
|
|
166 |
return jsonify({'error': f'Impossible de télécharger le podcast: {e}'}), 500
|
167 |
except Exception as e:
|
168 |
-
db.session.rollback()
|
169 |
logger.error(f"Erreur inattendue lors du traitement du podcast {podcast.id}: {e}")
|
170 |
-
|
171 |
-
finally:
|
172 |
if final_cached_filepath and os.path.exists(final_cached_filepath) and (not podcast or podcast.filename_cache != os.path.basename(final_cached_filepath)):
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
|
179 |
@app.route('/audio_cache/<path:filename>')
|
180 |
def serve_cached_audio(filename):
|
@@ -182,11 +196,7 @@ def serve_cached_audio(filename):
|
|
182 |
return send_from_directory(AUDIO_CACHE_DIR, filename)
|
183 |
|
184 |
if __name__ == '__main__':
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
logger.info("Tables de base de données vérifiées/créées (si elles n'existaient pas).")
|
189 |
-
except Exception as e:
|
190 |
-
logger.error(f"Erreur lors de la création des tables de la base de données: {e}")
|
191 |
-
logger.error("Assurez-vous que votre serveur PostgreSQL est en cours d'exécution et que la base de données existe.")
|
192 |
app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))
|
|
|
12 |
app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'une_cle_secrete_par_defaut_pour_dev')
|
13 |
|
14 |
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://Podcast_owner:npg_gFdMDLO9lVa0@ep-delicate-surf-a4v7wopn-pooler.us-east-1.aws.neon.tech/Podcast?sslmode=require'
|
|
|
15 |
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
16 |
|
17 |
db = SQLAlchemy(app)
|
|
|
34 |
def __repr__(self):
|
35 |
return f'<Podcast {self.name}>'
|
36 |
|
37 |
+
# Moved db.create_all() here to ensure it runs on app initialization
|
38 |
+
with app.app_context():
|
39 |
+
try:
|
40 |
+
db.create_all()
|
41 |
+
logger.info("Tables de base de données vérifiées/créées (si elles n'existaient pas).")
|
42 |
+
except Exception as e:
|
43 |
+
logger.error(f"Erreur lors de la création des tables de la base de données: {e}")
|
44 |
+
# Depending on the severity, you might want to exit or re-raise the exception.
|
45 |
+
# For now, it logs the error, and the app will likely fail on subsequent DB operations if this fails.
|
46 |
+
|
47 |
@app.route('/')
|
48 |
def index():
|
49 |
try:
|
|
|
131 |
return jsonify({'audio_url': audio_url})
|
132 |
else:
|
133 |
logger.warning(f"Fichier cache {podcast.filename_cache} pour podcast {podcast.id} non trouvé. Re-téléchargement.")
|
134 |
+
podcast.filename_cache = None # Clear invalid cache entry
|
135 |
|
136 |
final_cached_filepath = None
|
137 |
try:
|
138 |
parsed_url = urlparse(podcast.url)
|
139 |
path_parts = os.path.splitext(parsed_url.path)
|
140 |
+
extension = path_parts[1] if path_parts[1] else '.audio' # Default extension
|
141 |
|
142 |
+
base_filename = str(podcast.id) # Use podcast ID for a unique, simple base name
|
143 |
|
144 |
logger.info(f"Téléchargement de {podcast.url} pour le podcast ID {podcast.id}")
|
145 |
+
# Increased timeout for potentially large files; connect timeout, read timeout
|
146 |
+
response = requests.get(podcast.url, stream=True, timeout=(10, 60))
|
147 |
response.raise_for_status()
|
148 |
|
149 |
+
# Try to get a better extension from Content-Type header
|
150 |
content_type = response.headers.get('Content-Type')
|
151 |
if content_type:
|
152 |
if 'mpeg' in content_type: extension = '.mp3'
|
153 |
elif 'ogg' in content_type: extension = '.ogg'
|
154 |
elif 'wav' in content_type: extension = '.wav'
|
155 |
elif 'aac' in content_type: extension = '.aac'
|
156 |
+
elif 'mp4' in content_type: extension = '.m4a' # Often audio in mp4 container
|
157 |
|
158 |
cached_filename_with_ext = f"{base_filename}{extension}"
|
159 |
final_cached_filepath = os.path.join(AUDIO_CACHE_DIR, cached_filename_with_ext)
|
|
|
174 |
return jsonify({'error': 'Le téléchargement du podcast a pris trop de temps.'}), 504
|
175 |
except requests.exceptions.RequestException as e:
|
176 |
logger.error(f"Erreur de téléchargement pour {podcast.url}: {e}")
|
177 |
+
# Clean up partial download if it exists and wasn't successfully associated
|
178 |
+
if final_cached_filepath and os.path.exists(final_cached_filepath) and (not podcast or podcast.filename_cache != os.path.basename(final_cached_filepath)):
|
179 |
+
try: os.remove(final_cached_filepath); logger.info(f"Fichier partiel (RequestException) nettoyé : {final_cached_filepath}")
|
180 |
+
except OSError as e_clean: logger.error(f"Erreur nettoyage fichier partiel (RequestException) {final_cached_filepath}: {e_clean}")
|
181 |
return jsonify({'error': f'Impossible de télécharger le podcast: {e}'}), 500
|
182 |
except Exception as e:
|
183 |
+
db.session.rollback() # Rollback DB session on unexpected error
|
184 |
logger.error(f"Erreur inattendue lors du traitement du podcast {podcast.id}: {e}")
|
185 |
+
# Clean up partial download similar to above
|
|
|
186 |
if final_cached_filepath and os.path.exists(final_cached_filepath) and (not podcast or podcast.filename_cache != os.path.basename(final_cached_filepath)):
|
187 |
+
try: os.remove(final_cached_filepath); logger.info(f"Fichier partiel (Exception) nettoyé : {final_cached_filepath}")
|
188 |
+
except OSError as e_clean: logger.error(f"Erreur nettoyage fichier partiel (Exception) {final_cached_filepath}: {e_clean}")
|
189 |
+
return jsonify({'error': f'Erreur inattendue: {e}'}), 500
|
190 |
+
# The finally block for cleanup was a bit complex; simplified by handling cleanup in error cases.
|
191 |
+
# If successful, filename_cache is set, so it won't be cleaned.
|
192 |
|
193 |
@app.route('/audio_cache/<path:filename>')
|
194 |
def serve_cached_audio(filename):
|
|
|
196 |
return send_from_directory(AUDIO_CACHE_DIR, filename)
|
197 |
|
198 |
if __name__ == '__main__':
|
199 |
+
# The db.create_all() call is now above, so it's not needed here.
|
200 |
+
# The line `app.run(...):19:12 =====` from the problem description seems to have a typo.
|
201 |
+
# Corrected to a standard app.run() call.
|
|
|
|
|
|
|
|
|
202 |
app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))
|