import { useEffect } from "react"; import { useForm, Controller } from "react-hook-form"; import { authApiClient } from "../../lib/api-client"; import { toast } from "sonner"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; // --- Type Definitions --- interface SpotifyApiSettings { client_id: string; client_secret: string; } interface WebhookSettings { url: string; events: string[]; available_events: string[]; // Provided by API, not saved } // --- API Functions --- const fetchSpotifyApiConfig = async (): Promise => { const { data } = await authApiClient.client.get("/credentials/spotify_api_config"); return data; }; const saveSpotifyApiConfig = (data: SpotifyApiSettings) => authApiClient.client.put("/credentials/spotify_api_config", data); const fetchWebhookConfig = async (): Promise => { // Mock a response since backend endpoint doesn't exist // This will prevent the UI from crashing. return Promise.resolve({ url: "", events: [], available_events: ["download_start", "download_complete", "download_failed", "watch_added"], }); }; const saveWebhookConfig = (data: Partial) => { toast.info("Webhook configuration is not available."); return Promise.resolve(data); }; const testWebhook = (url: string) => { toast.info("Webhook testing is not available."); return Promise.resolve(url); }; // --- Components --- function SpotifyApiForm() { const queryClient = useQueryClient(); const { data, isLoading } = useQuery({ queryKey: ["spotifyApiConfig"], queryFn: fetchSpotifyApiConfig }); const { register, handleSubmit, reset } = useForm(); const mutation = useMutation({ mutationFn: saveSpotifyApiConfig, onSuccess: () => { toast.success("Spotify API settings saved!"); queryClient.invalidateQueries({ queryKey: ["spotifyApiConfig"] }); }, onError: (e) => { console.error("Failed to save Spotify API settings:", (e as any).message); toast.error(`Failed to save: ${(e as any).message}`); }, }); useEffect(() => { if (data) reset(data); }, [data, reset]); const onSubmit = (formData: SpotifyApiSettings) => mutation.mutate(formData); if (isLoading) return

Loading Spotify API settings...

; return (
); } function UtilityConcurrencyForm() { const queryClient = useQueryClient(); const { data: configData, isLoading } = useQuery({ queryKey: ["config"], queryFn: () => authApiClient.getConfig(), }); const { register, handleSubmit, reset, formState: { isDirty } } = useForm<{ utilityConcurrency: number }>(); useEffect(() => { if (configData) { reset({ utilityConcurrency: Number(configData.utilityConcurrency ?? 1) }); } }, [configData, reset]); const mutation = useMutation({ mutationFn: (payload: { utilityConcurrency: number }) => authApiClient.updateConfig(payload), onSuccess: () => { toast.success("Utility worker concurrency saved!"); queryClient.invalidateQueries({ queryKey: ["config"] }); }, onError: (e) => { toast.error(`Failed to save: ${(e as any).message}`); }, }); const onSubmit = (values: { utilityConcurrency: number }) => { const value = Math.max(1, Number(values.utilityConcurrency || 1)); mutation.mutate({ utilityConcurrency: value }); }; if (isLoading) return

Loading server settings...

; return (

Controls concurrency of the utility Celery worker. Minimum 1.

); } function LibrespotConcurrencyForm() { const queryClient = useQueryClient(); const { data: configData, isLoading } = useQuery({ queryKey: ["config"], queryFn: () => authApiClient.getConfig(), }); const { register, handleSubmit, reset, formState: { isDirty } } = useForm<{ librespotConcurrency: number }>(); useEffect(() => { if (configData) { reset({ librespotConcurrency: Number(configData.librespotConcurrency ?? 2) }); } }, [configData, reset]); const mutation = useMutation({ mutationFn: (payload: { librespotConcurrency: number }) => authApiClient.updateConfig(payload), onSuccess: () => { toast.success("Librespot concurrency saved!"); queryClient.invalidateQueries({ queryKey: ["config"] }); }, onError: (e) => { toast.error(`Failed to save: ${(e as any).message}`); }, }); const onSubmit = (values: { librespotConcurrency: number }) => { const raw = Number(values.librespotConcurrency || 2); const safe = Math.max(1, Math.min(16, raw)); mutation.mutate({ librespotConcurrency: safe }); }; if (isLoading) return

Loading server settings...

; return (

Controls worker threads used by the Librespot client. 1–16 is recommended.

); } // --- Components --- function WebhookForm() { const queryClient = useQueryClient(); const { data, isLoading } = useQuery({ queryKey: ["webhookConfig"], queryFn: fetchWebhookConfig }); const { register, handleSubmit, control, reset, watch } = useForm(); const currentUrl = watch("url"); const mutation = useMutation({ mutationFn: saveWebhookConfig, onSuccess: () => { // No toast needed since the function shows one queryClient.invalidateQueries({ queryKey: ["webhookConfig"] }); }, onError: (e) => { toast.error(`Failed to save: ${(e as any).message}`); }, }); const testMutation = useMutation({ mutationFn: testWebhook, onSuccess: () => { // No toast needed }, onError: (e) => toast.error(`Webhook test failed: ${(e as any).message}`), }); useEffect(() => { if (data) reset(data); }, [data, reset]); const onSubmit = (formData: WebhookSettings) => mutation.mutate(formData); if (isLoading) return

Loading Webhook settings...

; return (
{data?.available_events.map((event) => ( ( )} /> ))}
); } export function ServerTab() { return (

Spotify API

Provide your own API credentials to avoid rate-limiting issues.


Utility Worker

Tune background utility worker concurrency for low-powered systems.


Librespot

Adjust Librespot client worker threads.


Webhooks

Get notifications for events like download completion. (Currently disabled)

); }