class API { /** * @type {!string} */ apiURL; /** * @param {!string} apiURL */ constructor(apiURL) { this.apiURL = apiURL; } /** * @param {"GET" | "POST" | "PUT"} method * @param {!string} path * @param {?Object.} 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); } /** * @param {string} id * @returns {Video} * @throws {APIError} */ async getVideoMetadata(id) { return await this.call("GET", "/library/video/" + encodeURIComponent(id) + "/metadata", null, null); } } class APIError { /** * @type {string} */ errorMessage; /** * @param {!string} errorMessage */ constructor(errorMessage) { this.errorMessage = errorMessage } } class Library { /** * @type {Object} */ videos; } class Video { /** * @type {string} */ id; /** * @type {VideoMetadata} */ metadata; } /** * @typedef {{series:string, title: string, author: string, index: number, [key: string]: any}} VideoMetadata */ /** * @param {any} error * @returns {string} */ function errorToString(error) { if (typeof error == 'string') { return error; } else if (error.toString) { return error.toString(); } else { return 'Unknown error'; } }