Invidify Youtube

Werbung auf Youtube umgehen, indem Video-Links direkt auf Invidious verweisen

目前为 2023-11-06 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Invidify Youtube
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Werbung auf Youtube umgehen, indem Video-Links direkt auf Invidious verweisen
  6. // @author Thomas Theiner
  7. // @match https://www.youtube.com/*
  8. // @match https://youtube.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  10. // @grant none
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. window.addEventListener("load", e => {
  18. console.log("INVIDIFY initialisiert");
  19.  
  20. let configButton = document.createElement("button");
  21. configButton.type = "button";
  22. configButton.innerHTML = `<img src="https://websocket.bplaced.net/gear.png" />`;
  23. configButton.style.cssText = `z-index: 3000;
  24. background-color: #dddddd;
  25. position: fixed;
  26. top: 15px;
  27. left: 270px;
  28. border-radius: 3px;
  29. border-color: #dddddd;
  30. padding: 2px 10px 2px 10px;`;
  31.  
  32. let newButton = document.createElement("button");
  33. newButton.type = "button";
  34. newButton.innerText = "Invidify";
  35.  
  36. newButton.style.cssText = `z-index: 3000;
  37. background-color: #dddddd;
  38. position: fixed;
  39. top: 15px;
  40. left: 200px;
  41. border-radius: 3px;
  42. border-color: #dddddd;
  43. padding: 5px 10px 5px 10px;`;
  44.  
  45. let bodyEl = document.querySelector("body");
  46. bodyEl.prepend(configButton);
  47. bodyEl.prepend(newButton);
  48.  
  49. let instanceURL = window.localStorage.getItem("invidiousInstance");
  50. if(!instanceURL) {
  51. instanceURL = "https://yt.oelrichsgarcia.de/";
  52. window.localStorage.setItem("invidiousInstance", instanceURL);
  53. }
  54.  
  55. newButton.addEventListener("click", e => {
  56.  
  57. // get the correct ytd-browse element
  58. let currentPage = window.location.href;
  59.  
  60. let ytdBrowse = "home";
  61. let relExclude = "";
  62. if(currentPage.includes("feed")) {
  63. ytdBrowse = currentPage.match(/\/feed\/(.*)$/)[1];
  64. if(ytdBrowse.includes("?")) {
  65. ytdBrowse = ytdBrowse.match(/(.*)\?/)[1];
  66. }
  67. } else if(currentPage.includes("/watch?v=")) {
  68. ytdBrowse = "";
  69. relExclude = "[rel]:not([rel='null'])";
  70. } else if(currentPage.includes("gaming")) {
  71. ytdBrowse = "channels";
  72. } else if(currentPage.includes("UCYfdidRxbB8Qhf0Nx7ioOYw")) {
  73. ytdBrowse = "news";
  74. } else if(currentPage.includes("UCEgdi0XIXXZ-qJOFPf4JSKw")) {
  75. ytdBrowse = "sports";
  76. } else if(currentPage.includes("UCtFRv9O2AHqOZjjynzrv-xg")) {
  77. ytdBrowse = "learning";
  78. } else if(currentPage.includes("UC4R8DWoMoI7CAwX8_LjQHig")) {
  79. ytdBrowse = "live";
  80. } else if(currentPage.includes("/@") || currentPage.includes("/channel/")) {
  81. ytdBrowse = "channels";
  82. }
  83.  
  84. console.log("currentPage", currentPage, "Section", ytdBrowse, "relExclude", relExclude);
  85.  
  86. let allLinks = ytdBrowse ? document.querySelector(`ytd-browse[page-subtype=${ytdBrowse}]`).querySelectorAll(`a.ytd-thumbnail${relExclude}`) : document.querySelector("ytd-watch-flexy").querySelectorAll(`a.ytd-thumbnail${relExclude}`);
  87.  
  88. let infoLookup = {};
  89. let linkCollection = new Set();
  90. for(let link of allLinks) {
  91.  
  92. if(link.href.includes("/watch")) {
  93. let videoID = link.href.match(/\/watch\?v\=(.*)$/)[1];
  94. if(videoID.includes("&")) {
  95. videoID = videoID.match(/(.*)\&/)[1];
  96. }
  97. let image = link.querySelector("img");
  98. if(image) {
  99. if(!image.src) {
  100. image.src = `https://i.ytimg.com/vi/${videoID}/hqdefault.jpg`;
  101. }
  102. linkCollection.add(videoID);
  103.  
  104. let details = link.parentNode.parentNode.parentNode.querySelector("#details");
  105.  
  106. if(!details) {
  107. details = link.parentNode.parentNode.querySelector(".details");
  108.  
  109. if(!details) {
  110. details = link.parentNode.parentNode.querySelector("#meta");
  111. }
  112. }
  113.  
  114. let title = details.querySelector("#video-title").innerText;
  115.  
  116. let channelInfo = details.querySelector("#channel-name");
  117. let channelName = channelInfo.innerText;
  118. let channelLink = channelInfo.querySelector("a")?.href;
  119.  
  120. let metaData = details.querySelector("#metadata-line").innerText;
  121.  
  122. let timeStatus = link.querySelector("#time-status")?.innerText;
  123. // add Info
  124. infoLookup[videoID] = {
  125. imageSrc: image.src,
  126. title: title,
  127. channelName: channelName,
  128. channelLink: channelLink?.match(/\/(\@.*)/) ? channelLink?.match(/\/(\@.*)/)[1] : channelLink?.match(/\/(channel.*)/)[1],
  129. metaData: metaData,
  130. length: timeStatus
  131. };
  132. }
  133. }
  134.  
  135. }
  136. let videoIDs = Array.from(linkCollection);
  137.  
  138. // prepend current videoID
  139. if(currentPage.includes("/watch?v=")) {
  140. let videoID = currentPage.match(/\/watch\?v\=(.*)$/)[1];
  141. if(videoID.includes("&")) {
  142. videoID = videoID.match(/(.*)\&/)[1];
  143. }
  144. videoIDs.unshift(videoID);
  145.  
  146. let primaryVideo = document.querySelector("#primary.ytd-watch-flexy");
  147.  
  148. let metaInfo = primaryVideo.querySelector("#below #view-count").getAttribute("aria-label") + primaryVideo.querySelector("#below #date-text").getAttribute("aria-label");
  149. if(primaryVideo.querySelector("#info-container #info")) {
  150. metaInfo += " " + primaryVideo.querySelector("#info-container #info").innerText;
  151. }
  152.  
  153. infoLookup[videoID] = {
  154. imageSrc: `https://i.ytimg.com/vi/${videoID}/hqdefault.jpg`,
  155. title: primaryVideo.querySelector("#below #title").innerText,
  156. channelName: primaryVideo.querySelector("#below #channel-name").innerText,
  157. channelLink: primaryVideo.querySelector("#below #channel-name").querySelector("a").href.match(/\/(\@.*)/)[1],
  158. metaData: metaInfo,
  159. length: "-:--"
  160. };
  161. }
  162.  
  163. // opening new window/tab
  164. let newWindow = window.open("", "_blank");
  165. let newDocument = newWindow.document;
  166.  
  167. newDocument.body.style.cssText = `font-family: Roboto, Arial, sans-serif;`;
  168.  
  169. let newLogoDiv = document.createElement("div");
  170. newLogoDiv.style.cssText = "margin-bottom: 20px;";
  171.  
  172. let newLogoImage = document.createElement("img");
  173. newLogoImage.src = "https://websocket.bplaced.net/invidify.png";
  174. newLogoDiv.appendChild(newLogoImage);
  175.  
  176. newDocument.body.appendChild(newLogoDiv);
  177.  
  178. for(let videoID of videoIDs) {
  179.  
  180. let newCard = document.createElement("div");
  181. newCard.style.cssText = `position: relative; margin: 10px; border-radius: 5px; width: 310px; height: 350px; display: inline-block; vertical-align: top;`;
  182.  
  183. let newLink = newDocument.createElement("a");
  184. newLink.href = `${instanceURL}watch?v=${videoID}`;
  185. newLink.target = "_blank";
  186. newLink.style.cssText = "text-decoration: none; color: black;";
  187. newLink.title = infoLookup[videoID].title;
  188.  
  189. let newImage = newDocument.createElement("img");
  190. newImage.src = infoLookup[videoID].imageSrc;
  191. newImage.width = "310";
  192. newImage.style.cssText = "border-radius: 12px;";
  193. newLink.appendChild(newImage);
  194.  
  195. if(infoLookup[videoID].length) {
  196. let newTimeBox = newDocument.createElement("div");
  197. newTimeBox.style.cssText = "color: white; background-color: black; border-radius: 4px; position: relative; float:right; top: -23px; padding: 2px; font-size: 0.75rem; margin-right: 5px;";
  198. newTimeBox.innerText = infoLookup[videoID].length;
  199.  
  200. newLink.appendChild(newTimeBox);
  201. }
  202.  
  203. let newTitleBox = document.createElement("div");
  204. newTitleBox.style.cssText = "position: relative; height: 3.2rem;";
  205.  
  206. let newNode = document.createElement("h3");
  207. newNode.style.cssText = "font-size: 1.1rem; line-height: 1.5rem; font-weight: 600; max-height: 3rem; overflow: hidden; text-overflow: ellipsis; -webkit-box-orient: vertical; display: -webkit-box; -webkit-line-clamp: 2;";
  208. newNode.innerText = infoLookup[videoID].title;
  209.  
  210. newTitleBox.appendChild(newNode);
  211. newLink.appendChild(newTitleBox);
  212.  
  213. newCard.appendChild(newLink);
  214.  
  215. let newChannel = document.createElement("h4");
  216. newChannel.style.cssText = "font-size: 0.9rem; font-weight: 100; color: #737373;";
  217. newChannel.innerHTML = infoLookup[videoID].channelName + "<br/>" + infoLookup[videoID].metaData;
  218.  
  219. if(infoLookup[videoID].channelLink) {
  220. let newChannelLink = document.createElement("a");
  221. newChannelLink.style.cssText = "text-decoration: none;";
  222. newChannelLink.target = "_blank";
  223. newChannelLink.href = `${instanceURL}${infoLookup[videoID].channelLink}`;
  224.  
  225. newChannelLink.appendChild(newChannel);
  226. newCard.appendChild(newChannelLink);
  227. } else {
  228. newCard.appendChild(newChannel);
  229. }
  230. newDocument.body.appendChild(newCard);
  231. }
  232.  
  233. }, false);
  234.  
  235. let isOpen = false;
  236.  
  237. configButton.addEventListener("click", e => {
  238.  
  239. if(!isOpen) {
  240. isOpen = true;
  241. instanceURL = window.localStorage.getItem("invidiousInstance");
  242.  
  243. let configDiv = document.createElement("div");
  244. configDiv.id = "configDiv";
  245. configDiv.style.cssText = `z-index: 3000;
  246. background-color: #f3f3f3;
  247. position: fixed;
  248. top: 50px;
  249. left: 270px;
  250. border-radius: 3px;
  251. border: 1px solid #000000;
  252. font-size: 1.2rem;
  253. padding: 20px;`;
  254.  
  255. let configInput = document.createElement("input");
  256. configInput.size = "50";
  257. configInput.id = "invidiousConfig";
  258. configInput.type = "text";
  259. configInput.style.cssText = "margin-bottom: 20px;";
  260. configInput.value = instanceURL;
  261.  
  262. let configLabel = document.createElement("label");
  263. configLabel.htmlFor = "invidiousConfig";
  264. configLabel.innerText = "Invidious Instance Domain or URL";
  265.  
  266. let configBr = document.createElement("br");
  267.  
  268. let configButtonDiv = document.createElement("div");
  269.  
  270. let configOk = document.createElement("button");
  271. configOk.type = "button";
  272. configOk.innerText = "Ok";
  273. configOk.style.cssText = "margin-right: 10px;";
  274.  
  275. let configCancel = document.createElement("button");
  276. configCancel.type = "button";
  277. configCancel.innerText = "Cancel";
  278.  
  279. configButtonDiv.appendChild(configOk);
  280. configButtonDiv.appendChild(configCancel);
  281.  
  282. configDiv.appendChild(configLabel);
  283. configDiv.appendChild(configBr);
  284. configDiv.appendChild(configInput);
  285. configDiv.appendChild(configButtonDiv);
  286.  
  287. document.body.prepend(configDiv);
  288.  
  289. function closeConfig() {
  290. configDiv.parentNode.removeChild(configDiv);
  291. isOpen = false;
  292. }
  293.  
  294. configOk.addEventListener("click", btnE => {
  295. instanceURL = configInput.value;
  296. if(!instanceURL.startsWith("https://")) {
  297. instanceURL = "https://" + instanceURL;
  298. }
  299. if(!instanceURL.endsWith("/")) {
  300. instanceURL += "/";
  301. }
  302. window.localStorage.setItem("invidiousInstance", instanceURL);
  303. closeConfig();
  304. }, false);
  305.  
  306. configCancel.addEventListener("click", btnE => {
  307. closeConfig();
  308. }, false);
  309.  
  310. }
  311. }, false);
  312. }, false);
  313. })();