|
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'); |
|
buttonDiv.dataset.youtubebuttons=true |
|
for (i of [1, 1.2,1.5,2,2.5,3]) { |
|
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; |
|
}); |
|
blockContainer.querySelector(":scope div[data-youtubebuttons=true]").remove(); |
|
} |
|
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); |
Issue: 1.2, 1.5, 2x sizing buttons duplicate every time you click and out of the embedded YT block
Enhancement request: Could Ctrl+Shift+Y minify the HH:MM:SS syntax so if it's a 3 minute video I only see 1:38 for example?