Moved listenbrainz api in as its tiny. Add initial schema
This commit is contained in:
36
src/daemon.rs
Normal file
36
src/daemon.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use crate::models::TrackedPlaylist;
|
||||
use sqlx::postgres::PgPool;
|
||||
use std::env;
|
||||
use std::time::Duration;
|
||||
use tokio::time::{interval, MissedTickBehavior};
|
||||
|
||||
pub async fn update_playlists_daemon() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let database = PgPool::connect(&env::var("DATABASE_URL").unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut ticker = interval(Duration::from_secs(1800));
|
||||
ticker.set_missed_tick_behavior(MissedTickBehavior::Skip);
|
||||
|
||||
loop {
|
||||
let records_res = sqlx::query_as!(TrackedPlaylist, "SELECT * FROM \"tracked_playlist\"")
|
||||
.fetch_all(&database)
|
||||
.await;
|
||||
|
||||
match records_res {
|
||||
Ok(records) => {
|
||||
for record in records {
|
||||
update_playlist(record).await;
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
println!("Could not fetch tracked_playlists: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
ticker.tick().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_playlist(playlist: TrackedPlaylist) {}
|
93
src/listenbrainz.rs
Normal file
93
src/listenbrainz.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
const BASE: &'static str = "https://api.listenbrainz.org/1";
|
||||
|
||||
enum StatsType {
|
||||
Artists,
|
||||
Recordings,
|
||||
Releases,
|
||||
ReleaseGroups,
|
||||
}
|
||||
|
||||
impl StatsType {
|
||||
fn stub(&self) -> &'static str {
|
||||
match self {
|
||||
StatsType::Artists => "artists",
|
||||
StatsType::Recordings => "recordings",
|
||||
StatsType::Releases => "releases",
|
||||
StatsType::ReleaseGroups => "release-groups",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum StatsRange {
|
||||
ThisWeek,
|
||||
ThisMonth,
|
||||
ThisYear,
|
||||
Week,
|
||||
Month,
|
||||
Quarter,
|
||||
HalfYear,
|
||||
Year,
|
||||
All,
|
||||
}
|
||||
|
||||
impl StatsRange {
|
||||
fn param(&self) -> &'static str {
|
||||
match self {
|
||||
StatsRange::ThisWeek => "this_week",
|
||||
StatsRange::ThisMonth => "this_month",
|
||||
StatsRange::ThisYear => "this_year",
|
||||
StatsRange::Week => "week",
|
||||
StatsRange::Month => "month",
|
||||
StatsRange::Quarter => "quarter",
|
||||
StatsRange::HalfYear => "half_yearly",
|
||||
StatsRange::Year => "year",
|
||||
StatsRange::All => "all_time",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RecordingsResponse {
|
||||
payload: RecordingsPayload,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RecordingsPayload {
|
||||
count: u64,
|
||||
from_ts: u64,
|
||||
to_ts: u64,
|
||||
last_updated: u64,
|
||||
total_recording_count: u64,
|
||||
user_id: String,
|
||||
offset: u64,
|
||||
range: String,
|
||||
pub recordings: Vec<RecordingsEntry>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RecordingsEntry {
|
||||
pub track_name: String,
|
||||
pub recording_mbid: String,
|
||||
pub release_mbid: String,
|
||||
}
|
||||
|
||||
pub async fn recordings(
|
||||
user: &str,
|
||||
range: StatsRange,
|
||||
) -> Result<RecordingsPayload, reqwest::Error> {
|
||||
let url = format!(
|
||||
"{}/stats/user/{}/{}?range={}",
|
||||
BASE,
|
||||
user,
|
||||
StatsType::Recordings.stub(),
|
||||
range.param()
|
||||
);
|
||||
|
||||
Ok(reqwest::get(url)
|
||||
.await?
|
||||
.json::<RecordingsResponse>()
|
||||
.await?
|
||||
.payload)
|
||||
}
|
38
src/main.rs
38
src/main.rs
@ -1,14 +1,44 @@
|
||||
mod daemon;
|
||||
mod listenbrainz;
|
||||
mod models;
|
||||
|
||||
use crate::daemon::update_playlists_daemon;
|
||||
use crate::listenbrainz::StatsRange;
|
||||
use axum::extract::State;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use sqlx::postgres::PgPool;
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::main]
|
||||
fn main() {
|
||||
let app = Router::new().route("/", get(index));
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let database = PgPool::connect(&env::var("DATABASE_URL")?).await?;
|
||||
sqlx::migrate!().run(&database).await?;
|
||||
|
||||
let app = Router::new().route("/", get(index)).with_state(database);
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
axum::Server::bind(&addr)
|
||||
.serve()
|
||||
.serve(app.into_make_service())
|
||||
.await?;
|
||||
|
||||
tokio::spawn(async move {
|
||||
update_playlists_daemon().await.unwrap();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn index() {
|
||||
async fn index(State(pool): State<PgPool>) -> String {
|
||||
let response = listenbrainz::recordings("jellywx", StatsRange::Week)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
response
|
||||
.recordings
|
||||
.iter()
|
||||
.map(|a| a.track_name.clone())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
11
src/models.rs
Normal file
11
src/models.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use sqlx::types::Uuid;
|
||||
|
||||
pub struct TrackedPlaylist {
|
||||
pub rule_id: i64,
|
||||
pub playlist_id: Option<Uuid>,
|
||||
pub playlist_name: Option<String>,
|
||||
pub playlist_size: i32,
|
||||
pub tracking_user: Option<String>,
|
||||
pub tracking_type: Option<String>,
|
||||
pub reduce_duplication_on: Option<String>,
|
||||
}
|
Reference in New Issue
Block a user