mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-18 21:47:34 +02:00
fix(playback): reload audio sources when switching tracks
This commit is contained in:
1 parent
00cff9b3c3
commit
829aeb156e
2 files changed
+45
-2
No files matched your search
@@ -151,7 +151,7 @@ class MusicPlaybackDeck {
|
||||
_source = null;
|
||||
_routeLost = false;
|
||||
_nativeStarted = false;
|
||||
await _ordinary.setSource(UrlSource(url));
|
||||
await _setOrdinarySource(UrlSource(url));
|
||||
}
|
||||
|
||||
Future<void> setSource(
|
||||
@@ -245,6 +245,14 @@ class MusicPlaybackDeck {
|
||||
usbAudioStatus.value = const UsbAudioStatus(reason: 'dsd_unsupported');
|
||||
throw UsbDsdUnavailable();
|
||||
}
|
||||
await _setOrdinarySource(source);
|
||||
}
|
||||
|
||||
Future<void> _setOrdinarySource(Source source) async {
|
||||
// stop mode retains the native source. Android can reuse the same
|
||||
// /proc/self/fd number for a different SAF file, so URL equality must not
|
||||
// reuse the previous MediaPlayer or its pending preparation callbacks.
|
||||
await _ordinary.release();
|
||||
await _ordinary.setSource(source);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,8 @@ class _AudioNative {
|
||||
final live = <String>{};
|
||||
final playing = <String>{};
|
||||
final sources = <String, String>{};
|
||||
final sourceContents = <String, String>{};
|
||||
final preparedContents = <String, String>{};
|
||||
final sourceGates = <String, Completer<void>>{};
|
||||
final resumedSources = <String>[];
|
||||
Completer<void>? resumeGate;
|
||||
@@ -98,7 +100,11 @@ class _AudioNative {
|
||||
case 'setSourceUrl':
|
||||
final source = args['url']! as String;
|
||||
await sourceGates[source]?.future;
|
||||
sources[id] = source;
|
||||
// Android caches equal URLs, including recycled SAF descriptors.
|
||||
if (sources[id] != source) {
|
||||
sources[id] = source;
|
||||
preparedContents[id] = sourceContents[source] ?? source;
|
||||
}
|
||||
unawaited(event(id, 'audio.onPrepared', true));
|
||||
if (emitsDuration) unawaited(event(id, 'audio.onDuration', 60000));
|
||||
case 'seek':
|
||||
@@ -110,6 +116,10 @@ class _AudioNative {
|
||||
resumedSources.add(sources[id]!);
|
||||
case 'pause' || 'stop':
|
||||
playing.remove(id);
|
||||
case 'release':
|
||||
playing.remove(id);
|
||||
sources.remove(id);
|
||||
preparedContents.remove(id);
|
||||
case 'dispose':
|
||||
playing.remove(id);
|
||||
live.remove(id);
|
||||
@@ -937,6 +947,31 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test('recycled SAF descriptor prepares the newly selected file', () async {
|
||||
const descriptor = '/proc/self/fd/42';
|
||||
native.sourceContents[descriptor] = 'first file';
|
||||
await handler.setQueueAndPlay([
|
||||
const PlayableMedia(
|
||||
id: 'first',
|
||||
source: descriptor,
|
||||
title: 'First',
|
||||
artist: 'Artist',
|
||||
),
|
||||
const PlayableMedia(
|
||||
id: 'second',
|
||||
source: descriptor,
|
||||
title: 'Second',
|
||||
artist: 'Artist',
|
||||
),
|
||||
]);
|
||||
expect(native.preparedContents['music-player'], 'first file');
|
||||
native.sourceContents[descriptor] = 'second file';
|
||||
await handler.skipToNext();
|
||||
expect(handler.mediaItem.value?.title, 'Second');
|
||||
expect(native.preparedContents['music-player'], 'second file');
|
||||
expect(native.playing, contains('music-player'));
|
||||
});
|
||||
|
||||
test('replacing a queue during preparation drops the old request', () async {
|
||||
final gate = Completer<void>();
|
||||
native.sourceGates['/one.flac'] = gate;
|
||||
|
||||
Reference in new issue
Block a user