From 873d751ec4eb6040cd36d6a3e946a5920f5f4537 Mon Sep 17 00:00:00 2001 From: elliotwaite <1767836+elliotwaite@users.noreply.github.com> Date: Wed, 9 Oct 2024 19:33:33 +0800 Subject: [PATCH] Improve caching performance (version 1.8.17) --- extension/background.js | 93 ++++++---- extension/content-script.js | 315 +++++++++++++++++----------------- extension/manifest.json | 2 +- manifest-v2/background.js | 93 ++++++---- manifest-v2/content-script.js | 315 +++++++++++++++++----------------- manifest-v2/manifest.json | 2 +- 6 files changed, 436 insertions(+), 384 deletions(-) diff --git a/extension/background.js b/extension/background.js index ccf7a72..ef6a55f 100644 --- a/extension/background.js +++ b/extension/background.js @@ -4,6 +4,25 @@ let cache = {} let cacheTimes = [] let cacheDuration = 600000 // Default is 10 mins. +let videoApiRequestCallbacks = {} + +function removeExpiredCacheData() { + const now = Date.now() + let numRemoved = 0 + + for (const [fetchTime, videoId] of cacheTimes) { + if (now - fetchTime > cacheDuration) { + delete cache[videoId] + numRemoved++ + } else { + break + } + } + + if (numRemoved > 0) { + cacheTimes = cacheTimes.slice(numRemoved) + } +} chrome.runtime.onInstalled.addListener(() => { chrome.storage.sync.get({ cacheDuration: 600000 }, function (settings) { @@ -15,54 +34,58 @@ chrome.runtime.onInstalled.addListener(() => { chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { switch (message.query) { - case 'videoApiRequest': - // Remove expired cache data. - const now = Date.now() - let numRemoved = 0 - for (const [fetchTime, videoId] of cacheTimes) { - if (now - fetchTime > cacheDuration) { - delete cache[videoId] - numRemoved++ - } else { - break - } - } - if (numRemoved > 0) { - cacheTimes = cacheTimes.slice(numRemoved) - } + case "videoApiRequest": + removeExpiredCacheData() + // If the data is in the cache, return it. if (message.videoId in cache) { - // Use cached data if it exists. + // Return the cached data if it exists. sendResponse(cache[message.videoId]) return } - // Otherwise, fetch new data and cache it. - fetch( - 'https://returnyoutubedislikeapi.com/Votes?videoId=' + message.videoId, - ).then((response) => { - if (!response.ok) { - sendResponse(null) - } else { - response.json().then((data) => { - const likesData = { - likes: data.likes, - dislikes: data.dislikes, - } - if (!(message.videoId in cache)) { - cache[message.videoId] = likesData + if (message.videoId in videoApiRequestCallbacks) { + // If a request for the same video ID is already in progress, add the + // current `sendResponse` function to the `videoApiRequestCallbacks` + // array for this video ID. + videoApiRequestCallbacks[message.videoId].push(sendResponse) + } else { + // Otherwise, insert a new callbacks array for this video ID, then + // start a new request to fetch the likes/dislikes data. + videoApiRequestCallbacks[message.videoId] = [sendResponse] + + fetch( + "https://returnyoutubedislikeapi.com/Votes?videoId=" + + message.videoId, + ) + .then( + (response) => + response.ok + ? response.json().then((data) => ({ + likes: data.likes, + dislikes: data.dislikes, + })) + : null, // If the response failed, we return `null`. + ) + .then((data) => { + if (data !== null) { + cache[message.videoId] = data cacheTimes.push([Date.now(), message.videoId]) } - sendResponse(likesData) + + for (const callback of videoApiRequestCallbacks[message.videoId]) { + callback(data) + } + + delete videoApiRequestCallbacks[message.videoId] }) - } - }) + } // Returning `true` signals to the browser that we will send our // response asynchronously using `sendResponse()`. return true - case 'insertCss': + case "insertCss": chrome.scripting.insertCSS({ target: { tabId: sender.tab.id, @@ -71,7 +94,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { }) break - case 'updateSettings': + case "updateSettings": cacheDuration = message.cacheDuration break } diff --git a/extension/content-script.js b/extension/content-script.js index 62017ba..782c327 100755 --- a/extension/content-script.js +++ b/extension/content-script.js @@ -22,8 +22,8 @@ const ADD_RATING_BAR_TO_SHORTS = false // `isDarkTheme` will be true if the appearance setting is in dark theme mode. const isDarkTheme = getComputedStyle(document.body).getPropertyValue( - '--yt-spec-general-background-a', - ) === ' #181818' + "--yt-spec-general-background-a", + ) === " #181818" // We use these JQuery selectors to find new thumbnails on the page. We need to // check all combinations of these modes and types: @@ -54,63 +54,63 @@ const isDarkTheme = // figuring out the video ID associated with that thumbnail. const THUMBNAIL_SELECTORS = [] THUMBNAIL_SELECTORS[THEME_MODERN] = - '' + + "" + // All types except the video wall. The URL is on the selected a link. // The mini-player thumbnail will not have an href attribute, which is why // we require that it exists. - 'a#thumbnail[href]' + "a#thumbnail[href]" THUMBNAIL_SELECTORS[THEME_CLASSIC] = - '' + + "" + // Search results videos. (url on parent) // Creator's videos. (url on parent) // Playlist page small thumbnails. (url on parent) // Sidebar suggested playlist. (url on grandparent) // Playing playlist small thumbnails. (url on parent) - '.video-thumb' + - ':not(.yt-thumb-20)' + - ':not(.yt-thumb-27)' + - ':not(.yt-thumb-32)' + - ':not(.yt-thumb-36)' + - ':not(.yt-thumb-48)' + - ':not(.yt-thumb-64), ' + + ".video-thumb" + + ":not(.yt-thumb-20)" + + ":not(.yt-thumb-27)" + + ":not(.yt-thumb-32)" + + ":not(.yt-thumb-36)" + + ":not(.yt-thumb-48)" + + ":not(.yt-thumb-64), " + // (For search results, if a channel is in the results, it's thumbnail will // be caught by this selector, but won't have an matchable video URL. // Since this does not cause an error, it should be fine to ignore it.) // Sidebar suggested video. (url on first child) - '.thumb-wrapper, ' + + ".thumb-wrapper, " + // Playlist page big thumbnail. (url on second child) - '.pl-header-thumb' + ".pl-header-thumb" THUMBNAIL_SELECTORS[THEME_GAMING] = - '' + + "" + // Gaming all types except video wall. URL is on the great-grandparent, // except for search result playlists it is on the grandparent. - 'ytg-thumbnail' + - ':not([avatar])' + - ':not(.avatar)' + - ':not(.ytg-user-avatar)' + - ':not(.ytg-box-art)' + - ':not(.ytg-compact-gaming-event-renderer)' + - ':not(.ytg-playlist-header-renderer)' + "ytg-thumbnail" + + ":not([avatar])" + + ":not(.avatar)" + + ":not(.ytg-user-avatar)" + + ":not(.ytg-box-art)" + + ":not(.ytg-compact-gaming-event-renderer)" + + ":not(.ytg-playlist-header-renderer)" THUMBNAIL_SELECTORS[THEME_MOBILE] = - '' + - 'a.media-item-thumbnail-container, ' + - 'a.compact-media-item-image, ' + - 'a.video-card-image' + "" + + "a.media-item-thumbnail-container, " + + "a.compact-media-item-image, " + + "a.video-card-image" // All themes use this selector for video wall videos. -const THUMBNAIL_SELECTOR_VIDEOWALL = '' + 'a.ytp-videowall-still' +const THUMBNAIL_SELECTOR_VIDEOWALL = "" + "a.ytp-videowall-still" // The default user settings. `userSettings` is replaced with the stored user's // settings once they are loaded. const DEFAULT_USER_SETTINGS = { - barPosition: 'bottom', - barColor: 'blue-gray', - barLikesColor: '#3095e3', - barDislikesColor: '#cfcfcf', + barPosition: "bottom", + barColor: "blue-gray", + barLikesColor: "#3095e3", + barDislikesColor: "#cfcfcf", barColorsSeparator: false, barHeight: 4, barOpacity: 100, @@ -125,7 +125,7 @@ let userSettings = DEFAULT_USER_SETTINGS function ratingToPercentage(rating) { // When the rating is 100%, we display "100%" instead of "100.0%". if (rating === 1) { - return (100).toLocaleString() + '%' + return (100).toLocaleString() + "%" } // We use `floor` instead of `round` to ensure that any rating lower than 100% @@ -134,20 +134,20 @@ function ratingToPercentage(rating) { (Math.floor(rating * 1000) / 10).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2, - }) + '%' + }) + "%" ) } function getToolTipText(videoData) { return ( videoData.likes.toLocaleString() + - ' / ' + + " / " + videoData.dislikes.toLocaleString() + - ' ' + + " " + ratingToPercentage(videoData.rating) + - ' ' + + " " + videoData.total.toLocaleString() + - ' total' + " total" ) } @@ -158,7 +158,7 @@ function exponentialRatingWidthPercentage(rating) { function getRatingBarHtml(videoData) { let ratingElement if (videoData.rating == null) { - ratingElement = '' + ratingElement = "" } else { let likesWidthPercentage if (userSettings.useExponentialScaling) { @@ -167,27 +167,27 @@ function getRatingBarHtml(videoData) { likesWidthPercentage = 100 * videoData.rating } ratingElement = - '' + + "" + '' + - '' + - '' + "" + + "" } return ( - '' + + : "") + + ">" + ratingElement + (userSettings.barTooltip - ? '' + + ? "" + getToolTipText(videoData) + - '' - : '') + - '' + "" + : "") + + "" ) } @@ -200,7 +200,7 @@ function getRatingPercentageHtml(videoData) { return ( '' + ratingToPercentage(videoData.rating) + - '' + "" ) } @@ -209,14 +209,14 @@ function getRatingPercentageHtml(videoData) { if (!isDarkTheme) { g = Math.min(g, 255) * 0.85 } - const rgb = 'rgb(' + r + ',' + g + ',0)' + const rgb = "rgb(" + r + "," + g + ",0)" return ( '' + ratingToPercentage(videoData.rating) + - '' + "" ) } @@ -248,42 +248,42 @@ function getThumbnailsAndIds(thumbnails) { let url if (curTheme === THEME_MODERN) { // The URL should be on the current element. - url = $(thumbnail).attr('href') + url = $(thumbnail).attr("href") } else if (curTheme === THEME_CLASSIC) { // Check the current element, then the parent, then the grandparent, // then the first child, then the second child. url = - $(thumbnail).attr('href') || - $(thumbnail).parent().attr('href') || - $(thumbnail).parent().parent().attr('href') || - $(thumbnail).children(':first').attr('href') || - $(thumbnail).children(':first').next().attr('href') + $(thumbnail).attr("href") || + $(thumbnail).parent().attr("href") || + $(thumbnail).parent().parent().attr("href") || + $(thumbnail).children(":first").attr("href") || + $(thumbnail).children(":first").next().attr("href") } else if (curTheme === THEME_GAMING) { // Check the current element, then the grandparent. url = - $(thumbnail).attr('href') || - $(thumbnail).parent().parent().attr('href') || - $(thumbnail).parent().parent().parent().attr('href') + $(thumbnail).attr("href") || + $(thumbnail).parent().parent().attr("href") || + $(thumbnail).parent().parent().parent().attr("href") // Unless the element is a video wall thumbnail, change the thumbnail // element to the parent element, so that it will show over the thumbnail // preview video that plays when you hover over the thumbnail. - if (!$(thumbnail).is('a')) { + if (!$(thumbnail).is("a")) { thumbnail = $(thumbnail).parent() } } else if (curTheme === THEME_MOBILE) { // The URL should be on the current element. - url = $(thumbnail).attr('href') + url = $(thumbnail).attr("href") // On mobile gaming (m.youtube.com/gaming), the thumbnail should be // reassigned to the child container. - const firstChild = $(thumbnail).children(':first')[0] - if ($(firstChild).is('.video-thumbnail-container-compact')) { + const firstChild = $(thumbnail).children(":first")[0] + if ($(firstChild).is(".video-thumbnail-container-compact")) { thumbnail = firstChild } } else { // The theme may not be set if only video-wall thumbnails were found. - url = $(thumbnail).attr('href') + url = $(thumbnail).attr("href") } if (!url) { @@ -291,7 +291,7 @@ function getThumbnailsAndIds(thumbnails) { } // Check if this thumbnail was previously found. - const previousUrl = $(thumbnail).attr('data-ytrb-url') + const previousUrl = $(thumbnail).attr("data-ytrb-url") if (previousUrl) { // Check if this thumbnail is for the same URL as previously. if (previousUrl === url) { @@ -301,7 +301,7 @@ function getThumbnailsAndIds(thumbnails) { // On mobile, we have to check to make sure the bar is still present, // because thumbnails can sometimes be recreated (such as when they // are scrolled out of view) which causes the bar to be removed. - if ($(thumbnail).children().last().is('ytrb-bar')) { + if ($(thumbnail).children().last().is("ytrb-bar")) { return true } } else { @@ -309,15 +309,15 @@ function getThumbnailsAndIds(thumbnails) { } } else { // If not, remove the old rating bar and retries count. - $(thumbnail).children('ytrb-bar').remove() - $(thumbnail).removeAttr('data-ytrb-retries') + $(thumbnail).children("ytrb-bar").remove() + $(thumbnail).removeAttr("data-ytrb-retries") } } // Save the URL that corresponds with this thumbnail in a separate // attribute so that we can check if the URL has changed in the future, in // which case we'll have to update the rating bar. - $(thumbnail).attr('data-ytrb-url', url) + $(thumbnail).attr("data-ytrb-url", url) // Extract the video ID from the URL. const match = @@ -349,11 +349,11 @@ function retryProcessingThumbnailInTheFuture(thumbnail) { setTimeout(() => { isPendingApiRetry = false thumbnailsToRetry.forEach((thumbnail) => { - const retriesAttr = $(thumbnail).attr('data-ytrb-retries') + const retriesAttr = $(thumbnail).attr("data-ytrb-retries") const retriesNum = retriesAttr ? Number.parseInt(retriesAttr, 10) : 0 if (retriesNum < MAX_RETRIES_PER_THUMBNAIL) { - $(thumbnail).attr('data-ytrb-retries', retriesNum + 1) - $(thumbnail).removeAttr('data-ytrb-url') + $(thumbnail).attr("data-ytrb-retries", retriesNum + 1) + $(thumbnail).removeAttr("data-ytrb-url") hasUnseenDomMutations = true } }) @@ -370,7 +370,7 @@ function retryProcessingThumbnailInTheFuture(thumbnail) { function getVideoData(thumbnail, videoId) { return new Promise((resolve) => { chrome.runtime.sendMessage( - { query: 'videoApiRequest', videoId: videoId }, + { query: "videoApiRequest", videoId: videoId }, (likesData) => { if (likesData === null) { // The API request failed, which is usually due to rate limiting, so @@ -395,32 +395,32 @@ function addRatingPercentage(thumbnail, videoData) { let metadataLine if (curTheme === THEME_MOBILE) { metadataLine = $(thumbnail) - .closest('ytm-media-item') - .find('ytm-badge-and-byline-renderer') + .closest("ytm-media-item") + .find("ytm-badge-and-byline-renderer") .last() } else { metadataLine = $(thumbnail) .closest( - '.ytd-rich-item-renderer, ' + // Home page. - '.ytd-grid-renderer, ' + // Trending and subscriptions page. - '.ytd-expanded-shelf-contents-renderer, ' + // Subscriptions page. - '.yt-horizontal-list-renderer, ' + // Channel page. - '.ytd-item-section-renderer, ' + // History page. - '.ytd-horizontal-card-list-renderer, ' + // Gaming page. - '.ytd-playlist-video-list-renderer', // Playlist page. + ".ytd-rich-item-renderer, " + // Home page. + ".ytd-grid-renderer, " + // Trending and subscriptions page. + ".ytd-expanded-shelf-contents-renderer, " + // Subscriptions page. + ".yt-horizontal-list-renderer, " + // Channel page. + ".ytd-item-section-renderer, " + // History page. + ".ytd-horizontal-card-list-renderer, " + // Gaming page. + ".ytd-playlist-video-list-renderer", // Playlist page. ) - .find('#metadata-line') + .find("#metadata-line") .last() } if (metadataLine) { // Remove any previously added percentages. - for (const oldPercentage of metadataLine.children('.ytrb-percentage')) { + for (const oldPercentage of metadataLine.children(".ytrb-percentage")) { oldPercentage.remove() } if (curTheme === THEME_MOBILE) { for (const oldPercentage of metadataLine.children( - '.ytrb-percentage-separator', + ".ytrb-percentage-separator", )) { oldPercentage.remove() } @@ -437,7 +437,7 @@ function addRatingPercentage(thumbnail, videoData) { !(videoData.likes === 0 && videoData.dislikes >= 10) ) { const ratingPercentageHtml = getRatingPercentageHtml(videoData) - const lastSpan = metadataLine.children('span').last() + const lastSpan = metadataLine.children("span").last() if (lastSpan.length) { lastSpan.after(ratingPercentageHtml) if (curTheme === THEME_MOBILE) { @@ -503,37 +503,37 @@ function processNewThumbnails() { // ) // const NUMBERING_SYSTEM_DIGIT_STRINGS = [ - '٠١٢٣٤٥٦٧٨٩', - '۰۱۲۳۴۵۶۷۸۹', - '᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙', - '০১২৩৪৫৬৭৮৯', - '०१२३४५६७८९', - '0123456789', - '૦૧૨૩૪૫૬૭૮૯', - '੦੧੨੩੪੫੬੭੮੯', - '〇一二三四五六七八九', - '០១២៣៤៥៦៧៨៩', - '೦೧೨೩೪೫೬೭೮೯', - '໐໑໒໓໔໕໖໗໘໙', - '0123456789', - '᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏', - '൦൧൨൩൪൫൬൭൮൯', - '᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙', - '၀၁၂၃၄၅၆၇၈၉', - '୦୧୨୩୪୫୬୭୮୯', - '௦௧௨௩௪௫௬௭௮௯', - '౦౧౨౩౪౫౬౭౮౯', - '๐๑๒๓๔๕๖๗๘๙', - '༠༡༢༣༤༥༦༧༨༩', + "٠١٢٣٤٥٦٧٨٩", + "۰۱۲۳۴۵۶۷۸۹", + "᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙", + "০১২৩৪৫৬৭৮৯", + "०१२३४५६७८९", + "0123456789", + "૦૧૨૩૪૫૬૭૮૯", + "੦੧੨੩੪੫੬੭੮੯", + "〇一二三四五六七八九", + "០១២៣៤៥៦៧៨៩", + "೦೧೨೩೪೫೬೭೮೯", + "໐໑໒໓໔໕໖໗໘໙", + "0123456789", + "᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏", + "൦൧൨൩൪൫൬൭൮൯", + "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙", + "၀၁၂၃၄၅၆၇၈၉", + "୦୧୨୩୪୫୬୭୮୯", + "௦௧௨௩௪௫௬௭௮௯", + "౦౧౨౩౪౫౬౭౮౯", + "๐๑๒๓๔๕๖๗๘๙", + "༠༡༢༣༤༥༦༧༨༩", ] function parseInternationalInt(string) { // Parses an internationalized integer string (e.g. "1,234" or "١٬٢٣٤") into a // JavaScript integer. - string = string.replace(/[\s,.]/g, '') + string = string.replace(/[\s,.]/g, "") if (/[^0-9]/.test(string)) { - let newString = '' + let newString = "" for (const char of string) { for (const digitString of NUMBERING_SYSTEM_DIGIT_STRINGS) { const index = digitString.indexOf(char) @@ -569,13 +569,13 @@ function getVideoDataFromTooltipText(text) { } function updateVideoRatingBar() { - $('.ryd-tooltip').each(function (_, rydTooltip) { - const tooltip = $(rydTooltip).find('#tooltip') + $(".ryd-tooltip").each(function (_, rydTooltip) { + const tooltip = $(rydTooltip).find("#tooltip") const curText = $(tooltip).text() // We add a zero-width space to the end of any processed tooltip text to // prevent it from being reprocessed. - if (!curText.endsWith('\u200b')) { + if (!curText.endsWith("\u200b")) { const videoData = getVideoDataFromTooltipText(curText) if (userSettings.barTooltip && videoData) { @@ -589,8 +589,11 @@ function updateVideoRatingBar() { } if (userSettings.useExponentialScaling && videoData && videoData.rating) { - $(rydTooltip).find('#ryd-bar')[0].style.width = - exponentialRatingWidthPercentage(videoData.rating) + '%' + const rydBar = $(rydTooltip).find("#ryd-bar")[0] + if (rydBar) { + rydBar.style.width = + exponentialRatingWidthPercentage(videoData.rating) + "%" + } } } }) @@ -640,99 +643,99 @@ chrome.storage.sync.get(DEFAULT_USER_SETTINGS, function (storedSettings) { const cssFiles = [] if (userSettings.barHeight !== 0) { - cssFiles.push('css/bar.css') + cssFiles.push("css/bar.css") - if (userSettings.barPosition === 'top') { - cssFiles.push('css/bar-top.css') + if (userSettings.barPosition === "top") { + cssFiles.push("css/bar-top.css") } else { - cssFiles.push('css/bar-bottom.css') + cssFiles.push("css/bar-bottom.css") } if (userSettings.barSeparator) { - if (userSettings.barPosition === 'top') { - cssFiles.push('css/bar-top-separator.css') + if (userSettings.barPosition === "top") { + cssFiles.push("css/bar-top-separator.css") } else { - cssFiles.push('css/bar-bottom-separator.css') + cssFiles.push("css/bar-bottom-separator.css") } } if (userSettings.barTooltip) { - cssFiles.push('css/bar-tooltip.css') - if (userSettings.barPosition === 'top') { - cssFiles.push('css/bar-top-tooltip.css') + cssFiles.push("css/bar-tooltip.css") + if (userSettings.barPosition === "top") { + cssFiles.push("css/bar-top-tooltip.css") } else { - cssFiles.push('css/bar-bottom-tooltip.css') + cssFiles.push("css/bar-bottom-tooltip.css") } } if (userSettings.useOnVideoPage) { - cssFiles.push('css/bar-video-page.css') + cssFiles.push("css/bar-video-page.css") } } if (cssFiles.length > 0) { chrome.runtime.sendMessage({ - query: 'insertCss', + query: "insertCss", files: cssFiles, }) } document.documentElement.style.setProperty( - '--ytrb-bar-height', - userSettings.barHeight + 'px', + "--ytrb-bar-height", + userSettings.barHeight + "px", ) document.documentElement.style.setProperty( - '--ytrb-bar-opacity', + "--ytrb-bar-opacity", userSettings.barOpacity / 100, ) - if (userSettings.barColor === 'blue-gray') { + if (userSettings.barColor === "blue-gray") { document.documentElement.style.setProperty( - '--ytrb-bar-likes-color', - '#3095e3', + "--ytrb-bar-likes-color", + "#3095e3", ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-color', - '#cfcfcf', + "--ytrb-bar-dislikes-color", + "#cfcfcf", ) document.documentElement.style.setProperty( - '--ytrb-bar-likes-shadow', - 'none', + "--ytrb-bar-likes-shadow", + "none", ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-shadow', - 'none', + "--ytrb-bar-dislikes-shadow", + "none", ) - } else if (userSettings.barColor === 'green-red') { - document.documentElement.style.setProperty('--ytrb-bar-likes-color', '#060') + } else if (userSettings.barColor === "green-red") { + document.documentElement.style.setProperty("--ytrb-bar-likes-color", "#060") document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-color', - '#c00', + "--ytrb-bar-dislikes-color", + "#c00", ) document.documentElement.style.setProperty( - '--ytrb-bar-likes-shadow', - '1px 0 #fff', + "--ytrb-bar-likes-shadow", + "1px 0 #fff", ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-shadow', - 'inset 1px 0 #fff', + "--ytrb-bar-dislikes-shadow", + "inset 1px 0 #fff", ) - } else if (userSettings.barColor === 'custom-colors') { + } else if (userSettings.barColor === "custom-colors") { document.documentElement.style.setProperty( - '--ytrb-bar-likes-color', + "--ytrb-bar-likes-color", userSettings.barLikesColor, ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-color', + "--ytrb-bar-dislikes-color", userSettings.barDislikesColor, ) document.documentElement.style.setProperty( - '--ytrb-bar-likes-shadow', - userSettings.barColorsSeparator ? '1px 0 #fff' : 'none', + "--ytrb-bar-likes-shadow", + userSettings.barColorsSeparator ? "1px 0 #fff" : "none", ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-shadow', - userSettings.barColorsSeparator ? 'inset 1px 0 #fff' : 'none', + "--ytrb-bar-dislikes-shadow", + userSettings.barColorsSeparator ? "inset 1px 0 #fff" : "none", ) } diff --git a/extension/manifest.json b/extension/manifest.json index e6906d3..2242310 100755 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Thumbnail Rating Bar for YouTube™", - "version": "1.8.16", + "version": "1.8.17", "description": "Displays a rating bar (likes/dislikes) on the bottom of every YouTube™ video thumbnail.", "author": "Elliot Waite", "icons": { diff --git a/manifest-v2/background.js b/manifest-v2/background.js index 24598c9..0761b4b 100644 --- a/manifest-v2/background.js +++ b/manifest-v2/background.js @@ -4,6 +4,25 @@ let cache = {} let cacheTimes = [] let cacheDuration = 600000 // Default is 10 mins. +let videoApiRequestCallbacks = {} + +function removeExpiredCacheData() { + const now = Date.now() + let numRemoved = 0 + + for (const [fetchTime, videoId] of cacheTimes) { + if (now - fetchTime > cacheDuration) { + delete cache[videoId] + numRemoved++ + } else { + break + } + } + + if (numRemoved > 0) { + cacheTimes = cacheTimes.slice(numRemoved) + } +} chrome.storage.local.get({ cacheDuration: 600000 }, function (settings) { if (settings && settings.cacheDuration !== undefined) { @@ -13,60 +32,64 @@ chrome.storage.local.get({ cacheDuration: 600000 }, function (settings) { chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { switch (message.query) { - case 'videoApiRequest': - // Remove expired cache data. - const now = Date.now() - let numRemoved = 0 - for (const [fetchTime, videoId] of cacheTimes) { - if (now - fetchTime > cacheDuration) { - delete cache[videoId] - numRemoved++ - } else { - break - } - } - if (numRemoved > 0) { - cacheTimes = cacheTimes.slice(numRemoved) - } + case "videoApiRequest": + removeExpiredCacheData() + // If the data is in the cache, return it. if (message.videoId in cache) { - // Use cached data if it exists. + // Return the cached data if it exists. sendResponse(cache[message.videoId]) return } - // Otherwise, fetch new data and cache it. - fetch( - 'https://returnyoutubedislikeapi.com/Votes?videoId=' + message.videoId, - ).then((response) => { - if (!response.ok) { - sendResponse(null) - } else { - response.json().then((data) => { - const likesData = { - likes: data.likes, - dislikes: data.dislikes, - } - if (!(message.videoId in cache)) { - cache[message.videoId] = likesData + if (message.videoId in videoApiRequestCallbacks) { + // If a request for the same video ID is already in progress, add the + // current `sendResponse` function to the `videoApiRequestCallbacks` + // array for this video ID. + videoApiRequestCallbacks[message.videoId].push(sendResponse) + } else { + // Otherwise, insert a new callbacks array for this video ID, then + // start a new request to fetch the likes/dislikes data. + videoApiRequestCallbacks[message.videoId] = [sendResponse] + + fetch( + "https://returnyoutubedislikeapi.com/Votes?videoId=" + + message.videoId, + ) + .then( + (response) => + response.ok + ? response.json().then((data) => ({ + likes: data.likes, + dislikes: data.dislikes, + })) + : null, // If the response failed, we return `null`. + ) + .then((data) => { + if (data !== null) { + cache[message.videoId] = data cacheTimes.push([Date.now(), message.videoId]) } - sendResponse(likesData) + + for (const callback of videoApiRequestCallbacks[message.videoId]) { + callback(data) + } + + delete videoApiRequestCallbacks[message.videoId] }) - } - }) + } // Returning `true` signals to the browser that we will send our // response asynchronously using `sendResponse()`. return true - case 'insertCss': + case "insertCss": for (const file of message.files) { chrome.tabs.insertCSS(sender.tab.id, { file }) } break - case 'updateSettings': + case "updateSettings": cacheDuration = message.cacheDuration break } diff --git a/manifest-v2/content-script.js b/manifest-v2/content-script.js index a2791ba..1333402 100755 --- a/manifest-v2/content-script.js +++ b/manifest-v2/content-script.js @@ -22,8 +22,8 @@ const ADD_RATING_BAR_TO_SHORTS = false // `isDarkTheme` will be true if the appearance setting is in dark theme mode. const isDarkTheme = getComputedStyle(document.body).getPropertyValue( - '--yt-spec-general-background-a', - ) === ' #181818' + "--yt-spec-general-background-a", + ) === " #181818" // We use these JQuery selectors to find new thumbnails on the page. We need to // check all combinations of these modes and types: @@ -54,63 +54,63 @@ const isDarkTheme = // figuring out the video ID associated with that thumbnail. const THUMBNAIL_SELECTORS = [] THUMBNAIL_SELECTORS[THEME_MODERN] = - '' + + "" + // All types except the video wall. The URL is on the selected a link. // The mini-player thumbnail will not have an href attribute, which is why // we require that it exists. - 'a#thumbnail[href]' + "a#thumbnail[href]" THUMBNAIL_SELECTORS[THEME_CLASSIC] = - '' + + "" + // Search results videos. (url on parent) // Creator's videos. (url on parent) // Playlist page small thumbnails. (url on parent) // Sidebar suggested playlist. (url on grandparent) // Playing playlist small thumbnails. (url on parent) - '.video-thumb' + - ':not(.yt-thumb-20)' + - ':not(.yt-thumb-27)' + - ':not(.yt-thumb-32)' + - ':not(.yt-thumb-36)' + - ':not(.yt-thumb-48)' + - ':not(.yt-thumb-64), ' + + ".video-thumb" + + ":not(.yt-thumb-20)" + + ":not(.yt-thumb-27)" + + ":not(.yt-thumb-32)" + + ":not(.yt-thumb-36)" + + ":not(.yt-thumb-48)" + + ":not(.yt-thumb-64), " + // (For search results, if a channel is in the results, it's thumbnail will // be caught by this selector, but won't have an matchable video URL. // Since this does not cause an error, it should be fine to ignore it.) // Sidebar suggested video. (url on first child) - '.thumb-wrapper, ' + + ".thumb-wrapper, " + // Playlist page big thumbnail. (url on second child) - '.pl-header-thumb' + ".pl-header-thumb" THUMBNAIL_SELECTORS[THEME_GAMING] = - '' + + "" + // Gaming all types except video wall. URL is on the great-grandparent, // except for search result playlists it is on the grandparent. - 'ytg-thumbnail' + - ':not([avatar])' + - ':not(.avatar)' + - ':not(.ytg-user-avatar)' + - ':not(.ytg-box-art)' + - ':not(.ytg-compact-gaming-event-renderer)' + - ':not(.ytg-playlist-header-renderer)' + "ytg-thumbnail" + + ":not([avatar])" + + ":not(.avatar)" + + ":not(.ytg-user-avatar)" + + ":not(.ytg-box-art)" + + ":not(.ytg-compact-gaming-event-renderer)" + + ":not(.ytg-playlist-header-renderer)" THUMBNAIL_SELECTORS[THEME_MOBILE] = - '' + - 'a.media-item-thumbnail-container, ' + - 'a.compact-media-item-image, ' + - 'a.video-card-image' + "" + + "a.media-item-thumbnail-container, " + + "a.compact-media-item-image, " + + "a.video-card-image" // All themes use this selector for video wall videos. -const THUMBNAIL_SELECTOR_VIDEOWALL = '' + 'a.ytp-videowall-still' +const THUMBNAIL_SELECTOR_VIDEOWALL = "" + "a.ytp-videowall-still" // The default user settings. `userSettings` is replaced with the stored user's // settings once they are loaded. const DEFAULT_USER_SETTINGS = { - barPosition: 'bottom', - barColor: 'blue-gray', - barLikesColor: '#3095e3', - barDislikesColor: '#cfcfcf', + barPosition: "bottom", + barColor: "blue-gray", + barLikesColor: "#3095e3", + barDislikesColor: "#cfcfcf", barColorsSeparator: false, barHeight: 4, barOpacity: 100, @@ -125,7 +125,7 @@ let userSettings = DEFAULT_USER_SETTINGS function ratingToPercentage(rating) { // When the rating is 100%, we display "100%" instead of "100.0%". if (rating === 1) { - return (100).toLocaleString() + '%' + return (100).toLocaleString() + "%" } // We use `floor` instead of `round` to ensure that any rating lower than 100% @@ -134,20 +134,20 @@ function ratingToPercentage(rating) { (Math.floor(rating * 1000) / 10).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2, - }) + '%' + }) + "%" ) } function getToolTipText(videoData) { return ( videoData.likes.toLocaleString() + - ' / ' + + " / " + videoData.dislikes.toLocaleString() + - ' ' + + " " + ratingToPercentage(videoData.rating) + - ' ' + + " " + videoData.total.toLocaleString() + - ' total' + " total" ) } @@ -158,7 +158,7 @@ function exponentialRatingWidthPercentage(rating) { function getRatingBarHtml(videoData) { let ratingElement if (videoData.rating == null) { - ratingElement = '' + ratingElement = "" } else { let likesWidthPercentage if (userSettings.useExponentialScaling) { @@ -167,27 +167,27 @@ function getRatingBarHtml(videoData) { likesWidthPercentage = 100 * videoData.rating } ratingElement = - '' + + "" + '' + - '' + - '' + "" + + "" } return ( - '' + + : "") + + ">" + ratingElement + (userSettings.barTooltip - ? '' + + ? "" + getToolTipText(videoData) + - '' - : '') + - '' + "" + : "") + + "" ) } @@ -200,7 +200,7 @@ function getRatingPercentageHtml(videoData) { return ( '' + ratingToPercentage(videoData.rating) + - '' + "" ) } @@ -209,14 +209,14 @@ function getRatingPercentageHtml(videoData) { if (!isDarkTheme) { g = Math.min(g, 255) * 0.85 } - const rgb = 'rgb(' + r + ',' + g + ',0)' + const rgb = "rgb(" + r + "," + g + ",0)" return ( '' + ratingToPercentage(videoData.rating) + - '' + "" ) } @@ -248,42 +248,42 @@ function getThumbnailsAndIds(thumbnails) { let url if (curTheme === THEME_MODERN) { // The URL should be on the current element. - url = $(thumbnail).attr('href') + url = $(thumbnail).attr("href") } else if (curTheme === THEME_CLASSIC) { // Check the current element, then the parent, then the grandparent, // then the first child, then the second child. url = - $(thumbnail).attr('href') || - $(thumbnail).parent().attr('href') || - $(thumbnail).parent().parent().attr('href') || - $(thumbnail).children(':first').attr('href') || - $(thumbnail).children(':first').next().attr('href') + $(thumbnail).attr("href") || + $(thumbnail).parent().attr("href") || + $(thumbnail).parent().parent().attr("href") || + $(thumbnail).children(":first").attr("href") || + $(thumbnail).children(":first").next().attr("href") } else if (curTheme === THEME_GAMING) { // Check the current element, then the grandparent. url = - $(thumbnail).attr('href') || - $(thumbnail).parent().parent().attr('href') || - $(thumbnail).parent().parent().parent().attr('href') + $(thumbnail).attr("href") || + $(thumbnail).parent().parent().attr("href") || + $(thumbnail).parent().parent().parent().attr("href") // Unless the element is a video wall thumbnail, change the thumbnail // element to the parent element, so that it will show over the thumbnail // preview video that plays when you hover over the thumbnail. - if (!$(thumbnail).is('a')) { + if (!$(thumbnail).is("a")) { thumbnail = $(thumbnail).parent() } } else if (curTheme === THEME_MOBILE) { // The URL should be on the current element. - url = $(thumbnail).attr('href') + url = $(thumbnail).attr("href") // On mobile gaming (m.youtube.com/gaming), the thumbnail should be // reassigned to the child container. - const firstChild = $(thumbnail).children(':first')[0] - if ($(firstChild).is('.video-thumbnail-container-compact')) { + const firstChild = $(thumbnail).children(":first")[0] + if ($(firstChild).is(".video-thumbnail-container-compact")) { thumbnail = firstChild } } else { // The theme may not be set if only video-wall thumbnails were found. - url = $(thumbnail).attr('href') + url = $(thumbnail).attr("href") } if (!url) { @@ -291,7 +291,7 @@ function getThumbnailsAndIds(thumbnails) { } // Check if this thumbnail was previously found. - const previousUrl = $(thumbnail).attr('data-ytrb-url') + const previousUrl = $(thumbnail).attr("data-ytrb-url") if (previousUrl) { // Check if this thumbnail is for the same URL as previously. if (previousUrl === url) { @@ -301,7 +301,7 @@ function getThumbnailsAndIds(thumbnails) { // On mobile, we have to check to make sure the bar is still present, // because thumbnails can sometimes be recreated (such as when they // are scrolled out of view) which causes the bar to be removed. - if ($(thumbnail).children().last().is('ytrb-bar')) { + if ($(thumbnail).children().last().is("ytrb-bar")) { return true } } else { @@ -309,15 +309,15 @@ function getThumbnailsAndIds(thumbnails) { } } else { // If not, remove the old rating bar and retries count. - $(thumbnail).children('ytrb-bar').remove() - $(thumbnail).removeAttr('data-ytrb-retries') + $(thumbnail).children("ytrb-bar").remove() + $(thumbnail).removeAttr("data-ytrb-retries") } } // Save the URL that corresponds with this thumbnail in a separate // attribute so that we can check if the URL has changed in the future, in // which case we'll have to update the rating bar. - $(thumbnail).attr('data-ytrb-url', url) + $(thumbnail).attr("data-ytrb-url", url) // Extract the video ID from the URL. const match = @@ -349,11 +349,11 @@ function retryProcessingThumbnailInTheFuture(thumbnail) { setTimeout(() => { isPendingApiRetry = false thumbnailsToRetry.forEach((thumbnail) => { - const retriesAttr = $(thumbnail).attr('data-ytrb-retries') + const retriesAttr = $(thumbnail).attr("data-ytrb-retries") const retriesNum = retriesAttr ? Number.parseInt(retriesAttr, 10) : 0 if (retriesNum < MAX_RETRIES_PER_THUMBNAIL) { - $(thumbnail).attr('data-ytrb-retries', retriesNum + 1) - $(thumbnail).removeAttr('data-ytrb-url') + $(thumbnail).attr("data-ytrb-retries", retriesNum + 1) + $(thumbnail).removeAttr("data-ytrb-url") hasUnseenDomMutations = true } }) @@ -370,7 +370,7 @@ function retryProcessingThumbnailInTheFuture(thumbnail) { function getVideoData(thumbnail, videoId) { return new Promise((resolve) => { chrome.runtime.sendMessage( - { query: 'videoApiRequest', videoId: videoId }, + { query: "videoApiRequest", videoId: videoId }, (likesData) => { if (likesData === null) { // The API request failed, which is usually due to rate limiting, so @@ -395,32 +395,32 @@ function addRatingPercentage(thumbnail, videoData) { let metadataLine if (curTheme === THEME_MOBILE) { metadataLine = $(thumbnail) - .closest('ytm-media-item') - .find('ytm-badge-and-byline-renderer') + .closest("ytm-media-item") + .find("ytm-badge-and-byline-renderer") .last() } else { metadataLine = $(thumbnail) .closest( - '.ytd-rich-item-renderer, ' + // Home page. - '.ytd-grid-renderer, ' + // Trending and subscriptions page. - '.ytd-expanded-shelf-contents-renderer, ' + // Subscriptions page. - '.yt-horizontal-list-renderer, ' + // Channel page. - '.ytd-item-section-renderer, ' + // History page. - '.ytd-horizontal-card-list-renderer, ' + // Gaming page. - '.ytd-playlist-video-list-renderer', // Playlist page. + ".ytd-rich-item-renderer, " + // Home page. + ".ytd-grid-renderer, " + // Trending and subscriptions page. + ".ytd-expanded-shelf-contents-renderer, " + // Subscriptions page. + ".yt-horizontal-list-renderer, " + // Channel page. + ".ytd-item-section-renderer, " + // History page. + ".ytd-horizontal-card-list-renderer, " + // Gaming page. + ".ytd-playlist-video-list-renderer", // Playlist page. ) - .find('#metadata-line') + .find("#metadata-line") .last() } if (metadataLine) { // Remove any previously added percentages. - for (const oldPercentage of metadataLine.children('.ytrb-percentage')) { + for (const oldPercentage of metadataLine.children(".ytrb-percentage")) { oldPercentage.remove() } if (curTheme === THEME_MOBILE) { for (const oldPercentage of metadataLine.children( - '.ytrb-percentage-separator', + ".ytrb-percentage-separator", )) { oldPercentage.remove() } @@ -437,7 +437,7 @@ function addRatingPercentage(thumbnail, videoData) { !(videoData.likes === 0 && videoData.dislikes >= 10) ) { const ratingPercentageHtml = getRatingPercentageHtml(videoData) - const lastSpan = metadataLine.children('span').last() + const lastSpan = metadataLine.children("span").last() if (lastSpan.length) { lastSpan.after(ratingPercentageHtml) if (curTheme === THEME_MOBILE) { @@ -503,37 +503,37 @@ function processNewThumbnails() { // ) // const NUMBERING_SYSTEM_DIGIT_STRINGS = [ - '٠١٢٣٤٥٦٧٨٩', - '۰۱۲۳۴۵۶۷۸۹', - '᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙', - '০১২৩৪৫৬৭৮৯', - '०१२३४५६७८९', - '0123456789', - '૦૧૨૩૪૫૬૭૮૯', - '੦੧੨੩੪੫੬੭੮੯', - '〇一二三四五六七八九', - '០១២៣៤៥៦៧៨៩', - '೦೧೨೩೪೫೬೭೮೯', - '໐໑໒໓໔໕໖໗໘໙', - '0123456789', - '᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏', - '൦൧൨൩൪൫൬൭൮൯', - '᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙', - '၀၁၂၃၄၅၆၇၈၉', - '୦୧୨୩୪୫୬୭୮୯', - '௦௧௨௩௪௫௬௭௮௯', - '౦౧౨౩౪౫౬౭౮౯', - '๐๑๒๓๔๕๖๗๘๙', - '༠༡༢༣༤༥༦༧༨༩', + "٠١٢٣٤٥٦٧٨٩", + "۰۱۲۳۴۵۶۷۸۹", + "᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙", + "০১২৩৪৫৬৭৮৯", + "०१२३४५६७८९", + "0123456789", + "૦૧૨૩૪૫૬૭૮૯", + "੦੧੨੩੪੫੬੭੮੯", + "〇一二三四五六七八九", + "០១២៣៤៥៦៧៨៩", + "೦೧೨೩೪೫೬೭೮೯", + "໐໑໒໓໔໕໖໗໘໙", + "0123456789", + "᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏", + "൦൧൨൩൪൫൬൭൮൯", + "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙", + "၀၁၂၃၄၅၆၇၈၉", + "୦୧୨୩୪୫୬୭୮୯", + "௦௧௨௩௪௫௬௭௮௯", + "౦౧౨౩౪౫౬౭౮౯", + "๐๑๒๓๔๕๖๗๘๙", + "༠༡༢༣༤༥༦༧༨༩", ] function parseInternationalInt(string) { // Parses an internationalized integer string (e.g. "1,234" or "١٬٢٣٤") into a // JavaScript integer. - string = string.replace(/[\s,.]/g, '') + string = string.replace(/[\s,.]/g, "") if (/[^0-9]/.test(string)) { - let newString = '' + let newString = "" for (const char of string) { for (const digitString of NUMBERING_SYSTEM_DIGIT_STRINGS) { const index = digitString.indexOf(char) @@ -569,13 +569,13 @@ function getVideoDataFromTooltipText(text) { } function updateVideoRatingBar() { - $('.ryd-tooltip').each(function (_, rydTooltip) { - const tooltip = $(rydTooltip).find('#tooltip') + $(".ryd-tooltip").each(function (_, rydTooltip) { + const tooltip = $(rydTooltip).find("#tooltip") const curText = $(tooltip).text() // We add a zero-width space to the end of any processed tooltip text to // prevent it from being reprocessed. - if (!curText.endsWith('\u200b')) { + if (!curText.endsWith("\u200b")) { const videoData = getVideoDataFromTooltipText(curText) if (userSettings.barTooltip && videoData) { @@ -589,8 +589,11 @@ function updateVideoRatingBar() { } if (userSettings.useExponentialScaling && videoData && videoData.rating) { - $(rydTooltip).find('#ryd-bar')[0].style.width = - exponentialRatingWidthPercentage(videoData.rating) + '%' + const rydBar = $(rydTooltip).find("#ryd-bar")[0] + if (rydBar) { + rydBar.style.width = + exponentialRatingWidthPercentage(videoData.rating) + "%" + } } } }) @@ -640,99 +643,99 @@ chrome.storage.local.get(DEFAULT_USER_SETTINGS, function (storedSettings) { const cssFiles = [] if (userSettings.barHeight !== 0) { - cssFiles.push('css/bar.css') + cssFiles.push("css/bar.css") - if (userSettings.barPosition === 'top') { - cssFiles.push('css/bar-top.css') + if (userSettings.barPosition === "top") { + cssFiles.push("css/bar-top.css") } else { - cssFiles.push('css/bar-bottom.css') + cssFiles.push("css/bar-bottom.css") } if (userSettings.barSeparator) { - if (userSettings.barPosition === 'top') { - cssFiles.push('css/bar-top-separator.css') + if (userSettings.barPosition === "top") { + cssFiles.push("css/bar-top-separator.css") } else { - cssFiles.push('css/bar-bottom-separator.css') + cssFiles.push("css/bar-bottom-separator.css") } } if (userSettings.barTooltip) { - cssFiles.push('css/bar-tooltip.css') - if (userSettings.barPosition === 'top') { - cssFiles.push('css/bar-top-tooltip.css') + cssFiles.push("css/bar-tooltip.css") + if (userSettings.barPosition === "top") { + cssFiles.push("css/bar-top-tooltip.css") } else { - cssFiles.push('css/bar-bottom-tooltip.css') + cssFiles.push("css/bar-bottom-tooltip.css") } } if (userSettings.useOnVideoPage) { - cssFiles.push('css/bar-video-page.css') + cssFiles.push("css/bar-video-page.css") } } if (cssFiles.length > 0) { chrome.runtime.sendMessage({ - query: 'insertCss', + query: "insertCss", files: cssFiles, }) } document.documentElement.style.setProperty( - '--ytrb-bar-height', - userSettings.barHeight + 'px', + "--ytrb-bar-height", + userSettings.barHeight + "px", ) document.documentElement.style.setProperty( - '--ytrb-bar-opacity', + "--ytrb-bar-opacity", userSettings.barOpacity / 100, ) - if (userSettings.barColor === 'blue-gray') { + if (userSettings.barColor === "blue-gray") { document.documentElement.style.setProperty( - '--ytrb-bar-likes-color', - '#3095e3', + "--ytrb-bar-likes-color", + "#3095e3", ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-color', - '#cfcfcf', + "--ytrb-bar-dislikes-color", + "#cfcfcf", ) document.documentElement.style.setProperty( - '--ytrb-bar-likes-shadow', - 'none', + "--ytrb-bar-likes-shadow", + "none", ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-shadow', - 'none', + "--ytrb-bar-dislikes-shadow", + "none", ) - } else if (userSettings.barColor === 'green-red') { - document.documentElement.style.setProperty('--ytrb-bar-likes-color', '#060') + } else if (userSettings.barColor === "green-red") { + document.documentElement.style.setProperty("--ytrb-bar-likes-color", "#060") document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-color', - '#c00', + "--ytrb-bar-dislikes-color", + "#c00", ) document.documentElement.style.setProperty( - '--ytrb-bar-likes-shadow', - '1px 0 #fff', + "--ytrb-bar-likes-shadow", + "1px 0 #fff", ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-shadow', - 'inset 1px 0 #fff', + "--ytrb-bar-dislikes-shadow", + "inset 1px 0 #fff", ) - } else if (userSettings.barColor === 'custom-colors') { + } else if (userSettings.barColor === "custom-colors") { document.documentElement.style.setProperty( - '--ytrb-bar-likes-color', + "--ytrb-bar-likes-color", userSettings.barLikesColor, ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-color', + "--ytrb-bar-dislikes-color", userSettings.barDislikesColor, ) document.documentElement.style.setProperty( - '--ytrb-bar-likes-shadow', - userSettings.barColorsSeparator ? '1px 0 #fff' : 'none', + "--ytrb-bar-likes-shadow", + userSettings.barColorsSeparator ? "1px 0 #fff" : "none", ) document.documentElement.style.setProperty( - '--ytrb-bar-dislikes-shadow', - userSettings.barColorsSeparator ? 'inset 1px 0 #fff' : 'none', + "--ytrb-bar-dislikes-shadow", + userSettings.barColorsSeparator ? "inset 1px 0 #fff" : "none", ) } diff --git a/manifest-v2/manifest.json b/manifest-v2/manifest.json index 4145468..6f6fac7 100644 --- a/manifest-v2/manifest.json +++ b/manifest-v2/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Thumbnail Rating Bar for YouTube™", - "version": "1.8.16", + "version": "1.8.17", "description": "Displays a rating bar (likes/dislikes) on the bottom of every YouTube™ video thumbnail.", "author": "Elliot Waite", "icons": {