Scrolller.com Autoplay Feed

Autoplay Videos in Feed on Scrolller.com

  1. // ==UserScript==
  2. // @name Scrolller.com Autoplay Feed
  3. // @name:de Scrolller.com Automatische Wiedergabe im Feed
  4. // @version 1.0.1
  5. // @description Autoplay Videos in Feed on Scrolller.com
  6. // @description:de Spiele Videos im Feed automatisch ab auf Scrolller.com
  7. // @icon https://scrolller.com/assets/favicon-16x16.png
  8. // @author TalkLounge (https://github.com/TalkLounge)
  9. // @namespace https://github.com/TalkLounge/scrolller.com-autoplay-feed
  10. // @license MIT
  11. // @match https://scrolller.com/*
  12. // @grant none
  13. // @run-at document-start
  14. // ==/UserScript==
  15.  
  16. (function () {
  17. 'use strict';
  18.  
  19. let interval, muted = true;
  20. let cooldown = Date.now();
  21.  
  22. function video2SVGParent(video) {
  23. return video.parentNode.parentNode.parentNode;
  24. }
  25.  
  26. function insertSound(parent, mute) {
  27. //console.log("insertSound()", parent, mute);
  28. if (!parent || [...parent.classList].includes("noaudio") || ![...parent.classList].includes("loaded")) {
  29. return;
  30. }
  31.  
  32. let html;
  33. if (mute) {
  34. html = `
  35. <svg class="sound muted" viewBox="0 0 512 512" style="fill: grey; position: absolute; z-index: 1; width: 1.5em; cursor: pointer; margin-left: 0.2em">
  36. <path d="M232 416a23.88 23.88 0 01-14.2-4.68 8.27 8.27 0 01-.66-.51L125.76 336H56a24 24 0 01-24-24V200a24 24 0 0124-24h69.75l91.37-74.81a8.27 8.27 0 01.66-.51A24 24 0 01256 120v272a24 24 0 01-24 24zm-106.18-80zm-.27-159.86zM320 336a16 16 0 01-14.29-23.19c9.49-18.87 14.3-38 14.3-56.81 0-19.38-4.66-37.94-14.25-56.73a16 16 0 0128.5-14.54C346.19 208.12 352 231.44 352 256c0 23.86-6 47.81-17.7 71.19A16 16 0 01320 336z"></path>
  37. <path d="M368 384a16 16 0 01-13.86-24C373.05 327.09 384 299.51 384 256c0-44.17-10.93-71.56-29.82-103.94a16 16 0 0127.64-16.12C402.92 172.11 416 204.81 416 256c0 50.43-13.06 83.29-34.13 120a16 16 0 01-13.87 8z"></path>
  38. <path d="M416 432a16 16 0 01-13.39-24.74C429.85 365.47 448 323.76 448 256c0-66.5-18.18-108.62-45.49-151.39a16 16 0 1127-17.22C459.81 134.89 480 181.74 480 256c0 64.75-14.66 113.63-50.6 168.74A16 16 0 01416 432z"></path>
  39. </svg>`;
  40. } else {
  41. html = `
  42. <svg class="sound volume" viewBox="0 0 512 512" style="fill: white; position: absolute; z-index: 1; width: 1.5em; cursor: pointer; margin-left: 0.2em">
  43. <path d="M232 416a23.88 23.88 0 01-14.2-4.68 8.27 8.27 0 01-.66-.51L125.76 336H56a24 24 0 01-24-24V200a24 24 0 0124-24h69.75l91.37-74.81a8.27 8.27 0 01.66-.51A24 24 0 01256 120v272a24 24 0 01-24 24zm-106.18-80zm-.27-159.86zM320 336a16 16 0 01-14.29-23.19c9.49-18.87 14.3-38 14.3-56.81 0-19.38-4.66-37.94-14.25-56.73a16 16 0 0128.5-14.54C346.19 208.12 352 231.44 352 256c0 23.86-6 47.81-17.7 71.19A16 16 0 01320 336z"></path>
  44. <path d="M368 384a16 16 0 01-13.86-24C373.05 327.09 384 299.51 384 256c0-44.17-10.93-71.56-29.82-103.94a16 16 0 0127.64-16.12C402.92 172.11 416 204.81 416 256c0 50.43-13.06 83.29-34.13 120a16 16 0 01-13.87 8z"></path>
  45. <path d="M416 432a16 16 0 01-13.39-24.74C429.85 365.47 448 323.76 448 256c0-66.5-18.18-108.62-45.49-151.39a16 16 0 1127-17.22C459.81 134.89 480 181.74 480 256c0 64.75-14.66 113.63-50.6 168.74A16 16 0 01416 432z"></path>
  46. </svg>`;
  47. }
  48. html = html.replace(/>\s+</g, '><').trim(); // Clean up formatted html, Thanks to https://stackoverflow.com/a/27841683
  49. const child = new DOMParser().parseFromString(html, "text/html");
  50.  
  51. child.body.firstChild.addEventListener("click", (e) => {
  52. e.stopPropagation();
  53.  
  54. const item = e.target.closest(".sound");
  55. //console.log("Clicked", item, [...item.classList].includes("muted"));
  56.  
  57. document.querySelectorAll("video").forEach(video => {
  58. if (!video.muted) {
  59. const svg = video2SVGParent(video).querySelector(".sound");
  60. //console.log("Loud", video, svg, svg.parentNode);
  61. insertSound(svg.parentNode, true);
  62. svg.remove();
  63. }
  64.  
  65. video.muted = true;
  66. });
  67.  
  68. if ([...item.classList].includes("muted")) {
  69. //console.log("Muted");
  70. muted = false;
  71.  
  72. item.parentNode.querySelector("video").muted = false;
  73.  
  74. insertSound(item.parentNode);
  75. } else {
  76. //console.log("Unmuted");
  77. muted = true;
  78.  
  79. insertSound(item.parentNode, true);
  80. }
  81.  
  82. item.remove();
  83. });
  84.  
  85. parent.insertBefore(child.body.firstChild, parent.firstChild);
  86. }
  87.  
  88. async function loadVideo(parent) {
  89. const child = parent.firstChild;
  90. child.querySelector(".media-icon").remove();
  91.  
  92. let data = await fetch(parent.href);
  93. data = await data.text();
  94. data = new DOMParser().parseFromString(data, "text/html");
  95. data = [...data.querySelectorAll("head script")];
  96. data = data.find(item => item.innerText.includes("window.scrolllerConfig"));
  97. data = data.textContent;
  98. data = data.replace("window.scrolllerConfig=", "");
  99. data = data.replace(/\\'/g, "'");
  100. data = JSON.parse(JSON.parse(data));
  101. data = data.item.mediaSources;
  102. data = data.filter(item => item.url.endsWith(".mp4"));
  103. data = data.reverse();
  104. data = data.map(item => item.url);
  105.  
  106. const video = document.createElement("video");
  107. video.autoplay = true;
  108. video.muted = true;
  109. video.loop = true;
  110. video.style.height = "100%";
  111. video.style.position = "absolute";
  112. video.addEventListener("loadeddata", () => {
  113. child.querySelector("img").remove();
  114.  
  115. const hasAudio = video.mozHasAudio || Boolean(video.webkitAudioDecodedByteCount) || Boolean(video.audioTracks && video.audioTracks.length);
  116.  
  117. if (!hasAudio) {
  118. //console.log("GIF", video);
  119. video2SVGParent(video).classList.add("noaudio");
  120. }
  121.  
  122. video2SVGParent(video).classList.add("loaded");
  123.  
  124. insertSound(child.parentNode.parentNode, true);
  125.  
  126. /*parent.onmouseenter = () => {
  127. console.log("Mouse Enter");
  128. video.muted = false;
  129. };
  130.  
  131. parent.onmouseleave = () => {
  132. console.log("Mouse Leave");
  133. video.muted = true;
  134. };*/
  135. });
  136.  
  137. for (const src of data) {
  138. const source = document.createElement("source");
  139. source.src = src;
  140. video.append(source);
  141. }
  142.  
  143. child.insertBefore(video, child.firstChild);
  144. }
  145.  
  146. function loadVideos() {
  147. const items = document.querySelectorAll(".vertical-view__item-container .media-icon");
  148.  
  149. for (const item of items) {
  150. const parent = item.closest(".vertical-view__item-container a");
  151.  
  152. loadVideo(parent);
  153. }
  154. }
  155.  
  156. async function init() {
  157. if (!document.querySelector("main.gallery-view .vertical-view__columns")) {
  158. return;
  159. }
  160.  
  161. window.clearInterval(interval);
  162.  
  163. window.setInterval(loadVideos, 500);
  164.  
  165. document.body.onscroll = function () {
  166. if (muted) {
  167. return;
  168. }
  169.  
  170. if (Date.now() - cooldown < 250) {
  171. return;
  172. }
  173. cooldown = Date.now();
  174.  
  175. //console.log("Scroll");
  176. let diffMin = 1000000;
  177. let nearest;
  178. let middle = window.innerHeight / 2;
  179.  
  180. document.querySelectorAll("video").forEach(video => {
  181. const loud = video2SVGParent(video).querySelector(".sound:not(.muted)");
  182. if (loud) {
  183. loud.remove();
  184. insertSound(video2SVGParent(video), true);
  185. }
  186. video.muted = true;
  187. const rect = video.getBoundingClientRect();
  188. const elemMiddle = rect.y + (rect.height / 2);
  189. const diff = Math.abs(middle - elemMiddle);
  190. if (diff < diffMin) {
  191. diffMin = diff;
  192. nearest = video;
  193. }
  194. });
  195.  
  196. if (!nearest || diffMin > middle || video2SVGParent(nearest).querySelector(".sound:not(.muted)")) {
  197. return;
  198. }
  199.  
  200. nearest.muted = false;
  201.  
  202. video2SVGParent(nearest).querySelector(".sound")?.remove();
  203. insertSound(video2SVGParent(nearest));
  204. }
  205. }
  206.  
  207. interval = window.setInterval(init, 500);
  208. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址