Re-organized providers and added a few
This commit is contained in:
@@ -147,6 +147,10 @@ pub fn artist_page(props: &Props) -> Html {
|
||||
|
||||
html! {
|
||||
<div>
|
||||
if let Some(ref banner) = d.artist_banner {
|
||||
<div class="artist-banner" style={format!("background-image: url('{banner}')")}>
|
||||
</div>
|
||||
}
|
||||
<div class="page-header">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex gap-2 items-center">
|
||||
|
||||
@@ -265,6 +265,56 @@ pub fn settings_page() -> Html {
|
||||
html! {}
|
||||
};
|
||||
|
||||
let lastfm_key_html = {
|
||||
let key_set = ytauth.as_ref().map(|s| s.lastfm_api_key_set).unwrap_or(false);
|
||||
if key_set {
|
||||
html! {
|
||||
<p class="text-sm" style="margin: 0.25rem 0 0 0;">
|
||||
<span style="color: var(--success);">{ "\u{2713}" }</span>
|
||||
{ " API key configured via " }
|
||||
<code>{ "SHANTY_LASTFM_API_KEY" }</code>
|
||||
</p>
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
<p class="text-sm" style="margin: 0.25rem 0 0 0; color: var(--warning);">
|
||||
{ "Set " }
|
||||
<code>{ "SHANTY_LASTFM_API_KEY" }</code>
|
||||
{ " environment variable. Get a key at " }
|
||||
<a href="https://www.last.fm/api/account/create" target="_blank">{ "last.fm/api/account/create" }</a>
|
||||
{ " (use any name, leave callback URL blank)." }
|
||||
</p>
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let fanart_key_html = {
|
||||
let key_set = ytauth
|
||||
.as_ref()
|
||||
.map(|s| s.fanart_api_key_set)
|
||||
.unwrap_or(false);
|
||||
if key_set {
|
||||
html! {
|
||||
<p class="text-sm" style="margin: 0.25rem 0 0 0;">
|
||||
<span style="color: var(--success);">{ "\u{2713}" }</span>
|
||||
{ " API key configured via " }
|
||||
<code>{ "SHANTY_FANART_API_KEY" }</code>
|
||||
{ ". Provides artist thumbnails and HD banners." }
|
||||
</p>
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
<p class="text-sm" style="margin: 0.25rem 0 0 0; color: var(--warning);">
|
||||
{ "Set " }
|
||||
<code>{ "SHANTY_FANART_API_KEY" }</code>
|
||||
{ " environment variable. Get a key at " }
|
||||
<a href="https://fanart.tv/get-an-api-key/" target="_blank">{ "fanart.tv" }</a>
|
||||
{ "." }
|
||||
</p>
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<div class="page-header">
|
||||
@@ -441,6 +491,82 @@ pub fn settings_page() -> Html {
|
||||
{ ytauth_html }
|
||||
</div>
|
||||
|
||||
// Metadata Providers
|
||||
<div class="card">
|
||||
<h3>{ "Metadata Providers" }</h3>
|
||||
<div class="form-group">
|
||||
<label>{ "Music Database" }</label>
|
||||
<select onchange={let config = config.clone(); Callback::from(move |e: Event| {
|
||||
let select: HtmlSelectElement = e.target_unchecked_into();
|
||||
let mut cfg = (*config).clone().unwrap();
|
||||
cfg.metadata.metadata_source = select.value();
|
||||
config.set(Some(cfg));
|
||||
})}>
|
||||
{ for [("musicbrainz", "MusicBrainz")].iter().map(|(v, label)| html! {
|
||||
<option value={*v} selected={c.metadata.metadata_source == *v}>{ label }</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{ "Artist Images" }</label>
|
||||
<select onchange={let config = config.clone(); Callback::from(move |e: Event| {
|
||||
let select: HtmlSelectElement = e.target_unchecked_into();
|
||||
let mut cfg = (*config).clone().unwrap();
|
||||
cfg.metadata.artist_image_source = select.value();
|
||||
config.set(Some(cfg));
|
||||
})}>
|
||||
{ for [("wikipedia", "Wikipedia"), ("fanarttv", "fanart.tv")].iter().map(|(v, label)| html! {
|
||||
<option value={*v} selected={c.metadata.artist_image_source == *v}>{ label }</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
if c.metadata.artist_image_source == "fanarttv" {
|
||||
{ fanart_key_html.clone() }
|
||||
}
|
||||
<div class="form-group">
|
||||
<label>{ "Artist Bios" }</label>
|
||||
<select onchange={let config = config.clone(); Callback::from(move |e: Event| {
|
||||
let select: HtmlSelectElement = e.target_unchecked_into();
|
||||
let mut cfg = (*config).clone().unwrap();
|
||||
cfg.metadata.artist_bio_source = select.value();
|
||||
config.set(Some(cfg));
|
||||
})}>
|
||||
{ for [("wikipedia", "Wikipedia"), ("lastfm", "Last.fm")].iter().map(|(v, label)| html! {
|
||||
<option value={*v} selected={c.metadata.artist_bio_source == *v}>{ label }</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
if c.metadata.artist_bio_source == "lastfm" {
|
||||
{ lastfm_key_html.clone() }
|
||||
}
|
||||
<div class="form-group">
|
||||
<label>{ "Lyrics" }</label>
|
||||
<select onchange={let config = config.clone(); Callback::from(move |e: Event| {
|
||||
let select: HtmlSelectElement = e.target_unchecked_into();
|
||||
let mut cfg = (*config).clone().unwrap();
|
||||
cfg.metadata.lyrics_source = select.value();
|
||||
config.set(Some(cfg));
|
||||
})}>
|
||||
{ for [("lrclib", "LRCLIB")].iter().map(|(v, label)| html! {
|
||||
<option value={*v} selected={c.metadata.lyrics_source == *v}>{ label }</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{ "Cover Art" }</label>
|
||||
<select onchange={let config = config.clone(); Callback::from(move |e: Event| {
|
||||
let select: HtmlSelectElement = e.target_unchecked_into();
|
||||
let mut cfg = (*config).clone().unwrap();
|
||||
cfg.metadata.cover_art_source = select.value();
|
||||
config.set(Some(cfg));
|
||||
})}>
|
||||
{ for [("coverartarchive", "Cover Art Archive")].iter().map(|(v, label)| html! {
|
||||
<option value={*v} selected={c.metadata.cover_art_source == *v}>{ label }</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// Indexing
|
||||
<div class="card">
|
||||
<h3>{ "Indexing" }</h3>
|
||||
|
||||
@@ -67,6 +67,8 @@ pub struct FullArtistDetail {
|
||||
pub artist_photo: Option<String>,
|
||||
#[serde(default)]
|
||||
pub artist_bio: Option<String>,
|
||||
#[serde(default)]
|
||||
pub artist_banner: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
@@ -176,6 +178,10 @@ pub struct YtAuthStatus {
|
||||
pub ytdlp_latest: Option<String>,
|
||||
#[serde(default)]
|
||||
pub ytdlp_update_available: bool,
|
||||
#[serde(default)]
|
||||
pub lastfm_api_key_set: bool,
|
||||
#[serde(default)]
|
||||
pub fanart_api_key_set: bool,
|
||||
}
|
||||
|
||||
// --- Downloads ---
|
||||
@@ -289,6 +295,8 @@ pub struct AppConfig {
|
||||
pub download: DownloadConfigFe,
|
||||
#[serde(default)]
|
||||
pub indexing: IndexingConfigFe,
|
||||
#[serde(default)]
|
||||
pub metadata: MetadataConfigFe,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
|
||||
@@ -341,3 +349,45 @@ pub struct IndexingConfigFe {
|
||||
#[serde(default)]
|
||||
pub concurrency: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MetadataConfigFe {
|
||||
#[serde(default = "default_metadata_source")]
|
||||
pub metadata_source: String,
|
||||
#[serde(default = "default_artist_image_source")]
|
||||
pub artist_image_source: String,
|
||||
#[serde(default = "default_artist_bio_source")]
|
||||
pub artist_bio_source: String,
|
||||
#[serde(default = "default_lyrics_source")]
|
||||
pub lyrics_source: String,
|
||||
#[serde(default = "default_cover_art_source")]
|
||||
pub cover_art_source: String,
|
||||
}
|
||||
|
||||
impl Default for MetadataConfigFe {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
metadata_source: default_metadata_source(),
|
||||
artist_image_source: default_artist_image_source(),
|
||||
artist_bio_source: default_artist_bio_source(),
|
||||
lyrics_source: default_lyrics_source(),
|
||||
cover_art_source: default_cover_art_source(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_metadata_source() -> String {
|
||||
"musicbrainz".into()
|
||||
}
|
||||
fn default_artist_image_source() -> String {
|
||||
"wikipedia".into()
|
||||
}
|
||||
fn default_artist_bio_source() -> String {
|
||||
"wikipedia".into()
|
||||
}
|
||||
fn default_lyrics_source() -> String {
|
||||
"lrclib".into()
|
||||
}
|
||||
fn default_cover_art_source() -> String {
|
||||
"coverartarchive".into()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user