обновление

This commit is contained in:
2026-10-13 16:24:18 +02:00
parent f1c5deb4bc
commit 255a99ed0f
7 changed files with 491 additions and 158 deletions

133
src/app.rs Normal file
View File

@@ -0,0 +1,133 @@
use std::time::Duration;
use ratatui::widgets::ListState;
use crate::audio::AudioPlayer;
use crate::track::{scan_dir, Track};
pub struct App {
pub tracks: Vec<Track>,
pub list_state: ListState,
pub player: AudioPlayer,
pub status: String,
pub playing_index: Option<usize>,
}
impl App {
pub fn new(music_dir: &str) -> Result<Self, Box<dyn std::error::Error>> {
let tracks = scan_dir(music_dir);
let mut list_state = ListState::default();
if !tracks.is_empty() {
list_state.select(Some(0));
}
Ok(Self {
tracks,
list_state,
player: AudioPlayer::new()?,
status: "Нажмите Enter для воспроизведения".into(),
playing_index: None,
})
}
pub fn next(&mut self) {
if self.tracks.is_empty() { return; }
let i = match self.list_state.selected() {
Some(i) if i >= self.tracks.len() - 1 => 0,
Some(i) => i + 1,
None => 0,
};
self.list_state.select(Some(i));
}
pub fn prev(&mut self) {
if self.tracks.is_empty() { return; }
let i = match self.list_state.selected() {
Some(0) => self.tracks.len() - 1,
Some(i) => i - 1,
None => 0,
};
self.list_state.select(Some(i));
}
pub fn play_index(&mut self, index: usize) {
let Some(track) = self.tracks.get(index) else { return; };
match self.player.play(&track.path) {
Ok(_) => {
self.status = format!("▶ {} — {}", track.artist, track.title);
self.playing_index = Some(index);
}
Err(e) => self.status = format!("Ошибка: {}", e),
}
}
pub fn play_selected(&mut self) {
if let Some(i) = self.list_state.selected() {
self.play_index(i);
}
}
pub fn stop(&mut self) {
self.player.stop();
self.playing_index = None;
self.status = "Остановлено".into();
}
pub fn toggle_pause(&mut self) {
self.player.toggle_pause();
}
pub fn maybe_advance(&mut self) {
if self.playing_index.is_none() || !self.player.is_finished() {
return;
}
let next = match self.playing_index {
Some(i) if i + 1 < self.tracks.len() => i + 1,
_ => 0,
};
self.play_index(next);
self.list_state.select(Some(next));
}
pub fn volume_up(&mut self) {
self.player.change_volume(0.05);
self.status = format!("Громкость: {}%", (self.player.volume() * 100.0) as u32);
}
pub fn volume_down(&mut self) {
self.player.change_volume(-0.05);
self.status = format!("Громкость: {}%", (self.player.volume() * 100.0) as u32);
}
pub fn seek(&mut self, delta_secs: i64) {
if self.playing_index.is_none() {
self.status = "Ничего не воспроизводится".into();
return;
}
let total = self
.playing_index
.and_then(|i| self.tracks.get(i))
.map(|t| t.duration)
.unwrap_or(Duration::ZERO);
match self.player.seek_by(delta_secs, total) {
Ok(new_pos) => {
let sign = if delta_secs >= 0 { "+" } else { "" };
self.status = format!(
"Перемотка {}{}с → {:02}:{:02}",
sign, delta_secs,
new_pos.as_secs() / 60,
new_pos.as_secs() % 60,
);
}
Err(_) => self.status = "Перемотка недоступна для этого формата".into(),
}
}
/// Текущая позиция и длина играющего трека (для прогресс-бара)
pub fn progress(&self) -> (Duration, Duration) {
match self.playing_index.and_then(|i| self.tracks.get(i)) {
Some(t) => (self.player.position(), t.duration),
None => (Duration::ZERO, Duration::ZERO),
}
}
}