unlock lpsg videos

unlock lpsg video access. may need to manually switch video format since I can't figure that part out. Also load all images ASAP instead of having to scroll to them. Also added a button on top left to hide user info, so you can browse the page more quickly.

  1. // ==UserScript==
  2. // @name unlock lpsg videos
  3. // @namespace MBing
  4. // @version 5.0
  5. // @description unlock lpsg video access. may need to manually switch video format since I can't figure that part out. Also load all images ASAP instead of having to scroll to them. Also added a button on top left to hide user info, so you can browse the page more quickly.
  6. // @author MBing
  7. // @match https://www.lpsg.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=lpsg.com
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. //#隐藏用户信息的部分
  17. // 用于存储用户的选择状态
  18. const storageKey = 'hideUserInfo';
  19.  
  20. // 动态获取当前的隐藏状态
  21. function getIsHidden() {
  22. return localStorage.getItem(storageKey) === 'true';
  23. }
  24.  
  25. // 添加开关按钮
  26. const toggleButton = document.createElement('button');
  27. toggleButton.style.position = 'fixed';
  28. toggleButton.style.top = '10px';
  29. toggleButton.style.left = '10px';
  30. toggleButton.style.zIndex = '999999';
  31. toggleButton.style.padding = '5px 10px';
  32. toggleButton.style.fontSize = '14px';
  33. toggleButton.style.backgroundColor = '#4CAF50';
  34. toggleButton.style.color = 'white';
  35. toggleButton.style.border = 'none';
  36. toggleButton.style.borderRadius = '5px';
  37. toggleButton.style.cursor = 'pointer';
  38.  
  39. // 初始按钮文本
  40. toggleButton.textContent = getIsHidden() ? 'Show User Info' : 'Hide User Info';
  41.  
  42. // 添加按钮到页面
  43. document.body.appendChild(toggleButton);
  44.  
  45. // 切换隐藏状态的函数
  46. function toggleVisibility() {
  47. const newState = !getIsHidden(); // 获取新的状态
  48.  
  49. // 更新本地存储
  50. localStorage.setItem(storageKey, newState.toString());
  51.  
  52. // 更新按钮文本
  53. toggleButton.textContent = newState ? 'Show User Info' : 'Hide User Info';
  54.  
  55. // 更新元素的显示状态
  56. document.querySelectorAll('.message-userExtras').forEach(element => {
  57. element.style.display = newState ? 'none' : '';
  58. });
  59. }
  60.  
  61. // 初始状态设置
  62. if (getIsHidden()) {
  63. document.querySelectorAll('.message-userExtras').forEach(element => {
  64. element.style.display = 'none';
  65. });
  66. }
  67.  
  68. // 绑定点击事件
  69. toggleButton.addEventListener('click', toggleVisibility);
  70.  
  71.  
  72. //#改变图片加载速度的部分
  73. // 获取所有包含 lazy loading 的图片
  74. const images = document.querySelectorAll('img[loading="lazy"]');
  75.  
  76. // 遍历所有懒加载图片,将其 loading 属性更改为 "eager"
  77. images.forEach(img => {
  78. img.loading = 'eager'; // 立刻加载图片
  79. });
  80.  
  81.  
  82. //#替换视频的部分
  83. var easterEggPoster = document.getElementsByClassName("video-easter-egg-poster");
  84. var volume=0.0;
  85. var autoSwitchInterval=2500
  86. var videoDiv=[];
  87. var imageUrl;
  88. var newDiv;
  89. var baseVideoUrl;
  90.  
  91. if(easterEggPoster.length==0){
  92. //console.log("官方时间,无需替换");
  93. return;
  94. }else if(easterEggPoster.length>5){
  95. autoSwitchInterval=5000;
  96. }
  97.  
  98. // 控制是否继续切换后缀
  99. let shouldContinue = true;
  100.  
  101. //替换预览照片为视频播放器
  102. for (let i=easterEggPoster.length-1;i>-1;i--){
  103. let videoUrl
  104. imageUrl =easterEggPoster[i].children[0].src;
  105. videoUrl=imageUrl.replace("attachments/posters","video").replace("/lsvideo/thumbnails","lsvideo/videos").replace(".jpg",".mp4");
  106.  
  107.  
  108. videoDiv[i]=`<video onloadstart="this.volume=${volume}" style="width:750px; max-height: 750px;" controls=\"\" data-xf-init=\"video-init\" data-poster=\"${imageUrl}\" class=\"\" style=\"\" poster=\"${imageUrl}\"><source data-src=\"${videoUrl}\" src=\"${videoUrl}\"><div class=\"bbMediaWrapper-fallback\">Your browser is not able to display this video.</div></video>`;
  109. //void(easterEggPoster[i].innerHTML=videoDiv[i]);
  110.  
  111. newDiv = document.createElement("div");
  112. newDiv.setAttribute("class","newVideoDiv");
  113. newDiv.innerHTML=videoDiv[i];
  114. easterEggPoster[i].parentElement.parentElement.append(newDiv);
  115. //console.log("正在插入第几条:"+i+",url:"+videoUrl)
  116. easterEggPoster[i].parentElement.parentElement.append(createButton("mov",i));
  117. easterEggPoster[i].parentElement.parentElement.append(createButton("m4v",i));
  118. easterEggPoster[i].parentElement.parentElement.append(createButton("mp4",i));
  119. }
  120.  
  121. //删除easterEggPoster
  122. for (let i=easterEggPoster.length-1;i>-1;i--){
  123. easterEggPoster[i].parentElement.parentElement.removeChild(easterEggPoster[i].parentElement);
  124. }
  125.  
  126. //删除video-easter-egg-blocker
  127. var easterEggBlocker = document.getElementsByClassName("video-easter-egg-blocker");
  128. for (let i=easterEggBlocker.length-1;i>-1;i--){
  129. easterEggBlocker[i].parentElement.removeChild(easterEggBlocker[i]);
  130. }
  131.  
  132. //删除video-easter-egg-overlay
  133. var easterEggOverlay = document.getElementsByClassName("video-easter-egg-overlay");
  134. for (let i=easterEggOverlay.length-1;i>-1;i--){
  135. easterEggOverlay[i].parentElement.removeChild(easterEggOverlay[i]);
  136. }
  137.  
  138. //调小音量
  139. var allVideoPlayers = document.getElementsByTagName('video');
  140. for (let i=allVideoPlayers.length-1;i>-1;i--){
  141. allVideoPlayers[i].volume = volume;
  142. }
  143.  
  144. // 延时后开始检查视频
  145. setTimeout(checkVideosAndUpdate, autoSwitchInterval);
  146.  
  147.  
  148. //创建按钮
  149. function createButton(format,entryId){
  150. var inp;
  151. inp = document.createElement("input");
  152. inp.type = "button";
  153. inp.value = format;
  154. inp.id = entryId;
  155. inp.addEventListener('click', function () {
  156. //点击任何手动切换后缀按钮,即停止自动切换后缀
  157. shouldContinue = false;
  158. //console.log('停止切换后缀');
  159. //切换后缀
  160. var oldUrl = document.getElementsByClassName("newVideoDiv")[this.id].innerHTML;
  161. document.getElementsByClassName("newVideoDiv")[this.id].innerHTML=oldUrl.replaceAll("mp4",format).replaceAll("m4v",format).replaceAll("mov",format);
  162. });
  163. return inp;
  164. }
  165.  
  166.  
  167. // 定义一个函数,用于遍历所有 video 元素并检查其 readyState
  168. function checkVideosAndUpdate() {
  169. // 如果点击了停止按钮,则不再继续执行
  170. if (!shouldContinue) return;
  171.  
  172. // 获取页面上所有的 video 元素
  173. const videoElements = document.querySelectorAll('video');
  174.  
  175. let allLoaded = true;
  176. let failCount=0;
  177. // 遍历所有 video 元素
  178. videoElements.forEach(videoElement => {
  179. // 获取当前视频的 readyState
  180. const state = videoElement.readyState;
  181.  
  182. // 如果视频的 readyState 不是 4(HAVE_ENOUGH_DATA),说明视频还没有加载完成
  183. if (state !== 4) {
  184. failCount+=1;
  185. allLoaded = false;
  186.  
  187. // 获取视频中的所有 source 标签
  188. const sources = videoElement.querySelectorAll('source');
  189. sources.forEach(source => {
  190. // 获取当前 source 的 src 和 data-src 属性
  191. const currentSrc = source.src || source.getAttribute('data-src');
  192.  
  193. // 根据原视频格式替换为对应的新格式
  194. let newSrc = currentSrc;
  195. if (currentSrc.endsWith('.mp4')) {
  196. newSrc = currentSrc.replace('.mp4', '.mov');
  197. } else if (currentSrc.endsWith('.mov')) {
  198. newSrc = currentSrc.replace('.mov', '.m4v');
  199. } else if (currentSrc.endsWith('.m4v')) {
  200. newSrc = currentSrc.replace('.m4v', '.mp4');
  201. }
  202.  
  203. // 更新 source 的 src 和 data-src 属性为新的链接
  204. //console.log(`替换视频链接: ${currentSrc} 为 ${newSrc}`);
  205. source.src = newSrc;
  206. source.setAttribute('data-src', newSrc);
  207. });
  208.  
  209. // 重新加载视频
  210. videoElement.load();
  211. }
  212. });
  213.  
  214. // 如果还有未加载完成的 video,则延时 2 秒再次执行
  215. if (!allLoaded) {
  216. //console.log(failCount+"条视频加载失败,稍后切换后缀重试");
  217. setTimeout(checkVideosAndUpdate, autoSwitchInterval);
  218. } else {
  219. //console.log("所有视频都已加载完成!");
  220. }
  221. }
  222.  
  223.  
  224. })();