mirror of
https://github.com/narrrl/pl8list-rewind.git
synced 2026-11-04 16:47:00 +01:00
Add discovery modes, track tags and an artist graph
Tracks get tags from their title, description, artist name and BPM (styles such as riddim, versions such as VIP/dub/flip, and tempo), stored in track_tags and shown on cards. Related pages the crawler reads are kept in track_related, which links artists to each other. The feed takes ?mode=: riddim (140 BPM, riddim artists and their graph neighbours first), fresh, deep cuts, and an artist radio that plays an artist and the artists around them. Opening a radio crawls that artist's related pages first. The crawler searches riddim-adjacent terms first. The filters panel picks the feed, each card has a radio button (r), and a chip in the top bar returns to the normal feed.
This commit is contained in:
1 parent
b4470850bf
commit
f6c505907a
14 files changed
+737
-36
No files matched your search
@@ -61,9 +61,14 @@ pub async fn feed(State(app): St, Extension(current): Extension<CurrentUser>, Qu
|
||||
Ok(u) => u.clone(),
|
||||
Err(r) => return Ok(r),
|
||||
};
|
||||
let Some(ranker) = ranker::by_name(q.get("mode").map(String::as_str)) else {
|
||||
let artist = q.get("artist").map(String::as_str).filter(|h| valid_handle(h));
|
||||
let Some(mode) = ranker::Mode::parse(q.get("mode").map(String::as_str), artist) else {
|
||||
return Ok(json_error(StatusCode::BAD_REQUEST, "unknown mode"));
|
||||
};
|
||||
// The artist graph is thin at first: crawl the radio's artist and their related pages.
|
||||
if let ranker::Mode::Artist(h) = &mode {
|
||||
app.crawler.explore_artist(h.clone()).await;
|
||||
}
|
||||
let n = num(&q, "n").map(|n| n as usize).unwrap_or(10).clamp(1, 30);
|
||||
let filters = ranker::Filters {
|
||||
free_only: q.get("free").map(String::as_str) == Some("1"),
|
||||
@@ -74,7 +79,8 @@ pub async fn feed(State(app): St, Extension(current): Extension<CurrentUser>, Qu
|
||||
let deadline = Instant::now() + FEED_WAIT;
|
||||
let uid = user.id;
|
||||
let mut tracks = loop {
|
||||
let tracks = app.db.call(move |c| ranker::pick(c, uid, n, filters, ranker)).await?;
|
||||
let mode = mode.clone();
|
||||
let tracks = app.db.call(move |c| ranker::pick(c, uid, n, filters, &*mode.ranker(c)?)).await?;
|
||||
if !tracks.is_empty() || Instant::now() >= deadline {
|
||||
break tracks;
|
||||
}
|
||||
|
||||
@@ -380,3 +380,40 @@ async fn token_rotation_keeps_the_pl8list_connection() {
|
||||
assert_eq!(app.sync.key.sealer(&new, 5).open(&new_blob.unwrap()).unwrap().expose_secret(), "PL8");
|
||||
assert!(old_blob.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn feed_modes_filter_and_tag_tracks() {
|
||||
let (r, app, _) = setup("modes", "http://127.0.0.1:9").await;
|
||||
let token = app
|
||||
.db
|
||||
.call(|c| {
|
||||
let t = |id, title: &str, bpm| crate::db::Track {
|
||||
id,
|
||||
handle: "h".into(),
|
||||
artist_name: "H".into(),
|
||||
slug: format!("s{id}"),
|
||||
title: title.into(),
|
||||
bpm: Some(bpm),
|
||||
full_access: true,
|
||||
..Default::default()
|
||||
};
|
||||
crate::db::upsert_tracks(c, &[t(1, "Riddim VIP", 140.0), t(2, "Roller", 174.0)], None)?;
|
||||
crate::db::upsert_user(c, 5, "tester", None, None)?;
|
||||
crate::db::create_session(c, 5)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let cookie = format!("__Host-pl8inf={token}");
|
||||
let get = |uri: &str| r.clone().oneshot(req("GET", uri, None, Some(&cookie), "203.0.113.1"));
|
||||
let res = get("/local/feed?mode=riddim").await.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let body = json_body(res).await;
|
||||
let tracks = body["tracks"].as_array().unwrap();
|
||||
assert_eq!(tracks.len(), 1);
|
||||
assert_eq!(tracks[0]["tags"], serde_json::json!(["140", "riddim", "vip"]));
|
||||
for bad in ["/local/feed?mode=nope", "/local/feed?mode=artist", "/local/feed?mode=artist&artist=..%2Fx"] {
|
||||
assert_eq!(get(bad).await.unwrap().status(), StatusCode::BAD_REQUEST, "{bad}");
|
||||
}
|
||||
let res = get("/local/feed?mode=artist&artist=h").await.unwrap();
|
||||
assert_eq!(json_body(res).await["tracks"].as_array().unwrap().len(), 1);
|
||||
}
|
||||
Reference in new issue
Block a user