Skip to content

Instantly share code, notes, and snippets.

@ackvf
Last active February 3, 2025 23:15
Show Gist options
  • Save ackvf/b180e9883069ad753969a30cb2622787 to your computer and use it in GitHub Desktop.
Save ackvf/b180e9883069ad753969a30cb2622787 to your computer and use it in GitHub Desktop.
Bookmarklets and script snippets

Bookmarklets and script snippets

This repository contains a collection of bookmarklets and script snippets that can be used to enhance the browsing experience.

How to create and use Bookmarklets?

Create new Bookmark

add new bookmark page

Edit Bookmark details

  • Name: for example "Enable 2/5/10 sec rewind | Netflix"
  • URL: paste the script *Note, double-check that the script starts with: javascript:

    javascript:eval(atob('Ci8qCiAgbWFkZ...

edit bookmark

See the new Bookmarklet

preview bookmark

Note

Bookmarklets are constrained by a single line, so they can be written in plain JavaScript with properly used semicolons, or encoded with base64.

javascript:var who = 'world'; alert('Hello ' + who); // says 'Hello world'

javascript:eval(atob('Cgl2YXIgd2hvID0gJ3dvcmxkJwoJYWxlcnQoJ0hlbGxvICcgKyB3aG8pCg==')) // says 'Hello world'

The advantage of plain format is that it can be easily edited inline or with https://beautifier.io/, but it loses all formatting.
If the script is more complex, contains comments or formatting is relevant, the code as a whole can be encoded with base64 https://www.base64decode.org/ or the included toBase64Bookmarklet function toBase64Bookmarklet(() => { /* your code here */ }).

toBase64Bookmarklet(() => {
	var who = 'world'
	alert('Hello ' + who)
})
// Result: "javascript:eval(atob('Cgl2YXIgd2hvID0gJ3dvcmxkJwoJYWxlcnQoJ0hlbGxvICcgKyB3aG8pCg=='))"
function toBase64Bookmarklet(fn) {
const text = fn.toString()
const body = text.slice(text.indexOf('{') + 1, text.lastIndexOf('}'))
const bstring = `javascript:eval(atob('${btoa(body)}'))`
copy(bstring)
return bstring
}
// Create Bookmarklet code (result is copied to clipboard)
toBase64Bookmarklet(() => { /* your code goes here */ }) // returns javascript:eval(atob('ewogIC8vIG1hZGUgYnk...
/**
* @author Qwerty <[email protected]>
*
* @name Enable 2/5/10 sec rewind (HUD)
*
* @description
* This script allows you to precisely and more granularly rewind and fast forward videos on any website.\
* It also displays a HUD showing the total saved time and time spent seeking and buffering.
*
* - Use the `<` and `>` keys to rewind and fast forward by 5 seconds, respectively.
* - Use the **Shift** or **RightAlt** modifier keys to skip by 10 and 2 seconds, respectively.
* - When the video is **paused**, it will seek frame by frame instead at 30 fps *(configurable)* and double or half the speed with the modifier keys.
*
* @description
* submit issues here: https://gist.github.com/ackvf/b180e9883069ad753969a30cb2622787
*/
((_ = window.q_skipCount = {
/* change these values in devTools `q_skipCount.<...>` to your liking */
videoSelector: 'video', /* configure the querySelector for the video element */
expectedFramerate: 30, /* configure your video's framerate */
timeout: 2000, /* HUD automatically hides after this time. Requires restart. */
/* --- */
skippedTime: 0,
spentLoading: 0,
el: null,
video: null,
timeoutRef: null,
handleKey(ev) {
_.video = document.querySelector(_.videoSelector) ?? document.querySelector('video');
let d = 0;
let f = _.expectedFramerate;
switch (ev.key) {
/* < > keys */
case ",": d = -5; break;
case ",": d = -5; break;
case ".": d = +5; break;
/* Shift + < > keys */
case "?": d = -10; f /= 2; break;
case ":": d = +10; f /= 2; break;
/* RightAlt + < > keys */
case "<": d = -2; f *= 2; break;
case ">": d = +2; f *= 2; break;
case " ": togglePlayback(); break;
}
if (_.video && d) {
/* framestep seek if video is paused */
if (_.video.paused) _.video.currentTime += Math.sign(d) / f;
else {
_.video.currentTime += d;
_.skippedTime += d;
_.lastDelta = d;
const bufferingStartTime = performance.now();
_.video.addEventListener('canplaythrough', () => {
_.spentLoading += performance.now() - bufferingStartTime;
_.renderHUD();
}, { once: true });
}
}
if (!_.video || _.video !== document.querySelector(_.videoSelector)) console.warn(`Video ${_.videoSelector} not found`, { video: _.video });
},
renderHUD() {
if (_.timeoutRef) _.removeHUD();
const ID = "skipCountContainer";
_.el ??= _.createHUD(ID, _.timeout);
_.el.querySelector('#theSeek-m').textContent = ~~(_.skippedTime / 60);
_.el.querySelector('#theSeek-s').textContent = _.skippedTime % 60;
_.el.querySelector('#spentLoading').textContent = ~~(_.spentLoading / 100) / 10;
_.el.classList.add(_.lastDelta > 0 ? 'positive' : 'negative');
setTimeout(() => _.el.classList.remove('positive', 'negative'));
_.timeoutRef = setTimeout(_.removeHUD, _.timeout);
document.body.insertAdjacentElement('afterbegin', _.el);
},
removeHUD() {
clearTimeout(_.timeoutRef);
_.timeoutRef = null;
_.el.remove();
_.el.classList.remove('positive', 'negative');
},
createHUD(ID, timeout) {
const hud = document.createElement('div');
hud.id = ID;
hud.className = `default transition`;
const style = document.createElement('style');
style.textContent = `
#${ID} {
position: absolute;
z-index: 10000;
left: 5px;
top: 5px;
font-size: 32px;
padding: 5px 13px;
opacity: 0.6;
backdrop-filter: blur(5px) sepia(1);
}
#${ID}.default {
border: 1px solid slategray;
color: slategray;
opacity: 0;
box-shadow: inset 0 0 11px -1px slategray;
}
#${ID}.positive {
border-color: dodgerblue;
color: dodgerblue;
opacity: 0.7;
box-shadow: inset 0 0 11px -1px dodgerblue;
}
#${ID}.negative {
border-color: tomato;
color: tomato;
opacity: 0.7;
box-shadow: inset 0 0 11px -1px tomato;
}
#${ID}.transition {
transition: all 800ms ease-out, opacity ${timeout}ms ease-in;
}
#${ID} #theSeek-m::after { content: 'm'; }
#${ID} #theSeek-s::after, #${ID} #spentLoading::after { content: 's'; }
`;
hud.appendChild(style);
const savedText = document.createTextNode('saved');
hud.appendChild(savedText);
hud.appendChild(document.createElement('br'));
const spanM = document.createElement('span');
spanM.id = 'theSeek-m';
hud.appendChild(spanM);
const spanS = document.createElement('span');
spanS.id = 'theSeek-s';
hud.appendChild(spanS);
hud.appendChild(document.createElement('br'));
const spanL = document.createElement('span');
spanL.id = 'spentLoading';
hud.appendChild(spanL);
return hud;
},
}) => document.onkeypress = _.handleKey)(/* window.q_skipCount */);
/**
* @author Qwerty <[email protected]>
*
* @name Enable 2/5/10 sec rewind | Netflix
*
* @description This script allows you to more granularly rewind and fast forward Netflix videos.
*
* - Use the `<` and `>` keys to rewind and fast forward by 5 seconds, respectively.
* - Use the **Shift** or **RightAlt** modifier keys to rewind and fast forward by 10 and 2 seconds, respectively.
*
* @description
* submit issues here: https://gist.github.com/ackvf/b180e9883069ad753969a30cb2622787
*
* SO thread: https://stackoverflow.com/questions/4298084/html5-frame-by-frame-viewing-frame-seeking/66464235#66464235
*/
function handleKey(ev) {
let d = 0;
switch (ev.key) {
/* < > keys */
case ",": d = -5; break;
case ".": d = +5; break;
/* Shift + < > keys */
case "?": d = -10; break;
case ":": d = +10; break;
/* RightAlt + < > keys */
case "<": d = -2; break;
case ">": d = +2; break;
}
if (!q_video) q_video = getPlayer();
if (d) q_video.seek(q_video.getCurrentTime() + d * 1000);
}
document.onkeypress = handleKey;
function getPlayer() {
let player = netflix.appContext.state.playerApp.getAPI().videoPlayer;
let session = player.getAllPlayerSessionIds().find(s => s.startsWith("watch"));
return player.getVideoPlayerBySessionId(session);
}
let q_video = getPlayer();
/**
* @author Qwerty <[email protected]>
*
* @name Enable 2/5/10 sec rewind
*
* @description This script allows you to precisely and more granularly rewind and fast forward videos on any website.
*
* - Use the `<` and `>` keys to rewind and fast forward by 5 seconds, respectively.
* - Use the **Shift** or **RightAlt** modifier keys to skip by 10 and 2 seconds, respectively.
* - When the video is **paused**, it will seek frame by frame instead at 30 fps *(configurable)* and double or half the speed with the modifier keys.
*
* @description
* submit issues here: https://gist.github.com/ackvf/b180e9883069ad753969a30cb2622787
*/
/* change these values in devTools to your liking */
window.q_videoSelector = 'video'; /* the querySelector for the video element */
window.q_expectedFramerate = 30; /* your video's framerate */
/* script */
function handleKey(ev) {
window.q_video = document.querySelector(q_videoSelector) ?? document.querySelector('video');
let d = 0;
let f = q_expectedFramerate;
switch (ev.key) {
/* < > keys */
case ",": d = -5; break;
case ".": d = +5; break;
/* Shift + < > keys */
case "?": d = -10; f /= 2; break;
case ":": d = +10; f /= 2; break;
/* RightAlt + < > keys */
case "<": d = -2; f *= 2; break;
case ">": d = +2; f *= 2; break;
case " ": togglePlayback(); break;
}
if (q_video && d) {
/* framestep seek if video is paused */
if (q_video.paused) q_video.currentTime += Math.sign(d) / f;
else q_video.currentTime += d;
}
if (!q_video || q_video !== document.querySelector(q_videoSelector)) console.warn(`Video ${q_videoSelector} not found`, { video: q_video });
}
document.onkeypress = handleKey;
function togglePlayback() {
q_video.paused
? q_video.play()
: q_video.pause();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment