Files
QueueCube/src/InvidiousAPI.ts

79 lines
2.3 KiB
TypeScript
Raw Normal View History

2025-02-22 01:09:33 -08:00
import fetch from 'node-fetch';
interface InvidiousVideoThumbnail {
quality: string;
url: string;
width: number;
height: number;
}
interface InvidiousResult {
type: string;
title: string;
videoId: string;
playlistId: string;
author: string;
videoThumbnails?: InvidiousVideoThumbnail[];
}
export interface SearchResult {
type: string;
title: string;
author: string;
mediaUrl: string;
thumbnailUrl: string;
}
const USE_INVIDIOUS = process.env.USE_INVIDIOUS || true;
const INVIDIOUS_BASE_URL = process.env.INVIDIOUS_BASE_URL || 'http://invidious.nor';
const INVIDIOUS_API_ENDPOINT = `${INVIDIOUS_BASE_URL}/api/v1`;
export const getInvidiousSearchURL = (query: string): string =>
`${INVIDIOUS_API_ENDPOINT}/search?q=${encodeURIComponent(query)}`;
export const getInvidiousThumbnailURL = (url: string): string =>
`${INVIDIOUS_BASE_URL}${url}`;
const preferredThumbnailAPIURL = (thumbnails: InvidiousVideoThumbnail[] | undefined): string => {
if (!thumbnails || thumbnails.length === 0) {
return '/assets/placeholder.jpg';
}
const mediumThumbnail = thumbnails.find(t => t.quality === 'medium');
const thumbnail = mediumThumbnail || thumbnails[0];
return `/api/thumbnail?url=${encodeURIComponent(thumbnail.url)}`;
};
const getMediaURL = (result: InvidiousResult): string => {
if (result.type === 'video') {
return `https://www.youtube.com/watch?v=${result.videoId}`;
} else if (result.type === 'playlist') {
return `https://www.youtube.com/playlist?list=${result.playlistId}`;
}
throw new Error(`Unknown result type: ${result.type}`);
};
export const searchInvidious = async (query: string): Promise<SearchResult[]> => {
try {
const response = await fetch(getInvidiousSearchURL(query));
if (!response.ok) {
throw new Error(`Invidious HTTP error: ${response.status}`);
}
const data = await response.json() as Array<InvidiousResult>;
return data.filter(item => {
return item.type === 'video' || item.type === 'playlist';
}).map(item => ({
type: item.type,
title: item.title,
author: item.author,
mediaUrl: getMediaURL(item),
thumbnailUrl: preferredThumbnailAPIURL(item.videoThumbnails)
}));
} catch (error) {
console.error('Failed to search Invidious:', error);
throw error;
}
}