Skip to content

Instantly share code, notes, and snippets.

@jangxx
Last active September 6, 2024 14:48
Show Gist options
  • Save jangxx/998baf5fd94a6bd6cf400d54c631b213 to your computer and use it in GitHub Desktop.
Save jangxx/998baf5fd94a6bd6cf400d54c631b213 to your computer and use it in GitHub Desktop.
Integrates the Justin Y. Detector right into the YouTube comment section.
// ==UserScript==
// @name Justin Y. Detector Integration
// @namespace https://literalchaos.de/
// @version 0.3
// @description Automatically try to find out if Justin. Y has left a comment on a video
// @author jangxx
// @match http://www.youtube.com/*
// @match https://www.youtube.com/*
// @grant GM_xmlhttpRequest
// @downloadURL https://gist.github.com/jangxx/998baf5fd94a6bd6cf400d54c631b213/raw/justiny_detector_integration.user.js
// @updateURL https://gist.github.com/jangxx/998baf5fd94a6bd6cf400d54c631b213/raw/justiny_detector_integration.user.js
// ==/UserScript==
const escapeHTMLPolicy = trustedTypes.createPolicy("passthrough", {
createHTML: (to_escape) => to_escape,
})
const API_BASE = "https://justiny-detector.literalchaos.de";
function getVideoId() {
if (!location.pathname.includes("watch")) return null; // not a video page
let params = new URLSearchParams(location.search);
return params.get("v");
}
function checkVideoId(id) {
let url = new URL("/api/check-and-retrieve", API_BASE);
url.searchParams.set("v", id);
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: url.toString(),
method: "GET",
responseType: "json",
onerror: reject,
onload: resp => {
if (resp.status != 200) return reject(new Error(resp.statusText));
return resolve(resp.response);
}
});
});
}
function showCommentLink(comment_id) {
// create html element which displays the result
let elem = document.createElement("yt-formatted-string");
elem.className = "count-text style-scope ytd-comments-header-renderer";
elem.id = "justiny-result";
elem.style.display = "inline";
let commentUrl = new URL(location.href);
commentUrl.searchParams.set("lc", comment_id);
// put next to the comment count
waitForNonNull(() => document.querySelector("h2#count")).then(commentCount => {
// remove old results
document.querySelectorAll("#justiny-result").forEach(oldResult => {
oldResult.parentNode.removeChild(oldResult);
});
commentCount.appendChild(elem);
elem.innerHTML = escapeHTMLPolicy.createHTML(`(Justin Y. has left a comment here: <a href="${commentUrl}" class="yt-formatted-string style-scope">Comment Link</a>)`);
});
}
function showMissingCommentText() {
// create html element which displays the result
let elem = document.createElement("yt-formatted-string");
elem.className = "count-text style-scope ytd-comments-header-renderer";
elem.id = "justiny-result";
elem.style.display = "inline";
// put next to the comment count
waitForNonNull(() => document.querySelector("h2#count")).then(commentCount => {
// remove old results
document.querySelectorAll("#justiny-result").forEach(oldResult => {
oldResult.parentNode.removeChild(oldResult);
});
commentCount.appendChild(elem);
elem.textContent = `(Justin Y. has not left a comment here. Yet.)`;
});
}
function processCurrentPage() {
let video_id = getVideoId();
if (video_id == null) return;
checkVideoId(video_id).then(result => {
if (result.comment_id != null) {
showCommentLink(result.comment_id);
} else {
showMissingCommentText();
}
}).catch(err => {
console.log("Error while checking for Justin Y's comment:", err);
});
}
function waitForNonNull(checkFn) {
return new Promise(resolve => {
let interval = setInterval(() => {
let checkValue = checkFn();
if (checkValue != null) {
clearInterval(interval);
resolve(checkValue);
}
}, 250);
});
}
// stay updated about navigation on the site
window.addEventListener("yt-navigate-start", () => {
processCurrentPage();
});
processCurrentPage();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment