libminau 0.1.0

A library version of Minau, a music player built directly on top of Symphonia and CPAL.
Documentation
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use parking_lot::Mutex;
pub(crate) mod play;
pub(crate) mod player_structs;
pub(crate) mod metadata;
pub(crate) mod info;


/// State of Play
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PlaybackState {
    Playing,
    Paused,
    Stopped,
    Finished,
}

/// For local files
pub struct LocalPlayer {
    player: player_structs::Player,
    play_handle: Option<Arc<Mutex<play::MusicPlay>>>,
    state: Arc<Mutex<PlaybackState>>,
}

impl LocalPlayer {
    /// Create a new Player
    pub fn new<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
        let player = player_structs::Player::new(path);
        Ok(Self {
            player,
            play_handle: None,
            state: Arc::new(Mutex::new(PlaybackState::Stopped)),
        })
    }

    /// Play the music
    pub fn play(&mut self) -> crate::Result<()> {
        let music_play = self.player.clone().play();
        self.play_handle = Some(Arc::new(Mutex::new(music_play)));
        *self.state.lock() = PlaybackState::Playing;
        Ok(())
    }

    /// Stop the music
    pub fn pause(&self) -> crate::Result<()> {
        if let Some(handle) = &self.play_handle {
            handle.lock().pause();
            *self.state.lock() = PlaybackState::Paused;
        }
        Ok(())
    }

    /// Restart Music
    pub fn resume(&self) -> crate::Result<()> {
        if let Some(handle) = &self.play_handle {
            handle.lock().resume();
            *self.state.lock() = PlaybackState::Playing;
        }
        Ok(())
    }

    /// Seek
    pub fn seek(&self, position: Duration) -> crate::Result<()> {
        if let Some(handle) = &self.play_handle {
            handle.lock().seek(position)
                .map_err(|e| crate::Error::SeekError(e))?;
        }
        Ok(())
    }

    /// Volume setting (0.0 - 0.1)
    pub fn set_volume(&self, volume: f32) -> crate::Result<()> {
        if let Some(handle) = &self.play_handle {
            handle.lock().set_volume_mut(volume);
        }
        Ok(())
    }

    /// Get Volume
    pub fn volume(&self) -> f32 {
        self.play_handle
            .as_ref()
            .map(|h| h.lock().get_volume())
            .unwrap_or(1.0)
    }

    /// Get Playing position
    pub fn position(&self) -> Duration {
        self.play_handle
            .as_ref()
            .map(|h| h.lock().get_pos())
            .unwrap_or(Duration::ZERO)
    }

    /// Get playing state
    pub fn state(&self) -> PlaybackState {
        if let Some(handle) = &self.play_handle {
            if handle.lock().is_empty() {
                return PlaybackState::Finished;
            }
        }
        *self.state.lock()
    }

    /// Get MetaData
    pub fn metadata(&self) -> metadata::MetaData {
        self.player.metadata()
    }

    /// Get samplerate
    pub fn sample_rate(&self) -> u32 {
        self.player.sample_rate()
    }

    /// Get channels
    pub fn channels(&self) -> u16 {
        self.player.channels()
    }
}

/// for Streaming Player
pub struct StreamPlayer {
    player: Arc<Mutex<crate::play_url::UrlPlayer>>,
    url: String,
}

impl StreamPlayer {
    /// Create Streaming Player from URL
    pub async fn new(url: &str, volume: f32) -> crate::Result<Self> {
        let player = crate::play_url::setup_url_player(url, volume)
            .await
            .map_err(|e| crate::Error::NetworkError(e.to_string()))?;
        
        Ok(Self {
            player: Arc::new(Mutex::new(player)),
            url: url.to_string(),
        })
    }

    /// Stop the music
    pub fn pause(&self) {
        self.player.lock().pause();
    }

    /// Resume the music
    pub fn resume(&self) {
        self.player.lock().resume();
    }

    /// Set volume 
    pub fn set_volume(&self, volume: f32) {
        self.player.lock().set_volume(volume);
    }

    /// Get Volume
    pub fn volume(&self) -> f32 {
        self.player.lock().get_volume()
    }

    /// check is paused
    pub fn is_paused(&self) -> bool {
        self.player.lock().is_paused()
    }

    /// Check Play is Finished
    pub fn is_finished(&self) -> bool {
        self.player.lock().is_empty()
    }

    /// Get download progress (0.0-100.0)
    pub fn download_progress(&self) -> Option<f32> {
        self.player.lock().get_download_progress()
    }

    /// Downloaded bytes (MB)
    pub fn downloaded_mb(&self) -> f64 {
        self.player.lock().get_downloaded_mb()
    }
}

/// Integrated player interface
pub enum Player {
    Local(LocalPlayer),
    Stream(StreamPlayer),
}

impl Player {
    /// Create from local file
    pub fn from_file<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
        Ok(Player::Local(LocalPlayer::new(path)?))
    }

    /// Create from url (async)
    pub async fn from_url(url: &str, volume: f32) -> crate::Result<Self> {
        Ok(Player::Stream(StreamPlayer::new(url, volume).await?))
    }

    /// Play music
    pub fn play(&mut self) -> crate::Result<()> {
        match self {
            Player::Local(p) => p.play(),
            Player::Stream(_) => Ok(()), // StreamはすでにURL作成時に再生開始
        }
    }

    /// Set volume
    pub fn set_volume(&self, volume: f32) -> crate::Result<()> {
        match self {
            Player::Local(p) => p.set_volume(volume),
            Player::Stream(p) => {
                p.set_volume(volume);
                Ok(())
            }
        }
    }

    /// pause music
    pub fn pause(&self) -> crate::Result<()> {
        match self {
            Player::Local(p) => p.pause(),
            Player::Stream(p) => {
                p.pause();
                Ok(())
            }
        }
    }

    /// restart
    pub fn resume(&self) -> crate::Result<()> {
        match self {
            Player::Local(p) => p.resume(),
            Player::Stream(p) => {
                p.resume();
                Ok(())
            }
        }
    }

    /// Get current position
    pub fn position(&self) -> Duration {
        match self {
            Player::Local(p) => p.position(),
            Player::Stream(_) => Duration::ZERO, // Streamはpositionをサポートしない
        }
    }

    /// Get playing state
    pub fn state(&self) -> PlaybackState {
        match self {
            Player::Local(p) => p.state(),
            Player::Stream(p) => {
                if p.is_finished() {
                    PlaybackState::Finished
                } else if p.is_paused() {
                    PlaybackState::Paused
                } else {
                    PlaybackState::Playing
                }
            }
        }
    }

    /// wait until music is finished
    pub async fn wait_until_finished(&self) {
        loop {
            if self.state() == PlaybackState::Finished {
                break;
            }
            smol::Timer::after(Duration::from_millis(100)).await;
        }
    }
}