commit 7b75373a57ec642c073a772831d7517a29d27f14 Author: Oleg Tolchin Date: Tue Feb 24 16:00:09 2026 +0300 Initial commit - ColdFilm parser diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..702114b --- /dev/null +++ b/.htaccess @@ -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] + +# Кэширование статических файлов + + 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" + + +# Сжатие + + AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript + + +# Защита от прямого доступа к конфигурационным файлам + + Order allow,deny + Deny from all + + +# Безопасные заголовки + + Header set X-Content-Type-Options "nosniff" + Header set X-Frame-Options "SAMEORIGIN" + Header set X-XSS-Protection "1; mode=block" + + +# Правила для SEO +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^$ index.html [L] diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb50b82 --- /dev/null +++ b/README.md @@ -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 + +## Автор + +Для вопросов и предложений: свяжитесь с автором diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 0000000..d01a0f7 --- /dev/null +++ b/favicon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..0fd3a02 --- /dev/null +++ b/index.html @@ -0,0 +1,30 @@ + + + + + + 🎬 ColdFilm + + + + +
+

🎥 Новые релизы ColdFilm

+
+ +
🔄 Инициализация...
+ + + + + + + + + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..8a6f67e --- /dev/null +++ b/robots.txt @@ -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 diff --git a/script.js b/script.js new file mode 100644 index 0000000..5b533c6 --- /dev/null +++ b/script.js @@ -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 = [ + ']+href="(/news/[^"#]+)"[^>]*>\\s*]+src="([^"]+)"', + ']+src="([^"]+)"[^>]*>\\s*]+href="(/news/[^"#]+)"', + 'class="kino-h"[^>]*title="[^"]*"[^>]*>[\\s\\S]*?]+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(']+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(']+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); diff --git a/sitemap-images.xml b/sitemap-images.xml new file mode 100644 index 0000000..7310475 --- /dev/null +++ b/sitemap-images.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/sitemap-index.xml b/sitemap-index.xml new file mode 100644 index 0000000..787be14 --- /dev/null +++ b/sitemap-index.xml @@ -0,0 +1,16 @@ + + + + + + https://films.tolchin.pro/sitemap.xml + 2026-02-24 + + + + + https://films.tolchin.pro/sitemap-images.xml + 2026-02-24 + + + diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..07b89a8 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,25 @@ + + + + + + https://films.tolchin.pro/ + hourly + 1.0 + 2026-02-24 + + + + + https://films.tolchin.pro/ + + + + + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..0e22e82 --- /dev/null +++ b/style.css @@ -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; +}