Rewrite of ESH-Media v1 with separated main/renderer/shared architecture (vite-plugin-electron, React 18, react-router-dom). Includes NeDB storage, electron-store config, proxy manager with FoxyProxy/uBlock extensions, custom server-checked updater, NSIS installer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.5 KiB
Система Поисковых Скриптов
Обзор
Media Center использует систему кастомных JavaScript скриптов для поиска контента на различных сайтах. Каждый сайт имеет свой собственный скрипт, который знает, как выполнять поиск на этом конкретном сайте.
Преимущества подхода
- Гибкость: Каждый сайт может иметь уникальную логику поиска
- Расширяемость: Легко добавлять новые сайты без изменения основного кода
- Обновляемость: Скрипты можно обновлять с сервера
- Нет зависимости от Node.js: Скрипты выполняются в контексте Electron
Структура скрипта
Каждый скрипт - это JavaScript файл, который экспортирует функцию search:
async function search(query, siteUrl, useProxy, axios, cheerio, proxyConfig) {
// Ваша логика поиска
return [
{
name: "Название фильма",
url: "https://site.com/movie/123",
image: "https://site.com/poster.jpg", // опционально
year: "2023", // опционально
description: "Описание фильма", // опционально
rating: "8.5" // опционально
}
];
}
Параметры функции
query(string) - поисковый запрос пользователяsiteUrl(string) - базовый URL сайтаuseProxy(boolean) - флаг использования проксиaxios- HTTP клиент для запросовcheerio- библиотека для парсинга HTMLproxyConfig- объект с настройками прокси{host, port}
Возвращаемое значение
Массив объектов с результатами. Обязательные поля:
name- название фильма/сериалаurl- ссылка на страницу
Опциональные поля:
image- URL постераyear- год выпускаdescription- краткое описаниеrating- рейтинг
Доступные инструменты
axios
HTTP клиент для выполнения запросов:
// GET запрос
const response = await axios.get('https://site.com/search', {
params: { q: query },
timeout: 15000,
headers: {
'User-Agent': 'Mozilla/5.0...'
}
});
// POST запрос
const response = await axios.post('https://site.com/search', {
query: query
}, {
timeout: 15000
});
// С прокси
if (useProxy && proxyConfig) {
const response = await axios.get(url, {
proxy: {
host: proxyConfig.host,
port: proxyConfig.port
}
});
}
cheerio
jQuery-подобная библиотека для парсинга HTML:
const $ = cheerio.load(html);
// Поиск элементов
const title = $('.movie-title').text();
const link = $('a.movie-link').attr('href');
// Итерация по элементам
$('.movie-card').each((index, element) => {
const $el = $(element);
const name = $el.find('.title').text().trim();
const url = $el.find('a').attr('href');
});
Примеры реализаций
Пример 1: HTML парсинг (Kinogo)
async function search(query, siteUrl, useProxy, axios, cheerio, proxyConfig) {
const config = {
params: { do: 'search', subaction: 'search', story: query },
timeout: 15000,
headers: { 'User-Agent': 'Mozilla/5.0...' }
};
if (useProxy && proxyConfig) {
config.proxy = { host: proxyConfig.host, port: proxyConfig.port };
}
const response = await axios.get(`${siteUrl}/index.php`, config);
const $ = cheerio.load(response.data);
const results = [];
$('.shortstory').each((i, el) => {
const $item = $(el);
const name = $item.find('.title a').text().trim();
const url = $item.find('.title a').attr('href');
const image = $item.find('img').attr('src');
if (name && url) {
results.push({ name, url: siteUrl + url, image });
}
});
return results;
}
Пример 2: JSON API (Rutube)
async function search(query, siteUrl, useProxy, axios, cheerio, proxyConfig) {
const config = {
params: { query, limit: 20 },
timeout: 15000
};
const response = await axios.get(`${siteUrl}/api/search/video/`, config);
const data = response.data;
if (!data.results) return [];
return data.results.map(item => ({
name: item.title,
url: item.video_url || `${siteUrl}/video/${item.id}`,
image: item.thumbnail_url,
description: item.description
}));
}
Хранение скриптов
Скрипты хранятся в двух местах:
-
Встроенные скрипты:
<app>/search-scripts/- Поставляются с приложением
- Только для чтения
- Используются по умолчанию
-
Пользовательские скрипты:
<userData>/search-scripts/- Загружаются с сервера
- Можно обновлять
- Имеют приоритет над встроенными
Конфигурация сайта
В config/sites.json:
{
"id": "kinogo",
"name": "Kinogo",
"url": "https://kinogo.biz",
"logo": "https://kinogo.biz/favicon.ico",
"enabled": true,
"useProxy": true,
"searchScript": "kinogo.js"
}
Создание нового скрипта
- Скопируйте
SCRIPT_TEMPLATE.js - Назовите файл по имени сайта (например,
mysite.js) - Реализуйте функцию
search() - Протестируйте с разными запросами
- Добавьте сайт в конфигурацию
Обновление скриптов с сервера
Пользователи могут обновлять скрипты через настройки приложения:
- Настройки → Конфигурация
- Кнопка "Обновить конфигурацию"
- Скачиваются новые скрипты и конфигурация
- Применяются автоматически
Безопасность
- Скрипты выполняются в изолированном контексте
- Таймаут выполнения: 30 секунд
- Нет доступа к файловой системе
- Нет доступа к другим модулям Node.js
- Только axios и cheerio
Отладка
Для отладки скриптов:
- Откройте DevTools (F12 в приложении)
- Выполните поиск
- Проверьте консоль на наличие ошибок
- Логи включают:
- Найденное количество результатов
- Ошибки выполнения
- Невалидные результаты
Требования к серверу
Сервер должен предоставлять:
GET /api/config/sites
Возвращает конфигурацию со списком сайтов и ссылками на скрипты:
{
"version": "1.0.0",
"lastUpdated": "2025-10-14T12:00:00Z",
"sites": [
{
"id": "kinogo",
"name": "Kinogo",
"url": "https://kinogo.biz",
"logo": "https://kinogo.biz/favicon.ico",
"enabled": true,
"useProxy": true,
"searchScript": "kinogo.js",
"scriptUrl": "https://server.com/scripts/kinogo.js"
}
]
}
GET /scripts/{scriptName}
Возвращает содержимое скрипта (JavaScript файл).
Best Practices
- Обработка ошибок: Всегда оборачивайте код в try-catch
- Таймауты: Устанавливайте разумные таймауты для запросов
- Валидация: Проверяйте наличие обязательных полей
- User-Agent: Используйте реалистичный User-Agent
- Относительные URL: Преобразуйте в абсолютные
- Пустые результаты: Возвращайте
[]при ошибке, а неnull