Last active
March 17, 2025 15:48
-
-
Save h-lunah/e6dfb2a86dc4726cc54d66eb4ec73580 to your computer and use it in GitHub Desktop.
YouTube Captions Fixer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name YouTube Captions Fix | |
// @namespace http://tampermonkey.net/ | |
// @version 2025-03-13 | |
// @description Blank out all instances of YouTube's censorship/sound detection in auto-generated captions | |
// @author luna (h-lunah) | |
// @match https://www.youtube.com/* | |
// @match https://www.youtube-nocookie.com/* | |
// @match https://m.youtube.com/* | |
// @match https://music.youtube.com/* | |
// @icon https://cdn.discordapp.com/emojis/1236984687126384640.png?v=2 | |
// @grant none | |
// @run-at document-start | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
/* match all instances of YouTube's censorship or sound detection | |
* example captions given substitute "fuck" (censored) for 1 or more spaces */ | |
const censorRe = new RegExp(/\[(.*?)\]/gm); // censorship signatures, [Music], [Applause], [Laughter], etc. for any language | |
const spaceRe = new RegExp(/\s{2,}/gm); // spaces leftover after signature cleanup, eg. "well fuck you man" | |
const punctRe = new RegExp(/\s(?=[!?.])/gm); // spaces leftover before punctuation marks preceded by signature markers | |
const altRe = new RegExp(/^foreign$|^thank you$|^laughs$/gm); // alternatives for [Music] and [Laughter] | |
const removeCaptions = () => { | |
// interface with any caption element, even preview ones | |
var captionBox = document.querySelector(".captions-text"); | |
var segmentBox = document.querySelector(".ytd-transcript-segment-renderer"); | |
var captions = document.getElementsByClassName("ytp-caption-segment"); | |
var transcriptCaptions = document.getElementsByClassName("segment-text"); | |
// match detected captions | |
for ( const caption of captions ) { | |
caption.style.whiteSpace = "normal"; | |
// blank out detected signatures | |
if ( caption.innerText.search(censorRe) !== -1 ) { | |
console.log(`removed: ${caption.innerText.match(censorRe).join(", ")}`); | |
caption.innerText = caption.innerText.replaceAll(censorRe, ""); | |
} | |
// blank out other signatures for sound detection | |
if ( caption.innerText.search(altRe) !== -1 ) { | |
console.log(`removed alternative signature: ${caption.innerText.match(altRe)}`); | |
caption.innerText = caption.innerText.replaceAll(altRe, ""); | |
} | |
// clean up leftovers after signature blanking | |
if ( caption.innerText.search(spaceRe) !== -1 ) { | |
console.log("removed double whitespace"); | |
caption.innerText = caption.innerText.replaceAll(spaceRe, " "); | |
} | |
// clean up excess whitespace after [censors] followed by punctuation (human captions) | |
if ( caption.innerText.search(punctRe) !== -1 ) { | |
console.log("removed whitespace before punctutation mark"); | |
caption.innerText = caption.innerText.replaceAll(punctRe, ""); | |
} | |
if ( caption.innerText.search(/Click for settings/gm) !== -1 ) { | |
caption.innerText = caption.innerText.replaceAll(/Click for settings/gm, "These captions have been cleaned up"); | |
} | |
/* remove fully blanked out caption lines | |
* applies to caption lines during non-spoken section of videos, eg. https://www.youtube.com/watch?v=1HJRGygc1yc&t=50s | |
* | |
* "fuck fuck fucking football" | |
* "[Music] " */ | |
if ( !caption.innerText ) { | |
caption.parentElement.remove(); | |
console.log("removed stray blank caption"); | |
} | |
/* remove fully blanked captions | |
* applies to music-only videos, eg. https://www.youtube.com/watch?v=OpaKHVOEDiE | |
* | |
* "[Music]" */ | |
if ( captionBox && !captionBox.childNodes.length ) { | |
captionBox.parentElement.remove(); | |
console.log("removed blank caption box"); | |
} | |
} | |
// match caption transcripts | |
for ( const transcriptCaption of transcriptCaptions ) { | |
if ( transcriptCaption.innerText.search(censorRe) !== -1 ) { | |
console.log(`removed: ${transcriptCaption.innerText.match(censorRe).join(", ")}`); | |
transcriptCaption.innerText = transcriptCaption.innerText.replaceAll(censorRe, ""); | |
} | |
// blank out other signatures for sound detection | |
if ( transcriptCaption.innerText.search(altRe) !== -1 ) { | |
console.log(`removed alternative signature: ${transcriptCaption.innerText.match(altRe)}`); | |
transcriptCaption.innerText = transcriptCaption.innerText.replaceAll(altRe, ""); | |
} | |
// clean up leftovers after signature blanking | |
if ( transcriptCaption.innerText.search(spaceRe) !== -1 ) { | |
console.log("removed double whitespace"); | |
transcriptCaption.innerText = transcriptCaption.innerText.replaceAll(spaceRe, " "); | |
} | |
// clean up excess whitespace after [censors] followed by punctuation (human captions) | |
if ( transcriptCaption.innerText.search(punctRe) !== -1 ) { | |
console.log("removed whitespace before punctutation mark"); | |
transcriptCaption.innerText = transcriptCaption.innerText.replaceAll(punctRe, ""); | |
} | |
/* remove fully blanked out transcript lines | |
* applies to sections where there was only music/unknown sounds | |
* "[Music]" */ | |
if ( !transcriptCaption.innerText ) { | |
transcriptCaption.parentNode.remove(); | |
console.log("removed blank transcript box"); | |
} | |
} | |
} | |
// Optimized removals are done using MutationObservers. You will not see any markers flash at all. | |
const setupObserver = () => { | |
// Make sure the <body> exists | |
if (document.body) { | |
const observer = new MutationObserver(() => removeCaptions()); | |
// Observe the <body> | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
// Run filter on what's present, there isn't anything loaded at this stage though. | |
removeCaptions(); | |
} else { | |
// Wait for <body> to appear | |
setTimeout(setupObserver, 10); | |
} | |
}; | |
// Enable caption cleaning | |
setupObserver(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment