init: media-center v2
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>
This commit is contained in:
271
SEARCH_SCRIPTS.md
Normal file
271
SEARCH_SCRIPTS.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# Система Поисковых Скриптов
|
||||
|
||||
## Обзор
|
||||
|
||||
Media Center использует систему кастомных JavaScript скриптов для поиска контента на различных сайтах. Каждый сайт имеет свой собственный скрипт, который знает, как выполнять поиск на этом конкретном сайте.
|
||||
|
||||
## Преимущества подхода
|
||||
|
||||
1. **Гибкость**: Каждый сайт может иметь уникальную логику поиска
|
||||
2. **Расширяемость**: Легко добавлять новые сайты без изменения основного кода
|
||||
3. **Обновляемость**: Скрипты можно обновлять с сервера
|
||||
4. **Нет зависимости от Node.js**: Скрипты выполняются в контексте Electron
|
||||
|
||||
## Структура скрипта
|
||||
|
||||
Каждый скрипт - это JavaScript файл, который экспортирует функцию `search`:
|
||||
|
||||
```javascript
|
||||
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` - библиотека для парсинга HTML
|
||||
- `proxyConfig` - объект с настройками прокси `{host, port}`
|
||||
|
||||
### Возвращаемое значение
|
||||
|
||||
Массив объектов с результатами. Обязательные поля:
|
||||
- `name` - название фильма/сериала
|
||||
- `url` - ссылка на страницу
|
||||
|
||||
Опциональные поля:
|
||||
- `image` - URL постера
|
||||
- `year` - год выпуска
|
||||
- `description` - краткое описание
|
||||
- `rating` - рейтинг
|
||||
|
||||
## Доступные инструменты
|
||||
|
||||
### axios
|
||||
|
||||
HTTP клиент для выполнения запросов:
|
||||
|
||||
```javascript
|
||||
// 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:
|
||||
|
||||
```javascript
|
||||
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)
|
||||
|
||||
```javascript
|
||||
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)
|
||||
|
||||
```javascript
|
||||
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
|
||||
}));
|
||||
}
|
||||
```
|
||||
|
||||
## Хранение скриптов
|
||||
|
||||
Скрипты хранятся в двух местах:
|
||||
|
||||
1. **Встроенные скрипты**: `<app>/search-scripts/`
|
||||
- Поставляются с приложением
|
||||
- Только для чтения
|
||||
- Используются по умолчанию
|
||||
|
||||
2. **Пользовательские скрипты**: `<userData>/search-scripts/`
|
||||
- Загружаются с сервера
|
||||
- Можно обновлять
|
||||
- Имеют приоритет над встроенными
|
||||
|
||||
## Конфигурация сайта
|
||||
|
||||
В `config/sites.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "kinogo",
|
||||
"name": "Kinogo",
|
||||
"url": "https://kinogo.biz",
|
||||
"logo": "https://kinogo.biz/favicon.ico",
|
||||
"enabled": true,
|
||||
"useProxy": true,
|
||||
"searchScript": "kinogo.js"
|
||||
}
|
||||
```
|
||||
|
||||
## Создание нового скрипта
|
||||
|
||||
1. Скопируйте `SCRIPT_TEMPLATE.js`
|
||||
2. Назовите файл по имени сайта (например, `mysite.js`)
|
||||
3. Реализуйте функцию `search()`
|
||||
4. Протестируйте с разными запросами
|
||||
5. Добавьте сайт в конфигурацию
|
||||
|
||||
## Обновление скриптов с сервера
|
||||
|
||||
Пользователи могут обновлять скрипты через настройки приложения:
|
||||
|
||||
1. Настройки → Конфигурация
|
||||
2. Кнопка "Обновить конфигурацию"
|
||||
3. Скачиваются новые скрипты и конфигурация
|
||||
4. Применяются автоматически
|
||||
|
||||
## Безопасность
|
||||
|
||||
- Скрипты выполняются в изолированном контексте
|
||||
- Таймаут выполнения: 30 секунд
|
||||
- Нет доступа к файловой системе
|
||||
- Нет доступа к другим модулям Node.js
|
||||
- Только axios и cheerio
|
||||
|
||||
## Отладка
|
||||
|
||||
Для отладки скриптов:
|
||||
|
||||
1. Откройте DevTools (F12 в приложении)
|
||||
2. Выполните поиск
|
||||
3. Проверьте консоль на наличие ошибок
|
||||
4. Логи включают:
|
||||
- Найденное количество результатов
|
||||
- Ошибки выполнения
|
||||
- Невалидные результаты
|
||||
|
||||
## Требования к серверу
|
||||
|
||||
Сервер должен предоставлять:
|
||||
|
||||
### GET /api/config/sites
|
||||
|
||||
Возвращает конфигурацию со списком сайтов и ссылками на скрипты:
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
1. **Обработка ошибок**: Всегда оборачивайте код в try-catch
|
||||
2. **Таймауты**: Устанавливайте разумные таймауты для запросов
|
||||
3. **Валидация**: Проверяйте наличие обязательных полей
|
||||
4. **User-Agent**: Используйте реалистичный User-Agent
|
||||
5. **Относительные URL**: Преобразуйте в абсолютные
|
||||
6. **Пустые результаты**: Возвращайте `[]` при ошибке, а не `null`
|
||||
Reference in New Issue
Block a user