A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz
98
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(feed): enhance feed handling with new repository structure and data types

+232 -22
+24 -4
crates/feed/src/feed.rs
··· 1 1 use crate::config::Config; 2 + use crate::feed_handler::FeedHandler; 2 3 use crate::subscriber::ScrobbleSubscriber; 3 - use crate::types::{DidDocument, FeedSkeletonParameters, Service}; 4 - use crate::{feed_handler::FeedHandler, types::FeedSkeletonQuery}; 4 + use crate::types::{DidDocument, FeedSkeleton, Request, Service, SkeletonFeedScrobbleData}; 5 5 use anyhow::Error; 6 + use atrium_api::app::bsky::feed::get_feed_skeleton::Parameters as FeedSkeletonQuery; 7 + use atrium_api::app::bsky::feed::get_feed_skeleton::ParametersData as FeedSkeletonParameters; 6 8 use sqlx::postgres::PgPoolOptions; 7 9 use sqlx::{Pool, Postgres}; 8 10 use std::env; ··· 149 151 })) 150 152 } 151 153 152 - async fn describe_feed_generator(feed_name: String) -> Result<impl warp::Reply, warp::Rejection> { 154 + async fn describe_feed_generator(_feed_name: String) -> Result<impl warp::Reply, warp::Rejection> { 153 155 Ok(warp::reply::json(&serde_json::json!({}))) 154 156 } 155 157 ··· 157 159 query: FeedSkeletonQuery, 158 160 handler: Handler, 159 161 ) -> Result<impl warp::Reply, warp::Rejection> { 160 - Ok(warp::reply::json(&serde_json::json!({}))) 162 + let skeleton = handler 163 + .serve_feed(Request { 164 + cursor: query.cursor.clone(), 165 + feed: query.feed.clone(), 166 + limit: query.limit.map(u8::from), 167 + }) 168 + .await; 169 + 170 + Ok::<warp::reply::Json, warp::Rejection>(warp::reply::json(&FeedSkeleton { 171 + cursor: skeleton.cursor, 172 + feed: skeleton 173 + .feed 174 + .into_iter() 175 + .map(|uri| SkeletonFeedScrobbleData { 176 + feed_context: None, 177 + scrobble: uri.0, 178 + }) 179 + .collect(), 180 + })) 161 181 }
crates/feed/src/feeds/discover/mod.rs

This is a binary file and will not be displayed.

+4
crates/feed/src/feeds/mod.rs
··· 1 + pub mod discover; 2 + pub mod new; 3 + pub mod scrobbles; 4 + pub mod trending;
crates/feed/src/feeds/new/mod.rs

This is a binary file and will not be displayed.

crates/feed/src/feeds/scrobbles/mod.rs

This is a binary file and will not be displayed.

crates/feed/src/feeds/trending/mod.rs

This is a binary file and will not be displayed.

+21 -6
crates/feed/src/lib.rs
··· 1 1 use anyhow::Error; 2 + use duckdb::Connection; 3 + use sqlx::{postgres::PgPoolOptions, Pool, Postgres}; 2 4 use std::{env, net::SocketAddr, sync::Arc}; 3 5 use tokio::sync::Mutex; 4 6 ··· 11 13 pub mod config; 12 14 pub mod feed; 13 15 pub mod feed_handler; 16 + pub mod feeds; 17 + pub mod repo; 14 18 pub mod subscriber; 15 19 pub mod types; 16 20 pub mod xata; ··· 27 31 28 32 #[derive(Clone)] 29 33 pub struct RecentlyPlayedFeedHandler { 30 - pub scrobbles: Arc<Mutex<Vec<Scrobble>>>, 34 + pub conn: Arc<Mutex<Connection>>, 35 + pub pool: Arc<Mutex<Pool<Postgres>>>, 31 36 } 32 37 33 38 impl FeedHandler for RecentlyPlayedFeedHandler { 34 - async fn insert_scrobble(&self, scrobble: Scrobble) { 39 + async fn insert_scrobble(&self, _scrobble: Scrobble) { 35 40 todo!() 36 41 } 37 42 38 - async fn delete_scrobble(&self, uri: types::Uri) { 43 + async fn delete_scrobble(&self, _uri: types::Uri) { 39 44 todo!() 40 45 } 41 46 42 - async fn serve_feed(&self, request: types::Request) -> FeedResult { 43 - todo!() 47 + async fn serve_feed(&self, _request: types::Request) -> FeedResult { 48 + FeedResult { 49 + feed: vec![], 50 + cursor: None, 51 + } 44 52 } 45 53 } 46 54 47 55 pub async fn run() -> Result<(), Error> { 56 + let conn = Connection::open("./rocksky-seed.ddb")?; 57 + let pool = PgPoolOptions::new() 58 + .max_connections(5) 59 + .connect(&env::var("XATA_POSTGRES_URL")?) 60 + .await?; 61 + 48 62 let mut feed = RecentlyPlayedFeed { 49 63 handler: RecentlyPlayedFeedHandler { 50 - scrobbles: Arc::new(Mutex::new(Vec::new())), 64 + conn: Arc::new(Mutex::new(conn)), 65 + pool: Arc::new(Mutex::new(pool)), 51 66 }, 52 67 }; 53 68 let host = env::var("FEED_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
crates/feed/src/repo/duckdb/album.rs

