您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
metro ui forever
// ==UserScript== // @namespace http://leopardindustries.net // @name NewMetro UI for Netflix // @version 1.4.5 // @license MIT // @match https://www.netflix.com/* // @match https://www.netflix.com/watch/* // @grant none // @grant GM_xmlhttpRequest // @connect unogs.com // @connect openweathermap.org // @connect corsproxy.io // @connect omdbapi.com // @icon https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcROBF8-UheUPgFsPHy112UgmBWtXIma2jNBm1xD2q7UW__i9cLU6nM5LrxnsQ&s // @description metro ui forever // ==/UserScript== (() => { 'use strict'; // Modifying this script is one hundered percent allowed but may break it eventually // To support the old UI without the registration popup, please purchase the 99 cent license // I will provide support for this project for the forseeable features // Updates also require a valid license // You’ll need to edit the stylesheet if using it independently of the script (() => { 'use strict'; const apiKey = "d06b19a448302595d8a8226d6cf6bee5"; const zipCode = "53074"; // change to your zip const countryCode = "us"; let lastFetchTime = 0; let tempEl = null; async function insertTemperature() { const now = Date.now(); // Only fetch again if more than 10 minutes (600,000 ms) passed if (now - lastFetchTime < 600000 && tempEl && document.body.contains(tempEl)) { return; // Too soon, and tempEl is still attached } try { const response = await fetch( `https://api.openweathermap.org/data/2.5/weather?zip=${zipCode},${countryCode}&appid=${apiKey}&units=imperial` ); if (!response.ok) throw new Error("weather fetch issue"); const data = await response.json(); const tempF = Math.round(data.main.temp); // Remove old element if it exists if (tempEl && tempEl.parentNode) { tempEl.parentNode.removeChild(tempEl); } // Create and style new element tempEl = document.createElement("div"); tempEl.textContent = `Weather -- ${tempF}°F`; tempEl.style.fontWeight = "bold"; tempEl.style.position = "relative"; tempEl.style.bottom = "5px"; tempEl.style.right = "0px"; // Insert next to your button const btn = document.querySelector("button.searchTab"); if (btn && btn.parentNode) { btn.parentNode.insertBefore(tempEl, btn); } // Update fetch time lastFetchTime = now; } catch (err) { console.error("weather error - ", err); } } let updateClockInterval = null; let mutationObserver = null; function styleAndUpdateClock(button) { if (!button) return; Object.assign(button.style, { padding: '6px 12px', fontSize: '15px', background: 'black', color: '#fff', border: '1px solid #444', borderRadius: '0px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: '9999', userSelect: 'none', }); let clockSpan = button.querySelector('#clock-span'); if (!clockSpan) { clockSpan = document.createElement('span'); clockSpan.id = 'clock-span'; clockSpan.style.fontSize = '16px'; button.appendChild(clockSpan); } function updateClock() { const now = new Date(); const hrs = String(now.getHours()).padStart(2, '0'); const mins = String(now.getMinutes()).padStart(2, '0'); clockSpan.textContent = `${hrs}:${mins}`; } updateClock(); if (updateClockInterval) clearInterval(updateClockInterval); updateClockInterval = setInterval(updateClock, 1000); } function applyClockToSearch() { const button = document.querySelector('button.searchTab'); if (!button) return false; // Prevent recursion by disconnecting observer during update if (mutationObserver) mutationObserver.disconnect(); button.innerHTML = 'Click here to search -- '; styleAndUpdateClock(button); // Reconnect observer after update if (mutationObserver) { mutationObserver.observe(button, { childList: true, subtree: true }); } return true; } function startObserving() { const button = document.querySelector('button.searchTab'); if (!button) return false; applyClockToSearch(); insertTemperature(); mutationObserver = new MutationObserver(() => { applyClockToSearch(); insertTemperature() }); mutationObserver.observe(button, { childList: true, subtree: true }); return true; } const waitForButton = setInterval(() => { if (startObserving()) clearInterval(waitForButton); }, 100); })(); (function () { const LICENSE_URL = "https://corsproxy.io/?" + encodeURIComponent("https://pastebin.com/raw/Ujb6sHm5"); const LICENSE_KEY = "newmetro_license_email"; const blurID = "nm-blur"; const popupID = "nm-popup"; const hudID = "nm-hud"; // convert floats function parseFloatToBigIntFraction(floatNum) { const numStr = floatNum.toString(); const decimalIndex = numStr.indexOf('.'); if (decimalIndex === -1) { return { numerator: BigInt(numStr), denominator: 1n }; } const integerPartStr = numStr.substring(0, decimalIndex); const decimalPartStr = numStr.substring(decimalIndex + 1); const denominator = 10n ** BigInt(decimalPartStr.length); const numerator = BigInt(integerPartStr + decimalPartStr); return { numerator, denominator }; } function multiplyHexByShift(hexString, shiftFactor) { try { const hexBigInt = BigInt('0x' + hexString); const { numerator: shiftNumerator, denominator: shiftDenominator } = parseFloatToBigIntFraction(shiftFactor); // perform multiplication const resultBigInt = (hexBigInt * shiftNumerator) / shiftDenominator; return resultBigInt.toString(16).toUpperCase(); } catch (e) { // This will catch any errors during BigInt operations or conversions throw new Error("fail multiply hexes with " + e.message); } } if (!document.getElementById("nm-style")) { const style = document.createElement("style"); style.id = "nm-style"; style.textContent = ` .lolomo.nm-hide { opacity: 0; brightness: 0%; visibility: hidden; display: none !important; zIndex: -1000 !important; } body.nm-scroll-lock { overflow: hidden !important; height: 100% !important; } #${blurID} { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.4); backdrop-filter: blur(8px); z-index: 98; opacity: 0; transition: opacity 0.2s ease; } #${popupID} { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: black; color: white; border: 1px solid white; padding: 20px; font-family: sans-serif; text-align: center; z-index: 99; width: 280px; opacity: 0; transition: opacity 0.2s ease; font-size: 18px; text-transform: lowercase; } #${popupID} > div.title { font-weight: normal; margin-bottom: 12px; } #${popupID} > div.title b.newmetro { font-weight: bold; color: skyblue; } #${popupID} > div.title b.netflix { font-weight: bold; color: darkred; } #${popupID} input { margin-top: 10px; width: 90%; padding: 6px; border: 2px solid red; background: black; color: white; font-size: 16px; z-index: 100; text-transform: lowercase; } #${popupID} input:focus { outline: none; box-shadow: none; } .nm-button-row { display: flex; gap: 10px; justify-content: space-between; margin-top: 10px; text-transform: lowercase; } #${popupID} button { flex: 1; padding: 8px; font-size: 12px; border: 1px solid white; background: #111; color: white; cursor: pointer; text-transform: lowercase; } #${hudID} { position: fixed; bottom: 10px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.7); color: white; border: 1px solid white; font-size: 12px; padding: 6px 12px; z-index: 99997; opacity: 0; transition: opacity 0.2s ease; text-transform: lowercase; } `; document.head.appendChild(style); } function lockScroll(lock) { if (lock) { document.body.classList.add("nm-scroll-lock"); } else { document.body.classList.remove("nm-scroll-lock"); } } function showHUD(message, duration = 2000) { let hud = document.getElementById(hudID); if (!hud) { hud = document.createElement("div"); hud.id = hudID; document.body.appendChild(hud); } hud.textContent = message.toLowerCase(); hud.style.opacity = "1"; setTimeout(() => { hud.style.opacity = "0"; }, duration); } // Helper function to convert string to uppercase hex function toUppercaseHex(str) { return Array.from(str).map(char => { return char.charCodeAt(0).toString(16).toUpperCase(); }).join(''); } function showActivationPopup() { if (document.getElementById(popupID)) return; lockScroll(true); const blur = document.createElement("div"); blur.id = blurID; const popup = document.createElement("div"); popup.id = popupID; document.querySelector(".lolomo")?.classList.add("nm-hide"); popup.innerHTML = ` <div class="title">pls activate <b class="newmetro">newmetro</b> for <b class="netflix">netflix</b><br> but first make sure <a style="textDecoration: none; color:green" href="https://tinyurl.com/newmetdoc">this</a></div> <input id="nm-email-input" type="email" placeholder="[email protected]" autocorrect="off" autocapitalize="off" spellcheck="false"> <input id="nm-shift-input" type="number" min="1" value="1" max="10" placeholder="shift" style="margin-top: 10px; width: 50%; padding: 6px; border: 2px solid red; background: black; color: white; font-size: 16px; width: 75%"> <div class="nm-button-row"> <button id="nm-submit-btn">check</button> <button id="nm-free-btn">use for free</button> </div> `; document.body.append(blur, popup); const emailInput = document.getElementById("nm-email-input"); const checkBtn = document.getElementById("nm-submit-btn"); emailInput.addEventListener("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); checkBtn.click(); } }); requestAnimationFrame(() => { blur.style.opacity = "1"; popup.style.opacity = "1"; }); document.getElementById("nm-submit-btn").onclick = () => { console.log('dbg log check clicked'); const emailInputElem = document.getElementById("nm-email-input"); const shiftInputElem = document.getElementById("nm-shift-input"); if (!emailInputElem || !shiftInputElem) { showHUD("setup error"); return; } const emailInputVal = emailInputElem.value.trim().toLowerCase(); const shiftInputVal = shiftInputElem.value; if (!emailInputVal || !emailInputVal.includes("@")) { showHUD("invalid email"); return; } const parsedShift = parseFloat(shiftInputVal); if (isNaN(parsedShift) || parsedShift < 0 || parsedShift > 10) { showHUD("invalid shift"); return; } let baseHex = ''; let finalStoredHex = ''; try { baseHex = toUppercaseHex(emailInputVal); } catch (e) { showHUD("hex conversion error"); return; } try { finalStoredHex = multiplyHexByShift(baseHex, parsedShift); } catch (e) { showHUD("hex multiplication error"); return; } // Your original log for cryptography, kept as requested console.log("dbg log cryptography", `"${finalStoredHex}"`); try { localStorage.setItem(LICENSE_KEY, finalStoredHex); } catch (e) { showHUD("storage error"); return; } checkLicense(true); }; document.getElementById("nm-free-btn").onclick = () => { // Hide popup and unlock scroll lockScroll(false); blur.style.opacity = "0"; popup.style.opacity = "0"; setTimeout(() => { blur.remove(); popup.remove(); document.querySelector(".lolomo")?.classList.remove("nm-hide"); showHUD("using free mode"); }, 200); }; } async function fetchLicenseList() { try { // Generate a unique timestamp for cache-busting const timestamp = Date.now(); // Append the timestamp as a query parameter to the LICENSE_URL const cacheBustedLicenseUrl = `${LICENSE_URL}?_=${timestamp}`; const res = await fetch(cacheBustedLicenseUrl, { cache: 'no-store' // Explicitly tell the browser not to use or store in cache }); if (!res.ok) throw new Error("Fetch failed " + res.status); const text = await res.text(); if ( !text || text.trim().length === 0 || text.includes("404") || text.includes("500") ) { return null; } // Ensure each line is trimmed and converted to uppercase for comparison return text.split("\n").map(e => e.trim().toUpperCase()).filter(Boolean); } catch (error) { return null; } } async function checkLicense(skipPopup = false) { showHUD("starting license check..."); await new Promise(r => setTimeout(r, 600)); // Retrieve the saved email (which is already in uppercase hex and potentially repeated) const saved = localStorage.getItem(LICENSE_KEY); if (!saved) { if (!skipPopup) showActivationPopup(); return; } const list = await fetchLicenseList(); // This list should contain the *repeated* hexes if (!list) { showHUD("failed fetch list"); document.querySelector(".lolomo")?.classList.add("nm-hide"); if (!skipPopup) showActivationPopup(); return; } // Directly compare the saved (repeated) hex with the list (containing repeated hexes) if (!list.includes(saved)) { localStorage.removeItem(LICENSE_KEY); showHUD("not in list"); document.querySelector(".lolomo")?.classList.add("nm-hide"); if (!skipPopup) showActivationPopup(); return; } showHUD("license valid"); // Remove popup and blur if present, unlock scroll document.querySelector(".lolomo")?.classList.remove("nm-hide"); document.getElementById(blurID)?.remove(); document.getElementById(popupID)?.remove(); lockScroll(false); } window.addEventListener("load", () => checkLicense()); })(); const OMDB_API_KEY = '678ad194'; const SELECTOR = '.ltr-m1ta4i.medium > h4'; let storedTitle = null; let storedYear = null; async function fetchYear(title) { try { const response = await fetch(`https://www.omdbapi.com/?apikey=${OMDB_API_KEY}&t=${encodeURIComponent(title)}`); const data = await response.json(); if (data.Response === 'True') { return data.Year; } else { console.log('omdb api fail:', data.Error); return null; } } catch (e) { console.error('fetch data error:', e); return null; } } async function checkTitleAndYear() { let elem = document.querySelector('.ltr-m1ta4i.medium > h4, .default-ltr-iqcdef-cache-m1ta4i.medium > h4'); let currentTitle = null; if (elem) { currentTitle = elem.textContent.trim(); } else { elem = document.querySelector('.ltr-m1ta4i.medium, .default-ltr-iqcdef-cache-m1ta4i.medium'); if (elem) { currentTitle = elem.textContent.trim(); } else { console.error('all title elements missing...'); return; } } if (storedTitle === null) { // First time storedTitle = currentTitle; storedYear = await fetchYear(currentTitle); console.log(`fetched as "${storedTitle}" with year (${storedYear})`); } else if (currentTitle !== storedTitle) { // Title changed, fetch again once storedTitle = currentTitle; storedYear = await fetchYear(currentTitle); console.log(`title changed, year went to ${storedYear}`); } else { // Title same, keep stored year console.log(`title unchanged, year remains`); } if (storedYear !== null) { const container = document.querySelector('.ltr-lapyk4, .default-ltr-iqcdef-cache-lapyk4'); if (container) { let yearElem = container.querySelector('.release-year'); if (!yearElem) { yearElem = document.createElement('div'); yearElem.className = 'release-year'; yearElem.style.marginTop = '4px'; yearElem.style.fontWeight = 'bold'; yearElem.style.color = 'white'; yearElem.style.fontSize = '0.9em'; container.appendChild(yearElem); } yearElem.textContent = storedYear; } } } async function waitAndCheck() { const startTime = Date.now(); const timeout = 30000; // 30 seconds const interval = 5000; // 5 seconds } waitAndCheck(); function getNetflixTitle() { const docTitle = document.title; const match = docTitle.match(/^(.*?)\s*-\s*Netflix$/i); return match ? match[1].trim() : null; } function getTitleId() { const url = new URL(window.location.href); const jbv = url.searchParams.get('jbv'); if (jbv && jbv.length === 8) { return jbv; } const pathname = url.pathname.replace(/\/+$/, ''); if (pathname.length >= 8) { return pathname.slice(-8); } return null; } function decodeHtmlEntities(str) { const txt = document.createElement("textarea"); txt.innerHTML = str; return txt.value; } async function fetchTitle(titleId) { if (!titleId) return null; try { const timestamp = Date.now(); const proxiedUrl = `https://corsproxy.io/?${encodeURIComponent(`https://unogs.com/title/${titleId}`)}`; const res = await fetch(proxiedUrl, { cache: 'no-store' }); const html = await res.text(); const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); const rawTitle = doc.querySelector("title")?.innerText || ""; const match = rawTitle.match(/^(.*?)\s+on\s+Netflix/i); return match ? decodeHtmlEntities(match[1].trim()) : null; } catch { return null; } } function makeSearchQuery(str) { // keep only letters, numbers, spaces return str.replace(/[^\p{L}\p{N}\s]/gu, "").trim().replace(/\s+/g, " "); } function createModal() { if (document.getElementById('fakeDownloadModal')) return; const overlay = document.createElement('div'); overlay.id = 'fakeDownloadModalOverlay'; Object.assign(overlay.style, { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.5)', backdropFilter: 'blur(4px)', display: 'none', justifyContent: 'center', alignItems: 'center', zIndex: 99999, }); const modal = document.createElement('div'); modal.id = 'fakeDownloadModal'; Object.assign(modal.style, { backgroundColor: 'black', padding: '30px 40px 60px', borderRadius: '0px', border: '1px solid white', color: 'white', fontSize: '16px', textAlign: 'left', minWidth: '280px', maxWidth: '90%', position: 'relative', boxShadow: 'none', }); const message = document.createElement('div'); message.id = 'fakeDownloadModalMessage'; message.style.marginBottom = '20px'; modal.appendChild(message); const searchLink = document.createElement('a'); searchLink.id = 'fakeDownloadModalSearchLink'; searchLink.style.display = 'none'; searchLink.target = '_blank'; searchLink.rel = 'noopener noreferrer'; Object.assign(searchLink.style, { color: '#66ccff', display: 'block', marginTop: '10px', textDecoration: 'underline', fontSize: '14px', }); modal.appendChild(searchLink); const okBtn = document.createElement('button'); okBtn.id = 'fakeDownloadOkButton'; okBtn.textContent = 'OK'; Object.assign(okBtn.style, { padding: '8px 30px', backgroundColor: 'red', color: 'white', border: '1px solid white', borderRadius: '0px', cursor: 'pointer', fontWeight: 'bold', fontSize: '14px', position: 'absolute', bottom: '20px', right: '40px', display: 'none', }); okBtn.addEventListener('click', () => { overlay.style.display = 'none'; document.body.style.overflow = ''; }); okBtn.addEventListener('mouseover', () => { okBtn.style.backgroundColor = '#ff4d4d'; }); okBtn.addEventListener('mouseout', () => { okBtn.style.backgroundColor = 'red'; }); modal.appendChild(okBtn); overlay.appendChild(modal); document.body.appendChild(overlay); } async function showModalWithTitle() { document.body.style.overflow = 'hidden'; const overlay = document.getElementById('fakeDownloadModalOverlay'); const message = document.getElementById('fakeDownloadModalMessage'); const searchLink = document.getElementById('fakeDownloadModalSearchLink'); const okBtn = document.getElementById('fakeDownloadOkButton'); if (!overlay || !message || !searchLink || !okBtn) return; message.innerHTML = '<span style="font-weight:bold;">UNOGS Method</span><br>is querying title information...'; searchLink.style.display = 'none'; okBtn.style.display = 'none'; overlay.style.display = 'flex'; const titleId = getTitleId(); console.log('attempting with:', titleId); const cleanedTitle = await fetchTitle(titleId); message.innerHTML = "<b>Direct downloads are not available... yet.</b><br><br>BTW, we now use only UNOGS for getting DL links - <br><br>Page method that we've used for IMDB is too unreliable.<br>It is faster, however, so you may change code if you want..."; if (cleanedTitle) { // build a safe query for the URL const safeQuery = cleanedTitle.replace(/[^\p{L}\p{N}\s]/gu, "").trim().replace(/\s+/g, " "); searchLink.href = `https://thepiratebay.org/search.php?q=${encodeURIComponent(safeQuery)}`; searchLink.style.pointerEvents = 'auto'; // still show the full title (with punctuation) to the user const bold = document.createElement("b"); bold.textContent = `'${cleanedTitle}'`; searchLink.textContent = "Search for "; searchLink.appendChild(bold); searchLink.append(" online"); searchLink.style.display = "block"; searchLink.style.textDecoration = 'underline'; searchLink.style.cursor = 'pointer'; searchLink.style.marginBottom = "20px"; searchLink.style.color = "#00aaff"; searchLink.style.fontWeight = "normal"; } else { searchLink.innerHTML = `No online links found.<br>Either content too new or please retry.`; searchLink.style.cursor = 'default'; searchLink.style.display = "block"; searchLink.style.color = 'white'; searchLink.style.marginBottom = "20px"; searchLink.style.textDecoration = 'none'; searchLink.style.pointerEvents = 'none'; searchLink.style.fontWeight = "normal"; searchLink.removeAttribute('href'); } okBtn.style.display = 'block'; } function renameHome() { const homeLink = document.querySelector('a[data-navigation-tab-name="home"]'); if (homeLink && homeLink.textContent.trim() === "Home") { homeLink.textContent = "Watch Instantly"; } } function renameDVDs() { const dvdLink = document.querySelector('a[data-navigation-tab-name="audioSubtitles"]'); if (dvdLink && dvdLink.textContent.trim() === "Browse by Languages") { dvdLink.textContent = "DVDs"; } } function insertDownloadButtons() { const myListButtons = document.querySelectorAll('button[data-uia^="add-to-my-list"]'); myListButtons.forEach(myList => { const container = myList.closest('.ltr-bjn8wh, .ptrack-content'); if (!container || container.dataset.downloadAdded === "true") return; const wrapperDiv = document.createElement('div'); wrapperDiv.className = 'ptrack-content'; const downloadBtn = document.createElement('button'); downloadBtn.className = 'color-supplementary hasIcon round ltr-1t5n97z fake-download-button'; downloadBtn.type = 'button'; downloadBtn.setAttribute('aria-label', 'Download'); downloadBtn.innerHTML = ` <div class="ltr-1st24vv"> <div class="small ltr-iyulz3" role="presentation"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true"> <path d="M12 16L8 12h3V4h2v8h3l-4 4zM4 20h16v-2H4v2z" fill="currentColor"/> </svg> </div> </div>`; downloadBtn.addEventListener('click', showModalWithTitle); wrapperDiv.appendChild(downloadBtn); container.insertAdjacentElement('afterend', wrapperDiv); container.dataset.downloadAdded = "true"; }); } function insertInfoButtons() { const title = getNetflixTitle(); if (!title) return; const downloadButtons = document.querySelectorAll('button.fake-download-button'); downloadButtons.forEach(btn => { if (btn.dataset.infoBtnAdded) return; const infoBtn = document.createElement('button'); infoBtn.type = 'button'; infoBtn.className = 'color-supplementary hasIcon round fake-info-button'; // matching style infoBtn.setAttribute('aria-label', 'Search IMDb'); // Accessible tooltip text on hover infoBtn.title = 'get more title info'; // Inner structure to mimic existing buttons (with a question mark icon) infoBtn.innerHTML = ` <div class="ltr-1st24vv" style="display: flex; align-items: center; gap: 6px;"> <div class="small ltr-iyulz3" role="presentation" style="font-weight: bold; font-size: 20px; line-height: 20px; user-select:none;">?</div> <span class="info-text" style="font-size: 14px; color: white; user-select:none;">Info</span> </div> `; // Click opens IMDb search for the extracted title infoBtn.addEventListener('click', () => { const imdbUrl = `https://www.imdb.com/find/?q=${encodeURIComponent(title)}`; window.open(imdbUrl, '_blank'); }); btn.insertAdjacentElement('afterend', infoBtn); btn.dataset.infoBtnAdded = 'true'; }); } function hideGamesTab() { document.querySelectorAll('li.navigation-tab').forEach(li => { if (li.querySelector('a[href="/games"]')) { li.style.display = 'none'; } }); } function moveLastDayMessage() { const msg = document.querySelector('.supplemental-message.previewModal--supplemental-message'); if (msg && msg.textContent.includes('Last day')) { msg.style.right = '18px'; } } function moveAnotherMessage() { const msg = document.querySelector('.supplemental-message.previewModal--supplemental-message'); if (msg && msg.textContent.includes('Another')) { msg.style.right = '14px'; } } function moveNextDayMessage() { const msg = document.querySelector('.supplemental-message.previewModal--supplemental-message'); if (msg && msg.textContent.includes('Next')) { msg.style.right = '9px'; } } function moveNewDayMessage() { const msg = document.querySelector('.supplemental-message.previewModal--supplemental-message'); if (msg && msg.textContent.toLowerCase().includes('new')) { msg.style.right = '9px'; } } function addCaseToggleBtn() { const container = document.querySelector('.watch-video--flag-container'); if (!container || container.querySelector('.case-toggle-btn')) return; // 1. Define and inject a new CSS class for both uppercase and lowercase const style = document.createElement('style'); style.textContent = ` .text-transform-uppercase { text-transform: uppercase !important; } .text-transform-lowercase { text-transform: lowercase !important; } `; document.head.appendChild(style); container.style.position = 'relative'; const btn = document.createElement('div'); btn.className = 'case-toggle-btn'; btn.textContent = 'Aa'; btn.style.cssText = ` position: relative; top: 0; right: 0; background: rgba(0,0,0,0.4); color: white; font-size: 22px; padding: 0px 0px; border-radius: 3px; cursor: pointer; z-index: 9999; user-select: none; font-weight: bold; `; btn.addEventListener('click', (event) => { event.stopPropagation(); document.querySelectorAll('div.player-timedtext').forEach(el => { if (el.classList.contains('text-transform-uppercase')) { // If it's currently uppercase, change it to lowercase el.classList.remove('text-transform-uppercase'); el.classList.add('text-transform-lowercase'); } else if (el.classList.contains('text-transform-lowercase')) { // If it's lowercase, remove the class to revert to default el.classList.remove('text-transform-lowercase'); } else { // If it's in the default state, change it to uppercase el.classList.add('text-transform-uppercase'); } }); }); container.appendChild(btn); } function runAll() { renameHome(); renameDVDs(); insertDownloadButtons(); moveAnotherMessage(); insertInfoButtons(); hideGamesTab(); moveLastDayMessage(); moveNextDayMessage(); addCaseToggleBtn(); moveNewDayMessage(); checkTitleAndYear(); } createModal(); runAll(); let timeout; const observer = new MutationObserver(() => { clearTimeout(timeout); timeout = setTimeout(runAll, 200); }); observer.observe(document.body, { childList: true, subtree: true }); // Inject CSS styles for font popup function addFontPopupStyles() { const css = ` #fontPopupBackdrop { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 100000; } #fontPopup { background: black; border: 1px solid white; padding: 20px; width: 320px; color: white; font-family: monospace, monospace; display: flex; flex-direction: column; gap: 10px; box-sizing: border-box; position: relative; border-bottom: 1px solid white; font-size: 18px; } #fontPopup input[type="text"] { outline: none; margin: 20px 0 0 0; border: 2px solid red; background: black; color: white; padding: 5px 8px; font-size: 16px; } #fontPopup button { background: black; border: 1px solid white; color: white; padding: 6px 12px; cursor: pointer; font-size: 14px; align-self: flex-end; } #fontPopup button:hover { background: white; color: black; } /* ===== Custom Toast Popup ===== */ #custom-toast-popup { outline: none; position: fixed; box-shadow: none; bottom: 20px; left: 50%; transform: translateX(-50%); background-color: black; color: white; border: 1px solid white; padding: 8px 16px; border-radius: 0px; font-size: 14px; opacity: 0; transition: opacity 0.5s ease; z-index: 100000; pointer-events: none; box-shadow: 0 2px 6px rgba(0,0,0,0.7); } `; const styleElem = document.createElement('style'); styleElem.textContent = css; document.head.appendChild(styleElem); } function addFontPopupMarkup() { if (document.getElementById('fontPopupBackdrop')) return; const backdrop = document.createElement('div'); backdrop.id = 'fontPopupBackdrop'; backdrop.style.display = 'none'; backdrop.innerHTML = ` <div id="fontPopup" role="dialog" aria-modal="true" aria-labelledby="fontPopupTitle"> <div id="fontPopupTitle" style="font-weight:bold; margin-bottom: 10px;"> a font is missing </div> <label for="fontChoice">choose an option:</label> <input type="text" id="fontChoice" placeholder="option" autofocus /> <button id="btnSubmit">submit</button> <div style="font-size: 12px; margin-top: 8px; color: #aaa;"> 1 - find<br> 2 - get<br> 3 - ignore for now </div> </div> `; document.body.appendChild(backdrop); } function showToast(message) { const toast = document.createElement('div'); toast.textContent = message; // Set styles toast.style.position = 'fixed'; toast.style.bottom = '20px'; toast.style.left = '50%'; toast.style.transform = 'translateX(-50%)'; toast.style.background = 'black'; toast.style.color = 'white'; toast.style.padding = '10px 20px'; toast.style.border = '1px solid white'; toast.style.borderRadius = '6px'; toast.style.zIndex = '999999'; toast.style.opacity = '1'; toast.style.transition = 'opacity 0.4s ease'; toast.style.borderRadius = '0px' document.body.appendChild(toast); // Show for 1 second before blinking setTimeout(() => { let flashes = 0; const flashInterval = setInterval(() => { toast.style.opacity = toast.style.opacity === '1' ? '0' : '1'; flashes++; if (flashes >= 6) { // 3 full flashes clearInterval(flashInterval); // Ensure opacity is 1 before fading out toast.style.opacity = '1'; // Wait 100ms to allow reflow, then fade out setTimeout(() => { toast.style.opacity = '0'; // Wait for transition to complete, then remove toast.addEventListener('transitionend', () => { toast.remove(); }, { once: true }); }, 100); // let browser apply the "1" before fading } }, 120); }, 1000); // 1 second solid show } // Fact check helpers and logic function checkForSegoeUISuproFont() { function isFontAvailable(font) { const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); const text = 'abcdefg'; context.font = '72px monospace'; const defaultWidth = context.measureText(text).width; context.font = `72px "${font}", monospace`; const testWidth = context.measureText(text).width; return testWidth !== defaultWidth; } const font = "Segoe UI Supro"; function showPopup() { document.body.style.overflow = 'hidden'; const backdrop = document.getElementById('fontPopupBackdrop'); const input = document.getElementById('fontChoice'); backdrop.style.display = 'flex'; input.value = ''; input.focus(); return new Promise((resolve) => { function cleanup() { document.body.style.overflow = ''; backdrop.style.display = 'none'; btnSubmit.removeEventListener('click', onSubmit); btnCancel?.removeEventListener('click', onCancel); input.removeEventListener('keydown', onKeyDown); } function onSubmit() { const val = input.value.trim(); if (["1", "2", "3"].includes(val)) { cleanup(); resolve(val); } else { showToast("illegal value"); } } function onCancel() { cleanup(); resolve("3"); // treat cancel as ignore } function onKeyDown(e) { if (e.key === "Enter") onSubmit(); else if (e.key === "Escape") onCancel(); } const btnSubmit = document.getElementById('btnSubmit'); const btnCancel = document.getElementById('btnCancel'); btnSubmit.addEventListener('click', onSubmit); if (btnCancel) btnCancel.addEventListener('click', onCancel); input.addEventListener('keydown', onKeyDown); }); } async function askUser() { const choice = await showPopup(); switch (choice) { case "1": if (!isFontAvailable(font)) return askUser(); break; case "2": window.open("https://www.google.com/search?q=segoe+ui+supro+download", "_blank"); return askUser(); case "3": // silently continue break; default: return askUser(); } } if (!isFontAvailable(font)) { askUser(); } } // Inject styles and markup, then run font check addFontPopupStyles(); addFontPopupMarkup(); checkForSegoeUISuproFont(); })();