Vinh.Vu commited on
Commit
218059f
·
1 Parent(s): 7357db4

Add route file

Browse files
Files changed (2) hide show
  1. App/app.py +3 -105
  2. App/route.py +128 -0
App/app.py CHANGED
@@ -8,10 +8,7 @@ import numpy as np
8
  import imageio_ffmpeg
9
  from mtcnn import MTCNN
10
  from ultralytics import YOLO
11
- from flask import Flask, request, render_template, send_from_directory, jsonify
12
- from werkzeug.utils import secure_filename
13
- import uuid
14
- import threading
15
  from tensorflow.keras.models import load_model
16
  from tensorflow.keras.applications.efficientnet import preprocess_input
17
  import keras.src.layers.normalization.batch_normalization as _bn_module
@@ -296,107 +293,8 @@ def cleanup_old_uploads(exclude=None):
296
  pass
297
 
298
 
299
- @app.route('/', methods=['GET'])
300
- def index():
301
- return render_template('index.html')
302
-
303
-
304
- @app.route('/uploads/<filename>')
305
- def uploaded_video(filename):
306
- return send_from_directory(app.config['UPLOAD_FOLDER'], filename, mimetype='video/mp4')
307
-
308
-
309
- def process_video_job(job_id, filepath, unique_name):
310
- """Background worker: extract faces, predict, create processed video."""
311
- try:
312
- logger.info('[Job %s] Starting face detection', job_id)
313
- jobs[job_id]['status'] = 'detecting'
314
-
315
- faces = extract_faces_from_video(filepath)
316
- avg_score, num_faces, faces_detail = predict_deepfake(faces)
317
-
318
- if avg_score is None:
319
- logger.warning('[Job %s] No faces detected', job_id)
320
- jobs[job_id].update({
321
- 'status': 'done',
322
- 'error': 'No faces detected in the video.',
323
- 'video_url': f'/uploads/{unique_name}',
324
- })
325
- return
326
-
327
- is_real = avg_score > 0.5
328
- label = 'REAL' if is_real else 'FAKE'
329
- confidence = avg_score if is_real else (1 - avg_score)
330
-
331
- # Publish detection results immediately
332
- logger.info('[Job %s] Detection done — result: %s, confidence: %.2f%%, faces: %d',
333
- job_id, label, confidence * 100, num_faces)
334
- jobs[job_id].update({
335
- 'status': 'processing_video',
336
- 'result': label,
337
- 'confidence': round(confidence * 100, 2),
338
- 'score': round(avg_score, 4),
339
- 'num_faces': num_faces,
340
- 'faces_detail': faces_detail,
341
- 'video_url': f'/uploads/{unique_name}',
342
- })
343
-
344
- # Now generate processed video (results already visible to client)
345
- logger.info('[Job %s] Starting video processing', job_id)
346
- processed_name = f"processed_{unique_name}"
347
- processed_path = os.path.join(app.config['UPLOAD_FOLDER'], processed_name)
348
- create_processed_video(filepath, processed_path)
349
-
350
- logger.info('[Job %s] Video processing done', job_id)
351
- jobs[job_id].update({
352
- 'status': 'done',
353
- 'processed_url': f'/uploads/{processed_name}',
354
- })
355
- except Exception as e:
356
- logger.error('[Job %s] Error: %s', job_id, e)
357
- jobs[job_id].update({'status': 'done', 'error': str(e)})
358
-
359
-
360
- @app.route('/predict', methods=['POST'])
361
- def predict():
362
- if 'video' not in request.files:
363
- return jsonify({'error': 'No video file uploaded.'}), 400
364
-
365
- file = request.files['video']
366
- if file.filename == '':
367
- return jsonify({'error': 'No file selected.'}), 400
368
-
369
- if not allowed_file(file.filename):
370
- return jsonify({'error': 'Invalid file type. Allowed: mp4, avi, mov, mkv, wmv'}), 400
371
-
372
- cleanup_old_uploads()
373
-
374
- ext = secure_filename(file.filename).rsplit('.', 1)[1].lower()
375
- unique_name = f"{uuid.uuid4().hex}.{ext}"
376
- filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_name)
377
- file.save(filepath)
378
- logger.info('Video uploaded: %s (%s)', file.filename, unique_name)
379
-
380
- # Re-encode upload to H.264 so browser can play it
381
- logger.info('Re-encoding uploaded video to H.264')
382
- reencode_to_h264(filepath)
383
-
384
- job_id = uuid.uuid4().hex
385
- logger.info('Created job %s for %s', job_id, unique_name)
386
- jobs[job_id] = {'status': 'uploading', 'video_url': f'/uploads/{unique_name}'}
387
-
388
- thread = threading.Thread(target=process_video_job, args=(job_id, filepath, unique_name))
389
- thread.start()
390
-
391
- return jsonify({'job_id': job_id, 'video_url': f'/uploads/{unique_name}'})
392
-
393
-
394
- @app.route('/status/<job_id>')
395
- def job_status(job_id):
396
- job = jobs.get(job_id)
397
- if not job:
398
- return jsonify({'error': 'Job not found'}), 404
399
- return jsonify(job)
400
 
401
 
402
  if __name__ == '__main__':
 
8
  import imageio_ffmpeg
9
  from mtcnn import MTCNN
10
  from ultralytics import YOLO
11
+ from flask import Flask
 
 
 
12
  from tensorflow.keras.models import load_model
13
  from tensorflow.keras.applications.efficientnet import preprocess_input
14
  import keras.src.layers.normalization.batch_normalization as _bn_module
 
