1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
6pub enum Source {
7 #[default]
8 Local,
9 Server(String),
10}
11
12impl Source {
13 pub fn as_str(&self) -> &str {
15 match self {
16 Source::Local => "local",
17 Source::Server(id) => id.as_str(),
18 }
19 }
20
21 pub fn from_column(s: &str) -> Self {
23 if s == "local" {
24 Source::Local
25 } else {
26 Source::Server(s.to_owned())
27 }
28 }
29
30 pub fn server_id(&self) -> Option<&str> {
32 match self {
33 Source::Server(id) => Some(id),
34 Source::Local => None,
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
40pub enum MusicService {
41 #[default]
42 Jellyfin,
43 #[serde(alias = "Navidrome")]
44 Subsonic,
45 Custom,
46 YtMusic,
47 SoundCloud,
48}
49
50impl MusicService {
51 pub fn display_name(&self) -> &'static str {
52 match self {
53 Self::Jellyfin => "Jellyfin",
54 Self::Subsonic => "Subsonic",
55 Self::Custom => "Custom",
56 Self::YtMusic => "YouTube Music",
57 Self::SoundCloud => "SoundCloud",
58 }
59 }
60
61 pub fn uses_browser_signin(&self) -> bool {
64 matches!(self, Self::YtMusic | Self::SoundCloud)
65 }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
69pub struct MusicServer {
70 pub name: String,
71 pub url: String,
72 #[serde(default)]
73 pub service: MusicService,
74 pub access_token: Option<String>,
75 pub user_id: Option<String>,
76 #[serde(default)]
77 pub id: Option<String>,
78 #[serde(default)]
80 pub yt_browser: Option<Browser>,
81 #[serde(default)]
83 pub yt_anonymous: bool,
84}
85
86impl MusicServer {
87 pub fn new(name: String, url: String) -> Self {
88 Self::new_with_service(name, url, MusicService::Jellyfin)
89 }
90
91 pub fn new_with_service(name: String, url: String, service: MusicService) -> Self {
92 Self {
93 name,
94 url: url.trim_end_matches('/').to_string(),
95 service,
96 access_token: None,
97 user_id: None,
98 id: Some(uuid::Uuid::new_v4().to_string()),
99 yt_browser: None,
100 yt_anonymous: false,
101 }
102 }
103
104 pub fn yt_browser(&self) -> Option<Browser> {
105 self.yt_browser
106 }
107}
108
109impl Default for MusicServer {
110 fn default() -> Self {
111 Self {
112 name: String::new(),
113 url: String::new(),
114 service: MusicService::Jellyfin,
115 access_token: None,
116 user_id: None,
117 id: None,
118 yt_browser: None,
119 yt_anonymous: false,
120 }
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
125#[serde(rename_all = "lowercase")]
126pub enum Browser {
127 Chrome,
128 Chromium,
129 Brave,
130 Edge,
131 Vivaldi,
132}
133
134impl Browser {
135 pub const ALL: &'static [Browser] = &[
136 Browser::Chrome,
137 Browser::Chromium,
138 Browser::Brave,
139 Browser::Edge,
140 Browser::Vivaldi,
141 ];
142
143 pub fn id(self) -> &'static str {
146 match self {
147 Browser::Chrome => "chrome",
148 Browser::Chromium => "chromium",
149 Browser::Brave => "brave",
150 Browser::Edge => "edge",
151 Browser::Vivaldi => "vivaldi",
152 }
153 }
154
155 pub fn label(self) -> &'static str {
156 match self {
157 Browser::Chrome => "Chrome",
158 Browser::Chromium => "Chromium",
159 Browser::Brave => "Brave",
160 Browser::Edge => "Edge",
161 Browser::Vivaldi => "Vivaldi",
162 }
163 }
164
165 pub fn from_id(s: &str) -> Option<Browser> {
166 Browser::ALL
167 .iter()
168 .copied()
169 .find(|browser| browser.id() == s)
170 }
171}
172
173impl std::fmt::Display for Browser {
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 f.write_str(self.label())
176 }
177}
178
179pub type JellyfinServer = MusicServer;
180
181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
182pub struct SavedServer {
183 pub id: String,
184 pub name: String,
185 pub url: String,
186 #[serde(default)]
187 pub service: MusicService,
188 #[serde(default)]
190 pub yt_browser: Option<Browser>,
191 #[serde(default)]
193 pub yt_anonymous: bool,
194}
195
196impl SavedServer {
197 pub fn new(name: String, url: String, service: MusicService) -> Self {
198 Self {
199 id: uuid::Uuid::new_v4().to_string(),
200 name,
201 url: url.trim_end_matches('/').to_string(),
202 service,
203 yt_browser: None,
204 yt_anonymous: false,
205 }
206 }
207
208 pub fn from_music_server(server: &MusicServer) -> Self {
209 Self {
210 id: server
211 .id
212 .clone()
213 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
214 name: server.name.clone(),
215 url: server.url.clone(),
216 service: server.service,
217 yt_browser: server.yt_browser,
218 yt_anonymous: server.yt_anonymous,
219 }
220 }
221
222 pub fn matches(&self, server: &MusicServer) -> bool {
223 if let Some(sid) = server.id.as_ref()
224 && sid == &self.id
225 {
226 return true;
227 }
228 self.url == server.url && self.service == server.service
229 }
230}