music-lounge 0.5.0

Yet another music player
use stopwatch2::Stopwatch;
use crate::{
    library::{Playlist, Song},
    player::{
        LOG,
        PlayerState,
        RepeatMode,
        source::GSTSource
    }
};

// player object for the background process
pub struct BackgroundPlayer {
    // the play queue (can be a playlist)
    pub queue: Playlist,

    // the currently playing song
    pub song: Song,

    // the state of the player
    pub state: PlayerState,

    // should one song, all songs, or no songs be repeated?
    pub repeat: RepeatMode,

    // the song the player is at in the playlist
    pub position: usize,

    // the timer that tracks the song time
    timer: Stopwatch,

    // the time that was seeked
    seeked_time: u64,

    // GStreamer player source
    source: GSTSource
}

impl BackgroundPlayer {
    // create a new player (should be done only once!)
    pub fn new() -> BackgroundPlayer {
        BackgroundPlayer {
            song: Song::empty(),
            queue: Playlist::new("_"),
            position: usize::MAX,
            state: PlayerState::Stopped,
            repeat: RepeatMode::Once,
            timer: Stopwatch::default(),
            seeked_time: 0,
            source: GSTSource::new()
        }
    }

    // load a new playlist
    pub fn load_list(&mut self, list: &Playlist) {
        self.queue = list.clone();
        self.song = list.songs[0].clone();
        self.position = 0;
        self.play();
    }

    // cycle the repeat mode
    pub fn cycle_repeat(&mut self) {
        let new_mode = match self.repeat {
            RepeatMode::All => RepeatMode::Once,
            RepeatMode::Once => RepeatMode::One,
            RepeatMode::One => RepeatMode::All
        };

        self.repeat = new_mode;
    }

    // play the current song or start from the beginning
    pub fn play(&mut self) {
        if self.song.name.is_empty() { return; }
        self.seeked_time = 0;
        self.timer = Stopwatch::default();
        self.timer.start();
        self.state = PlayerState::Playing;
        self.song = self.queue.songs[self.position].clone();
        self.source.set_song(&self.song);
        LOG.line_basic(format!("Playing {} by {}...", self.song.name, self.song.artist), true);
    }

    pub fn restart(&mut self) {
        self.timer = Stopwatch::default();
        self.timer.start();
        self.seeked_time = 0;
        self.source.set_song(&self.song);
        LOG.line_basic("Restarting current song...", true);
    }

    // next song
    pub fn next(&mut self) {
        if self.position == self.queue.songs.len() - 1 {
            match self.repeat {
                RepeatMode::All => {
                    self.position = 0;
                    self.song = self.queue.songs[0].clone();
                    self.play();
                    return;
                }

                RepeatMode::Once => {
                    self.timer = Stopwatch::default();
                    self.seeked_time = 0;
                    self.position = 0;
                    self.song = self.queue.songs[0].clone();
                    self.source.set_song(&self.queue.songs[0]);
                    self.silent_pause();
                    return;
                }

                RepeatMode::One => { }
            }
        }
        self.player_stop();
        self.timer = Stopwatch::default();

        if self.repeat != RepeatMode::One {
            self.position += 1;
            self.song = self.queue.songs[self.position].clone();
            self.play();
        }
        else if self.repeat == RepeatMode::One {
            self.restart();
        }
    }

    // previous song
    pub fn prev(&mut self) {
        if self.position == 0 {
            self.position = self.queue.songs.len() - 1;
        }
        else {
            self.position -= 1;
        }
        self.player_stop();
        self.song = self.queue.songs[self.position].clone();
        self.play();
    }

    // resume paused song
    pub fn resume(&mut self) {
        if let PlayerState::Paused(_) = self.state {
            self.timer.start();
            self.state = PlayerState::Playing;
            self.source.play();
        }
        LOG.line_basic("Resuming current song...", true);
    }

    // pause the song
    pub fn pause(&mut self) {
        self.silent_pause();
        LOG.line_basic("Pausing current song...", true);
    }

    fn silent_pause(&mut self) {
        self.timer.stop();
        self.state = PlayerState::Paused(self.seeked_time + self.timer.elapsed().as_secs());
        self.source.pause();
    }

    // stop the player and clear the play queue
    pub fn stop(&mut self) {
        if self.is_stopped() { return; }
        self.player_stop();
        self.queue = Playlist::new("_");
        self.song = Song::empty();
        self.position = usize::MAX;
        self.state = PlayerState::Stopped;
        self.timer.stop();
        self.timer = Stopwatch::default();
        LOG.line_basic("Stopping player...", true);
    }

    // shuffle the play queue
    pub fn shuffle_queue(&mut self) {
        LOG.line_basic("Shuffling play queue...", true);
        if self.state == PlayerState::Stopped {
            return;
        }
        self.queue.shuffle(self.position);
        self.position = 0;
    }

    // get the current time of the song (as seconds)
    pub fn cur_time_secs(&self) -> u64 {
        if let PlayerState::Paused(elapsed) = self.state {
            elapsed
        }
        else if self.state == PlayerState::Stopped {
            0
        }
        else {
            self.seeked_time + self.timer.elapsed().as_secs()
        }
    }

    // is the player paused
    pub fn is_paused(&self) -> bool { matches!(self.state, PlayerState::Paused(_)) }

    // is the player stopped
    pub fn is_stopped(&self) -> bool { self.state == PlayerState::Stopped }

    // go to another song in the play queue
    pub fn set_pos(&mut self, song: &Song) {
        self.player_stop();
        // find the song's position in the play queue
        for (i, s) in self.queue.songs.iter().enumerate() {
            if s == song {
                self.position = i;
            }
        }

        // set the song
        self.song = song.clone();
        self.play()
    }

    // automatically advance the player to the next song when the song is finished
    pub fn auto_advance(&mut self) {
        if self.state == PlayerState::Playing && self.timer.elapsed().as_secs() + self.seeked_time >= self.song.duration {
            self.next();
        }
    }

    pub fn find_pos(&mut self) {
        for (i, song) in self.queue.songs.iter().enumerate() {
            if *song == self.song {
                self.position = i;
            }
        }
    }

    // seek to specific spot in the track
    pub fn seek(&mut self, time: u64) {
        self.silent_pause();
        self.timer = Stopwatch::default();
        self.seeked_time = time;
        if time >= self.song.duration {
            self.next();
            return;
        }

        self.source.seek(time);
    }

    // just stop the player
    fn player_stop(&mut self) { self.source.stop(); }
}