ソースを参照

Merge branch 'main' of https://github.com/Anarios/return-youtube-dislike

Dmitrii Selivanov 3 年 前
コミット
7fd4904c39

BIN
Extensions/combined/icons/icon_hold128.png


+ 8 - 6
Extensions/combined/popup.html

@@ -19,11 +19,12 @@
       <button id="link_discord">Discord</button>
 
       <br><br>
+       <button id="link_faq">FAQ</button>
       <button id="link_donate">Donate</button>
       <br>
 
       <br>
-<!--      <button id="advancedToggle">Show Settings</button>-->
+      <button id="advancedToggle">Show Settings</button>
       <br>
 
     </center>
@@ -32,16 +33,17 @@
       <legend id="advancedLegend">Settings</legend>
 
       <label class="switch">
-        <input type="checkbox" id="disable_ratio_bar" />
+        <input type="checkbox" id="disable_vote_submission" />
         <span class="slider" />
-        <span class="switchLabel">Lorem ipsum dolor sit amet</span> </label
-      ><br />
+        <span class="switchLabel">Disable like/dislike submission</span>
+      </label>
+      <br/>
 
-      <label class="switch">
+      <!-- <label class="switch">
         <input type="checkbox" id="disable_api_unlisted" />
         <span class="slider" />
         <span class="switchLabel">Lorem ipsum dolor sit amet</span> </label
-      ><br />
+      ><br /> -->
     </fieldset>
   </body>
   <script src="popup.js"></script>

+ 36 - 4
Extensions/combined/popup.js

@@ -3,12 +3,14 @@ const config = {
   advanced: false,
   showAdvancedMessage: "Show Settings",
   hideAdvancedMessage: "Hide Settings",
+  disableVoteSubmission: false,
 
   links: {
     website: "https://returnyoutubedislike.com",
     github: "https://github.com/Anarios/return-youtube-dislike",
     discord: "https://discord.gg/mYnESY4Md5",
-    donate: 'https://returnyoutubedislike.com/donate'
+    donate: 'https://returnyoutubedislike.com/donate',
+    faq: 'https://returnyoutubedislike.com/faq'
   },
 };
 
@@ -25,14 +27,20 @@ document.getElementById("link_discord").addEventListener("click", () => {
   chrome.tabs.create({ url: config.links.discord });
 });
 
+document.getElementById("link_faq").addEventListener("click", () => {
+  chrome.tabs.create({ url: config.links.faq });
+});
+
 document.getElementById("link_donate").addEventListener("click", () => {
   chrome.tabs.create({ url: config.links.donate });
 });
 
 
-chrome.runtime.sendMessage({ message: 'get_auth_token' });
+document.getElementById("disable_vote_submission").addEventListener("click", (ev) => {
+  chrome.storage.sync.set({ disableVoteSubmission: ev.target.checked });
+});
+
 /*   Advanced Toggle   */
