feat(recording): add ffmpeg logs writing and viewing in UI

This commit is contained in:
Michael committed 2026-03-08 19:26:54 +01:00
1 parent abbac75926
commit 966311f560
3 files changed
+110 -2

No files matched your search

+27
View File
@@ -1,3 +1,4 @@
import 'dart:io';
import 'dart:convert';
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
@@ -85,6 +86,32 @@ class RecordingsApi {
);
});
// Consulter les logs d'un enregistrement
router.get('/<id>/log', (Request request, String id) async {
final recording = _db.getRecordingById(id);
if (recording == null) {
return Response.notFound(json.encode({'error': 'Enregistrement non trouvé'}));
}
if (recording.filePath == null) {
return Response.notFound(json.encode({'error': 'Aucun fichier ni log associé pour le moment.'}));
}
final logFilePath = recording.filePath!.replaceAll('.mp4', '.log');
final logFile = File(logFilePath);
if (!await logFile.exists()) {
return Response.notFound(json.encode({'error': 'Le fichier de log est introuvable.'}));
}
final logs = await logFile.readAsString();
return Response.ok(
json.encode({'logs': logs}),
headers: {'Content-Type': 'application/json'},
);
});
return router;
}
}
+24 -2
View File
@@ -118,21 +118,43 @@ class RecordingScheduler {
_ffmpegProcess = await Process.start('ffmpeg', args);
// Création d'un fichier de log dédié pour FFmpeg
final logFilePath = filePath.replaceAll('.mp4', '.log');
final logFile = File(logFilePath);
final logSink = logFile.openWrite();
logSink.writeln('[${DateTime.now()}] Démarrage de l\'enregistrement : ${recording.title}');
logSink.writeln('URL source: ${recording.streamUrl}');
logSink.writeln('Fichier destination: $filePath');
logSink.writeln('Commande: ffmpeg ${args.join(' ')}\n');
// Rediriger la sortie de ffmpeg vers ce fichier
_ffmpegProcess!.stdout.listen((event) {
logSink.add(event);
});
_ffmpegProcess!.stderr.listen((event) {
logSink.add(event); // FFmpeg écrit beaucoup d'infos (dont la progression) sur stderr
});
// Enregistrer le chemin du fichier dans la BDD
_db.updateRecordingStatus(recording.id, 'recording', filePath: filePath);
// Écouter de manière asynchrone la fin du processus FFmpeg
_ffmpegProcess!.exitCode.then((exitCode) {
_ffmpegProcess!.exitCode.then((exitCode) async {
if (_currentRecording?.id == recording.id) {
logSink.writeln('\n[${DateTime.now()}] Processus FFmpeg terminé avec le code $exitCode');
await logSink.close();
if (exitCode == 0 || exitCode == 255) { // 255 est souvent renvoyé lors d'un arrêt forcé (SIGKILL/SIGTERM) qui est attendu
print('[RecordingScheduler] Enregistrement terminé avec succès (${recording.title})');
_db.updateRecordingStatus(recording.id, 'completed');
} else {
print('[RecordingScheduler] Erreur FFmpeg (code: $exitCode) pour l\'enregistrement ${recording.title}');
_db.updateRecordingStatus(recording.id, 'failed', errorReason: 'Erreur FFmpeg code $exitCode');
_db.updateRecordingStatus(recording.id, 'failed', errorReason: 'Erreur FFmpeg code $exitCode. Voir logs.');
}
_currentRecording = null;
_ffmpegProcess = null;
} else {
await logSink.close();
}
});