File size: 8,741 Bytes
fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 43f84cb fc53ab2 |
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 |
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_from_directory
from flask_sqlalchemy import SQLAlchemy
import os
import requests
from urllib.parse import urlparse
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'une_cle_secrete_par_defaut_pour_dev')
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://Podcast_owner:npg_gFdMDLO9lVa0@ep-delicate-surf-a4v7wopn-pooler.us-east-1.aws.neon.tech/Podcast?sslmode=require'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
AUDIO_CACHE_DIR = '/tmp/audio_cache'
try:
os.makedirs(AUDIO_CACHE_DIR, exist_ok=True)
logger.info(f"Répertoire de cache audio configuré sur : {AUDIO_CACHE_DIR}")
except OSError as e:
logger.error(f"Impossible de créer le répertoire de cache audio à {AUDIO_CACHE_DIR}: {e}")
class Podcast(db.Model):
__tablename__ = 'podcast'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
url = db.Column(db.String(500), nullable=False, unique=True)
subject = db.Column(db.String(100), nullable=False)
filename_cache = db.Column(db.String(255), nullable=True)
def __repr__(self):
return f'<Podcast {self.name}>'
@app.route('/')
def index():
try:
podcasts = Podcast.query.order_by(Podcast.name).all()
except Exception as e:
logger.error(f"Erreur lors de la récupération des podcasts: {e}")
flash("Erreur lors du chargement des podcasts depuis la base de données.", "error")
podcasts = []
return render_template('index.html', podcasts=podcasts)
@app.route('/gestion', methods=['GET', 'POST'])
def gestion():
if request.method == 'POST':
name = request.form.get('name')
url = request.form.get('url')
subject = request.form.get('subject')
if not name or not url or not subject:
flash('Tous les champs sont requis !', 'error')
else:
existing_podcast = Podcast.query.filter_by(url=url).first()
if existing_podcast:
flash('Un podcast avec cette URL existe déjà.', 'warning')
else:
try:
new_podcast = Podcast(name=name, url=url, subject=subject)
db.session.add(new_podcast)
db.session.commit()
flash('Podcast ajouté avec succès !', 'success')
return redirect(url_for('gestion'))
except Exception as e:
db.session.rollback()
logger.error(f"Erreur lors de l'ajout du podcast: {e}")
flash(f"Erreur lors de l'ajout du podcast: {e}", 'error')
try:
podcasts = Podcast.query.order_by(Podcast.name).all()
except Exception as e:
logger.error(f"Erreur lors de la récupération des podcasts pour la gestion: {e}")
flash("Erreur lors du chargement des podcasts pour la gestion.", "error")
podcasts = []
return render_template('gestion.html', podcasts=podcasts)
@app.route('/delete_podcast/<int:podcast_id>', methods=['POST'])
def delete_podcast(podcast_id):
try:
podcast_to_delete = db.session.get(Podcast, podcast_id)
if not podcast_to_delete:
flash('Podcast non trouvé.', 'error')
return redirect(url_for('gestion'))
if podcast_to_delete.filename_cache:
cached_file_path = os.path.join(AUDIO_CACHE_DIR, podcast_to_delete.filename_cache)
if os.path.exists(cached_file_path):
try:
os.remove(cached_file_path)
logger.info(f"Fichier cache {podcast_to_delete.filename_cache} supprimé.")
except OSError as e:
logger.error(f"Erreur lors de la suppression du fichier cache {podcast_to_delete.filename_cache}: {e}")
flash(f'Erreur lors de la suppression du fichier cache {podcast_to_delete.filename_cache}.', 'error')
db.session.delete(podcast_to_delete)
db.session.commit()
flash('Podcast supprimé avec succès.', 'success')
except Exception as e:
db.session.rollback()
logger.error(f"Erreur lors de la suppression du podcast ID {podcast_id}: {e}")
flash(f"Erreur lors de la suppression du podcast: {e}", 'error')
return redirect(url_for('gestion'))
@app.route('/play/<int:podcast_id>')
def play_podcast_route(podcast_id):
podcast = db.session.get(Podcast, podcast_id)
if not podcast:
logger.warning(f"Tentative de lecture d'un podcast non trouvé: ID {podcast_id}")
return jsonify({'error': 'Podcast non trouvé'}), 404
if podcast.filename_cache:
cached_filepath = os.path.join(AUDIO_CACHE_DIR, podcast.filename_cache)
if os.path.exists(cached_filepath):
logger.info(f"Service du podcast {podcast.id} depuis le cache: {podcast.filename_cache}")
audio_url = url_for('serve_cached_audio', filename=podcast.filename_cache)
return jsonify({'audio_url': audio_url})
else:
logger.warning(f"Fichier cache {podcast.filename_cache} pour podcast {podcast.id} non trouvé. Re-téléchargement.")
podcast.filename_cache = None
final_cached_filepath = None
try:
parsed_url = urlparse(podcast.url)
path_parts = os.path.splitext(parsed_url.path)
extension = path_parts[1] if path_parts[1] else '.audio'
base_filename = str(podcast.id)
logger.info(f"Téléchargement de {podcast.url} pour le podcast ID {podcast.id}")
response = requests.get(podcast.url, stream=True, timeout=(10, 60))
response.raise_for_status()
content_type = response.headers.get('Content-Type')
if content_type:
if 'mpeg' in content_type: extension = '.mp3'
elif 'ogg' in content_type: extension = '.ogg'
elif 'wav' in content_type: extension = '.wav'
elif 'aac' in content_type: extension = '.aac'
elif 'mp4' in content_type: extension = '.m4a'
cached_filename_with_ext = f"{base_filename}{extension}"
final_cached_filepath = os.path.join(AUDIO_CACHE_DIR, cached_filename_with_ext)
with open(final_cached_filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
logger.info(f"Téléchargement terminé: {final_cached_filepath}")
podcast.filename_cache = cached_filename_with_ext
db.session.commit()
audio_url = url_for('serve_cached_audio', filename=cached_filename_with_ext)
return jsonify({'audio_url': audio_url})
except requests.exceptions.Timeout:
logger.error(f"Timeout lors du téléchargement de {podcast.url}")
return jsonify({'error': 'Le téléchargement du podcast a pris trop de temps.'}), 504
except requests.exceptions.RequestException as e:
logger.error(f"Erreur de téléchargement pour {podcast.url}: {e}")
return jsonify({'error': f'Impossible de télécharger le podcast: {e}'}), 500
except Exception as e:
db.session.rollback()
logger.error(f"Erreur inattendue lors du traitement du podcast {podcast.id}: {e}")
return jsonify({'error': f'Erreur inattendue: {e}'}), 500
finally:
if final_cached_filepath and os.path.exists(final_cached_filepath) and (not podcast or podcast.filename_cache != os.path.basename(final_cached_filepath)):
try:
os.remove(final_cached_filepath)
logger.info(f"Fichier partiel nettoyé : {final_cached_filepath}")
except OSError as e_clean:
logger.error(f"Erreur lors du nettoyage du fichier partiel {final_cached_filepath}: {e_clean}")
@app.route('/audio_cache/<path:filename>')
def serve_cached_audio(filename):
logger.debug(f"Service du fichier cache: {filename} depuis {AUDIO_CACHE_DIR}")
return send_from_directory(AUDIO_CACHE_DIR, filename)
if __name__ == '__main__':
with app.app_context():
try:
db.create_all()
logger.info("Tables de base de données vérifiées/créées (si elles n'existaient pas).")
except Exception as e:
logger.error(f"Erreur lors de la création des tables de la base de données: {e}")
logger.error("Assurez-vous que votre serveur PostgreSQL est en cours d'exécution et que la base de données existe.")
app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 5000))) |