richpresence: remove listenbrainz

This commit is contained in:
thororen1234 2026-07-15 21:35:42 -04:00
parent c0d0791961
commit caa851ece9
No known key found for this signature in database
6 changed files with 2 additions and 327 deletions

View file

@ -141,29 +141,6 @@ function JellyfinSettings() {
); );
} }
function ListenBrainzSettings() {
return (
<>
<SettingsSection id="listenbrainz-settings" name="" description="Show what you're currently listening to via ListenBrainz. The MusicBrainz API requires a meaningful user agent string (an email usually works)." />
<TextSetting name="Username" description="ListenBrainz username." settingsKey="lb_username" placeholder="ListenBrainz username" />
<TextSetting name="MusicBrainz Contact" description="MusicBrainz contact for user agent." settingsKey="lb_mbContact" placeholder="your@email.com" />
<TextSetting name="Custom Status Text" description="Custom status text." settingsKey="lb_statusName" placeholder="some music" />
<SelectSetting name="Name Format" description="Name format." settingsKey="lb_nameFormat" options={nameFormatOptions} />
<SelectSetting name="Missing Art Fallback" description="Fallback when art is missing." settingsKey="lb_missingArt" options={[
{ label: "Use large ListenBrainz logo", value: "listenbrainzLogo" },
{ label: "Use generic placeholder", value: "placeholder" },
]} />
<SwitchSetting name="Show Listening Status" description="Show listening status." settingsKey="lb_useListeningStatus" />
<SwitchSetting name="Show Time Bar" description="Use track duration to display a time bar." settingsKey="lb_useTimeBar" />
<SwitchSetting name="Show ListenBrainz Logo" description="Show ListenBrainz logo on album art." settingsKey="lb_useLogo" />
<SwitchSetting name="Show Profile Link" description="Show link to ListenBrainz profile." settingsKey="lb_shareUsername" />
<SwitchSetting name="Show Song Link" description="Show link to song on ListenBrainz." settingsKey="lb_shareSong" />
<SwitchSetting name="Hide With Spotify" description="Hide presence if Spotify is running." settingsKey="lb_hideWithSpotify" />
<SwitchSetting name="Hide With Any Activity" description="Hide presence if any other presence exists." settingsKey="lb_hideWithActivity" />
</>
);
}
function GensokyoRadioSettings() { function GensokyoRadioSettings() {
const [value, setValue] = useState(settings.store.gr_refreshInterval ?? 15); const [value, setValue] = useState(settings.store.gr_refreshInterval ?? 15);
return ( return (
@ -239,7 +216,6 @@ const TAB_COMPONENTS: Record<ServiceTab, React.ComponentType> = {
[ServiceTab.Tosu]: TosuSettings, [ServiceTab.Tosu]: TosuSettings,
[ServiceTab.StatsFm]: StatsFmSettings, [ServiceTab.StatsFm]: StatsFmSettings,
[ServiceTab.Jellyfin]: JellyfinSettings, [ServiceTab.Jellyfin]: JellyfinSettings,
[ServiceTab.ListenBrainz]: ListenBrainzSettings,
[ServiceTab.GensokyoRadio]: GensokyoRadioSettings, [ServiceTab.GensokyoRadio]: GensokyoRadioSettings,
[ServiceTab.Navidrome]: NavidromeSettings, [ServiceTab.Navidrome]: NavidromeSettings,
}; };
@ -249,7 +225,6 @@ const TAB_LABELS: Record<ServiceTab, string> = {
[ServiceTab.Tosu]: "osu!", [ServiceTab.Tosu]: "osu!",
[ServiceTab.StatsFm]: "stats.fm", [ServiceTab.StatsFm]: "stats.fm",
[ServiceTab.Jellyfin]: "Jellyfin", [ServiceTab.Jellyfin]: "Jellyfin",
[ServiceTab.ListenBrainz]: "ListenBrainz",
[ServiceTab.GensokyoRadio]: "Gensokyo Radio", [ServiceTab.GensokyoRadio]: "Gensokyo Radio",
[ServiceTab.Navidrome]: "Navidrome", [ServiceTab.Navidrome]: "Navidrome",
}; };
@ -259,7 +234,6 @@ const ENABLE_KEYS: Record<ServiceTab, SettingsKey> = {
[ServiceTab.Tosu]: "tosu_enabled", [ServiceTab.Tosu]: "tosu_enabled",
[ServiceTab.StatsFm]: "sfm_enabled", [ServiceTab.StatsFm]: "sfm_enabled",
[ServiceTab.Jellyfin]: "jf_enabled", [ServiceTab.Jellyfin]: "jf_enabled",
[ServiceTab.ListenBrainz]: "lb_enabled",
[ServiceTab.GensokyoRadio]: "gr_enabled", [ServiceTab.GensokyoRadio]: "gr_enabled",
[ServiceTab.Navidrome]: "nd_enabled", [ServiceTab.Navidrome]: "nd_enabled",
}; };

View file

@ -12,7 +12,6 @@ import { migrateOldSettings } from "./migration";
import * as abs from "./services/audiobookshelf"; import * as abs from "./services/audiobookshelf";
import * as gensokyoRadio from "./services/gensokyoRadio"; import * as gensokyoRadio from "./services/gensokyoRadio";
import * as jellyfin from "./services/jellyfin"; import * as jellyfin from "./services/jellyfin";
import * as listenbrainz from "./services/listenbrainz";
import * as navidrome from "./services/navidrome"; import * as navidrome from "./services/navidrome";
import * as statsfm from "./services/statsfm"; import * as statsfm from "./services/statsfm";
import * as tosu from "./services/tosu"; import * as tosu from "./services/tosu";
@ -28,7 +27,6 @@ const services: Record<string, { start(): void; stop(): void; forceUpdate?(): vo
[ServiceTab.Tosu]: tosu, [ServiceTab.Tosu]: tosu,
[ServiceTab.StatsFm]: statsfm, [ServiceTab.StatsFm]: statsfm,
[ServiceTab.Jellyfin]: jellyfin, [ServiceTab.Jellyfin]: jellyfin,
[ServiceTab.ListenBrainz]: listenbrainz,
[ServiceTab.GensokyoRadio]: gensokyoRadio, [ServiceTab.GensokyoRadio]: gensokyoRadio,
[ServiceTab.Navidrome]: navidrome, [ServiceTab.Navidrome]: navidrome,
}; };
@ -38,7 +36,6 @@ const enableKeys: Record<string, SettingsKey> = {
[ServiceTab.Tosu]: "tosu_enabled", [ServiceTab.Tosu]: "tosu_enabled",
[ServiceTab.StatsFm]: "sfm_enabled", [ServiceTab.StatsFm]: "sfm_enabled",
[ServiceTab.Jellyfin]: "jf_enabled", [ServiceTab.Jellyfin]: "jf_enabled",
[ServiceTab.ListenBrainz]: "lb_enabled",
[ServiceTab.GensokyoRadio]: "gr_enabled", [ServiceTab.GensokyoRadio]: "gr_enabled",
[ServiceTab.Navidrome]: "nd_enabled", [ServiceTab.Navidrome]: "nd_enabled",
}; };
@ -77,7 +74,7 @@ function stopAllServices() {
export default definePlugin({ export default definePlugin({
name: "RichPresence", name: "RichPresence",
description: "Unified rich presence hub for AudioBookShelf, osu!, stats.fm, Jellyfin, ListenBrainz, Navidrome, and Gensokyo Radio.", description: "Unified rich presence hub for AudioBookShelf, osu!, stats.fm, Jellyfin, Navidrome, and Gensokyo Radio.",
tags: ["Activity"], tags: ["Activity"],
authors: [ authors: [
EquicordDevs.vmohammad, EquicordDevs.vmohammad,

View file

@ -63,24 +63,6 @@ const migrations: MigrationMapping[] = [
privacyMode: "jf_privacyMode", privacyMode: "jf_privacyMode",
}, },
}, },
{
oldPlugin: "ListenBrainzRPC",
enableKey: "lb_enabled",
keys: {
username: "lb_username",
mbContact: "lb_mbContact",
shareUsername: "lb_shareUsername",
shareSong: "lb_shareSong",
hideWithSpotify: "lb_hideWithSpotify",
hideWithActivity: "lb_hideWithActivity",
useTimeBar: "lb_useTimeBar",
statusName: "lb_statusName",
nameFormat: "lb_nameFormat",
useListeningStatus: "lb_useListeningStatus",
missingArt: "lb_missingArt",
useLogo: "lb_useLogo",
},
},
{ {
oldPlugin: "GensokyoRadioRPC", oldPlugin: "GensokyoRadioRPC",
enableKey: "gr_enabled", enableKey: "gr_enabled",
@ -97,7 +79,7 @@ function setStoreValue(key: SettingsKey, value: boolean | string | number) {
export function migrateOldSettings() { export function migrateOldSettings() {
if (Settings.plugins.RichPresence._migrated) return; if (Settings.plugins.RichPresence._migrated) return;
migratePluginSettings("RichPresence", "AudioBookShelfRichPresence", "GensokyoRadioRPC", "JellyfinRichPresence", "ListenBrainzRPC", "StatsfmPresence", "TosuRPC"); migratePluginSettings("RichPresence", "AudioBookShelfRichPresence", "GensokyoRadioRPC", "JellyfinRichPresence", "StatsfmPresence", "TosuRPC");
for (const migration of migrations) { for (const migration of migrations) {
const oldSettings = Settings.plugins[migration.oldPlugin]; const oldSettings = Settings.plugins[migration.oldPlugin];

View file

@ -1,186 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2026 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Logger } from "@utils/Logger";
import { Activity, ActivityButton } from "@vencord/discord-types";
import { ActivityFlags, ActivityType } from "@vencord/discord-types/enums";
import { findByPropsLazy } from "@webpack";
import { ApplicationAssetUtils, FluxDispatcher } from "@webpack/common";
import { settings } from "../settings";
import { NameFormat } from "../types";
import { LbTrackData } from "../types/listenbrainz";
const APPLICATION_ID = "1090155131007406132";
const PLACEHOLDER_ID = "2a96cbd8b46e442fc41c2b86b821562f";
const SOCKET_ID = "RichPresence_LB";
const logger = new Logger("RichPresence:ListenBrainz");
const PresenceStore = findByPropsLazy("getLocalPresence");
let updateInterval: NodeJS.Timeout | undefined;
let currentRecordingMBID = "";
let currentStart = 0;
async function getAsset(key: string): Promise<string> {
return (await ApplicationAssetUtils.fetchAssetIds(APPLICATION_ID, [key]))[0];
}
function setActivity(activity: Activity | null) {
FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity, socketId: SOCKET_ID });
}
async function fetchTrackData(): Promise<LbTrackData | null> {
if (!settings.store.lb_username) {
logger.warn("ListenBrainz username is not set.");
return null;
}
try {
const lbRes = await fetch(`https://api.listenbrainz.org/1/user/${settings.store.lb_username}/playing-now`);
if (!lbRes.ok) throw `${lbRes.status} ${lbRes.statusText}`;
const lbJson = await lbRes.json();
if (lbJson.error) {
logger.error("Error from ListenBrainz API", `${lbJson.error}: ${lbJson.message}`);
return null;
}
const listen = lbJson.payload?.listens?.[0];
if (!listen?.playing_now) return null;
const trackMetadata = listen.track_metadata;
const albumName = trackMetadata.release_name || "Unknown";
const artistName = trackMetadata.artist_name || "Unknown";
const mbRes = await fetch(
`https://musicbrainz.org/ws/2/release/?query=release:${encodeURIComponent(albumName)}%20AND%20artist:${encodeURIComponent(artistName)}&fmt=json`
);
if (!mbRes.ok) throw `${mbRes.status} ${mbRes.statusText}`;
const mbJson = await mbRes.json();
const releases = mbJson.releases || [];
const releaseGroup = releases[0]?.["release-group"]?.id;
let url = "";
let imageUrl = "";
if (releaseGroup) {
const caaRes = await fetch(`https://coverartarchive.org/release-group/${releaseGroup}`);
if (caaRes.ok) {
const caaJson = await caaRes.json();
url = caaJson.release ?? "";
for (const image of caaJson.images) {
imageUrl = image.thumbnails.large || "";
if (imageUrl) break;
}
}
}
return {
name: trackMetadata.track_name || "Unknown",
album: albumName,
artist: artistName,
durationMs: trackMetadata.additional_info?.duration_ms,
recordingMBID: trackMetadata.additional_info?.recording_mbid,
url,
imageUrl,
};
} catch (e) {
logger.error("Failed to query ListenBrainz API", e);
return null;
}
}
function getLargeImage(track: LbTrackData): string | undefined {
if (track.imageUrl && !track.imageUrl.includes(PLACEHOLDER_ID)) return track.imageUrl;
if (settings.store.lb_missingArt === "placeholder") return "placeholder";
}
async function getActivity(): Promise<Activity | null> {
if (settings.store.lb_hideWithActivity) {
if (PresenceStore.getActivities().some(a => a.application_id !== APPLICATION_ID)) return null;
}
if (settings.store.lb_hideWithSpotify) {
if (PresenceStore.getActivities().some(a => a.type === ActivityType.LISTENING && a.application_id !== APPLICATION_ID))
return null;
}
const trackData = await fetchTrackData();
if (!trackData) return null;
const largeImage = getLargeImage(trackData);
const assets = largeImage
? {
large_image: await getAsset(largeImage),
large_text: trackData.album || undefined,
small_image: settings.store.lb_useLogo ? await getAsset("listenbrainz") : undefined,
small_text: "ListenBrainz",
}
: {
large_image: await getAsset("listenbrainz"),
large_text: trackData.album || undefined,
};
const buttons: ActivityButton[] = [];
if (settings.store.lb_shareUsername)
buttons.push({ label: "ListenBrainz Profile", url: `https://www.listenbrainz.org/user/${settings.store.lb_username}` });
if (settings.store.lb_shareSong && trackData.url)
buttons.push({ label: "View Song", url: trackData.url });
const statusName = (() => {
switch (settings.store.lb_nameFormat) {
case NameFormat.ArtistFirst: return trackData.artist + " - " + trackData.name;
case NameFormat.SongFirst: return trackData.name + " - " + trackData.artist;
case NameFormat.ArtistOnly: return trackData.artist;
case NameFormat.SongOnly: return trackData.name;
case NameFormat.AlbumName: return trackData.album || settings.store.lb_statusName;
default: return settings.store.lb_statusName;
}
})();
if (trackData.recordingMBID && trackData.recordingMBID !== currentRecordingMBID) {
currentRecordingMBID = trackData.recordingMBID;
currentStart = Date.now();
}
return {
application_id: APPLICATION_ID,
name: statusName,
details: trackData.name,
state: trackData.artist,
assets,
timestamps: settings.store.lb_useTimeBar && currentStart
? { start: currentStart, end: trackData.durationMs ? currentStart + trackData.durationMs : undefined }
: undefined,
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
metadata: buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
type: settings.store.lb_useListeningStatus ? ActivityType.LISTENING : ActivityType.PLAYING,
flags: ActivityFlags.INSTANCE,
};
}
async function updatePresence() {
try {
setActivity(await getActivity());
} catch (e) {
logger.error("Failed to update presence", e);
setActivity(null);
}
}
export function start() {
currentRecordingMBID = "";
currentStart = 0;
updatePresence();
updateInterval = setInterval(updatePresence, 16000);
}
export function stop() {
clearInterval(updateInterval);
updateInterval = undefined;
setActivity(null);
}

View file

@ -56,13 +56,6 @@ export const settings = definePluginSettings({
hidden: true, hidden: true,
onChange: () => onServiceChange?.(), onChange: () => onServiceChange?.(),
}, },
lb_enabled: {
description: "Enable ListenBrainz presence.",
type: OptionType.BOOLEAN,
default: false,
hidden: true,
onChange: () => onServiceChange?.(),
},
gr_enabled: { gr_enabled: {
description: "Enable Gensokyo Radio presence.", description: "Enable Gensokyo Radio presence.",
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
@ -261,90 +254,6 @@ export const settings = definePluginSettings({
hidden: true, hidden: true,
}, },
// ListenBrainz
lb_username: {
description: "ListenBrainz username.",
type: OptionType.STRING,
default: "",
hidden: true,
},
lb_mbContact: {
description: "MusicBrainz contact for user agent.",
type: OptionType.STRING,
default: "",
hidden: true,
},
lb_shareUsername: {
description: "Show link to ListenBrainz profile.",
type: OptionType.BOOLEAN,
default: false,
hidden: true,
},
lb_shareSong: {
description: "Show link to song on ListenBrainz.",
type: OptionType.BOOLEAN,
default: true,
hidden: true,
},
lb_hideWithSpotify: {
description: "Hide presence if Spotify is running.",
type: OptionType.BOOLEAN,
default: true,
hidden: true,
},
lb_hideWithActivity: {
description: "Hide presence if any other presence exists.",
type: OptionType.BOOLEAN,
default: false,
hidden: true,
},
lb_useTimeBar: {
description: "Use track duration to display a time bar.",
type: OptionType.BOOLEAN,
default: true,
hidden: true,
},
lb_statusName: {
description: "Custom status text.",
type: OptionType.STRING,
default: "some music",
hidden: true,
},
lb_nameFormat: {
description: "Name format.",
type: OptionType.SELECT,
options: [
{ label: "Use custom status name", value: NameFormat.StatusName, default: true },
{ label: "Use format 'artist - song'", value: NameFormat.ArtistFirst },
{ label: "Use format 'song - artist'", value: NameFormat.SongFirst },
{ label: "Use artist name only", value: NameFormat.ArtistOnly },
{ label: "Use song name only", value: NameFormat.SongOnly },
{ label: "Use album name", value: NameFormat.AlbumName },
],
hidden: true,
},
lb_useListeningStatus: {
description: "Show listening status.",
type: OptionType.BOOLEAN,
default: true,
hidden: true,
},
lb_missingArt: {
description: "Fallback when art is missing.",
type: OptionType.SELECT,
options: [
{ label: "Use large ListenBrainz logo", value: "listenbrainzLogo", default: true },
{ label: "Use generic placeholder", value: "placeholder" },
],
hidden: true,
},
lb_useLogo: {
description: "Show ListenBrainz logo on album art.",
type: OptionType.BOOLEAN,
default: true,
hidden: true,
},
// Gensokyo Radio // Gensokyo Radio
gr_refreshInterval: { gr_refreshInterval: {
description: "Refresh interval in seconds.", description: "Refresh interval in seconds.",

View file

@ -9,7 +9,6 @@ export enum ServiceTab {
Tosu = "tosu", Tosu = "tosu",
StatsFm = "statsfm", StatsFm = "statsfm",
Jellyfin = "jellyfin", Jellyfin = "jellyfin",
ListenBrainz = "listenbrainz",
GensokyoRadio = "gensokyoRadio", GensokyoRadio = "gensokyoRadio",
Navidrome = "navidrome", Navidrome = "navidrome",
} }