Unbreak Snapchat web. Disable focus tracking, screenshot prevention, and enable video downloads

Improve the Snapchat web experience by disabling screenshot prevention features which don't prevent screenshots but do actively harm the usability. And enabling video downloads

  1. // ==UserScript==
  2. // @name Unbreak Snapchat web. Disable focus tracking, screenshot prevention, and enable video downloads
  3. // @name:zh-CN 修复Snapchat网络版:禁用焦点追踪、屏幕截图防护,并启用视频下载
  4. // @namespace http://tampermonkey.net/
  5. // @version 0.1.4.1
  6. // @description Improve the Snapchat web experience by disabling screenshot prevention features which don't prevent screenshots but do actively harm the usability. And enabling video downloads
  7. // @description:zh-CN 通过禁用防护功能并启用视频下载,改善Snapchat网络版的体验。
  8. // @homepage https://gist.github.com/varenc/20e4dbfe8e7a2cc305043ffcbc5454d0
  9. // @author https://github.com/varenc
  10. // @match https://web.snapchat.com/*
  11. // @match https://www.snapchat.com/web/*
  12. // @icon http://snapchat.com/favicon.ico
  13. // @license MIT
  14. // @run-at document-idle
  15. // @grant none
  16. // ==/UserScript==
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. function __unblockControlKeyEvents() {
  22. const events = ["keydown", "keyup", "keypress"];
  23. const modifyKeys = ["Control", "Meta", "Alt", "Shift"];
  24. for (var i = 0; i < events.length; i++) {
  25. var event_type = events[i];
  26. document.addEventListener(
  27. event_type,
  28. function (e) {
  29. // console.log(`${event_type}[${i}]=`, e.key);
  30. if (modifyKeys.includes(e.key)) {
  31. e.preventDefault();
  32. e.stopPropagation();
  33. console.log(`'${event_type}' event for '${e.key}' received and prevented:`, e);
  34. e.stopImmediatePropagation();
  35. }
  36. },
  37. true
  38. );
  39. }
  40. }
  41.  
  42. function __unblockEvent() {
  43. for (var i = 0; i < arguments.length; i++) {
  44. var event_type = arguments[i];
  45. document.addEventListener(
  46. arguments[i],
  47. function (e) {
  48. // e.preventDefault();
  49. e.stopPropagation();
  50. console.log(`'${event_type}' event received and prevented:`, e);
  51. // e.stopImmediatePropagation();
  52. },
  53. true
  54. );
  55. }
  56. }
  57.  
  58. function __fixConsole() {
  59. // snapchat tries to disable console.log.. how mean. So we copy the real Console object from a new iframe
  60. const iframe = document.createElement("iframe");
  61. iframe.style.display = "none";
  62. document.body.appendChild(iframe);
  63. const nativeConsole = iframe.contentWindow.console;
  64. window.console = nativeConsole;
  65. }
  66.  
  67. function __enableVideoDownloads() {
  68. // Process all videos on the page, making them downloadable and fully functional
  69. document.querySelectorAll("video").forEach((video, index) => {
  70. console.log(`VideoFixer: Processing <video> #${index + 1}:`, video);
  71.  
  72. // Make video visible and interactive
  73. video.style.position = "relative";
  74. video.style.zIndex = "999999";
  75. video.controls = true;
  76. video.style.pointerEvents = "auto";
  77.  
  78. // Track and remove restrictive attributes
  79. const removedAttributes = [];
  80. const removedControlsListItems = [];
  81.  
  82. // Remove attributes that restrict functionality
  83. ["disablePictureInPicture", "disableRemotePlayback"].forEach((attr) => {
  84. if (video.hasAttribute(attr)) {
  85. removedAttributes.push(attr);
  86. video.removeAttribute(attr);
  87. }
  88. });
  89.  
  90. // Remove controlsList restrictions
  91. if (video.hasAttribute("controlsList")) {
  92. removedControlsListItems.push(...video.getAttribute("controlsList").split(/\s+/));
  93. video.removeAttribute("controlsList");
  94. }
  95. });
  96.  
  97. console.log("VideoFixer: All videos processed and fixed!");
  98. }
  99.  
  100. // Set up a MutationObserver to detect new videos being added to the page
  101. function __setupVideoObserver() {
  102. // Create a MutationObserver to watch for new video elements
  103. const videoObserver = new MutationObserver((mutations) => {
  104. let shouldProcess = false;
  105.  
  106. // Check if any videos were added or modified
  107. for (const mutation of mutations) {
  108. if (mutation.type === 'childList') {
  109. const addedVideos = Array.from(mutation.addedNodes).filter(node =>
  110. node.nodeName === 'VIDEO' ||
  111. (node.nodeType === Node.ELEMENT_NODE && node.querySelector('video'))
  112. );
  113.  
  114. if (addedVideos.length > 0) {
  115. shouldProcess = true;
  116. break;
  117. }
  118. } else if (mutation.type === 'attributes' &&
  119. mutation.target.nodeName === 'VIDEO') {
  120. shouldProcess = true;
  121. break;
  122. }
  123. }
  124.  
  125. // If videos were added or modified, process all videos
  126. if (shouldProcess) {
  127. __enableVideoDownloads();
  128. }
  129. });
  130.  
  131. videoObserver.observe(document.body, {
  132. childList: true,
  133. subtree: true,
  134. attributes: true,
  135. attributeFilter: ['src', 'controlsList', 'disablePictureInPicture', 'disableRemotePlayback'],
  136. attributeOldValue: true
  137. });
  138.  
  139. return videoObserver;
  140. }
  141.  
  142. function __setupUnblocker() {
  143. __fixConsole();
  144. __unblockControlKeyEvents();
  145.  
  146. ///////// NOTE /////////
  147. // The below makes right-click work like normal, but it also disables Snapchat's special right click behavior in certain places.
  148. __unblockEvent("contextmenu");
  149.  
  150. // Process any videos that are already on the page
  151. __enableVideoDownloads();
  152. }
  153. console.dir("Snapchat unbreaker running!") // Snapchat doesn't disable `console.dir`... silly Snapchat.
  154. // Wait for the DOM to be fully loaded
  155. if (document.readyState === 'loading') {
  156. document.addEventListener('DOMContentLoaded', () => {
  157. __setupUnblocker();
  158. __setupVideoObserver();
  159. });
  160. } else {
  161. __setupUnblocker();
  162. __setupVideoObserver();
  163. }
  164. // Run a few extra times to ensure our event listeners take priority.
  165. setTimeout(__setupUnblocker, 1000);
  166. setTimeout(__setupUnblocker, 5000);
  167. setTimeout(__setupUnblocker, 10000);
  168.  
  169. // Also set up a periodic check every 30 seconds as a fallback
  170. setInterval(__enableVideoDownloads, 30000);
  171. // Works 90% of the time, but they also use some other method to check focus I haven't tracked down yet.
  172. document.hasFocus = function(){return true}
  173. })();

QingJ © 2025

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