mirror of
https://github.com/Mastermindzh/tidal-hifi.git
synced 2025-04-20 19:52:32 +02:00
152 lines
4.7 KiB
TypeScript
152 lines
4.7 KiB
TypeScript
import { Client, SetActivity } from "@xhayper/discord-rpc";
|
|
import { app, ipcMain } from "electron";
|
|
import { globalEvents } from "../constants/globalEvents";
|
|
import { settings } from "../constants/settings";
|
|
import { Logger } from "../features/logger";
|
|
import { convertDurationToSeconds } from "../features/time/parse";
|
|
import { MediaStatus } from "../models/mediaStatus";
|
|
import { mediaInfo } from "./mediaInfo";
|
|
import { settingsStore } from "./settings";
|
|
|
|
const clientId = "833617820704440341";
|
|
|
|
export let rpc: Client;
|
|
|
|
const ACTIVITY_LISTENING = 2;
|
|
const MAX_RETRIES = 5;
|
|
const RETRY_DELAY = 10000;
|
|
|
|
const observer = () => {
|
|
if (rpc) {
|
|
updateActivity();
|
|
}
|
|
};
|
|
|
|
const defaultPresence = {
|
|
largeImageKey: "tidal-hifi-icon",
|
|
largeImageText: `TIDAL Hi-Fi ${app.getVersion()}`,
|
|
instance: false,
|
|
type: ACTIVITY_LISTENING
|
|
};
|
|
|
|
const updateActivity = () => {
|
|
const showIdle = settingsStore.get<string, boolean>(settings.discord.showIdle) ?? true;
|
|
if (mediaInfo.status === MediaStatus.paused && !showIdle) {
|
|
rpc.user?.clearActivity();
|
|
} else {
|
|
rpc.user?.setActivity(getActivity());
|
|
}
|
|
};
|
|
|
|
const getActivity = (): SetActivity => {
|
|
const presence: SetActivity = { ...defaultPresence };
|
|
|
|
if (mediaInfo.status === MediaStatus.paused) {
|
|
presence.details =
|
|
settingsStore.get<string, string>(settings.discord.idleText) ?? "Browsing Tidal";
|
|
} else {
|
|
const showSong = settingsStore.get<string, boolean>(settings.discord.showSong) ?? false;
|
|
if (showSong) {
|
|
const { includeTimestamps, detailsPrefix, buttonText } = getFromStore();
|
|
includeTimeStamps(includeTimestamps);
|
|
setPresenceFromMediaInfo(detailsPrefix, buttonText);
|
|
} else {
|
|
presence.details =
|
|
settingsStore.get<string, string>(settings.discord.usingText) ?? "Playing media on TIDAL";
|
|
}
|
|
}
|
|
|
|
return presence;
|
|
|
|
function getFromStore() {
|
|
const includeTimestamps =
|
|
settingsStore.get<string, boolean>(settings.discord.includeTimestamps) ?? true;
|
|
const detailsPrefix =
|
|
settingsStore.get<string, string>(settings.discord.detailsPrefix) ?? "Listening to ";
|
|
const buttonText =
|
|
settingsStore.get<string, string>(settings.discord.buttonText) ?? "Play on TIDAL";
|
|
|
|
return { includeTimestamps, detailsPrefix, buttonText };
|
|
}
|
|
|
|
/**
|
|
* Pad a string using spaces to at least 2 characters
|
|
* @param input string to pad with 2 characters
|
|
* @returns
|
|
*/
|
|
function pad(input: string): string {
|
|
return input.padEnd(2, " ");
|
|
}
|
|
|
|
function setPresenceFromMediaInfo(detailsPrefix: string, buttonText: string) {
|
|
// discord requires a minimum of 2 characters
|
|
const title = pad(mediaInfo.title);
|
|
const album = pad(mediaInfo.album);
|
|
const artists = pad(mediaInfo.artists);
|
|
|
|
if (mediaInfo.url) {
|
|
presence.details = `${detailsPrefix}${title}`;
|
|
presence.state = artists ? artists : "unknown artist(s)";
|
|
presence.largeImageKey = mediaInfo.image;
|
|
if (album) {
|
|
presence.largeImageText = album;
|
|
}
|
|
|
|
presence.buttons = [{ label: buttonText, url: mediaInfo.url }];
|
|
} else {
|
|
presence.details = `Watching ${title}`;
|
|
presence.state = artists;
|
|
}
|
|
}
|
|
|
|
function includeTimeStamps(includeTimestamps: boolean) {
|
|
if (includeTimestamps) {
|
|
const currentSeconds = convertDurationToSeconds(mediaInfo.current);
|
|
const durationSeconds = convertDurationToSeconds(mediaInfo.duration);
|
|
const now = Math.trunc((Date.now() + 500) / 1000);
|
|
presence.startTimestamp = now - currentSeconds;
|
|
presence.endTimestamp = presence.startTimestamp + durationSeconds;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Try to login to RPC and retry if it errors
|
|
* @param retryCount Max retry count
|
|
*/
|
|
const connectWithRetry = async (retryCount = 0) => {
|
|
try {
|
|
await rpc.login();
|
|
Logger.log('Connected to Discord');
|
|
rpc.on("ready", updateActivity);
|
|
Object.values(globalEvents).forEach(event => ipcMain.on(event, observer));
|
|
} catch (error) {
|
|
if (retryCount < MAX_RETRIES) {
|
|
Logger.log(`Failed to connect to Discord, retrying in ${RETRY_DELAY/1000} seconds... (Attempt ${retryCount + 1}/${MAX_RETRIES})`);
|
|
setTimeout(() => connectWithRetry(retryCount + 1), RETRY_DELAY);
|
|
} else {
|
|
Logger.log('Failed to connect to Discord after maximum retry attempts');
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set up the discord rpc and listen on globalEvents.updateInfo
|
|
*/
|
|
export const initRPC = () => {
|
|
rpc = new Client({ transport: {type: "ipc"}, clientId });
|
|
connectWithRetry();
|
|
};
|
|
|
|
/**
|
|
* Remove any RPC connection with discord and remove the event listener on globalEvents.updateInfo
|
|
*/
|
|
export const unRPC = () => {
|
|
if (rpc) {
|
|
rpc.user?.clearActivity();
|
|
rpc.destroy();
|
|
rpc = null;
|
|
ipcMain.removeListener(globalEvents.updateInfo, observer);
|
|
}
|
|
};
|