invidious: move searching to backend
This commit is contained in:
@@ -33,6 +33,19 @@ export interface MetadataUpdateEvent {
|
||||
};
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
type: string;
|
||||
title: string;
|
||||
author: string;
|
||||
mediaUrl: string;
|
||||
thumbnailUrl: string;
|
||||
}
|
||||
|
||||
export interface SearchResponse {
|
||||
success: boolean;
|
||||
results: SearchResult[];
|
||||
}
|
||||
|
||||
export const API = {
|
||||
async getPlaylist(): Promise<PlaylistItem[]> {
|
||||
const response = await fetch('/api/playlist');
|
||||
@@ -90,6 +103,11 @@ export const API = {
|
||||
});
|
||||
},
|
||||
|
||||
async search(query: string): Promise<SearchResponse> {
|
||||
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
|
||||
return response.json();
|
||||
},
|
||||
|
||||
subscribeToEvents(onMessage: (event: any) => void): WebSocket {
|
||||
const ws = new WebSocket(`ws://${window.location.host}/api/events`);
|
||||
ws.onmessage = (event) => {
|
||||
|
||||
@@ -1,21 +1,6 @@
|
||||
import React, { useState, KeyboardEvent } from 'react';
|
||||
import { FaSearch, FaSpinner, FaTimes } from 'react-icons/fa';
|
||||
import { getInvidiousSearchURL, INVIDIOUS_BASE_URL } from '../config';
|
||||
|
||||
interface InvidiousVideoThumbnail {
|
||||
quality: string;
|
||||
url: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface InvidiousResult {
|
||||
type: string;
|
||||
title: string;
|
||||
videoId: string;
|
||||
author: string;
|
||||
videoThumbnails?: InvidiousVideoThumbnail[];
|
||||
}
|
||||
import { API, SearchResult } from '../api/player';
|
||||
|
||||
interface InvidiousSearchModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -23,17 +8,10 @@ interface InvidiousSearchModalProps {
|
||||
onSelectVideo: (url: string) => void;
|
||||
}
|
||||
|
||||
const ResultCell: React.FC<{ result: InvidiousResult, onClick: () => void }> = ({ result, onClick, ...props }) => {
|
||||
const thumbnailUrl = (result: InvidiousResult) => {
|
||||
if (!result.videoThumbnails) return '/assets/placeholder.jpg';
|
||||
|
||||
const thumbnail = result.videoThumbnails.find(t => t.quality === 'medium');
|
||||
return thumbnail ? `${INVIDIOUS_BASE_URL}${thumbnail.url}` : '/assets/placeholder.jpg';
|
||||
};
|
||||
|
||||
const ResultCell: React.FC<{ result: SearchResult, onClick: () => void }> = ({ result, onClick, ...props }) => {
|
||||
return (
|
||||
<div className="flex gap-4 bg-black/20 p-2 rounded-lg cursor-pointer hover:bg-black/30 transition-colors" onClick={onClick} {...props}>
|
||||
<img src={thumbnailUrl(result)} alt={result.title} className="w-32 h-18 object-cover rounded" />
|
||||
<img src={result.thumbnailUrl} alt={result.title} className="w-32 h-18 object-cover rounded" />
|
||||
<div className="flex flex-col justify-center">
|
||||
<h3 className="text-white font-semibold">{result.title}</h3>
|
||||
<p className="text-white/60">{result.author}</p>
|
||||
@@ -44,7 +22,7 @@ const ResultCell: React.FC<{ result: InvidiousResult, onClick: () => void }> = (
|
||||
|
||||
const InvidiousSearchModal: React.FC<InvidiousSearchModalProps> = ({ isOpen, onClose, onSelectVideo }) => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [results, setResults] = useState<InvidiousResult[]>([]);
|
||||
const [results, setResults] = useState<SearchResult[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleSearch = async () => {
|
||||
@@ -52,14 +30,12 @@ const InvidiousSearchModal: React.FC<InvidiousSearchModalProps> = ({ isOpen, onC
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await fetch(getInvidiousSearchURL(searchQuery));
|
||||
const data = await response.json();
|
||||
|
||||
const videoResults = data.filter((item: InvidiousResult) => {
|
||||
return item.type === 'video' || item.type === 'playlist'
|
||||
});
|
||||
|
||||
setResults(videoResults);
|
||||
const response = await API.search(searchQuery);
|
||||
if (response.success) {
|
||||
setResults(response.results);
|
||||
} else {
|
||||
console.error('Search failed:', response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to search:', error);
|
||||
} finally {
|
||||
@@ -119,9 +95,9 @@ const InvidiousSearchModal: React.FC<InvidiousSearchModalProps> = ({ isOpen, onC
|
||||
<div className="grid gap-4">
|
||||
{results.map((result) => (
|
||||
<ResultCell
|
||||
key={result.videoId}
|
||||
key={result.mediaUrl}
|
||||
result={result}
|
||||
onClick={() => _onSelectVideo(`https://www.youtube.com/watch?v=${result.videoId}`)}
|
||||
onClick={() => _onSelectVideo(result.mediaUrl)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user