Pārlūkot izejas kodu

feat: finish like & dislike labels feature

Sv443 10 mēneši atpakaļ
vecāks
revīzija
48b89d7dc3

+ 14 - 14
assets/translations/README.md

@@ -75,9 +75,9 @@ This means to figure out which keys are untranslated, you will need to manually
 | `votes_format_short` | `Shortened` |
 | `votes_format_full` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
-| `feature_helptext_showVotes` | `This feature is powered by ReturnYoutubeDislikes and will show the approximate amount of likes and dislikes on the currently playing song.` |
+| `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
-| `feature_desc_showVoteRatio` | `Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
+| `feature_desc_showVoteRatio` | `TODO: Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
 | `feature_desc_deleteFromQueueButton` | `Add a button to each song in a list to quickly remove it` |
 | `feature_desc_listButtonsPlacement` | `Where should the list buttons show up?` |
 | `feature_desc_autoLikeChannels` | `Automatically like all songs and videos of certain channels` |
@@ -130,9 +130,9 @@ This means to figure out which keys are untranslated, you will need to manually
 | `votes_format_short` | `Shortened` |
 | `votes_format_full` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
-| `feature_helptext_showVotes` | `This feature is powered by ReturnYoutubeDislikes and will show the approximate amount of likes and dislikes on the currently playing song.` |
+| `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
-| `feature_desc_showVoteRatio` | `Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
+| `feature_desc_showVoteRatio` | `TODO: Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
 | `feature_desc_deleteFromQueueButton` | `Add a button to each song in a list to quickly remove it` |
 | `feature_desc_listButtonsPlacement` | `Where should the list buttons show up?` |
 | `feature_desc_autoLikeChannels` | `Automatically like all songs and videos of certain channels` |
@@ -185,9 +185,9 @@ This means to figure out which keys are untranslated, you will need to manually
 | `votes_format_short` | `Shortened` |
 | `votes_format_full` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
-| `feature_helptext_showVotes` | `This feature is powered by ReturnYoutubeDislikes and will show the approximate amount of likes and dislikes on the currently playing song.` |
+| `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
-| `feature_desc_showVoteRatio` | `Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
+| `feature_desc_showVoteRatio` | `TODO: Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
 | `feature_desc_deleteFromQueueButton` | `Add a button to each song in a list to quickly remove it` |
 | `feature_desc_listButtonsPlacement` | `Where should the list buttons show up?` |
 | `feature_desc_autoLikeChannels` | `Automatically like all songs and videos of certain channels` |
@@ -240,9 +240,9 @@ This means to figure out which keys are untranslated, you will need to manually
 | `votes_format_short` | `Shortened` |
 | `votes_format_full` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
-| `feature_helptext_showVotes` | `This feature is powered by ReturnYoutubeDislikes and will show the approximate amount of likes and dislikes on the currently playing song.` |
+| `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
-| `feature_desc_showVoteRatio` | `Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
+| `feature_desc_showVoteRatio` | `TODO: Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
 | `feature_desc_deleteFromQueueButton` | `Add a button to each song in a list to quickly remove it` |
 | `feature_desc_listButtonsPlacement` | `Where should the list buttons show up?` |
 | `feature_desc_autoLikeChannels` | `Automatically like all songs and videos of certain channels` |
@@ -295,9 +295,9 @@ This means to figure out which keys are untranslated, you will need to manually
 | `votes_format_short` | `Shortened` |
 | `votes_format_full` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
-| `feature_helptext_showVotes` | `This feature is powered by ReturnYoutubeDislikes and will show the approximate amount of likes and dislikes on the currently playing song.` |
+| `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
-| `feature_desc_showVoteRatio` | `Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
+| `feature_desc_showVoteRatio` | `TODO: Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
 | `feature_desc_deleteFromQueueButton` | `Add a button to each song in a list to quickly remove it` |
 | `feature_desc_listButtonsPlacement` | `Where should the list buttons show up?` |
 | `feature_desc_autoLikeChannels` | `Automatically like all songs and videos of certain channels` |
@@ -350,9 +350,9 @@ This means to figure out which keys are untranslated, you will need to manually
 | `votes_format_short` | `Shortened` |
 | `votes_format_full` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
-| `feature_helptext_showVotes` | `This feature is powered by ReturnYoutubeDislikes and will show the approximate amount of likes and dislikes on the currently playing song.` |
+| `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
-| `feature_desc_showVoteRatio` | `Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
+| `feature_desc_showVoteRatio` | `TODO: Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
 | `feature_desc_deleteFromQueueButton` | `Add a button to each song in a list to quickly remove it` |
 | `feature_desc_listButtonsPlacement` | `Where should the list buttons show up?` |
 | `feature_desc_autoLikeChannels` | `Automatically like all songs and videos of certain channels` |
@@ -405,9 +405,9 @@ This means to figure out which keys are untranslated, you will need to manually
 | `votes_format_short` | `Shortened` |
 | `votes_format_full` | `Full number` |
 | `feature_desc_showVotes` | `Show the amount of likes and dislikes on the currently playing song` |
