mirror of
https://github.com/narrrl/pl8list-rewind.git
synced 2026-11-04 16:47:00 +01:00
Turn artist radio into a station sheet with reasons on each track
The radio button (r) opens a sheet with the artist, their tags and their closest neighbours instead of switching the feed at once; tapping a neighbour walks the artist graph. Starting jumps to the radio's first track, the top-bar chip shows the station, each radio card says why it is there, and an end card offers the way back when the radio runs dry. GET /local/artist feeds the sheet and starts crawling a new artist's neighbourhood (at most every 10 minutes per artist). Artist-radio tracks carry why: seed, related, or via the neighbour that leads to them.
This commit is contained in:
1 parent
e8c0f40128
commit
76cfbb12ac
13 files changed
+654
-42
No files matched your search
@@ -144,6 +144,47 @@ pub async fn event(State(app): St, Extension(current): Extension<CurrentUser>, J
|
||||
Ok((no_store(), Json(json!({ "ok": true }))).into_response())
|
||||
}
|
||||
|
||||
/// How many neighbours the radio sheet shows, and how few count as still exploring.
|
||||
const SHEET_NEIGHBOURS: usize = 12;
|
||||
const FEW_NEIGHBOURS: usize = 3;
|
||||
|
||||
/// An artist and the ones next to them in the artist graph, for the radio sheet.
|
||||
/// Starts crawling the neighbourhood when it is new, so the sheet can poll while
|
||||
/// `exploring` is true.
|
||||
pub async fn artist(State(app): St, Extension(current): Extension<CurrentUser>, Query(q): Query<HashMap<String, String>>) -> Res {
|
||||
let uid = match current.require() {
|
||||
Ok(u) => u.id,
|
||||
Err(r) => return Ok(r),
|
||||
};
|
||||
let Some(handle) = q.get("handle").filter(|h| valid_handle(h)).cloned() else {
|
||||
return Ok(StatusCode::BAD_REQUEST.into_response());
|
||||
};
|
||||
app.crawler.explore_artist(handle.clone()).await;
|
||||
let body = app
|
||||
.db
|
||||
.call(move |c| {
|
||||
let Some(artist) = db::artist(c, &handle)? else { return Ok(None) };
|
||||
let mut neighbours = vec![];
|
||||
for (h, weight) in ranker::scaled_neighbours(c, &handle)?.into_iter().take(SHEET_NEIGHBOURS) {
|
||||
if let Some(a) = db::artist(c, &h)? {
|
||||
neighbours.push(json!({ "handle": a.handle, "name": a.name, "img": a.img, "weight": weight }));
|
||||
}
|
||||
}
|
||||
Ok(Some(json!({
|
||||
"followed": db::follows(c, uid)?.contains(&handle),
|
||||
"tags": db::artist_tags(c, &handle, 4)?,
|
||||
"exploring": neighbours.len() < FEW_NEIGHBOURS,
|
||||
"artist": artist,
|
||||
"neighbours": neighbours,
|
||||
})))
|
||||
})
|
||||
.await?;
|
||||
Ok(match body {
|
||||
Some(b) => (no_store(), Json(b)).into_response(),
|
||||
None => json_error(StatusCode::NOT_FOUND, "unknown artist"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Valid pl8list handles, as seen in every handle crawled so far.
|
||||
fn valid_handle(h: &str) -> bool {
|
||||
(1..=64).contains(&h.len()) && h.bytes().all(|b| b.is_ascii_alphanumeric() || b"_.-".contains(&b)) && h != "." && h != ".."
|
||||
|
||||
Reference in new issue
Block a user