forked from
rocksky.app/rocksky
A decentralized music tracking and discovery platform built on AT Protocol 馃幍
1pub mod auth;
2pub mod cache;
3pub mod crypto;
4pub mod handlers;
5pub mod listenbrainz;
6pub mod musicbrainz;
7pub mod params;
8pub mod repo;
9pub mod response;
10pub mod rocksky;
11pub mod scrobbler;
12pub mod signature;
13pub mod spotify;
14pub mod types;
15pub mod xata;
16
17use std::{env, sync::Arc, time::Duration};
18
19use actix_limitation::{Limiter, RateLimiter};
20use actix_session::SessionExt;
21use actix_web::{
22 dev::ServiceRequest,
23 web::{self, Data},
24 App, HttpServer,
25};
26use anyhow::Error;
27use owo_colors::OwoColorize;
28use sqlx::postgres::PgPoolOptions;
29
30use crate::{cache::Cache, musicbrainz::client::MusicbrainzClient};
31
32pub const BANNER: &str = r#"
33 ___ ___ _____ __ __ __
34 / | __ ______/ (_)___ / ___/______________ / /_ / /_ / /__ _____
35 / /| |/ / / / __ / / __ \ \__ \/ ___/ ___/ __ \/ __ \/ __ \/ / _ \/ ___/
36 / ___ / /_/ / /_/ / / /_/ / ___/ / /__/ / / /_/ / /_/ / /_/ / / __/ /
37/_/ |_\__,_/\__,_/_/\____/ /____/\___/_/ \____/_.___/_.___/_/\___/_/
38
39 This is the Rocksky Scrobbler API compatible with Last.fm AudioScrobbler API
40"#;
41
42pub async fn run() -> Result<(), Error> {
43 println!("{}", BANNER.magenta());
44
45 let cache = Cache::new()?;
46
47 let pool = PgPoolOptions::new()
48 .max_connections(5)
49 .connect(&env::var("XATA_POSTGRES_URL")?)
50 .await?;
51 let conn = Arc::new(pool);
52
53 let host = env::var("SCROBBLE_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
54 let port = env::var("SCROBBLE_PORT")
55 .unwrap_or_else(|_| "7882".to_string())
56 .parse::<u16>()
57 .unwrap_or(7882);
58
59 tracing::info!(url = %format!("http://{}:{}", host, port).bright_green(), "Starting Scrobble server @");
60
61 let limiter = web::Data::new(
62 Limiter::builder("redis://127.0.0.1")
63 .key_by(|req: &ServiceRequest| {
64 req.get_session()
65 .get(&"session-id")
66 .unwrap_or_else(|_| req.cookie(&"rate-api-id").map(|c| c.to_string()))
67 })
68 .limit(100)
69 .period(Duration::from_secs(60)) // 60 minutes
70 .build()
71 .unwrap(),
72 );
73
74 let mb_client = MusicbrainzClient::new().await?;
75 let mb_client = Arc::new(mb_client);
76
77 HttpServer::new(move || {
78 App::new()
79 .wrap(RateLimiter::default())
80 .app_data(limiter.clone())
81 .app_data(Data::new(conn.clone()))
82 .app_data(Data::new(cache.clone()))
83 .app_data(Data::new(mb_client.clone()))
84 .service(handlers::handle_methods)
85 .service(handlers::handle_nowplaying)
86 .service(handlers::handle_submission)
87 .service(listenbrainz::handlers::handle_submit_listens)
88 .service(listenbrainz::handlers::handle_validate_token)
89 .service(listenbrainz::handlers::handle_search_users)
90 .service(listenbrainz::handlers::handle_get_playing_now)
91 .service(listenbrainz::handlers::handle_get_listens)
92 .service(listenbrainz::handlers::handle_get_listen_count)
93 .service(listenbrainz::handlers::handle_get_artists)
94 .service(listenbrainz::handlers::handle_get_recordings)
95 .service(listenbrainz::handlers::handle_get_release_groups)
96 .service(handlers::index)
97 .service(handlers::handle_get)
98 })
99 .bind((host, port))?
100 .run()
101 .await?;
102
103 Ok(())
104}