mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
fix(discord): remove dead fields, timeChanged invoke loop, and unsupported {paused} placeholder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,10 +34,6 @@ pub struct DiscordState {
|
||||
pub artwork_cache: Arc<Mutex<HashMap<String, ArtworkCacheEntry>>>,
|
||||
/// HTTP client for iTunes API requests. blocking::Client is Clone (Arc-internally).
|
||||
pub http_client: Client,
|
||||
/// Last calculated start timestamp for the current track (for freezing timer when paused).
|
||||
pub last_start_timestamp: Mutex<Option<i64>>,
|
||||
/// Track whether we were playing in the last update to detect transitions.
|
||||
pub was_playing: Mutex<bool>,
|
||||
}
|
||||
|
||||
impl DiscordState {
|
||||
@@ -49,8 +45,6 @@ impl DiscordState {
|
||||
.timeout(std::time::Duration::from_secs(5))
|
||||
.build()
|
||||
.unwrap_or_else(|_| Client::new()),
|
||||
last_start_timestamp: Mutex::new(None),
|
||||
was_playing: Mutex::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,16 +217,13 @@ fn try_connect() -> Option<DiscordIpcClient> {
|
||||
}
|
||||
|
||||
/// Apply a template string, replacing placeholders with actual values.
|
||||
/// Supported placeholders: {title}, {artist}, {album}, {paused}
|
||||
fn apply_template(template: &str, title: &str, artist: &str, album: Option<&str>, is_playing: bool) -> String {
|
||||
let paused_text = if is_playing { "" } else { "(Paused) " };
|
||||
/// Supported placeholders: {title}, {artist}, {album}
|
||||
fn apply_template(template: &str, title: &str, artist: &str, album: Option<&str>) -> String {
|
||||
let album_text = album.unwrap_or("");
|
||||
|
||||
template
|
||||
.replace("{title}", title)
|
||||
.replace("{artist}", artist)
|
||||
.replace("{album}", album_text)
|
||||
.replace("{paused}", paused_text)
|
||||
}
|
||||
|
||||
/// Update the Discord Rich Presence activity.
|
||||
@@ -245,11 +236,11 @@ fn apply_template(template: &str, title: &str, artist: &str, album: Option<&str>
|
||||
/// `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.
|
||||
/// - `details_template`: template string for the "details" field. Default: "{artist} - {title}".
|
||||
/// Supported placeholders: {title}, {artist}, {album}, {paused}
|
||||
/// Supported placeholders: {title}, {artist}, {album}
|
||||
/// - `state_template`: template string for the "state" field. Default: "{album}".
|
||||
/// Supported placeholders: {title}, {artist}, {album}, {paused}
|
||||
/// Supported placeholders: {title}, {artist}, {album}
|
||||
/// - `large_text_template`: template string for the large image tooltip. Default: "{album}".
|
||||
/// Supported placeholders: {title}, {artist}, {album}, {paused}
|
||||
/// Supported placeholders: {title}, {artist}, {album}
|
||||
#[tauri::command]
|
||||
pub async fn discord_update_presence(
|
||||
state: tauri::State<'_, DiscordState>,
|
||||
@@ -258,7 +249,6 @@ pub async fn discord_update_presence(
|
||||
album: Option<String>,
|
||||
is_playing: bool,
|
||||
elapsed_secs: Option<f64>,
|
||||
duration_secs: Option<f64>,
|
||||
cover_art_url: Option<String>,
|
||||
fetch_itunes_covers: bool,
|
||||
details_template: Option<String>,
|
||||
@@ -304,13 +294,13 @@ pub async fn discord_update_presence(
|
||||
|
||||
// Apply templates for the three configurable text fields.
|
||||
let details_str = details_template.as_deref().unwrap_or("{artist} - {title}");
|
||||
let details_text = apply_template(details_str, &title, &artist, album.as_deref(), is_playing);
|
||||
let details_text = apply_template(details_str, &title, &artist, album.as_deref());
|
||||
|
||||
let state_str = state_template.as_deref().unwrap_or("{album}");
|
||||
let state_text = apply_template(state_str, &title, &artist, album.as_deref(), is_playing);
|
||||
let state_text = apply_template(state_str, &title, &artist, album.as_deref());
|
||||
|
||||
let large_text_str = large_text_template.as_deref().unwrap_or("{album}");
|
||||
let large_text = apply_template(large_text_str, &title, &artist, album.as_deref(), is_playing);
|
||||
let large_text = apply_template(large_text_str, &title, &artist, album.as_deref());
|
||||
|
||||
let assets = if let Some(ref url) = artwork_url {
|
||||
Assets::new()
|
||||
@@ -365,8 +355,5 @@ pub fn discord_clear_presence(state: tauri::State<DiscordState>) -> Result<(), S
|
||||
*guard = None;
|
||||
}
|
||||
}
|
||||
// Clear saved timestamp so next track starts fresh
|
||||
let mut start_guard = state.last_start_timestamp.lock().unwrap();
|
||||
*start_guard = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+1
-1
@@ -540,7 +540,7 @@ export const deTranslation = {
|
||||
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.',
|
||||
discordTemplates: 'Benutzerdefinierte Textvorlagen',
|
||||
discordTemplatesDesc: 'Passen Sie an, welche Informationen in Ihrem Discord-Profil angezeigt werden. Variablen: {title}, {artist}, {album}, {paused}',
|
||||
discordTemplatesDesc: 'Passen Sie an, welche Informationen in Ihrem Discord-Profil angezeigt werden. Variablen: {title}, {artist}, {album}',
|
||||
discordTemplateDetails: 'Primäre Zeile (details)',
|
||||
discordTemplateState: 'Sekundäre Zeile (state)',
|
||||
discordTemplateLargeText: 'Album-Tooltip (largeText)',
|
||||
|
||||
+1
-1
@@ -542,7 +542,7 @@ export const enTranslation = {
|
||||
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.',
|
||||
discordTemplates: 'Custom text templates',
|
||||
discordTemplatesDesc: 'Customize what information is shown on your Discord profile. Variables: {title}, {artist}, {album}, {paused}',
|
||||
discordTemplatesDesc: 'Customize what information is shown on your Discord profile. Variables: {title}, {artist}, {album}',
|
||||
discordTemplateDetails: 'Primary line (details)',
|
||||
discordTemplateState: 'Secondary line (state)',
|
||||
discordTemplateLargeText: 'Album tooltip (largeText)',
|
||||
|
||||
+1
-1
@@ -543,7 +543,7 @@ export const esTranslation = {
|
||||
discordAppleCovers: 'Obtener portadas de Apple Music para Discord',
|
||||
discordAppleCoversDesc: 'Envía el artista y nombre del álbum a la API de búsqueda de Apple para encontrar portadas para tu perfil de Discord. Desactivado por defecto por privacidad.',
|
||||
discordTemplates: 'Plantillas de texto personalizadas',
|
||||
discordTemplatesDesc: 'Personaliza qué información se muestra en tu perfil de Discord. Variables: {title}, {artist}, {album}, {paused}',
|
||||
discordTemplatesDesc: 'Personaliza qué información se muestra en tu perfil de Discord. Variables: {title}, {artist}, {album}',
|
||||
discordTemplateDetails: 'Línea principal (details)',
|
||||
discordTemplateState: 'Línea secundaria (state)',
|
||||
discordTemplateLargeText: 'Tooltip del álbum (largeText)',
|
||||
|
||||
+1
-1
@@ -538,7 +538,7 @@ export const frTranslation = {
|
||||
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é.',
|
||||
discordTemplates: 'Modèles de texte personnalisés',
|
||||
discordTemplatesDesc: 'Personnalisez les informations affichées sur votre profil Discord. Variables : {title}, {artist}, {album}, {paused}',
|
||||
discordTemplatesDesc: 'Personnalisez les informations affichées sur votre profil Discord. Variables : {title}, {artist}, {album}',
|
||||
discordTemplateDetails: 'Ligne principale (details)',
|
||||
discordTemplateState: 'Ligne secondaire (state)',
|
||||
discordTemplateLargeText: 'Info-bulle album (largeText)',
|
||||
|
||||
+1
-1
@@ -537,7 +537,7 @@ export const nbTranslation = {
|
||||
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.',
|
||||
discordTemplates: 'Egendefinerte tekstmaler',
|
||||
discordTemplatesDesc: 'Tilpass hvilken informasjon som vises på Discord-profilen din. Variabler: {title}, {artist}, {album}, {paused}',
|
||||
discordTemplatesDesc: 'Tilpass hvilken informasjon som vises på Discord-profilen din. Variabler: {title}, {artist}, {album}',
|
||||
discordTemplateDetails: 'Primær linje (details)',
|
||||
discordTemplateState: 'Sekundær linje (state)',
|
||||
discordTemplateLargeText: 'Album-verktøytips (largeText)',
|
||||
|
||||
+1
-1
@@ -537,7 +537,7 @@ export const nlTranslation = {
|
||||
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.',
|
||||
discordTemplates: 'Aangepaste tekstsjablonen',
|
||||
discordTemplatesDesc: 'Pas aan welke informatie wordt weergegeven op je Discord-profiel. Variabelen: {title}, {artist}, {album}, {paused}',
|
||||
discordTemplatesDesc: 'Pas aan welke informatie wordt weergegeven op je Discord-profiel. Variabelen: {title}, {artist}, {album}',
|
||||
discordTemplateDetails: 'Primaire regel (details)',
|
||||
discordTemplateState: 'Secundaire regel (state)',
|
||||
discordTemplateLargeText: 'Album-tooltip (largeText)',
|
||||
|
||||
+1
-1
@@ -560,7 +560,7 @@ export const ruTranslation = {
|
||||
discordAppleCovers: 'Загружать обложки через Apple Music для Discord',
|
||||
discordAppleCoversDesc: 'Отправляет имя исполнителя и альбома в Apple Music API для поиска обложки для профиля Discord. По умолчанию отключено из соображений конфиденциальности.',
|
||||
discordTemplates: 'Пользовательские шаблоны текста',
|
||||
discordTemplatesDesc: 'Настройте, какая информация отображается в профиле Discord. Переменные: {title}, {artist}, {album}, {paused}',
|
||||
discordTemplatesDesc: 'Настройте, какая информация отображается в профиле Discord. Переменные: {title}, {artist}, {album}',
|
||||
discordTemplateDetails: 'Основная строка (details)',
|
||||
discordTemplateState: 'Вторичная строка (state)',
|
||||
discordTemplateLargeText: 'Подсказка альбома (largeText)',
|
||||
|
||||
+1
-1
@@ -533,7 +533,7 @@ export const zhTranslation = {
|
||||
discordAppleCovers: '通过 Apple Music 为 Discord 获取封面',
|
||||
discordAppleCoversDesc: '将艺术家和专辑名称发送至 Apple 搜索 API,以为 Discord 个人资料查找封面图片。出于隐私考虑,默认禁用。',
|
||||
discordTemplates: '自定义文本模板',
|
||||
discordTemplatesDesc: '自定义在 Discord 个人资料上显示的信息。变量:{title}, {artist}, {album}, {paused}',
|
||||
discordTemplatesDesc: '自定义在 Discord 个人资料上显示的信息。变量:{title}, {artist}, {album}',
|
||||
discordTemplateDetails: '主行 (details)',
|
||||
discordTemplateState: '副行 (state)',
|
||||
discordTemplateLargeText: '专辑提示 (largeText)',
|
||||
|
||||
@@ -692,7 +692,6 @@ export function initAudioListeners(): () => void {
|
||||
let discordPrevTemplateDetails: string | null = null;
|
||||
let discordPrevTemplateState: string | null = null;
|
||||
let discordPrevTemplateLargeText: string | null = null;
|
||||
let discordPrevCurrentTime: number | null = null;
|
||||
|
||||
function syncDiscord() {
|
||||
const { currentTrack, isPlaying, currentTime } = usePlayerStore.getState();
|
||||
@@ -723,8 +722,7 @@ export function initAudioListeners(): () => void {
|
||||
const detailsTemplateChanged = discordTemplateDetails !== discordPrevTemplateDetails;
|
||||
const stateTemplateChanged = discordTemplateState !== discordPrevTemplateState;
|
||||
const largeTextTemplateChanged = discordTemplateLargeText !== discordPrevTemplateLargeText;
|
||||
const timeChanged = Math.abs(currentTime - (discordPrevCurrentTime ?? -1)) > 1; // Only update if time changed by more than 1 second
|
||||
if (!trackChanged && !playingChanged && !coversSettingChanged && !detailsTemplateChanged && !stateTemplateChanged && !largeTextTemplateChanged && !timeChanged) return;
|
||||
if (!trackChanged && !playingChanged && !coversSettingChanged && !detailsTemplateChanged && !stateTemplateChanged && !largeTextTemplateChanged) return;
|
||||
|
||||
discordPrevTrackId = currentTrack.id;
|
||||
discordPrevIsPlaying = isPlaying;
|
||||
@@ -732,17 +730,13 @@ export function initAudioListeners(): () => void {
|
||||
discordPrevTemplateDetails = discordTemplateDetails;
|
||||
discordPrevTemplateState = discordTemplateState;
|
||||
discordPrevTemplateLargeText = discordTemplateLargeText;
|
||||
discordPrevCurrentTime = currentTime;
|
||||
|
||||
invoke('discord_update_presence', {
|
||||
title: currentTrack.title,
|
||||
artist: currentTrack.artist ?? 'Unknown Artist',
|
||||
album: currentTrack.album ?? null,
|
||||
isPlaying,
|
||||
// Always pass elapsedSecs so backend can handle both playing and paused states.
|
||||
// Backend will freeze timer when paused using end timestamp.
|
||||
elapsedSecs: currentTime,
|
||||
durationSecs: currentTrack.duration,
|
||||
elapsedSecs: isPlaying ? currentTime : null,
|
||||
// coverArtUrl is intentionally not passed — Subsonic URLs require auth.
|
||||
// iTunes cover fetching is only done when explicitly opted in.
|
||||
coverArtUrl: null,
|
||||
|
||||
Reference in New Issue
Block a user