Implement MPV integration for audio/video playback

- Initialize libmpv with audio-only configuration
- Implement play, pause, resume, stop, seek controls
- Add position and duration tracking from MPV
- Auto-advance to next track when current ends
- Update keybindings to use actual player
- Add shell.nix for development environment with libmpv
- Real playback now working with Enter/Space/n/p keys
This commit is contained in:
2025-12-06 12:49:46 +01:00
parent 0093db98c2
commit e840aa9b26
3 changed files with 106 additions and 19 deletions

View File

@@ -1,38 +1,88 @@
// Player module - MPV integration placeholder
// Full implementation in Phase 3
use anyhow::Result;
use anyhow::{anyhow, Result};
use libmpv::Mpv;
use std::path::Path;
pub struct Player {
// MPV instance will be added in Phase 3
mpv: Mpv,
}
impl Player {
pub fn new() -> Result<Self> {
Ok(Self {})
let mpv = Mpv::new().map_err(|e| anyhow!("Failed to create MPV instance: {:?}", e))?;
// Configure MPV for audio/video playback
mpv.set_property("vo", "null").ok(); // No video output for TUI mode
mpv.set_property("video", "no").ok(); // Disable video decoding
mpv.set_property("volume", 100).ok();
mpv.set_property("pause", false).ok();
Ok(Self { mpv })
}
pub fn play(&mut self, _path: &Path) -> Result<()> {
// TODO: Implement MPV playback in Phase 3
tracing::info!("Play called (not yet implemented)");
pub fn play(&mut self, path: &Path) -> Result<()> {
let path_str = path.to_string_lossy();
self.mpv
.command("loadfile", &[&path_str, "replace"])
.map_err(|e| anyhow!("Failed to load file: {:?}", e))?;
self.mpv.set_property("pause", false).ok();
tracing::info!("Playing: {}", path_str);
Ok(())
}
pub fn pause(&mut self) -> Result<()> {
// TODO: Implement pause in Phase 3
tracing::info!("Pause called (not yet implemented)");
self.mpv
.set_property("pause", true)
.map_err(|e| anyhow!("Failed to pause: {:?}", e))?;
Ok(())
}
pub fn resume(&mut self) -> Result<()> {
self.mpv
.set_property("pause", false)
.map_err(|e| anyhow!("Failed to resume: {:?}", e))?;
Ok(())
}
pub fn stop(&mut self) -> Result<()> {
// TODO: Implement stop in Phase 3
tracing::info!("Stop called (not yet implemented)");
self.mpv
.command("stop", &[])
.map_err(|e| anyhow!("Failed to stop: {:?}", e))?;
Ok(())
}
pub fn set_volume(&mut self, _volume: i64) -> Result<()> {
// TODO: Implement volume control in Phase 3
pub fn set_volume(&mut self, volume: i64) -> Result<()> {
self.mpv
.set_property("volume", volume)
.map_err(|e| anyhow!("Failed to set volume: {:?}", e))?;
Ok(())
}
pub fn get_position(&self) -> Option<f64> {
self.mpv.get_property("time-pos").ok()
}
pub fn get_duration(&self) -> Option<f64> {
self.mpv.get_property("duration").ok()
}
pub fn is_playing(&self) -> bool {
self.mpv
.get_property::<bool>("pause")
.map(|paused| !paused)
.unwrap_or(false)
}
pub fn is_idle(&self) -> bool {
self.mpv
.get_property::<String>("idle-active")
.map(|s| s == "yes")
.unwrap_or(true)
}
pub fn seek(&mut self, seconds: f64) -> Result<()> {
self.mpv
.command("seek", &[&seconds.to_string(), "relative"])
.map_err(|e| anyhow!("Failed to seek: {:?}", e))?;
Ok(())
}
}