Initial commit - ColdFilm parser
This commit is contained in:
46
.htaccess
Normal file
46
.htaccess
Normal file
@@ -0,0 +1,46 @@
|
||||
# .htaccess для films.tolchin.pro
|
||||
|
||||
# Включение RewriteEngine
|
||||
RewriteEngine On
|
||||
|
||||
# Перенаправление на HTTPS (раскомментируйте если есть SSL)
|
||||
# RewriteCond %{HTTPS} off
|
||||
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||
|
||||
# Перенаправление www на без-www
|
||||
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
|
||||
RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
|
||||
|
||||
# Кэширование статических файлов
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType image/jpeg "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/webp "access plus 1 year"
|
||||
ExpiresByType image/x-icon "access plus 1 year"
|
||||
ExpiresByType text/css "access plus 1 month"
|
||||
ExpiresByType application/javascript "access plus 1 month"
|
||||
</IfModule>
|
||||
|
||||
# Сжатие
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript
|
||||
</IfModule>
|
||||
|
||||
# Защита от прямого доступа к конфигурационным файлам
|
||||
<FilesMatch "^\.">
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</FilesMatch>
|
||||
|
||||
# Безопасные заголовки
|
||||
<IfModule mod_headers.c>
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
Header set X-Frame-Options "SAMEORIGIN"
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
</IfModule>
|
||||
|
||||
# Правила для SEO
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^$ index.html [L]
|
||||
71
README.md
Normal file
71
README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 🎬 ColdFilm - Парсер фильмов и сериалов
|
||||
|
||||
Веб-приложение для мониторинга новых релизов с сайта ColdFilm и быстрого доступа к торрентам.
|
||||
|
||||
## Возможности
|
||||
|
||||
- 📡 Автоматическое получение списка новых фильмов и сериалов
|
||||
- 🖼️ Отображение постеров
|
||||
- 📥 Быстрый выбор качества торрента (720p, 1080p, 4K, Magnet)
|
||||
- 🔄 Автообновление списка каждые 15 минут
|
||||
- 🎨 Тёмная тема с адаптивным дизайном
|
||||
|
||||
## Использование
|
||||
|
||||
### Локальный запуск
|
||||
|
||||
Просто откройте `index.html` в браузере.
|
||||
|
||||
### На Synology (Web Station)
|
||||
|
||||
1. Скопируйте файлы в папку веб-сервера:
|
||||
```
|
||||
/volume1/web/films/
|
||||
```
|
||||
2. Настройте Web Station
|
||||
3. Откройте `http://ваш-ip/films/`
|
||||
|
||||
### Структура файлов
|
||||
|
||||
```
|
||||
├── index.html # Основная страница
|
||||
├── style.css # Стили
|
||||
├── script.js # Скрипты
|
||||
├── favicon.svg # Иконка сайта
|
||||
├── robots.txt # Правила для поисковиков
|
||||
├── .htaccess # Настройки Apache
|
||||
├── sitemap.xml # Карта сайта
|
||||
├── sitemap-index.xml # Индекс карт сайта
|
||||
├── sitemap-images.xml # Карта изображений
|
||||
└── README.md # Этот файл
|
||||
```
|
||||
|
||||
### SEO и карты сайта
|
||||
|
||||
- **sitemap.xml** — основная карта сайта с главной страницей
|
||||
- **sitemap-index.xml** — индекс всех sitemap-файлов
|
||||
- **sitemap-images.xml** — карта изображений (постеров) — заполняется динамически
|
||||
- **robots.txt** — правила для поисковых роботов
|
||||
|
||||
## Требования
|
||||
|
||||
- Современный браузер с поддержкой JavaScript
|
||||
- Доступ к интернету (для проксирования запросов)
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### CORS-прокси
|
||||
|
||||
По умолчанию используются публичные прокси. Для повышения надёжности можно использовать свой прокси-сервер.
|
||||
|
||||
### Автообновление
|
||||
|
||||
Интервал обновления: 15 минут (настраивается в `script.js`)
|
||||
|
||||
## Лицензия
|
||||
|
||||
MIT License
|
||||
|
||||
## Автор
|
||||
|
||||
Для вопросов и предложений: свяжитесь с автором
|
||||
13
favicon.svg
Normal file
13
favicon.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<defs>
|
||||
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#00d4ff;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#007bff;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="32" height="32" rx="6" fill="#1a1a2e"/>
|
||||
<circle cx="16" cy="16" r="10" fill="none" stroke="url(#grad)" stroke-width="2"/>
|
||||
<circle cx="16" cy="16" r="4" fill="url(#grad)"/>
|
||||
<rect x="6" y="12" width="4" height="8" rx="1" fill="#00d4ff"/>
|
||||
<rect x="22" y="12" width="4" height="8" rx="1" fill="#00d4ff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 640 B |
30
index.html
Normal file
30
index.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>🎬 ColdFilm</title>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>🎥 Новые релизы ColdFilm</h1>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status loading">🔄 Инициализация...</div>
|
||||
<button class="refresh-btn" onclick="loadFilms()">🔄 Обновить</button>
|
||||
<ul id="films" class="film-list"></ul>
|
||||
|
||||
<!-- Модальное окно выбора качества -->
|
||||
<div id="qualityModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h2>📥 Выберите качество</h2>
|
||||
<div id="qualityOptions" class="quality-options"></div>
|
||||
<button class="btn quality-close" onclick="closeModal()">Отмена</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
34
robots.txt
Normal file
34
robots.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
# robots.txt для films.tolchin.pro
|
||||
|
||||
Host: films.tolchin.pro
|
||||
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
# Разрешаем основные страницы
|
||||
Allow: /$
|
||||
Allow: /index.html
|
||||
|
||||
# Запрещаем индексацию технических файлов
|
||||
Disallow: /style.css
|
||||
Disallow: /script.js
|
||||
Disallow: /favicon.svg
|
||||
Disallow: /.htaccess
|
||||
Disallow: /README.md
|
||||
|
||||
# Запрещаем служебные пути
|
||||
Disallow: /*.md$
|
||||
Disallow: /*.txt$
|
||||
Disallow: /*.xml$
|
||||
|
||||
# Индексация
|
||||
Sitemap: https://films.tolchin.pro/sitemap.xml
|
||||
Sitemap: https://films.tolchin.pro/sitemap-index.xml
|
||||
|
||||
# Clean параметры
|
||||
Clean-param: ref /
|
||||
|
||||
# Ограничение индексации дубликатов
|
||||
Noindex: /style.css
|
||||
Noindex: /script.js
|
||||
Noindex: /favicon.svg
|
||||
259
script.js
Normal file
259
script.js
Normal file
@@ -0,0 +1,259 @@
|
||||
const targetUrl = 'https://coldfilm.ink';
|
||||
|
||||
let currentFilmName = '';
|
||||
let availableTorrents = [];
|
||||
|
||||
// Публичные CORS-прокси
|
||||
const PROXIES = [
|
||||
'https://corsproxy.io/?',
|
||||
'https://api.allorigins.win/raw?url=',
|
||||
'https://proxy.cors.sh/',
|
||||
'https://corsproxy.org/?',
|
||||
'https://allorigins.win/raw?url='
|
||||
];
|
||||
|
||||
// Кеш страниц фильмов и постеров
|
||||
const filmData = new Map();
|
||||
|
||||
async function fetchWithProxy(url) {
|
||||
for (const proxy of PROXIES) {
|
||||
try {
|
||||
const response = await fetch(proxy + encodeURIComponent(url), { timeout: 15000 });
|
||||
if (response.ok) return await response.text();
|
||||
} catch (e) {}
|
||||
}
|
||||
throw new Error('Все прокси недоступны');
|
||||
}
|
||||
|
||||
function parseColdfilm(html) {
|
||||
const films = [];
|
||||
|
||||
// Сначала соберём все ссылки на фильмы с названиями
|
||||
// Сразу исключаем Telegram
|
||||
const linksMap = new Map();
|
||||
let match;
|
||||
const linkRegex = new RegExp('href="(/news/[^"#]+)"[^>]*class="kino-h"[^>]*title="([^"]+)\\[Смотреть Онлайн\\]"', 'gi');
|
||||
while ((match = linkRegex.exec(html)) !== null) {
|
||||
const title = match[2].trim();
|
||||
if (title.toLowerCase().includes('telegram')) continue;
|
||||
linksMap.set(match[1], title);
|
||||
}
|
||||
|
||||
console.log('linksMap:', linksMap.size);
|
||||
|
||||
// Пробуем разные паттерны для поиска постеров
|
||||
const patterns = [
|
||||
'<a[^>]+href="(/news/[^"#]+)"[^>]*>\\s*<img[^>]+src="([^"]+)"',
|
||||
'<img[^>]+src="([^"]+)"[^>]*>\\s*<a[^>]+href="(/news/[^"#]+)"',
|
||||
'class="kino-h"[^>]*title="[^"]*"[^>]*>[\\s\\S]*?<img[^>]+src="([^"]+)"'
|
||||
];
|
||||
|
||||
for (const pat of patterns) {
|
||||
const regex = new RegExp(pat, 'gi');
|
||||
let count = 0;
|
||||
while ((match = regex.exec(html)) !== null) {
|
||||
count++;
|
||||
}
|
||||
console.log('Pattern:', pat.substring(0, 50), 'matches:', count);
|
||||
}
|
||||
|
||||
// Ищем постеры по alt/title - там есть название фильма
|
||||
const posterMap = new Map();
|
||||
const posterRegex = new RegExp('<img[^>]+src="([^"]+)"[^>]+alt="([^"]*\\[Смотреть Онлайн\\])"[^>]*>', 'gi');
|
||||
while ((match = posterRegex.exec(html)) !== null) {
|
||||
const poster = match[1];
|
||||
const alt = match[2].replace('[Смотреть Онлайн]', '').trim();
|
||||
|
||||
let fullPoster = poster;
|
||||
if (!fullPoster.startsWith('http')) {
|
||||
fullPoster = targetUrl + poster;
|
||||
}
|
||||
posterMap.set(alt, fullPoster);
|
||||
}
|
||||
|
||||
// Альтернативно - title вместо alt
|
||||
const posterRegex2 = new RegExp('<img[^>]+src="([^"]+)"[^>]+title="([^"]*\\[Смотреть Онлайн\\])"[^>]*>', 'gi');
|
||||
while ((match = posterRegex2.exec(html)) !== null) {
|
||||
const poster = match[1];
|
||||
const title = match[2].replace('[Смотреть Онлайн]', '').trim();
|
||||
|
||||
if (!posterMap.has(title)) {
|
||||
let fullPoster = poster;
|
||||
if (!fullPoster.startsWith('http')) {
|
||||
fullPoster = targetUrl + poster;
|
||||
}
|
||||
posterMap.set(title, fullPoster);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Найдено постеров:', posterMap.size);
|
||||
|
||||
// Собираем фильмы и ищем постер по названию
|
||||
const urls = Array.from(linksMap.keys());
|
||||
const titles = Array.from(linksMap.values());
|
||||
|
||||
console.log('URLs:', urls.length, 'Posters:', posterMap.size);
|
||||
|
||||
for (let i = 0; i < titles.length; i++) {
|
||||
const url = urls[i];
|
||||
const title = titles[i];
|
||||
const poster = posterMap.get(title) || '';
|
||||
|
||||
films.push({ title, url, poster });
|
||||
filmData.set(title, { url, poster });
|
||||
}
|
||||
|
||||
console.log('Фильмов с постерами:', films.length);
|
||||
return films.slice(0, 30);
|
||||
}
|
||||
|
||||
async function loadFilms() {
|
||||
const status = document.getElementById('status');
|
||||
const list = document.getElementById('films');
|
||||
|
||||
try {
|
||||
status.textContent = '🌐 Загружаем...';
|
||||
const html = await fetchWithProxy(targetUrl);
|
||||
status.textContent = '🔍 Парсим...';
|
||||
|
||||
const films = parseColdfilm(html);
|
||||
|
||||
if (films.length > 0) {
|
||||
status.className = 'status success';
|
||||
status.textContent = `✅ Найдено ${films.length} релизов!`;
|
||||
|
||||
list.innerHTML = '';
|
||||
films.forEach((film, i) => {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'film-item';
|
||||
|
||||
// Постер
|
||||
const img = document.createElement('img');
|
||||
img.className = 'film-poster';
|
||||
img.src = film.poster;
|
||||
img.alt = film.title;
|
||||
img.onerror = () => { img.style.display = 'none'; };
|
||||
|
||||
// Инфо
|
||||
const info = document.createElement('div');
|
||||
info.className = 'film-info';
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.className = 'film-title';
|
||||
span.textContent = `${i + 1}. ${film.title}`;
|
||||
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'btn';
|
||||
btn.textContent = '📥 Скачать';
|
||||
btn.onclick = () => showQualityModal(film.title);
|
||||
|
||||
info.appendChild(span);
|
||||
info.appendChild(btn);
|
||||
|
||||
li.appendChild(img);
|
||||
li.appendChild(info);
|
||||
|
||||
list.appendChild(li);
|
||||
});
|
||||
} else {
|
||||
status.textContent = '❌ Фильмы не найдены';
|
||||
status.className = 'status error';
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
status.textContent = `❌ Ошибка: ${e.message}`;
|
||||
status.className = 'status error';
|
||||
}
|
||||
}
|
||||
|
||||
async function showQualityModal(filmName) {
|
||||
const status = document.getElementById('status');
|
||||
const modal = document.getElementById('qualityModal');
|
||||
const options = document.getElementById('qualityOptions');
|
||||
|
||||
currentFilmName = filmName;
|
||||
availableTorrents = [];
|
||||
options.innerHTML = '';
|
||||
|
||||
try {
|
||||
status.className = 'status downloading';
|
||||
status.textContent = `🔍 Ищем торренты для ${filmName}...`;
|
||||
|
||||
const filmInfo = filmData.get(filmName);
|
||||
if (!filmInfo) throw new Error('Ссылка не найдена');
|
||||
const filmUrl = filmInfo.url;
|
||||
|
||||
const filmPageHtml = await fetchWithProxy(targetUrl + filmUrl);
|
||||
|
||||
// Ищем все торренты на странице
|
||||
let match;
|
||||
const torrentRegex = /href="(\/t\d+\/[^"]+\.torrent)"/gi;
|
||||
while ((match = torrentRegex.exec(filmPageHtml)) !== null) {
|
||||
let url = match[1];
|
||||
if (!url.startsWith('http')) {
|
||||
url = targetUrl + url;
|
||||
}
|
||||
|
||||
// Определяем качество из названия файла
|
||||
const filename = url.toLowerCase();
|
||||
let quality = 'Стандарт';
|
||||
if (filename.includes('720p') || filename.includes('hd720')) quality = '720p';
|
||||
else if (filename.includes('1080p') || filename.includes('hd1080')) quality = '1080p';
|
||||
else if (filename.includes('4k') || filename.includes('2160p')) quality = '4K';
|
||||
|
||||
// Проверяем, не добавляли ли уже
|
||||
if (!availableTorrents.find(t => t.url === url)) {
|
||||
availableTorrents.push({ url, quality });
|
||||
}
|
||||
}
|
||||
|
||||
// Ищем magnet
|
||||
const magnetRegex = /href="(magnet:[^"]+)"/gi;
|
||||
while ((match = magnetRegex.exec(filmPageHtml)) !== null) {
|
||||
if (!availableTorrents.find(t => t.url === match[1])) {
|
||||
availableTorrents.push({ url: match[1], quality: 'Magnet' });
|
||||
}
|
||||
}
|
||||
|
||||
if (availableTorrents.length === 0) {
|
||||
throw new Error('Торренты не найдены');
|
||||
}
|
||||
|
||||
// Сортируем: 4K, 1080p, 720p, STD, Magnet
|
||||
const sortOrder = { '4K': 1, '1080p': 2, '720p': 3, 'Стандарт': 4, 'Magnet': 5 };
|
||||
availableTorrents.sort((a, b) => (sortOrder[a.quality] || 99) - (sortOrder[b.quality] || 99));
|
||||
|
||||
// Создаём кнопки
|
||||
availableTorrents.forEach(t => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'quality-btn';
|
||||
btn.textContent = `📥 ${t.quality}`;
|
||||
btn.onclick = () => downloadTorrent(t.url);
|
||||
options.appendChild(btn);
|
||||
});
|
||||
|
||||
modal.classList.add('active');
|
||||
status.textContent = `✅ Найдено ${availableTorrents.length} торрентов`;
|
||||
|
||||
} catch (e) {
|
||||
status.className = 'status error';
|
||||
status.textContent = `❌ Ошибка: ${e.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('qualityModal').classList.remove('active');
|
||||
}
|
||||
|
||||
function downloadTorrent(url) {
|
||||
const status = document.getElementById('status');
|
||||
status.textContent = '⬇️ Открываем торрент...';
|
||||
window.open(url, '_blank');
|
||||
status.className = 'status success';
|
||||
status.textContent = `✅ Торрент открыт!`;
|
||||
closeModal();
|
||||
}
|
||||
|
||||
// Запуск при загрузке
|
||||
loadFilms();
|
||||
setInterval(loadFilms, 15 * 60 * 1000);
|
||||
20
sitemap-images.xml
Normal file
20
sitemap-images.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Sitemap для изображений - заполняется автоматически при обновлении контента
|
||||
https://developers.google.com/search/docs/advanced/sitemaps/image-sitemaps
|
||||
-->
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
|
||||
|
||||
<!-- Пример структуры (заполняется динамически) -->
|
||||
<!--
|
||||
<url>
|
||||
<loc>https://films.tolchin.pro/</loc>
|
||||
<image:image>
|
||||
<image:loc>https://films.tolchin.pro/images/poster1.jpg</image:loc>
|
||||
<image:title>Название фильма</image:title>
|
||||
</image:image>
|
||||
</url>
|
||||
-->
|
||||
|
||||
</urlset>
|
||||
16
sitemap-index.xml
Normal file
16
sitemap-index.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
|
||||
<!-- Основной sitemap -->
|
||||
<sitemap>
|
||||
<loc>https://films.tolchin.pro/sitemap.xml</loc>
|
||||
<lastmod>2026-02-24</lastmod>
|
||||
</sitemap>
|
||||
|
||||
<!-- Sitemap для изображений (когда постеры будут на своём домене) -->
|
||||
<sitemap>
|
||||
<loc>https://films.tolchin.pro/sitemap-images.xml</loc>
|
||||
<lastmod>2026-02-24</lastmod>
|
||||
</sitemap>
|
||||
|
||||
</sitemapindex>
|
||||
25
sitemap.xml
Normal file
25
sitemap.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
||||
xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0"
|
||||
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
|
||||
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
|
||||
xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
|
||||
|
||||
<!-- Главная страница -->
|
||||
<url>
|
||||
<loc>https://films.tolchin.pro/</loc>
|
||||
<changefreq>hourly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
<lastmod>2026-02-24</lastmod>
|
||||
</url>
|
||||
|
||||
<!-- Альтернативные языковые версии (если будут) -->
|
||||
<url>
|
||||
<loc>https://films.tolchin.pro/</loc>
|
||||
<xhtml:link rel="alternate" hreflang="ru" href="https://films.tolchin.pro/"/>
|
||||
<xhtml:link rel="alternate" hreflang="en" href="https://films.tolchin.pro/"/>
|
||||
<xhtml:link rel="alternate" hreflang="x-default" href="https://films.tolchin.pro/"/>
|
||||
</url>
|
||||
|
||||
</urlset>
|
||||
162
style.css
Normal file
162
style.css
Normal file
@@ -0,0 +1,162 @@
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #1a1a2e;
|
||||
min-height: 100vh;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.header { text-align: center; margin-bottom: 30px; }
|
||||
h1 { font-size: 2.5em; color: #00d4ff; }
|
||||
|
||||
.status {
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
border-radius: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.loading { background: #333; color: #ffd700; animation: pulse 1.5s infinite; }
|
||||
.success { background: #28a745; color: white; }
|
||||
.error { background: #dc3545; color: white; }
|
||||
.downloading { background: #007bff; color: white; }
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
.film-list {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.film-item {
|
||||
background: #16213e;
|
||||
padding: 15px;
|
||||
border-radius: 12px;
|
||||
border-left: 4px solid #00d4ff;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.film-item:hover {
|
||||
background: #0f3460;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.film-poster {
|
||||
width: 80px;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
border-radius: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.film-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.film-title {
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: #00d4ff;
|
||||
color: #1a1a2e;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.btn:hover { background: #00b8e6; }
|
||||
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
|
||||
.refresh-btn {
|
||||
background: #00d4ff;
|
||||
color: #1a1a2e;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.refresh-btn:hover { background: #00b8e6; }
|
||||
|
||||
/* Модальное окно выбора качества */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.8);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: #16213e;
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
margin-bottom: 20px;
|
||||
color: #00d4ff;
|
||||
}
|
||||
|
||||
.quality-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.quality-btn {
|
||||
background: #0f3460;
|
||||
color: white;
|
||||
border: 2px solid #00d4ff;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 1.1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.quality-btn:hover {
|
||||
background: #00d4ff;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.quality-close {
|
||||
margin-top: 15px;
|
||||
background: transparent;
|
||||
border: 1px solid #666;
|
||||
color: #999;
|
||||
}
|
||||
Reference in New Issue
Block a user