This commit is contained in:
Dmitrii Selivanov
2021-12-19 01:42:49 +03:00
6 changed files with 419 additions and 374 deletions
+49 -374
View File
@@ -1,387 +1,62 @@
function RYD() {
const LIKED_STATE = "LIKED_STATE";
const DISLIKED_STATE = "DISLIKED_STATE";
const NEUTRAL_STATE = "NEUTRAL_STATE";
let storedData = {
likes: 0,
dislikes: 0,
previousState: NEUTRAL_STATE,
};
import {
getButtons,
getLikeButton,
getDislikeButton,
checkForSignInButton,
} from "./src/buttons";
import {
isMobile,
isVideoDisliked,
isVideoLiked,
getState,
setState,
setInitialState,
setLikes,
setDislikes,
getLikeCountFromButton,
LIKED_STATE,
DISLIKED_STATE,
NEUTRAL_STATE,
} from "./src/state";
import { numberFormat, getBrowser, getVideoId, isVideoLoaded, cLog } from "./src/utils";
import { createRateBar } from "./src/bar";
import { sendVideoIds, sendVote, likeClicked, dislikeClicked } from "./src/events"
function cLog(message, writer) {
message = `[return youtube dislike]: ${message}`;
if (writer) {
writer(message);
} else {
console.log(message);
}
}
let storedData = {
likes: 0,
dislikes: 0,
previousState: NEUTRAL_STATE,
};
function isMobile() {
return location.hostname == "m.youtube.com";
}
let jsInitChecktimer = null;
function getButtons() {
if (isMobile()) {
return document.querySelector(".slim-video-action-bar-actions");
}
// If Menu Element Is Displayed:
if (document.getElementById("menu-container")?.offsetParent === null) {
return document.querySelector(
"ytd-menu-renderer.ytd-watch-metadata > div"
);
// If Menu Element Isnt Displayed:
} else {
return document
.getElementById("menu-container")
?.querySelector("#top-level-buttons-computed");
}
}
function getLikeButton() {
return getButtons().children[0];
}
function getDislikeButton() {
return getButtons().children[1];
}
function isVideoLiked() {
if (isMobile()) {
return (
getLikeButton().querySelector("button").getAttribute("aria-label") ==
"true"
);
}
return getLikeButton().classList.contains("style-default-active");
}
function isVideoDisliked() {
if (isMobile()) {
return (
getDislikeButton().querySelector("button").getAttribute("aria-label") ==
"true"
);
}
return getDislikeButton().classList.contains("style-default-active");
}
function checkForSignInButton() {
if (
document.querySelector(
"a[href^='https://accounts.google.com/ServiceLogin']"
)
) {
return true;
} else {
return false;
}
}
function getState() {
if (isVideoLiked()) {
return { current: LIKED_STATE, previous: storedData.previousState };
}
if (isVideoDisliked()) {
return { current: DISLIKED_STATE, previous: storedData.previousState };
}
return { current: NEUTRAL_STATE, previous: storedData.previousState };
}
//--- Sets The Likes And Dislikes Values ---//
function setLikes(likesCount) {
(getButtons().children[0].querySelector("#text")).innerText = likesCount;
}
function setDislikes(dislikesCount) {
if (isMobile()) {
getButtons().children[1].querySelector(
".button-renderer-text"
).innerText = dislikesCount;
return;
}
(getButtons().children[1].querySelector("#text").innerText) = dislikesCount;
}
function getLikeCountFromButton() {
let likesStr = getLikeButton()
.querySelector("button")
.getAttribute("aria-label")
.replace(/\D/g, "");
return likesStr.length > 0 ? parseInt(likesStr) : false;
}
function processResponse(response) {
const formattedDislike = numberFormat(response.dislikes);
setDislikes(formattedDislike);
storedData.dislikes = parseInt(response.dislikes);
storedData.likes = getLikeCountFromButton() || parseInt(response.likes);
createRateBar(storedData.likes, storedData.dislikes);
}
function setState() {
storedData.previousState = isVideoDisliked()
? DISLIKED_STATE
: isVideoLiked()
? LIKED_STATE
: NEUTRAL_STATE;
let statsSet = false;
RYDTools.getBrowser().runtime.sendMessage(
{
message: "set_state",
videoId: getVideoId(window.location.href),
state: getState().current,
likeCount: getLikeCountFromButton() || null
},
function (response) {
cLog("response from api:");
cLog(JSON.stringify(response));
if (response !== undefined && !("traceId" in response) && !statsSet) {
processResponse(response);
} else {
}
}
);
}
function sendVote(vote) {
RYDTools.getBrowser().runtime.sendMessage({
message: "send_vote",
vote: vote,
videoId: getVideoId(window.location.href)
});
}
function likeClicked() {
if (checkForSignInButton() === false) {
if (storedData.previousState === DISLIKED_STATE) {
sendVote(1);
storedData.dislikes--;
storedData.likes++;
createRateBar(storedData.likes, storedData.dislikes);
setDislikes(numberFormat(storedData.dislikes));
storedData.previousState = LIKED_STATE;
} else if (storedData.previousState === NEUTRAL_STATE) {
sendVote(1);
storedData.likes++;
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = LIKED_STATE;
} else if ((storedData.previousState = LIKED_STATE)) {
sendVote(0);
storedData.likes--;
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = NEUTRAL_STATE;
function setEventListeners(evt) {
function checkForJS_Finish() {
if (getButtons()?.offsetParent && isVideoLoaded()) {
clearInterval(jsInitChecktimer);
jsInitChecktimer = null;
const buttons = getButtons();
if (!window.returnDislikeButtonlistenersSet) {
buttons.children[0].addEventListener("click", () => likeClicked(storedData));
buttons.children[1].addEventListener("click", () => dislikeClicked(storedData));
window.returnDislikeButtonlistenersSet = true;
}
setInitialState(storedData);
}
}
function dislikeClicked() {
if (checkForSignInButton() == false) {
if (storedData.previousState === NEUTRAL_STATE) {
sendVote(-1);
storedData.dislikes++;
setDislikes(numberFormat(storedData.dislikes));
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = DISLIKED_STATE;
} else if (storedData.previousState === DISLIKED_STATE) {
sendVote(0);
storedData.dislikes--;
setDislikes(numberFormat(storedData.dislikes));
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = NEUTRAL_STATE;
} else if (storedData.previousState === LIKED_STATE) {
sendVote(-1);
storedData.likes--;
storedData.dislikes++;
setDislikes(numberFormat(storedData.dislikes));
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = DISLIKED_STATE;
}
}
if (window.location.href.indexOf("watch?") >= 0) {
jsInitChecktimer = setInterval(checkForJS_Finish, 111);
}
function setInitialState() {
setState();
setTimeout(() => {
sendVideoIds();
}, 1500);
}
function getVideoId(url) {
const urlObject = new URL(url);
const pathname = urlObject.pathname;
if (pathname.startsWith("/clip")) {
return document.querySelector("meta[itemprop='videoId']").content;
} else {
return urlObject.searchParams.get("v");
}
}
function isVideoLoaded() {
const videoId = getVideoId(window.location.href);
return (
document.querySelector(`ytd-watch-flexy[video-id='${videoId}']`) !==
null ||
// mobile: no video-id attribute
document.querySelector('#player[loading="false"]:not([hidden])') !== null
);
}
function roundDown(num) {
if (num < 1000) return num;
const int = Math.floor(Math.log10(num) - 2);
const decimal = int + (int % 3 ? 1 : 0);
const value = Math.floor(num / 10 ** decimal);
return value * 10 ** decimal;
}
function numberFormat(numberState) {
let userLocales;
try {
userLocales = new URL(
Array.from(document.querySelectorAll("head > link[rel='search']"))
?.find((n) => n?.getAttribute("href")?.includes("?locale="))
?.getAttribute("href")
)?.searchParams?.get("locale");
} catch {}
const formatter = Intl.NumberFormat(
document.documentElement.lang || userLocales || navigator.language,
{
notation: "compact",
}
);
return formatter.format(roundDown(numberState));
}
let jsInitChecktimer = null;
function setEventListeners(evt) {
function checkForJS_Finish() {
if (getButtons()?.offsetParent && isVideoLoaded()) {
clearInterval(jsInitChecktimer);
jsInitChecktimer = null;
const buttons = getButtons();
if (!window.returnDislikeButtonlistenersSet) {
buttons.children[0].addEventListener("click", likeClicked);
buttons.children[1].addEventListener("click", dislikeClicked);
window.returnDislikeButtonlistenersSet = true;
}
setInitialState();
}
}
if (window.location.href.indexOf("watch?") >= 0) {
jsInitChecktimer = setInterval(checkForJS_Finish, 111);
}
}
function createRateBar(likes, dislikes) {
let rateBar = document.getElementById("ryd-bar-container");
const widthPx =
getButtons().children[0].clientWidth +
getButtons().children[1].clientWidth +
8;
const widthPercent =
likes + dislikes > 0 ? (likes / (likes + dislikes)) * 100 : 50;
if (!rateBar) {
(
document.getElementById("menu-container") ||
document.querySelector("ytm-slim-video-action-bar-renderer")
).insertAdjacentHTML(
"beforeend",
`
<div class="ryd-tooltip" style="width: ${widthPx}px">
<div class="ryd-tooltip-bar-container">
<div
id="ryd-bar-container"
style="width: 100%; height: 2px;"
>
<div
id="ryd-bar"
style="width: ${widthPercent}%; height: 100%"
></div>
</div>
</div>
<tp-yt-paper-tooltip position="top" id="ryd-dislike-tooltip" class="style-scope ytd-sentiment-bar-renderer" role="tooltip" tabindex="-1">
<!--css-build:shady-->${likes.toLocaleString()}&nbsp;/&nbsp;${dislikes.toLocaleString()}
</tp-yt-paper-tooltip>
</div>
`
);
} else {
document.getElementById("ryd-bar-container").style.width = widthPx + "px";
document.getElementById("ryd-bar").style.width = widthPercent + "%";
document.querySelector(
"#ryd-dislike-tooltip > #tooltip"
).innerHTML = `${likes.toLocaleString()}&nbsp;/&nbsp;${dislikes.toLocaleString()}`;
}
}
function sendVideoIds() {
let links = Array.from(
document.getElementsByClassName(
"yt-simple-endpoint ytd-compact-video-renderer"
)
).concat(
Array.from(
document.getElementsByClassName("yt-simple-endpoint ytd-thumbnail")
)
);
// Also try mobile
if (links.length < 1)
links = Array.from(
document.querySelectorAll(
".large-media-item-metadata > a, a.large-media-item-thumbnail-container"
)
);
const ids = links
.filter((x) => x.href && x.href.indexOf("/watch?v=") > 0)
.map((x) => getVideoId(x.href));
RYDTools.getBrowser().runtime.sendMessage({
message: "send_links",
videoIds: ids,
});
}
setEventListeners();
document.addEventListener("yt-navigate-finish", function (event) {
if (jsInitChecktimer !== null) clearInterval(jsInitChecktimer);
window.returnDislikeButtonlistenersSet = false;
setEventListeners();
});
setTimeout(() => sendVideoIds(), 2500);
this.init = function () {};
}
RYD.getInstance = function () {
if (typeof RYD.instance == "undefined") RYD.instance = new RYD();
return RYD.instance;
};
setEventListeners();
RYDTools = {};
document.addEventListener("yt-navigate-finish", function (event) {
if (jsInitChecktimer !== null) clearInterval(jsInitChecktimer);
window.returnDislikeButtonlistenersSet = false;
setEventListeners();
});
RYDTools.getBrowser = function () {
if (typeof chrome !== "undefined" && typeof chrome.runtime !== "undefined") {
return chrome;
} else if (
typeof browser !== "undefined" &&
typeof browser.runtime !== "undefined"
) {
return browser;
} else {
console.log("browser is not supported");
return false;
}
};
RYD.getInstance().init();
setTimeout(() => sendVideoIds(), 2500);
+49
View File
@@ -0,0 +1,49 @@
import { getButtons } from "./buttons";
function createRateBar(likes, dislikes) {
let rateBar = document.getElementById("ryd-bar-container");
const widthPx =
getButtons().children[0].clientWidth +
getButtons().children[1].clientWidth +
8;
const widthPercent =
likes + dislikes > 0 ? (likes / (likes + dislikes)) * 100 : 50;
if (!rateBar) {
(
document.getElementById("menu-container") ||
document.querySelector("ytm-slim-video-action-bar-renderer")
).insertAdjacentHTML(
"beforeend",
`
<div class="ryd-tooltip" style="width: ${widthPx}px">
<div class="ryd-tooltip-bar-container">
<div
id="ryd-bar-container"
style="width: 100%; height: 2px;"
>
<div
id="ryd-bar"
style="width: ${widthPercent}%; height: 100%"
></div>
</div>
</div>
<tp-yt-paper-tooltip position="top" id="ryd-dislike-tooltip" class="style-scope ytd-sentiment-bar-renderer" role="tooltip" tabindex="-1">
<!--css-build:shady-->${likes.toLocaleString()}&nbsp;/&nbsp;${dislikes.toLocaleString()}
</tp-yt-paper-tooltip>
</div>
`
);
} else {
document.getElementById("ryd-bar-container").style.width = widthPx + "px";
document.getElementById("ryd-bar").style.width = widthPercent + "%";
document.querySelector(
"#ryd-dislike-tooltip > #tooltip"
).innerHTML = `${likes.toLocaleString()}&nbsp;/&nbsp;${dislikes.toLocaleString()}`;
}
}
export { createRateBar };
+43
View File
@@ -0,0 +1,43 @@
import { isMobile } from "./state";
function getButtons() {
if (isMobile()) {
return document.querySelector(".slim-video-action-bar-actions");
}
//--- If Menu Element Is Displayed: ---//
if (document.getElementById("menu-container")?.offsetParent === null) {
return document.querySelector("ytd-menu-renderer.ytd-watch-metadata > div");
//--- If Menu Element Isnt Displayed: ---//
} else {
return document
.getElementById("menu-container")
?.querySelector("#top-level-buttons-computed");
}
}
function getLikeButton() {
return getButtons().children[0];
}
function getDislikeButton() {
return getButtons().children[1];
}
function checkForSignInButton() {
if (
document.querySelector(
"a[href^='https://accounts.google.com/ServiceLogin']"
)
) {
return true;
} else {
return false;
}
}
export {
getButtons,
getLikeButton,
getDislikeButton,
checkForSignInButton,
};
+88
View File
@@ -0,0 +1,88 @@
import { getBrowser, getVideoId, numberFormat } from "./utils"
import { checkForSignInButton } from "./buttons"
import { NEUTRAL_STATE, LIKED_STATE, DISLIKED_STATE, setDislikes } from "./state"
import { createRateBar } from "./bar"
function sendVote(vote) {
getBrowser().runtime.sendMessage({
message: "send_vote",
vote: vote,
videoId: getVideoId(window.location.href),
});
}
function sendVideoIds() {
let links = Array.from(
document.getElementsByClassName(
"yt-simple-endpoint ytd-compact-video-renderer"
)
).concat(
Array.from(
document.getElementsByClassName("yt-simple-endpoint ytd-thumbnail")
)
);
// Also try mobile
if (links.length < 1)
links = Array.from(
document.querySelectorAll(
".large-media-item-metadata > a, a.large-media-item-thumbnail-container"
)
);
const ids = links
.filter((x) => x.href && x.href.indexOf("/watch?v=") > 0)
.map((x) => getVideoId(x.href));
getBrowser().runtime.sendMessage({
message: "send_links",
videoIds: ids,
});
}
function likeClicked(storedData) {
if (checkForSignInButton() === false) {
if (storedData.previousState === DISLIKED_STATE) {
sendVote(1);
storedData.dislikes--;
storedData.likes++;
createRateBar(storedData.likes, storedData.dislikes);
setDislikes(numberFormat(storedData.dislikes));
storedData.previousState = LIKED_STATE;
} else if (storedData.previousState === NEUTRAL_STATE) {
sendVote(1);
storedData.likes++;
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = LIKED_STATE;
} else if ((storedData.previousState = LIKED_STATE)) {
sendVote(0);
storedData.likes--;
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = NEUTRAL_STATE;
}
}
}
function dislikeClicked(storedData) {
if (checkForSignInButton() == false) {
if (storedData.previousState === NEUTRAL_STATE) {
sendVote(-1);
storedData.dislikes++;
setDislikes(numberFormat(storedData.dislikes));
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = DISLIKED_STATE;
} else if (storedData.previousState === DISLIKED_STATE) {
sendVote(0);
storedData.dislikes--;
setDislikes(numberFormat(storedData.dislikes));
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = NEUTRAL_STATE;
} else if (storedData.previousState === LIKED_STATE) {
sendVote(-1);
storedData.likes--;
storedData.dislikes++;
setDislikes(numberFormat(storedData.dislikes));
createRateBar(storedData.likes, storedData.dislikes);
storedData.previousState = DISLIKED_STATE;
}
}
}
export { sendVote, sendVideoIds, likeClicked, dislikeClicked }
+120
View File
@@ -0,0 +1,120 @@
import { getLikeButton, getDislikeButton, getButtons } from "./buttons";
import { createRateBar } from "./bar";
import { getBrowser, getVideoId, cLog, numberFormat } from "./utils";
import { sendVideoIds } from "./events";
const LIKED_STATE = "LIKED_STATE";
const DISLIKED_STATE = "DISLIKED_STATE";
const NEUTRAL_STATE = "NEUTRAL_STATE";
function isMobile() {
return location.hostname == "m.youtube.com";
}
function isVideoLiked() {
if (isMobile()) {
return (
getLikeButton().querySelector("button").getAttribute("aria-label") ==
"true"
);
}
return getLikeButton().classList.contains("style-default-active");
}
function isVideoDisliked() {
if (isMobile()) {
return (
getDislikeButton().querySelector("button").getAttribute("aria-label") ==
"true"
);
}
return getDislikeButton().classList.contains("style-default-active");
}
function getState(storedData) {
if (isVideoLiked()) {
return { current: LIKED_STATE, previous: storedData.previousState };
}
if (isVideoDisliked()) {
return { current: DISLIKED_STATE, previous: storedData.previousState };
}
return { current: NEUTRAL_STATE, previous: storedData.previousState };
}
//--- Sets The Likes And Dislikes Values ---//
function setLikes(likesCount) {
getButtons().children[0].querySelector("#text").innerText = likesCount;
}
function setDislikes(dislikesCount) {
if (isMobile()) {
getButtons().children[1].querySelector(".button-renderer-text").innerText =
dislikesCount;
return;
}
getButtons().children[1].querySelector("#text").innerText = dislikesCount;
}
function getLikeCountFromButton() {
let likesStr = getLikeButton()
.querySelector("button")
.getAttribute("aria-label")
.replace(/\D/g, "");
return likesStr.length > 0 ? parseInt(likesStr) : false;
}
function processResponse(response, storedData) {
const formattedDislike = numberFormat(response.dislikes);
setDislikes(formattedDislike);
storedData.dislikes = parseInt(response.dislikes);
storedData.likes = getLikeCountFromButton() || parseInt(response.likes);
createRateBar(storedData.likes, storedData.dislikes);
}
function setState(storedData) {
storedData.previousState = isVideoDisliked()
? DISLIKED_STATE
: isVideoLiked()
? LIKED_STATE
: NEUTRAL_STATE;
let statsSet = false;
getBrowser().runtime.sendMessage(
{
message: "set_state",
videoId: getVideoId(window.location.href),
state: getState(storedData).current,
likeCount: getLikeCountFromButton() || null,
},
function (response) {
cLog("response from api:");
cLog(JSON.stringify(response));
if (response !== undefined && !("traceId" in response) && !statsSet) {
processResponse(response, storedData);
} else {
}
}
);
}
function setInitialState(storedData) {
setState(storedData);
setTimeout(() => {
sendVideoIds();
}, 1500);
}
export {
isMobile,
isVideoDisliked,
isVideoLiked,
getState,
setState,
setInitialState,
setLikes,
setDislikes,
getLikeCountFromButton,
LIKED_STATE,
DISLIKED_STATE,
NEUTRAL_STATE,
};
+70
View File
@@ -0,0 +1,70 @@
function roundDown(num) {
if (num < 1000) return num;
const int = Math.floor(Math.log10(num) - 2);
const decimal = int + (int % 3 ? 1 : 0);
const value = Math.floor(num / 10 ** decimal);
return value * 10 ** decimal;
}
function numberFormat(numberState) {
let userLocales;
try {
userLocales = new URL(
Array.from(document.querySelectorAll("head > link[rel='search']"))
?.find((n) => n?.getAttribute("href")?.includes("?locale="))
?.getAttribute("href")
)?.searchParams?.get("locale");
} catch {}
const formatter = Intl.NumberFormat(
document.documentElement.lang || userLocales || navigator.language,
{
notation: "compact",
}
);
return formatter.format(roundDown(numberState));
}
function getBrowser() {
if (typeof chrome !== "undefined" && typeof chrome.runtime !== "undefined") {
return chrome;
} else if (
typeof browser !== "undefined" &&
typeof browser.runtime !== "undefined"
) {
return browser;
} else {
console.log("browser is not supported");
return false;
}
}
function getVideoId(url) {
const urlObject = new URL(url);
const pathname = urlObject.pathname;
if (pathname.startsWith("/clip")) {
return document.querySelector("meta[itemprop='videoId']").content;
} else {
return urlObject.searchParams.get("v");
}
}
function isVideoLoaded() {
const videoId = getVideoId(window.location.href);
return (
document.querySelector(`ytd-watch-flexy[video-id='${videoId}']`) !== null ||
// mobile: no video-id attribute
document.querySelector('#player[loading="false"]:not([hidden])') !== null
);
}
function cLog(message, writer) {
message = `[return youtube dislike]: ${message}`;
if (writer) {
writer(message);
} else {
console.log(message);
}
}
export { numberFormat, getBrowser, getVideoId, isVideoLoaded, cLog }