Added Deezer sounce for searching
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import type {
|
||||
MusicResult, YouTubeResult, DownloadRequest,
|
||||
MusicResult, MusicSource, YouTubeResult, DownloadRequest,
|
||||
DownloadStatus, Artist,
|
||||
} from './types';
|
||||
|
||||
const BASE = '/api';
|
||||
|
||||
export async function searchMusic(query: string): Promise<MusicResult[]> {
|
||||
const res = await fetch(`${BASE}/search/music?q=${encodeURIComponent(query)}`);
|
||||
export async function searchMusic(query: string, source: MusicSource = 'itunes'): Promise<MusicResult[]> {
|
||||
const res = await fetch(`${BASE}/search/music?q=${encodeURIComponent(query)}&source=${source}`);
|
||||
if (!res.ok) throw new Error(await res.text());
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export type MusicSource = 'itunes' | 'deezer';
|
||||
|
||||
export interface MusicResult {
|
||||
trackId: number;
|
||||
trackName: string;
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { Search, Loader2, Music } from 'lucide-react';
|
||||
import { searchMusic } from '../api/client';
|
||||
import type { MusicResult } from '../api/types';
|
||||
import type { MusicResult, MusicSource } from '../api/types';
|
||||
|
||||
interface Props {
|
||||
onSelect: (track: MusicResult) => void;
|
||||
}
|
||||
|
||||
const SOURCES: { value: MusicSource; label: string }[] = [
|
||||
{ value: 'itunes', label: 'iTunes' },
|
||||
{ value: 'deezer', label: 'Deezer' },
|
||||
];
|
||||
|
||||
export default function MusicSearch({ onSelect }: Props) {
|
||||
const [query, setQuery] = useState('');
|
||||
const [source, setSource] = useState<MusicSource>('itunes');
|
||||
const [results, setResults] = useState<MusicResult[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
async function handleSearch(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (!query.trim()) return;
|
||||
async function runSearch(q: string, src: MusicSource) {
|
||||
if (!q.trim()) return;
|
||||
setLoading(true);
|
||||
setError('');
|
||||
try {
|
||||
const data = await searchMusic(query.trim());
|
||||
const data = await searchMusic(q.trim(), src);
|
||||
setResults(data);
|
||||
if (data.length === 0) setError('No results found. Try a different search.');
|
||||
} catch {
|
||||
@@ -30,9 +35,30 @@ export default function MusicSearch({ onSelect }: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
runSearch(query, source);
|
||||
}
|
||||
|
||||
function handleSourceChange(e: React.ChangeEvent<HTMLSelectElement>) {
|
||||
const next = e.target.value as MusicSource;
|
||||
setSource(next);
|
||||
if (query.trim() && results.length > 0) runSearch(query, next);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={handleSearch} className="search-form">
|
||||
<select
|
||||
className="source-select"
|
||||
value={source}
|
||||
onChange={handleSourceChange}
|
||||
aria-label="Metadata source"
|
||||
>
|
||||
{SOURCES.map(s => (
|
||||
<option key={s.value} value={s.value}>{s.label}</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="search-input-wrap">
|
||||
<Search size={18} className="search-icon" />
|
||||
<input
|
||||
|
||||
@@ -145,6 +145,18 @@ button { font-family: inherit; cursor: pointer; }
|
||||
}
|
||||
.search-input:focus { border-color: var(--accent); }
|
||||
|
||||
.source-select {
|
||||
padding: 9px 10px;
|
||||
background: var(--surface2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
color: var(--text);
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
.source-select:focus { border-color: var(--accent); }
|
||||
|
||||
/* ── Buttons ──────────────────────────────────────────────────── */
|
||||
.btn-primary {
|
||||
display: inline-flex;
|
||||
|
||||
Reference in New Issue
Block a user