Skip to content

Instantly share code, notes, and snippets.

@lwcorp
Last active July 27, 2026 17:47
Show Gist options
  • Select an option

  • Save lwcorp/b85b82126f373fbf435f9edecb6f1a0c to your computer and use it in GitHub Desktop.

Select an option

Save lwcorp/b85b82126f373fbf435f9edecb6f1a0c to your computer and use it in GitHub Desktop.
Bookmarklet - Adds full video features to all video players including blocked ones
javascript:(function(){var pct=String.fromCharCode(37);const CONFIG={enablePlayPause:true,enableSpeed:true,enableReset:true,enableLoop:true,enablePip:true,enableCast:true,enableMute:true,enableVolumeSlider:true,enableTime:true,enableScreenshot:true,enableFullscreen:true,enableClose:true,enableCloseAll:true,PLAYBACK_SPEEDS:[0.5,0.75,1,1.25,1.5,1.75,2]};if(window.trustedTypes&&window.trustedTypes.createPolicy&&!window.trustedTypes.defaultPolicy){try{window.trustedTypes.createPolicy('default',{createHTML:function(s){return s;},createScriptURL:function(s){return s;},createScript:function(s){return s;}});}catch(e){}}document.querySelectorAll('.media-above-vol-wrapper').forEach(function(el){el.remove();});const existingStyle=document.getElementById('media-above-style');if(existingStyle)existingStyle.remove();var rawCss=".media-above-vol-wrapper{display:flex;justify-content:center !important;width:100"+pct+";margin-bottom:6px;z-index:99999;position:relative;direction:ltr !important}.media-above-vol{display:inline-flex;flex-wrap:wrap;align-items:center;gap:8px;background:rgba(30,31,32,0.95);padding:6px 14px;border-radius:20px;border:1px solid #555;box-shadow:0 4px 12px rgba(0,0,0,0.3);font-family:system-ui,-apple-system,sans-serif;user-select:none;direction:ltr !important}.media-above-btn{cursor:pointer;font-size:15px;line-height:1;color:#fff;padding:2px;display:flex;align-items:center;justify-content:center;transition:transform 0.1s ease,color 0.15s ease,opacity 0.15s ease}.media-above-btn:hover{transform:scale(1.15)}.media-above-btn[data-active=\"true\"]{color:#8ab4f8;text-shadow:0 0 8px rgba(138,180,248,0.5)}.media-above-slider{width:65px;cursor:pointer;accent-color:#8ab4f8}.media-above-label{font-size:12px;font-weight:500;color:#fff;min-width:36px;text-align:right;font-variant-numeric:tabular-nums}.media-above-time-container{display:inline-flex;align-items:center;font-size:12px;color:#ccc;font-variant-numeric:tabular-nums;gap:4px}.media-above-time-input{color:inherit !important;padding:inherit !important;margin:inherit !important;background-color:inherit !important;width:50px !important;border:1.5px solid #8ab4f8;font-family:inherit !important;font-size:12px !important;font-weight:500 !important;text-align:center !important;border-radius:6px !important;outline:none !important;cursor:text !important;box-shadow:0 0 4px rgba(138,180,248,0.25) !important;transition:all 0.15s ease !important}.media-above-time-input:hover{box-shadow:0 0 8px rgba(138,180,248,0.5)}.media-above-time-input:focus{border-color:#fff;box-shadow:0 0 10px rgba(138,180,248,0.8)}.media-above-select{background:rgba(0,0,0,0.5);color:#fff;border:1px solid #666;border-radius:6px;font-size:11px;padding:2px 4px;cursor:pointer;outline:none}.media-above-divider{width:1px;height:14px;background:rgba(255,255,255,0.2);margin:0 2px}.media-above-close{cursor:pointer;font-size:18px;font-weight:bold;color:#aaa;margin-left:2px;padding:0 2px;line-height:1;transition:color 0.15s ease}.media-above-close:hover{color:#fff}.media-above-close-all{cursor:pointer;font-size:20px;font-weight:900;color:#ff6b6b;margin-left:4px;padding:0 4px;line-height:1;transition:transform 0.15s ease,color 0.15s ease}";const style=document.createElement('style');style.id='media-above-style';style.textContent=rawCss;document.head.appendChild(style);const formatTime=(secs)=>{if(!secs||isNaN(secs))return'0:00';const h=Math.floor(secs / 3600);const m=Math.floor((secs-(h*3600))/ 60);const s=Math.floor(secs-Math.floor(secs / 60)*60);const sStr=s<10?'0'+s:s;if(h>0){const mStr=m<10?'0'+m:m;return h+':'+mStr+':'+sStr;}return m+':'+sStr;};const parseTimeInput=(str)=>{const parts=str.trim().split(':').map(Number);if(parts.some(isNaN))return null;if(parts.length===3)return parts[0]*3600+parts[1]*60+parts[2];if(parts.length===2)return parts[0]*60+parts[1];if(parts.length===1)return parts[0];return null;};const createDivider=()=>{const d=document.createElement('span');d.className='media-above-divider';return d;};document.querySelectorAll('video').forEach((video)=>{const container=video.closest('generated-video')||video.closest('video-player')||video.parentElement;const wrapper=document.createElement('div');wrapper.className='media-above-vol-wrapper';const ctrlBox=document.createElement('div');ctrlBox.className='media-above-vol';['click','mousedown','touchstart','dblclick'].forEach(evt=>{ctrlBox.addEventListener(evt,(e)=>e.stopPropagation());});const elementsToAppend=[];if(CONFIG.enablePlayPause){const ppBtn=document.createElement('span');ppBtn.className='media-above-btn';const PLAY_ICON='&#9654;';const PAUSE_ICON='<span style="font-size: 14px; letter-spacing: 2px; font-weight: 800;">&#10074;&#10074;</span>';ppBtn.innerHTML=video.paused?PLAY_ICON:PAUSE_ICON;ppBtn.title='Play/Pause';ppBtn.addEventListener('click',()=>{video.paused?video.play():video.pause();});video.addEventListener('play',()=>ppBtn.innerHTML=PAUSE_ICON);video.addEventListener('pause',()=>ppBtn.innerHTML=PLAY_ICON);elementsToAppend.push(ppBtn);}if(CONFIG.enableSpeed){const speedSelect=document.createElement('select');speedSelect.className='media-above-select';speedSelect.title='Playback Speed';CONFIG.PLAYBACK_SPEEDS.forEach(rate=>{const opt=document.createElement('option');opt.value=rate;opt.innerText=rate+'x';if(video.playbackRate===rate||(rate===1&&!video.playbackRate))opt.selected=true;speedSelect.appendChild(opt);});speedSelect.addEventListener('change',(e)=>{video.playbackRate=parseFloat(e.target.value);});elementsToAppend.push(speedSelect);}if(CONFIG.enableReset){const resetBtn=document.createElement('span');resetBtn.className='media-above-btn';resetBtn.innerHTML='<span style="font-size: 11px; font-weight: 800; letter-spacing: 0.5px;">RESET</span>';resetBtn.title='Resets Speed, Volume, Time, Loop, PiP, and Cast';resetBtn.addEventListener('click',()=>{video.playbackRate=1;const speedSelect=ctrlBox.querySelector('.media-above-select');if(speedSelect)speedSelect.value='1';video.muted=false;video.volume=1;video.currentTime=0;video.loop=false;const allBtns=ctrlBox.querySelectorAll('.media-above-btn');allBtns.forEach(function(b){if(b.title==='Toggle Loop')b.dataset.active='false';});if(document.pictureInPictureElement){document.exitPictureInPicture().catch(()=>{});}if(video.remote){try{video.disableRemotePlayback=true;setTimeout(()=>{video.disableRemotePlayback=false;},100);}catch(e){}}});elementsToAppend.push(resetBtn);}if(CONFIG.enableLoop){const loopBtn=document.createElement('span');loopBtn.className='media-above-btn';loopBtn.innerHTML='&#8635;';loopBtn.title='Toggle Loop';loopBtn.dataset.active=video.loop.toString();loopBtn.addEventListener('click',()=>{video.loop=!video.loop;loopBtn.dataset.active=video.loop.toString();});elementsToAppend.push(loopBtn);}if(CONFIG.enablePip&&document.pictureInPictureEnabled){const pipBtn=document.createElement('span');pipBtn.className='media-above-btn';pipBtn.innerHTML='&#9638;';pipBtn.title='Picture-in-Picture';pipBtn.addEventListener('click',()=>{video.disablePictureInPicture=false;try{video.removeAttribute('disablePictureInPicture');}catch(e){}if(document.pictureInPictureElement===video){document.exitPictureInPicture().catch(()=>{});}else{video.requestPictureInPicture().catch((err)=>{alert('PiP Blocked by Website:\n\nThis website strictly prevents Picture-in-Picture natively. This usually happens because:\n1. The video is DRM-encrypted (Netflix, Hulu, etc.)\n2. The player uses a custom Canvas render rather than standard HTML5\n3. Aggressive anti-PiP scripts are running.\n\nWorkaround: Try using your browser\'s native Media Control hub (the music note icon next to your extensions) or a dedicated browser extension.');});}});elementsToAppend.push(pipBtn);}if(CONFIG.enableCast){const castBtn=document.createElement('span');castBtn.className='media-above-btn';castBtn.innerHTML='&#128250;';castBtn.title='Cast / Remote Playback';castBtn.addEventListener('click',(e)=>{e.preventDefault();e.stopPropagation();if(navigator.userActivation&&!navigator.userActivation.isActive){alert('Browser Security Warning:\n\nThe Cast prompt was blocked because the browser did not register a direct user gesture. Please try clicking inside the video player once before clicking Cast.');return;}if(video.remote){try{video.disableRemotePlayback=false;}catch(err){}video.remote.prompt().then(()=>{}).catch(err=>{if(err.name==='NotFoundError'){alert('No compatible Cast or AirPlay devices found on your local network.');}else if(err.name==='NotSupportedError'||err.name==='InvalidStateError'){alert('Stream Incompatible:\n\nThis video stream (e.g., Blob URL, MSE, or CORS-protected CDN) cannot be cast natively by the browser.\n\nWorkaround: Use Chrome or Edge\'s 3-dot menu -> "Cast..." to mirror your entire tab instead!');}else if(err.name==='NotAllowedError'){alert('Cast Prompt Dismissed or Blocked:\n\nThe browser blocked the request or the device picker was closed.\n\nIf this persists on this website, use your browser\'s 3-dot menu -> "Cast..." to mirror the tab directly.');}else{alert('Cast failed ('+err.name+'): '+err.message+'\n\nTip: Use your browser\'s 3-dot menu -> "Cast..." to mirror this tab.');}});}else{alert('Your browser does not support the native Web Remote Playback API.\n\nTip: Use Chrome or Edge\'s 3-dot menu -> "Cast..." to mirror this tab!');}});elementsToAppend.push(castBtn);}let lastVol=video.volume||1;if(CONFIG.enableMute||CONFIG.enableVolumeSlider){elementsToAppend.push(createDivider());const speakerBtn=document.createElement('span');const slider=document.createElement('input');const label=document.createElement('span');if(CONFIG.enableMute){speakerBtn.className='media-above-btn';speakerBtn.innerHTML=video.muted||video.volume===0?'&#128263;':'&#128266;';speakerBtn.title='Mute/Unmute';speakerBtn.addEventListener('click',()=>{if(video.muted||video.volume===0){video.muted=false;video.volume=lastVol||0.5;}else{lastVol=video.volume;video.muted=true;video.volume=0;}});elementsToAppend.push(speakerBtn);}if(CONFIG.enableVolumeSlider){slider.type='range';slider.className='media-above-slider';slider.min='0';slider.max='1';slider.step='0.05';slider.value=video.muted?'0':(video.volume||'1');label.className='media-above-label';label.innerText=Math.round((video.muted?0:video.volume)*100)+pct;slider.addEventListener('input',(e)=>{const vol=parseFloat(e.target.value);if(vol>0)lastVol=vol;video.muted=(vol===0);video.volume=vol;});elementsToAppend.push(slider,label);}video.addEventListener('volumechange',()=>{if(CONFIG.enableMute){speakerBtn.innerHTML=video.muted||video.volume===0?'&#128263;':'&#128266;';}if(CONFIG.enableVolumeSlider){slider.value=video.muted?'0':video.volume;label.innerText=Math.round((video.muted?0:video.volume)*100)+pct;}});}if(CONFIG.enableTime){elementsToAppend.push(createDivider());const timeContainer=document.createElement('span');timeContainer.className='media-above-time-container';const timeInput=document.createElement('input');timeInput.type='text';timeInput.className='media-above-time-input';timeInput.title='Click to edit timestamp (e.g. 1:15 or 75) and press Enter to jump';const durationSpan=document.createElement('span');let isEditingTime=false;const updateTimeDisplay=()=>{if(!isEditingTime){timeInput.value=formatTime(video.currentTime);}durationSpan.innerText='/ '+formatTime(video.duration);};timeInput.addEventListener('focus',()=>{isEditingTime=true;timeInput.select();});const applyTypedTime=()=>{isEditingTime=false;const targetSeconds=parseTimeInput(timeInput.value);if(targetSeconds!==null&&targetSeconds<=video.duration){video.currentTime=targetSeconds;}updateTimeDisplay();};timeInput.addEventListener('blur',applyTypedTime);timeInput.addEventListener('keydown',(e)=>{if(e.key==='Enter'){timeInput.blur();}else if(e.key==='Escape'){isEditingTime=false;timeInput.blur();}});updateTimeDisplay();video.addEventListener('timeupdate',updateTimeDisplay);video.addEventListener('loadedmetadata',updateTimeDisplay);timeContainer.append(timeInput,durationSpan);elementsToAppend.push(timeContainer);}if(CONFIG.enableScreenshot){elementsToAppend.push(createDivider());const snapBtn=document.createElement('span');snapBtn.className='media-above-btn';snapBtn.innerHTML='&#128247;';snapBtn.title='Take Screenshot';snapBtn.addEventListener('click',()=>{try{const canvas=document.createElement('canvas');canvas.width=video.videoWidth||video.clientWidth;canvas.height=video.videoHeight||video.clientHeight;canvas.getContext('2d').drawImage(video,0,0,canvas.width,canvas.height);const link=document.createElement('a');link.download='screenshot-'+Math.floor(video.currentTime)+'s.png';link.href=canvas.toDataURL('image/png');link.click();}catch(err){alert('Screenshot failed: Video stream is protected by CORS.');}});elementsToAppend.push(snapBtn);}if(CONFIG.enableFullscreen){const fsBtn=document.createElement('span');fsBtn.className='media-above-btn';fsBtn.innerHTML='&#x26F6;';fsBtn.title='Toggle Fullscreen';fsBtn.addEventListener('click',()=>{const target=video.closest('video-player')||video;if(document.fullscreenElement){if(document.exitFullscreen)document.exitFullscreen();}else{if(target.requestFullscreen)target.requestFullscreen();else if(target.webkitRequestFullscreen)target.webkitRequestFullscreen();}});elementsToAppend.push(fsBtn);}if(CONFIG.enableClose||CONFIG.enableCloseAll){elementsToAppend.push(createDivider());if(CONFIG.enableClose){const closeBtn=document.createElement('span');closeBtn.className='media-above-close';closeBtn.innerHTML='&times;';closeBtn.title='Close This Control';closeBtn.addEventListener('click',()=>wrapper.remove());elementsToAppend.push(closeBtn);}if(CONFIG.enableCloseAll){const closeAllBtn=document.createElement('span');closeAllBtn.className='media-above-close-all';closeAllBtn.innerHTML='&#10006;';closeAllBtn.title='Close ALL Controls';closeAllBtn.addEventListener('click',()=>{document.querySelectorAll('.media-above-vol-wrapper').forEach(el=>el.remove());const s=document.getElementById('media-above-style');if(s)s.remove();});elementsToAppend.push(closeAllBtn);}}elementsToAppend.forEach(el=>ctrlBox.appendChild(el));wrapper.appendChild(ctrlBox);if(container&&container.parentNode){container.parentNode.insertBefore(wrapper,container);}});})();
(function () {
var pct = String.fromCharCode(37);
const CONFIG = {
enablePlayPause: true,
enableSpeed: true,
enableReset: true,
enableLoop: true,
enablePip: true,
enableCast: true,
enableMute: true,
enableVolumeSlider: true,
enableTime: true,
enableScreenshot: true,
enableFullscreen: true,
enableClose: true,
enableCloseAll: true,
PLAYBACK_SPEEDS: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]
};
if (window.trustedTypes && window.trustedTypes.createPolicy && !window.trustedTypes.defaultPolicy) {
try {
window.trustedTypes.createPolicy('default', {
createHTML: function(s) { return s; },
createScriptURL: function(s) { return s; },
createScript: function(s) { return s; }
});
} catch (e) {}
}
document.querySelectorAll('.media-above-vol-wrapper').forEach(function(el) { el.remove(); });
const existingStyle = document.getElementById('media-above-style');
if (existingStyle) existingStyle.remove();
var rawCss = ".media-above-vol-wrapper{display:flex;justify-content:center !important;width:100" + pct + ";margin-bottom:6px;z-index:99999;position:relative;direction:ltr !important}.media-above-vol{display:inline-flex;flex-wrap:wrap;align-items:center;gap:8px;background:rgba(30,31,32,0.95);padding:6px 14px;border-radius:20px;border:1px solid #555;box-shadow:0 4px 12px rgba(0,0,0,0.3);font-family:system-ui,-apple-system,sans-serif;user-select:none;direction:ltr !important}.media-above-btn{cursor:pointer;font-size:15px;line-height:1;color:#fff;padding:2px;display:flex;align-items:center;justify-content:center;transition:transform 0.1s ease,color 0.15s ease,opacity 0.15s ease}.media-above-btn:hover{transform:scale(1.15)}.media-above-btn[data-active=\"true\"]{color:#8ab4f8;text-shadow:0 0 8px rgba(138,180,248,0.5)}.media-above-slider{width:65px;cursor:pointer;accent-color:#8ab4f8}.media-above-label{font-size:12px;font-weight:500;color:#fff;min-width:36px;text-align:right;font-variant-numeric:tabular-nums}.media-above-time-container{display:inline-flex;align-items:center;font-size:12px;color:#ccc;font-variant-numeric:tabular-nums;gap:4px}.media-above-time-input{color:inherit !important;padding:inherit !important;margin:inherit !important;background-color:inherit !important;width:50px !important;border:1.5px solid #8ab4f8;font-family:inherit !important;font-size:12px !important;font-weight:500 !important;text-align:center !important;border-radius:6px !important;outline:none !important;cursor:text !important;box-shadow:0 0 4px rgba(138,180,248,0.25) !important;transition:all 0.15s ease !important}.media-above-time-input:hover{box-shadow:0 0 8px rgba(138,180,248,0.5)}.media-above-time-input:focus{border-color:#fff;box-shadow:0 0 10px rgba(138,180,248,0.8)}.media-above-select{background:rgba(0,0,0,0.5);color:#fff;border:1px solid #666;border-radius:6px;font-size:11px;padding:2px 4px;cursor:pointer;outline:none}.media-above-divider{width:1px;height:14px;background:rgba(255,255,255,0.2);margin:0 2px}.media-above-close{cursor:pointer;font-size:18px;font-weight:bold;color:#aaa;margin-left:2px;padding:0 2px;line-height:1;transition:color 0.15s ease}.media-above-close:hover{color:#fff}.media-above-close-all{cursor:pointer;font-size:20px;font-weight:900;color:#ff6b6b;margin-left:4px;padding:0 4px;line-height:1;transition:transform 0.15s ease,color 0.15s ease}";
const style = document.createElement('style');
style.id = 'media-above-style';
style.textContent = rawCss;
document.head.appendChild(style);
const formatTime = (secs) => {
if (!secs || isNaN(secs)) return '0:00';
const h = Math.floor(secs / 3600);
const m = Math.floor((secs - (h * 3600)) / 60);
const s = Math.floor(secs - Math.floor(secs / 60) * 60);
const sStr = s < 10 ? '0' + s : s;
if (h > 0) {
const mStr = m < 10 ? '0' + m : m;
return h + ':' + mStr + ':' + sStr;
}
return m + ':' + sStr;
};
const parseTimeInput = (str) => {
const parts = str.trim().split(':').map(Number);
if (parts.some(isNaN)) return null;
if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2];
if (parts.length === 2) return parts[0] * 60 + parts[1];
if (parts.length === 1) return parts[0];
return null;
};
const createDivider = () => {
const d = document.createElement('span');
d.className = 'media-above-divider';
return d;
};
document.querySelectorAll('video').forEach((video) => {
const container = video.closest('generated-video') || video.closest('video-player') || video.parentElement;
const wrapper = document.createElement('div');
wrapper.className = 'media-above-vol-wrapper';
const ctrlBox = document.createElement('div');
ctrlBox.className = 'media-above-vol';
['click', 'mousedown', 'touchstart', 'dblclick'].forEach(evt => {
ctrlBox.addEventListener(evt, (e) => e.stopPropagation());
});
const elementsToAppend = [];
if (CONFIG.enablePlayPause) {
const ppBtn = document.createElement('span');
ppBtn.className = 'media-above-btn';
const PLAY_ICON = '&#9654;';
const PAUSE_ICON = '<span style="font-size: 14px; letter-spacing: 2px; font-weight: 800;">&#10074;&#10074;</span>';
ppBtn.innerHTML = video.paused ? PLAY_ICON : PAUSE_ICON;
ppBtn.title = 'Play/Pause';
ppBtn.addEventListener('click', () => {
video.paused ? video.play() : video.pause();
});
video.addEventListener('play', () => ppBtn.innerHTML = PAUSE_ICON);
video.addEventListener('pause', () => ppBtn.innerHTML = PLAY_ICON);
elementsToAppend.push(ppBtn);
}
if (CONFIG.enableSpeed) {
const speedSelect = document.createElement('select');
speedSelect.className = 'media-above-select';
speedSelect.title = 'Playback Speed';
CONFIG.PLAYBACK_SPEEDS.forEach(rate => {
const opt = document.createElement('option');
opt.value = rate;
opt.innerText = rate + 'x';
if (video.playbackRate === rate || (rate === 1 && !video.playbackRate)) opt.selected = true;
speedSelect.appendChild(opt);
});
speedSelect.addEventListener('change', (e) => {
video.playbackRate = parseFloat(e.target.value);
});
elementsToAppend.push(speedSelect);
}
if (CONFIG.enableReset) {
const resetBtn = document.createElement('span');
resetBtn.className = 'media-above-btn';
resetBtn.innerHTML = '<span style="font-size: 11px; font-weight: 800; letter-spacing: 0.5px;">RESET</span>';
resetBtn.title = 'Resets Speed, Volume, Time, Loop, PiP, and Cast';
resetBtn.addEventListener('click', () => {
video.playbackRate = 1;
const speedSelect = ctrlBox.querySelector('.media-above-select');
if (speedSelect) speedSelect.value = '1';
video.muted = false;
video.volume = 1;
video.currentTime = 0;
video.loop = false;
const allBtns = ctrlBox.querySelectorAll('.media-above-btn');
allBtns.forEach(function(b) {
if (b.title === 'Toggle Loop') b.dataset.active = 'false';
});
if (document.pictureInPictureElement) {
document.exitPictureInPicture().catch(() => {});
}
if (video.remote) {
try {
video.disableRemotePlayback = true;
setTimeout(() => { video.disableRemotePlayback = false; }, 100);
} catch (e) {}
}
});
elementsToAppend.push(resetBtn);
}
if (CONFIG.enableLoop) {
const loopBtn = document.createElement('span');
loopBtn.className = 'media-above-btn';
loopBtn.innerHTML = '&#8635;';
loopBtn.title = 'Toggle Loop';
loopBtn.dataset.active = video.loop.toString();
loopBtn.addEventListener('click', () => {
video.loop = !video.loop;
loopBtn.dataset.active = video.loop.toString();
});
elementsToAppend.push(loopBtn);
}
if (CONFIG.enablePip && document.pictureInPictureEnabled) {
const pipBtn = document.createElement('span');
pipBtn.className = 'media-above-btn';
pipBtn.innerHTML = '&#9638;';
pipBtn.title = 'Picture-in-Picture';
pipBtn.addEventListener('click', () => {
video.disablePictureInPicture = false;
try { video.removeAttribute('disablePictureInPicture'); } catch(e) {}
if (document.pictureInPictureElement === video) {
document.exitPictureInPicture().catch(() => {});
} else {
video.requestPictureInPicture().catch((err) => {
alert('PiP Blocked by Website:\n\nThis website strictly prevents Picture-in-Picture natively. This usually happens because:\n1. The video is DRM-encrypted (Netflix, Hulu, etc.)\n2. The player uses a custom Canvas render rather than standard HTML5\n3. Aggressive anti-PiP scripts are running.\n\nWorkaround: Try using your browser\'s native Media Control hub (the music note icon next to your extensions) or a dedicated browser extension.');
});
}
});
elementsToAppend.push(pipBtn);
}
if (CONFIG.enableCast) {
const castBtn = document.createElement('span');
castBtn.className = 'media-above-btn';
castBtn.innerHTML = '&#128250;';
castBtn.title = 'Cast / Remote Playback';
castBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
if (navigator.userActivation && !navigator.userActivation.isActive) {
alert('Browser Security Warning:\n\nThe Cast prompt was blocked because the browser did not register a direct user gesture. Please try clicking inside the video player once before clicking Cast.');
return;
}
if (video.remote) {
try {
video.disableRemotePlayback = false;
} catch (err) {}
video.remote.prompt()
.then(() => {})
.catch(err => {
if (err.name === 'NotFoundError') {
alert('No compatible Cast or AirPlay devices found on your local network.');
} else if (err.name === 'NotSupportedError' || err.name === 'InvalidStateError') {
alert('Stream Incompatible:\n\nThis video stream (e.g., Blob URL, MSE, or CORS-protected CDN) cannot be cast natively by the browser.\n\nWorkaround: Use Chrome or Edge\'s 3-dot menu -> "Cast..." to mirror your entire tab instead!');
} else if (err.name === 'NotAllowedError') {
alert('Cast Prompt Dismissed or Blocked:\n\nThe browser blocked the request or the device picker was closed.\n\nIf this persists on this website, use your browser\'s 3-dot menu -> "Cast..." to mirror the tab directly.');
} else {
alert('Cast failed (' + err.name + '): ' + err.message + '\n\nTip: Use your browser\'s 3-dot menu -> "Cast..." to mirror this tab.');
}
});
} else {
alert('Your browser does not support the native Web Remote Playback API.\n\nTip: Use Chrome or Edge\'s 3-dot menu -> "Cast..." to mirror this tab!');
}
});
elementsToAppend.push(castBtn);
}
let lastVol = video.volume || 1;
if (CONFIG.enableMute || CONFIG.enableVolumeSlider) {
elementsToAppend.push(createDivider());
const speakerBtn = document.createElement('span');
const slider = document.createElement('input');
const label = document.createElement('span');
if (CONFIG.enableMute) {
speakerBtn.className = 'media-above-btn';
speakerBtn.innerHTML = video.muted || video.volume === 0 ? '&#128263;' : '&#128266;';
speakerBtn.title = 'Mute/Unmute';
speakerBtn.addEventListener('click', () => {
if (video.muted || video.volume === 0) {
video.muted = false;
video.volume = lastVol || 0.5;
} else {
lastVol = video.volume;
video.muted = true;
video.volume = 0;
}
});
elementsToAppend.push(speakerBtn);
}
if (CONFIG.enableVolumeSlider) {
slider.type = 'range';
slider.className = 'media-above-slider';
slider.min = '0';
slider.max = '1';
slider.step = '0.05';
slider.value = video.muted ? '0' : (video.volume || '1');
label.className = 'media-above-label';
label.innerText = Math.round((video.muted ? 0 : video.volume) * 100) + pct;
slider.addEventListener('input', (e) => {
const vol = parseFloat(e.target.value);
if (vol > 0) lastVol = vol;
video.muted = (vol === 0);
video.volume = vol;
});
elementsToAppend.push(slider, label);
}
video.addEventListener('volumechange', () => {
if (CONFIG.enableMute) {
speakerBtn.innerHTML = video.muted || video.volume === 0 ? '&#128263;' : '&#128266;';
}
if (CONFIG.enableVolumeSlider) {
slider.value = video.muted ? '0' : video.volume;
label.innerText = Math.round((video.muted ? 0 : video.volume) * 100) + pct;
}
});
}
if (CONFIG.enableTime) {
elementsToAppend.push(createDivider());
const timeContainer = document.createElement('span');
timeContainer.className = 'media-above-time-container';
const timeInput = document.createElement('input');
timeInput.type = 'text';
timeInput.className = 'media-above-time-input';
timeInput.title = 'Click to edit timestamp (e.g. 1:15 or 75) and press Enter to jump';
const durationSpan = document.createElement('span');
let isEditingTime = false;
const updateTimeDisplay = () => {
if (!isEditingTime) {
timeInput.value = formatTime(video.currentTime);
}
durationSpan.innerText = '/ ' + formatTime(video.duration);
};
timeInput.addEventListener('focus', () => {
isEditingTime = true;
timeInput.select();
});
const applyTypedTime = () => {
isEditingTime = false;
const targetSeconds = parseTimeInput(timeInput.value);
if (targetSeconds !== null && targetSeconds <= video.duration) {
video.currentTime = targetSeconds;
}
updateTimeDisplay();
};
timeInput.addEventListener('blur', applyTypedTime);
timeInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
timeInput.blur();
} else if (e.key === 'Escape') {
isEditingTime = false;
timeInput.blur();
}
});
updateTimeDisplay();
video.addEventListener('timeupdate', updateTimeDisplay);
video.addEventListener('loadedmetadata', updateTimeDisplay);
timeContainer.append(timeInput, durationSpan);
elementsToAppend.push(timeContainer);
}
if (CONFIG.enableScreenshot) {
elementsToAppend.push(createDivider());
const snapBtn = document.createElement('span');
snapBtn.className = 'media-above-btn';
snapBtn.innerHTML = '&#128247;';
snapBtn.title = 'Take Screenshot';
snapBtn.addEventListener('click', () => {
try {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth || video.clientWidth;
canvas.height = video.videoHeight || video.clientHeight;
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
const link = document.createElement('a');
link.download = 'screenshot-' + Math.floor(video.currentTime) + 's.png';
link.href = canvas.toDataURL('image/png');
link.click();
} catch (err) {
alert('Screenshot failed: Video stream is protected by CORS.');
}
});
elementsToAppend.push(snapBtn);
}
if (CONFIG.enableFullscreen) {
const fsBtn = document.createElement('span');
fsBtn.className = 'media-above-btn';
fsBtn.innerHTML = '&#x26F6;';
fsBtn.title = 'Toggle Fullscreen';
fsBtn.addEventListener('click', () => {
const target = video.closest('video-player') || video;
if (document.fullscreenElement) {
if (document.exitFullscreen) document.exitFullscreen();
} else {
if (target.requestFullscreen) target.requestFullscreen();
else if (target.webkitRequestFullscreen) target.webkitRequestFullscreen();
}
});
elementsToAppend.push(fsBtn);
}
if (CONFIG.enableClose || CONFIG.enableCloseAll) {
elementsToAppend.push(createDivider());
if (CONFIG.enableClose) {
const closeBtn = document.createElement('span');
closeBtn.className = 'media-above-close';
closeBtn.innerHTML = '&times;';
closeBtn.title = 'Close This Control';
closeBtn.addEventListener('click', () => wrapper.remove());
elementsToAppend.push(closeBtn);
}
if (CONFIG.enableCloseAll) {
const closeAllBtn = document.createElement('span');
closeAllBtn.className = 'media-above-close-all';
closeAllBtn.innerHTML = '&#10006;';
closeAllBtn.title = 'Close ALL Controls';
closeAllBtn.addEventListener('click', () => {
document.querySelectorAll('.media-above-vol-wrapper').forEach(el => el.remove());
const s = document.getElementById('media-above-style');
if (s) s.remove();
});
elementsToAppend.push(closeAllBtn);
}
}
elementsToAppend.forEach(el => ctrlBox.appendChild(el));
wrapper.appendChild(ctrlBox);
if (container && container.parentNode) {
container.parentNode.insertBefore(wrapper, container);
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment