diff --git a/app.go b/app.go index 030de2a..c0d5dea 100644 --- a/app.go +++ b/app.go @@ -85,6 +85,42 @@ func containsStreamingURL(body []byte) bool { return isStreamingURL(trimmedBody) } +func containsLRCLIBResults(body []byte) bool { + trimmedBody := strings.TrimSpace(string(body)) + if trimmedBody == "" { + return false + } + + var searchResults []map[string]interface{} + if err := json.Unmarshal(body, &searchResults); err == nil { + return len(searchResults) > 0 + } + + var exactResult map[string]interface{} + if err := json.Unmarshal(body, &exactResult); err == nil { + return len(exactResult) > 0 + } + + return false +} + +func containsMusicBrainzResults(body []byte) bool { + trimmedBody := strings.TrimSpace(string(body)) + if trimmedBody == "" { + return false + } + + var payload struct { + Count int `json:"count"` + Recordings []json.RawMessage `json:"recordings"` + } + if err := json.Unmarshal(body, &payload); err != nil { + return false + } + + return payload.Count > 0 || len(payload.Recordings) > 0 +} + func isStreamingURL(raw string) bool { candidate := strings.TrimSpace(raw) if candidate == "" { @@ -948,6 +984,10 @@ func (a *App) CheckAPIStatus(apiType string, apiURL string) bool { checkURL = fmt.Sprintf("%s/api/track/360735657?quality=27", apiURL) } else if apiType == "amazon" { checkURL = fmt.Sprintf("%s/status", apiURL) + } else if apiType == "lrclib" { + checkURL = fmt.Sprintf("%s/api/search?artist_name=Adele&track_name=Hello", strings.TrimRight(apiURL, "/")) + } else if apiType == "musicbrainz" { + checkURL = fmt.Sprintf("%s/ws/2/recording?query=%s&fmt=json&limit=1", strings.TrimRight(apiURL, "/"), url.QueryEscape(`recording:"Hello" AND artist:"Adele"`)) } else { checkURL = apiURL } @@ -958,6 +998,7 @@ func (a *App) CheckAPIStatus(apiType string, apiURL string) bool { return false, err } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36") + req.Header.Set("Accept", "application/json") maxRetries := 3 for i := 0; i < maxRetries; i++ { @@ -981,7 +1022,15 @@ func (a *App) CheckAPIStatus(apiType string, apiURL string) bool { return true, nil } - if apiType != "amazon" && apiType != "qobuz" && apiType != "qbz" && statusCode == 200 { + if apiType == "lrclib" && statusCode == 200 && containsLRCLIBResults(body) { + return true, nil + } + + if apiType == "musicbrainz" && statusCode == 200 && containsMusicBrainzResults(body) { + return true, nil + } + + if apiType != "amazon" && apiType != "qobuz" && apiType != "qbz" && apiType != "lrclib" && apiType != "musicbrainz" && statusCode == 200 { return true, nil } } diff --git a/frontend/src/assets/icons/amazon-music.png b/frontend/src/assets/icons/amazon-music.png deleted file mode 100644 index 92d42cf..0000000 Binary files a/frontend/src/assets/icons/amazon-music.png and /dev/null differ diff --git a/frontend/src/assets/icons/amzn.png b/frontend/src/assets/icons/amzn.png new file mode 100644 index 0000000..e138846 Binary files /dev/null and b/frontend/src/assets/icons/amzn.png differ diff --git a/frontend/src/assets/icons/lrclib.png b/frontend/src/assets/icons/lrclib.png new file mode 100644 index 0000000..ac3e5e7 Binary files /dev/null and b/frontend/src/assets/icons/lrclib.png differ diff --git a/frontend/src/assets/icons/musicbrainz_d.png b/frontend/src/assets/icons/musicbrainz_d.png new file mode 100644 index 0000000..7467b95 Binary files /dev/null and b/frontend/src/assets/icons/musicbrainz_d.png differ diff --git a/frontend/src/assets/icons/musicbrainz_l.png b/frontend/src/assets/icons/musicbrainz_l.png new file mode 100644 index 0000000..1b864f7 Binary files /dev/null and b/frontend/src/assets/icons/musicbrainz_l.png differ diff --git a/frontend/src/assets/icons/qbz.png b/frontend/src/assets/icons/qbz.png new file mode 100644 index 0000000..a6eb75e Binary files /dev/null and b/frontend/src/assets/icons/qbz.png differ diff --git a/frontend/src/assets/icons/qobuz.png b/frontend/src/assets/icons/qobuz.png deleted file mode 100644 index d4a3be1..0000000 Binary files a/frontend/src/assets/icons/qobuz.png and /dev/null differ diff --git a/frontend/src/assets/icons/songlink.ico b/frontend/src/assets/icons/songlink.ico deleted file mode 100644 index 4fdec81..0000000 Binary files a/frontend/src/assets/icons/songlink.ico and /dev/null differ diff --git a/frontend/src/assets/icons/songlink_d.png b/frontend/src/assets/icons/songlink_d.png new file mode 100644 index 0000000..b988734 Binary files /dev/null and b/frontend/src/assets/icons/songlink_d.png differ diff --git a/frontend/src/assets/icons/songlink_l.png b/frontend/src/assets/icons/songlink_l.png new file mode 100644 index 0000000..fd0cb1a Binary files /dev/null and b/frontend/src/assets/icons/songlink_l.png differ diff --git a/frontend/src/assets/icons/songstats.png b/frontend/src/assets/icons/songstats.png index fc8a223..ae111fc 100644 Binary files a/frontend/src/assets/icons/songstats.png and b/frontend/src/assets/icons/songstats.png differ diff --git a/frontend/src/assets/icons/tidal.png b/frontend/src/assets/icons/tidal.png deleted file mode 100644 index 141e014..0000000 Binary files a/frontend/src/assets/icons/tidal.png and /dev/null differ diff --git a/frontend/src/assets/icons/tidal_d.png b/frontend/src/assets/icons/tidal_d.png new file mode 100644 index 0000000..4760bfa Binary files /dev/null and b/frontend/src/assets/icons/tidal_d.png differ diff --git a/frontend/src/assets/icons/tidal_l.png b/frontend/src/assets/icons/tidal_l.png new file mode 100644 index 0000000..7397386 Binary files /dev/null and b/frontend/src/assets/icons/tidal_l.png differ diff --git a/frontend/src/components/ApiStatusTab.tsx b/frontend/src/components/ApiStatusTab.tsx index d1bb34f..674ccd4 100644 --- a/frontend/src/components/ApiStatusTab.tsx +++ b/frontend/src/components/ApiStatusTab.tsx @@ -1,6 +1,6 @@ import { Button } from "@/components/ui/button"; import { RefreshCw, CheckCircle2, XCircle, Loader2 } from "lucide-react"; -import { TidalIcon, QobuzIcon, AmazonIcon } from "./PlatformIcons"; +import { TidalIcon, QobuzIcon, AmazonIcon, LrclibIcon, MusicBrainzIcon } from "./PlatformIcons"; import { useApiStatus } from "@/hooks/useApiStatus"; export function ApiStatusTab() { const { sources, statuses, isCheckingAll, refreshAll } = useApiStatus(); @@ -12,12 +12,12 @@ export function ApiStatusTab() { -
+
{sources.map((source) => { const status = statuses[source.id] || "idle"; return (
- {source.type === "tidal" ? : source.type === "amazon" ? : } + {source.type === "tidal" ? : source.type === "amazon" ? : source.type === "lrclib" ? : source.type === "musicbrainz" ? : }

{source.name}

diff --git a/frontend/src/components/PlatformIcons.tsx b/frontend/src/components/PlatformIcons.tsx index 3e98ea0..e944837 100644 --- a/frontend/src/components/PlatformIcons.tsx +++ b/frontend/src/components/PlatformIcons.tsx @@ -1,11 +1,13 @@ -import amazonMusicIcon from "../assets/icons/amazon-music.png"; -import qobuzIcon from "../assets/icons/qobuz.png"; -import tidalIcon from "../assets/icons/tidal.png"; -const PLATFORM_ICON_URLS = { - tidal: tidalIcon, - qobuz: qobuzIcon, - amazon: amazonMusicIcon, -} as const; +import amazonMusicIcon from "../assets/icons/amzn.png"; +import lrclibIcon from "../assets/icons/lrclib.png"; +import musicBrainzDarkIcon from "../assets/icons/musicbrainz_d.png"; +import musicBrainzLightIcon from "../assets/icons/musicbrainz_l.png"; +import qobuzIcon from "../assets/icons/qbz.png"; +import songlinkDarkIcon from "../assets/icons/songlink_d.png"; +import songlinkLightIcon from "../assets/icons/songlink_l.png"; +import songstatsIcon from "../assets/icons/songstats.png"; +import tidalDarkIcon from "../assets/icons/tidal_d.png"; +import tidalLightIcon from "../assets/icons/tidal_l.png"; type PlatformIconProps = { className?: string; }; @@ -48,14 +50,48 @@ function PlatformIcon({ src, alt, className = "w-4 h-4", defaultClassName = "" } .join(" "); return {alt}; } +function ThemedPlatformIcon({ lightSrc, darkSrc, alt, className = "w-4 h-4", defaultClassName = "" }: { + lightSrc: string; + darkSrc: string; + alt: string; + className?: string; + defaultClassName?: string; +}) { + const cleanedClassName = sanitizeClassName(className); + const statusClasses = getStatusClasses(className); + const wrapperClassName = [ + cleanedClassName || "w-4 h-4", + "relative inline-flex shrink-0", + !hasRoundedClass(cleanedClassName) ? defaultClassName : "", + statusClasses, + ] + .filter(Boolean) + .join(" "); + return + + + ; +} export function TidalIcon({ className = "w-4 h-4" }: PlatformIconProps) { - return ; + return ; } export function QobuzIcon({ className = "w-4 h-4" }: PlatformIconProps) { - return ; + return ; } export function AmazonIcon({ className = "w-4 h-4" }: PlatformIconProps) { - return ; + return ; +} +export function LrclibIcon({ className = "w-4 h-4" }: PlatformIconProps) { + return ; +} +export function MusicBrainzIcon({ className = "w-4 h-4" }: PlatformIconProps) { + return ; +} +export function SonglinkIcon({ className = "w-4 h-4" }: PlatformIconProps) { + return ; +} +export function SongstatsIcon({ className = "w-4 h-4" }: PlatformIconProps) { + return ; } export function TidalAvailabilityIcon({ className = "w-4 h-4" }: PlatformIconProps) { return diff --git a/frontend/src/components/SettingsPage.tsx b/frontend/src/components/SettingsPage.tsx index d8aba2c..04e01e7 100644 --- a/frontend/src/components/SettingsPage.tsx +++ b/frontend/src/components/SettingsPage.tsx @@ -13,9 +13,7 @@ import { themes, applyTheme } from "@/lib/themes"; import { SelectFolder, OpenConfigFolder } from "../../wailsjs/go/main/App"; import { toastWithSound as toast } from "@/lib/toast-with-sound"; import { ApiStatusTab } from "./ApiStatusTab"; -import { AmazonIcon, QobuzIcon, TidalIcon } from "./PlatformIcons"; -import songlinkIcon from "@/assets/icons/songlink.ico"; -import songstatsIcon from "@/assets/icons/songstats.png"; +import { AmazonIcon, QobuzIcon, SonglinkIcon, SongstatsIcon, TidalIcon } from "./PlatformIcons"; interface SettingsPageProps { onUnsavedChangesChange?: (hasUnsavedChanges: boolean) => void; onResetRequest?: (resetFn: () => void) => void; @@ -245,13 +243,13 @@ export function SettingsPage({ onUnsavedChangesChange, onResetRequest, }: Settin - Songlink + Songlink - Songstats + Songstats diff --git a/frontend/src/lib/api-status.ts b/frontend/src/lib/api-status.ts index 379e886..ba80ccb 100644 --- a/frontend/src/lib/api-status.ts +++ b/frontend/src/lib/api-status.ts @@ -19,6 +19,8 @@ export const API_SOURCES: ApiSource[] = [ { id: "qobuz2", type: "qobuz", name: "Qobuz B", url: "https://dabmusic.xyz" }, { id: "qobuz3", type: "qbz", name: "Qobuz C", url: "https://qbz.afkarxyz.qzz.io" }, { id: "amazon1", type: "amazon", name: "Amazon Music", url: "https://amzn.afkarxyz.qzz.io" }, + { id: "lrclib", type: "lrclib", name: "LRCLIB", url: "https://lrclib.net" }, + { id: "musicbrainz", type: "musicbrainz", name: "MusicBrainz", url: "https://musicbrainz.org" }, ]; type ApiStatusState = { isCheckingAll: boolean;