This is a binary file and will not be displayed.

crates/feed/src/repo/duckdb/artist.rs

This is a binary file and will not be displayed.

+67
crates/feed/src/repo/duckdb/mod.rs
··· 1 + use super::Repo; 2 + 3 + pub mod album; 4 + pub mod artist; 5 + pub mod scrobble; 6 + pub mod track; 7 + pub mod user; 8 + 9 + pub struct DuckdbRepo {} 10 + 11 + impl Repo for DuckdbRepo { 12 + fn insert_album() -> Result<(), anyhow::Error> { 13 + todo!() 14 + } 15 + 16 + fn insert_artist() -> Result<(), anyhow::Error> { 17 + todo!() 18 + } 19 + 20 + fn insert_scrobble() -> Result<(), anyhow::Error> { 21 + todo!() 22 + } 23 + 24 + fn insert_track() -> Result<(), anyhow::Error> { 25 + todo!() 26 + } 27 + 28 + fn insert_user() -> Result<(), anyhow::Error> { 29 + todo!() 30 + } 31 + 32 + fn get_albums() -> Result<(), anyhow::Error> { 33 + todo!() 34 + } 35 + 36 + fn get_artists() -> Result<(), anyhow::Error> { 37 + todo!() 38 + } 39 + 40 + fn get_scrobbles() -> Result<(), anyhow::Error> { 41 + todo!() 42 + } 43 + 44 + fn get_tracks() -> Result<(), anyhow::Error> { 45 + todo!() 46 + } 47 + 48 + fn get_users() -> Result<(), anyhow::Error> { 49 + todo!() 50 + } 51 + 52 + fn get_album() -> Result<(), anyhow::Error> { 53 + todo!() 54 + } 55 + 56 + fn get_artist() -> Result<(), anyhow::Error> { 57 + todo!() 58 + } 59 + 60 + fn get_track() -> Result<(), anyhow::Error> { 61 + todo!() 62 + } 63 + 64 + fn get_user() -> Result<(), anyhow::Error> { 65 + todo!() 66 + } 67 + }
crates/feed/src/repo/duckdb/scrobble.rs

This is a binary file and will not be displayed.

crates/feed/src/repo/duckdb/track.rs

This is a binary file and will not be displayed.

crates/feed/src/repo/duckdb/user.rs

This is a binary file and will not be displayed.

+21
crates/feed/src/repo/mod.rs
··· 1 + use anyhow::Error; 2 + 3 + pub mod duckdb; 4 + pub mod postgres; 5 + 6 + pub trait Repo { 7 + fn insert_album() -> Result<(), Error>; 8 + fn insert_artist() -> Result<(), Error>; 9 + fn insert_scrobble() -> Result<(), Error>; 10 + fn insert_track() -> Result<(), Error>; 11 + fn insert_user() -> Result<(), Error>; 12 + fn get_albums() -> Result<(), Error>; 13 + fn get_artists() -> Result<(), Error>; 14 + fn get_scrobbles() -> Result<(), Error>; 15 + fn get_tracks() -> Result<(), Error>; 16 + fn get_users() -> Result<(), Error>; 17 + fn get_album() -> Result<(), Error>; 18 + fn get_artist() -> Result<(), Error>; 19 + fn get_track() -> Result<(), Error>; 20 + fn get_user() -> Result<(), Error>; 21 + }
crates/feed/src/repo/postgres/album.rs

