您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Додає рейтинги епізодів з IMDb до HDRezka через OMDB API
// ==UserScript== // @name HDRezka IMDb Episode Ratings (OMDB API) // @namespace http://tampermonkey.net/ // @version 2.3 // @description Додає рейтинги епізодів з IMDb до HDRezka через OMDB API // @author You // @match https://hdrezka.me/series/* // @exclude https://hdrezka.me/series/?filter=watching // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @license MIT // ==/UserScript== (function() { 'use strict'; if (window.location.search.includes('filter=watching')) return; // Ваш API ключ для OMDB const OMDB_API_KEY = YOU_KEY; // Кеш для зберігання рейтингів const ratingsCache = {}; // Додаємо CSS стилі для рейтингів function addRatingStyles() { if (document.getElementById('hdrezka-rating-styles')) return; const style = document.createElement('style'); style.id = 'hdrezka-rating-styles'; style.textContent = ` .td-rating .rating-value { padding-left: 29px; background-size: auto 24px; background-position: left center; background-repeat: no-repeat; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABmJLR0QA/wD/AP+gvaeTAAAEu0lEQVR4nO2czYscRRiHn+nxY7ORdc1ulGgQYoxGNwbEKIoHDxpQNGxEURQPQQ34D/gRCFE8hJCDIIoHz7noRTeCB4MmB0OMHxFRE0wkStCIGM0SV1fDJuOhetze2uqu/piZ7u35PfAyzLxVv3q73uma6uquASGEEEIIIYQQQgghhBBCCCGEEEIIIUTdaCT4LgM2AuPAamA5sLgXQdWAv4CfgCPAbuBd4HTayouALcAk0JJ1xE4DLwADvs6/Evi0AgHX1b4Erk7q/BMVCLLudiLs6zksAj6rQHD9YgcJh6NmmIDngSfsrIiucRUwBexvYGY7PwCXlhpS/zEJXBMAD6LOL4NhYDwANpQdSR+zIQDGyo6ijxlrAH8Cl5QdSZ8y1cBMi7LyD7ATM3W9DXiWuVd508AO4BBwR+i/MIP+v8B2zHTtduZfRbbbPwDMRD4fAJYBK4H7gTUdOp6ukmce+5Sl8bTl32T57wPOptQ+C9xr1X/S034c1wG7gHMFj6eblqvSkBXwkOV3LdptAs57dM8zP3mEeknt+1gHHC9wPJVLgAufH2CrR3drQt00+kmMAh/HtOtrrzYJAHgtRvNNT72iCQDzzf7W0bavvVoloAlMWOUnmF0WicOnvwd4D3gVuJv4ex3XA3+n0KttAgAGMTOYVvg6mKJO1g47AFwbU3ZHCr17LHsA2Iz5Uf/D0d6CSgCYMXl3+JqGPN/Y33FfaA4DZzLGG2UxsI35Z9KCSkBW8iSgBXwHXOQo/5ZHLw23Aj8ntF2pBKxLeVBx5fImoAU84yj/uEcvLWsxqwmVT8CvmCvUJFaG5fLoJ8X7kaP8Wo9eFjZ72o+1vEsRrhlGK4X/GHAn8JvDvxTYD6wqoB/HqVA/ymgkDpfeHuv9NPAhZro8bfmawNfADQkxxNKrM6Dt+4T5s57B8PNO6LvM7jAwaz159D7H3MK12eKJofQhKOr/gNnZz9LwfSf1bTvqKL+igN5OR/lbPHUqlYAWZgXySPjaDf2oveEof1cBvZOO8qOeOpVLQK/0Z4AbHeVfLhCvb0hLbUFM43ViO3DY8fnGAppfOD5blleszmfAK4DrS7a+QLznwvo2D2U8xtoOQTPAXsyCnIsAc6cuT7y/AA/HlN/lOSanXRAjthBZj1n7+ZHkJ5FfAm5OqRflFPANc2+BtlkFPJJC00ldzoA0PIb7rlwRmpiLtjz9mHsIssfVIKO/0/o+Gpgb+3H3hvNORhrA6zGaXU2AvcS7xuO/qcv6SYwB72dsLw0jwDsZjqmjCdgLLAkDWQLss/z7Iv4Rh7+oftTvYgh4FHgbM2Znac/HauBFzO9M0c7PvRgH5uneY5gfINeDXVPA96E/z9amNPpfMfeiaAQzH7+C5O1Xce3ZelFGME81X55RN5EiCRAdIMBkXpTDmQD3wpLoDScD3OskojccDjBPJohymGhvUTqOeVRD9I5JYEUTc0MkIH7xSnSHbUQeFhhAG7R7af9vU42ijdq9MedG7WgSDlYgyLraIRL+qqDNAGbjdkfWOmS0wr58DrjY7uyk9ZJhzF/VjGMeOFqONvOlZYrZv6uZCG2y1IiEEEIIIYQQQgghhBBCCCGEEEIIIYQQPeM/uURowB/mrvMAAAAASUVORK5CYII='); color: #666; } .td-rating .rating-value.low { filter: invert(30%) sepia(53%) saturate(2254%) hue-rotate(337deg) brightness(97%) contrast(95%); } .td-rating .rating-value.medium { filter: invert(66%) sepia(77%) saturate(1448%) hue-rotate(347deg) brightness(99%) contrast(91%); } .td-rating .rating-value.high { filter: invert(68%) sepia(79%) saturate(5115%) hue-rotate(105deg) brightness(99%) contrast(99%); } `; document.head.appendChild(style); } // Функція для отримання назви серіалу зі сторінки function getSeriesTitle() { // Шукаємо англійську назву в елементі b-post__origtitle const origTitleElement = document.querySelector('.b-post__origtitle'); if (origTitleElement) { let titleText = origTitleElement.textContent.trim(); if (titleText) { // Якщо назва містить слеш, беремо частину після слешу (англійська назва) if (titleText.includes(' / ')) { const parts = titleText.split(' / '); titleText = parts[parts.length - 1].trim(); // Остання частина після останнього слешу } console.log('🎬 Знайдено оригінальну назву:', titleText); return titleText; } } // Fallback - шукаємо в мета-тегах const ogTitle = document.querySelector('meta[property="og:title"]'); if (ogTitle) { const ogContent = ogTitle.getAttribute('content'); // Витягуємо англійську частину з og:title const englishMatch = ogContent.match(/\(([^)]+)\)\s*\d{4}/); if (englishMatch) { return englishMatch[1].trim(); } } // Якщо не знайшли, шукаємо в основній інформації const infoItems = document.querySelectorAll('.b-post__info tr'); for (let item of infoItems) { const label = item.querySelector('td:first-child'); const value = item.querySelector('td:last-child'); if (label && value) { const labelText = label.textContent.trim().toLowerCase(); if (labelText.includes('оригинальное название') || labelText.includes('original title')) { return value.textContent.trim(); } } } return null; } // Функція для пошуку серіалу через OMDB API async function searchSeriesOMDB(title) { const cacheKey = `omdb_series_${title}`; const cached = GM_getValue(cacheKey); if (cached) { const cachedData = JSON.parse(cached); if (Date.now() - cachedData.timestamp < 24 * 60 * 60 * 1000) { // 24 години кеш console.log('📋 Використовуємо кеш для серіалу:', cachedData.data); return cachedData.data; } } const searchUrl = `https://www.omdbapi.com/?apikey=${OMDB_API_KEY}&t=${encodeURIComponent(title)}&type=series`; console.log('🔍 Пошук серіалу через OMDB:', searchUrl); return new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url: searchUrl, onload: function(response) { console.log('📥 Відповідь OMDB для серіалу:', response.status); try { const data = JSON.parse(response.responseText); if (data.Response === 'True' && data.imdbID) { console.log('✅ Серіал знайдено:', data.Title, data.imdbID); const seriesData = { imdbID: data.imdbID, title: data.Title, totalSeasons: data.totalSeasons || 'N/A' }; // Зберігаємо в кеш GM_setValue(cacheKey, JSON.stringify({ data: seriesData, timestamp: Date.now() })); resolve(seriesData); } else { console.log('❌ Серіал не знайдено:', data.Error || 'Невідома помилка'); resolve(null); } } catch (e) { console.error('❌ Помилка парсингу відповіді OMDB:', e); resolve(null); } }, onerror: function(error) { console.error('❌ Помилка запиту до OMDB:', error); resolve(null); } }); }); } // Функція для отримання рейтингів сезону через OMDB API async function getSeasonRatings(imdbID, season) { const cacheKey = `omdb_season_${imdbID}_${season}`; const cached = ratingsCache[cacheKey] || GM_getValue(cacheKey); if (cached) { const cachedData = typeof cached === 'string' ? JSON.parse(cached) : cached; if (Date.now() - cachedData.timestamp < 24 * 60 * 60 * 1000) { // 24 години кеш console.log(`📋 Використовуємо кеш для сезону ${season}:`, cachedData.data); return cachedData.data; } } const seasonUrl = `https://www.omdbapi.com/?apikey=${OMDB_API_KEY}&i=${imdbID}&Season=${season}`; console.log(`🔍 Завантажуємо сезон ${season}:`, seasonUrl); return new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url: seasonUrl, onload: function(response) { console.log(`📥 Відповідь OMDB для сезону ${season}:`, response.status); try { const data = JSON.parse(response.responseText); if (data.Response === 'True' && data.Episodes) { console.log(`✅ Сезон ${season} завантажено, епізодів: ${data.Episodes.length}`); // Створюємо об'єкт для швидкого пошуку const episodeRatings = {}; data.Episodes.forEach(episode => { episodeRatings[parseInt(episode.Episode)] = { rating: episode.imdbRating !== 'N/A' ? parseFloat(episode.imdbRating) : null, title: episode.Title, imdbID: episode.imdbID }; }); // Зберігаємо в кеш const seasonData = { episodes: episodeRatings, totalEpisodes: data.Episodes.length }; ratingsCache[cacheKey] = { data: seasonData, timestamp: Date.now() }; GM_setValue(cacheKey, JSON.stringify(ratingsCache[cacheKey])); resolve(seasonData); } else { console.log(`❌ Сезон ${season} не знайдено:`, data.Error || 'Невідома помилка'); resolve(null); } } catch (e) { console.error(`❌ Помилка парсингу сезону ${season}:`, e); resolve(null); } }, onerror: function(error) { console.error(`❌ Помилка запиту сезону ${season}:`, error); resolve(null); } }); }); } // Функція для отримання класу рейтингу (high/medium/low) function getRatingClass(rating) { if (rating >= 7.5) return 'high'; if (rating >= 6.0) return 'medium'; return 'low'; } // Функція для створення HTML рейтингу function createRatingHTML(rating) { if (rating === null || rating === undefined) { return '<span class="rating-value">N/A</span>'; } const ratingClass = getRatingClass(rating); return `<span class="rating-value ${ratingClass}"><b>${rating.toFixed(1)}</b></span>`; } // Функція для додавання колонки рейтингів до всіх таблиць function addRatingColumns() { const tables = document.querySelectorAll('.b-post__schedule_table'); if (tables.length === 0) return false; let addedCount = 0; tables.forEach(table => { // Перевіряємо чи вже додана колонка в цій таблиці if (table.querySelector('.td-rating')) { addedCount++; return; // Колонка вже існує в цій таблиці } const rows = table.querySelectorAll('tbody tr'); rows.forEach(row => { if (row.querySelector('.load-more')) return; const ratingTd = document.createElement('td'); ratingTd.className = 'td-rating'; ratingTd.style.cssText = 'text-align: center; min-width: 70px; padding: 5px;'; ratingTd.innerHTML = '<span class="rating-value">⏳</span>'; // Вставляємо між td-2 і td-3 const td2 = row.querySelector('.td-2'); const td3 = row.querySelector('.td-3'); if (td2 && td3) { row.insertBefore(ratingTd, td3); } }); if (rows.length > 0) addedCount++; }); console.log(`📊 Додано колонки рейтингів до ${addedCount} таблиць з ${tables.length}`); return addedCount > 0; } // Функція для парсингу номеру сезону та серії function parseEpisodeInfo(episodeText) { const match = episodeText.match(/(\d+)\s+сезон\s+(\d+)\s+серия/); if (match) { return { season: parseInt(match[1]), episode: parseInt(match[2]) }; } return null; } // Функція для оновлення всіх рейтингів одним значенням function updateAllRatings(value) { const ratingCells = document.querySelectorAll('.td-rating'); ratingCells.forEach(cell => { cell.innerHTML = `<span class="rating-value">${value}</span>`; }); } // Головна функція для завантаження рейтингів async function loadRatings() { const seriesTitle = getSeriesTitle(); if (!seriesTitle) { console.log('❌ Не вдалося знайти назву серіалу'); updateAllRatings('N/A'); return; } console.log('🔍 Шукаємо серіал:', seriesTitle); const seriesData = await searchSeriesOMDB(seriesTitle); if (!seriesData || !seriesData.imdbID) { console.log('❌ Серіал не знайдено на OMDB'); updateAllRatings('N/A'); return; } console.log('✅ Знайдено серіал:', seriesData.title, seriesData.imdbID); const tables = document.querySelectorAll('.b-post__schedule_table'); if (tables.length === 0) { console.log('❌ Таблиці серій не знайдені'); return; } console.log(`📋 Знайдено ${tables.length} таблиць з сезонами`); console.log(`📋 Знайдено ${tables.length} таблиць з сезонами`); // Збираємо інформацію про всі епізоди з усіх таблиць const episodesInfo = []; const seasonNumbers = new Set(); tables.forEach((table, tableIndex) => { const rows = table.querySelectorAll('tbody tr'); console.log(`📊 Таблиця ${tableIndex + 1}: ${rows.length} рядків`); rows.forEach(row => { if (row.querySelector('.load-more')) return; const episodeCell = row.querySelector('.td-1'); if (!episodeCell) return; const episodeInfo = parseEpisodeInfo(episodeCell.textContent); if (!episodeInfo) return; const ratingCell = row.querySelector('.td-rating'); if (!ratingCell) return; episodesInfo.push({ ...episodeInfo, ratingCell: ratingCell, tableIndex: tableIndex }); seasonNumbers.add(episodeInfo.season); }); }); console.log(`📊 Знайдено ${episodesInfo.length} епізодів у ${seasonNumbers.size} сезонах`); // Завантажуємо рейтинги для кожного сезону const seasonRatings = {}; for (let seasonNumber of seasonNumbers) { console.log(`🎬 Завантажуємо сезон ${seasonNumber}...`); const seasonData = await getSeasonRatings(seriesData.imdbID, seasonNumber); if (seasonData) { seasonRatings[seasonNumber] = seasonData.episodes; console.log(`✅ Сезон ${seasonNumber} завантажено з ${seasonData.totalEpisodes} епізодами`); } else { console.log(`❌ Сезон ${seasonNumber} не завантажено`); seasonRatings[seasonNumber] = {}; } // Затримка між сезонами await new Promise(resolve => setTimeout(resolve, 300)); } // Оновлюємо рейтинги для кожного епізоду let updatedCount = 0; for (let episodeInfo of episodesInfo) { const seasonEpisodes = seasonRatings[episodeInfo.season]; if (seasonEpisodes && seasonEpisodes[episodeInfo.episode]) { const episodeData = seasonEpisodes[episodeInfo.episode]; episodeInfo.ratingCell.innerHTML = createRatingHTML(episodeData.rating); if (episodeData.rating !== null) { console.log(`✅ S${episodeInfo.season}E${episodeInfo.episode}: ${episodeData.rating}`); updatedCount++; } else { console.log(`❌ S${episodeInfo.season}E${episodeInfo.episode}: рейтинг недоступний`); } } else { episodeInfo.ratingCell.innerHTML = createRatingHTML(null); console.log(`❌ S${episodeInfo.season}E${episodeInfo.episode}: епізод не знайдено`); } } console.log(`🏁 Завершено! Оновлено ${updatedCount} з ${episodesInfo.length} епізодів`); } // Ініціалізація let isInitialized = false; function init() { // Запобігаємо подвійному виклику if (isInitialized) return; // Перевіряємо чи є таблиця з серіями const scheduleBlock = document.querySelector('.b-post__schedule_block'); if (!scheduleBlock) return; // Додаємо CSS стилі addRatingStyles(); const columnsAdded = addRatingColumns(); if (!columnsAdded) return; isInitialized = true; console.log('🚀 Ініціалізація HDRezka IMDb рейтингів...'); loadRatings(); } // Запускаємо скрипт після завантаження сторінки if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { // Невелика затримка для динамічного контенту setTimeout(init, 1000); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址