From 6456b3e5617c4f455043aa2f963d03fb08df8fad Mon Sep 17 00:00:00 2001 From: Psychotoxical Date: Wed, 11 Mar 2026 21:56:26 +0100 Subject: [PATCH] chore: prepare release v1.0.1 and ignore CLAUDE.md --- .gitignore | 3 + CHANGELOG.md | 12 + logo-psysonic.png | Bin 39399 -> 0 bytes logo-square.png | Bin 32271 -> 0 bytes package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/capabilities/default.json | 3 +- src-tauri/gen/schemas/capabilities.json | 2 +- src-tauri/tauri.conf.json | 2 +- src/App.tsx | 4 +- src/api/subsonic.ts | 122 ++++--- src/components/AlbumRow.tsx | 6 +- src/components/ArtistRow.tsx | 6 +- src/components/ContextMenu.tsx | 262 +++++++------- src/components/FullscreenPlayer.tsx | 19 +- src/components/Hero.tsx | 14 +- src/components/LiveSearch.tsx | 18 +- src/components/NowPlayingDropdown.tsx | 37 +- src/i18n.ts | 431 ++++++++++++++++++++++-- src/pages/AlbumDetail.tsx | 253 +++++++------- src/pages/Albums.tsx | 14 +- src/pages/ArtistDetail.tsx | 100 +++--- src/pages/Artists.tsx | 51 +-- src/pages/Favorites.tsx | 22 +- src/pages/Home.tsx | 19 +- src/pages/LabelAlbums.tsx | 16 +- src/pages/Login.tsx | 163 +++++---- src/pages/Playlists.tsx | 33 +- src/pages/RandomMix.tsx | 52 ++- src/pages/Settings.tsx | 269 +++++++++++---- src/pages/Statistics.tsx | 41 +-- src/store/authStore.ts | 95 +++--- src/store/playerStore.ts | 14 +- src/styles/components.css | 6 + 35 files changed, 1355 insertions(+), 740 deletions(-) delete mode 100644 logo-psysonic.png delete mode 100644 logo-square.png diff --git a/.gitignore b/.gitignore index 4125ba78..398d1732 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ dist-ssr # Tauri src-tauri/target/ + +# Documentation +CLAUDE.md diff --git a/CHANGELOG.md b/CHANGELOG.md index feae1481..fa4ea895 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.1] - 2026-03-11 + +### Fixed +- **Optimized Codebase**: Integrated core fixes and performance improvements. +- **Improved Multi-Server Support**: Fixed edge cases in server switching and credential management. +- **Enhanced Security**: Switched to `crypto.getRandomValues()` for more robust auth salt generation. +- **Connection Reliability**: Added pre-verification for server connections to prevent state synchronization issues. +- **Linux Compatibility**: Applied workarounds for WebKitGTK compositing issues on Linux. + +### Changed +- Repository maintenance and preparation for the 1.0.1 release. + ## [1.0.0] - 2026-03-09 ### Added diff --git a/logo-psysonic.png b/logo-psysonic.png deleted file mode 100644 index bfb490c5fb05cd8b2fab5db32dd34dcc0ac3340c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39399 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 diff --git a/logo-square.png b/logo-square.png deleted file mode 100644 index 3ddf7cbc23c93af905a6d593614ab2350a6532e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32271 zcmeFYWl$YY)INA|f`m(OcSs<(yIX)D!QI^@xCaR?!QJhm!QI_mF9i3C`-SEA{;Rfj z_uGDdXKJRadghFr>F)FNbDsIF^j#Vqg%AY*0HDjtNT>h+u$upE$cXPXo#v_Q?*+Mu zjEVvP;6)1n1pER3p5Chh4gml+769Pb5CGsy1px3IGh3Af-VY!e%SlVTm+#A?c$@!w zf#fQyAc=GghxmzuI#6!O@x6-1RZ`Q{)X3G0&&1j6y#TPWuyHZ6urso7sj_nOv2yTn zvNN!-@UgILwi&!X(*MK2&cW2m-1Glqz{1AI!TWyo3!*_pYx8reDi zzifE;INA8vx&DvMJr>tL0Kf+#Dw!+7LFEr)(*6eQX zE-vzooan_@IERLUe-F(6H!blx^ME)Y{C}7K^}zpn;D0^v|IY*8Or^;Hm>sSL9+qW3 z_Z>fQ0G)RS>ZqwsA;uhoWz{!eqJwrP@CSIMTOejBEFj@c41g)pA(GIA$k74A0nHGF z3ID80L3sg=#1HhsU_~rPp+W?E2Lkd@!UMxgSyj-2a)GP4+-SDo2wP0Uu@HwGe8{1V zw8V|JVSr?P;!`gqqoh)M#d%rC`@HjQTK8v)o6QnBmMDG;53~i>X%a@sqk4~LR2vmb zljne(GCZG>Ee&m(C$s_PeaDIj*dj5E5>@>QD-}QT>jF$wPjU4qP=~{Nb9a~DT8Gln z(SiJzKtdVb2G#-OBns?TOG;9fG&3U#8b3ym%fPK(sqynPQ@fo#rV!spqGaf5^Y2S0 z@nvjk$~d=6#=^(|G4Bbb=->a!bXnDBJ`$4P@L=txTpjUwoLQuZOkos0A@M%H8@an{ zbgG4Sq`ZhzDgIFVY9)>;>Nt^GrhMEet0yEadL0x#<9^NBhh4?{RRCQq>C)G{Y;-WVpc0Sa90CI5)g@O8Olesu8t@|PkxtlxeN|%Iu6AMnNxbXvAla;*5@*o z;kbBV0o?j?W7T&Jl{B97m_imv<0~3nm`2AcMFE5eqRC(RK2QgIW7mq8ONNtMsp5%w zMzDsRp~j-&YEdAjqfy-(S0_~RyfJH2<&?D`D7g480got~n*#3uAa`VvGDO=V0U>W0 zkKmJ^AN3q!QgK&lw!LCjJ3~v2#9tj}+D;<*pPst#xi{A6H#S1<<+C~VOLSHnIlNev z9OikHA%d`QtlKo$x%h4^j9CK}p=booVAdh|^W~3itg4ISX^9TV6-KHDzI` zQcY!RZlG5xhJ=Q<7#H8P{_bARMkPl8re6a!k9gH^l;BuLc0wp?gv9VA6BzHFKHNt8 z`&$3oW*Ceiemp_l)Jxf%sUB$5y&p5@J-ZlrO3X+mW-*u8Q`iuk!MAZn!7POnV+B?R zcxb}F+8AxYr%SITpYsRF-xUmz2{*V%=OAI)37h{nc0k=LE0!{OsN)K@PAKzFmXp|;?^~`Rmd`U#|EBg&El_1pE`huhTayC4GeKCck%$m^ zo<=T+5_(R&=ZXw!AXGzVC0ga`^4*S$;I|%%eS@yt zkMm!hU{u%eKiw{`T7@YtGC1h**;tF8;gIMJu!1e-D*_|yeWHWOMGUFS0r|m#e#{XDz zZ^b@g7p)XfBhNY~fBEpswkt}dY4&T`~SW3>dRrS**A zX#OD!i*_L3lerjJL{L3@og5@aZgp!22!~b2UerwDNdReK2e906Fe!CVTYZGV2V9l79}{L@V@yVR~~f(6*v zdAb_|&Hu%J$rn#c3r#{ z*S>!ln1FqpN;0PXHZYkvJpeGt+x$7`NuNLnWPI(Pt>x%>F9J#l~!Lxe)d!E1DkGDPE zn~Dm0vz({RCV%fx6TF~s<1fYU#&7mZ%TbwD`B=xXva*yy zaXe;PM37e~`>Y-t=qIYF0(98gaSb#4@|?72_M5fFC=OAL7K(G>2#2~gS2UnPj&KQF zF7#}t)#6rGI37`*GZQTk1$M9QvP!CgaKbg;n~f3%W@6?1N4i$$v(Sw&i!(tw-PaOk zzuVXS^%#$6Tt>}@8T6Ayj#ZY5#n2HOV{A3viZ`$A6$ig3Dt13~0QY*QJYb2-_-0u# zDud;f01vO<3JqVjE`aqSRM0%jb;nK4ePaf~PwBPy9$?UoQ7Th;eOE0_#CZ@X4+^od zlh~AtHhgKIku%TSIA;bH3&I6zOi>UrnW8aoD7yyu*>#1**YC(1R9Fhe7>-F|Gl9k~ zc^fbdLdJXuG;LY{Blrx1{2|vD;J%9Td(!WD`hh`6`88sCRr%H_BUoK~ zC<7VHFK1WUfaZsPYHJ{7LCBN8ZA$AL**c#hV9UatDdV#9zWt1vm4MtX#Hf@7HND2M&A5>np`g=N*;T4f*=S z9SqhPa@xUA6m%1Vj{zJXJbY#qN9oeYY?Gqtq1q_vTOGzt>KK zUbBAOuP|-QOg&8#Lyz?2OkZCf$@Ko(Zxrr2e+FRKw8Q7!)w|#7Ui~wny9acuH8I7{ zDW>ZAo!c_`T#USpw+UfUpE0*9)_K#GcH6hf3sW++a-SaFlVWzTq1A*Vvf9`g%B!F= z8jr1%9T1rX`|;mQGv|tRiIlt{XC=JG>O8UiG;{z}>hS$}1^o%^4Y)%uZg+By)ZYU@|-Y-ort0P%ZDFslT~f6j>FSN4B{2WqtM}~-r-?M)C1p##*DN9%GI<> zq*P1b-^JcS-=cYHIn$A=%)(pE&ULendGT**nDDqP(1=yZ(H*-4kzD3O6$RMb*#!N! zZdv(R;vWcBp$zEFaoBkd9WNGk0JZKi#h6M1>4#UjI7-@vvWm0*ER^M+``4$t9`I>jMHlFT# z*gsf=@%B%Vdj%9m`Hd-%690A+nrl8`Wyq+3ecn2fqY9jwSn7c>WlY4pY@=v~(zeC;Z)v zoWL}}`xzKIOHxr~Z$qZ1^TTZd;hu@_aKS^*&*wQ|1md~07Q=u|ay5VqM$P{m6nW)g z{yMw)D-U`S>6s~bx4!elx+7K1qCS9_&{)c{x|T>bS8sb%#(%dVEl~~sj?#$L8!1ixpp`M}**x@P8*d^+LS3~kPm;C>soH&HIaq7BnD! z>~w@S{x7aDcgX!B6{NGnb2P>es#UYi3CQVj;R*+gT8gHK!V_0-1D>7oMmuX2xnO8EDw-^-_h@Aw|!W8V$}^ppgqMqG>VJVrq$| zjV}HM^QWB9Af5Ga{ub3XAxf>x#QSaXOb7Dqbn`RzUVtppsrkCOLseyZE^L68D7JSg z}3J(70vozAx+~7G{JEZ6> z*+y2~c`S=)DN)V(U#HB_M&01EnfEUto&EZwWzni4EpX(_gmo~yw$AMt{tMJ^>#Mjx ziF*0lfP;bom$$i=gH@Sk9pFb<<)bVPNwz4gg@{ zeNZHt@26BN>C>WZ2^v!4<4;>9)M8=64|Wq)6ya*8Pdqi( z5wF*qF{yhD{HiubzHA15(~E6?+J8jYCSD=3HlMm znbLH?MVTrMGu*8~D(N=-`s%EhUElr+LmLm!bN$?hP&~JiY0YcG^-cbZth{O>`&=LSV z7<1DDi(gS{qt(?N{AsPu^c(9^Y`f@4T=R~&JVJnXM@NU*p<<1z?~uq98h-dZymEv! z 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 + const { isLoggedIn, servers, activeServerId } = useAuthStore(); + if (!isLoggedIn || !activeServerId || servers.length === 0) return ; return <>{children}; } diff --git a/src/api/subsonic.ts b/src/api/subsonic.ts index c7264dd3..ee7c5286 100644 --- a/src/api/subsonic.ts +++ b/src/api/subsonic.ts @@ -2,31 +2,38 @@ import axios from 'axios'; import md5 from 'md5'; import { useAuthStore } from '../store/authStore'; +// ─── Secure random salt ──────────────────────────────────────── +function secureRandomSalt(): string { + const buf = new Uint8Array(8); + crypto.getRandomValues(buf); + return Array.from(buf, b => b.toString(16).padStart(2, '0')).join(''); +} + // ─── Token Auth ─────────────────────────────────────────────── function getAuthParams(username: string, password: string) { - const salt = Math.random().toString(36).substring(2, 10); + const salt = secureRandomSalt(); const token = md5(password + salt); return { u: username, t: token, s: salt, v: '1.16.1', c: 'psysonic', f: 'json' }; } function getClient() { - const { getBaseUrl, username, password } = useAuthStore.getState(); + const { getBaseUrl, getActiveServer } = useAuthStore.getState(); + const server = getActiveServer(); const baseUrl = getBaseUrl(); - const params = getAuthParams(username, password); - - return { - baseUrl: `${baseUrl}/rest`, - params, - }; + if (!baseUrl) throw new Error('No server configured'); + const params = getAuthParams(server?.username ?? '', server?.password ?? ''); + return { baseUrl: `${baseUrl}/rest`, params }; } -async function api(endpoint: string, extra: Record = {}): Promise { +async function api(endpoint: string, extra: Record = {}, timeout = 15000): Promise { const { baseUrl, params } = getClient(); const resp = await axios.get(`${baseUrl}/${endpoint}`, { params: { ...params, ...extra }, - paramsSerializer: { indexes: null } + paramsSerializer: { indexes: null }, + timeout, }); - const data = resp.data['subsonic-response']; + const data = resp.data?.['subsonic-response']; + if (!data) throw new Error('Invalid response from server (possibly not a Subsonic server)'); if (data.status !== 'ok') throw new Error(data.error?.message ?? 'Subsonic API error'); return data as T; } @@ -59,11 +66,11 @@ export interface SubsonicSong { year?: number; userRating?: number; // Audio technical info - bitRate?: number; // kbps - suffix?: string; // mp3, flac, opus… - contentType?: string; // audio/mpeg, audio/flac… - size?: number; // bytes - samplingRate?: number; // Hz + bitRate?: number; + suffix?: string; + contentType?: string; + size?: number; + samplingRate?: number; channelCount?: number; starred?: string; } @@ -95,7 +102,7 @@ export interface SubsonicArtist { } export interface SubsonicGenre { - value: string; // The genre name is returned as "value" by subsonic + value: string; songCount: number; albumCount: number; } @@ -119,6 +126,24 @@ export async function ping(): Promise { } } +/** Test a connection with explicit credentials — does NOT depend on store state. */ +export async function pingWithCredentials(serverUrl: string, username: string, password: string): Promise { + try { + const base = serverUrl.startsWith('http') ? serverUrl.replace(/\/$/, '') : `http://${serverUrl.replace(/\/$/, '')}`; + const salt = secureRandomSalt(); + const token = md5(password + salt); + const resp = await axios.get(`${base}/rest/ping.view`, { + params: { u: username, t: token, s: salt, v: '1.16.1', c: 'psysonic', f: 'json' }, + paramsSerializer: { indexes: null }, + timeout: 15000, + }); + const data = resp.data?.['subsonic-response']; + return data?.status === 'ok'; + } catch { + return false; + } +} + export async function getRandomAlbums(size = 6): Promise { const data = await api<{ albumList2: { album: SubsonicAlbum[] } }>('getAlbumList2.view', { type: 'random', size }); return data.albumList2?.album ?? []; @@ -205,13 +230,8 @@ export async function getStarred(): Promise { song?: SubsonicSong[]; } }>('getStarred2.view'); - const r = data.starred2 ?? {}; - return { - artists: r.artist ?? [], - albums: r.album ?? [], - songs: r.song ?? [], - }; + return { artists: r.artist ?? [], albums: r.album ?? [], songs: r.song ?? [] }; } export async function star(id: string, type: 'song' | 'album' | 'artist' = 'album'): Promise { @@ -270,33 +290,44 @@ export async function reportNowPlaying(id: string): Promise { // ─── Stream URL ─────────────────────────────────────────────── export function buildStreamUrl(id: string): string { - const { getBaseUrl, username, password } = useAuthStore.getState(); + const { getBaseUrl, getActiveServer } = useAuthStore.getState(); + const server = getActiveServer(); const baseUrl = getBaseUrl(); - const { u, t, s, v, c, f } = (() => { - const salt = Math.random().toString(36).substring(2, 10); - const token = md5(password + salt); - return { u: username, t: token, s: salt, v: '1.16.1', c: 'psysonic', f: 'json' }; - })(); - - const p = new URLSearchParams({ id, u, t, s, v, c, f }); + const salt = secureRandomSalt(); + const token = md5((server?.password ?? '') + salt); + const p = new URLSearchParams({ + id, + u: server?.username ?? '', + t: token, s: salt, v: '1.16.1', c: 'psysonic', f: 'json', + }); return `${baseUrl}/rest/stream.view?${p.toString()}`; } export function buildCoverArtUrl(id: string, size = 256): string { - const { getBaseUrl, username, password } = useAuthStore.getState(); + const { getBaseUrl, getActiveServer } = useAuthStore.getState(); + const server = getActiveServer(); const baseUrl = getBaseUrl(); - const salt = Math.random().toString(36).substring(2, 10); - const token = md5(password + salt); - const p = new URLSearchParams({ id, size: String(size), u: username, t: token, s: salt, v: '1.16.1', c: 'psysonic', f: 'json' }); + const salt = secureRandomSalt(); + const token = md5((server?.password ?? '') + salt); + const p = new URLSearchParams({ + id, size: String(size), + u: server?.username ?? '', + t: token, s: salt, v: '1.16.1', c: 'psysonic', f: 'json', + }); return `${baseUrl}/rest/getCoverArt.view?${p.toString()}`; } export function buildDownloadUrl(id: string): string { - const { getBaseUrl, username, password } = useAuthStore.getState(); + const { getBaseUrl, getActiveServer } = useAuthStore.getState(); + const server = getActiveServer(); const baseUrl = getBaseUrl(); - const salt = Math.random().toString(36).substring(2, 10); - const token = md5(password + salt); - const p = new URLSearchParams({ id, u: username, t: token, s: salt, v: '1.16.1', c: 'psysonic', f: 'json' }); + const salt = secureRandomSalt(); + const token = md5((server?.password ?? '') + salt); + const p = new URLSearchParams({ + id, + u: server?.username ?? '', + t: token, s: salt, v: '1.16.1', c: 'psysonic', f: 'json', + }); return `${baseUrl}/rest/download.view?${p.toString()}`; } @@ -307,7 +338,6 @@ export async function getPlaylists(): Promise { } export async function getPlaylist(id: string): Promise<{ playlist: SubsonicPlaylist; songs: SubsonicSong[] }> { - // Songs inside a playlist are typically in the 'entry' array const data = await api<{ playlist: SubsonicPlaylist & { entry: SubsonicSong[] } }>('getPlaylist.view', { id }); const { entry, ...playlist } = data.playlist; return { playlist, songs: entry ?? [] }; @@ -330,11 +360,7 @@ export async function getPlayQueue(): Promise<{ current?: string; position?: num try { const data = await api<{ playQueue: { current?: string; position?: number; entry?: SubsonicSong[] } }>('getPlayQueue.view'); const pq = data.playQueue; - return { - current: pq?.current, - position: pq?.position, - songs: pq?.entry ?? [] - }; + return { current: pq?.current, position: pq?.position, songs: pq?.entry ?? [] }; } catch { return { songs: [] }; } @@ -342,12 +368,9 @@ export async function getPlayQueue(): Promise<{ current?: string; position?: num export async function savePlayQueue(songIds: string[], current?: string, position?: number): Promise { const params: Record = {}; - if (songIds.length > 0) { - params.id = songIds; - } + if (songIds.length > 0) params.id = songIds; if (current !== undefined) params.current = current; if (position !== undefined) params.position = position; - await api('savePlayQueue.view', params); } @@ -355,7 +378,6 @@ export async function savePlayQueue(songIds: string[], current?: string, positio export async function getNowPlaying(): Promise { try { const data = await api<{ nowPlaying: { entry?: SubsonicNowPlaying[] } | '' }>('getNowPlaying.view'); - // Navidrome might return an empty string or empty object if nobody is playing if (!data.nowPlaying || typeof data.nowPlaying === 'string') return []; return data.nowPlaying.entry ?? []; } catch { diff --git a/src/components/AlbumRow.tsx b/src/components/AlbumRow.tsx index 132f5556..f13243d6 100644 --- a/src/components/AlbumRow.tsx +++ b/src/components/AlbumRow.tsx @@ -3,6 +3,7 @@ import { SubsonicAlbum } from '../api/subsonic'; import AlbumCard from './AlbumCard'; import { ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; interface Props { title: string; @@ -13,6 +14,7 @@ interface Props { } export default function AlbumRow({ title, albums, moreLink, moreText, onLoadMore }: Props) { + const { t } = useTranslation(); const scrollRef = useRef(null); const navigate = useNavigate(); const [showLeft, setShowLeft] = useState(false); @@ -86,7 +88,7 @@ export default function AlbumRow({ title, albums, moreLink, moreText, onLoadMore
- Lädt... + {t('common.loadingMore')}
)} {!loadingMore && moreLink && ( @@ -94,7 +96,7 @@ export default function AlbumRow({ title, albums, moreLink, moreText, onLoadMore
- {moreText || 'Alle ansehen'} + {moreText} )} diff --git a/src/components/ArtistRow.tsx b/src/components/ArtistRow.tsx index 6eb44408..fa03c8a0 100644 --- a/src/components/ArtistRow.tsx +++ b/src/components/ArtistRow.tsx @@ -3,6 +3,7 @@ import { SubsonicArtist } from '../api/subsonic'; import ArtistCardLocal from './ArtistCardLocal'; import { ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; interface Props { title: string; @@ -13,6 +14,7 @@ interface Props { } export default function ArtistRow({ title, artists, moreLink, moreText, onLoadMore }: Props) { + const { t } = useTranslation(); const scrollRef = useRef(null); const navigate = useNavigate(); const [showLeft, setShowLeft] = useState(false); @@ -86,7 +88,7 @@ export default function ArtistRow({ title, artists, moreLink, moreText, onLoadMo
- Lädt... + {t('common.loadingMore')}
)} {!loadingMore && moreLink && ( @@ -94,7 +96,7 @@ export default function ArtistRow({ title, artists, moreLink, moreText, onLoadMo
- {moreText || 'Alle ansehen'} + {moreText} )} diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx index 72c32a15..87167fe7 100644 --- a/src/components/ContextMenu.tsx +++ b/src/components/ContextMenu.tsx @@ -7,13 +7,23 @@ import { useAuthStore } from '../store/authStore'; import { open } from '@tauri-apps/plugin-shell'; import { writeFile } from '@tauri-apps/plugin-fs'; import { join } from '@tauri-apps/api/path'; +import { useTranslation } from 'react-i18next'; + +function sanitizeFilename(name: string): string { + return name + .replace(/[/\\?%*:|"<>]/g, '-') + .replace(/\.{2,}/g, '.') + .replace(/^[\s.]+|[\s.]+$/g, '') + .substring(0, 200) || 'download'; +} export default function ContextMenu() { + const { t } = useTranslation(); const { contextMenu, closeContextMenu, playTrack, enqueue, queue, currentTrack, removeTrack } = usePlayerStore(); const auth = useAuthStore(); const navigate = useNavigate(); const menuRef = useRef(null); - + // Adjusted coordinates to keep menu on screen const [coords, setCoords] = useState({ x: 0, y: 0 }); @@ -28,37 +38,14 @@ export default function ContextMenu() { const rect = menuRef.current.getBoundingClientRect(); const winW = window.innerWidth; const winH = window.innerHeight; - let finalX = contextMenu.x; let finalY = contextMenu.y; - if (finalX + rect.width > winW) finalX = winW - rect.width - 10; if (finalY + rect.height > winH) finalY = winH - rect.height - 10; - setCoords({ x: finalX, y: finalY }); } }, [contextMenu.isOpen, contextMenu.x, contextMenu.y]); - useEffect(() => { - const handleClickOutside = (e: MouseEvent) => { - if (menuRef.current && !menuRef.current.contains(e.target as Node)) { - closeContextMenu(); - } - }; - const handleEsc = (e: KeyboardEvent) => { - if (e.key === 'Escape') closeContextMenu(); - }; - - if (contextMenu.isOpen) { - document.addEventListener('mousedown', handleClickOutside); - document.addEventListener('keydown', handleEsc); - } - return () => { - document.removeEventListener('mousedown', handleClickOutside); - document.removeEventListener('keydown', handleEsc); - }; - }, [contextMenu.isOpen, closeContextMenu]); - if (!contextMenu.isOpen || !contextMenu.item) return null; const { type, item, queueIndex } = contextMenu; @@ -91,139 +78,140 @@ export default function ContextMenu() { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const blob = await response.blob(); - + if (auth.downloadFolder) { const buffer = await blob.arrayBuffer(); - const path = await join(auth.downloadFolder, `${albumName}.zip`); + const path = await join(auth.downloadFolder, `${sanitizeFilename(albumName)}.zip`); await writeFile(path, new Uint8Array(buffer)); } else { const blobUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = blobUrl; - a.download = `${albumName}.zip`; + a.download = `${sanitizeFilename(albumName)}.zip`; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(() => URL.revokeObjectURL(blobUrl), 2000); } } catch (e) { - console.error('Download fehlgeschlagen:', e); + console.error('Download failed:', e); } }; - return ( -
- {(type === 'song' || type === 'album-song') && (() => { - const song = item as Track; - return ( - <> -
handleAction(() => playTrack(song, [song]))}> - Direkt abspielen -
-
handleAction(() => { - if (!currentTrack) { - playTrack(song, [song]); - return; - } - const currentIdx = usePlayerStore.getState().queueIndex; - const newQueue = [...queue]; - newQueue.splice(currentIdx + 1, 0, song); - usePlayerStore.setState({ queue: newQueue }); - })}> - Als Nächstes abspielen -
-
handleAction(() => enqueue([song]))}> - Zur Warteschlange hinzufügen -
- {type === 'album-song' && ( -
handleAction(async () => { - const albumData = await getAlbum(song.albumId); - const tracks = albumData.songs.map(s => ({ - id: s.id, title: s.title, artist: s.artist, album: s.album, - albumId: s.albumId, duration: s.duration, coverArt: s.coverArt, track: s.track, - year: s.year, bitRate: s.bitRate, suffix: s.suffix, userRating: s.userRating, - })); - enqueue(tracks); - })}> - Ganzes Album einreihen + <> + {/* Transparent backdrop — catches all outside clicks cleanly, preventing freeze */} +
closeContextMenu()} + /> +
+ {(type === 'song' || type === 'album-song') && (() => { + const song = item as Track; + return ( + <> +
handleAction(() => playTrack(song, [song]))}> + {t('contextMenu.playNow')}
- )} - -
- -
handleAction(() => startRadio(song.artist, song.artist))}> - Song-Radio starten -
-
handleAction(() => star(song.id, 'song'))}> - Favorisieren -
- - ); - })()} +
handleAction(() => { + if (!currentTrack) { + playTrack(song, [song]); + return; + } + const currentIdx = usePlayerStore.getState().queueIndex; + const newQueue = [...queue]; + newQueue.splice(currentIdx + 1, 0, song); + usePlayerStore.setState({ queue: newQueue }); + })}> + {t('contextMenu.playNext')} +
+
handleAction(() => enqueue([song]))}> + {t('contextMenu.addToQueue')} +
+ {type === 'album-song' && ( +
handleAction(async () => { + const albumData = await getAlbum(song.albumId); + const tracks = albumData.songs.map(s => ({ + id: s.id, title: s.title, artist: s.artist, album: s.album, + albumId: s.albumId, duration: s.duration, coverArt: s.coverArt, track: s.track, + year: s.year, bitRate: s.bitRate, suffix: s.suffix, userRating: s.userRating, + })); + enqueue(tracks); + })}> + {t('contextMenu.enqueueAlbum')} +
+ )} +
+
handleAction(() => startRadio(song.artist, song.artist))}> + {t('contextMenu.startRadio')} +
+
handleAction(() => star(song.id, 'song'))}> + {t('contextMenu.favorite')} +
+ + ); + })()} - {type === 'album' && (() => { - const album = item as SubsonicAlbum; - return ( - <> -
handleAction(() => { - // we don't have tracks here immediately, so we'd navigate or fetch. For now, navigate. - navigate(`/album/${album.id}`); - })}> - Album öffnen -
-
-
handleAction(() => navigate(`/artist/${album.artistId}`))}> - Zum Künstler -
-
handleAction(() => star(album.id, 'album'))}> - Album favorisieren -
-
handleAction(() => downloadAlbum(album.name, album.id))}> - Herunterladen (ZIP) -
- - ); - })()} + {type === 'album' && (() => { + const album = item as SubsonicAlbum; + return ( + <> +
handleAction(() => navigate(`/album/${album.id}`))}> + {t('contextMenu.openAlbum')} +
+
+
handleAction(() => navigate(`/artist/${album.artistId}`))}> + {t('contextMenu.goToArtist')} +
+
handleAction(() => star(album.id, 'album'))}> + {t('contextMenu.favoriteAlbum')} +
+
handleAction(() => downloadAlbum(album.name, album.id))}> + {t('contextMenu.download')} +
+ + ); + })()} - {type === 'artist' && (() => { - const artist = item as SubsonicArtist; - return ( - <> -
handleAction(() => startRadio(artist.id, artist.name))}> - Künstler-Radio starten -
-
-
handleAction(() => star(artist.id, 'artist'))}> - Künstler favorisieren -
- - ); - })()} + {type === 'artist' && (() => { + const artist = item as SubsonicArtist; + return ( + <> +
handleAction(() => startRadio(artist.id, artist.name))}> + {t('contextMenu.startRadio')} +
+
+
handleAction(() => star(artist.id, 'artist'))}> + {t('contextMenu.favoriteArtist')} +
+ + ); + })()} - {type === 'queue-item' && (() => { - const song = item as Track; - return ( - <> -
handleAction(() => playTrack(song, queue))}> - Direkt abspielen -
-
handleAction(() => { - if (queueIndex !== undefined) removeTrack(queueIndex); - })}> - Diesen Song entfernen -
-
-
handleAction(() => startRadio(song.artist, song.artist))}> - Song-Radio starten -
- - ); - })()} -
+ {type === 'queue-item' && (() => { + const song = item as Track; + return ( + <> +
handleAction(() => playTrack(song, queue))}> + {t('contextMenu.playNow')} +
+
handleAction(() => { + if (queueIndex !== undefined) removeTrack(queueIndex); + })}> + {t('contextMenu.removeFromQueue')} +
+
+
handleAction(() => startRadio(song.artist, song.artist))}> + {t('contextMenu.startRadio')} +
+ + ); + })()} +
+ ); } diff --git a/src/components/FullscreenPlayer.tsx b/src/components/FullscreenPlayer.tsx index b205534e..17571f2c 100644 --- a/src/components/FullscreenPlayer.tsx +++ b/src/components/FullscreenPlayer.tsx @@ -5,6 +5,7 @@ import { } from 'lucide-react'; import { usePlayerStore } from '../store/playerStore'; import { buildCoverArtUrl } from '../api/subsonic'; +import { useTranslation } from 'react-i18next'; function formatTime(seconds: number): string { if (!seconds || isNaN(seconds)) return '0:00'; @@ -71,7 +72,7 @@ const FsProgress = memo(function FsProgress({ duration }: { duration: number }) value={progress} onChange={handleSeek} style={{ '--pct': `${progress * 100}%` } as React.CSSProperties} - aria-label="Songfortschritt" + aria-label="progress" />
{formatTime(duration)} @@ -84,10 +85,11 @@ const FsProgress = memo(function FsProgress({ duration }: { duration: number }) // ─── Isolated play/pause button (subscribes to isPlaying only) ─── const FsPlayBtn = memo(function FsPlayBtn() { + const { t } = useTranslation(); const isPlaying = usePlayerStore(s => s.isPlaying); const togglePlay = usePlayerStore(s => s.togglePlay); return ( - ); @@ -98,6 +100,7 @@ interface FullscreenPlayerProps { } export default function FullscreenPlayer({ onClose }: FullscreenPlayerProps) { + const { t } = useTranslation(); // Static/slow-changing state only — does NOT subscribe to progress or currentTime const currentTrack = usePlayerStore(s => s.currentTrack); const repeatMode = usePlayerStore(s => s.repeatMode); @@ -121,13 +124,13 @@ export default function FullscreenPlayer({ onClose }: FullscreenPlayerProps) { }, [onClose]); return ( -
+
{/* Crossfading blurred background */}