1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use std::fmt::{Display, Formatter, Result as FmtResult};
use crate::library::{Playlist, Song, PlaylistStore};

#[cfg(feature = "client")]
use {
    std::{
        env,
        os::unix::net::UnixStream,
        io::{Write, BufReader, Read},
        net::Shutdown
    },
    serde::Deserialize,
    lazy_static::lazy_static,
    rust_utils::logging::{Log, LogLevel}
};

#[cfg(feature = "client")]
lazy_static! {
    static ref TMP_DIR: String = format!("/tmp/mlounge-{}/", env::var("USER").expect("What is your name again?"));
}
/// Result of a player action
pub type PlayerResult<T> = Result<T, String>;

/// Repeat mode of the player
#[derive(PartialEq, Eq, Copy, Clone, serde_derive::Serialize, serde_derive::Deserialize)]
pub enum RepeatMode {
    One,
    All,
    Once
}

impl RepeatMode {
    /// How wide is the displayed repeat mode in terminal cells?
    pub fn disp_width(self) -> usize {
        match self {
            Self::One => 12,
            Self::All => 3,
            Self::Once => 9
        }
    }
}

impl Display for RepeatMode {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            RepeatMode::All => write!(f, "All"),
            RepeatMode::One => write!(f, "Current Song"),
            RepeatMode::Once => write!(f, "No Repeat")
        }
    }
}

/// State of the player
#[derive(PartialEq, Eq, Copy, Clone, serde_derive::Serialize, serde_derive::Deserialize)]
pub enum PlayerState {
    Playing,
    Paused(u64),
    Stopped
}

impl PlayerState {
    /// How wide is the displayed state in terminal cells?
    pub fn disp_width(self) -> usize {
        match self {
            Self::Paused(_) => 8,
            _ => 9
        }
    }
}

impl Display for PlayerState {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            PlayerState::Paused(_) => write!(f, "[paused]"),
            PlayerState::Playing => write!(f, "[playing]"),
            PlayerState::Stopped => write!(f, "[stopped]")
        }
    }
}

/// Status of the player
#[derive(Copy, Clone, serde_derive::Serialize, serde_derive::Deserialize)]
pub struct PlayerStatus {
    pub stopped: bool,
    pub paused: bool,
    pub position: usize,
    pub repeat_mode: RepeatMode,
    pub state: PlayerState,
    pub song_id: u64
}

#[doc(hidden)]
#[derive(Clone, serde_derive::Serialize, serde_derive::Deserialize, PartialEq, Eq, Debug)]
pub enum PlayerCommand {
    Load(Playlist),
    CycleRepeat,
    Play,
    Restart,
    Next,
    Prev,
    Resume,
    Pause,
    Stop,
    Seek(u64),
    SetQueue(Playlist),
    Shuffle,
    SetPos(Song),
    ProcessID,
    CurrentTime,
    Status,
    GetQueue,
    GetLibrary,
    RescanLibrary,
    GetPlaylists,
    UpdatePlaylists(PlaylistStore)
}

impl PlayerCommand {
    /// Is this command mutable?
    pub fn is_mut(&self) -> bool {
        matches!(*self, Self::Load(_) | Self::CycleRepeat | Self::Play | Self::Restart | Self::Next | Self::Prev | Self::Resume | Self::Pause | Self::Stop | Self::Seek(_) | Self::SetQueue(_) | Self::Shuffle | Self::SetPos(_))
    }
}

#[cfg(feature = "client")]
#[doc(hidden)]
pub fn audio_cmd<T: for <'de> Deserialize<'de>>(log: Option<&Log>, cmd: PlayerCommand) -> PlayerResult<T> {
    let socket_file = format!("{}/socket", *TMP_DIR);
    match UnixStream::connect(socket_file) {
        Ok(mut stream) => {
            let encoded = bincode::serialize(&cmd).expect("What went wrong?!");
            stream.write_all(&encoded).expect("Unable to write to socket!");
            stream.shutdown(Shutdown::Write).expect("What went wrong?!");
            let buffer = BufReader::new(&stream);
            let encoded: Vec<u8> = buffer.bytes().map(Result::unwrap_or_default).collect();

            bincode::deserialize(&encoded).map_err(|why| why.to_string())
        }

        Err(why) => {
            if let Some(log) = log {
                log.line(LogLevel::Error, format!("Unable to connect to socket: {why}"), true);
            }

            Err(why.to_string())
        }
    }
}