-| `feature_helptext_showVotes` | `This feature is powered by ReturnYoutubeDislikes and will show the approximate amount of likes and dislikes on the currently playing song.` |
+| `feature_helptext_showVotes` | `This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.` |
 | `feature_desc_showVotesFormat` | `How should the likes and dislikes be formatted?` |
-| `feature_desc_showVoteRatio` | `Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
+| `feature_desc_showVoteRatio` | `TODO: Show a colored bar that represents the ratio of likes and dislikes of the currently playing song` |
 | `feature_desc_deleteFromQueueButton` | `Add a button to each song in a list to quickly remove it` |
 | `feature_desc_listButtonsPlacement` | `Where should the list buttons show up?` |
 | `feature_desc_autoLikeChannels` | `Automatically like all songs and videos of certain channels` |

+ 2 - 2
assets/translations/en_US.json

@@ -202,9 +202,9 @@
     "feature_desc_disableDarkReaderSites": "On which sites should the Dark Reader extension be disabled to fix layout issues?",
     "feature_helptext_disableDarkReaderSites": "The Dark Reader extension can cause issues with the layout of the site.\nThis feature allows you to disable Dark Reader on certain or all sites to prevent those issues.\nIf the extension is not installed, this feature will have no effect and can be left activated.",
     "feature_desc_showVotes": "Show the amount of likes and dislikes on the currently playing song",
-    "feature_helptext_showVotes": "This feature is powered by ReturnYoutubeDislikes and will show the approximate amount of likes and dislikes on the currently playing song.",
+    "feature_helptext_showVotes": "This feature is powered by Return YouTube Dislike and will show the approximate amount of likes and dislikes on the currently playing song.",
     "feature_desc_showVotesFormat": "How should the likes and dislikes be formatted?",
-    "feature_desc_showVoteRatio": "Show a colored bar that represents the ratio of likes and dislikes of the currently playing song",
+    "feature_desc_showVoteRatio": "TODO: Show a colored bar that represents the ratio of likes and dislikes of the currently playing song",
 
     "feature_desc_lyricsQueueButton": "Add a button to each song in a queue to quickly open its lyrics page",
     "feature_desc_deleteFromQueueButton": "Add a button to each song in a list to quickly remove it",

+ 6 - 1
dist/BetterYTM.css

@@ -1647,7 +1647,12 @@ ytmusic-player#player #bezel {
 .bytm-vote-label {
   cursor: pointer;
   font-size: 1.25rem;
-  padding: 4px;
+  padding: 6px;
+  padding-left: 3px;
+}
+
+.bytm-vote-label:last-child {
+  padding-right: 0px;
 }
 
 :root {

+ 6 - 1
src/features/layout.css

@@ -388,5 +388,10 @@ ytmusic-player#player #bezel {
 .bytm-vote-label {
   cursor: pointer;
   font-size: 1.25rem;
-  padding: 4px;
+  padding: 6px;
+  padding-left: 3px;
+}
+
+.bytm-vote-label:last-child {
+  padding-right: 0px;
 }

+ 34 - 20
src/features/layout.ts

@@ -2,7 +2,7 @@ import { addParent, autoPlural, debounce, fetchAdvanced, pauseFor } from "@sv443
 import { getFeature, getFeatures } from "../config.js";
 import { siteEvents } from "../siteEvents.js";
 import { addSelectorListener } from "../observers.js";
-import { error, getResourceUrl, log, warn, t, onInteraction, openInTab, getBestThumbnailUrl, getDomain, addStyle, currentMediaType, domLoaded, waitVideoElementReady, getVideoTime, fetchCss, addStyleFromResource, fetchVideoVotes, getWatchId, type ReturnYoutubeDislikesVotesObj } from "../utils/index.js";
+import { error, getResourceUrl, log, warn, t, onInteraction, openInTab, getBestThumbnailUrl, getDomain, addStyle, currentMediaType, domLoaded, waitVideoElementReady, getVideoTime, fetchCss, addStyleFromResource, fetchVideoVotes, getWatchId, type ReturnYouTubeDislikeVotesObj } from "../utils/index.js";
 import { mode, scriptInfo } from "../constants.js";
 import { openCfgMenu } from "../menu/menu_old.js";
 import { createCircularBtn, createRipple } from "../components/index.js";
@@ -757,22 +757,30 @@ export async function initShowVotes() {
   });
 
   siteEvents.on("watchIdChanged", async (watchId) => {
-    const voteObj = await fetchVideoVotes(watchId);
-    if(!voteObj || !("likes" in voteObj) || !("dislikes" in voteObj) || !("rating" in voteObj))
-      return error("Couldn't fetch votes from ReturnYouTubeDislikes API");
-
     const labelLikes = document.querySelector<HTMLElement>("ytmusic-like-button-renderer .bytm-vote-label.likes");
     const labelDislikes = document.querySelector<HTMLElement>("ytmusic-like-button-renderer .bytm-vote-label.dislikes");
 
     if(!labelLikes || !labelDislikes)
       return error("Couldn't find vote label elements while updating like and dislike counts");
 
-    labelLikes.textContent = voteObj.likes.toLocaleString(undefined, getVoteNumberFormat());
-    labelDislikes.textContent = voteObj.dislikes.toLocaleString(undefined, getVoteNumberFormat());
+    if(labelLikes.dataset.watchId === watchId && labelDislikes.dataset.watchId === watchId)
+      return log("Vote labels already updated for this video");
+
+    const voteObj = await fetchVideoVotes(watchId);
+    if(!voteObj || !("likes" in voteObj) || !("dislikes" in voteObj) || !("rating" in voteObj))
+      return error("Couldn't fetch votes from ReturnYouTubeDislikes API");
+
+    labelLikes.dataset.watchId = getWatchId() ?? "";
+    labelLikes.textContent = formatVoteNumber(voteObj.likes);
+    labelLikes.title = labelLikes.ariaLabel = t("vote_label_likes", voteObj.likes);
+
+    labelDislikes.textContent = formatVoteNumber(voteObj.dislikes);
+    labelDislikes.title = labelDislikes.ariaLabel = t("vote_label_dislikes", voteObj.dislikes);
+    labelDislikes.dataset.watchId = getWatchId() ?? "";
   });
 }
 
-function addVoteNumbers(voteCont: HTMLElement, voteObj: ReturnYoutubeDislikesVotesObj) {
+function addVoteNumbers(voteCont: HTMLElement, voteObj: ReturnYouTubeDislikeVotesObj) {
   const likeBtn = voteCont.querySelector<HTMLElement>("#button-shape-like");
   const dislikeBtn = voteCont.querySelector<HTMLElement>("#button-shape-dislike");
 
@@ -782,8 +790,9 @@ function addVoteNumbers(voteCont: HTMLElement, voteObj: ReturnYoutubeDislikesVot
   const createLabel = (amount: number, type: "likes" | "dislikes"): HTMLElement => {
     const label = document.createElement("span");
     label.classList.add("bytm-vote-label", "bytm-no-select", type);
-    label.textContent = amount.toLocaleString(undefined, getVoteNumberFormat());
+    label.textContent = String(formatVoteNumber(amount));
     label.title = label.ariaLabel = t(`vote_label_${type}`, amount);
+    label.dataset.watchId = getWatchId() ?? "";
     label.addEventListener("click", (e) => {
       e.preventDefault();
       e.stopPropagation();
@@ -801,18 +810,23 @@ function addVoteNumbers(voteCont: HTMLElement, voteObj: ReturnYoutubeDislikesVot
   dislikeBtn.insertAdjacentElement("afterend", dislikeLblEl);
 }
 
-function getVoteNumberFormat(): Partial<Intl.NumberFormatOptions> {
-  return getFeature("showVotesFormat") === "short"
-    ? {
-      notation: "compact",
-      compactDisplay: "short",
-    }
-    : {
-      style: "decimal",
-      maximumFractionDigits: 0,
-    };
+/** Formats a number with the notation based on the config */
+function formatVoteNumber(num: number) {
+  return num.toLocaleString(
+    undefined,
+    getFeature("showVotesFormat") === "short"
+      ? {
+        notation: "compact",
+        compactDisplay: "short",
+        maximumFractionDigits: 1,
+      }
+      : {
+        style: "decimal",
+        maximumFractionDigits: 0,
+      },
+  );
 }
 
-function addVoteRatio(voteCont: HTMLElement, voteObj: ReturnYoutubeDislikesVotesObj) {
+function addVoteRatio(voteCont: HTMLElement, voteObj: ReturnYouTubeDislikeVotesObj) {
   console.log("># TODO: addVoteRatio", voteCont, voteObj);
 }

+ 4 - 4
src/utils/xhr.ts

@@ -57,7 +57,7 @@ export async function fetchCss(key: ResourceKey & `css-${string}`) {
   }
 }
 
-export type ReturnYoutubeDislikesVotesObj = {
+export type ReturnYouTubeDislikeVotesObj = {
   /** The watch ID of the video */
   id: string;
   /** ISO timestamp of when the video was uploaded */
@@ -75,15 +75,15 @@ export type ReturnYoutubeDislikesVotesObj = {
 };
 
 /**
- * Fetches the votes object for a YouTube video from the [Return YouTube Dislikes API.](https://returnyoutubedislike.com/docs)
+ * Fetches the votes object for a YouTube video from the [Return YouTube Dislike API.](https://returnyoutubedislike.com/docs)
  * @param watchId The watch ID of the video
  */
 export async function fetchVideoVotes(watchId: string) {
   try {
-    return JSON.parse((await sendRequest<ReturnYoutubeDislikesVotesObj>({
+    return JSON.parse((await sendRequest<ReturnYouTubeDislikeVotesObj>({
       method: "GET",
       url: `https://returnyoutubedislikeapi.com/votes?videoId=${watchId}`,
-    })).response) as ReturnYoutubeDislikesVotesObj;
+    })).response) as ReturnYouTubeDislikeVotesObj;
   }
   catch(err) {
     error("Couldn't fetch video votes due to an error:", err);