-
-
Save c3founder/0d47fe3f15f677a0a6f0e96b4868e46a to your computer and use it in GitHub Desktop.
// ==UserScript== | |
// @name Responsive YouTube with Timestamp Control for Roamresearch | |
// @author Connected Cognition Crumbs <[email protected]> | |
// @require Roam42: Wait until Roam42 loads completely | |
// @version 0.3 | |
// @description Add timestamp controls to YouTube videos embedded in Roam and makes the player responsive. | |
// Parameters: | |
// Shortcuts: for grabbing title and current time as a timestamp. | |
// grabTitleKey: if in a DIRECT child block of the YT video, | |
// grabs the title and paste it to the beginning of the current block. | |
// grabTimeKey: if in ANY child blocks of the YT video, | |
// grabs the current time of the player and paste it to the beginning. | |
// Player Size: Video height and width when the right sidebar is closed. | |
// @match https://*.roamresearch.com | |
const ytParams = { | |
//Player Size | |
vidHeight : 480, | |
vidWidth : 720, | |
//Shortcuts | |
grabTitleKey : 'alt+a t', | |
grabTimeKey : 'alt+a n' | |
}; | |
const players = new Map(); | |
var ytReady = setInterval(() => { | |
if(typeof(YT) == 'undefined' || typeof(YT.Player) == 'undefined') { | |
const tag = document.createElement('script'); | |
tag.src = 'https://www.youtube.com/iframe_api'; | |
const firstScriptTag = document.getElementsByTagName('script')[0]; | |
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); | |
clearInterval(ytReady); | |
} | |
}, 1000); | |
//Fill out the current block with the given text | |
function fillTheBlock(givenTxt){ | |
var setValue = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set; | |
let newTextArea = document.querySelector("textarea.rm-block-input"); | |
setValue.call(newTextArea, givenTxt); | |
var e = new Event('input', { bubbles: true }); | |
newTextArea.dispatchEvent(e); | |
} | |
var mouseTrapReady = setInterval(() => { | |
if(Mousetrap === undefined) return; | |
Mousetrap.bind(ytParams.grabTitleKey, async function(e) { | |
e.preventDefault() | |
if (e.srcElement.localName == "textarea") { | |
var container = e.srcElement.closest('.roam-block-container'); | |
var parContainer = container.parentElement.closest('.roam-block-container'); | |
var myIframe = parContainer.querySelector("iframe"); | |
if(myIframe === null) return false; | |
var oldTxt = document.querySelector("textarea.rm-block-input").value; | |
var newValue = players[myIframe.id].getVideoData().title + " " + oldTxt; | |
fillTheBlock(newValue); | |
} | |
return false; | |
}); | |
Mousetrap.bind(ytParams.grabTimeKey, async function(e) { | |
e.preventDefault() | |
for (var plyId in players) { | |
if(players[plyId].getPlayerState() == 1){ | |
var timeStr = new Date(players[plyId].getCurrentTime() * 1000).toISOString().substr(11, 8) | |
var oldTxt = document.querySelector("textarea.rm-block-input").value; | |
fillTheBlock(timeStr + " " + oldTxt); | |
return false; | |
} | |
} | |
return false; | |
}); | |
clearInterval(mouseTrapReady); | |
}, 1000); | |
const activateYtVideos = () => { | |
if(typeof(YT) == 'undefined' || typeof(YT.Player) == 'undefined') return; | |
Array.from(document.getElementsByTagName('IFRAME')) | |
.filter(iframe => iframe.src.includes('youtube.com')) | |
.forEach(ytEl => { | |
if(ytEl.closest('.rm-zoom-item') !== null) { | |
return; //ignore breadcrumbs | |
} | |
const block = ytEl.closest('.roam-block-container'); | |
if(ytEl.src.indexOf("enablejsapi") === -1){ | |
var ytId = extractVideoID(ytEl.src); | |
var frameId = "yt-" + ytEl.closest('.roam-block').id; | |
ytEl.parentElement.id = frameId; | |
ytEl.remove(); | |
players[frameId] = new window.YT.Player(frameId, { | |
height: ytParams.vidHeight, | |
width: ytParams.vidWidth, | |
videoId: ytId | |
}); | |
wrapIframe(frameId); | |
} else { | |
var frameId = ytEl.id | |
} | |
addTimestampControls(block, players[frameId]); | |
var sideBarOpen = document.getElementById("right-sidebar").childElementCount - 1; | |
//Make iframes flexible | |
adjustIframe(frameId, sideBarOpen); | |
}); | |
}; | |
const addTimestampControls = (block, player) => { | |
if (block.children.length < 2) return null; | |
const childBlocks = Array.from(block.children[1].querySelectorAll('.roam-block-container')); | |
childBlocks.forEach(child => { | |
const timestamp = getTimestamp(child); | |
const buttonIfPresent = child.querySelectorAll('.timestamp-control')[0] | |
if (buttonIfPresent) { | |
buttonIfPresent.remove(); | |
} | |
if (timestamp) { | |
addControlButton(child, () => player.seekTo(timestamp, true)); | |
} | |
}); | |
}; | |
const adjustIframe = (frameId, sideBarOpen) => { | |
var child = document.getElementById(frameId); //Iframe | |
var par = child.parentElement; | |
if(sideBarOpen){ | |
child.style.position = 'absolute'; | |
child.style.margin = '0px'; | |
child.style.border = '0px'; | |
child.style.width = '100%'; | |
child.style.height = '100%'; | |
child.style.borderStyle = 'inset'; | |
child.style.borderRadius = '25px'; | |
par.style.position = 'relative'; | |
par.style.paddingBottom = '56.25%'; | |
par.style.height = '0px'; | |
} else { | |
child.style.position = null; | |
child.style.margin = '0px'; | |
child.style.border = '0px'; | |
child.style.width = ytParams.vidWidth + 'px'; | |
child.style.height = ytParams.vidHeight + 'px'; | |
child.style.borderStyle = 'inset'; | |
child.style.borderRadius = '25px'; | |
par.style.position = null; | |
par.style.paddingBottom = '0px'; | |
par.style.height = ytParams.vidHeight + 20 + 'px'; | |
} | |
} | |
const wrapIframe = (frameId) => { | |
var child = document.getElementById(frameId); //Iframe | |
var par = document.createElement('div'); | |
child.parentNode.insertBefore(par, child); | |
par.appendChild(child); | |
child.style.position = 'absolute'; | |
child.style.margin = '0px'; | |
child.style.border = '0px'; | |
child.style.width = '100%'; | |
child.style.height = '100%'; | |
par.style.position = 'relative'; | |
par.style.paddingBottom = '56.25%'; | |
par.style.height = '0px'; | |
}; | |
const getControlButton = (block) => block.querySelectorAll('.timestamp-control')[0]; | |
const addControlButton = (block, fn) => { | |
const button = document.createElement('button'); | |
button.innerText = '►'; | |
button.classList.add('timestamp-control'); | |
button.style.borderRadius = '50%'; | |
button.addEventListener('click', fn); | |
const parentEl = block.children[0]; | |
parentEl.insertBefore(button, parentEl.querySelectorAll('.roam-block')[0]); | |
}; | |
const getTimestamp = (block) => { | |
const innerBlockSelector = block.querySelectorAll('.roam-block'); | |
const blockText = innerBlockSelector.length ? innerBlockSelector[0].textContent : ''; | |
const matches = blockText.match(/^((?:\d+:)?\d+:\d\d)\D/); // start w/ m:ss or h:mm:ss | |
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; | |
}; | |
const extractVideoID = (url) => { | |
var regExp = /^(https?:\/\/)?((www\.)?(youtube(-nocookie)?|youtube.googleapis)\.com.*(v\/|v=|vi=|vi\/|e\/|embed\/\/?|user\/.*\/u\/\d+\/)|youtu\.be\/)([_0-9a-z-]+)/i; | |
var match = url.match(regExp); | |
if ( match && match[7].length == 11 ){ | |
return match[7]; | |
}else{ | |
return null; | |
} | |
}; | |
setInterval(activateYtVideos, 1000); |
Hi having trouble installing. When I copy from https://c3founder.github.io/Roam-Enhancement/ nothing changes to my embedded youtube videos. I am indenting and placing it correctly, I have installed other roam/js code blocks without issue.
I can get the rounded corners on my youtube videos but only if I place this https://gist.github.com/c3founder/0d47fe3f15f677a0a6f0e96b4868e46a#file-responsive-timestamped-youtube-js
in to the javascript code block. But I don't have the timestamp button. I also don't have the ability to resize the youtube window. I really like the idea of working with youtube videos in Roam research. Any help would be appreciated!Also the page, if entered in to a browser window, https://c3founder.github.io/Roam-Enhancement/enhancedX.css
States that the above file can not be found. It is giving me a 404 error.
Hey,
X here is a template!
https://c3founder.github.io/Roam-Enhancement/enhanced**X**.css
e.g., use this for YT:
@import url('https://c3founder.github.io/Roam-Enhancement/enhancedYouTube.css');
X
I have not written the corresponding smart block and have no idea why it is not working with the YT extension. If you are using it for the button that inserts timestamps you can add it as a feature request in issues here.