您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds upcoming episodes to the main page summary
// ==UserScript== // @name Edna.cz Future Schedule with Enhanced Countdown // @name:cs Edna.cz Budoucí Epizody s Odpočtem // @namespace https://gf.qytechs.cn/en/scripts/530726-edna-cz-future-schedule-with-enhanced-countdown // @version 0.93 // @description Adds upcoming episodes to the main page summary // @description:cs Přidá budoucí epizody do přehledu na hlavní stránce // @author Setcher // @match https://www.edna.cz/ // @match https://www.edna.cz/?* // @match https://www.edna.cz/# // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_getResourceText // @grant GM_addStyle // @require https://code.jquery.com/jquery-3.6.0.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.2/jquery.modal.min.js // @resource modalCSS https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.2/jquery.modal.min.css // ==/UserScript== (function() { 'use strict'; // Add modal CSS GM_addStyle(GM_getResourceText("modalCSS")); GM_addStyle(` .future-episode { opacity: 0.9; } .countdown-cell { text-align: right !important; white-space: nowrap; min-width: 120px; } .seen-episode { display: none; } #edna-settings-btn { margin-left: 0.5rem; } /* Dark mode for modal */ #edna-settings-modal { color: #f8f9fa; } #edna-settings-modal > div { background-color: #343a40 !important; border: 1px solid #495057; } #edna-settings-modal h2 { color: #f8f9fa !important; } #edna-settings-modal label { color: #dee2e6 !important; } #edna-settings-modal input[type="number"], #edna-settings-modal input[type="checkbox"] { background-color: #495057; border-color: #6c757d; color: #f8f9fa; } #edna-settings-modal .btn-secondary { background-color: #6c757d; border-color: #6c757d; } #edna-settings-modal .btn-primary { background-color: #0d6efd; border-color: #0d6efd; } /* Combine rating and seen columns for countdown */ .combined-countdown-column { display: flex; justify-content: space-between; align-items: center; min-width: 120px; } .combined-countdown-column .stars { margin-right: 8px; } `); // Settings const defaultSettings = { maxDaysInFuture: 7, maxFutureItems: 7, countdownFormat: 'DD:HH:MM:SS', hideSeenEpisodes: true }; let settings = { ...defaultSettings, ...{ maxDaysInFuture: GM_getValue('maxDaysInFuture', defaultSettings.maxDaysInFuture), maxFutureItems: GM_getValue('maxFutureItems', defaultSettings.maxFutureItems), countdownFormat: GM_getValue('countdownFormat', defaultSettings.countdownFormat), hideSeenEpisodes: GM_getValue('hideSeenEpisodes', defaultSettings.hideSeenEpisodes) } }; // Create settings modal function createSettingsModal() { const modalHTML = ` <div id="edna-settings-modal" class="modal" style="display: none;"> <div style="padding: 20px; border-radius: 5px; max-width: 500px; margin: 0 auto;"> <h2 style="margin-top: 0;">Nastavení budoucích epizod</h2> <div style="margin-bottom: 15px;"> <label style="display: block; margin-bottom: 5px; font-weight: bold;">Maximální dní dopředu:</label> <input type="number" id="edna-max-days" value="${settings.maxDaysInFuture}" min="1" max="60" style="width: 100%; padding: 8px; border-radius: 4px;"> </div> <div style="margin-bottom: 15px;"> <label style="display: block; margin-bottom: 5px; font-weight: bold;">Maximálně epizod:</label> <input type="number" id="edna-max-items" value="${settings.maxFutureItems}" min="1" max="100" style="width: 100%; padding: 8px; border-radius: 4px;"> </div> <div style="margin-bottom: 15px;"> <label style="display: block; margin-bottom: 8px; font-weight: bold;">Formát odpočtu:</label> <div style="display: flex; flex-direction: column; gap: 8px;"> <label style="display: flex; align-items: center; gap: 8px;"> <input type="radio" name="countdownFormat" value="DD:HH" ${settings.countdownFormat === 'DD:HH' ? 'checked' : ''}> <span>DD:HH (Dny a hodiny)</span> </label> <label style="display: flex; align-items: center; gap: 8px;"> <input type="radio" name="countdownFormat" value="DD:HH:MM" ${settings.countdownFormat === 'DD:HH:MM' ? 'checked' : ''}> <span>DD:HH:MM (Dny, hodiny, minuty)</span> </label> <label style="display: flex; align-items: center; gap: 8px;"> <input type="radio" name="countdownFormat" value="DD:HH:MM:SS" ${settings.countdownFormat === 'DD:HH:MM:SS' ? 'checked' : ''}> <span>DD:HH:MM:SS (Přesný odpočet)</span> </label> </div> </div> <div style="margin-bottom: 20px;"> <label style="display: flex; align-items: center; gap: 8px; cursor: pointer;"> <input type="checkbox" id="edna-hide-seen" ${settings.hideSeenEpisodes ? 'checked' : ''}> <span style="font-weight: bold;">Skrýt již zhlédnuté epizody</span> </label> </div> <div style="display: flex; justify-content: flex-end; gap: 10px;"> <button id="edna-cancel-settings" class="btn btn-secondary">Zrušit</button> <button id="edna-save-settings" class="btn btn-primary">Uložit</button> </div> </div> </div> `; $('body').append(modalHTML); // Add settings button to controls const settingsBtn = $(` <a href="#" class="nav-link" id="edna-settings-btn"> <i class="bi bi-gear-fill"></i> Nastavení </a> `); $('.custom-tabs').append(settingsBtn); // Modal handlers $('#edna-settings-btn').click(function(e) { e.preventDefault(); $('#edna-settings-modal').modal({ escapeClose: true, clickClose: false, showClose: false }); }); $('#edna-save-settings').click(function() { settings.maxDaysInFuture = parseInt($('#edna-max-days').val()); settings.maxFutureItems = parseInt($('#edna-max-items').val()); settings.countdownFormat = $('input[name="countdownFormat"]:checked').val(); settings.hideSeenEpisodes = $('#edna-hide-seen').is(':checked'); GM_setValue('maxDaysInFuture', settings.maxDaysInFuture); GM_setValue('maxFutureItems', settings.maxFutureItems); GM_setValue('countdownFormat', settings.countdownFormat); GM_setValue('hideSeenEpisodes', settings.hideSeenEpisodes); $.modal.close(); location.reload(); }); $('#edna-cancel-settings').click(function() { $.modal.close(); }); } // Main function to load and process future schedule function processFutureSchedule() { // Wait for the main schedule table to load const checkTable = setInterval(function() { const $scheduleTable = $('table.table-responsive'); if ($scheduleTable.length && $('#snippet--episodes').length) { clearInterval(checkTable); loadFutureSchedule(); } }, 200); } // Fix episode images (replace placeholder with data-src) function fixImages($element) { $element.find('img[data-src]').each(function() { const $img = $(this); if ($img.attr('src') !== $img.attr('data-src')) { $img.attr('src', $img.attr('data-src')); } }); } // Convert timestamp to countdown string function getCountdownString(timestamp) { const now = new Date().getTime(); const diff = timestamp - now; if (diff < 0) return "Právě vysíláno"; const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((diff % (1000 * 60)) / 1000); switch(settings.countdownFormat) { case 'DD:HH': return `${days}d ${hours}h`; case 'DD:HH:MM': return `${days}d ${hours}h ${minutes}m`; case 'DD:HH:MM:SS': default: return `${days}d ${hours}h ${minutes}m ${seconds}s`; } } // Update all countdowns function updateCountdowns() { $('.countdown-text').each(function() { const $cell = $(this); const timestamp = parseInt($cell.attr('data-countdown')); $cell.text(getCountdownString(timestamp)); }); } // Hide seen episodes if setting is enabled function handleSeenEpisodes() { if (settings.hideSeenEpisodes) { $('a[title^="Označit jako viděno"], a[title^="Označit jako neviděno"]').each(function() { const $link = $(this); const isWatched = $link.attr('title').startsWith("Označit jako neviděno"); if (isWatched) { $link.closest('tr').addClass('seen-episode'); } }); } else { $('.seen-episode').removeClass('seen-episode'); } } // Load and merge future episodes function loadFutureSchedule() { GM_xmlhttpRequest({ method: "GET", url: "https://www.edna.cz/vysilani/nadchazejici/", onload: function(response) { const $futureHtml = $($.parseHTML(response.responseText)); const $scheduleTable = $('table.table-responsive'); const $episodesContainer = $('#snippet--episodes'); if (!$scheduleTable.length || !$episodesContainer.length) { console.error('Required elements not found'); return; } const now = new Date(); const maxDate = new Date(now.getTime() + (settings.maxDaysInFuture * 24 * 60 * 60 * 1000)); let addedItems = 0; $futureHtml.find('#snippet--episodes table.episodes tr').each(function() { if (addedItems >= settings.maxFutureItems) return false; const $row = $(this); const $timeCell = $row.find('td[data-countdown]'); if ($timeCell.length) { const timestamp = parseInt($timeCell.attr('data-countdown')); const itemDate = new Date(timestamp); if (itemDate > now && itemDate <= maxDate) { // Extract show information const $showLink = $row.find('td:nth-child(3) a'); const showLink = $showLink.attr('href'); const showTitle = $showLink.text(); const episodeCode = $row.find('td:nth-child(4)').text(); const imgSrc = $row.find('img').attr('data-src'); const dateText = $timeCell.text().trim().split(' | ')[0]; const shortDate = dateText.split('.').slice(0, 2).join('.'); // Create new row matching the current layout const $newRow = $(` <tr class="future-episode"> <td class="text-secondary text-nowrap ps-3 text-start" title="${dateText}">${shortDate}</td> <td onclick="location.href='${showLink}'" class="cursor-pointer" href="${showLink}"> <div class="d-flex align-items-center"> <div class="latest-series-image position-relative rounded-circle overflow-hidden"> <img src="${imgSrc}" alt="${showTitle}" class="position-absolute top-0 start-0 w-100"> </div> <div class="ms-3"> <a class="text-reset text-decoration-none" href="${showLink}"> <p class="m-0 my-auto text-start text-white">${episodeCode} ${showTitle}</p> </a> </div> </div> </td> <td class="pe-3 combined-countdown-column"> <span class="countdown-text" data-countdown="${timestamp}" title="${$timeCell.text().trim()}"></span> </td> </tr> `); $episodesContainer.prepend($newRow); fixImages($newRow); addedItems++; } } }); if (addedItems > 0) { handleSeenEpisodes(); updateCountdowns(); const updateInterval = settings.countdownFormat === 'DD:HH:MM:SS' ? 1000 : 60000; setInterval(updateCountdowns, updateInterval); } }, onerror: function(error) { console.error('Error loading future schedule:', error); } }); } // Initialize $(document).ready(function() { createSettingsModal(); // Wait a bit longer to ensure all elements are loaded setTimeout(processFutureSchedule, 1500); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址