Spaces:
Paused
Paused
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)))
|