use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
pub enum Source {
#[default]
Local,
Server(String),
}
impl Source {
pub fn as_str(&self) -> &str {
match self {
Source::Local => "local",
Source::Server(id) => id.as_str(),
}
}
pub fn from_column(s: &str) -> Self {
if s == "local" {
Source::Local
} else {
Source::Server(s.to_owned())
}
}
pub fn server_id(&self) -> Option<&str> {
match self {
Source::Server(id) => Some(id),
Source::Local => None,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
pub enum MusicService {
#[default]
Jellyfin,
#[serde(alias = "Navidrome")]
Subsonic,
Custom,
YtMusic,
SoundCloud,
}
impl MusicService {
pub fn display_name(&self) -> &'static str {
match self {
Self::Jellyfin => "Jellyfin",
Self::Subsonic => "Subsonic",
Self::Custom => "Custom",
Self::YtMusic => "YouTube Music",
Self::SoundCloud => "SoundCloud",
}
}
pub fn uses_browser_signin(&self) -> bool {
matches!(self, Self::YtMusic | Self::SoundCloud)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MusicServer {
pub name: String,
pub url: String,
#[serde(default)]
pub service: MusicService,
pub access_token: Option<String>,
pub user_id: Option<String>,
#[serde(default)]
pub id: Option<String>,
#[serde(default)]
pub yt_browser: Option<Browser>,
#[serde(default)]
pub yt_anonymous: bool,
}
impl MusicServer {
pub fn new(name: String, url: String) -> Self {
Self::new_with_service(name, url, MusicService::Jellyfin)
}
pub fn new_with_service(name: String, url: String, service: MusicService) -> Self {
Self {
name,
url: url.trim_end_matches('/').to_string(),
service,
access_token: None,
user_id: None,
id: Some(uuid::Uuid::new_v4().to_string()),
yt_browser: None,
yt_anonymous: false,
}
}
pub fn yt_browser(&self) -> Option<Browser> {
self.yt_browser
}
}
impl Default for MusicServer {
fn default() -> Self {
Self {
name: String::new(),
url: String::new(),
service: MusicService::Jellyfin,
access_token: None,
user_id: None,
id: None,
yt_browser: None,
yt_anonymous: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Browser {
Chrome,
Chromium,
Brave,
Edge,
Vivaldi,
}
impl Browser {
pub const ALL: &'static [Browser] = &[
Browser::Chrome,
Browser::Chromium,
Browser::Brave,
Browser::Edge,
Browser::Vivaldi,
];
pub fn id(self) -> &'static str {
match self {
Browser::Chrome => "chrome",
Browser::Chromium => "chromium",
Browser::Brave => "brave",
Browser::Edge => "edge",
Browser::Vivaldi => "vivaldi",
}
}
pub fn label(self) -> &'static str {
match self {
Browser::Chrome => "Chrome",
Browser::Chromium => "Chromium",
Browser::Brave => "Brave",
Browser::Edge => "Edge",
Browser::Vivaldi => "Vivaldi",
}
}
pub fn from_id(s: &str) -> Option<Browser> {
Browser::ALL
.iter()
.copied()
.find(|browser| browser.id() == s)
}
}
impl std::fmt::Display for Browser {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.label())
}
}
pub type JellyfinServer = MusicServer;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SavedServer {
pub id: String,
pub name: String,
pub url: String,
#[serde(default)]
pub service: MusicService,
#[serde(default)]
pub yt_browser: Option<Browser>,
#[serde(default)]
pub yt_anonymous: bool,
}
impl SavedServer {
pub fn new(name: String, url: String, service: MusicService) -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
name,
url: url.trim_end_matches('/').to_string(),
service,
yt_browser: None,
yt_anonymous: false,
}
}
pub fn from_music_server(server: &MusicServer) -> Self {
Self {
id: server
.id
.clone()
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
name: server.name.clone(),
url: server.url.clone(),
service: server.service,
yt_browser: server.yt_browser,
yt_anonymous: server.yt_anonymous,
}
}
pub fn matches(&self, server: &MusicServer) -> bool {
if let Some(sid) = server.id.as_ref()
&& sid == &self.id
{
return true;
}
self.url == server.url && self.service == server.service
}
}