diff options
author | Dmitrii Selivanov <selivano.d@gmail.com> | 2021-12-13 04:55:36 +0300 |
---|---|---|
committer | Dmitrii Selivanov <selivano.d@gmail.com> | 2021-12-13 04:55:36 +0300 |
commit | c36a0ab58d6ffa1d3dd090fe20d787e1b33bb43e (patch) | |
tree | 89ecafb4956145607e8bcc6647cf0fd07e165ae1 /Extensions | |
parent | a1ca95231d68481b7365f53bb6b057904b819107 (diff) |
FF and chrome are now combined, version 2.0 - votes counting, proof of work
Diffstat (limited to 'Extensions')
-rw-r--r-- | Extensions/combined/content-style.css | 26 | ||||
-rw-r--r-- | Extensions/combined/icons/icon128.png | bin | 0 -> 3850 bytes | |||
-rw-r--r-- | Extensions/combined/icons/icon48.png | bin | 0 -> 1632 bytes | |||
-rw-r--r-- | Extensions/combined/init/init.js | 1 | ||||
-rw-r--r-- | Extensions/combined/manifest.json | 27 | ||||
-rw-r--r-- | Extensions/combined/manifestv3.json | 41 | ||||
-rw-r--r-- | Extensions/combined/popup.css | 119 | ||||
-rw-r--r-- | Extensions/combined/popup.html | 48 | ||||
-rw-r--r-- | Extensions/combined/popup.js | 58 | ||||
-rw-r--r-- | Extensions/combined/ryd.background.js | 257 | ||||
-rw-r--r-- | Extensions/combined/ryd.content-script.js | 399 | ||||
-rw-r--r-- | Extensions/combined/ryd.tools.js | 17 |
12 files changed, 993 insertions, 0 deletions
diff --git a/Extensions/combined/content-style.css b/Extensions/combined/content-style.css new file mode 100644 index 0000000..c06b29c --- /dev/null +++ b/Extensions/combined/content-style.css @@ -0,0 +1,26 @@ +#ryd-bar-container { + background: var(--yt-spec-icon-disabled); + border-radius: 2px; +} + +#ryd-bar { + background: var(--yt-spec-text-primary); + border-radius: 2px; + transition: all 0.15s ease-in-out; +} + +.ryd-tooltip { + position: relative; + display: block; + height: 2px; + top: 9px; +} + +.ryd-tooltip-bar-container { + width: 100%; + height: 2px; + position: absolute; + padding-top: 6px; + padding-bottom: 28px; + top: -6px; +} diff --git a/Extensions/combined/icons/icon128.png b/Extensions/combined/icons/icon128.png Binary files differnew file mode 100644 index 0000000..3d06eba --- /dev/null +++ b/Extensions/combined/icons/icon128.png diff --git a/Extensions/combined/icons/icon48.png b/Extensions/combined/icons/icon48.png Binary files differnew file mode 100644 index 0000000..59a90ae --- /dev/null +++ b/Extensions/combined/icons/icon48.png diff --git a/Extensions/combined/init/init.js b/Extensions/combined/init/init.js new file mode 100644 index 0000000..b044234 --- /dev/null +++ b/Extensions/combined/init/init.js @@ -0,0 +1 @@ +RYD.getInstance().init(); diff --git a/Extensions/combined/manifest.json b/Extensions/combined/manifest.json new file mode 100644 index 0000000..214f4bf --- /dev/null +++ b/Extensions/combined/manifest.json @@ -0,0 +1,27 @@ +{ + "name": "Return YouTube Dislike", + "description": "Returns ability to see dislikes", + "version": "2.0.0.0", + "manifest_version": 2, + "background": { + "scripts": ["ryd.background.js"] + }, + "icons": { + "48": "icons/icon48.png", + "128": "icons/icon128.png" + }, + "permissions": ["activeTab", "*://*.youtube.com/*", "storage"], + "browser_action": { + "default_popup": "popup.html" + }, + "content_scripts": [ + { + "matches": ["*://*.youtube.com/*"], + "exclude_matches": ["*://*.music.youtube.com/*"], + "run_at": "document_idle", + "css": ["content-style.css"], + "js": ["ryd.content-script.js", "ryd.tools.js", "/init/init.js"] + } + ] + +} diff --git a/Extensions/combined/manifestv3.json b/Extensions/combined/manifestv3.json new file mode 100644 index 0000000..f8c3375 --- /dev/null +++ b/Extensions/combined/manifestv3.json @@ -0,0 +1,41 @@ +{ + "name": "Return YouTube Dislike", + "description": "Returns ability to see dislikes", + "version": "2.0.0.0", + "manifest_version": 3, + "background": { + "service_worker": "ryd.background.js" + }, + "icons": { + "48": "icons/icon48.png", + "128": "icons/icon128.png" + }, + "host_permissions": ["*://*.youtube.com/*"], + "permissions": [ + "storage" + ], + "action": { + "default_popup": "popup.html" + }, + "content_scripts": [ + { + "matches": [ + "*://youtube.com/*", + "*://www.youtube.com/*", + "*://m.youtube.com/*" + ], + "exclude_matches": ["*://*.music.youtube.com/*"], + "js": ["ryd.content-script.js", "ryd.tools.js", "/init/init.js"], + "css": ["content-style.css"] + } + ], + "externally_connectable": { + "matches": ["*://*.youtube.com/*"] + }, + "web_accessible_resources": [ + { + "resources": ["ryd.script.js"], + "matches": ["*://*.youtube.com/*"] + } + ] +} diff --git a/Extensions/combined/popup.css b/Extensions/combined/popup.css new file mode 100644 index 0000000..3075989 --- /dev/null +++ b/Extensions/combined/popup.css @@ -0,0 +1,119 @@ +/* Variables */ +:root { + --primary: #cc2929; + --accent: #581111; + + --background: #111; + --secondary: #272727; + --tertiary: #333333; + --lightGrey: #999; + --white: #fff; +} + +/* Window Styling */ +html, +body { + background-color: var(--background); + color: var(--white); + min-width: 300px; + padding: 0.5em; + font-family: 'Roboto', Arial, Helvetica, sans-serif; + font-size: 14px; +} + +h1 { + font-size: 26px; +} + +button { + color: var(--white); + background: var(--secondary); + cursor: pointer; + padding: 5px 16px; + border: none; + border-radius: 4px; + font-weight: 500; + box-shadow: 0 2px 4px -1px rgb(0 0 0 / 20%), 0 4px 5px 0 rgb(0 0 0 / 14%), 0 1px 10px 0 rgb(0 0 0 / 12%); + transition: .4s; +} + +button:hover { + background: #444; +} + +#advancedToggle { + margin-top: 1em; + margin-bottom: 2em; +} + +#advancedSettings { + display: none; + border: 2px solid var(--secondary); + border-radius: 0.5rem; + padding: 1rem; +} + +#advancedLegend { + color: var(--tertiary) !important; + /* margin: auto; */ /* Center the label */ + /* padding: .25rem .5rem; */ + /* border-radius: .25rem; */ + /* border: .25rem solid var(--secondary); */ +} + +/* Switches */ +.switch { + position: relative; + display: inline-block; + width: 30px; + height: 17px; + margin-bottom: 1rem; +} + +.switch:last-of-type { + margin-bottom: 0; +} + +.switch input { + display: none; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: var(--secondary); + transition: 0.4s; + border-radius: 34px; +} + +.slider:before { + position: absolute; + content: ""; + height: 13px; + width: 13px; + left: 2px; + bottom: 2px; + background: var(--lightGrey); + transition: 0.4s; + border-radius: 50%; +} + +input:checked + .slider { + background: var(--accent); +} + +input:checked + .slider:before { + transform: translateX(13px); + background: var(--primary); +} + +.switchLabel { + margin-left: 0.5rem; + width: 250px !important; + transform: translateX(35px); + display: inline-block; +} diff --git a/Extensions/combined/popup.html b/Extensions/combined/popup.html new file mode 100644 index 0000000..1ae8707 --- /dev/null +++ b/Extensions/combined/popup.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta content="text/html; charset=utf-8" /> + <title>Return YouTube Dislike</title> + <link rel="stylesheet" href="popup.css" /> + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap" rel="stylesheet"> + </head> + <body> + <center> + <img src="icons/icon48.png" alt="Logo" /> + <h1>Return YouTube Dislike</h1> + <p>by Dmitrii Selivanov & Community</p> + + <button id="link_website">Website</button> + <button id="link_github">GitHub</button> + <button id="link_discord">Discord</button> + + <br><br> + <button id="link_donate">Donate</button> + <br> + + <br> +<!-- <button id="advancedToggle">Show Settings</button>--> + <br> + + </center> + + <fieldset id="advancedSettings"> + <legend id="advancedLegend">Settings</legend> + + <label class="switch"> + <input type="checkbox" id="disable_ratio_bar" /> + <span class="slider" /> + <span class="switchLabel">Lorem ipsum dolor sit amet</span> </label + ><br /> + + <label class="switch"> + <input type="checkbox" id="disable_api_unlisted" /> + <span class="slider" /> + <span class="switchLabel">Lorem ipsum dolor sit amet</span> </label + ><br /> + </fieldset> + </body> + <script src="popup.js"></script> +</html> diff --git a/Extensions/combined/popup.js b/Extensions/combined/popup.js new file mode 100644 index 0000000..002cba2 --- /dev/null +++ b/Extensions/combined/popup.js @@ -0,0 +1,58 @@ +/* Config */ +const config = { + advanced: false, + showAdvancedMessage: "Show Settings", + hideAdvancedMessage: "Hide Settings", + + links: { + website: "https://returnyoutubedislike.com", + github: "https://github.com/Anarios/return-youtube-dislike", + discord: "https://discord.gg/mYnESY4Md5", + donate: 'https://returnyoutubedislike.com/donate' + }, +}; + +/* Links */ +document.getElementById("link_website").addEventListener("click", () => { + chrome.tabs.create({ url: config.links.website }); +}); + +document.getElementById("link_github").addEventListener("click", () => { + chrome.tabs.create({ url: config.links.github }); +}); + +document.getElementById("link_discord").addEventListener("click", () => { + chrome.tabs.create({ url: config.links.discord }); +}); + +document.getElementById("link_donate").addEventListener("click", () => { + chrome.tabs.create({ url: config.links.donate }); +}); + +/* Advanced Toggle */ +/* Not currently used in this version +const advancedToggle = document.getElementById("advancedToggle"); +advancedToggle.addEventListener("click", () => { + const adv = document.getElementById("advancedSettings"); + if (config.advanced) { + adv.style.display = "none"; + advancedToggle.innerHTML = config.showAdvancedMessage; + config.advanced = false; + } else { + adv.style.display = "block"; + advancedToggle.innerHTML = config.hideAdvancedMessage; + config.advanced = true; + } +}); +*/ + +/* popup-script.js +document.querySelector('#login') +.addEventListener('click', function () { + chrome.runtime.sendMessage({ message: 'get_auth_token' }); +}); + +document.querySelector("#log_off").addEventListener("click", function () { + chrome.runtime.sendMessage({ message: "log_off" }); +}); +*/ diff --git a/Extensions/combined/ryd.background.js b/Extensions/combined/ryd.background.js new file mode 100644 index 0000000..5e3e117 --- /dev/null +++ b/Extensions/combined/ryd.background.js @@ -0,0 +1,257 @@ +const apiUrl = "https://returnyoutubedislikeapi.com"; +let api; +if (typeof chrome !== "undefined" && typeof chrome.runtime !== "undefined") + api = chrome; +else if ( + typeof browser !== "undefined" && + typeof browser.runtime !== "undefined" +) + api = browser; + +api.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.message === "get_auth_token") { + // chrome.identity.getAuthToken({ interactive: true }, function (token) { + // console.log(token); + // chrome.identity.getProfileUserInfo(function (userInfo) { + // console.log(JSON.stringify(userInfo)); + // }); + // }); + } else if (request.message === "log_off") { + // chrome.identity.clearAllCachedAuthTokens(() => console.log("logged off")); + } else if (request.message == "set_state") { + // chrome.identity.getAuthToken({ interactive: true }, function (token) { + let token = ""; + fetch(`${apiUrl}/votes?videoId=${request.videoId}&likeCount=${request.likeCount || ''}`, { + method: "GET", + headers: { + Accept: "application/json", + }, + }) + .then((response) => response.json()) + .then((response) => { + sendResponse(response); + }) + .catch(); + return true; + } else if (request.message == "send_links") { + toSend = toSend.concat(request.videoIds.filter((x) => !sentIds.has(x))); + if (toSend.length >= 20) { + fetch(`${apiUrl}/votes`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(toSend), + }); + for (const toSendUrl of toSend) { + sentIds.add(toSendUrl); + } + toSend = []; + } + } else if (request.message == "fetch_from_youtube") { + fetch(`https://www.youtube.com/watch?v=${request.videoId}`, { + method: "GET", + }) + .then((response) => response.text()) + .then((text) => { + let result = getDislikesFromYoutubeResponse(text); + sendUserSubmittedStatisticsToApi({ + ...result, + videoId: request.videoId, + }); + sendResponse(result); + }); + return true; + } else if (request.message == "register") { + register(); + return true; + } else if (request.message == "send_vote") { + sendVote(request.videoId, request.vote); + return true; + } +}); + +async function sendVote(videoId, vote) { + api.storage.sync.get(null, async (storageResult) => { + if (!storageResult.userId || !storageResult.registrationConfirmed) { + register().then(() => sendVote(videoId, vote)); + } + fetch(`${apiUrl}/interact/vote`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + userId: storageResult.userId, + videoId, + value: vote, + }), + }) + .then((response) => response.json()) + .then((response) => { + solvePuzzle(response).then((solvedPuzzle) => { + fetch(`${apiUrl}/interact/confirmVote`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + ...solvedPuzzle, + userId: storageResult.userId, + videoId, + }), + }); + }); + }); + }); +} + +function register() { + let userId = generateUserID(); + api.storage.sync.set({ userId }); + return fetch(`${apiUrl}/puzzle/registration?userId=${userId}`, { + method: "GET", + headers: { + Accept: "application/json", + }, + }) + .then((response) => response.json()) + .then((response) => { + return solvePuzzle(response).then((solvedPuzzle) => { + return fetch(`${apiUrl}/puzzle/registration?userId=${userId}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(solvedPuzzle), + }).then((response) => + response.json().then((result) => { + if (result === true) { + return api.storage.sync.set({ registrationConfirmed: true }); + } + }) + ); + }); + }) + .catch(); +} + +api.storage.sync.get(null, (res) => { + if (!res.userId || !res.registrationConfirmed) { + register(); + } +}); + +const sentIds = new Set(); +let toSend = []; + +function getDislikesFromYoutubeResponse(htmlResponse) { + let start = + htmlResponse.indexOf('"videoDetails":') + '"videoDetails":'.length; + let end = + htmlResponse.indexOf('"isLiveContent":false}', start) + + '"isLiveContent":false}'.length; + if (end < start) { + end = + htmlResponse.indexOf('"isLiveContent":true}', start) + + '"isLiveContent":true}'.length; + } + let jsonStr = htmlResponse.substring(start, end); + let jsonResult = JSON.parse(jsonStr); + let rating = jsonResult.averageRating; + + start = htmlResponse.indexOf('"topLevelButtons":[', end); + start = + htmlResponse.indexOf('"accessibilityData":', start) + + '"accessibilityData":'.length; + end = htmlResponse.indexOf("}", start); + let likes = +htmlResponse.substring(start, end).replace(/\D/g, ""); + let dislikes = (likes * (5 - rating)) / (rating - 1); + let result = { + likes, + dislikes: Math.round(dislikes), + rating, + viewCount: +jsonResult.viewCount, + }; + return result; +} + +function sendUserSubmittedStatisticsToApi(statistics) { + fetch(`${apiUrl}/votes/user-submitted`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(statistics), + }); +} + +function countLeadingZeroes(uInt8View, limit) { + let zeroes = 0; + let value = 0; + for (let i = 0; i < uInt8View.length; i++) { + value = uInt8View[i]; + if (value === 0) { + zeroes += 8; + } else { + let count = 1; + if (value >>> 4 === 0) { + count += 4; + value <<= 4; + } + if (value >>> 6 === 0) { + count += 2; + value <<= 2; + } + zeroes += count - (value >>> 7); + break; + } + if (zeroes >= limit) { + break; + } + } + return zeroes; +} + +async function solvePuzzle(puzzle) { + let challenge = Uint8Array.from(atob(puzzle.challenge), (c) => + c.charCodeAt(0) + ); + let buffer = new ArrayBuffer(20); + let uInt8View = new Uint8Array(buffer); + let uInt32View = new Uint32Array(buffer); + let maxCount = Math.pow(2, puzzle.difficulty) * 5; + for (let i = 4; i < 20; i++) { + uInt8View[i] = challenge[i - 4]; + } + + for (let i = 0; i < maxCount; i++) { + uInt32View[0] = i; + let hash = await crypto.subtle.digest("SHA-512", buffer); + let hashUint8 = new Uint8Array(hash); + if (countLeadingZeroes(hashUint8) >= puzzle.difficulty) { + return { + solution: btoa(String.fromCharCode.apply(null, uInt8View.slice(0, 4))), + }; + } + } +} + +function generateUserID(length = 36) { + const charset = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let result = ""; + if (crypto && crypto.getRandomValues) { + const values = new Uint32Array(length); + crypto.getRandomValues(values); + for (let i = 0; i < length; i++) { + result += charset[values[i] % charset.length]; + } + return result; + } else { + for (let i = 0; i < length; i++) { + result += charset[Math.floor(Math.random() * charset.length)]; + } + return result; + } +} diff --git a/Extensions/combined/ryd.content-script.js b/Extensions/combined/ryd.content-script.js new file mode 100644 index 0000000..dcfa7c6 --- /dev/null +++ b/Extensions/combined/ryd.content-script.js @@ -0,0 +1,399 @@ +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, + }; + + function cLog(message, writer) { + message = `[return youtube dislike]: ${message}`; + if (writer) { + writer(message); + } else { + console.log(message); + } + } + + function isMobile() { + return location.hostname == "m.youtube.com"; + } + + 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 isVideoNotLiked() { + return getLikeButton().classList.contains("style-text"); + } + + function isVideoNotDisliked() { + return getDislikeButton().classList.contains("style-text"); + } + + 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: "fetch_from_youtube", + videoId: getVideoId(window.location.href), + }, + function (response) { + if (response !== undefined) { + cLog("response from youtube:"); + cLog(JSON.stringify(response)); + try { + if ( + "likes" in response && + "dislikes" in response && + response.dislikes !== null + ) { + processResponse(response); + statsSet = true; + } + } catch (e) {} + } + } + ); + + 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 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; + } + } + } + + 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()} / ${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()} / ${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; +}; diff --git a/Extensions/combined/ryd.tools.js b/Extensions/combined/ryd.tools.js new file mode 100644 index 0000000..b6d9817 --- /dev/null +++ b/Extensions/combined/ryd.tools.js @@ -0,0 +1,17 @@ +RYDTools = {}; + +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; + } +}; + + |