Merge commit '5a29265854cee9c039f746f5685a538eff048fae'

This commit is contained in:
2025-11-28 22:43:39 +09:00
6 changed files with 269 additions and 96 deletions
+75 -25
View File
@@ -13,28 +13,28 @@ const MOCK_LIBRARIES: PlexLibrary[] = [
];
// Helper to simulate network request or call actual API
const fetchPlaylists = async (type: ServerType): Promise<Playlist[]> => {
// In a real Docker environment with FastAPI, you would do:
// const response = await fetch(`/api/playlists/${type.toLowerCase()}`);
// const data = await response.json();
// return data;
// Mocking for UI demonstration
return new Promise((resolve) => {
setTimeout(() => {
const fetchPlaylists = async (type: ServerType, signal?: AbortSignal): Promise<Playlist[]> => {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
if (type === ServerType.LOCAL) {
resolve([...MOCK_LOCAL_PLAYLISTS]);
} else {
resolve([...MOCK_CLOUD_PLAYLISTS]);
}
}, SIMULATE_DELAY_MS);
if (signal) {
signal.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
});
}
});
};
const fetchServerStatus = async (): Promise<PlexServerConnection> => {
// Mocking server status
return new Promise((resolve) => {
setTimeout(() => {
const fetchServerStatus = async (signal?: AbortSignal): Promise<PlexServerConnection> => {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
// 90% chance of success for demo
const isSuccess = Math.random() > 0.1;
if (isSuccess) {
@@ -51,12 +51,32 @@ const fetchServerStatus = async (): Promise<PlexServerConnection> => {
});
}
}, SIMULATE_DELAY_MS);
if (signal) {
signal.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
});
}
});
};
const authenticatePlex = async (settings: PlexConnectionSettings): Promise<{ token: string, serverInfo: PlexServerConnection }> => {
const authenticatePlex = async (settings: PlexConnectionSettings, signal?: AbortSignal): Promise<{ token: string, serverInfo: PlexServerConnection }> => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Determine effective timeout
const timeoutSeconds = settings.timeout || 9;
const timeoutMs = timeoutSeconds * 1000;
// Simulate latency (random between 1s and 2s, or longer to test timeout)
const latency = 1500;
const timer = setTimeout(() => {
// Check if we timed out (simulated)
if (latency > timeoutMs) {
reject(new Error(`Connection timed out after ${settings.timeout}s`));
return;
}
// Simulate validation
if (!settings.address) {
reject(new Error("Server address is required"));
@@ -84,26 +104,49 @@ const authenticatePlex = async (settings: PlexConnectionSettings): Promise<{ tok
libraries: MOCK_LIBRARIES
}
});
}, 1500);
}, latency);
// Handle User Cancellation
if (signal) {
signal.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
});
}
// Handle Actual Timeout Logic (if latency was indeterminate in real world)
// In this mock, the latency is fixed at 1500, but logic above simulates the check.
// However, if the user set timeout < 1.5s, we should reject sooner.
if (timeoutMs < latency) {
setTimeout(() => {
clearTimeout(timer);
reject(new Error(`Connection timed out after ${settings.timeout}s`));
}, timeoutMs);
}
});
}
export const apiService = {
getPlaylists: async (serverType: ServerType): Promise<ApiResponse<Playlist[]>> => {
getPlaylists: async (serverType: ServerType, signal?: AbortSignal): Promise<ApiResponse<Playlist[]>> => {
try {
const data = await fetchPlaylists(serverType);
const data = await fetchPlaylists(serverType, signal);
return { data, status: 'success' };
} catch (error) {
} catch (error: any) {
if (error.name === 'AbortError') {
// Return a specific status or handle gracefully
return { data: [], status: 'error', message: 'Request cancelled' };
}
console.error(`Error fetching ${serverType} playlists:`, error);
return { data: [], status: 'error', message: 'Failed to fetch playlists' };
}
},
getServerStatus: async (): Promise<ApiResponse<PlexServerConnection>> => {
getServerStatus: async (signal?: AbortSignal): Promise<ApiResponse<PlexServerConnection>> => {
try {
const data = await fetchServerStatus();
const data = await fetchServerStatus(signal);
return { data, status: 'success' };
} catch (error) {
} catch (error: any) {
if (error.name === 'AbortError') return { data: { isConnected: false }, status: 'error', message: 'Cancelled' };
return {
data: { isConnected: false },
status: 'error',
@@ -112,11 +155,18 @@ export const apiService = {
}
},
connectToPlex: async (settings: PlexConnectionSettings): Promise<ApiResponse<{ token: string, serverInfo: PlexServerConnection }>> => {
connectToPlex: async (settings: PlexConnectionSettings, signal?: AbortSignal): Promise<ApiResponse<{ token: string, serverInfo: PlexServerConnection }>> => {
try {
const data = await authenticatePlex(settings);
const data = await authenticatePlex(settings, signal);
return { data, status: 'success', message: 'Connected successfully' };
} catch (error: any) {
if (error.name === 'AbortError') {
return {
data: { token: '', serverInfo: { isConnected: false } },
status: 'error',
message: 'Connection cancelled'
};
}
return {
data: { token: '', serverInfo: { isConnected: false } },
status: 'error',
@@ -124,4 +174,4 @@ export const apiService = {
};
}
}
};
};