File size: 9,353 Bytes
2ac7a3f
 
 
4f0de92
 
2ac7a3f
4f0de92
 
 
2ac7a3f
 
 
a531b8b
4f0de92
 
2ac7a3f
 
 
4f0de92
2ac7a3f
 
 
 
 
4f0de92
 
a531b8b
 
 
2ac7a3f
 
 
 
 
 
 
 
 
4f0de92
2ac7a3f
 
 
 
 
 
 
 
4f0de92
2ac7a3f
 
 
 
a531b8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ac7a3f
 
 
 
4f0de92
 
2ac7a3f
 
4f0de92
2ac7a3f
4f0de92
2ac7a3f
 
4f0de92
 
2ac7a3f
4f0de92
2ac7a3f
4f0de92
a531b8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ac7a3f
 
4f0de92
 
 
 
 
 
 
 
 
 
2ac7a3f
4f0de92
2ac7a3f
 
4f0de92
2ac7a3f
 
 
 
 
4f0de92
2ac7a3f
 
 
 
 
 
4f0de92
 
 
2ac7a3f
 
 
 
 
 
 
 
 
 
a531b8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ac7a3f
a531b8b
2ac7a3f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f0de92
 
2ac7a3f
 
4f0de92
2ac7a3f
4f0de92
 
2ac7a3f
 
 
 
 
 
 
 
 
4f0de92
 
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
from flask import Flask, render_template, request, jsonify, session, send_file
from werkzeug.utils import secure_filename
import os
from google import genai
from google.genai import types
import io
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from datetime import datetime
import secrets
import re

app = Flask(__name__)
app.secret_key = secrets.token_hex(16)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024  # 50MB max

# Créer le dossier uploads
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

# Configuration Gemini
GEMINI_API_KEY = os.environ.get('GEMINI_API_KEY', 'YOUR_API_KEY_HERE')
client = genai.Client(api_key=GEMINI_API_KEY)

# Limite de tokens pour les vidéos
MAX_VIDEO_TOKENS = 600000

ALLOWED_EXTENSIONS = {
    'pdf': 'application/pdf',
    'mp3': 'audio/mp3',
    'mp4': 'video/mp4',
    'wav': 'audio/wav',
    'txt': 'text/plain',
    'doc': 'application/msword',
    'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
}

SUMMARY_TYPES = {
    'court': 'Fais un résumé très court (2-3 paragraphes maximum) des points clés essentiels.',
    'moyen': 'Fais un résumé détaillé structuré avec les points principaux et sous-points importants.',
    'detaille': 'Fais un résumé exhaustif et détaillé avec tous les points importants, citations clés et analyse approfondie.'
}

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def get_mime_type(filename):
    ext = filename.rsplit('.', 1)[1].lower()
    return ALLOWED_EXTENSIONS.get(ext, 'application/octet-stream')

def is_youtube_url(url):
    """Vérifie si l'URL est une URL YouTube valide"""
    youtube_regex = r'(https?://)?(www\.)?(youtube\.com/watch\?v=|youtu\.be/)[\w-]+'
    return bool(re.match(youtube_regex, url))

def estimate_video_tokens(video_duration_seconds, fps=1, use_low_resolution=False):
    """Estime le nombre de tokens pour une vidéo"""
    # Tokens par frame
    tokens_per_frame = 66 if use_low_resolution else 258
    # Tokens audio par seconde
    tokens_per_second_audio = 32
    # Total par seconde
    tokens_per_second = (tokens_per_frame * fps) + tokens_per_second_audio
    return video_duration_seconds * tokens_per_second

def generate_summary(file_path, filename, summary_type):
    """Génère un résumé avec l'API Gemini"""
    mime_type = get_mime_type(filename)
    prompt = SUMMARY_TYPES.get(summary_type, SUMMARY_TYPES['moyen'])
    
    try:
        # Upload le fichier via File API
        uploaded_file = client.files.upload(file=file_path)
        
        # Génère le résumé
        response = client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[uploaded_file, prompt]
        )
        
        return response.text
    except Exception as e:
        return f"Erreur lors de la génération du résumé: {str(e)}"

def generate_summary_from_youtube(youtube_url, summary_type):
    """Génère un résumé à partir d'une URL YouTube"""
    prompt = SUMMARY_TYPES.get(summary_type, SUMMARY_TYPES['moyen'])
    
    try:
        # Vérifier les informations du modèle
        model_info = client.models.get(model="gemini-2.5-flash")
        context_window = model_info.input_token_limit
        
        print(f"Context window: {context_window} tokens")
        
        # Générer le contenu avec l'URL YouTube
        response = client.models.generate_content(
            model='gemini-2.5-flash',
            contents=types.Content(
                parts=[
                    types.Part(
                        file_data=types.FileData(file_uri=youtube_url)
                    ),
                    types.Part(text=prompt)
                ]
            )
        )
        
        return response.text
    except Exception as e:
        error_msg = str(e)
        # Vérifier si l'erreur est liée à la limite de tokens
        if 'token' in error_msg.lower() or 'too large' in error_msg.lower():
            return f"Erreur: La vidéo est trop longue (dépasse la limite de {MAX_VIDEO_TOKENS:,} tokens). Veuillez utiliser une vidéo plus courte."
        return f"Erreur lors de la génération du résumé: {error_msg}"

