Skip to content

Commit b5fd6b4

Browse files
committed
Discord add reconnecting functionality
Clear rpc on disconnect Add menu button to reconnect
1 parent 36bc9c6 commit b5fd6b4

File tree

3 files changed

+167
-77
lines changed

3 files changed

+167
-77
lines changed

plugins/discord/back.js

+125-56
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,140 @@
11
const Discord = require("discord-rpc");
2+
const { dev } = require("electron-is")
23

34
const registerCallback = require("../../providers/song-info");
45

5-
const rpc = new Discord.Client({
6-
transport: "ipc",
7-
});
8-
96
// Application ID registered by @semvis123
107
const clientId = "790655993809338398";
118

9+
/**
10+
* @typedef {Object} Info
11+
* @property {import('discord-rpc').Client} rpc
12+
* @property {boolean} ready
13+
* @property {import('../../providers/song-info').SongInfo} lastSongInfo
14+
*/
15+
/**
16+
* @type {Info}
17+
*/
18+
const info = {
19+
rpc: null,
20+
ready: false,
21+
lastSongInfo: null,
22+
};
23+
/**
24+
* @type {(() => void)[]}
25+
*/
26+
const refreshCallbacks = [];
27+
const resetInfo = () => {
28+
info.rpc = null;
29+
info.ready = false;
30+
clearTimeout(clearActivity);
31+
if (dev()) console.log("discord disconnected");
32+
refreshCallbacks.forEach(cb => cb());
33+
};
34+
35+
const connect = () => {
36+
if (info.rpc) {
37+
if (dev())
38+
console.log('Attempted to connect with active RPC object');
39+
return;
40+
}
41+
42+
info.rpc = new Discord.Client({
43+
transport: "ipc",
44+
});
45+
info.ready = false;
46+
47+
info.rpc.once("connected", () => {
48+
if (dev()) console.log("discord connected");
49+
refreshCallbacks.forEach(cb => cb());
50+
});
51+
info.rpc.once("ready", () => {
52+
info.ready = true;
53+
if (info.lastSongInfo) updateActivity(info.lastSongInfo)
54+
});
55+
info.rpc.once("disconnected", resetInfo);
56+
57+
// Startup the rpc client
58+
info.rpc.login({ clientId }).catch(err => {
59+
resetInfo();
60+
if (dev()) console.error(err);
61+
});
62+
};
63+
1264
let clearActivity;
65+
/**
66+
* @type {import('../../providers/song-info').songInfoCallback}
67+
*/
68+
let updateActivity;
1369

1470
module.exports = (win, {activityTimoutEnabled, activityTimoutTime}) => {
71+
// We get multiple events
72+
// Next song: PAUSE(n), PAUSE(n+1), PLAY(n+1)
73+
// Skip time: PAUSE(N), PLAY(N)
74+
updateActivity = songInfo => {
75+
if (songInfo.title.length === 0 && songInfo.artist.length === 0) {
76+
return;
77+
}
78+
info.lastSongInfo = songInfo;
79+
80+
// stop the clear activity timout
81+
clearTimeout(clearActivity);
82+
83+
// stop early if discord connection is not ready
84+
// do this after clearTimeout to avoid unexpected clears
85+
if (!info.rpc || !info.ready) {
86+
return;
87+
}
88+
89+
// clear directly if timeout is 0
90+
if (songInfo.isPaused && activityTimoutEnabled && activityTimoutTime === 0) {
91+
info.rpc.clearActivity().catch(console.error);
92+
return;
93+
}
94+
95+
// Song information changed, so lets update the rich presence
96+
const activityInfo = {
97+
details: songInfo.title,
98+
state: songInfo.artist,
99+
largeImageKey: "logo",
100+
largeImageText: [
101+
songInfo.uploadDate,
102+
songInfo.views.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " views"
103+
].join(' || '),
104+
};
105+
106+
if (songInfo.isPaused) {
107+
// Add an idle icon to show that the song is paused
108+
activityInfo.smallImageKey = "idle";
109+
activityInfo.smallImageText = "idle/paused";
110+
// Set start the timer so the activity gets cleared after a while if enabled
111+
if (activityTimoutEnabled)
112+
clearActivity = setTimeout(() => info.rpc.clearActivity().catch(console.error), activityTimoutTime || 10000);
113+
} else {
114+
// Add the start and end time of the song
115+
const songStartTime = Date.now() - songInfo.elapsedSeconds * 1000;
116+
activityInfo.startTimestamp = songStartTime;
117+
activityInfo.endTimestamp =
118+
songStartTime + songInfo.songDuration * 1000;
119+
}
120+
121+
info.rpc.setActivity(activityInfo).catch(console.error);
122+
};
123+
15124
// If the page is ready, register the callback
16125
win.once("ready-to-show", () => {
17-
rpc.once("ready", () => {
18-
// Register the callback
19-
//
20-
// We get multiple events
21-
// Next song: PAUSE(n), PAUSE(n+1), PLAY(n+1)
22-
// Skip time: PAUSE(N), PLAY(N)
23-
registerCallback((songInfo) => {
24-
if (songInfo.title.length === 0 && songInfo.artist.length === 0) {
25-
return;
26-
}
27-
// Song information changed, so lets update the rich presence
28-
const activityInfo = {
29-
details: songInfo.title,
30-
state: songInfo.artist,
31-
largeImageKey: "logo",
32-
largeImageText: [
33-
songInfo.uploadDate,
34-
songInfo.views.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " views"
35-
].join(' || '),
36-
};
37-
38-
// stop the clear activity timout
39-
clearTimeout(clearActivity);
40-
41-
// clear directly if timeout is 0
42-
if (songInfo.isPaused && activityTimoutEnabled && activityTimoutTime === 0) {
43-
rpc.clearActivity().catch(console.error);
44-
return;
45-
}
46-
47-
if (songInfo.isPaused) {
48-
// Add an idle icon to show that the song is paused
49-
activityInfo.smallImageKey = "idle";
50-
activityInfo.smallImageText = "idle/paused";
51-
// Set start the timer so the activity gets cleared after a while if enabled
52-
if (activityTimoutEnabled)
53-
clearActivity = setTimeout(() => rpc.clearActivity().catch(console.error), activityTimoutTime || 10000);
54-
} else {
55-
// Add the start and end time of the song
56-
const songStartTime = Date.now() - songInfo.elapsedSeconds * 1000;
57-
activityInfo.startTimestamp = songStartTime;
58-
activityInfo.endTimestamp =
59-
songStartTime + songInfo.songDuration * 1000;
60-
}
61-
62-
rpc.setActivity(activityInfo).catch(console.error);
63-
});
64-
});
65-
66-
// Startup the rpc client
67-
rpc.login({ clientId }).catch(console.error);
126+
registerCallback(updateActivity);
127+
connect();
68128
});
69129
};
70130

