feat: analysis providers, settings UI, song search, WAV duration fix

- Multi-provider AI analysis (Anthropic, OpenAI, Ollama, Algorithmic)
- server-only guards on all provider files; client bundle fix
- /settings page with provider status, Ollama model picker, preferences
- Song search box on /analyze replacing raw MBID input (debounced, keyboard nav)
- Auto-register song via MusicBrainz on POST /api/tracks (no more 404)
- Fix WAV duration bug: last section songEnd was double-counting elapsed time
- Registry sync comment updated for self-hosted HTTPS git servers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AJ Avezzano
2026-04-03 18:46:17 -04:00
parent 51f67f0aeb
commit 8b9d72bc9d
22 changed files with 1803 additions and 293 deletions

View File

@@ -0,0 +1,63 @@
"use client";
import { RECOMMENDED_OLLAMA_MODELS } from "@/lib/analysis/constants";
interface OllamaModelPickerProps {
models: string[];
value: string;
onChange: (model: string) => void;
onRefresh: () => void;
refreshing: boolean;
}
export default function OllamaModelPicker({
models,
value,
onChange,
onRefresh,
refreshing,
}: OllamaModelPickerProps) {
if (models.length === 0) {
return (
<p className="text-sm text-zinc-500">
No models found. Pull a model with{" "}
<code className="text-zinc-400">ollama pull qwen2.5:7b</code> and refresh.
</p>
);
}
return (
<div className="space-y-3">
<div className="flex flex-col gap-2">
{models.map((model) => {
const isRecommended = RECOMMENDED_OLLAMA_MODELS.includes(model);
return (
<label key={model} className="flex items-center gap-3 cursor-pointer group">
<input
type="radio"
name="ollama-model"
value={model}
checked={value === model}
onChange={() => onChange(model)}
className="accent-green-500"
/>
<span className="text-sm text-zinc-300 group-hover:text-zinc-100 transition-colors">
{model}
</span>
{isRecommended && (
<span className="text-xs text-amber-400"> recommended</span>
)}
</label>
);
})}
</div>
<button
onClick={onRefresh}
disabled={refreshing}
className="text-xs text-zinc-500 hover:text-zinc-300 underline disabled:opacity-50"
>
{refreshing ? "Refreshing…" : "Refresh model list"}
</button>
</div>
);
}