2025-02-11 23:04:28 +01:00

113 lines
1.9 KiB
JavaScript

class API {
/**
* @type {!string}
*/
apiURL;
/**
* @param {!string} apiURL
*/
constructor(apiURL) {
this.apiURL = apiURL;
}
/**
* @param {"GET" | "POST" | "PUT"} method
* @param {!string} path
* @param {?Object.<string, string>} query
* @param {?any} body
* @returns {?any}
* @throws {APIError}
*/
async call(method, path, query, body) {
let params = new URLSearchParams();
if (query != null) {
for (let key in query) {
let value = query[key];
if (value == null) continue;
params.set(key, value);
}
}
let response = await fetch(this.apiURL + path + (params.size > 0 ? "?" + params : ""),
{
method,
headers: (body != null ? { "Content-Type": "application/json" } : undefined),
body
}
);
let json = null;
try {
json = await response.json();
} catch (e) {
throw "Failed to decode response";
}
if (!response.ok) {
throw json?.error || "Request failed: " + response.status + " " + response.statusText;
}
return json
}
/**
* @param {?("series" | "author")} groupBy
* @returns {Library}
* @throws {APIError}
*/
async getLibrary(groupBy) {
return await this.call("GET", "/library", { 'groupBy': groupBy }, null);
}
/**
* @param {!string} id
* @returns {Video}
* @throws {APIError}
*/
async getVideo(id) {
return await this.call("GET", "/library/video/" + encodeURIComponent(id), null, null);
}
}
class APIError {
/**
* @type {string}
*/
errorMessage;
/**
* @param {!string} errorMessage
*/
constructor(errorMessage) {
this.errorMessage = errorMessage
}
}
class Library {
/**
* @type {Object<string, Video[]>}
*/
videos;
}
class Video {
/**
* @type {string}
*/
id;
/**
* @type {VideoMetadata}
*/
metadata;
}
/**
* @typedef {{series:string, title: string, author: string, index: number, [key: string]: any}} VideoMetadata
*/