MELON SOUR

About

Posts

Projects

Developing a Chrome Extension to Pin YouTube Comments

Programming

10/09/2024

programming, VS Code, chrome extension

I like to watch VTuber streams through archives and a neat thing that some people do is write a comment with timestamps that list the interesting parts of the stream. The annoying thing is that on desktop, YouTube shows comments below the video so I have to repeatedly scroll down to view the comment again whenever I click on a timestamp. This isn't an issue on the mobile app because there's an option to view comments on the right side of the video which gave me the idea of pinning comments. Below is a screenshot of the finished result and the repository with the code can be found here. I haven't gone through the process of uploading it to the extension market but I've referenced a method to install it in just several easy steps.

Making the Chrome Extension

I researched how to develop Chrome extensions and found out they're simply custom html/js/css which are bundled with a manifest.json which inicates how they should be run. The following manifest activates my script on YouTube pages with videos after the browser becomes idle.

{ "manifest_version": 3, "name": "Pin YouTube Comment", "version": "1.0", "description": "Pin a YouTube comment to the right side of the video", "content_scripts": [ { "matches": ["https://www.youtube.com/watch*"], "css": ["style.css"], "js": ["script.js"], "run_at": "document_idle" } ] }

The JavaScript file works as follows. It first periodically checks to see if the initial comments have loaded to initialize a mutation observer. This is because YouTube only loads comments when you scroll down. The mutation observer checks for additional comments when the view port reaches the bottom.

const waitForCommentsSectionLoad = setInterval(() => { // the entire comments section that loads new comments with scroll const commentsSection = document.querySelector('#comments') if (commentsSection) { clearInterval(waitForCommentsSectionLoad) const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length > 0) { addPinButtonToComments() } }) }) observer.observe(commentsSection, { childList: true, subtree: true, }) } }, 1000)

For each comment that's detected, a pin comment link is attached to it.

function addPinButtonToComments() { // container for each comment element let commentsContainer = document.querySelectorAll('ytd-comment-thread-renderer') commentsContainer.forEach((commentContainer) => { let comment = commentContainer.querySelector('#comment') let commentHeader = commentContainer.querySelector('#body #main #header #header-author') // pin button isn't already added if (!commentHeader.querySelector('.pin-button')) { const pinButton = document.createElement('a') togglePinText(pinButton) pinButton.className = 'pin-button' let originalParent = null // parent node includes elements like replies i.e. the full comment let commentParent = comment.parentNode // event listener 1 const onPinComment = (event) => { if (isTheaterMode()) return event.stopPropagation() // store info to restore position when unpinning originalParent = commentParent.parentNode commentParent.style.overflowY = 'scroll' const videoPlayerHeight = document.querySelector('#player').getBoundingClientRect().height commentParent.style.maxHeight = videoPlayerHeight / 2 + 'px' const rightColumn = document.querySelector('#secondary') rightColumn.prepend(commentParent) togglePinText(pinButton) pinButton.removeEventListener('click', onPinComment) pinButton.addEventListener('click', onUnpinComment) } // event listener 2 const onUnpinComment = (event) => { if (isTheaterMode()) return event.stopPropagation() // insert back into original position originalParent.prepend(commentParent) commentParent.style.maxHeight = null togglePinText(pinButton) pinButton.removeEventListener('click', onUnpinComment) pinButton.addEventListener('click', onPinComment) } pinButton.addEventListener('click', onPinComment) commentHeader.appendChild(pinButton) } }) }

There are two event listeners added to the 'pin comment' link which are swapped interchangeably whenever the link is clicked. The first one stores the comment's position in the chain, does some visual modifications to it and pins it to the right of the video. The second one undoes the modifications and uses the position reference to place it back in its original location.

Some additional characteristics of the extensions include...

  • Pinned comments have a maximum height of half the video player so that chat is still partially visible
  • Multiple comments can be pinned for now (though I might change it so that one comment gets replaced)
  • Pinning only works in default mode and not theater mode

And there you have it. 🧁