Skip to content

Instantly share code, notes, and snippets.

@Joilence
Forked from jlongtine/README.md
Created October 3, 2020 06:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Joilence/75e47c994bcba0926aa0fb9b2dfb0d9b to your computer and use it in GitHub Desktop.
Save Joilence/75e47c994bcba0926aa0fb9b2dfb0d9b to your computer and use it in GitHub Desktop.
youtube-timestamp.ts

You can add this to Roam using {{[[roam/js]]}}

Grab the code in youtube-timestamps.js and drop it in a javascript code block:

  ```javascript```

You can have timestamps at the beginning of any nested block. H:MM:SS

You can use the ctrl+shift+y hotkey (currently assumes you have Roam42 installed, let me know if you'd prefer I drop that requirement) to grab the current timestamp of the video you're cursor is nested under and add it to the beginning of your block.

window.players = {}
scanYouTube = (event, observer) => {
event.forEach((mutation) => {
if (mutation.type == "childList") {
mutation.addedNodes.forEach((node) => {
if (node.nodeType == Node.ELEMENT_NODE) {
node.querySelectorAll("iframe[src*='youtube.com']:not([src*='enablejsapi'])").forEach( (element)=>{
switchYouTube(element);
})
}
})
}
})
}
switchYouTube = (element) => {
const pathParts = new URL(element.src).pathname.split("/");
const youtubeid = pathParts[pathParts.length - 1];
const blockContainer = element.closest('.roam-block-container');
const block = element.closest('.roam-block');
if (!block) { return; }
const parent = element.parentElement;
console.log(element);
console.log(block);
full_id = block.id + "-" + youtubeid;
blockContainer.dataset.full_id = full_id;
blockContainer.dataset.youtube = true;
parent.id = full_id;
element.remove();
const player = new window.YT.Player(parent.id, {
height: '300', width: '450', videoId: youtubeid
});
const iframe = player.getIframe();
players[full_id] = player;
iframe.dataset.youtubeid = youtubeid
console.log("Found Youtube ID: " + youtubeid );
setTimeout(()=>{
addTimestampControls(blockContainer, block.id, player)
}, 200);
height = 300;
width = 450;
buttonDiv = document.createElement('div');
for (i of [1.2,1.5,2]) {
console.log(i);
const multiplier = i;
const button = document.createElement('button');
button.innerText = multiplier.toString();
button.addEventListener('click', () => {
console.log("Setting height: " + multiplier * height + "; width: " + multiplier * width);
iframe.height = multiplier * height;
iframe.width = multiplier * width;
});
button.style.marginRight = '8px';
buttonDiv.appendChild(button);
}
block.parentElement.parentElement.appendChild(buttonDiv);
}
addTimestampControls = (blockContainer, blockID, player) => {
const getTimestampAddButton = (event, observer) => {
if (Array.from(event[0].removedNodes).some((el) => { return el.id == blockID})) {
console.log("block was removed: " + blockID);
observer.disconnect();
removeButtons();
} else {
console.log(event);
addButtons();
}
}
const removeButtons = () => {
blockContainer.querySelectorAll(":scope .roam-block-container .roam-block[data-youtubets]").forEach(child => {
child.parentElement.querySelector('button[data-youtubets="' + child.dataset.youtubets + '"]').remove();
delete child.dataset.youtubets;
});
}
const addButtons = () => {
blockContainer.querySelectorAll(":scope .roam-block-container .roam-block:not([data-youtubets])").forEach(child => {
const timestamp = getTimestamp(child);
if (timestamp != null) {
child.dataset.iframe_id = player.getIframe().id;
child.dataset.youtubets = timestamp;
addControlButton(child, timestamp, () => {
player.seekTo(timestamp, true)
pState = player.getPlayerState();
if (pState == -1 || pState == 0 || pState == 2) {
player.playVideo();
}
});
}
});
}
addButtons();
const x = new MutationObserver(getTimestampAddButton);
x.observe(blockContainer, {
childList: true,
subtree: true
});
};
getTimestamp = (block) => {
const matches = block.textContent.match(/^((?:\d+:)?\d+:\d\d)($|\D)/);
if (!matches || matches.length < 2) return null;
const timeParts = matches[1].split(':').map(part => parseInt(part));
if (timeParts.length == 3) return timeParts[0]*3600 + timeParts[1]*60 + timeParts[2];
else if (timeParts.length == 2) return timeParts[0]*60 + timeParts[1];
else return null;
}
addControlButton = (block, timestamp, fn) => {
const button = document.createElement('button');
button.innerText = '►';
button.addEventListener('click', fn);
button.style.marginRight = '8px';
button.dataset.youtubets = timestamp;
const printElement = (e) => {
if (Array.from(e[0].removedNodes).some((el) => { return el.id == block.id})) {
console.log("Removed Nodes: ");
console.log(e[0].removedNodes);
button.remove();
}
}
const x = new MutationObserver(printElement);
x.observe(block.parentElement, { childList: true });
block.parentElement.insertBefore(button, block);
};
if (document.querySelectorAll("html > script[src*='https://www.youtube.com/iframe_api']").length == 0) {
console.log("Load Youtube API");
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
const htmlEl = document.getElementsByTagName('html')[0];
htmlEl.appendChild(tag);
}
if (document.querySelectorAll("head > script[src='https://craig.global.ssl.fastly.net/js/mousetrap/mousetrap.min.js?a4098']").length == 0) {
console.log("Installing Mousetrap");
const tag = document.createElement('script');
tag.src = 'https://craig.global.ssl.fastly.net/js/mousetrap/mousetrap.min.js?a4098';
const htmlEl = document.getElementsByTagName('head')[0];
htmlEl.appendChild(tag);
}
window.onYouTubeIframeAPIReady = () => {
document.querySelectorAll("iframe[src*='youtube.com']:not([src*='enablejsapi'])").forEach( (element)=>{
switchYouTube(element);
})
const observerYouTube = new MutationObserver(scanYouTube);
observerYouTube.observe(document.querySelector('.roam-body-main'), {
childList: true,
subtree: true
});
}
setTimeout(()=>{
// Add bindGlobal
(function(a){var c={},d=a.prototype.stopCallback;a.prototype.stopCallback=function(e,b,a,f){return this.paused?!0:c[a]||c[f]?!1:d.call(this,e,b,a)};a.prototype.bindGlobal=function(a,b,d){this.bind(a,b,d);if(a instanceof Array)for(b=0;b<a.length;b++)c[a[b]]=!0;else c[a]=!0};a.init()})(Mousetrap);
console.log("Set up hotkey");
Mousetrap.bindGlobal('ctrl+shift+y', (event, handler) => {
console.log("Getting timestamp");
event.preventDefault()
if (event.srcElement.localName == "textarea") {
bc = event.srcElement.closest('.roam-block-container[data-youtube=true]');
player = players[bc.dataset.full_id];
var timeStr = new Date(player.getCurrentTime() * 1000).toISOString().substr(11, 8)
console.log(timeStr);
var newValue = timeStr + " " + event.srcElement.innerHTML;
document.getElementById(event.srcElement.id).value = newValue;
return false;
}
});
},1000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment