-
-
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); |
I'm using a mac. I've changed the shortcuts to ctrl+a n and ctrl+a t but for some reason it's not picking up the change. The new shortcut is does nothing but the original alt+ still works (even though I'm on a mac). Any ideas?
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.
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');


the issue is updated here c3founder/Roam-Enhancement#1