159 lines
6.5 KiB
TypeScript
159 lines
6.5 KiB
TypeScript
import { Playlist, ServerType, ApiResponse, PlexServerConnection, PlexConnectionSettings, PlexLibrary, RegexReplacement, SyncStrategy } from '../types';
|
|
|
|
const API_BASE = import.meta.env.VITE_API_BASE_URL || '';
|
|
|
|
const MODE_TO_STRATEGY: Record<string, SyncStrategy> = {
|
|
local_force: SyncStrategy.LOCAL_OVERWRITE,
|
|
remote_force: SyncStrategy.CLOUD_OVERWRITE,
|
|
merge_local_primary: SyncStrategy.MERGE_LOCAL,
|
|
merge_remote_primary: SyncStrategy.MERGE_CLOUD,
|
|
};
|
|
|
|
const STRATEGY_TO_MODE: Record<SyncStrategy, string> = {
|
|
[SyncStrategy.LOCAL_OVERWRITE]: 'local_force',
|
|
[SyncStrategy.CLOUD_OVERWRITE]: 'remote_force',
|
|
[SyncStrategy.MERGE_LOCAL]: 'merge_local_primary',
|
|
[SyncStrategy.MERGE_CLOUD]: 'merge_remote_primary',
|
|
};
|
|
|
|
const handleResponse = async <T>(response: Response): Promise<ApiResponse<T>> => {
|
|
try {
|
|
const data = await response.json();
|
|
if (!response.ok) {
|
|
return { data: data as T, status: 'error', message: (data as any)?.detail || response.statusText };
|
|
}
|
|
return { data, status: 'success' };
|
|
} catch (error: any) {
|
|
return { data: {} as T, status: 'error', message: error?.message || 'Unexpected error' };
|
|
}
|
|
};
|
|
|
|
const mapPlaylist = (item: any): Playlist => ({
|
|
id: item.id || `${item.title}-${item.trackCount}`,
|
|
title: item.title ?? item.name ?? 'Unknown',
|
|
trackCount: item.trackCount ?? item.track_count ?? 0,
|
|
lastUpdated: item.lastUpdated || item.last_updated || new Date().toISOString(),
|
|
});
|
|
|
|
const mapLibrary = (item: any): PlexLibrary => ({
|
|
id: item.id ?? item.title,
|
|
title: item.title ?? item.id,
|
|
type: item.type ?? 'artist',
|
|
});
|
|
|
|
const mapRegexRules = (rules: any[]): RegexReplacement[] =>
|
|
(rules || []).map((rule, index) => ({
|
|
id: rule.id || `${rule.pattern || 'rule'}-${index}`,
|
|
pattern: rule.pattern || '',
|
|
replacement: rule.replacement || '',
|
|
}));
|
|
|
|
export const apiService = {
|
|
async getSettings(): Promise<ApiResponse<{ strategy: SyncStrategy; regex: RegexReplacement[]; connection: PlexConnectionSettings; localPath: string }>> {
|
|
const response = await fetch(`${API_BASE}/api/settings`);
|
|
const result = await handleResponse<any>(response);
|
|
if (result.status === 'success') {
|
|
const mode = result.data.sync_mode as string;
|
|
const strategy = MODE_TO_STRATEGY[mode] || SyncStrategy.LOCAL_OVERWRITE;
|
|
const regex = mapRegexRules(result.data.path_rules || []);
|
|
const connection: PlexConnectionSettings = {
|
|
protocol: (result.data.scheme as 'http' | 'https') || 'https',
|
|
address: result.data.server_url || '',
|
|
port: result.data.port || '32400',
|
|
token: result.data.token || '',
|
|
libraryName: result.data.library_name || '',
|
|
};
|
|
return { status: 'success', data: { strategy, regex, connection, localPath: result.data.local_path || '' } };
|
|
}
|
|
return result as ApiResponse<any> as ApiResponse<{ strategy: SyncStrategy; regex: RegexReplacement[]; connection: PlexConnectionSettings; localPath: string }>;
|
|
},
|
|
|
|
async updateSyncStrategy(strategy: SyncStrategy): Promise<ApiResponse<{ sync_mode: string }>> {
|
|
const payload = { mode: STRATEGY_TO_MODE[strategy] };
|
|
const response = await fetch(`${API_BASE}/api/settings/sync-mode`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
return handleResponse(response);
|
|
},
|
|
|
|
async saveRegexRules(replacements: RegexReplacement[]): Promise<ApiResponse<{ rules: RegexReplacement[] }>> {
|
|
const payload = { rules: replacements.map(({ pattern, replacement }) => ({ pattern, replacement })) };
|
|
const response = await fetch(`${API_BASE}/api/settings/regex-rules`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
return handleResponse(response);
|
|
},
|
|
|
|
async updateLibrary(libraryName: string): Promise<ApiResponse<{ library_name: string }>> {
|
|
const response = await fetch(`${API_BASE}/api/settings/library`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ library_name: libraryName }),
|
|
});
|
|
return handleResponse(response);
|
|
},
|
|
|
|
async getPlaylists(serverType: ServerType, signal?: AbortSignal, localPath?: string): Promise<ApiResponse<Playlist[]>> {
|
|
const params = new URLSearchParams({ server: serverType.toLowerCase() });
|
|
if (serverType === ServerType.LOCAL && localPath) {
|
|
params.append('local_path', localPath);
|
|
}
|
|
const response = await fetch(`${API_BASE}/api/playlists?${params.toString()}`, { signal });
|
|
const result = await handleResponse<any>(response);
|
|
if (result.status === 'success' && (result.data as any)?.playlists) {
|
|
return { data: (result.data.playlists as any[]).map(mapPlaylist), status: 'success' };
|
|
}
|
|
return result as ApiResponse<Playlist[]>;
|
|
},
|
|
|
|
async getServerStatus(signal?: AbortSignal): Promise<ApiResponse<PlexServerConnection>> {
|
|
const response = await fetch(`${API_BASE}/api/server`, { signal });
|
|
const result = await handleResponse<any>(response);
|
|
if (result.status === 'success') {
|
|
const info = result.data.serverInfo || {};
|
|
const libraries: PlexLibrary[] = (result.data.libraries || []).map(mapLibrary);
|
|
return {
|
|
status: 'success',
|
|
data: {
|
|
isConnected: !!info.isConnected,
|
|
name: info.name,
|
|
ip: info.ip,
|
|
port: info.port ? Number(info.port) : undefined,
|
|
libraryName: info.libraryName,
|
|
libraries,
|
|
},
|
|
};
|
|
}
|
|
return result as ApiResponse<PlexServerConnection>;
|
|
},
|
|
|
|
async connectToPlex(settings: PlexConnectionSettings, signal?: AbortSignal): Promise<ApiResponse<{ token: string; serverInfo: PlexServerConnection }>> {
|
|
const response = await fetch(`${API_BASE}/api/connect`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
protocol: settings.protocol,
|
|
address: settings.address,
|
|
port: settings.port,
|
|
token: settings.token,
|
|
username: settings.username,
|
|
password: settings.password,
|
|
library_name: settings.libraryName,
|
|
timeout: settings.timeout,
|
|
}),
|
|
signal,
|
|
});
|
|
const result = await handleResponse<any>(response);
|
|
if (result.status === 'success') {
|
|
const info = result.data.serverInfo;
|
|
info.libraries = (info.libraries || []).map(mapLibrary);
|
|
return { status: 'success', data: { token: result.data.token, serverInfo: info } };
|
|
}
|
|
return result as ApiResponse<{ token: string; serverInfo: PlexServerConnection }>;
|
|
},
|
|
};
|