feat: implement audio seeking with arrow keys via ffplay restart

Seek now works by restarting ffplay with -ss offset instead of the
broken player.seek() stub. MoveLeft/MoveRight added as aliases for
SeekBackward/SeekForward to fix HashMap non-deterministic iteration
order causing Left arrow to resolve to MoveLeft instead of SeekBackward.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Kilin
2026-02-09 18:51:45 +03:00
parent 8a467b6418
commit 6d08300daa
3 changed files with 60 additions and 44 deletions

View File

@@ -74,10 +74,10 @@ pub async fn handle_message_selection<T: TdClientTrait>(app: &mut App<T>, _key:
Some(crate::config::Command::TogglePlayback) => {
handle_toggle_voice_playback(app).await;
}
Some(crate::config::Command::SeekForward) => {
Some(crate::config::Command::SeekForward | crate::config::Command::MoveRight) => {
handle_voice_seek(app, 5.0);
}
Some(crate::config::Command::SeekBackward) => {
Some(crate::config::Command::SeekBackward | crate::config::Command::MoveLeft) => {
handle_voice_seek(app, -5.0);
}
Some(crate::config::Command::ReactMessage) => {
@@ -540,7 +540,6 @@ async fn handle_toggle_voice_playback<T: TdClientTrait>(app: &mut App<T>) {
/// Seek голосового сообщения на delta секунд
fn handle_voice_seek<T: TdClientTrait>(app: &mut App<T>, delta: f32) {
use crate::tdlib::PlaybackStatus;
use std::time::Duration;
let Some(ref mut playback) = app.playback_state else {
return;
@@ -549,14 +548,27 @@ fn handle_voice_seek<T: TdClientTrait>(app: &mut App<T>, delta: f32) {
return;
};
if matches!(playback.status, PlaybackStatus::Playing | PlaybackStatus::Paused) {
let was_playing = matches!(playback.status, PlaybackStatus::Playing);
let was_paused = matches!(playback.status, PlaybackStatus::Paused);
if was_playing || was_paused {
let new_position = (playback.position + delta).clamp(0.0, playback.duration);
if player.seek(Duration::from_secs_f32(new_position)).is_ok() {
if was_playing {
// Перезапускаем ffplay с новой позиции
if player.resume_from(new_position).is_ok() {
playback.position = new_position;
app.last_playback_tick = Some(std::time::Instant::now());
}
} else {
// На паузе — только двигаем позицию, воспроизведение начнётся при resume
player.stop();
playback.position = new_position;
let arrow = if delta > 0.0 { "" } else { "" };
app.status_message = Some(format!("{} {:.0}s", arrow, new_position));
app.needs_redraw = true;
}
let arrow = if delta > 0.0 { "" } else { "" };
app.status_message = Some(format!("{} {:.0}s", arrow, new_position));
app.needs_redraw = true;
}
}