def create_pdf(summary_text, original_filename, summary_type):
    """Crée un PDF du résumé"""
    buffer = io.BytesIO()
    doc = SimpleDocTemplate(buffer, pagesize=letter)
    styles = getSampleStyleSheet()
    story = []
    
    # Titre
    title_style = ParagraphStyle(
        'CustomTitle',
        parent=styles['Heading1'],
        fontSize=18,
        spaceAfter=30
    )
    title = Paragraph(f"Résumé: {original_filename}", title_style)
    story.append(title)
    
    # Info
    info_style = styles['Normal']
    info = Paragraph(f"Type: {summary_type.upper()} | Date: {datetime.now().strftime('%d/%m/%Y %H:%M')}", info_style)
    story.append(info)
    story.append(Spacer(1, 0.3*inch))
    
    # Contenu du résumé
    for paragraph in summary_text.split('\n\n'):
        if paragraph.strip():
            p = Paragraph(paragraph.replace('\n', '<br/>'), styles['Normal'])
            story.append(p)
            story.append(Spacer(1, 0.2*inch))
    
    doc.build(story)
    buffer.seek(0)
    return buffer

@app.route('/')
def index():
    if 'history' not in session:
        session['history'] = []
    return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload_file():
    youtube_url = request.form.get('youtube_url', '').strip()
    summary_type = request.form.get('summary_type', 'moyen')
    
    # Traitement YouTube
    if youtube_url:
        if not is_youtube_url(youtube_url):
            return jsonify({'error': 'URL YouTube invalide'}), 400
        
        try:
            summary = generate_summary_from_youtube(youtube_url, summary_type)
            
            # Vérifier si c'est un message d'erreur
            if summary.startswith("Erreur:"):
                return jsonify({'error': summary}), 400
            
            # Ajouter à l'historique
            if 'history' not in session:
                session['history'] = []
            
            history_item = {
                'id': len(session['history']),
                'filename': 'Vidéo YouTube',
                'summary_type': summary_type,
                'summary': summary,
                'date': datetime.now().strftime('%d/%m/%Y %H:%M'),
                'filepath': youtube_url
            }
            
            session['history'].append(history_item)
            session.modified = True
            
            return jsonify({
                'success': True,
                'summary': summary,
                'history_id': history_item['id']
            })
        
        except Exception as e:
            return jsonify({'error': str(e)}), 500
    
    # Traitement fichier normal
    if 'file' not in request.files:
        return jsonify({'error': 'Aucun fichier ou URL YouTube fourni'}), 400
    
    file = request.files['file']
    
    if file.filename == '':
        return jsonify({'error': 'Nom de fichier vide'}), 400
    
    if not allowed_file(file.filename):
        return jsonify({'error': 'Type de fichier non supporté'}), 400
    
    filename = secure_filename(file.filename)
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    file.save(filepath)
    
    try:
        # Génère le résumé
        summary = generate_summary(filepath, filename, summary_type)
        
        # Ajoute à l'historique
        if 'history' not in session:
            session['history'] = []
        
        history_item = {
            'id': len(session['history']),
            'filename': filename,
            'summary_type': summary_type,
            'summary': summary,
            'date': datetime.now().strftime('%d/%m/%Y %H:%M'),
            'filepath': filepath
        }
        
        session['history'].append(history_item)
        session.modified = True
        
        return jsonify({
            'success': True,
            'summary': summary,
            'history_id': history_item['id']
        })
    
    except Exception as e:
        return jsonify({'error': str(e)}), 500
    finally:
        # Nettoie le fichier après traitement
        if os.path.exists(filepath):
            os.remove(filepath)

@app.route('/download/<int:history_id>')
def download_pdf(history_id):
    if 'history' not in session or history_id >= len(session['history']):
        return jsonify({'error': 'Résumé non trouvé'}), 404
    
    item = session['history'][history_id]
    pdf_buffer = create_pdf(item['summary'], item['filename'], item['summary_type'])
    
    return send_file(
        pdf_buffer,
        mimetype='application/pdf',
        as_attachment=True,
        download_name=f"resume_{item['filename']}.pdf"
    )

@app.route('/history')
def get_history():
    return jsonify(session.get('history', []))

@app.route('/clear-history', methods=['POST'])
def clear_history():
    session['history'] = []
    return jsonify({'success': True})

if __name__ == '__main__':
    app.run(debug=True)