NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Crunchyroll Clickable Timestamps // @namespace http://openuserjs.org/ // @version 0.1 // @description Click on a timestamp in the comment section to skip there immediately! // @author L1lith // @match https://www.crunchyroll.com/* // @license MIT // @grant none // @run-at document-idle // ==/UserScript== (function() { const episodeURLRegex = /^[\/]?[a-z0-9\-]+\/[a-z\-0-9]+$/ if (!episodeURLRegex.test(window.location.pathname)) return const customStyles = ( `.smart-timestamp:hover { text-decoration: underline !important; }`) const timestampRegex = /^[0-9]+\:[0-9]{1,2}$/ const maxRetries = 10 let currentCommentLength = 0 const stylesDiv = document.createElement("style") stylesDiv.innerText = customStyles document.body.appendChild(stylesDiv) const commentsHolder = document.getElementById("allCommentsList") if (!commentsHolder) throw new Error("Could Not Find Comments Holder") makeTimestampsInteractive(commentsHolder) const timestampedDivs = [] const moreCommentsButton = document.getElementsByClassName('more_comments')[0] moreCommentsButton.addEventListener('click', makeTimestampsInteractive.bind(null, commentsHolder)) function makeTimestampsInteractive(commentsDiv, retryCount=0) { if (retryCount >= maxRetries) throw new Error("Exceeded Max Retry Limit") const commentContentDivs = [...commentsDiv.getElementsByClassName('guestbook-body')] if (commentContentDivs.length <= currentCommentLength) return setTimeout(makeTimestampsInteractive.bind(null, commentsDiv, retryCount + 1), 500) currentCommentLength = commentContentDivs.length commentContentDivs.forEach(div => { if (timestampedDivs.includes(div)) return timestampedDivs.push(div) const words = div.innerText.split(' ') div.innerText = ""; [...div.children].forEach(child => div.removeChild(child)) words.forEach(word => { div.appendChild(document.createTextNode(" ")) if (timestampRegex.test(word)) { const node = document.createElement("a") node.className = "smart-timestamp" node.innerText = word div.appendChild(node) div.addEventListener("click", goToTime.bind(null, word)) } else { div.appendChild(document.createTextNode(word)) } }) }) } function goToTime(time){ const minutes = parseInt(time.split(':')[0]) const seconds = parseInt(time.split(':')[1]) window.VILOS_PLAYERJS.setCurrentTime(minutes * 60 + seconds) document.getElementById('vilos-player').scrollIntoView() } })();