Compare commits

...

2 Commits

Author SHA1 Message Date
6745b375a0 Speed things up 2023-02-15 16:11:42 -05:00
829ebd0736 Update README.md 2021-03-24 14:08:40 -04:00
6 changed files with 133 additions and 98 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
node_modules
.web-extension-id
sign.sh
web-ext-artifacts/
sign-firefox-extension.sh

View File

@ -1,30 +1,49 @@
Replaces YouTube comments with random herp derps. Click on the comment text to reveal the original.
This is modification of github.com/twstokes/herpderp
This is a fork of [twstokes/herpderp](https://github.com/twstokes/herpderp) with some modifications.
Note: Ignores iframe embedded videos, including the chat replay iframe on YouTube livestream vids.
**Known bugs**
1. As of 2021-03-24, sometimes when showing the original comment the first few lines are missing.
This tends to happen when you've transitioned to the page from another video. Refreshing the page
tends to fix the problem. I'll eventually try to fix this.
## Install
**Firefox**
* I have a signed xpi that you can immediately install. Open `build/` and drag the xpi into Firefox.
It may take a few seconds for the browser to display the extension installation dialog box.
* You can also find the signed Firefox files in the project's [Releases page](https://git.michael.is/michael/youtube-herp-derp-browser-extension/releases).
**Chrome**
* Unzip the Chrome addon file in the `build` folder.
* Go to Chrome extensions page. Toggle the developer mode (top-right of page). Click `Load
unpacked`.
* Select the unzipped folder.
* Go to Chrome extensions page.
* Toggle the developer mode (top-right of page).
* Click `Load unpacked`.
* Select this project's root folder.
## Development
### Local Testing
In Firefox, open about:debugging and click `Load Temporary Add-on...` then select `manifest.json`.
file.
* Firefox: open about:debugging and click `Load Temporary Add-on...` then select `manifest.json` file.
* Chrome: follow the install steps from above.
### Signing and Building
## Signing and Building
### Firefox
* Can either sign and upload from the CLI or manually upload. The former is easy but it triggers a human review?
* CLI Upload
* Install web-ext with `$ npm install --global web-ext`
* Generate an unlisted xpi with:
`web-ext sign --api-key <your JWT issuer> --api-secret <your JWT secret>`
* You can obtain these keys from https://addons.mozilla.org/en-US/developers/addon/api/key/
* The signed xpi will be in `web-ext-artifacts/`. Drag this into Firefox to install it.
* Alternatively use the private sign-firefox-extension.sh script (not included in the repo) which places the xpi in `build/`.
* Manual Upload
* Go to https://addons.mozilla.org/en-US/developers, open the extension and then click on `Upload new version`.
* Pack the manifest, js script and icons directory into a zip and upload this file.
* To download the signed xpi, click on `view all` (versions), then the name of the new version, then the xpi filename.
* Install web-ext with `$ npm install --global web-ext`
* Generate an unlisted xpi with:
'web-ext sign --channel unlisted --api-key <your add-on signing key> --api-secret <your add-on signing secret>`.
Those keys can be obtained from https://addons.mozilla.org/en-US/developers/addon/api/key/
* Drag downloaded xpi into Firefox.

Binary file not shown.

171
index.js
View File

@ -1,88 +1,97 @@
// [1, max]
const RandomInt = max => Math.floor(Math.random() * max);
const RandomInt = max => Math.floor(Math.random() * max); // [1, max]
const DerpString = (length = 20) => {
const RandomDerp = () => {
let n = RandomInt(4);
let result = "";
if (n == 0)
result = "blah";
else if (n == 1)
result = "durr";
else if (n == 2)
result = "herp";
else if (n == 3)
result = "derp";
return result;
};
return Array.from({ length: (RandomInt(length) + 1) }, () => RandomDerp()).join(" ");
const RandomDerp = () => {
let n = RandomInt(4);
let result = "";
if (n == 0) {
result = "blah";
}
else if (n == 1) {
result = "durr";
}
else if (n == 2) {
result = "herp";
}
else if (n == 3) {
result = "derp";
}
return result;
};
return Array.from({ length: (RandomInt(length) + 1) }, () => RandomDerp()).join(" ");
};
// Herp derps an element.
const DerpElement = element => {
const c = element;
c.derpOriginal = c.textContent; // Preserve the original contents.
c.onclick = () => {
c.clicked = !c.clicked;
c.textContent = c.clicked ? c.derpOriginal : c.derp_str;
};
c.classList.add("derped");
c.derp_str = DerpString();
c.textContent = c.derp_str;
c.clicked = false;
// Herp derps an comment.
const DerpComment = (commentNode, textNode) => {
commentNode.isDerped = true;
const c = textNode;
c.derpOrig = c.textContent; // Preserve the original contents.
c.onclick = () => {
c.derpClicked = !c.derpClicked;
c.textContent = c.derpClicked ? c.derpOrig : c.derpStr;
};
c.classList.add("isDerped");
c.derpStr = DerpString();
c.derpClicked = false;
c.textContent = c.derpStr; // Set the derp text.
};
const ValidatePreviouslyDerpedComments = comment => {
const c = comment;
if (c.clicked && c.textContent === c.derpOriginal) return;
if (!c.clicked && c.textContent === c.derp_str) return;
const c = comment;
if (c.derpClicked && c.textContent === c.derpOrig) return;
if (!c.derpClicked && c.textContent === c.derpStr) return;
// Fix the comment. The only case of malformed comments encountered so far
// are these two cases:
if (c.textContent.indexOf(c.derp_str) !== -1) {
// In the case of the new comment being appended after the derp string,
// just grab it and put it in the derpOriginal variable
const idx = c.derp_str.length;
c.derpOriginal = c.textContent.substring(idx);
c.textContent = c.textContent.substring(0, idx);
c.clicked = false;
return;
}
// Fix the comment. The only case of malformed comments encountered so far
// are these two cases:
if (c.textContent.indexOf(c.derpStr) !== -1) {
// In the case of the new comment being appended after the derp string,
// just grab it and put it in the derpOrig variable
const idx = c.derpStr.length;
c.derpOrig = c.textContent.substring(idx);
c.textContent = c.textContent.substring(0, idx);
c.derpClicked = false;
return;
}
if (c.textContent.indexOf(c.derpOriginal) !== -1) {
// Same issue, but the comment was appended after derpOriginal.
const idx = c.derpOriginal.length;
c.derpOriginal = c.textContent.substring(idx);
c.textContent = c.derp_str;
c.clicked = false;
}
if (c.textContent.indexOf(c.derpOrig) !== -1) {
// Same issue, but the comment was appended after derpOrig.
const idx = c.derpOrig.length;
c.derpOrig = c.textContent.substring(idx);
c.textContent = c.derpStr;
c.derpClicked = false;
}
};
const Init = commentsSection => {
const commentContentSelector = ["#content-text"]; // Comment text content.
const commentTextId = "#content-text";
const derpedSelector = `${commentTextId}.isDerped`;
const commentRendererName = "YTD-COMMENT-RENDERER";
const notDerpedSelector = commentContentSelector
.map(sel => `${sel}:not(.derped)`)
.join(", ");
// Need to convert comments that were inserted before the mutation observer below runs.
commentsSection.querySelectorAll(commentRendererName).forEach(node => {
DerpComment(node, node.querySelector(commentTextId));
});
const derpedSelector = commentContentSelector.map(sel => `${sel}.derped`).join(", ");
// Detect when comments are added to the DOM.
const observer = new MutationObserver(mutations => {
// Check that everything's fine with the already derped comments.
// This is necessary because youtube does a lot of wizardry with comments in-between videos.
document.querySelectorAll(derpedSelector).forEach(ValidatePreviouslyDerpedComments);
// Only watch for child list changes, as we're watching the comments
// container.
const mutationConfig = { attributes: false, childList: true, subtree: true };
mutations.forEach((change, index) => {
console.assert(change.type === "childList");
change.addedNodes.forEach(node => {
if (node.nodeName === commentRendererName) {
if (!node.isDerped) {
DerpComment(node, node.querySelector(commentTextId));
}
}
});
});
});
// Detect when comments are added to the DOM.
const observer = new MutationObserver(() => {
// Check that everything's fine with the already derped comments.
// This is necessary because youtube does a lot of wizardry with comments
// in-between videos.
document.querySelectorAll(derpedSelector).forEach(ValidatePreviouslyDerpedComments);
// Derp all un-derped comments.
document.querySelectorAll(notDerpedSelector).forEach(DerpElement);
});
observer.observe(commentsSection, mutationConfig);
// Only watch for child list changes, as we're watching the comments container.
observer.observe(commentsSection, { attributes: false, childList: true, subtree: true });
};
// Check every so often if comments are loaded or not. Once they are, the
@ -90,18 +99,20 @@ const Init = commentsSection => {
// to be done since comments are added in the DOM through js at an undetermined
// point through Youtube's execution.
const CheckIfCommentsLoaded = () => {
const commentSectionSelector = "html body ytd-app ytd-comments ytd-item-section-renderer #contents";
setTimeout(() => {
const commentsSection = document.querySelector("ytd-comments #contents");
setTimeout(() => {
// This selector is awful, but Youtube re-uses a lot of the DOM (the
// selector for the comments is re-used across a bunch of pages) so we need
// the exact path to the comments to match
const commentsSection = document.querySelector(commentSectionSelector);
if (commentsSection !== null)
Init(commentsSection);
else
CheckIfCommentsLoaded();
}, 500);
if (commentsSection !== null) {
Init(commentsSection);
}
else {
CheckIfCommentsLoaded();
}
// If the timeout is <= 1000 then Chrome sometimes fails to run the mutation observer.
// It'll create it and active it, but it doesn't run the callback on DOM changes...
// I'm seeing this in Chrome 110.
}, 2000);
};
CheckIfCommentsLoaded();

View File

@ -1,17 +1,22 @@
{
"manifest_version": 2,
"manifest_version": 3,
"name": "YouTube Herp Derp",
"description": "Replaces YouTube comments with herp derps. Forked from github.com/twstokes/herpderp",
"homepage_url": "https://michael.is",
"version": "1.0.3",
"homepage_url": "https://git.michael.is/michael/youtube-herp-derp-browser-extension",
"version": "1.0.4.2",
"icons": {
"48": "icons/herp48.png",
"128": "icons/herp128.png"
},
"browser_specific_settings": {
"gecko": {
"id": "{8460883e-a350-4e55-8c07-eed5eefdc2b5}"
}
},
"content_scripts": [
{
"run_at": "document_idle",
"all_frames": false, // Ignore iframes.
"all_frames": false,
"matches": [
"https://apis.google.com/*",
"https://plus.googleapis.com/*",