Files
plezy/lib/mpv/player/player_stream_controllers.dart
T
edde746 4816e3928f fix(player): skip relative to the position a jump landed on
A coalesced key-repeat skip pins its target so a slow backend cannot make
the next press rebase off a position the seek has not reached yet. Nothing
retired that pin when something else moved the playhead, so for the ten
seconds it survived, a skip taken after a timeline tap, a chapter jump, an
OS media control or a peer sync resumed from the superseded target and threw
the user back across their own jump.

Publish every playhead movement on the player and retire the pin whenever
the announced destination is not the accumulator's own commit. Overlapping
seeks and backend-chosen relocations arbitrate by which operation the
backend accepted, so a request that was merely asked for cannot speak for
where the playhead ended up.

close #1819
2026-08-07 08:43:48 +02:00

88 lines
3.9 KiB
Dart

import 'dart:async';
import '../models.dart';
import 'player_streams.dart';
mixin PlayerStreamControllersMixin {
final playingController = StreamController<bool>.broadcast();
final completedController = StreamController<bool>.broadcast();
final bufferingController = StreamController<bool>.broadcast();
final positionController = StreamController<Duration>.broadcast();
final playheadJumpController = StreamController<Duration?>.broadcast();
final durationController = StreamController<Duration>.broadcast();
final seekableController = StreamController<bool>.broadcast();
final bufferController = StreamController<Duration>.broadcast();
final volumeController = StreamController<double>.broadcast();
final rateController = StreamController<double>.broadcast();
final tracksController = StreamController<Tracks>.broadcast();
final trackController = StreamController<TrackSelection>.broadcast();
final logController = StreamController<PlayerLog>.broadcast();
final errorController = StreamController<PlayerError>.broadcast();
final audioDeviceController = StreamController<AudioDevice>.broadcast();
final audioDevicesController = StreamController<List<AudioDevice>>.broadcast();
final bufferRangesController = StreamController<List<BufferRange>>.broadcast();
final playbackRestartController = StreamController<void>.broadcast();
final fileLoadedController = StreamController<void>.broadcast();
final fileStartedController = StreamController<void>.broadcast();
final fileLoadFailedController = StreamController<void>.broadcast();
final primaryMediaReadyController = StreamController<void>.broadcast();
final backendSwitchedController = StreamController<void>.broadcast();
final trackTransitionController = StreamController<String>.broadcast();
PlayerStreams createStreams() {
return PlayerStreams(
playing: playingController.stream,
completed: completedController.stream,
buffering: bufferingController.stream,
position: positionController.stream,
playheadJump: playheadJumpController.stream,
duration: durationController.stream,
seekable: seekableController.stream,
buffer: bufferController.stream,
volume: volumeController.stream,
rate: rateController.stream,
tracks: tracksController.stream,
track: trackController.stream,
log: logController.stream,
error: errorController.stream,
audioDevice: audioDeviceController.stream,
audioDevices: audioDevicesController.stream,
bufferRanges: bufferRangesController.stream,
playbackRestart: playbackRestartController.stream,
fileLoaded: fileLoadedController.stream,
fileStarted: fileStartedController.stream,
fileLoadFailed: fileLoadFailedController.stream,
primaryMediaReady: primaryMediaReadyController.stream,
backendSwitched: backendSwitchedController.stream,
trackTransition: trackTransitionController.stream,
);
}
Future<void> closeStreamControllers() async {
await playingController.close();
await completedController.close();
await bufferingController.close();
await positionController.close();
await playheadJumpController.close();
await durationController.close();
await seekableController.close();
await bufferController.close();
await volumeController.close();
await rateController.close();
await tracksController.close();
await trackController.close();
await logController.close();
await errorController.close();
await audioDeviceController.close();
await audioDevicesController.close();
await bufferRangesController.close();
await playbackRestartController.close();
await fileLoadedController.close();
await fileStartedController.close();
await fileLoadFailedController.close();
await primaryMediaReadyController.close();
await backendSwitchedController.close();
await trackTransitionController.close();
}
}