-/* Not currently used in this version
 const advancedToggle = document.getElementById("advancedToggle");
 advancedToggle.addEventListener("click", () => {
   const adv = document.getElementById("advancedSettings");
@@ -46,7 +54,31 @@ advancedToggle.addEventListener("click", () => {
     config.advanced = true;
   }
 });
-*/
+
+initConfig();
+
+function initConfig() {
+  initializeDisableVoteSubmission();
+}
+
+function initializeDisableVoteSubmission() {
+  chrome.storage.sync.get(['disableVoteSubmission'], (res) => {
+    handleDisableVoteSubmissionChangeEvent(res.disableVoteSubmission);
+  });
+}
+
+chrome.storage.onChanged.addListener(storageChangeHandler);
+
+function storageChangeHandler(changes, area) {
+  if (changes.disableVoteSubmission !== undefined) {
+    handleDisableVoteSubmissionChangeEvent(changes.disableVoteSubmission.newValue);
+  }
+}
+
+function handleDisableVoteSubmissionChangeEvent(value) {
+  config.disableVoteSubmission = value;
+  document.getElementById("disable_vote_submission").checked = value;
+}
 
 /* popup-script.js
 document.querySelector('#login')

+ 59 - 7
Extensions/combined/ryd.background.js

@@ -1,12 +1,17 @@
 const apiUrl = "https://returnyoutubedislikeapi.com";
+const voteDisabledIconName = 'icon_hold128.png';
+const defaultIconName = 'icon128.png';
 let api;
-if (typeof chrome !== "undefined" && typeof chrome.runtime !== "undefined")
-  api = chrome;
-else if (
-  typeof browser !== "undefined" &&
-  typeof browser.runtime !== "undefined"
-)
-  api = browser;
+
+/** stores extension's global config */
+let extConfig = {
+  disableVoteSubmission: false
+}
+
+if (isChrome()) api = chrome;
+else if (isFirefox()) api = browser;
+
+initExtConfig()
 
 api.runtime.onMessage.addListener((request, sender, sendResponse) => {
   if (request.message === "get_auth_token") {
@@ -218,3 +223,50 @@ function generateUserID(length = 36) {
     return result;
   }
 }
+
+function storageChangeHandler(changes, area) {
+  if (changes.disableVoteSubmission !== undefined) {
+    handleDisableVoteSubmissionChangeEvent(changes.disableVoteSubmission.newValue);
+  }
+}
+
+function handleDisableVoteSubmissionChangeEvent(value) {
+  extConfig.disableVoteSubmission = value;
+  if (value === true) {
+    changeIcon(voteDisabledIconName);
+  } else {
+    changeIcon(defaultIconName);
+  }
+}
+
+function changeIcon(iconName) {
+  if (api.action !== undefined) api.action.setIcon({path: "/icons/" + iconName});
+  else if (api.browserAction !== undefined) api.browserAction.setIcon({path: "/icons/" + iconName});
+  else console.log('changing icon is not supported');
+}
+
+api.storage.onChanged.addListener(storageChangeHandler);
+
+function initExtConfig() {
+  initializeDisableVoteSubmission();
+}
+
+function initializeDisableVoteSubmission() {
+  api.storage.sync.get(['disableVoteSubmission'], (res) => {
+    if (res.disableVoteSubmission === undefined) {
+      api.storage.sync.set({disableVoteSubmission: false});
+    }
+    else {
+      extConfig.disableVoteSubmission = res.disableVoteSubmission;
+      if (res.disableVoteSubmission) changeIcon(voteDisabledIconName);
+    }
+  });
+}
+
+function isChrome() {
+  return typeof chrome !== "undefined" && typeof chrome.runtime !== "undefined";
+}
+
+function isFirefox() {
+  return typeof browser !== "undefined" && typeof browser.runtime !== "undefined";
+}

+ 6 - 13
Extensions/combined/ryd.content-script.js

@@ -17,16 +17,13 @@ import {
   LIKED_STATE,
   DISLIKED_STATE,
   NEUTRAL_STATE,
+  initExtConfig,
 } 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"
+import { sendVideoIds, sendVote, likeClicked, dislikeClicked, addLikeDislikeEventListener, storageChangeHandler  } from "./src/events"
 
-let storedData = {
-  likes: 0,
-  dislikes: 0,
-  previousState: NEUTRAL_STATE,
-};
+initExtConfig()
 
 let jsInitChecktimer = null;
 
@@ -35,13 +32,9 @@ function setEventListeners(evt) {
     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);
+      addLikeDislikeEventListener();
+      setInitialState();
+      getBrowser().storage.onChanged.addListener(storageChangeHandler);
     }
   }
 

+ 39 - 11
Extensions/combined/src/events.js

