Compare commits

..

No commits in common. "master" and "1.0.3" have entirely different histories.

6 changed files with 98 additions and 133 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,49 +1,30 @@
Replaces YouTube comments with random herp derps. Click on the comment text to reveal the original.
This is a fork of [twstokes/herpderp](https://github.com/twstokes/herpderp) with some modifications.
This is modification of github.com/twstokes/herpderp
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**
* Go to Chrome extensions page.
* Toggle the developer mode (top-right of page).
* Click `Load unpacked`.
* Select this project's root folder.
* 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.
## Development
### Local Testing
* Firefox: open about:debugging and click `Load Temporary Add-on...` then select `manifest.json` file.
* Chrome: follow the install steps from above.
In Firefox, open about:debugging and click `Load Temporary Add-on...` then select `manifest.json`.
file.
## 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.
### Signing and Building
* 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,97 +1,88 @@
const RandomInt = max => Math.floor(Math.random() * max); // [1, max]
// [1, max]
const RandomInt = max => Math.floor(Math.random() * 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 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.
// 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;
};
const ValidatePreviouslyDerpedComments = comment => {
const c = comment;
if (c.derpClicked && c.textContent === c.derpOrig) return;
if (!c.derpClicked && c.textContent === c.derpStr) return;
const c = comment;
if (c.clicked && c.textContent === c.derpOriginal) return;
if (!c.clicked && c.textContent === c.derp_str) 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;
}
// 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;
}
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;
}
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;
}
};
const Init = commentsSection => {
const commentTextId = "#content-text";
const derpedSelector = `${commentTextId}.isDerped`;
const commentRendererName = "YTD-COMMENT-RENDERER";
const commentContentSelector = ["#content-text"]; // Comment text content.
// Need to convert comments that were inserted before the mutation observer below runs.
commentsSection.querySelectorAll(commentRendererName).forEach(node => {
DerpComment(node, node.querySelector(commentTextId));
});
const notDerpedSelector = commentContentSelector
.map(sel => `${sel}:not(.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);
const derpedSelector = commentContentSelector.map(sel => `${sel}.derped`).join(", ");
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));
}
}
});
});
});
// Only watch for child list changes, as we're watching the comments
// container.
const mutationConfig = { attributes: false, childList: true, subtree: true };
// Only watch for child list changes, as we're watching the comments container.
observer.observe(commentsSection, { attributes: false, childList: true, subtree: true });
// 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);
};
// Check every so often if comments are loaded or not. Once they are, the
@ -99,20 +90,18 @@ 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 = () => {
setTimeout(() => {
const commentsSection = document.querySelector("ytd-comments #contents");
const commentSectionSelector = "html body ytd-app ytd-comments ytd-item-section-renderer #contents";
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);
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);
};
CheckIfCommentsLoaded();

View File

@ -1,22 +1,17 @@
{
"manifest_version": 3,
"manifest_version": 2,
"name": "YouTube Herp Derp",
"description": "Replaces YouTube comments with herp derps. Forked from github.com/twstokes/herpderp",
"homepage_url": "https://git.michael.is/michael/youtube-herp-derp-browser-extension",
"version": "1.0.4.2",
"homepage_url": "https://michael.is",
"version": "1.0.3",
"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,
"all_frames": false, // Ignore iframes.
"matches": [
"https://apis.google.com/*",
"https://plus.googleapis.com/*",