mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
fix(discord): add Apple Music cover opt-in, show Paused state
- iTunes Search API calls are now gated behind enableAppleMusicCoversDiscord (default: false). No external requests to Apple are made unless the user explicitly enables the new opt-in toggle in Settings. - When the toggle is flipped, Discord presence is immediately re-sent for the current track (coversSettingChanged bypasses the early-return guard). - When paused, activity type switches to Playing (no auto-timer) and state text shows "Paused" instead of the artist name. - New authStore field + setter; new Settings toggle shown only when Discord RPC is enabled; i18n keys added to all 7 languages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -218,24 +218,31 @@ fn try_connect() -> Option<DiscordIpcClient> {
|
|||||||
|
|
||||||
/// Update the Discord Rich Presence activity.
|
/// Update the Discord Rich Presence activity.
|
||||||
///
|
///
|
||||||
/// - `elapsed_secs`: seconds already played. `None` when paused — Discord shows
|
/// - `is_playing`: true = playing (timer shown), false = paused (no timer, state shows "Paused").
|
||||||
/// the song/artist without a running timer.
|
/// - `elapsed_secs`: seconds already played. `None` when paused — no timestamp is sent so
|
||||||
/// - `cover_art_url`: optional direct URL to album artwork. If None, tries to
|
/// Discord stops any running timer.
|
||||||
/// fetch from iTunes Search API using artist + album.
|
/// - `cover_art_url`: optional direct URL to album artwork.
|
||||||
|
/// - `fetch_itunes_covers`: if true, fetch artwork from the iTunes Search API when no
|
||||||
|
/// `cover_art_url` is provided. If false (default), fall back to the Psysonic app icon
|
||||||
|
/// without making any external request — required for privacy opt-in.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn discord_update_presence(
|
pub async fn discord_update_presence(
|
||||||
state: tauri::State<'_, DiscordState>,
|
state: tauri::State<'_, DiscordState>,
|
||||||
title: String,
|
title: String,
|
||||||
artist: String,
|
artist: String,
|
||||||
album: Option<String>,
|
album: Option<String>,
|
||||||
|
is_playing: bool,
|
||||||
elapsed_secs: Option<f64>,
|
elapsed_secs: Option<f64>,
|
||||||
cover_art_url: Option<String>,
|
cover_art_url: Option<String>,
|
||||||
|
fetch_itunes_covers: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Resolve artwork on a dedicated blocking thread — reqwest::blocking must not
|
// Resolve artwork on a dedicated blocking thread — reqwest::blocking must not
|
||||||
// run on the Tokio async executor directly.
|
// run on the Tokio async executor directly.
|
||||||
|
// Only hit the iTunes API if the user has explicitly opted in.
|
||||||
let artwork_url: Option<String> = if let Some(url) = cover_art_url {
|
let artwork_url: Option<String> = if let Some(url) = cover_art_url {
|
||||||
Some(url)
|
Some(url)
|
||||||
} else if let Some(ref album_name) = album {
|
} else if fetch_itunes_covers {
|
||||||
|
if let Some(ref album_name) = album {
|
||||||
let http_client = state.http_client.clone();
|
let http_client = state.http_client.clone();
|
||||||
let cache = Arc::clone(&state.artwork_cache);
|
let cache = Arc::clone(&state.artwork_cache);
|
||||||
let artist_c = artist.clone();
|
let artist_c = artist.clone();
|
||||||
@@ -249,6 +256,9 @@ pub async fn discord_update_presence(
|
|||||||
.flatten()
|
.flatten()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut guard = state.client.lock().unwrap();
|
let mut guard = state.client.lock().unwrap();
|
||||||
@@ -280,14 +290,32 @@ pub async fn discord_update_presence(
|
|||||||
.large_text(large_text)
|
.large_text(large_text)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// When paused, show "Paused" as the state text (replaces artist name).
|
||||||
|
let state_text: String = if is_playing {
|
||||||
|
artist.clone()
|
||||||
|
} else {
|
||||||
|
"Paused".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// ActivityType::Listening causes the Discord client to auto-start a running
|
||||||
|
// timer from "now" even when no timestamps are provided. Switch to Playing
|
||||||
|
// when paused — Playing only shows a timer when timestamps are explicitly set.
|
||||||
|
let activity_type = if is_playing {
|
||||||
|
ActivityType::Listening
|
||||||
|
} else {
|
||||||
|
ActivityType::Playing
|
||||||
|
};
|
||||||
|
|
||||||
let mut activity = Activity::new()
|
let mut activity = Activity::new()
|
||||||
.activity_type(ActivityType::Listening)
|
.activity_type(activity_type)
|
||||||
.details(&title)
|
.details(&title)
|
||||||
.state(&artist)
|
.state(&state_text)
|
||||||
.assets(assets);
|
.assets(assets);
|
||||||
|
|
||||||
// Start timestamp: Discord auto-counts up from this point. We back-calculate
|
// Start timestamp: Discord auto-counts up from this point. We back-calculate
|
||||||
// it so the displayed elapsed time matches the actual playback position.
|
// it so the displayed elapsed time matches the actual playback position.
|
||||||
|
// Only set when playing — Playing type without timestamps shows no timer.
|
||||||
|
if is_playing {
|
||||||
if let Some(elapsed) = elapsed_secs {
|
if let Some(elapsed) = elapsed_secs {
|
||||||
let now = std::time::SystemTime::now()
|
let now = std::time::SystemTime::now()
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
@@ -296,6 +324,7 @@ pub async fn discord_update_presence(
|
|||||||
let start = now - elapsed.floor() as i64;
|
let start = now - elapsed.floor() as i64;
|
||||||
activity = activity.timestamps(Timestamps::new().start(start));
|
activity = activity.timestamps(Timestamps::new().start(start));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if client.set_activity(activity).is_err() {
|
if client.set_activity(activity).is_err() {
|
||||||
// IPC pipe broke (Discord restarted etc.) — drop the client so the next
|
// IPC pipe broke (Discord restarted etc.) — drop the client so the next
|
||||||
|
|||||||
@@ -437,6 +437,8 @@ export const deTranslation = {
|
|||||||
minimizeToTrayDesc: 'Beim Schließen des Fensters läuft Psysonic weiter im System-Tray statt zu beenden.',
|
minimizeToTrayDesc: 'Beim Schließen des Fensters läuft Psysonic weiter im System-Tray statt zu beenden.',
|
||||||
discordRichPresence: 'Discord Rich Presence',
|
discordRichPresence: 'Discord Rich Presence',
|
||||||
discordRichPresenceDesc: 'Zeigt den aktuell gespielten Titel im Discord-Profil an. Discord muss dafür geöffnet sein.',
|
discordRichPresenceDesc: 'Zeigt den aktuell gespielten Titel im Discord-Profil an. Discord muss dafür geöffnet sein.',
|
||||||
|
discordAppleCovers: 'Cover über Apple Music für Discord laden',
|
||||||
|
discordAppleCoversDesc: 'Sendet Künstler- und Albumname an die Apple-Such-API, um Cover für dein Discord-Profil zu finden. Standardmäßig aus Datenschutzgründen deaktiviert.',
|
||||||
nowPlayingEnabled: 'Im Livefenster anzeigen',
|
nowPlayingEnabled: 'Im Livefenster anzeigen',
|
||||||
nowPlayingEnabledDesc: 'Überträgt den aktuell gespielten Titel an die Livehörer-Ansicht des Servers. Deaktivieren, um keine Wiedergabedaten zu senden.',
|
nowPlayingEnabledDesc: 'Überträgt den aktuell gespielten Titel an die Livehörer-Ansicht des Servers. Deaktivieren, um keine Wiedergabedaten zu senden.',
|
||||||
lyricsServerFirst: 'Server-Lyrics bevorzugen',
|
lyricsServerFirst: 'Server-Lyrics bevorzugen',
|
||||||
|
|||||||
@@ -438,6 +438,8 @@ export const enTranslation = {
|
|||||||
minimizeToTrayDesc: 'When closing the window, keep Psysonic running in the system tray instead of quitting.',
|
minimizeToTrayDesc: 'When closing the window, keep Psysonic running in the system tray instead of quitting.',
|
||||||
discordRichPresence: 'Discord Rich Presence',
|
discordRichPresence: 'Discord Rich Presence',
|
||||||
discordRichPresenceDesc: 'Show the currently playing track on your Discord profile. Requires Discord to be running.',
|
discordRichPresenceDesc: 'Show the currently playing track on your Discord profile. Requires Discord to be running.',
|
||||||
|
discordAppleCovers: 'Fetch covers from Apple Music for Discord',
|
||||||
|
discordAppleCoversDesc: 'Sends the artist and album name to Apple\'s search API to find cover art for your Discord profile. Disabled by default for privacy.',
|
||||||
nowPlayingEnabled: 'Show in Now Playing',
|
nowPlayingEnabled: 'Show in Now Playing',
|
||||||
nowPlayingEnabledDesc: 'Broadcast your currently playing track to the server\'s live listener view. Disable to stop sending playback data.',
|
nowPlayingEnabledDesc: 'Broadcast your currently playing track to the server\'s live listener view. Disable to stop sending playback data.',
|
||||||
lyricsServerFirst: 'Prefer server lyrics',
|
lyricsServerFirst: 'Prefer server lyrics',
|
||||||
|
|||||||
@@ -437,6 +437,8 @@ export const frTranslation = {
|
|||||||
minimizeToTrayDesc: 'Lors de la fermeture, Psysonic continue de fonctionner dans la barre système au lieu de se fermer.',
|
minimizeToTrayDesc: 'Lors de la fermeture, Psysonic continue de fonctionner dans la barre système au lieu de se fermer.',
|
||||||
discordRichPresence: 'Discord Rich Presence',
|
discordRichPresence: 'Discord Rich Presence',
|
||||||
discordRichPresenceDesc: 'Affiche le titre en cours de lecture sur votre profil Discord. Discord doit être ouvert.',
|
discordRichPresenceDesc: 'Affiche le titre en cours de lecture sur votre profil Discord. Discord doit être ouvert.',
|
||||||
|
discordAppleCovers: 'Récupérer les pochettes via Apple Music pour Discord',
|
||||||
|
discordAppleCoversDesc: 'Envoie le nom de l\'artiste et de l\'album à l\'API Apple pour trouver une pochette pour votre profil Discord. Désactivé par défaut pour des raisons de confidentialité.',
|
||||||
nowPlayingEnabled: 'Afficher dans la fenêtre live',
|
nowPlayingEnabled: 'Afficher dans la fenêtre live',
|
||||||
nowPlayingEnabledDesc: 'Diffuse le titre en cours de lecture vers la vue des auditeurs en direct du serveur. Désactiver pour ne pas envoyer de données de lecture.',
|
nowPlayingEnabledDesc: 'Diffuse le titre en cours de lecture vers la vue des auditeurs en direct du serveur. Désactiver pour ne pas envoyer de données de lecture.',
|
||||||
lyricsServerFirst: 'Préférer les paroles du serveur',
|
lyricsServerFirst: 'Préférer les paroles du serveur',
|
||||||
|
|||||||
@@ -436,6 +436,8 @@ export const nbTranslation = {
|
|||||||
minimizeToTrayDesc: 'Når vinduet lukkes, vil Psysonic bli kjørende i oppgavelinjen fremfor å bli avsluttet.',
|
minimizeToTrayDesc: 'Når vinduet lukkes, vil Psysonic bli kjørende i oppgavelinjen fremfor å bli avsluttet.',
|
||||||
discordRichPresence: 'Discord Rich Presence',
|
discordRichPresence: 'Discord Rich Presence',
|
||||||
discordRichPresenceDesc: 'Vis sporet som spilles i din Discord-profil. Krever at Discord kjører.',
|
discordRichPresenceDesc: 'Vis sporet som spilles i din Discord-profil. Krever at Discord kjører.',
|
||||||
|
discordAppleCovers: 'Hent covere fra Apple Music til Discord',
|
||||||
|
discordAppleCoversDesc: 'Sender artist- og albumnavn til Apples søke-API for å finne cover til Discord-profilen din. Deaktivert som standard av personvernhensyn.',
|
||||||
nowPlayingEnabled: 'Vis i "Nå spiller"',
|
nowPlayingEnabled: 'Vis i "Nå spiller"',
|
||||||
nowPlayingEnabledDesc: 'Send sporet som spilles av til tjenerens live-lyttervisning. Deaktiver for å stoppe sending av avspillingsdata.',
|
nowPlayingEnabledDesc: 'Send sporet som spilles av til tjenerens live-lyttervisning. Deaktiver for å stoppe sending av avspillingsdata.',
|
||||||
lyricsServerFirst: 'Foretrekk server-sangtekst',
|
lyricsServerFirst: 'Foretrekk server-sangtekst',
|
||||||
|
|||||||
@@ -437,6 +437,8 @@ export const nlTranslation = {
|
|||||||
minimizeToTrayDesc: 'Bij het sluiten van het venster blijft Psysonic actief in het systeemvak in plaats van af te sluiten.',
|
minimizeToTrayDesc: 'Bij het sluiten van het venster blijft Psysonic actief in het systeemvak in plaats van af te sluiten.',
|
||||||
discordRichPresence: 'Discord Rich Presence',
|
discordRichPresence: 'Discord Rich Presence',
|
||||||
discordRichPresenceDesc: 'Toont het huidige nummer op je Discord-profiel. Discord moet daarvoor geopend zijn.',
|
discordRichPresenceDesc: 'Toont het huidige nummer op je Discord-profiel. Discord moet daarvoor geopend zijn.',
|
||||||
|
discordAppleCovers: 'Hoezen ophalen via Apple Music voor Discord',
|
||||||
|
discordAppleCoversDesc: 'Stuurt artiest- en albumnaam naar de Apple-zoek-API om een hoes te vinden voor je Discord-profiel. Standaard uitgeschakeld vanwege privacy.',
|
||||||
nowPlayingEnabled: 'Weergeven in live-venster',
|
nowPlayingEnabled: 'Weergeven in live-venster',
|
||||||
nowPlayingEnabledDesc: 'Stuurt het huidige nummer naar de live-luisteraarsweergave van de server. Uitschakelen om geen afspeelgegevens te verzenden.',
|
nowPlayingEnabledDesc: 'Stuurt het huidige nummer naar de live-luisteraarsweergave van de server. Uitschakelen om geen afspeelgegevens te verzenden.',
|
||||||
lyricsServerFirst: 'Server-songtekst voorrang geven',
|
lyricsServerFirst: 'Server-songtekst voorrang geven',
|
||||||
|
|||||||
@@ -456,6 +456,8 @@ export const ruTranslation = {
|
|||||||
discordRichPresence: 'Статус в Discord',
|
discordRichPresence: 'Статус в Discord',
|
||||||
discordRichPresenceDesc:
|
discordRichPresenceDesc:
|
||||||
'Показывать текущий трек в профиле и статусе Discord. Нужен запущенный клиент Discord.',
|
'Показывать текущий трек в профиле и статусе Discord. Нужен запущенный клиент Discord.',
|
||||||
|
discordAppleCovers: 'Загружать обложки через Apple Music для Discord',
|
||||||
|
discordAppleCoversDesc: 'Отправляет имя исполнителя и альбома в API поиска Apple для поиска обложки в профиле Discord. По умолчанию отключено из соображений конфиденциальности.',
|
||||||
nowPlayingEnabled: 'Показывать в «Сейчас играет»',
|
nowPlayingEnabled: 'Показывать в «Сейчас играет»',
|
||||||
nowPlayingEnabledDesc:
|
nowPlayingEnabledDesc:
|
||||||
'Отправлять на сервер, что вы сейчас слушаете. Отключите, чтобы не делиться этим.',
|
'Отправлять на сервер, что вы сейчас слушаете. Отключите, чтобы не делиться этим.',
|
||||||
|
|||||||
@@ -433,6 +433,8 @@ export const zhTranslation = {
|
|||||||
minimizeToTrayDesc: '关闭窗口时,Psysonic 将继续在系统托盘中运行,而不是退出。',
|
minimizeToTrayDesc: '关闭窗口时,Psysonic 将继续在系统托盘中运行,而不是退出。',
|
||||||
discordRichPresence: 'Discord Rich Presence',
|
discordRichPresence: 'Discord Rich Presence',
|
||||||
discordRichPresenceDesc: '在 Discord 个人资料上显示当前播放的曲目。需要 Discord 处于运行状态。',
|
discordRichPresenceDesc: '在 Discord 个人资料上显示当前播放的曲目。需要 Discord 处于运行状态。',
|
||||||
|
discordAppleCovers: '通过 Apple Music 为 Discord 获取封面',
|
||||||
|
discordAppleCoversDesc: '将艺术家和专辑名称发送至 Apple 搜索 API,以为 Discord 个人资料查找封面图片。出于隐私考虑,默认禁用。',
|
||||||
nowPlayingEnabled: '在实时窗口中显示',
|
nowPlayingEnabled: '在实时窗口中显示',
|
||||||
nowPlayingEnabledDesc: '将当前播放的曲目广播到服务器的实时听众视图。禁用以停止发送播放数据。',
|
nowPlayingEnabledDesc: '将当前播放的曲目广播到服务器的实时听众视图。禁用以停止发送播放数据。',
|
||||||
lyricsServerFirst: '优先使用服务器歌词',
|
lyricsServerFirst: '优先使用服务器歌词',
|
||||||
|
|||||||
@@ -637,6 +637,21 @@ export default function Settings() {
|
|||||||
<span className="toggle-track" />
|
<span className="toggle-track" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
{auth.discordRichPresence && (
|
||||||
|
<>
|
||||||
|
<div className="settings-section-divider" />
|
||||||
|
<div className="settings-toggle-row" style={{ paddingLeft: 16 }}>
|
||||||
|
<div>
|
||||||
|
<div style={{ fontWeight: 500 }}>{t('settings.discordAppleCovers')}</div>
|
||||||
|
<div style={{ fontSize: 12, color: 'var(--text-muted)' }}>{t('settings.discordAppleCoversDesc')}</div>
|
||||||
|
</div>
|
||||||
|
<label className="toggle-switch" aria-label={t('settings.discordAppleCovers')}>
|
||||||
|
<input type="checkbox" checked={auth.enableAppleMusicCoversDiscord} onChange={e => auth.setEnableAppleMusicCoversDiscord(e.target.checked)} />
|
||||||
|
<span className="toggle-track" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<div className="settings-section-divider" />
|
<div className="settings-section-divider" />
|
||||||
<div className="settings-toggle-row">
|
<div className="settings-toggle-row">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ interface AuthState {
|
|||||||
showTrayIcon: boolean;
|
showTrayIcon: boolean;
|
||||||
minimizeToTray: boolean;
|
minimizeToTray: boolean;
|
||||||
discordRichPresence: boolean;
|
discordRichPresence: boolean;
|
||||||
|
enableAppleMusicCoversDiscord: boolean;
|
||||||
nowPlayingEnabled: boolean;
|
nowPlayingEnabled: boolean;
|
||||||
lyricsServerFirst: boolean;
|
lyricsServerFirst: boolean;
|
||||||
showFullscreenLyrics: boolean;
|
showFullscreenLyrics: boolean;
|
||||||
@@ -103,6 +104,7 @@ interface AuthState {
|
|||||||
setShowTrayIcon: (v: boolean) => void;
|
setShowTrayIcon: (v: boolean) => void;
|
||||||
setMinimizeToTray: (v: boolean) => void;
|
setMinimizeToTray: (v: boolean) => void;
|
||||||
setDiscordRichPresence: (v: boolean) => void;
|
setDiscordRichPresence: (v: boolean) => void;
|
||||||
|
setEnableAppleMusicCoversDiscord: (v: boolean) => void;
|
||||||
setNowPlayingEnabled: (v: boolean) => void;
|
setNowPlayingEnabled: (v: boolean) => void;
|
||||||
setLyricsServerFirst: (v: boolean) => void;
|
setLyricsServerFirst: (v: boolean) => void;
|
||||||
setShowFullscreenLyrics: (v: boolean) => void;
|
setShowFullscreenLyrics: (v: boolean) => void;
|
||||||
@@ -153,6 +155,7 @@ export const useAuthStore = create<AuthState>()(
|
|||||||
showTrayIcon: true,
|
showTrayIcon: true,
|
||||||
minimizeToTray: false,
|
minimizeToTray: false,
|
||||||
discordRichPresence: false,
|
discordRichPresence: false,
|
||||||
|
enableAppleMusicCoversDiscord: false,
|
||||||
nowPlayingEnabled: false,
|
nowPlayingEnabled: false,
|
||||||
lyricsServerFirst: true,
|
lyricsServerFirst: true,
|
||||||
showFullscreenLyrics: true,
|
showFullscreenLyrics: true,
|
||||||
@@ -236,6 +239,7 @@ export const useAuthStore = create<AuthState>()(
|
|||||||
setShowTrayIcon: (v) => set({ showTrayIcon: v }),
|
setShowTrayIcon: (v) => set({ showTrayIcon: v }),
|
||||||
setMinimizeToTray: (v) => set({ minimizeToTray: v }),
|
setMinimizeToTray: (v) => set({ minimizeToTray: v }),
|
||||||
setDiscordRichPresence: (v) => set({ discordRichPresence: v }),
|
setDiscordRichPresence: (v) => set({ discordRichPresence: v }),
|
||||||
|
setEnableAppleMusicCoversDiscord: (v) => set({ enableAppleMusicCoversDiscord: v }),
|
||||||
setNowPlayingEnabled: (v) => set({ nowPlayingEnabled: v }),
|
setNowPlayingEnabled: (v) => set({ nowPlayingEnabled: v }),
|
||||||
setLyricsServerFirst: (v: boolean) => set({ lyricsServerFirst: v }),
|
setLyricsServerFirst: (v: boolean) => set({ lyricsServerFirst: v }),
|
||||||
setShowFullscreenLyrics: (v: boolean) => set({ showFullscreenLyrics: v }),
|
setShowFullscreenLyrics: (v: boolean) => set({ showFullscreenLyrics: v }),
|
||||||
|
|||||||
@@ -505,15 +505,17 @@ export function initAudioListeners(): () => void {
|
|||||||
// Discord auto-counts up the elapsed timer from the start_timestamp we set.
|
// Discord auto-counts up the elapsed timer from the start_timestamp we set.
|
||||||
let discordPrevTrackId: string | null = null;
|
let discordPrevTrackId: string | null = null;
|
||||||
let discordPrevIsPlaying: boolean | null = null;
|
let discordPrevIsPlaying: boolean | null = null;
|
||||||
|
let discordPrevFetchCovers: boolean | null = null;
|
||||||
|
|
||||||
function syncDiscord() {
|
function syncDiscord() {
|
||||||
const { currentTrack, isPlaying, currentTime } = usePlayerStore.getState();
|
const { currentTrack, isPlaying, currentTime } = usePlayerStore.getState();
|
||||||
const { discordRichPresence } = useAuthStore.getState();
|
const { discordRichPresence, enableAppleMusicCoversDiscord } = useAuthStore.getState();
|
||||||
|
|
||||||
if (!discordRichPresence || !currentTrack) {
|
if (!discordRichPresence || !currentTrack) {
|
||||||
if (discordPrevTrackId !== null) {
|
if (discordPrevTrackId !== null) {
|
||||||
discordPrevTrackId = null;
|
discordPrevTrackId = null;
|
||||||
discordPrevIsPlaying = null;
|
discordPrevIsPlaying = null;
|
||||||
|
discordPrevFetchCovers = null;
|
||||||
invoke('discord_clear_presence').catch(() => {});
|
invoke('discord_clear_presence').catch(() => {});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -521,21 +523,25 @@ export function initAudioListeners(): () => void {
|
|||||||
|
|
||||||
const trackChanged = currentTrack.id !== discordPrevTrackId;
|
const trackChanged = currentTrack.id !== discordPrevTrackId;
|
||||||
const playingChanged = isPlaying !== discordPrevIsPlaying;
|
const playingChanged = isPlaying !== discordPrevIsPlaying;
|
||||||
if (!trackChanged && !playingChanged) return;
|
const coversSettingChanged = enableAppleMusicCoversDiscord !== discordPrevFetchCovers;
|
||||||
|
if (!trackChanged && !playingChanged && !coversSettingChanged) return;
|
||||||
|
|
||||||
discordPrevTrackId = currentTrack.id;
|
discordPrevTrackId = currentTrack.id;
|
||||||
discordPrevIsPlaying = isPlaying;
|
discordPrevIsPlaying = isPlaying;
|
||||||
|
discordPrevFetchCovers = enableAppleMusicCoversDiscord;
|
||||||
|
|
||||||
invoke('discord_update_presence', {
|
invoke('discord_update_presence', {
|
||||||
title: currentTrack.title,
|
title: currentTrack.title,
|
||||||
artist: currentTrack.artist ?? 'Unknown Artist',
|
artist: currentTrack.artist ?? 'Unknown Artist',
|
||||||
album: currentTrack.album ?? null,
|
album: currentTrack.album ?? null,
|
||||||
|
isPlaying,
|
||||||
// Pass elapsed when playing so Discord shows a live running timer.
|
// Pass elapsed when playing so Discord shows a live running timer.
|
||||||
// Pass null when paused — Discord shows the song/artist without a timer.
|
// Pass null when paused — Discord clears the timer.
|
||||||
elapsedSecs: isPlaying ? currentTime : null,
|
elapsedSecs: isPlaying ? currentTime : null,
|
||||||
// coverArtUrl is intentionally not passed — Subsonic URLs require auth.
|
// coverArtUrl is intentionally not passed — Subsonic URLs require auth.
|
||||||
// Backend will fetch artwork from iTunes Search API instead.
|
// iTunes cover fetching is only done when explicitly opted in.
|
||||||
coverArtUrl: null,
|
coverArtUrl: null,
|
||||||
|
fetchItunesCovers: enableAppleMusicCoversDiscord,
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user