update to the playlists. testing

This commit is contained in:
Connor Johnstone
2026-04-01 22:12:58 -04:00
parent bd6656ff31
commit 4c42cf0131
3 changed files with 202 additions and 4 deletions
+130
View File
@@ -44,6 +44,12 @@ pub fn playlists_page() -> Html {
let count = use_state(|| 50usize);
let popularity_bias = use_state(|| 5u8);
let ordering = use_state(|| "interleave".to_string());
let discovery_range = use_state(|| 5u8);
let global_popularity = use_state(|| 0u8);
let country_filter = use_state(|| false);
let seed_weight = use_state(|| 5u8);
let max_tracks_per_artist = use_state(|| 0u8); // 0 = auto
let max_artists = use_state(|| 0u8); // 0 = unlimited
// Results
let generated = use_state(|| None::<GeneratedPlaylist>);
@@ -85,6 +91,12 @@ pub fn playlists_page() -> Html {
let count = count.clone();
let popularity_bias = popularity_bias.clone();
let ordering = ordering.clone();
let discovery_range = discovery_range.clone();
let global_popularity = global_popularity.clone();
let country_filter = country_filter.clone();
let seed_weight = seed_weight.clone();
let max_tracks_per_artist = max_tracks_per_artist.clone();
let max_artists = max_artists.clone();
let generated = generated.clone();
let error = error.clone();
let loading = loading.clone();
@@ -94,6 +106,12 @@ pub fn playlists_page() -> Html {
let count = *count;
let popularity_bias = *popularity_bias;
let ordering_val = (*ordering).clone();
let discovery_range_val = *discovery_range;
let global_popularity_val = *global_popularity;
let country_filter_val = *country_filter;
let seed_weight_val = *seed_weight;
let max_tpa_val = *max_tracks_per_artist;
let max_artists_val = *max_artists;
let generated = generated.clone();
let error = error.clone();
let loading = loading.clone();
@@ -110,6 +128,12 @@ pub fn playlists_page() -> Html {
popularity_bias,
ordering: ordering_val,
rules: None,
discovery_range: Some(discovery_range_val),
global_popularity: if global_popularity_val > 0 { Some(global_popularity_val) } else { None },
country_filter: if country_filter_val { Some(true) } else { None },
seed_weight: Some(seed_weight_val),
max_tracks_per_artist: if max_tpa_val > 0 { Some(max_tpa_val) } else { None },
max_artists: if max_artists_val > 0 { Some(max_artists_val) } else { None },
};
match api::generate_playlist(&req).await {
@@ -428,6 +452,112 @@ pub fn playlists_page() -> Html {
})
}}
/>
<label>{ format!("Discovery Range: {}", *discovery_range) }
<span class="text-sm text-muted">{ if *discovery_range <= 3 { " (focused)" } else if *discovery_range >= 7 { " (wide)" } else { "" } }</span>
</label>
<input
type="range"
min="0" max="10"
value={discovery_range.to_string()}
oninput={{
let dr = discovery_range.clone();
Callback::from(move |e: InputEvent| {
let input: HtmlInputElement = e.target_unchecked_into();
if let Ok(v) = input.value().parse() {
dr.set(v);
}
})
}}
/>
<label>{ format!("Seed Weight: {}", *seed_weight) }
<span class="text-sm text-muted">{ if *seed_weight == 0 { " (exclude seeds)" } else if *seed_weight >= 8 { " (heavy)" } else { "" } }</span>
</label>
<input
type="range"
min="0" max="10"
value={seed_weight.to_string()}
oninput={{
let sw = seed_weight.clone();
Callback::from(move |e: InputEvent| {
let input: HtmlInputElement = e.target_unchecked_into();
if let Ok(v) = input.value().parse() {
sw.set(v);
}
})
}}
/>
<label>{ format!("Global Popularity: {}", *global_popularity) }
<span class="text-sm text-muted">{ if *global_popularity == 0 { " (off)" } else { "" } }</span>
</label>
<input
type="range"
min="0" max="10"
value={global_popularity.to_string()}
oninput={{
let gp = global_popularity.clone();
Callback::from(move |e: InputEvent| {
let input: HtmlInputElement = e.target_unchecked_into();
if let Ok(v) = input.value().parse() {
gp.set(v);
}
})
}}
/>
<div class="form-group">
<label>
<input type="checkbox"
checked={*country_filter}
onchange={{
let cf = country_filter.clone();
Callback::from(move |e: Event| {
let input: HtmlInputElement = e.target_unchecked_into();
cf.set(input.checked());
})
}}
/>
{ " Same countries as seed artists" }
</label>
</div>
<div style="display: flex; gap: 1rem;">
<div class="form-group" style="flex: 1;">
<label>{ "Max Per Artist" }</label>
<select onchange={{
let mtpa = max_tracks_per_artist.clone();
Callback::from(move |e: Event| {
let select: HtmlSelectElement = e.target_unchecked_into();
if let Ok(v) = select.value().parse() {
mtpa.set(v);
}
})
}}>
<option value="0" selected={*max_tracks_per_artist == 0}>{ "Auto" }</option>
{ for [1u8, 2, 3, 5, 10, 15, 20, 25, 30, 50].iter().map(|n| html! {
<option value={n.to_string()} selected={*max_tracks_per_artist == *n}>
{ n.to_string() }
</option>
})}
</select>
</div>
<div class="form-group" style="flex: 1;">
<label>{ "Max Artists" }</label>
<select onchange={{
let ma = max_artists.clone();
Callback::from(move |e: Event| {
let select: HtmlSelectElement = e.target_unchecked_into();
if let Ok(v) = select.value().parse() {
ma.set(v);
}
})
}}>
<option value="0" selected={*max_artists == 0}>{ "Unlimited" }</option>
{ for [5u8, 10, 15, 20, 25, 30, 50, 75, 100].iter().map(|n| html! {
<option value={n.to_string()} selected={*max_artists == *n}>
{ n.to_string() }
</option>
})}
</select>
</div>
</div>
<div class="form-group">
<label>{ "Track Order" }</label>
<select onchange={{
+12
View File
@@ -362,6 +362,18 @@ pub struct GenerateRequest {
pub ordering: String,
#[serde(default)]
pub rules: Option<SmartRulesInput>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub discovery_range: Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub global_popularity: Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub country_filter: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub seed_weight: Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_tracks_per_artist: Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_artists: Option<u8>,
}
fn default_playlist_count() -> usize {