/** * Copyright (C) 2019-2021 Carl Kittelberger * * 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 . */ import deprecate from 'util-deprecate'; 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; } } function legacyMakeThumbnailURLSet( 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, }), {}); } export const makeThumbnailURLSet = deprecate(legacyMakeThumbnailURLSet, '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 { return getDirect('index.json'); } export async function getRunners(): Promise { return getDirect('runners.json'); } export async function getVideos(id: string): Promise { const result: VideoList = await getDirect(`videos/${id}.json`); result.videos = result.videos.reduce((all: Array, { 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, }); }