From d6cf9469aa0462d1b8313cc85907176eee1214a2 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Wed, 25 Mar 2026 17:10:54 +0100
Subject: [PATCH] fix: C3 debug logs, H1-H5 image cache, temp files, controller leak, validation, lifecycle
---
lib/services/audio_service.dart | 25 ++++++++++++++++++++++++-
1 files changed, 24 insertions(+), 1 deletions(-)
diff --git a/lib/services/audio_service.dart b/lib/services/audio_service.dart
index 76843e0..a547e6f 100644
--- a/lib/services/audio_service.dart
+++ b/lib/services/audio_service.dart
@@ -28,9 +28,16 @@
// Autoplay suppression
static bool _isBackgrounded = false;
+ // Track last played temp file so it can be cleaned up when the track ends
+ static String? _lastPlaybackTempPath;
+
+ // Lifecycle observer stored so we can remove it in dispose()
+ static _LifecycleObserver? _lifecycleObserver;
+
/// Initialize the audio service and set up lifecycle observer.
static void init() {
- WidgetsBinding.instance.addObserver(_LifecycleObserver());
+ _lifecycleObserver = _LifecycleObserver();
+ WidgetsBinding.instance.addObserver(_lifecycleObserver!);
// Configure audio session for background playback
_player.setAudioContext(AudioContext(
@@ -52,6 +59,13 @@
}
static void _onTrackComplete() {
+ // Clean up the temp file that just finished playing
+ final prev = _lastPlaybackTempPath;
+ _lastPlaybackTempPath = null;
+ if (prev != null) {
+ File(prev).delete().ignore();
+ }
+
if (_queue.isNotEmpty) {
_playNextInQueue();
} else {
@@ -68,6 +82,7 @@
}
final path = _queue.removeAt(0);
+ _lastPlaybackTempPath = path;
try {
// Brief pause between tracks — iOS audio player needs time to reset
await _player.stop();
@@ -143,10 +158,12 @@
if (source.startsWith('/')) {
await _player.play(DeviceFileSource(source));
+ // File path owned by caller — not tracked for deletion
} else {
// base64 data — write to temp file first
final path = await _base64ToFile(source);
if (path == null) return;
+ _lastPlaybackTempPath = path;
await _player.play(DeviceFileSource(path));
}
_isPlaying = true;
@@ -159,6 +176,7 @@
final path = await _base64ToFile(base64Audio);
if (path == null) return;
+ _lastPlaybackTempPath = path;
await _player.play(DeviceFileSource(path));
_isPlaying = true;
onPlaybackStateChanged?.call();
@@ -177,6 +195,7 @@
debugPrint('AudioService: queued (queue size: ${_queue.length})');
} else {
// Nothing playing — start immediately
+ _lastPlaybackTempPath = path;
try {
await _player.play(DeviceFileSource(path));
_isPlaying = true;
@@ -250,6 +269,10 @@
}
static Future<void> dispose() async {
+ if (_lifecycleObserver != null) {
+ WidgetsBinding.instance.removeObserver(_lifecycleObserver!);
+ _lifecycleObserver = null;
+ }
await cancelRecording();
await stopPlayback();
_recorder.dispose();
--
Gitblit v1.3.1