From 64e41af96fc49a79ab899718b702be09b0e9649b Mon Sep 17 00:00:00 2001
From: MrDulfin
Date: Wed, 1 Jan 2025 01:45:36 -0500
Subject: [PATCH] Implemented ListenBrainz Scrobbling
---
dmp-core/Cargo.toml | 1 +
dmp-core/src/music_controller/connections.rs | 88 +++++++++++++++-----
dmp-core/src/music_controller/controller.rs | 11 ++-
src/App.css | 7 ++
src/App.tsx | 14 +---
5 files changed, 85 insertions(+), 36 deletions(-)
diff --git a/dmp-core/Cargo.toml b/dmp-core/Cargo.toml
index c64b603..290d3cf 100644
--- a/dmp-core/Cargo.toml
+++ b/dmp-core/Cargo.toml
@@ -40,3 +40,4 @@ itertools = "0.13.0"
prismriver = { git = "https://github.com/Dangoware/prismriver.git"}
parking_lot = "0.12.3"
discord-presence = { version = "1.4.1", features = ["activity_type"] }
+listenbrainz = "0.8.1"
diff --git a/dmp-core/src/music_controller/connections.rs b/dmp-core/src/music_controller/connections.rs
index 9ada884..18d364d 100644
--- a/dmp-core/src/music_controller/connections.rs
+++ b/dmp-core/src/music_controller/connections.rs
@@ -1,15 +1,17 @@
#![allow(while_true)]
-use std::{thread::sleep, time::{Duration, SystemTime, UNIX_EPOCH}};
+use std::{sync::Arc, thread::sleep, time::{Duration, SystemTime, UNIX_EPOCH}};
use chrono::TimeDelta;
use crossbeam::{scope, select};
use crossbeam_channel::{bounded, Receiver};
use discord_presence::Client;
+use listenbrainz::ListenBrainz;
+use parking_lot::RwLock;
use prismriver::State as PrismState;
-use crate::music_storage::library::{Song, Tag};
+use crate::{config::Config, music_storage::library::{Song, Tag}};
-use super::controller::Controller;
+use super::controller::{Controller, PlaybackInfo};
#[derive(Debug, Clone)]
pub(super) enum ConnectionsNotification {
@@ -19,6 +21,7 @@ pub(super) enum ConnectionsNotification {
},
StateChange(PrismState),
SongChange(Song),
+ EOS,
}
#[derive(Debug)]
@@ -32,7 +35,9 @@ pub(super) struct ControllerConnections {
}
impl Controller {
- pub(super) fn handle_connections(ControllerConnections {
+ pub(super) fn handle_connections(
+ config: Arc>,
+ ControllerConnections {
notifications_tx,
inner: ConnectionsInput {
discord_rpc_client_id
@@ -41,35 +46,46 @@ impl Controller {
) {
let (dc_state_rx, dc_state_tx) = bounded::(1);
let (dc_song_rx, dc_song_tx) = bounded::(1);
+ let (lb_song_rx, lb_song_tx) = bounded::(1);
+ let (lb_eos_rx, lb_eos_tx) = bounded::<()>(1);
scope(|s| {
s.builder().name("Notifications Sorter".to_string()).spawn(|_| {
use ConnectionsNotification::*;
while true {
match notifications_tx.recv().unwrap() {
- Playback { position, duration } => { continue; }
+ Playback { position, duration } => {}
StateChange(state) => {
dc_state_rx.send(state.clone()).unwrap();
}
SongChange(song) => {
- dc_song_rx.send(song).unwrap();
+ dc_song_rx.send(song.clone()).unwrap();
+ lb_song_rx.send(song).unwrap();
+ }
+ EOS => {
+ lb_eos_rx.send(()).unwrap();
}
}
}
}).unwrap();
if let Some(client_id) = discord_rpc_client_id {
- println!("Discord thingy detected");
s.builder().name("Discord RPC Handler".to_string()).spawn(move |_| {
Controller::discord_rpc(client_id, dc_song_tx, dc_state_tx);
}).unwrap();
};
+
+ if let Some(token) = config.read().connections.listenbrainz_token.clone() {
+ s.builder().name("ListenBrainz Handler".to_string()).spawn(move |_| {
+ Controller::listenbrainz_scrobble(&token, lb_song_tx, lb_eos_tx);
+ }).unwrap();
+ }
}).unwrap();
}
fn discord_rpc(client_id: u64, song_tx: Receiver, state_tx: Receiver) {
- // TODO: Handle seeking position change
+ // TODO: Handle seeking position change and pause
std::thread::spawn(move || {
- let mut client = discord_presence::Client::new(client_id);
+ let mut client = discord_presence::Client::with_error_config(client_id, Duration::from_secs(5), None);
client.start();
while !Client::is_ready() { sleep(Duration::from_millis(100)); }
println!("discord connected");
@@ -97,8 +113,8 @@ impl Controller {
*song = Some(song_);
now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards?").as_secs();
}
- }
- default(Duration::from_millis(4500)) => ()
+ },
+ default(Duration::from_millis(99)) => ()
}
client.set_activity(|activity| {
@@ -131,17 +147,45 @@ impl Controller {
a
}.assets(|a| {
a.large_text(state.clone())
- })
+ }).instance(true)
}).unwrap();
- println!("Updated Discord Status");
}
});
}
+
+ fn listenbrainz_scrobble(token: &str, song_tx: Receiver, eos_tx: Receiver<()>) {
+ let mut client = ListenBrainz::new();
+ client.authenticate(token).unwrap();
+ if !client.is_authenticated() {
+ return;
+ }
+
+ let mut song: Option = None;
+ while true {
+ let song = &mut song;
+ let client = &client;
+ select! {
+ recv(song_tx) -> res => {
+ if let Ok(_song) = res {
+ client.playing_now(_song.get_tag(&Tag::Artist).map_or("", |tag| tag.as_str()), _song.get_tag(&Tag::Title).map_or("", |tag| tag.as_str()), None).unwrap();
+ *song = Some(_song);
+ println!("Song Listening")
+ }
+ },
+ recv(eos_tx) -> _ => {
+ if let Some(song) = song {
+ client.listen(song.get_tag(&Tag::Artist).map_or("", |tag| tag.as_str()), song.get_tag(&Tag::Title).map_or("", |tag| tag.as_str()), None).unwrap();
+ println!("Song Scrobbled");
+ }
+ }
+ }
+ }
+ }
}
#[cfg(test)]
mod test_super {
- use std::thread::sleep;
+ use std::thread::{sleep, spawn};
use crossbeam_channel::unbounded;
@@ -150,15 +194,17 @@ mod test_super {
use super::*;
#[test]
- fn discord_test() {
- let client_id = std::env!("DISCORD_CLIENT_ID").parse::().unwrap();
+ fn lb_test() {
let (song_rx, song_tx) = unbounded();
- let (_, state_tx) = unbounded();
+ let (eos_rx, eos_tx) = unbounded();
- let (_, lib ) = read_config_lib();
+ let (config, lib ) = read_config_lib();
song_rx.send(lib.library[0].clone()).unwrap();
-
- Controller::discord_rpc(client_id, song_tx, state_tx);
- sleep(Duration::from_secs(150));
+ spawn(|| {
+ Controller::listenbrainz_scrobble(config.connections.listenbrainz_token.unwrap().as_str(), song_tx, eos_tx);
+ });
+ sleep(Duration::from_secs(10));
+ eos_rx.send(()).unwrap();
+ sleep(Duration::from_secs(10));
}
}
\ No newline at end of file
diff --git a/dmp-core/src/music_controller/controller.rs b/dmp-core/src/music_controller/controller.rs
index c45c6e7..3079c05 100644
--- a/dmp-core/src/music_controller/controller.rs
+++ b/dmp-core/src/music_controller/controller.rs
@@ -278,6 +278,7 @@ impl Controller {
let a = scope.spawn({
let queue_mail = queue_mail.clone();
let _notifications_rx = notifications_rx.clone();
+ let _config = config.clone();
move || {
futures::executor::block_on(async {
moro::async_scope!(|scope| {
@@ -303,7 +304,7 @@ impl Controller {
Controller::library_loop(
lib_mail.1,
&mut library,
- config,
+ _config,
)
.await
.unwrap();
@@ -336,9 +337,11 @@ impl Controller {
if let Some(inner) = connections {
dbg!(&inner);
let d = scope.spawn(|| {
- Controller::handle_connections( ControllerConnections {
- notifications_tx,
- inner,
+ Controller::handle_connections(
+ config,
+ ControllerConnections {
+ notifications_tx,
+ inner,
});
});
}
diff --git a/src/App.css b/src/App.css
index 864c96c..adf0eb5 100644
--- a/src/App.css
+++ b/src/App.css
@@ -9,6 +9,7 @@
--baseColor: #101010;
--overlayColor: #1e1e1e;
--highlightColor: #1f1f1f;
+ --highlightColor2: #2b2a2c;
--playBarColor: #5c4bb9;
--lightTextColor: #7a7a6f;
--mediumTextColor: #cacab8;
@@ -22,6 +23,8 @@ main {
width: 100vw;
display: flex;
flex-direction: column;
+ overflow-y: hidden;
+ overflow-x: hidden;
}
.container {
@@ -221,6 +224,10 @@ main {
display: flex;
}
+.queueSong:hover {
+ background-color: var(--highlightColor2);
+}
+
.queueSongCoverArt {
aspect-ratio: 1;
object-fit: contain;
diff --git a/src/App.tsx b/src/App.tsx
index 7ca9321..555c283 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -273,14 +273,6 @@ function Song(props: SongProps) {
{ Math.round(+props.duration / 60) }:
{ (+props.duration % 60).toString().padStart(2, "0") }
-
- {/*
- */}
)
}
@@ -302,8 +294,8 @@ function PlayBar({ playing, setPlaying }: PlayBarProps) {
const pos_ = Array.isArray(info.position) ? info.position![0] : 0;
const dur_ = Array.isArray(info.duration) ? info.duration![0] : 0;
- setPosition(pos_);
- setDuration(dur_);
+ setPosition(dur_);
+ setDuration(pos_);
let progress = ((dur_/pos_) * 100);
setSeekBarSize(progress)
})
@@ -401,7 +393,7 @@ function QueueSong({ song, location, index }: QueueSongProps) {
}
return (
-