Focus always and replay if video is paused

To learn more faster and efficiently

  1. // ==UserScript==
  2. // @name Focus always and replay if video is paused
  3. // @name:zh-TW 始終保持焦點並在視頻暫停時自動播放
  4. // @namespace http://tampermonkey.net/
  5. // @version 0.4.5
  6. // @description To learn more faster and efficiently
  7. // @description:zh-tw 此腳本旨在提高在線學習效率,透過自動保持視頻播放視窗的焦點並在視頻暫停時自動重播。適用於 `https://iedu.foxconn.com/*` 網站,能夠確保學習過程中視頻連續播放,無需手動干預,特別適合忙碌且希望提高學習效率的用戶。
  8. // @author pjiaquan
  9. // @match https://iedu.foxconn.com/public/user/*
  10. // @match https://iedu.foxconn.com/public/play/*
  11. // @run-at document-start
  12. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  13. // @grant GM.xmlHttpRequest
  14. // @connect us-central1-exam-fc1f9.cloudfunctions.net
  15. // @connect 127.0.0.1
  16. // @license MIT
  17. // ==/UserScript==
  18. (function() {
  19. 'use strict';
  20.  
  21. window.onblur = null;
  22. window.blurred = false;
  23. let projectName = "";
  24. let playingVideoTitle = "";
  25. let isClicked = false;
  26.  
  27. document.hasFocus = () => true;
  28. window.onFocus = () => true;
  29.  
  30. [
  31. "hidden",
  32. "mozHidden",
  33. "msHidden",
  34. "webkitHidden"
  35. ].forEach(prop_name => {
  36. Object.defineProperty(document, prop_name, {value: false});
  37. });
  38.  
  39. Object.defineProperty(document, "visibilityState", {get: () => "visible"});
  40. Object.defineProperty(document, "webkitVisibilityState", {get: () => "visible"});
  41.  
  42. document.onvisibilitychange = undefined;
  43.  
  44. var event_handler = (event) => {
  45. if (["blur", "mouseleave", "mouseout"].includes(event.type) &&
  46. (event.target instanceof HTMLInputElement ||
  47. event.target instanceof HTMLAnchorElement ||
  48. event.target instanceof HTMLSpanElement)) {
  49. return; // exclude input, anchor, and span elements
  50. }
  51. event.preventDefault();
  52. event.stopPropagation();
  53. event.stopImmediatePropagation();
  54. };
  55.  
  56. [
  57. "visibilitychange",
  58. "webkitvisibilitychange",
  59. "blur",
  60. "hasFocus",
  61. "mouseleave",
  62. "mouseout",
  63. "mozvisibilitychange",
  64. "msvisibilitychange"
  65. ].forEach(event_name => {
  66. window.addEventListener(event_name, event_handler, true);
  67. document.addEventListener(event_name, event_handler, true);
  68. });
  69.  
  70. let videoData = {
  71. filename: null,
  72. waresMap: new Map()
  73. };
  74.  
  75. // script1 functionality
  76. function updateVideoProgress() {
  77. // if(sn) {
  78. // console.log(sn);
  79. // }
  80.  
  81. if (!isVideoPlaying()) {
  82. console.log('Video is not playing!');
  83. startPlayingVideo();
  84. } else {
  85.  
  86. // console.log(wares);
  87.  
  88. var videoElement = document.getElementById('realvideo_html5_api');
  89. if (videoElement && videoElement.tagName === 'VIDEO') {
  90. const activeElement = document.querySelector('dl .active');
  91.  
  92. // Check if the active element exists and log its title attribute or inner text
  93. if (activeElement) {
  94. // console.log('Active element found:', activeElement);
  95. // console.log('Title:', activeElement.getAttribute('title'));
  96. playingVideoTitle = activeElement.getAttribute('title');
  97. // console.log('Content:', activeElement.innerText);
  98.  
  99. //console.log('title: ', playingVideoTitle);
  100. } else {
  101. console.log('No active element found');
  102. }
  103.  
  104. // videoElement.muted();
  105. // console.log('#301: video is playing', videoElement);
  106. if(videoElement.src.indexOf("hdvideo") >= 0){
  107. //console.log("您正在播放的是高清版本的");
  108.  
  109. var currentSrc = videoElement.src;
  110.  
  111. if(currentSrc.indexOf() >= 0) {
  112. console.log('you are hd mode');
  113.  
  114. var newSrc = currentSrc.replace('hdvideo', 'sdvideo');
  115.  
  116. videoElement.src = newSrc;
  117.  
  118. videoElement.muted = true;
  119. // Load the new video source and mute it
  120. videoElement.load();
  121. }
  122.  
  123. }
  124.  
  125. if (wares && wares.length > 0) {
  126. //console.log(window.wares);
  127. var i = 0;
  128. for(i = 0; i < wares.length; i++) {
  129. if(wares[i].isComplete === "N") {
  130.  
  131. let v = wares[i];
  132. let duration = v.duration;
  133. let playtime = v.playtime;
  134. let percentage = (playtime / duration) * 100;
  135.  
  136. console.log(`${wares[i].name} 沒完成, 剩下: ${percentage.toFixed(2)}% ${new Date().toLocaleString()} #1`);
  137.  
  138. // Usage example
  139. const progress_data = {
  140. userId: userId,
  141. userName: userName,
  142. // examName: window.wares[i].name,
  143. progress: percentage.toFixed(2),
  144. examName: projectName + ': ' + playingVideoTitle || 'Sample Exam Title' // Replace with actual data
  145. };
  146.  
  147. sendDataToServer(progress_data);
  148.  
  149. // Find the element by its title attribute
  150. // var myElement = document.querySelector('dd[title="六西格玛项目定义"]');
  151. var myElement = document.querySelector(`dd[title="${wares[i].name}"]`);
  152.  
  153. if (myElement) {
  154.  
  155. console.log('found element 1');
  156.  
  157. if(playingVideoTitle !== wares[i].name) {
  158. console.log('found element 1 cliked');
  159. //myElement.click();
  160. }
  161. break; // Exit the loop after the first click
  162. } else {
  163. console.log('Element not found');
  164. }
  165. } else if(wares[i].isComplete === undefined) {
  166. let v = wares[i];
  167. let duration = v.duration;
  168. let playtime = v.playtime;
  169. let percentage = (playtime / duration) * 100;
  170.  
  171. console.log(`${wares[i].name} 沒完成, 剩下: ${percentage.toFixed(2)}% ${new Date().toLocaleString()} #2`);
  172.  
  173. // Usage example
  174. const progress_data2 = {
  175. userId: userId,
  176. userName: userName,
  177. // examName: window.wares[i].name,
  178. progress: percentage.toFixed(2),
  179. examName: projectName + ': ' + playingVideoTitle || 'Sample Exam Title' // Replace with actual data
  180. };
  181.  
  182. sendDataToServer(progress_data2);
  183.  
  184. var myElement2 = document.querySelector(`dd[title="${wares[i].name}"]`);
  185.  
  186. if (myElement2) {
  187. // Trigger the click event on the element
  188. console.log('found element 2');
  189. if(playingVideoTitle !== wares[i].name) {
  190. console.log('found element 2 cliked');
  191. //myElement2.click();
  192. }
  193. break; // Exit the loop after the first click
  194. } else {
  195. console.log('Element not found');
  196. }
  197.  
  198. // if (videoElement.readyState >= 1) { // Check if metadata is loaded
  199. // var duration = videoElement.duration;
  200. // var targetTime = duration * 0.95; // Calculate 95% of the total duration
  201.  
  202. // console.log('Setting currentTime to: ', targetTime);
  203. // videoElement.currentTime = targetTime; // Set the video progress to 95%
  204. // videoElement.play()
  205. // .then(() => console.log("Video playback started at 95%"))
  206. // .catch((error) => console.error("Error attempting to play video:", error));
  207. // } else {
  208. // // Wait for metadata to be loaded before setting currentTime
  209. // videoElement.addEventListener('loadedmetadata', function() {
  210. // var duration = videoElement.duration;
  211. // var targetTime = duration * 0.95; // Calculate 95% of the total duration
  212.  
  213. // console.log('Setting currentTime to: ', targetTime);
  214. // videoElement.currentTime = targetTime; // Set the video progress to 95%
  215. // videoElement.play()
  216. // .then(() => console.log("Video playback started at 95%"))
  217. // .catch((error) => console.error("Error attempting to play video:", error));
  218. // });
  219. // }
  220. } else {
  221.  
  222. // try {
  223. // let v = window.wares[i];
  224. // let duration = v.duration;
  225. // let playtime = v.playtime;
  226. // let percentage = (playtime / duration) * 100;
  227.  
  228. // console.log(`${window.wares[i].name} 已經完成, 剩下: ${percentage.toFixed(2)}%`);
  229.  
  230. // // URL of the Cloud Function endpoint
  231. // const url = 'https://us-central1-exam-fc1f9.cloudfunctions.net/saveProgress';
  232.  
  233. // // Data to be sent
  234. // const data = {
  235. // // userId: userId,
  236. // examName: window.wares[i].name,
  237. // progress: percentage.toFixed(2),
  238. // drink: 'mojito'
  239. // };
  240.  
  241. // // Using GM.xmlHttpRequest to send a POST request with the data
  242. // GM.xmlHttpRequest({
  243. // method: 'POST',
  244. // url: url,
  245. // headers: {
  246. // 'Content-Type': 'application/json'
  247. // },
  248. // data: JSON.stringify(data),
  249. // onload: function(response) {
  250. // // Parse the JSON response
  251. // const result = JSON.parse(response.responseText);
  252. // console.log('Success:', result);
  253. // },
  254. // onerror: function(error) {
  255. // console.error('Error:', error);
  256. // }
  257. // });
  258. // }catch (error) {
  259. // console.log(error);
  260. // }
  261.  
  262. }
  263. }
  264. }
  265. }
  266.  
  267. }
  268. }
  269.  
  270. function setupVideoCheck() {
  271. const activeElement = document.querySelector('.breadcrumb .active');
  272. projectName = activeElement.textContent;
  273.  
  274. //console.log('Page loaded, running script');
  275. setInterval(updateVideoProgress, 5000);
  276.  
  277. // Function to observe DOM changes and click the "确定" button
  278. function observeDOMChanges() {
  279. const targetNode = document.body;
  280. const config = { childList: true, subtree: true };
  281.  
  282. const callback = function(mutationsList, observer) {
  283. for (let mutation of mutationsList) {
  284. if (mutation.type === 'childList') {
  285. const confirmButton = document.querySelector('.layui-layer-btn0');
  286. if (confirmButton) {
  287. confirmButton.click();
  288. console.log('Clicked "确定" button');
  289. observer.disconnect(); // Stop observing after the button is clicked
  290. break;
  291. }
  292. }
  293. }
  294. };
  295.  
  296. const observer = new MutationObserver(callback);
  297. observer.observe(targetNode, config);
  298. }
  299.  
  300. // Start observing for DOM changes
  301. observeDOMChanges();
  302. }
  303.  
  304.  
  305.  
  306. function isVideoPlaying() {
  307. var videoElement = document.getElementById('realvideo_html5_api');
  308. if (videoElement && videoElement.tagName === 'VIDEO') {
  309. // console.log(videoElement);
  310.  
  311. if(videoElement.src.indexOf("hdvideo") >= 0){
  312. //console.log("您正在播放的是高清版本的");
  313.  
  314. var currentSrc = videoElement.src;
  315.  
  316. var newSrc = currentSrc.replace('hdvideo', 'sdvideo');
  317.  
  318. videoElement.muted = true;
  319.  
  320. videoElement.src = newSrc;
  321.  
  322. // Load the new video source and mute it
  323. //videoElement.load();
  324. }
  325.  
  326. // Extract the src attribute
  327. var videoSrc = videoElement.getAttribute("src");
  328.  
  329. // Extract the filename from the src URL
  330. var videoFilename = videoSrc.split('/').pop().split('.')[0];
  331.  
  332. // Save the filename to the videoData object
  333. videoData.filename = videoFilename;
  334.  
  335. // Map the videoFilename with wares if needed
  336. if (wares && wares.length > 0) {
  337.  
  338. wares.forEach((ware, index) => {
  339. videoData.waresMap.set(videoFilename, ware); // Example mapping
  340. });
  341. }
  342.  
  343. // Output the filename or save it as needed
  344. if (videoData && videoData.waresMap) {
  345. const firstEntry = videoData.waresMap.entries().next().value;
  346. if (firstEntry) {
  347. const [firstKey, firstValue] = firstEntry;
  348.  
  349. if(firstValue.isComplete === "Y") {
  350. return true;
  351.  
  352. }
  353. } else {
  354. console.log('Map is empty');
  355.  
  356.  
  357. }
  358. }
  359.  
  360. return !videoElement.paused;
  361. } else {
  362. console.log('Video element not found or is not a video tag');
  363. return null;
  364. }
  365. }
  366.  
  367.  
  368. function startPlayingVideo() {
  369. var videoElement = document.getElementById('realvideo_html5_api');
  370. if (videoElement && videoElement.tagName === 'VIDEO') {
  371. videoElement.muted = true;
  372. videoElement.play()
  373. .then(() => console.log("Video playback started"))
  374. .catch((error) => console.error("Error attempting to play video:", error));
  375.  
  376. } else {
  377. console.log('Video element not found or is not a video tag');
  378. }
  379. }
  380.  
  381. // Define the function to send data using GM.xmlHttpRequest
  382. function sendDataToServer(data) {
  383. const url = 'https://us-central1-exam-fc1f9.cloudfunctions.net/saveProgress';
  384. const testUrl = 'http://127.0.0.1:5001/exam-fc1f9/us-central1/saveProgress';
  385.  
  386. GM.xmlHttpRequest({
  387. method: 'POST',
  388. url: url,
  389. headers: {
  390. 'Content-Type': 'application/json'
  391. },
  392. data: JSON.stringify(data), // JSON stringify the payload
  393. onload: function(response) {
  394. try {
  395. // Parse the JSON response
  396. const result = JSON.parse(response.responseText);
  397. //console.log('Success:', result);
  398. } catch (e) {
  399. console.error('Error parsing response:', e);
  400. }
  401. },
  402. onerror: function(error) {
  403. console.error('Request failed:', error);
  404. },
  405. ontimeout: function() {
  406. console.error('Request timed out');
  407. }
  408. });
  409.  
  410. // GM.xmlHttpRequest({
  411. // method: 'POST',
  412. // url: testUrl,
  413. // headers: {
  414. // 'Content-Type': 'application/json'
  415. // },
  416. // data: JSON.stringify(data), // JSON stringify the payload
  417. // onload: function(response) {
  418. // try {
  419. // // Parse the JSON response
  420. // const result = JSON.parse(response.responseText);
  421. // //console.log('Success:', result);
  422. // } catch (e) {
  423. // console.error('Error parsing response:', e);
  424. // }
  425. // },
  426. // onerror: function(error) {
  427. // console.error('Request failed:', error);
  428. // },
  429. // ontimeout: function() {
  430. // console.error('Request timed out');
  431. // }
  432. // });
  433. }
  434.  
  435.  
  436. window.addEventListener('load', setupVideoCheck);
  437.  
  438. })();
  439.  
  440.