293
  pass
294
 
295
 
296
+ from route import routes
297
+ app.register_blueprint(routes)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
299
 
300
  if __name__ == '__main__':
App/route.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+ import uuid
4
+ import threading
5
+ from flask import Blueprint, request, render_template, send_from_directory, jsonify
6
+ from werkzeug.utils import secure_filename
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ routes = Blueprint('routes', __name__)
11
+
12
+
13
+ def _get_app_deps():
14
+ """Import app-level objects to avoid circular imports."""
15
+ from app import (
16
+ app, jobs, allowed_file, cleanup_old_uploads,
17
+ extract_faces_from_video, predict_deepfake,
18
+ create_processed_video, reencode_to_h264
19
+ )
20
+ return app, jobs, allowed_file, cleanup_old_uploads, \
21
+ extract_faces_from_video, predict_deepfake, \
22
+ create_processed_video, reencode_to_h264
23
+
24
+
25
+ @routes.route('/', methods=['GET'])
26
+ def index():
27
+ return render_template('index.html')
28
+
29
+
30
+ @routes.route('/uploads/<filename>')
31
+ def uploaded_video(filename):
32
+ from app import app
33
+ return send_from_directory(app.config['UPLOAD_FOLDER'], filename, mimetype='video/mp4')
34
+
35
+
36
+ def process_video_job(job_id, filepath, unique_name):
37
+ """Background worker: extract faces, predict, create processed video."""
38
+ app, jobs, _, _, extract_faces_from_video, predict_deepfake, \
39
+ create_processed_video, _ = _get_app_deps()
40
+ try:
41
+ logger.info('[Job %s] Starting face detection', job_id)
42
+ jobs[job_id]['status'] = 'detecting'
43
+
44
+ faces = extract_faces_from_video(filepath)
45
+ avg_score, num_faces, faces_detail = predict_deepfake(faces)
46
+
47
+ if avg_score is None:
48
+ logger.warning('[Job %s] No faces detected', job_id)
49
+ jobs[job_id].update({
50
+ 'status': 'done',
51
+ 'error': 'No faces detected in the video.',
52
+ 'video_url': f'/uploads/{unique_name}',
53
+ })
54
+ return
55
+
56
+ is_real = avg_score > 0.5
57
+ label = 'REAL' if is_real else 'FAKE'
58
+ confidence = avg_score if is_real else (1 - avg_score)
59
+
60
+ logger.info('[Job %s] Detection done — result: %s, confidence: %.2f%%, faces: %d',
61
+ job_id, label, confidence * 100, num_faces)
62
+ jobs[job_id].update({
63
+ 'status': 'processing_video',
64
+ 'result': label,
65
+ 'confidence': round(confidence * 100, 2),
66
+ 'score': round(avg_score, 4),
67
+ 'num_faces': num_faces,
68
+ 'faces_detail': faces_detail,
69
+ 'video_url': f'/uploads/{unique_name}',
70
+ })
71
+
72
+ logger.info('[Job %s] Starting video processing', job_id)
73
+ processed_name = f"processed_{unique_name}"
74
+ processed_path = os.path.join(app.config['UPLOAD_FOLDER'], processed_name)
75
+ create_processed_video(filepath, processed_path)
76
+
77
+ logger.info('[Job %s] Video processing done', job_id)
78
+ jobs[job_id].update({
79
+ 'status': 'done',
80
+ 'processed_url': f'/uploads/{processed_name}',
81
+ })
82
+ except Exception as e:
83
+ logger.error('[Job %s] Error: %s', job_id, e)
84
+ jobs[job_id].update({'status': 'done', 'error': str(e)})
85
+
86
+
87
+ @routes.route('/predict', methods=['POST'])
88
+ def predict():
89
+ app, jobs, allowed_file, cleanup_old_uploads, _, _, _, reencode_to_h264 = _get_app_deps()
90
+
91
+ if 'video' not in request.files:
92
+ return jsonify({'error': 'No video file uploaded.'}), 400
93
+
94
+ file = request.files['video']
95
+ if file.filename == '':
96
+ return jsonify({'error': 'No file selected.'}), 400
97
+
98
+ if not allowed_file(file.filename):
99
+ return jsonify({'error': 'Invalid file type. Allowed: mp4, avi, mov, mkv, wmv'}), 400
100
+
101
+ cleanup_old_uploads()
102
+
103
+ ext = secure_filename(file.filename).rsplit('.', 1)[1].lower()
104
+ unique_name = f"{uuid.uuid4().hex}.{ext}"
105
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_name)
106
+ file.save(filepath)
107
+ logger.info('Video uploaded: %s (%s)', file.filename, unique_name)
108
+
109
+ logger.info('Re-encoding uploaded video to H.264')
110
+ reencode_to_h264(filepath)
111
+
112
+ job_id = uuid.uuid4().hex
113
+ logger.info('Created job %s for %s', job_id, unique_name)
114
+ jobs[job_id] = {'status': 'uploading', 'video_url': f'/uploads/{unique_name}'}
115
+
116
+ thread = threading.Thread(target=process_video_job, args=(job_id, filepath, unique_name))
117
+ thread.start()
118
+
119
+ return jsonify({'job_id': job_id, 'video_url': f'/uploads/{unique_name}'})
120
+
121
+
122
+ @routes.route('/status/<job_id>')
123
+ def job_status(job_id):
124
+ from app import jobs
125
+ job = jobs.get(job_id)
126
+ if not job:
127
+ return jsonify({'error': 'Job not found'}), 404
128
+ return jsonify(job)