This is a binary file and will not be displayed.

crates/feed/src/repo/postgres/artist.rs

This is a binary file and will not be displayed.

+67
crates/feed/src/repo/postgres/mod.rs
··· 1 + use super::Repo; 2 + 3 + pub mod album; 4 + pub mod artist; 5 + pub mod scrobble; 6 + pub mod track; 7 + pub mod user; 8 + 9 + pub struct PostgresRepo {} 10 + 11 + impl Repo for PostgresRepo { 12 + fn insert_album() -> Result<(), anyhow::Error> { 13 + todo!() 14 + } 15 + 16 + fn insert_artist() -> Result<(), anyhow::Error> { 17 + todo!() 18 + } 19 + 20 + fn insert_scrobble() -> Result<(), anyhow::Error> { 21 + todo!() 22 + } 23 + 24 + fn insert_track() -> Result<(), anyhow::Error> { 25 + todo!() 26 + } 27 + 28 + fn insert_user() -> Result<(), anyhow::Error> { 29 + todo!() 30 + } 31 + 32 + fn get_albums() -> Result<(), anyhow::Error> { 33 + todo!() 34 + } 35 + 36 + fn get_artists() -> Result<(), anyhow::Error> { 37 + todo!() 38 + } 39 + 40 + fn get_scrobbles() -> Result<(), anyhow::Error> { 41 + todo!() 42 + } 43 + 44 + fn get_tracks() -> Result<(), anyhow::Error> { 45 + todo!() 46 + } 47 + 48 + fn get_users() -> Result<(), anyhow::Error> { 49 + todo!() 50 + } 51 + 52 + fn get_album() -> Result<(), anyhow::Error> { 53 + todo!() 54 + } 55 + 56 + fn get_artist() -> Result<(), anyhow::Error> { 57 + todo!() 58 + } 59 + 60 + fn get_track() -> Result<(), anyhow::Error> { 61 + todo!() 62 + } 63 + 64 + fn get_user() -> Result<(), anyhow::Error> { 65 + todo!() 66 + } 67 + }
+1
crates/feed/src/repo/postgres/scrobble.rs
··· 1 +
crates/feed/src/repo/postgres/track.rs

This is a binary file and will not be displayed.

crates/feed/src/repo/postgres/user.rs

This is a binary file and will not be displayed.

+13 -11
crates/feed/src/types.rs
··· 22 22 pub feed: Vec<Uri>, 23 23 } 24 24 25 - pub struct FeedSkeletonQuery {} 26 - 27 - #[derive(Deserialize)] 28 - pub struct FeedSkeletonParameters {} 29 - 30 - impl Into<FeedSkeletonQuery> for FeedSkeletonParameters { 31 - fn into(self) -> FeedSkeletonQuery { 32 - FeedSkeletonQuery {} 33 - } 34 - } 35 - 36 25 #[derive(Debug, Clone)] 37 26 pub struct Scrobble {} 38 27 ··· 72 61 pub kind: String, 73 62 pub commit: Option<Commit>, 74 63 } 64 + 65 + #[derive(Serialize, Deserialize)] 66 + pub struct SkeletonFeedScrobbleData { 67 + #[serde(skip_serializing_if = "core::option::Option::is_none")] 68 + pub feed_context: core::option::Option<String>, 69 + pub scrobble: String, 70 + } 71 + 72 + #[derive(Serialize, Deserialize)] 73 + pub struct FeedSkeleton { 74 + pub cursor: Option<String>, 75 + pub feed: Vec<SkeletonFeedScrobbleData>, 76 + }
+1 -1
crates/feed/src/xata/mod.rs
··· 1 - 1 + pub mod user;
+13
crates/feed/src/xata/user.rs
··· 1 + use chrono::{DateTime, Utc}; 2 + use serde::Deserialize; 3 + 4 + #[derive(Debug, sqlx::FromRow, Deserialize, Clone)] 5 + pub struct User { 6 + pub xata_id: String, 7 + pub display_name: String, 8 + pub did: String, 9 + pub handle: String, 10 + pub avatar: String, 11 + #[serde(with = "chrono::serde::ts_seconds")] 12 + pub xata_createdat: DateTime<Utc>, 13 + }