Last active
April 18, 2025 20:50
-
-
Save 1oh1/a52f038ec169f44a5386c14078b0e17d to your computer and use it in GitHub Desktop.
Hotstar Subtitles on Rewind
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Hotstar Subtitles on Rewind (with Hotkey & Toast UI) | |
// @namespace http://tampermonkey.net/ | |
// @version 2.0 | |
// @description Auto-enable subtitles for 15s on rewind, with hotkeys and on-screen feedback | |
// @match https://www.hotstar.com/* | |
// @grant none | |
// ==/UserScript== | |
(function () { | |
'use strict'; | |
let timeoutId = null; | |
let subtitleMode = 'auto'; // 'auto', 'on', 'off' | |
function simulateClick(el) { | |
if (el) { | |
el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true })); | |
el.click(); | |
} | |
} | |
function openAudioSubtitlesMenu() { | |
const icon = document.querySelector('i.icon-subtitle-line'); | |
const button = icon?.closest('button'); | |
if (button) { | |
simulateClick(button); | |
return true; | |
} | |
return false; | |
} | |
function waitForSubtitleButtons(timeout = 5000) { | |
return new Promise((resolve, reject) => { | |
const endTime = Date.now() + timeout; | |
function check() { | |
const allButtons = document.querySelectorAll('button, [role="button"]'); | |
if (allButtons.length >= 17) { | |
resolve(Array.from(allButtons)); | |
} else if (Date.now() > endTime) { | |
reject('Subtitle buttons did not appear'); | |
} else { | |
setTimeout(check, 100); | |
} | |
} | |
check(); | |
}); | |
} | |
async function setSubtitlesTo(label, clickCount = 1) { | |
try { | |
const opened = openAudioSubtitlesMenu(); | |
if (!opened) throw new Error('Could not open Audio & Subtitles menu'); | |
const buttons = await waitForSubtitleButtons(); | |
const matches = buttons.filter(btn => btn.textContent.trim().toLowerCase() === label.toLowerCase()); | |
if (matches.length === 0) throw new Error(`No button found for "${label}"`); | |
let clicked = 0; | |
for (let i = 0; i < matches.length && clicked < clickCount; i++) { | |
simulateClick(matches[i]); | |
clicked++; | |
} | |
console.log(`[Hotstar] Clicked "${label}" ${clicked} time(s).`); | |
} catch (err) { | |
console.warn('[Hotstar] Subtitle change failed:', err); | |
} | |
} | |
function triggerSubtitleWindow() { | |
if (subtitleMode !== 'auto') return; | |
if (timeoutId) clearTimeout(timeoutId); | |
setSubtitlesTo('English', 2); | |
timeoutId = setTimeout(() => { | |
setSubtitlesTo('Off', 1); | |
}, 15000); | |
} | |
function setupRewindListener() { | |
const observer = new MutationObserver(() => { | |
const rewindBtn = document.querySelector('[data-testid="seek-rewind"]'); | |
if (rewindBtn && !rewindBtn.dataset.listenerAdded) { | |
rewindBtn.dataset.listenerAdded = 'true'; | |
rewindBtn.addEventListener('click', triggerSubtitleWindow); | |
console.log('[Hotstar] Rewind listener (button) attached'); | |
} | |
}); | |
observer.observe(document.body, { childList: true, subtree: true }); | |
} | |
function getUiContainer() { | |
return document.fullscreenElement || document.body; | |
} | |
function showToast(message, duration = 3000) { | |
const container = getUiContainer(); | |
const toast = document.createElement('div'); | |
toast.textContent = message; | |
toast.style.cssText = ` | |
position: fixed; | |
bottom: 50px; | |
left: 50%; | |
transform: translateX(-50%); | |
background: rgba(0,0,0,0.85); | |
color: white; | |
padding: 10px 20px; | |
border-radius: 8px; | |
font-size: 14px; | |
z-index: 9999; | |
opacity: 0; | |
transition: opacity 0.3s ease; | |
pointer-events: none; | |
`; | |
container.appendChild(toast); | |
requestAnimationFrame(() => { | |
toast.style.opacity = '1'; | |
}); | |
setTimeout(() => { | |
toast.style.opacity = '0'; | |
setTimeout(() => toast.remove(), 300); | |
}, duration); | |
} | |
function showHelpBox() { | |
const container = getUiContainer(); | |
const existing = container.querySelector('#hotstar-help-box'); | |
if (existing) { | |
existing.remove(); | |
return; | |
} | |
const box = document.createElement('div'); | |
box.id = 'hotstar-help-box'; | |
box.innerHTML = ` | |
<div style="font-weight:bold; margin-bottom: 5px;">Hotstar Subtitle Hotkeys:</div> | |
<ul style="margin: 0; padding-left: 18px;"> | |
<li><strong>Shift + Alt + E</strong> — Enable English subtitles permanently</li> | |
<li><strong>Shift + Alt + D</strong> — Disable subtitles permanently</li> | |
<li><strong>Shift + Alt + A</strong> — Auto mode (15s subtitles on rewind)</li> | |
<li><strong>Shift + Alt + ?</strong> — Show/hide this help menu</li> | |
</ul> | |
<div style="margin-top: 10px; text-align: right;"> | |
<button id="close-help-btn" style="padding: 4px 8px; font-size: 12px;">Close</button> | |
</div> | |
`; | |
box.style.cssText = ` | |
position: fixed; | |
top: 60px; | |
right: 60px; | |
background: #111; | |
color: #fff; | |
border-radius: 10px; | |
padding: 15px 20px; | |
box-shadow: 0 0 10px rgba(0,0,0,0.3); | |
font-family: sans-serif; | |
font-size: 13px; | |
z-index: 10000; | |
max-width: 300px; | |
`; | |
container.appendChild(box); | |
box.querySelector('#close-help-btn').onclick = () => box.remove(); | |
} | |
function setupKeyboardListener() { | |
window.addEventListener('keydown', (e) => { | |
if (e.key === 'ArrowLeft') { | |
triggerSubtitleWindow(); | |
} | |
if (e.altKey && e.shiftKey) { | |
const key = e.key.toLowerCase(); | |
if (key === 'e') { | |
subtitleMode = 'on'; | |
clearTimeout(timeoutId); | |
setSubtitlesTo('English', 2); | |
showToast('Subtitles: ON (English)'); | |
} | |
if (key === 'd') { | |
subtitleMode = 'off'; | |
clearTimeout(timeoutId); | |
setSubtitlesTo('Off', 1); | |
showToast('Subtitles: OFF'); | |
} | |
if (key === 'a') { | |
subtitleMode = 'auto'; | |
showToast('Subtitles: AUTO (15s on rewind)'); | |
} | |
if (key === '?') { | |
showHelpBox(); | |
} | |
} | |
}); | |
} | |
setTimeout(() => { | |
setupRewindListener(); | |
setupKeyboardListener(); | |
}, 3000); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
QS7uMax.1.mp4