This commit is contained in:
afkarxyz
2026-04-14 07:28:39 +07:00
parent ce1e6cc65a
commit a9c52e7b6d
10 changed files with 35 additions and 58 deletions
+5 -26
View File
@@ -2,19 +2,16 @@ import type { ReactNode } from "react";
import type { TrackAvailability } from "@/types/api"; import type { TrackAvailability } from "@/types/api";
import { openExternal } from "@/lib/utils"; import { openExternal } from "@/lib/utils";
import { AmazonAvailabilityIcon, QobuzAvailabilityIcon, TidalAvailabilityIcon } from "./PlatformIcons"; import { AmazonAvailabilityIcon, QobuzAvailabilityIcon, TidalAvailabilityIcon } from "./PlatformIcons";
interface AvailabilityLinkEntry { interface AvailabilityLinkEntry {
id: string; id: string;
found: boolean; found: boolean;
url?: string; url?: string;
icon: ReactNode; icon: ReactNode;
} }
function getAvailabilityLinkEntries(availability: TrackAvailability): AvailabilityLinkEntry[] { function getAvailabilityLinkEntries(availability: TrackAvailability): AvailabilityLinkEntry[] {
const tidalUrl = availability.tidal_url?.trim() || ""; const tidalUrl = availability.tidal_url?.trim() || "";
const qobuzUrl = availability.qobuz_url?.trim() || ""; const qobuzUrl = availability.qobuz_url?.trim() || "";
const amazonUrl = availability.amazon_url?.trim() || ""; const amazonUrl = availability.amazon_url?.trim() || "";
return [ return [
{ {
id: "tidal", id: "tidal",
@@ -36,48 +33,30 @@ function getAvailabilityLinkEntries(availability: TrackAvailability): Availabili
}, },
]; ];
} }
export function hasAvailabilityLinks(availability?: TrackAvailability): boolean { export function hasAvailabilityLinks(availability?: TrackAvailability): boolean {
if (!availability) { if (!availability) {
return false; return false;
} }
return getAvailabilityLinkEntries(availability).some((entry) => entry.found); return getAvailabilityLinkEntries(availability).some((entry) => entry.found);
} }
export function AvailabilityLinks({ availability }: { export function AvailabilityLinks({ availability }: {
availability?: TrackAvailability; availability?: TrackAvailability;
}) { }) {
if (!availability) { if (!availability) {
return <p>Check Availability</p>; return <p>Check Availability</p>;
} }
const entries = getAvailabilityLinkEntries(availability); const entries = getAvailabilityLinkEntries(availability);
return ( return (<div className="flex flex-col gap-1.5 w-[260px] max-w-[260px] pointer-events-auto">
<div className="flex flex-col gap-1.5 w-[260px] max-w-[260px] pointer-events-auto"> {entries.map((entry) => entry.found ? (<button key={entry.id} type="button" onClick={() => entry.url && openExternal(entry.url)} className="flex items-center gap-2 text-left text-xs hover:underline min-w-0 cursor-pointer" title={entry.url}>
{entries.map((entry) => entry.found ? (
<button
key={entry.id}
type="button"
onClick={() => entry.url && openExternal(entry.url)}
className="flex items-center gap-2 text-left text-xs hover:underline min-w-0 cursor-pointer"
title={entry.url}
>
{entry.icon} {entry.icon}
<span className="truncate whitespace-nowrap leading-5 min-w-0"> <span className="truncate whitespace-nowrap leading-5 min-w-0">
{entry.url} {entry.url}
</span> </span>
</button> </button>) : (<div key={entry.id} className="flex items-center gap-2 text-left text-xs min-w-0">
) : (
<div
key={entry.id}
className="flex items-center gap-2 text-left text-xs min-w-0"
>
{entry.icon} {entry.icon}
<span className="truncate whitespace-nowrap leading-5 min-w-0 text-red-500"> <span className="truncate whitespace-nowrap leading-5 min-w-0 text-red-500">
Not Found Not Found
</span> </span>
</div> </div>))}
))} </div>);
</div>
);
} }
-2
View File
@@ -5,7 +5,6 @@ import { fetchCurrentIPInfo } from "@/lib/api";
import type { CurrentIPInfo } from "@/types/api"; import type { CurrentIPInfo } from "@/types/api";
import { openExternal } from "@/lib/utils"; import { openExternal } from "@/lib/utils";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
const IP_INFO_REFRESH_INTERVAL_MS = 30000; const IP_INFO_REFRESH_INTERVAL_MS = 30000;
const SPOTIFY_BLOCKED_COUNTRY_CODES = new Set([ const SPOTIFY_BLOCKED_COUNTRY_CODES = new Set([
"AF", "AF",
@@ -25,7 +24,6 @@ const SPOTIFY_BLOCKED_COUNTRY_CODES = new Set([
"TM", "TM",
"YE", "YE",
]); ]);
export function TitleBar() { export function TitleBar() {
const [currentIPInfo, setCurrentIPInfo] = useState<CurrentIPInfo | null>(null); const [currentIPInfo, setCurrentIPInfo] = useState<CurrentIPInfo | null>(null);
const [isLoadingCurrentIPInfo, setIsLoadingCurrentIPInfo] = useState(false); const [isLoadingCurrentIPInfo, setIsLoadingCurrentIPInfo] = useState(false);
+4 -2
View File
@@ -33,8 +33,10 @@ const CheckFilesExistence = (outputDir: string, rootDir: string, tracks: CheckFi
const SkipDownloadItem = (itemID: string, filePath: string): Promise<void> => (window as any)["go"]["main"]["App"]["SkipDownloadItem"](itemID, filePath); const SkipDownloadItem = (itemID: string, filePath: string): Promise<void> => (window as any)["go"]["main"]["App"]["SkipDownloadItem"](itemID, filePath);
const CreateM3U8File = (playlistName: string, outputDir: string, filePaths: string[]): Promise<void> => (window as any)["go"]["main"]["App"]["CreateM3U8File"](playlistName, outputDir, filePaths); const CreateM3U8File = (playlistName: string, outputDir: string, filePaths: string[]): Promise<void> => (window as any)["go"]["main"]["App"]["CreateM3U8File"](playlistName, outputDir, filePaths);
const GetTrackISRC = (spotifyId: string): Promise<string> => (window as any)["go"]["main"]["App"]["GetTrackISRC"](spotifyId); const GetTrackISRC = (spotifyId: string): Promise<string> => (window as any)["go"]["main"]["App"]["GetTrackISRC"](spotifyId);
async function resolveTemplateISRC(settings: {
async function resolveTemplateISRC(settings: { folderTemplate?: string; filenameTemplate?: string }, spotifyId?: string): Promise<string> { folderTemplate?: string;
filenameTemplate?: string;
}, spotifyId?: string): Promise<string> {
if (!spotifyId) { if (!spotifyId) {
return ""; return "";
} }
+4 -2
View File
@@ -6,8 +6,10 @@ import { joinPath, sanitizePath, getFirstArtist } from "@/lib/utils";
import { logger } from "@/lib/logger"; import { logger } from "@/lib/logger";
import type { TrackMetadata } from "@/types/api"; import type { TrackMetadata } from "@/types/api";
const GetTrackISRC = (spotifyId: string): Promise<string> => (window as any)["go"]["main"]["App"]["GetTrackISRC"](spotifyId); const GetTrackISRC = (spotifyId: string): Promise<string> => (window as any)["go"]["main"]["App"]["GetTrackISRC"](spotifyId);
async function resolveTemplateISRC(settings: {
async function resolveTemplateISRC(settings: { folderTemplate?: string; filenameTemplate?: string }, spotifyId?: string): Promise<string> { folderTemplate?: string;
filenameTemplate?: string;
}, spotifyId?: string): Promise<string> {
if (!spotifyId) { if (!spotifyId) {
return ""; return "";
} }
-1
View File
@@ -32,7 +32,6 @@ let apiStatusState: ApiStatusState = {
}; };
let activeCheckAll: Promise<void> | null = null; let activeCheckAll: Promise<void> | null = null;
const listeners = new Set<() => void>(); const listeners = new Set<() => void>();
type SpotiFLACUnifiedStatusResponse = { type SpotiFLACUnifiedStatusResponse = {
tidal?: string; tidal?: string;
qobuz_a?: string; qobuz_a?: string;
-3
View File
@@ -1,11 +1,9 @@
import type { ArtistSimple } from "@/types/api"; import type { ArtistSimple } from "@/types/api";
export interface ClickableArtist { export interface ClickableArtist {
id: string; id: string;
name: string; name: string;
external_urls: string; external_urls: string;
} }
export function splitArtistNames(value: string): string[] { export function splitArtistNames(value: string): string[] {
const trimmed = value.trim(); const trimmed = value.trim();
if (!trimmed) { if (!trimmed) {
@@ -14,7 +12,6 @@ export function splitArtistNames(value: string): string[] {
const parts = trimmed.split(/\s*[;,]\s*/).map((part) => part.trim()).filter(Boolean); const parts = trimmed.split(/\s*[;,]\s*/).map((part) => part.trim()).filter(Boolean);
return parts.length > 0 ? parts : [trimmed]; return parts.length > 0 ? parts : [trimmed];
} }
export function buildClickableArtists(artists: string, artistsData?: ArtistSimple[], fallbackArtistId?: string, fallbackArtistUrl?: string): ClickableArtist[] { export function buildClickableArtists(artists: string, artistsData?: ArtistSimple[], fallbackArtistId?: string, fallbackArtistUrl?: string): ClickableArtist[] {
const names = splitArtistNames(artists); const names = splitArtistNames(artists);
if (names.length === 0) { if (names.length === 0) {