2025-02-11 23:04:28 +01:00
|
|
|
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),
|
2025-02-17 23:04:50 +01:00
|
|
|
body: (body != null ? JSON.stringify(body) : undefined)
|
2025-02-11 23:04:28 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
let json = null;
|
|
|
|
try {
|
|
|
|
json = await response.json();
|
2025-02-17 23:04:50 +01:00
|
|
|
} catch (e) { }
|
2025-02-11 23:04:28 +01:00
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
throw json?.error || "Request failed: " + response.status + " " + response.statusText;
|
|
|
|
}
|
|
|
|
|
2025-02-17 23:04:50 +01:00
|
|
|
if (json == null) {
|
|
|
|
throw "Failed to decode response";
|
|
|
|
}
|
|
|
|
|
2025-02-11 23:04:28 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2025-02-12 23:40:37 +01:00
|
|
|
/**
|
|
|
|
* @param {string} id
|
2025-02-17 23:04:50 +01:00
|
|
|
* @returns {FullVideoMetadata}
|
2025-02-12 23:40:37 +01:00
|
|
|
* @throws {APIError}
|
|
|
|
*/
|
|
|
|
async getVideoMetadata(id) {
|
|
|
|
return await this.call("GET", "/library/video/" + encodeURIComponent(id) + "/metadata", null, null);
|
|
|
|
}
|
|
|
|
|
2025-02-17 23:04:50 +01:00
|
|
|
/**
|
|
|
|
* @param {string} id
|
|
|
|
* @param {VideoMetadata} metadata
|
|
|
|
* @returns {VideoMetadata}
|
|
|
|
*/
|
|
|
|
async updateVideoMetadata(id, metadata) {
|
|
|
|
return await this.call("PUT", "/library/video/" + encodeURIComponent(id) + "/metadata", null, metadata);
|
|
|
|
}
|
|
|
|
|
2025-02-11 23:04:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class APIError {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
errorMessage;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {!string} errorMessage
|
|
|
|
*/
|
|
|
|
constructor(errorMessage) {
|
|
|
|
this.errorMessage = errorMessage
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class Library {
|
2025-02-12 23:40:37 +01:00
|
|
|
|
2025-02-11 23:04:28 +01:00
|
|
|
/**
|
|
|
|
* @type {Object<string, Video[]>}
|
|
|
|
*/
|
|
|
|
videos;
|
2025-02-12 23:40:37 +01:00
|
|
|
|
2025-02-11 23:04:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class Video {
|
2025-02-12 23:40:37 +01:00
|
|
|
|
2025-02-11 23:04:28 +01:00
|
|
|
/**
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
id;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {VideoMetadata}
|
|
|
|
*/
|
|
|
|
metadata;
|
2025-02-12 23:40:37 +01:00
|
|
|
|
2025-02-11 23:04:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {{series:string, title: string, author: string, index: number, [key: string]: any}} VideoMetadata
|
2025-02-17 23:04:50 +01:00
|
|
|
* @typedef {{raw: VideoMetadata, inherited: VideoMetadata, effective: VideoMetadata}} FullVideoMetadata
|
2025-02-11 23:04:28 +01:00
|
|
|
*/
|
2025-02-12 23:40:37 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @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';
|
|
|
|
}
|
|
|
|
}
|