71-
module.exports.clear = () => rpc.clearActivity();
131+
module.exports.clear = () => {
132+
if (info.rpc) info.rpc.clearActivity();
133+
clearTimeout(clearActivity);
134+
};
135+
module.exports.connect = connect;
136+
module.exports.registerRefresh = (cb) => refreshCallbacks.push(cb);
137+
/**
138+
* @type {Info}
139+
*/
140+
module.exports.info = Object.defineProperties({}, Object.keys(info).reduce((o, k) => ({ ...o, [k]: { enumerable: true, get: () => info[k] } }), {}));

plugins/discord/menu.js

+31-21
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,38 @@
11
const { setOptions } = require("../../config/plugins");
22
const { edit } = require("../../config");
3-
const { clear } = require("./back");
3+
const { clear, info, connect, registerRefresh } = require("./back");
44

5-
module.exports = (win, options) => [
6-
{
7-
label: "Clear activity",
8-
click: () => {
9-
clear();
5+
let hasRegisterred = false;
6+
7+
module.exports = (win, options, refreshMenu) => {
8+
if (!hasRegisterred) {
9+
registerRefresh(refreshMenu);
10+
hasRegisterred = true;
11+
}
12+
13+
return [
14+
{
15+
label: info.rpc !== null ? "Connected" : "Reconnect",
16+
enabled: info.rpc === null,
17+
click: connect,
18+
},
19+
{
20+
label: "Clear activity",
21+
click: clear,
1022
},
11-
},
12-
{
13-
label: "Clear activity after timeout",
14-
type: "checkbox",
15-
checked: options.activityTimoutEnabled,
16-
click: (item) => {
17-
options.activityTimoutEnabled = item.checked;
18-
setOptions('discord', options);
23+
{
24+
label: "Clear activity after timeout",
25+
type: "checkbox",
26+
checked: options.activityTimoutEnabled,
27+
click: (item) => {
28+
options.activityTimoutEnabled = item.checked;
29+
setOptions('discord', options);
30+
},
1931
},
20-
},
21-
{
22-
label: "Set timeout time in config",
23-
click: () => {
32+
{
33+
label: "Set timeout time in config",
2434
// open config.json
25-
edit();
35+
click: edit,
2636
},
27-
},
28-
];
37+
];
38+
};

providers/song-info.js

+11
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ const getArtist = async (win) => {
4040
}
4141

4242
// Fill songInfo with empty values
43+
/**
44+
* @typedef {songInfo} SongInfo
45+
*/
4346
const songInfo = {
4447
title: "",
4548
artist: "",
@@ -71,6 +74,14 @@ const handleData = async (responseText, win) => {
7174
const callbacks = [];
7275

7376
// This function will allow plugins to register callback that will be triggered when data changes
77+
/**
78+
* @callback songInfoCallback
79+
* @param {songInfo} songInfo
80+
* @returns {void}
81+
*/
82+
/**
83+
* @param {songInfoCallback} callback
84+
*/
7485
const registerCallback = (callback) => {
7586
callbacks.push(callback);
7687
};

0 commit comments

Comments
 (0)