1- use std:: {
2- collections:: HashMap ,
3- sync:: { LazyLock , RwLock } ,
4- time:: Duration ,
5- } ;
1+ use std:: { borrow:: Cow , collections:: HashMap , sync:: LazyLock , time:: Duration } ;
62
7- use chrono:: { DateTime , SubsecRound , Utc } ;
8- use futures:: { FutureExt , TryFutureExt , future :: join_all } ;
3+ use chrono:: { DateTime , Local , SubsecRound , Utc } ;
4+ use futures:: TryFutureExt ;
95use lastfm:: {
106 artist:: Artist ,
117 imageset:: ImageSet ,
128 track:: { NowPlayingTrack , Track } ,
139} ;
1410use reqwest:: Response ;
1511use serde:: { Deserialize , Serialize } ;
12+ use tokio:: sync:: Mutex ;
13+ use tracing:: debug;
1614use ts_rs:: TS ;
1715
18- use crate :: config:: Config ;
16+ use crate :: config:: { Config , has_scope } ;
1917
2018#[ allow( unused) ]
2119#[ derive( Clone , Serialize , TS ) ]
@@ -64,14 +62,23 @@ impl PartialEq<NowPlayingTrack> for TypescriptTrack {
6462#[ ts( rename = "LastFmUserInfo" ) ]
6563pub struct UserInfo {
6664 username : String ,
65+ last_song_time : Option < DateTime < Local > > ,
6766 currently_playing : Option < TypescriptTrack > ,
6867}
6968
70- static PLAYING_TRACKS : LazyLock < RwLock < HashMap < String , UserInfo > > > =
71- LazyLock :: new ( Default :: default) ;
72-
73- pub fn fetch_lastfm_info ( username : & str ) -> Option < UserInfo > {
74- PLAYING_TRACKS . read ( ) . unwrap ( ) . get ( username) . cloned ( )
69+ static PLAYING_TRACKS : LazyLock < Mutex < HashMap < String , UserInfo > > > = LazyLock :: new ( Default :: default) ;
70+
71+ pub fn fetch_lastfm_info ( username : & str , auth_scopes : & Cow < ' static , [ String ] > ) -> Option < UserInfo > {
72+ PLAYING_TRACKS
73+ . blocking_lock ( )
74+ . get ( username)
75+ . cloned ( )
76+ . map ( |mut user| {
77+ user
78+ . last_song_time
79+ . take_if ( |_| !has_scope ( & auth_scopes, "lastfm.lasttime" ) ) ;
80+ user
81+ } )
7582}
7683
7784struct User {
@@ -83,7 +90,7 @@ pub async fn run(config: &'static Config) {
8390 return ;
8491 } ;
8592
86- * PLAYING_TRACKS . write ( ) . unwrap ( ) = config
93+ * PLAYING_TRACKS . lock ( ) . await = config
8794 . users
8895 . values ( )
8996 . filter_map ( |config| {
@@ -92,6 +99,7 @@ pub async fn run(config: &'static Config) {
9299 last_fm_username. to_owned ( ) ,
93100 UserInfo {
94101 username : last_fm_username,
102+ last_song_time : None ,
95103 currently_playing : None ,
96104 // currently_playing_recorded: None,
97105 } ,
@@ -112,20 +120,16 @@ pub async fn run(config: &'static Config) {
112120 . collect :: < Vec < _ > > ( ) ;
113121
114122 let perform_update = async move || {
115- join_all (
116- users
117- . iter ( )
118- . map ( |user| update_currently_listening ( & user. username , & last_fm_key) ) ,
119- )
120- . map ( drop)
121- . await ;
123+ for user in & users {
124+ update_currently_listening ( & user. username , & last_fm_key) . await ;
125+ }
122126 } ;
123127
124128 perform_update ( ) . await ;
125129
126130 tokio:: spawn ( async move {
127131 loop {
128- tokio:: time:: sleep ( Duration :: from_secs ( 10 ) ) . await ;
132+ tokio:: time:: sleep ( Duration :: from_secs ( 15 ) ) . await ;
129133 perform_update ( ) . await
130134 }
131135 } ) ;
@@ -152,11 +156,12 @@ pub async fn update_currently_listening(username: &str, api_key: &str) {
152156 Ok ( response) => {
153157 let currently_playing = response. recent_tracks . track . into_iter ( ) . find_map ( |track| {
154158 let Track :: NowPlaying ( now_playing) = track else {
159+ debug ! ( "{username} is not listening to music" ) ;
155160 return None ;
156161 } ;
157162 Some ( now_playing)
158163 } ) ;
159- let mut users = PLAYING_TRACKS . write ( ) . unwrap ( ) ;
164+ let mut users = PLAYING_TRACKS . lock ( ) . await ;
160165 if let Some ( user) = users. get_mut ( username) {
161166 user. currently_playing = currently_playing. map ( |track| {
162167 let start_time = user
@@ -175,6 +180,7 @@ pub async fn update_currently_listening(username: &str, api_key: &str) {
175180 image : track. image ,
176181 }
177182 } ) ;
183+ user. last_song_time = Some ( Local :: now ( ) ) ;
178184 }
179185 }
180186 Err ( error) => {
0 commit comments