@@ -1,14 +1,16 @@
-import { getBrowser, getVideoId, numberFormat } from "./utils"
-import { checkForSignInButton } from "./buttons"
-import { NEUTRAL_STATE, LIKED_STATE, DISLIKED_STATE, setDislikes } from "./state"
+import { getBrowser, getVideoId, numberFormat, cLog } from "./utils"
+import { checkForSignInButton, getButtons } from "./buttons"
+import { NEUTRAL_STATE, LIKED_STATE, DISLIKED_STATE, setDislikes, extConfig, storedData } from "./state"
 import { createRateBar } from "./bar"
 
 function sendVote(vote) {
-  getBrowser().runtime.sendMessage({
-    message: "send_vote",
-    vote: vote,
-    videoId: getVideoId(window.location.href),
-  });
+  if (extConfig.disableVoteSubmission !== true) {
+    getBrowser().runtime.sendMessage({
+      message: "send_vote",
+      vote: vote,
+      videoId: getVideoId(window.location.href),
+    });
+  }
 }
 
 function sendVideoIds() {
@@ -37,7 +39,7 @@ function sendVideoIds() {
   });
 }
 
-function likeClicked(storedData) {
+function likeClicked() {
   if (checkForSignInButton() === false) {
     if (storedData.previousState === DISLIKED_STATE) {
       sendVote(1);
@@ -60,7 +62,7 @@ function likeClicked(storedData) {
   }
 }
 
-function dislikeClicked(storedData) {
+function dislikeClicked() {
   if (checkForSignInButton() == false) {
     if (storedData.previousState === NEUTRAL_STATE) {
       sendVote(-1);
@@ -85,4 +87,30 @@ function dislikeClicked(storedData) {
   }
 }
 
-export { sendVote, sendVideoIds, likeClicked, dislikeClicked }
+function addLikeDislikeEventListener() {
+  const buttons = getButtons();
+    if (!window.returnDislikeButtonlistenersSet) {
+      buttons.children[0].addEventListener("click", likeClicked);
+      buttons.children[1].addEventListener("click", dislikeClicked);
+      window.returnDislikeButtonlistenersSet = true;
+    }
+}
+
+function storageChangeHandler(changes, area) {
+  if (changes.disableVoteSubmission !== undefined) {
+    handleDisableVoteSubmissionChangeEvent(changes.disableVoteSubmission.newValue);
+  }
+}
+
+function handleDisableVoteSubmissionChangeEvent(value) {
+  extConfig.disableVoteSubmission = value;
+}
+
+export {
+  sendVote,
+  sendVideoIds,
+  likeClicked,
+  dislikeClicked,
+  addLikeDislikeEventListener,
+  storageChangeHandler,
+};

+ 29 - 1
Extensions/combined/src/state.js

@@ -7,6 +7,16 @@ const LIKED_STATE = "LIKED_STATE";
 const DISLIKED_STATE = "DISLIKED_STATE";
 const NEUTRAL_STATE = "NEUTRAL_STATE";
 
+let extConfig = {
+  disableVoteSubmission: false,
+};
+
+let storedData = {
+  likes: 0,
+  dislikes: 0,
+  previousState: NEUTRAL_STATE,
+};
+
 function isMobile() {
   return location.hostname == "m.youtube.com";
 }
@@ -97,13 +107,28 @@ function setState(storedData) {
   );
 }
 
-function setInitialState(storedData) {
+function setInitialState() {
   setState(storedData);
   setTimeout(() => {
     sendVideoIds();
   }, 1500);
 }
 
+function initExtConfig() {
+  initializeDisableVoteSubmission();
+}
+
+function initializeDisableVoteSubmission() {
+  getBrowser().storage.sync.get(['disableVoteSubmission'], (res) => {
+    if (res.disableVoteSubmission === undefined) {
+      getBrowser().storage.sync.set({disableVoteSubmission: false});
+    }
+    else {
+      extConfig.disableVoteSubmission = res.disableVoteSubmission;
+    }
+  });
+}
+
 export {
   isMobile,
   isVideoDisliked,
@@ -117,4 +142,7 @@ export {
   LIKED_STATE,
   DISLIKED_STATE,
   NEUTRAL_STATE,
+  extConfig,
+  initExtConfig,
+  storedData,
 };

+ 3 - 0
FAQ.md

@@ -16,3 +16,6 @@ The extension collects the video id of the video you are watching, fetches the d
 
 ### **4. What will happen after the YouTube API stops returning the dislike count?**
 The backend will switch to using a combination of archived dislike stats, estimates extrapolated from extension user data and estimates based on view/like ratios for videos whose dislikes weren't archived and for outdated dislike archives.
+
+## I have security / privacy concerns
+See [this page](SECURITY-FAQ.md) for more info.

+ 3 - 1
README.md

@@ -52,5 +52,7 @@ Please read the [contribution guide.](https://github.com/Anarios/return-youtube-
 
 trig404
 
-Peter33
+[Peter33](https://www.youtube.com/watch?v=G5kzUpWAusI)
+
+[Seed4.Me VPN](https://www.seed4.me/users/register?gift=ReturnYoutubeDislike)
 

+ 30 - 0
SECURITY-FAQ.md

@@ -0,0 +1,30 @@
+# Security
+
+### Are you tracking my viewing history?
+
+No. The extension's code is public and you can see it for yourself. The only information being sent is the video ID, which is required to fetch the dislike count for the videos. There are no additional headers being sent. Over the communication layer, your public IP will be exposed to the server, as well as the time when the request was made. However, none of these are uniquely identifying you in any way. Assuming a zero-trust environment, the best we could get is a dynamic IP. Which, today is yours, tomorrow is your neighbor's. If you're really worried about your IP being traced, you probably already use a VPN.
+
+### Can you uniquely identify me if I dislike?
+
+Yes. When you dislike a video, we create a randomly generated unique ID for you that is not tied to your Google account. This is done to prevent botting.
+
+### What information do you have, exactly?
+
+Just the video ID. Not your comments, not your username, not who you've shared the video with, not any additional metadata. Nothing. Just the video ID. 
+
+### How is my IP stored?
+
+The backend keeps IP addresses in volatile memory (RAM) only. These addresses aren't stored on a hard drive, and therefore aren't logged. We hash the IP addresses, and that's stored instead. This is done to prevent database vandalism.
+
+### I heard some discussion over OAuth, and access to my YouTube account!
+
+This feature will be optional, and very much opt-in. If you are a YouTube creator, and would like to share your dislike stats with us, you can. The way [OAuth](https://en.wikipedia.org/wiki/OAuth#:~:text=but%20without%20giving%20them%20the%20passwords.) was structured, is it's actually very secure. You can revoke access to your account at any time, and can give very specific permissions to us. We will not ask for any permissions that aren't required. We'll only ask for permissions to view your video stats. 
+
+### How can I trust this dislike count?
+
+We have implemented measures to prevent bot attacks and are gonna continue to work on improving the effectiveness of the bot prevention system: this will help us keep the dislike count as a good representative of the actual count. Of course it will never be 100% accurate so it's up to you to decide whether you trust the count or not.
+
+### Why don't you share the backend code? 
+
+We may share it at some point - but there's really no real reason to share it. It gives a false sense of security - because in a zero-trust system, we could just as well disclose one version but deploy another. There are plenty of reasons to keep the code hidden, specifically, how we battle spam. Hiding/Obfuscating the spam handling code is a fairly standard practice.
+

+ 10 - 0
extension-description-store.txt

@@ -0,0 +1,10 @@
+Return YouTube Dislike restores the ability to see dislikes on YouTube.
+Starting December 13th 2021, YouTube removed the ability to see dislikes from their API.
+This extension aims to restore power to users by using a combination of archived like and dislike data, as well as the likes and dislikes made by extension users to show the most accurate ratings.
+    Currently has 200+ million videos likes/dislikes data stored before December 13th, 2021
+    Actively growing and keeping up to date with uploads after December 13th, 2021
+    The more users that use the extension, the more accurate it will be
+    Unpopular videos uploaded after December 13th, 2021 may have less accurate data shown than more popular videos.
+
+This extension is currently in an active development phase, so if you experience any issues, don’t hesitate to report them on our GitHub page or in our Discord server.
+More features to come soon!