Skip to content

Instantly share code, notes, and snippets.

@sawaYch
Last active September 12, 2024 12:52
Show Gist options
  • Save sawaYch/3b212b106a9c282b9692dd900873105e to your computer and use it in GitHub Desktop.
Save sawaYch/3b212b106a9c282b9692dd900873105e to your computer and use it in GitHub Desktop.
YouTube timestamp marker (via developer console)
// credit: https://www.reddit.com/r/VirtualYoutubers/comments/ifzqe7/i_made_a_live_timestamp_tool/
(function () {
if (!document.querySelector("#ytls-pane")) {
var pane = document.createElement("div");
var exit = document.createElement("span");
var list = document.createElement("ul");
var nowli = document.createElement("li");
var nowa = document.createElement("a");
var nowid;
var nowtext = document.createElement("input");
var box = document.createElement("textarea");
var buttons = document.createElement("div");
var paster = document.createElement("button");
var adder = document.createElement("button");
var firstcopy = true;
var copier = document.createElement("button");
var style = document.createElement("style");
function closePane() {
if (confirm("Close timestamp tool?")) {
pane.remove();
cancelAnimationFrame(nowid);
window.removeEventListener("beforeunload", warn);
}
}
function updateStamp(stamp, time) {
var vid = location.search.split(/.+v=|&/)[1] || location.href.split(/\/live\/|\/shorts\/|\?|&/)[1];
stamp.textContent = formatTime(time);
stamp.dataset.time = time;
stamp.href = "https://youtu.be/" + vid + "?t=" + time;
}
function clickStamp(e) {
if (e.target.dataset.time) {
e.preventDefault();
document.querySelector("video").currentTime = e.target.dataset.time;
}
else if (e.target.dataset.increment) {
e.preventDefault();
var li = e.target.parentElement;
var a = li.children[2];
var time = parseInt(a.dataset.time) + parseInt(e.target.dataset.increment);
updateStamp(a, time);
}
}
function watchTime() {
try {
var time = Math.floor(document.querySelector("video").duration);
updateStamp(nowa, time);
} catch (e) {}
nowid = requestAnimationFrame(watchTime);
}
function unformatTime(stamp) {
var hms = stamp.split(":").map(e => parseInt(e));
if (hms.length < 3) {
return 60 * hms[0] + hms[1];
}
return 3600 * hms[0] + 60 * hms[1] + hms[2];
}
function newLi(time, note) {
var li = document.createElement("li");
var minus = document.createElement("span");
var plus = document.createElement("span");
var a = document.createElement("a");
var text = document.createElement("input");
minus.textContent = "➖";
minus.dataset.increment = -1;
plus.textContent = "➕";
plus.dataset.increment = 1;
updateStamp(a, time);
li.appendChild(minus);
li.appendChild(plus);
li.appendChild(a);
li.appendChild(text);
list.appendChild(li);
return text;
}
function pasteList() {
var lines = box.value.split("\n");
list.textContent = "";
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
var stamp = line.split(/\s+/, 1)[0];
var time = unformatTime(stamp);
var note = line.slice(stamp.length + 1);
var text = newLi(time, note);
text.value = note;
}
list.appendChild(nowli);
}
function formatTime(time) {
var h = Math.floor(time / 3600);
var m = Math.floor(time / 60) % 60;
var s = Math.floor(time) % 60;
return (h ? (h + ":" + String(m).padStart(2, 0)) : m) + ":" + String(s).padStart(2, 0);
}
function addStamp() {
var time = Math.max(0, Math.floor(document.querySelector("video").currentTime - 5));
var text = newLi(time);
list.appendChild(nowli);
text.focus();
}
function resetCopier() {
firstcopy = true;
copier.textContent = "Copy List";
}
function copyList() {
var string = "";
if (firstcopy) {
firstcopy = false;
copier.textContent = "Copy Links";
setTimeout(resetCopier, 500);
for (var i = 0; i < list.children.length - 1; i++) {
var stamp = list.children[i].children[2].textContent;
var note = list.children[i].children[3].value;
string += (i > 0 ? "\n" : "") + (stamp + " " + note).trim();
}
}
else {
resetCopier();
for (var i = 0; i < list.children.length - 1; i++) {
var stamp = list.children[i].children[2].href;
var note = list.children[i].children[3].value;
string += (i > 0 ? "\n" : "") + (note + " " + stamp).trim();
}
}
box.value = string;
box.select();
document.execCommand("copy");
}
function warn(e) {
e.preventDefault();
e.returnValue = "Close timestamp tool?";
return e.returnValue;
}
pane.id = "ytls-pane";
exit.textContent = "×";
watchTime();
nowtext.disabled = true;
nowtext.value = "End of Video";
box.id = "ytls-box";
buttons.id = "ytls-buttons";
paster.textContent = "Import List";
adder.textContent = "Add Timestamp";
copier.textContent = "Copy List";
style.textContent = `
#ytls-pane {
background: rgba(0,0,0,.5);
text-align: right;
position: fixed;
bottom: 0;
padding: 0 5px;
opacity: .5;
z-index: 5000;
}
#ytls-pane:hover {
opacity: 1;
}
#ytls-pane span {
cursor: pointer;
}
#ytls-pane ul {
list-style: none;
}
#ytls-pane span, #ytls-pane a, #ytls-pane input {
background: none;
color: white;
font-family: inherit;
font-size: initial;
text-decoration: none;
border: none;
outline: none;
}
#ytls-box {
font-family: monospace;
width: 100%;
display: block;
padding: 0;
border: none;
outline: none;
resize: none;
}
#ytls-buttons {
display: flex;
}
#ytls-buttons button {
background: transparent;
color: white;
font-size: 12px;
flex: auto;
padding: 2px;
border: 1px solid white;
}
`;
exit.addEventListener("click", closePane);
list.addEventListener("click", clickStamp);
list.addEventListener("touchstart", clickStamp);
paster.addEventListener("click", pasteList);
adder.addEventListener("click", addStamp);
copier.addEventListener("click", copyList);
window.addEventListener("beforeunload", warn);
pane.appendChild(exit);
nowli.appendChild(nowa);
nowli.appendChild(nowtext);
list.appendChild(nowli);
pane.appendChild(list);
pane.appendChild(box);
buttons.appendChild(paster);
buttons.appendChild(adder);
buttons.appendChild(copier);
pane.appendChild(buttons);
pane.appendChild(style);
document.body.appendChild(pane);
box.focus();
}})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment