5
5
6
6
7
7
/* C++ libs */
8
+ #include < atomic>
8
9
#include < cctype>
9
10
#include < chrono>
10
11
#include < exception>
18
19
/* C libs */
19
20
#include < cstdio>
20
21
/* Qt */
21
- #include < QApplication>
22
- #include < QPushButton>
23
22
#include < QAction>
24
- #include < QMenu>
23
+ #include < QApplication>
24
+ #include < QCoreApplication>
25
25
#include < QIcon>
26
+ #include < QMenu>
27
+ #include < QObject>
28
+ #include < QPushButton>
26
29
#include < QSystemTrayIcon>
27
- #include < QCoreApplication>
30
+ #include < QtNetwork>
31
+
28
32
/* local libs*/
29
33
#include " discord_rpc.h"
30
34
#include " discord_register.h"
34
38
#ifdef WIN32
35
39
36
40
#include " windows_api_hook.hh"
41
+
37
42
#pragma comment(lib, "discord-rpc.lib")
38
43
39
44
#elif defined(__APPLE__) or defined(__MACH__)
40
- # include < Carbon/Carbon.h >
45
+
41
46
#include " osx_api_hook.hh"
42
47
43
- #elif defined(__linux__)
44
48
#else
45
- # error "unkown target os "
49
+ #error "Not supported target "
46
50
#endif
47
51
48
52
#define CURRENT_TIME std::time (nullptr )
49
53
#define HIFI_ASSET " hifi"
50
54
51
- static std::string lastfmUsername;
52
55
static const char *APPLICATION_ID = " 584458858731405315" ;
53
- volatile bool isPresenceActive = true ;
56
+ std::atomic< bool > isPresenceActive;
54
57
static char *countryCode = nullptr ;
55
58
56
59
struct Song {
57
60
enum AudioQualityEnum { master, hifi, normal };
58
-
59
61
std::string title;
60
62
std::string artist;
61
63
std::string album;
62
64
std::string url;
65
+ char id[10 ];
63
66
int64_t starttime;
64
67
int64_t runtime;
65
68
uint64_t pausedtime;
@@ -68,6 +71,7 @@ struct Song {
68
71
bool isPaused = false ;
69
72
AudioQualityEnum quality;
70
73
74
+
71
75
void setQuality (const std::string &q) {
72
76
if (q == " HI_RES" ) {
73
77
quality = master;
@@ -76,17 +80,21 @@ struct Song {
76
80
}
77
81
}
78
82
83
+
79
84
inline bool isHighRes () const noexcept {
80
85
return quality == master;
81
86
}
82
87
88
+
83
89
friend std::ostream &operator <<(std::ostream &out, const Song &song) {
84
90
out << song.title << " of " << song.album << " from " << song.artist << " (" << song.runtime << " )" ;
85
91
return out;
86
92
}
87
93
};
88
94
89
95
#include < locale>
96
+
97
+
90
98
std::string urlEncode (const std::string &value) {
91
99
std::ostringstream escaped;
92
100
escaped.fill (' 0' );
@@ -104,6 +112,7 @@ std::string urlEncode(const std::string &value) {
104
112
return escaped.str ();
105
113
}
106
114
115
+
107
116
static void updateDiscordPresence (const Song &song) {
108
117
if (isPresenceActive) {
109
118
DiscordRichPresence discordPresence;
@@ -119,52 +128,58 @@ static void updateDiscordPresence(const Song &song) {
119
128
}
120
129
discordPresence.largeImageKey = song.isHighRes () ? " test" : HIFI_ASSET;
121
130
discordPresence.largeImageText = song.isHighRes () ? " Playing High-Res Audio" : " " ;
131
+ if (song.id [0 ] != ' \0 ' )
132
+ discordPresence.spectateSecret = song.id ;
122
133
discordPresence.instance = 0 ;
123
134
135
+
124
136
Discord_UpdatePresence (&discordPresence);
125
137
} else {
126
138
Discord_ClearPresence ();
127
139
}
128
140
}
129
141
142
+
130
143
static void handleDiscordReady (const DiscordUser *connectedUser) {
131
- printf (" \n Discord: connected to user %s#%s - %s\n " ,
132
- connectedUser->username ,
133
- connectedUser->discriminator ,
134
- connectedUser->userId );
144
+ std::clog << " Connected to discord " << connectedUser->userId << " \n " ;
135
145
}
136
146
147
+
137
148
static void handleDiscordDisconnected (int errcode, const char *message) {
138
- printf ( " \n Discord : disconnected (%d: %s) \n " , errcode, message) ;
149
+ std::clog << " Discord : disconnected (" << errcode << " : " << message << " ) \n " ;
139
150
}
140
151
152
+
141
153
static void handleDiscordError (int errcode, const char *message) {
142
- printf ( " \n Discord: error (%d: %s) \n " , errcode, message) ;
154
+ std::cerr << " Discord: Error ( " << errcode << " : " << message << " ) \n " ;
143
155
}
144
156
157
+ // static void handleDiscordSpectate(const char* secret) {
158
+ // printf("\nDiscord: spectate (%s)\n", secret);
159
+ // }
160
+
145
161
static void discordInit () {
146
162
DiscordEventHandlers handlers;
147
163
memset (&handlers, 0 , sizeof (handlers));
148
164
handlers.ready = handleDiscordReady;
149
165
handlers.disconnected = handleDiscordDisconnected;
150
166
handlers.errored = handleDiscordError;
151
- Discord_Initialize (APPLICATION_ID, &handlers, 1 , NULL );
167
+
168
+ Discord_Initialize (APPLICATION_ID, &handlers, 1 , nullptr );
152
169
}
153
170
171
+
154
172
inline void rpcLoop () {
155
173
156
174
using json = nlohmann::json;
157
175
using string = std::string;
158
176
httplib::Client cli (" api.tidal.com" );
159
-
160
177
char getSongInfoBuf[1024 ];
161
178
json j;
162
-
163
- Song curSong;
179
+ static Song curSong;
164
180
165
181
for (;;) {
166
182
std::wstring tmpTrack, tmpArtist;
167
-
168
183
auto localStatus = tidalInfo (tmpTrack, tmpArtist);
169
184
170
185
// If song is playing
@@ -178,6 +193,7 @@ inline void rpcLoop() {
178
193
curSong.runtime = 0 ;
179
194
curSong.pausedtime = 0 ;
180
195
curSong.setQuality (" " );
196
+ curSong.id [0 ] = ' \0 ' ;
181
197
182
198
// get info form TIDAL api
183
199
auto search_param =
@@ -201,11 +217,16 @@ inline void rpcLoop() {
201
217
auto c_str = rawWstringToString (tmpTrack);
202
218
203
219
if (fetched_str == c_str) {
204
- curSong.setQuality (j[" tracks" ][" items" ][i][" audioQuality" ].get <std::string>());
205
- curSong.trackNumber = j[" tracks" ][" items" ][i][" trackNumber" ].get <uint_fast8_t >();
206
- curSong.volumeNumber = j[" tracks" ][" items" ][i][" volumeNumber" ].get <uint_fast8_t >();
207
- curSong.runtime = j[" tracks" ][" items" ][i][" duration" ].get <int64_t >();
208
- break ;
220
+ if (curSong.runtime == 0
221
+ or j[" tracks" ][" items" ][i][" audioQuality" ].get <std::string>().compare (" HI_RES" )
222
+ == 0 ) { // Ignore songs with same name if you have found song
223
+ curSong.setQuality (j[" tracks" ][" items" ][i][" audioQuality" ].get <std::string>());
224
+ curSong.trackNumber = j[" tracks" ][" items" ][i][" trackNumber" ].get <uint_fast8_t >();
225
+ curSong.volumeNumber = j[" tracks" ][" items" ][i][" volumeNumber" ].get <uint_fast8_t >();
226
+ curSong.runtime = j[" tracks" ][" items" ][i][" duration" ].get <int64_t >();
227
+ sprintf (curSong.id , " %u" , j[" tracks" ][" items" ][i][" id" ].get <unsigned >());
228
+ if (curSong.isHighRes ()) break ; // keep searching for high-res version.
229
+ }
209
230
}
210
231
}
211
232
} catch (...) {
@@ -218,7 +239,7 @@ inline void rpcLoop() {
218
239
#endif
219
240
220
241
// get time just before passing it to RPC handlers
221
- curSong.starttime = 2 + CURRENT_TIME ; // add 2 seconds to be more accurate.
242
+ curSong.starttime = CURRENT_TIME + 2 ; // add 2 seconds to be more accurate, not a chance
222
243
updateDiscordPresence (curSong);
223
244
} else {
224
245
if (curSong.isPaused ) {
@@ -228,42 +249,37 @@ inline void rpcLoop() {
228
249
}
229
250
230
251
} else if (localStatus == opened) {
231
- if ((CURRENT_TIME - (curSong.starttime + curSong.runtime + curSong.pausedtime ) > 5 )) {
232
- curSong.pausedtime += 1 ;
233
- curSong.isPaused = true ;
234
- }
252
+ curSong.pausedtime += 1 ;
253
+ curSong.isPaused = true ;
235
254
updateDiscordPresence (curSong);
255
+ } else {
256
+ Discord_ClearPresence ();
236
257
}
237
-
238
258
_continue:
259
+
260
+ #ifdef DISCORD_DISABLE_IO_THREAD
261
+ Discord_UpdateConnection ();
262
+ #endif
263
+ Discord_RunCallbacks ();
264
+
239
265
std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
240
266
241
267
}
242
268
}
243
269
270
+
244
271
int main (int argc, char **argv) {
245
- using json = nlohmann::json;
246
- using string = std::string;
247
272
248
273
// get country code for TIDAL api queries
249
274
countryCode = getLocale ();
275
+ isPresenceActive = true ;
250
276
251
- // try {
252
- // std::ifstream i("settings.json");
253
- // json j;
254
- // i >> j;
255
- // lastfmUsername = j["last_fm_username"].get<string>();
256
- // } catch (...) {
257
- // std::cerr << "Couldn't read last.fm username\n";
258
- // }
259
-
277
+ // Qt main app setup
260
278
QApplication app (argc, argv);
261
- app.setWindowIcon (QIcon (" icon.ico" ));
262
-
263
- QSystemTrayIcon tray (QIcon (" icon.ico" ), &app);
264
-
265
- QAction titleAction (QIcon (" icon.ico" ), " TIDAL - Discord RPC " , nullptr );
279
+ app.setWindowIcon (QIcon (" :assets/icon.ico" ));
266
280
281
+ QSystemTrayIcon tray (QIcon (" :assets/icon.ico" ), &app);
282
+ QAction titleAction (QIcon (" :assets/icon.ico" ), " TIDAL - Discord RPC " , nullptr );
267
283
QAction changePresenceStatusAction (" Running" , nullptr );
268
284
changePresenceStatusAction.setCheckable (true );
269
285
changePresenceStatusAction.setChecked (true );
@@ -292,11 +308,12 @@ int main(int argc, char **argv) {
292
308
293
309
tray.show ();
294
310
311
+ discordInit ();
312
+ // RPC loop call
295
313
std::thread t1 (rpcLoop);
296
314
t1.detach ();
297
315
298
- discordInit ();
299
316
300
317
return app.exec ();
301
318
302
- }
319
+ }
0 commit comments