Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/models/playback/playback_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ class PlaybackModelHelper {

final firstItemIsSynced = syncedItem != null && syncedItem.status == TaskStatus.complete;

final actualStartPosition = startPosition ?? fullItem.userData.playBackPosition;

final options = {
PlaybackType.directStream,
PlaybackType.transcode,
Expand All @@ -269,7 +271,7 @@ class PlaybackModelHelper {
forcedPlaybackType ?? playbackType,
oldModel: oldModel,
libraryQueue: queue,
startPosition: startPosition,
startPosition: actualStartPosition,
),
PlaybackType.offline => await _createOfflinePlaybackModel(
fullItem,
Expand All @@ -283,7 +285,7 @@ class PlaybackModelHelper {
fullItem,
item.streamModel,
forcedPlaybackType ?? PlaybackType.directStream,
startPosition: startPosition,
startPosition: actualStartPosition,
oldModel: oldModel,
libraryQueue: queue,
)) ??
Expand Down
20 changes: 4 additions & 16 deletions lib/providers/video_player_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,24 +126,12 @@ class VideoPlayerNotifier extends StateNotifier<MediaControlsWrapper> {
PlaybackModel? newPlaybackModel = model;

if (media != null) {
await state.loadVideo(model, startPosition, false);
await state.loadVideo(model, startPosition, true);
await state.setVolume(ref.read(videoPlayerSettingsProvider).volume);

state.stateStream?.takeWhile((event) => event.buffering == true).listen(
null,
onDone: () async {
final start = startPosition;
if (start != Duration.zero) {
await state.seek(start);
}
await state.setAudioTrack(null, model);
await state.setSubtitleTrack(null, model);
state.play();
ref.read(playBackModel.notifier).update((state) => newPlaybackModel);
},
);

ref.read(playBackModel.notifier).update((state) => model);
await state.setAudioTrack(null, model);
await state.setSubtitleTrack(null, model);
ref.read(playBackModel.notifier).update((state) => newPlaybackModel);

return true;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/stubs/web/lib_mdk_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class LibMDK extends BasePlayer {
Future<void> dispose() async {}

@override
Future<void> loadVideo(String url, bool play) async {}
Future<void> loadVideo(String url, bool play, {Duration startPosition = Duration.zero}) async {}

void setState(PlayerState state) {}

Expand Down
4 changes: 3 additions & 1 deletion lib/util/item_base_model/play_item_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@ extension ItemBaseModelExtensions on ItemBaseModel? {
return;
}

await _playVideo(context, startPosition: startPosition, current: model, ref: ref, cancelOperation: op);
final actualStartPosition = startPosition ?? await model.startDuration() ?? Duration.zero;

await _playVideo(context, startPosition: actualStartPosition, current: model, ref: ref, cancelOperation: op);
}
}

Expand Down
11 changes: 9 additions & 2 deletions lib/wrappers/media_control_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ class MediaControlsWrapper extends BaseAudioHandler implements VideoPlayerContro
final context = ref.read(localizationContextProvider);
await (_player as NativePlayer).sendPlaybackDataToNative(context, model, startPosition);
}
await _player?.loadVideo(model.media?.url ?? "", play);
await _player?.loadVideo(model.media?.url ?? "", play, startPosition: startPosition);
if (play) {
ref.read(playBackModel)?.playbackStarted(startPosition, ref);
}
_player?.applySubtitleSettings(ref.read(subtitleSettingsProvider));
}

Expand Down Expand Up @@ -223,8 +226,12 @@ class MediaControlsWrapper extends BaseAudioHandler implements VideoPlayerContro
Future<void> play() async {
WakelockPlus.enable();
_player?.play();

final currentPosition = await ref.read(playBackModel.select((value) => value?.startDuration()));
ref.read(playBackModel)?.playbackStarted(currentPosition ?? Duration.zero, ref);
final isPlaying = playbackState.value.playing;
if (!isPlaying) {
ref.read(playBackModel)?.playbackStarted(currentPosition ?? Duration.zero, ref);
}

final playBackItem = ref.read(playBackModel.select((value) => value?.item));
if (playBackItem == null) return;
Expand Down
2 changes: 1 addition & 1 deletion lib/wrappers/players/base_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ abstract class BasePlayer {
});
Future<void> dispose();
Future<void> open(BuildContext context);
Future<void> loadVideo(String url, bool play);
Future<void> loadVideo(String url, bool play, {Duration startPosition = Duration.zero});
Future<void> seek(Duration position);
Future<void> play();
Future<void> setVolume(double volume);
Expand Down
6 changes: 5 additions & 1 deletion lib/wrappers/players/lib_mdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class LibMDK extends BasePlayer {
}

@override
Future<void> loadVideo(String url, bool play) async {
Future<void> loadVideo(String url, bool play, {Duration startPosition = Duration.zero}) async {
_controller?.dispose();

final validUrl = isValidUrl(url);
Expand All @@ -81,6 +81,10 @@ class LibMDK extends BasePlayer {
await _controller?.initialize();
_controller?.addListener(() => updateState());

if (startPosition != Duration.zero) {
await _controller?.seekTo(startPosition);
}

if (play) {
await _controller?.play();
}
Expand Down
56 changes: 44 additions & 12 deletions lib/wrappers/players/lib_mpv.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,14 @@ class LibMPV extends BasePlayer {
}

@override
Future<void> loadVideo(String url, bool play) async {
Future<void> loadVideo(String url, bool play, {Duration startPosition = Duration.zero}) async {
_loadCompleter = Completer<void>();
await _player?.open(mpv.Media(url), play: play);
_firstLoadAttempt = DateTime.now();

await setStartPosition(startPosition);

await _player?.open(mpv.Media(url), play: play);

_retryTimer?.cancel();
_retryTimer = null;

Expand All @@ -111,27 +114,56 @@ class LibMPV extends BasePlayer {
_retryTimer?.cancel();
_retryTimer = null;
} else {
if (lastState.buffering == false) {
_finishedLoading();
} else {
log("Retrying to load video $url");
_player?.open(mpv.Media(url), play: play);
_retryTimer?.reset();
}
log("Retrying to load video $url");
await setStartPosition(startPosition);
await _player?.open(mpv.Media(url), play: play);
_retryTimer?.reset();
}
},
);

// Wait for the player to be ready
if (_loadCompleter?.isCompleted == false) {
StreamSubscription? subBuffering;
StreamSubscription? subDuration;

void onReady() {
if (_loadCompleter?.isCompleted == true) return;
_finishedLoading();
subBuffering?.cancel();
subDuration?.cancel();
}

subBuffering = _player?.stream.buffering.listen((event) {
if (event == false && (_player?.state.duration ?? Duration.zero) > Duration.zero) {
onReady();
}
});
subDuration = _player?.stream.duration.listen((event) {
if (event > Duration.zero) onReady();
});
}

_loadCompleter?.future.then(
(value) async {
await Future.delayed(const Duration(milliseconds: 150));
if (play && !lastState.playing) {
await _player?.play();
// Backup seek in case property didn't work
if (startPosition != Duration.zero && (_player?.state.position.inSeconds ?? 0) < startPosition.inSeconds - 5) {
await _player?.seek(startPosition);
}
},
);
return setState(lastState.update(buffering: true));
}

Future<void> setStartPosition(Duration position) async {
if (_player?.platform case final mpv.NativePlayer platform) {
await platform.setProperty(
'start',
'${position.inMilliseconds / 1000}',
);
}
}

void _finishedLoading() {
_loadCompleter?.complete();
_retryTimer?.cancel();
Expand Down
3 changes: 2 additions & 1 deletion lib/wrappers/players/native_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback {
}

@override
Future<void> loadVideo(String url, bool play) async => player.open(url, play);
Future<void> loadVideo(String url, bool play, {Duration startPosition = Duration.zero}) async =>
player.open(url, play);

@override
Future<StartResult> open(BuildContext newContext) async {
Expand Down
Loading