gdq-archive/frontend/util/api.ts

167 lines
4.7 KiB
TypeScript

/**
* Copyright (C) 2019-2021 Carl Kittelberger <icedream@icedream.pw>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { deprecate } from 'util';
import { getThumbnailURL } from './thumbnail';
import { RunnerList } from './datatypes/RunnerList';
import { VideoEntry, VideoList } from './datatypes/VideoList';
import { VideoOnDemandIndex } from './datatypes/VideoOnDemandIdentifier';
import sanitizeTitle from './sanitizeTitle';
const upstreamURL = process.env.UPSTREAM_URL;
const upstreamDirectURL = process.env.UPSTREAM_DIRECT_URL || upstreamURL;
// const apiURL = process.env.API_URL || `${upstreamURL}/api`;
const apiDirectURL = process.env.API_DIRECT_URL || `${upstreamDirectURL}/api`;
// TODO - where did the hourglass image go?
const hourglassImage: string = null;
export class HTTPError extends Error {
response: Response;
data: any;
constructor(response: Response, data: any) {
super(`HTTP server responded with error: ${response.statusText}`);
this.response = response;
this.data = data;
}
}
export async function fetchJson(
input: RequestInfo,
init?: RequestInit,
) {
try {
const response = await fetch(input, init);
// if the server replies, there's always some data in json
// if there's a network error, it will throw at the previous line
const data = await response.json();
if (response.ok) {
return data;
}
throw new HTTPError(response, data);
} catch (error) {
if (!error.data) {
error.data = { message: error.message };
}
throw error;
}
}
export function makeThumbnailURLSet(
thumbnailServerURL: string,
id: string,
{
downloadFileName,
thumbnails,
}: VideoEntry,
): { [srcSetSpec: string]: URL } {
// let relativePaths;
// If thumbnails were presented by the JSON API, use those
if (thumbnails) {
return thumbnails
.reduce((all, { sourceSize, fileName }) => ({
...all,
[sourceSize]: new URL(fileName, thumbnailServerURL),
}), {});
}
// Generate default set of URLs based on MP4s on the upstream server
return [96, 96 * 2, 96 * 3]
.reduce((all, width) => ({
[`${width}w`]: downloadFileName
? getThumbnailURL(thumbnailServerURL, id, downloadFileName)
: hourglassImage,
}), {});
}
deprecate(makeThumbnailURLSet, 'makeThumbnailURLSet() is deprecated.');
async function getDirect(relativeURL: string) {
return fetchJson(`${apiDirectURL}/${relativeURL}`);
}
// async function get(relativeURL: any) {
// return fetchJson(`${apiURL}/${relativeURL}`);
// }
export async function getIndex(): Promise<VideoOnDemandIndex> {
return getDirect('index.json');
}
export async function getRunners(): Promise<RunnerList> {
return getDirect('runners.json');
}
export async function getVideos(id: string): Promise<VideoList> {
const result: VideoList = await getDirect(`videos/${id}.json`);
result.videos = result.videos.reduce((all: Array<VideoEntry>, {
slug,
title,
...video
}: VideoEntry) => [
...all,
{
...video,
title,
slug: typeof slug === 'string'
? slug
: sanitizeTitle(title + (
all.find((v) => v.title === title)
? ` ${all.filter((v) => v.title === title).length + 1}`
: ''
)),
},
], []);
return result;
}
export function getDownloadURL(id: string, fileName: string): string {
return [upstreamURL, encodeURIComponent(id), encodeURIComponent(fileName)]
.join('/');
}
export function submitPreferences(data: {
enableDark?: boolean,
locale?: string,
volume?: number,
}) {
const formData = new URLSearchParams();
if (data.enableDark !== undefined && data.enableDark !== null) {
formData.append('enableDark', data.enableDark.toString());
}
if (data.locale !== undefined && data.locale !== null) {
formData.append('locale', data.locale);
}
if (data.volume !== undefined && data.volume !== null) {
formData.append('volume', data.volume.toString());
}
fetchJson('/api/changePreferences', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData,
});
}