├── .DS_Store ├── icons ├── icon16.png ├── icon48.png └── icon128.png ├── releases ├── chrome-3.4.0.zip ├── chrome-3.4.1.zip ├── chrome-3.4.2.zip ├── chrome-3.4.3.zip ├── chrome-3.4.4.zip ├── chrome-3.4.5.zip ├── chrome-3.5.0.zip ├── chrome-3.5.1.zip └── chrome-3.6.0.zip ├── scripts ├── attentionRemove.js ├── theaterRemove.js ├── theaterSet.js ├── darkThemeRemove.js ├── darkThemeSet.js ├── eduGraph.js ├── attentionSet.js └── tweaksSet.js ├── README.md ├── manifest.json ├── background.css ├── popup.html ├── sw.js └── background.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/.DS_Store -------------------------------------------------------------------------------- /icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/icons/icon16.png -------------------------------------------------------------------------------- /icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/icons/icon48.png -------------------------------------------------------------------------------- /icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/icons/icon128.png -------------------------------------------------------------------------------- /releases/chrome-3.4.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.4.0.zip -------------------------------------------------------------------------------- /releases/chrome-3.4.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.4.1.zip -------------------------------------------------------------------------------- /releases/chrome-3.4.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.4.2.zip -------------------------------------------------------------------------------- /releases/chrome-3.4.3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.4.3.zip -------------------------------------------------------------------------------- /releases/chrome-3.4.4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.4.4.zip -------------------------------------------------------------------------------- /releases/chrome-3.4.5.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.4.5.zip -------------------------------------------------------------------------------- /releases/chrome-3.5.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.5.0.zip -------------------------------------------------------------------------------- /releases/chrome-3.5.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.5.1.zip -------------------------------------------------------------------------------- /releases/chrome-3.6.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/notnavindu/SLIIT-Eduscope-Mods/HEAD/releases/chrome-3.6.0.zip -------------------------------------------------------------------------------- /scripts/attentionRemove.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | let eplayer = document.getElementById("eplayer_iframe"); 3 | 4 | if (eplayer) { 5 | const videoContainer = eplayer.contentWindow.document.getElementById("main-video-container").parentElement 6 | 7 | videoContainer.style.display = "block"; 8 | 9 | let node = eplayer.contentWindow.document.getElementById("attention-lock-container") 10 | 11 | if (node) { 12 | node.remove() 13 | } 14 | } 15 | })() 16 | -------------------------------------------------------------------------------- /scripts/theaterRemove.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | let parentFrame = document.getElementById("eplayer_iframe") 3 | if (parentFrame) { 4 | parentFrame.style.position = "relative" 5 | parentFrame.style.width = "100%" 6 | parentFrame.style.height = "624px" 7 | } 8 | 9 | let mainPlayer = document.getElementById("eplayer_iframe")?.contentWindow?.document?.getElementsByClassName("mainPlayer")[0] 10 | 11 | if (mainPlayer) { 12 | mainPlayer.style.paddingTop = "56.25%"; 13 | mainPlayer.style.width = "100%"; 14 | mainPlayer.style.height = "0px" 15 | } 16 | })() 17 | -------------------------------------------------------------------------------- /scripts/theaterSet.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | let parentFrame = document.getElementById("eplayer_iframe") 3 | if (parentFrame) { 4 | parentFrame.style.position = "fixed" 5 | parentFrame.style.top = "0" 6 | parentFrame.style.left = "0" 7 | parentFrame.style.width = "100%" 8 | parentFrame.style.height = "100vh" 9 | parentFrame.style.zIndex = "9999" 10 | } 11 | 12 | let mainPlayer = document.getElementById("eplayer_iframe")?.contentWindow?.document?.getElementsByClassName("mainPlayer")[0] 13 | 14 | if (mainPlayer) { 15 | mainPlayer.style.padding = "0"; 16 | mainPlayer.style.width = "100%"; 17 | mainPlayer.style.height = "100vh" 18 | } 19 | })() 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SLIIT Eduscope Mod v3.5.0 2 | 3 | A Simple Chrome Extension with a few simple mods for SLIIT's Eduscope video platform. 4 | 5 | ## Important update regarding lecture downloading 6 | 7 | The downloader did stop working due to a stupid bug (that I obviously wrote). HOWEVER, I have developed an entirely new version of the downloader and its in the final stages of going public. Meanwhile, please feel free to join the waitlist for it here https://downloader-onboarding.vercel.app/ 8 | 9 | ## Features 10 | 11 | - Change video playback speed (0.5x, 1x, 2x, 6x) 12 | - Theater mode 13 | - Save video playback time 14 | - Prevent the page from scrolling when the spacebar is pressed 15 | 16 | ### Supported Browsers 17 | 18 | - Chrome 19 | - Brave 20 | - Edge 21 | 22 | Did anyone ask for this? No. 23 | -------------------------------------------------------------------------------- /scripts/darkThemeRemove.js: -------------------------------------------------------------------------------- 1 | 2 | document.body.classList.remove("dark-mode"); 3 | 4 | if (document.querySelector(".boxview") != null) { 5 | var x = document.querySelectorAll(".boxview"); 6 | for (var i = 0; i < x.length; i++) { 7 | x[i].classList.remove("dark-mode"); 8 | } 9 | } 10 | 11 | var l = document.getElementsByTagName('label'); 12 | for (var i = 0; i < l.length; i++) { 13 | l[i].classList.remove("dark-mode"); 14 | } 15 | 16 | var hr = document.getElementsByTagName('hr'); 17 | for (var i = 0; i < hr.length; i++) { 18 | hr[i].style.borderTop = "1px solid #eee"; 19 | } 20 | 21 | if (document.querySelector(".main-div") != null) { 22 | document.querySelector(".main-div").classList.remove("dark-mode"); 23 | } 24 | 25 | if (document.getElementById("comment")) { 26 | document.getElementById("comment").classList.remove("dark-textarea"); 27 | document.getElementById("comment").classList.add("form-control"); 28 | } 29 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "SLIIT Eduscope Mods", 4 | "version": "3.6.0", 5 | "description": "A few simple mods for SLIIT's Eduscope Platform to make it a bit more user friendly", 6 | "short_name": "Eduscope Mods", 7 | "permissions": ["storage", "scripting", "tabs", "nativeMessaging"], 8 | "host_permissions": ["*://lecturecapture.sliit.lk/*"], 9 | "content_scripts": [ 10 | { 11 | "matches": ["*://lecturecapture.sliit.lk/*"], 12 | "css": ["background.css"], 13 | "js": ["background.js"], 14 | "run_at": "document_idle", 15 | "all_frames": true 16 | } 17 | ], 18 | "background": { 19 | "service_worker": "sw.js" 20 | }, 21 | 22 | "action": { 23 | "default_title": "Eduscope Mods", 24 | "default_popup": "popup.html", 25 | "default_icon": "icons/icon128.png" 26 | }, 27 | "icons": { 28 | "16": "icons/icon16.png", 29 | "48": "icons/icon48.png", 30 | "128": "icons/icon128.png" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /scripts/darkThemeSet.js: -------------------------------------------------------------------------------- 1 | document.body.classList.add("dark-mode"); 2 | 3 | if (document.querySelector(".boxview") != null) { 4 | var x = document.querySelectorAll(".boxview"); 5 | for (var i = 0; i < x.length; i++) { 6 | x[i].classList.add("dark-mode"); 7 | } 8 | } 9 | 10 | var l = document.getElementsByTagName('label'); 11 | for (var i = 0; i < l.length; i++) { 12 | l[i].classList.add("dark-mode"); 13 | } 14 | 15 | var hr = document.getElementsByTagName('hr'); 16 | for (var i = 0; i < hr.length; i++) { 17 | hr[i].style.borderTop = "1px solid #0388fc"; 18 | } 19 | 20 | if (document.querySelector(".main-div") != null) { 21 | document.querySelector(".main-div").classList.add("dark-mode"); 22 | } 23 | 24 | if (document.getElementById("comment")) { 25 | document.getElementById("comment").classList.add("dark-textarea"); 26 | document.getElementById("comment").style.width = "100%"; 27 | document.getElementById("comment").style.padding = "10px"; 28 | document.getElementById("comment").style.height = "100px"; 29 | document.getElementById("comment").classList.remove("form-control"); 30 | } 31 | -------------------------------------------------------------------------------- /scripts/eduGraph.js: -------------------------------------------------------------------------------- 1 | var edugraphInit = false; 2 | (() => { 3 | // var test; 4 | let eplayer = document.getElementById("eplayer_iframe"); 5 | 6 | if (eplayer) { 7 | let video = eplayer.contentWindow.document.getElementsByTagName("video")[0]; 8 | 9 | video.addEventListener("play", () => { 10 | chrome.runtime.sendMessage({ play: true }, function (response) { 11 | console.log(response); 12 | }); 13 | }) 14 | 15 | video.addEventListener("pause", () => { 16 | chrome.runtime.sendMessage({ pause: true }, function (response) { 17 | console.log(response); 18 | }); 19 | }) 20 | 21 | setInterval(async () => { 22 | try { 23 | chrome.runtime.sendMessage({ autoSave: true }, function () { }); 24 | } catch (error) { console.log("Page Refresh Required", error) } 25 | }, 1000 * 60 * 1) 26 | } 27 | 28 | let user = document.getElementById("dropdown08")?.text?.replace("(Logout)", "")?.trim(); 29 | 30 | chrome.runtime.sendMessage({ studentId: user }, function (response) { 31 | console.log(response); 32 | }); 33 | 34 | })() -------------------------------------------------------------------------------- /scripts/attentionSet.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const embedIds = { 3 | 1: 'IMKAK3iJB0Q?si=LuBc4_Tl_Z3DgfZp', 4 | 2: 'SuK5LUWj9do?si=G7JjfQP7vVKOZKH5', 5 | 3: 'ZF4DA2MEVqM?si=TsNe07wvXPQgGE0T' 6 | } 7 | let eplayer = document.getElementById("eplayer_iframe"); 8 | 9 | if (eplayer) { 10 | const videoContainer = eplayer.contentWindow.document.getElementById("main-video-container").parentElement 11 | 12 | videoContainer.style.display = "grid"; 13 | videoContainer.style.gridTemplateColumns = "5fr 1fr"; 14 | 15 | let node = eplayer.contentWindow.document.getElementById("attention-lock-container") 16 | 17 | if (!node) { 18 | node = document.createElement("div") 19 | node.id = "attention-lock-container" 20 | } 21 | 22 | node.innerHTML = ` 23 | 29 | ` 30 | 31 | videoContainer.appendChild(node) 32 | } 33 | })() 34 | -------------------------------------------------------------------------------- /scripts/tweaksSet.js: -------------------------------------------------------------------------------- 1 | var loop; 2 | var lastSavedTime; 3 | (() => { 4 | 5 | document.getElementsByTagName("body")[0].style.fontFamily = "Arial"; 6 | 7 | let eplayer = document.getElementById("eplayer_iframe"); 8 | 9 | // comment button 10 | let submitButton = document.getElementById("comment_submit") 11 | if (submitButton) { 12 | submitButton.style.borderRadius = "7px"; 13 | submitButton.style.marginTop = "15px"; 14 | submitButton.style.backgroundColor = "#121212"; 15 | submitButton.style.border = "2px solid #0388fc"; 16 | } 17 | 18 | // video frame 19 | if (eplayer) { 20 | eplayer.style.border = "2px solid #002647"; 21 | eplayer.contentWindow.document.documentElement.style.backgroundColor = "#121212"; 22 | 23 | controlBar = eplayer.contentWindow.document.querySelector(".video-react-control-bar-auto-hide"); 24 | controlBar.style.height = "45px"; 25 | controlBar.style.fontSize = "1.4em"; 26 | controlBar.style.marginBottom = "15px"; 27 | controlBar.style.backgroundColor = "#121212"; 28 | controlBar.style.opacity = "0.9"; 29 | controlBar.style.borderRadius = "7px"; 30 | controlBar.style.border = "1px solid #0388fc"; 31 | 32 | // handle mouse over 33 | player = eplayer.contentWindow.document.querySelector(".video-react-control-bar-auto-hide") 34 | eplayer.onmouseover = eplayer.onmouseout = handler; 35 | 36 | // On Time update 37 | let video = eplayer.contentWindow.document.getElementsByTagName("video")[0]; 38 | 39 | 40 | 41 | chrome.runtime.sendMessage({ connect: true }, function (response) { 42 | 43 | }); 44 | 45 | video.addEventListener("play", () => { 46 | if (loop) { 47 | return 48 | }; 49 | 50 | loop = setInterval(async () => { 51 | try { 52 | // console.log(video.currentTime, lastSavedTime) 53 | if (video.currentTime === lastSavedTime) return 54 | chrome.runtime.sendMessage({ currentTime: video.currentTime }, function (data) { lastSavedTime = data.saved }); 55 | } catch (error) { console.log("Page Refresh Required", error) } 56 | }, 1000) 57 | }) 58 | 59 | video.addEventListener("pause", () => { 60 | clearInterval(loop) 61 | loop = null 62 | }) 63 | } 64 | 65 | function handler(event) { 66 | controlBar = document.getElementById("eplayer_iframe").contentWindow.document.querySelector(".video-react-control-bar-auto-hide"); 67 | if (event.type == 'mouseover') { 68 | controlBar.style.opacity = 0.9; 69 | } 70 | if (event.type == 'mouseout') { 71 | controlBar.style.opacity = 0; 72 | } 73 | } 74 | })() 75 | -------------------------------------------------------------------------------- /background.css: -------------------------------------------------------------------------------- 1 | .--eduscope-mod-html { 2 | width: 350px; 3 | height: auto; 4 | font-family: "Courier New", Courier, monospace; 5 | background-color: #000; 6 | color: #fff; 7 | padding: 0; 8 | margin: 0; 9 | border: 3px #303030 solid; 10 | } 11 | #eduscope-mod-error { 12 | color: #f00; 13 | } 14 | .--eduscope-mod-body { 15 | padding: 0px 20px; 16 | } 17 | 18 | .--eduscope-mod-options-container { 19 | font-size: 100%; 20 | } 21 | 22 | .--eduscope-mod-option-subtitle { 23 | margin-top: -10px; 24 | font-size: 90%; 25 | color: #0073d6; 26 | } 27 | 28 | .--eduscope-mod-disabled { 29 | pointer-events: none; 30 | opacity: 0.5; 31 | } 32 | 33 | .--eduscope-mod-option-row { 34 | width: 100%; 35 | display: flex; 36 | } 37 | 38 | .--eduscope-mod-link { 39 | color: #004b8c; 40 | } 41 | 42 | .--eduscope-mod-link:hover { 43 | color: #0388fc; 44 | } 45 | 46 | .--eduscope-button { 47 | display: inline-block; 48 | margin-bottom: 10px; 49 | cursor: pointer; 50 | color: #fff; 51 | background-color: #000; 52 | padding: 5px 10px; 53 | font-size: 13px; 54 | text-align: center; 55 | border: 2px solid #444; 56 | border-radius: 4px; 57 | } 58 | 59 | .--eduscope-button:hover { 60 | background-color: #004b8c; 61 | border: 2px solid #0388fc; 62 | } 63 | 64 | /* SELECTORS */ 65 | .--eduscope-mod-radio-toolbar { 66 | margin: 10px 0px; 67 | } 68 | 69 | .--eduscope-mod-radio-toolbar input[type="radio"] { 70 | opacity: 0; 71 | position: fixed; 72 | width: 0; 73 | } 74 | 75 | .--eduscope-mod-radio-toolbar label { 76 | display: inline-block; 77 | width: 30px; 78 | margin-bottom: 10px; 79 | background-color: #000; 80 | padding: 5px 7px; 81 | font-size: 13px; 82 | text-align: center; 83 | border: 2px solid #444; 84 | border-radius: 4px; 85 | } 86 | 87 | .--eduscope-mod-radio-toolbar-attention-lock label:not(:first-of-type) { 88 | width: 10px; 89 | } 90 | 91 | .--eduscope-mod-speed-container label { 92 | width: 20px; 93 | padding: 3px 7px; 94 | font-size: 10px; 95 | border: 1px solid #444; 96 | } 97 | 98 | .--eduscope-mod-speed-container input[type="radio"]:focus + label, 99 | .--eduscope-mod-radio-toolbar label:hover { 100 | background-color: #00182e; 101 | } 102 | 103 | .--eduscope-mod-speed-container input[type="radio"]:focus + label, 104 | .--eduscope-mod-radio-toolbar input[type="radio"]:focus + label { 105 | border: 2px solid rgb(0, 0, 0); 106 | } 107 | 108 | .--eduscope-mod-speed-container input[type="radio"]:focus + label, 109 | .--eduscope-mod-radio-toolbar input[type="radio"]:checked + label { 110 | border: 1px solid #0388fc; 111 | background-color: #004b8c; 112 | } 113 | 114 | .--eduscope-mod-speed-container { 115 | display: flex; 116 | margin-top: 10px; 117 | gap: 3px; 118 | } 119 | 120 | /* END SELECTOR STYLES*/ 121 | 122 | /*Dark mode styles*/ 123 | 124 | .dark-mode { 125 | background-color: #121212 !important; 126 | color: #fff !important; 127 | } 128 | 129 | .dark-textarea { 130 | color: #fff !important; 131 | background: #121212 !important; 132 | border: 1px solid #0073d6 !important; 133 | border-radius: 7px; 134 | } 135 | 136 | ::-webkit-scrollbar { 137 | width: 10px; 138 | } 139 | 140 | /* Track */ 141 | ::-webkit-scrollbar-track { 142 | background: #303030; 143 | } 144 | 145 | /* Handle */ 146 | ::-webkit-scrollbar-thumb { 147 | background: #004b8c; 148 | } 149 | 150 | /* Handle on hover */ 151 | ::-webkit-scrollbar-thumb:hover { 152 | background: #0073d6; 153 | } 154 | 155 | .--eduscope-mod-slider { 156 | -webkit-appearance: none; 157 | width: 100%; 158 | height: 6px; 159 | border-radius: 5px; 160 | background: #444; 161 | outline: none; 162 | opacity: 1; 163 | -webkit-transition: 0.2s; 164 | transition: opacity 0.2s; 165 | } 166 | 167 | .--eduscope-mod-slider::-webkit-slider-thumb { 168 | -webkit-appearance: none; 169 | appearance: none; 170 | width: 15px; 171 | height: 15px; 172 | border-radius: 50%; 173 | background: #0388fc; 174 | cursor: pointer; 175 | } 176 | 177 | .--eduscope-mod-slider::-moz-range-thumb { 178 | width: 15px; 179 | height: 15px; 180 | border-radius: 50%; 181 | background: #0388fc; 182 | cursor: pointer; 183 | } 184 | 185 | .--eduscope-mod-speed-value { 186 | font-size: 2rem; 187 | color: #0388fc; 188 | font-weight: bold; 189 | } 190 | 191 | .--eduscope-mod-blinking { 192 | width: 10px; 193 | height: 10px; 194 | background: #0388fc; 195 | border-radius: 100%; 196 | margin-top: 2px; 197 | animation: eduscopeModBlinkAnimation 0.5s infinite alternate ease-in-out; 198 | } 199 | 200 | @keyframes eduscopeModBlinkAnimation { 201 | from { 202 | opacity: 0; 203 | } 204 | to { 205 | opacity: 1; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 |

SLIIT Eduscope Mods

11 |
12 | 13 |
14 | 15 |
16 |
17 | Playback Speed 18 |
19 |
20 |
1.00x
21 | 22 |
23 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 |
59 | 60 |
64 |
65 | 66 |
67 | 68 | 69 |
70 | 71 |
72 | UI Tweaks 73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 |
81 | 82 |
86 |
87 |
88 | 89 | 90 |
91 | Dark Mode 92 |
93 | 94 | 95 | 96 | 97 | 98 |
99 |
100 |
101 | 102 | 103 |
104 | 105 |
106 | Theater Mode 107 |
108 | 109 | 110 | 111 | 112 | 113 |
114 |
118 |
119 | 120 | 121 |
122 | Disable Spacebar Scroll 123 |
124 | 125 | 126 | 127 | 128 | 129 |
130 |
134 |
135 |
136 |
137 | 138 | 139 |
140 | 141 |
142 | Attention Lock 143 |
146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
158 | 159 | 160 | 171 | 172 | 173 | 177 |
178 |
179 | 180 |
181 | 182 | 183 |
184 |
185 | 186 |
187 | 188 | 191 |
192 | 193 |
194 | Version 3.4.4
195 | 200 | GitHub 201 | 202 |   203 | 208 | Report Bugs 209 | 210 |   211 | 216 | Request Mods 217 | 218 |
219 |
220 |
221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | let studentId; 2 | let saveLoop; 3 | const action_ver = 3 4 | 5 | chrome.runtime.onInstalled.addListener(async function (object) { 6 | let externalUrl = "https://edu-graph.vercel.app/attention-lock"; 7 | 8 | const ver = (await chrome.storage.local.get(`last-action-version`))[`last-action-version`] ?? 0 9 | 10 | if (action_ver > ver && object.reason === chrome.runtime.OnInstalledReason.UPDATE) { 11 | 12 | chrome.storage.local.set({ 'last-action-version': action_ver }) 13 | 14 | chrome.tabs.create({ url: externalUrl }, function (tab) { 15 | console.log("New tab launched with https://edu-graph.vercel.app/attention-lock"); 16 | }); 17 | } 18 | });// 19 | 20 | chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { 21 | if (changeInfo.status === "complete") { 22 | init(tab) 23 | } 24 | }); 25 | 26 | chrome.tabs.onRemoved.addListener( 27 | async (tabId) => { 28 | let videoId = (await chrome.storage.local.get(`edugraph-${tabId}-videoId`))[`edugraph-${tabId}-videoId`] 29 | await saveSession(tabId, videoId) 30 | 31 | try { 32 | await chrome.storage.local.remove([[ 33 | `edugraph-${videoId}-session-start`, 34 | `edugraph-${videoId}-duration`, 35 | `edugraph-${videoId}-isPlaying`, 36 | `edugraph-studentId` 37 | ]]) 38 | } catch (error) { 39 | 40 | } 41 | } 42 | 43 | ) 44 | 45 | 46 | async function init(tab) { 47 | if (tab.url.includes("lecturecapture.sliit.lk")) { 48 | // get saved values 49 | let { scroll } = await chrome.storage.sync.get(["scroll"]); 50 | let { dark } = await chrome.storage.sync.get(["dark"]); 51 | let { tweaks } = await chrome.storage.sync.get(["tweaks"]); 52 | let { theater } = await chrome.storage.sync.get(["theater"]); 53 | 54 | if (scroll) { 55 | setScroll(scroll, tab.id); 56 | } 57 | 58 | if (dark) { 59 | setDark(dark, tab.id); 60 | } 61 | 62 | if (tweaks) { 63 | setTweaks(tweaks, tab.id); 64 | } 65 | 66 | if (theater) { 67 | setTheaterMode(theater, tab.id); 68 | } 69 | 70 | setAnalytics(tab.id, tab.url) 71 | } 72 | } 73 | 74 | /* 75 | * 76 | * ------------------------------ 77 | * | Injectors 78 | * ------------------------------ 79 | * 80 | */ 81 | 82 | // set playback speed 83 | async function setPlaybackSpeed(speed, tabId) { 84 | chrome.scripting.executeScript({ 85 | target: { tabId: tabId, allFrames: true }, 86 | func: function (speed2) { 87 | videoElements = document.getElementById("eplayer_iframe")?.contentWindow?.document?.getElementsByTagName("video") || []; 88 | Array.prototype.forEach.call(videoElements, function (elm) { 89 | elm.playbackRate = speed2; 90 | }); 91 | }, 92 | args: [speed] 93 | }); 94 | } 95 | 96 | // set scrolling behaviour 97 | async function setScroll(state, tabId) { 98 | if (state == 1) { 99 | chrome.scripting.executeScript({ 100 | target: { tabId: tabId, allFrames: true }, 101 | func: () => { 102 | window.addEventListener('keydown', function (e) { 103 | if (e.keyCode == 32) { 104 | e.preventDefault(); 105 | } 106 | }); 107 | }, 108 | }); 109 | } 110 | } 111 | 112 | // set dark theme 113 | async function setDark(state, tabId) { 114 | if (state == 0) { 115 | chrome.scripting.executeScript({ 116 | target: { tabId: tabId, allFrames: true }, 117 | files: ['./scripts/darkThemeRemove.js'], 118 | }); 119 | } else { 120 | chrome.scripting.executeScript({ 121 | target: { tabId: tabId, allFrames: true }, 122 | files: ['./scripts/darkThemeSet.js'], 123 | }); 124 | } 125 | } 126 | 127 | // set UI Tweaks 128 | async function setTweaks(state, tabId) { 129 | if (state == 0) { 130 | console.log("off"); 131 | } else { 132 | chrome.scripting.executeScript({ 133 | target: { tabId: tabId, allFrames: true }, 134 | files: ['./scripts/tweaksSet.js'], 135 | }); 136 | } 137 | } 138 | 139 | // set Theater mode 140 | async function setTheaterMode(state, tabId) { 141 | if (state == 0) { 142 | chrome.scripting.executeScript({ 143 | target: { tabId: tabId, allFrames: true }, 144 | files: ['./scripts/theaterRemove.js'], 145 | }); 146 | } else { 147 | chrome.scripting.executeScript({ 148 | target: { tabId: tabId, allFrames: true }, 149 | files: ['./scripts/theaterSet.js'], 150 | }); 151 | } 152 | } 153 | 154 | // Analytics 155 | async function setAnalytics(tabId) { 156 | console.log("init edugraph") 157 | 158 | chrome.scripting.executeScript({ 159 | target: { tabId: tabId }, 160 | files: ['./scripts/eduGraph.js'], 161 | }); 162 | 163 | } 164 | 165 | /* 166 | * 167 | * ------------------------------ 168 | * | Message Listeners 169 | * ------------------------------ 170 | * 171 | */ 172 | 173 | chrome.runtime.onMessage.addListener( 174 | async function (request, sender, sendResponse) { 175 | let url = new URL(sender.tab.url) 176 | let videoId = url.searchParams.get("id"); 177 | 178 | // initial request 179 | if (request.connect) { 180 | sendResponse({ connected: true }); 181 | 182 | let { playbackSpeed } = await chrome.storage.sync.get(['playbackSpeed']) 183 | 184 | let savedTime = (await chrome.storage.local.get([`${videoId}`]))[`${videoId}`]; 185 | 186 | 187 | chrome.scripting.executeScript({ 188 | target: { tabId: sender.tab.id, allFrames: true }, 189 | func: function (time, speed) { 190 | videoElements = document.getElementById("eplayer_iframe")?.contentWindow?.document?.getElementsByTagName("video") || []; 191 | Array.prototype.forEach.call(videoElements, function (elm) { 192 | elm.playbackRate = speed; 193 | let playPromise = elm.play() 194 | if (playPromise !== undefined) { 195 | elm.currentTime = time; 196 | // elm.pause() 197 | } 198 | }); 199 | }, 200 | args: [savedTime || 1, playbackSpeed || 1] 201 | }); 202 | 203 | 204 | } 205 | 206 | // save time 207 | if (request.currentTime) { 208 | sendResponse({ saved: request.currentTime }); 209 | let values = {}; 210 | values[videoId] = request.currentTime; 211 | 212 | chrome.storage.local.set(values) 213 | } 214 | 215 | // eduGraph 216 | if (request.studentId) { 217 | sendResponse({ recieved: true }) 218 | studentId = request.studentId; 219 | resetLocalData(videoId, studentId, sender.tab.id) 220 | } 221 | 222 | if (request.pause) { 223 | sendResponse({ recieved: true }); 224 | console.log("Pause") 225 | 226 | let data = await chrome.storage.local.get([`edugraph-${videoId}-session-start`, `edugraph-${videoId}-duration`]) 227 | 228 | let now = Date.now(); 229 | let diff = now - data[`edugraph-${videoId}-session-start`]; 230 | 231 | let playingVal = {} 232 | playingVal[`edugraph-${videoId}-duration`] = data[`edugraph-${videoId}-duration`] + diff; 233 | playingVal[`edugraph-${videoId}-isPlaying`] = false; 234 | playingVal[`edugraph-${videoId}-session-start`] = now; 235 | 236 | await chrome.storage.local.set(playingVal) 237 | } 238 | 239 | if (request.play) { 240 | sendResponse({ recieved: true }); 241 | 242 | console.log("play"); 243 | 244 | let playingVal = {} 245 | playingVal[`edugraph-${videoId}-isPlaying`] = true; 246 | playingVal[`edugraph-${videoId}-session-start`] = Date.now() 247 | await chrome.storage.local.set(playingVal) 248 | } 249 | 250 | if (request.autoSave) { 251 | sendResponse({ recieved: true }); 252 | // ANALYTICS DISABLED 253 | // saveSession(sender.tab.id, videoId) 254 | } 255 | 256 | return true; 257 | } 258 | ); 259 | 260 | 261 | const test = async () => { 262 | return new Promise((resolve, reject) => { 263 | setTimeout(resolve, 500) 264 | }) 265 | } 266 | 267 | const resetLocalData = async (videoId, studentId, tabId) => { 268 | let values = {}; 269 | values[`edugraph-${videoId}-duration`] = 0; 270 | values[`edugraph-${videoId}-session-start`] = Date.now(); 271 | values[`edugraph-${videoId}-isPlaying`] = false; 272 | values[`edugraph-${tabId}-videoId`] = videoId; 273 | values[`edugraph-studentId`] = studentId; 274 | await chrome.storage.local.set(values) 275 | } 276 | 277 | const getEdugraphValuesForVideo = async (videoId) => { 278 | let data = await chrome.storage.local.get([ 279 | `edugraph-${videoId}-session-start`, 280 | `edugraph-${videoId}-duration`, 281 | `edugraph-${videoId}-isPlaying`, 282 | `edugraph-studentId` 283 | ]) 284 | 285 | return { 286 | sessionStart: data[`edugraph-${videoId}-session-start`], 287 | duration: data[`edugraph-${videoId}-duration`], 288 | isPlaying: data[`edugraph-${videoId}-isPlaying`], 289 | studentId: data["edugraph-studentId"] 290 | } 291 | } 292 | 293 | const saveSession = async (tabId, videoId) => { 294 | return new Promise(async (resolve, reject) => { 295 | 296 | let { sessionStart, duration, isPlaying, studentId } = await getEdugraphValuesForVideo(videoId) 297 | 298 | if (isPlaying) { 299 | duration = duration + (Date.now() - sessionStart) 300 | } 301 | 302 | let data = { 303 | studentId: studentId, 304 | start: sessionStart, 305 | duration: duration, 306 | videoId: videoId 307 | } 308 | 309 | let val = {} 310 | val[`edugraph-${videoId}-duration`] = 0; 311 | val[`edugraph-${videoId}-session-start`] = Date.now(); 312 | 313 | await chrome.storage.local.set(val) 314 | 315 | const { analytics } = await chrome.storage.sync.get(["analytics"]) 316 | 317 | console.log("debug:", analytics) 318 | 319 | if (analytics == "1" && studentId && duration > 1) { 320 | // send to API 321 | fetch("https://edu-graph.vercel.app/api/saveV2", { 322 | method: "POST", 323 | headers: new Headers({ 324 | 'Content-Type': 'application/json', 325 | Accept: 'application/json', 326 | }), 327 | body: JSON.stringify(data) 328 | }).then(res => { 329 | console.log("Request complete! response:", res); 330 | resolve() 331 | }).catch(e => { 332 | reject() 333 | }); 334 | } 335 | }) 336 | 337 | } 338 | 339 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", function () { 2 | setupUI() 3 | }); 4 | 5 | // check if user is in the eduscope website 6 | async function setupUI() { 7 | let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); 8 | 9 | let pageUrl = tabs[0].url; 10 | 11 | if (!pageUrl.includes("lecturecapture.sliit.lk")) { 12 | document.body.classList.add("--eduscope-mod-disabled"); 13 | document.getElementById("eduscope-mod-error").innerHTML = "Only works in lecturecapture.sliit.lk"; 14 | } else { 15 | // get saved values 16 | let { playbackSpeed } = await chrome.storage.sync.get(['playbackSpeed']); 17 | let { scroll } = await chrome.storage.sync.get(["scroll"]); 18 | let { dark } = await chrome.storage.sync.get(["dark"]); 19 | let { tweaks } = await chrome.storage.sync.get(["tweaks"]); 20 | let { theater } = await chrome.storage.sync.get(["theater"]); 21 | let { analytics } = await chrome.storage.sync.get(["analytics"]); 22 | let { attention } = await chrome.storage.sync.get(["attention"]); 23 | 24 | 25 | // set saved options 26 | if (playbackSpeed !== undefined) { 27 | setPlaybackSpeed(playbackSpeed, true); 28 | } 29 | 30 | if (scroll !== undefined) { 31 | setScroll(scroll); 32 | } 33 | 34 | if (dark !== undefined) { 35 | setDark(dark); 36 | } 37 | 38 | if (tweaks !== undefined) { 39 | setTweaks(tweaks); 40 | } 41 | 42 | if (theater !== undefined) { 43 | setTheaterMode(theater); 44 | } 45 | 46 | if (analytics !== undefined) { 47 | setAnalyticsOption(analytics) 48 | } 49 | 50 | console.log("🚀 ~ setupUI ~ attention:", attention) 51 | if (attention !== undefined) { 52 | setAttentionLock(attention) 53 | } 54 | 55 | 56 | // get input elements 57 | let speedSlider = document.getElementById("speedSlider"); 58 | let speedOptions = document.getElementsByName("speed"); 59 | let scrollOptions = document.getElementsByName("scroll"); 60 | let darkOptions = document.getElementsByName("dark"); 61 | let tweaksOptions = document.getElementsByName("tweaks"); 62 | let theaterOptions = document.getElementsByName("theater"); 63 | let analyticsOptions = document.getElementsByName("analytics"); 64 | let pipMode = document.getElementById("pip"); 65 | let attentionOptions = document.getElementsByName("attention"); 66 | // let downloadBtn = document.getElementById("download"); 67 | 68 | //add onclick handlers 69 | //speed 70 | speedSlider.oninput = onSpeedOptionChangeRealtime 71 | speedSlider.onmouseup = onSpeedOptionChange 72 | 73 | //speed buttons 74 | Array.prototype.forEach.call(speedOptions, function (radio) { 75 | radio.addEventListener("change", onButtonSpeedOptionChange); 76 | }); 77 | 78 | //scroll 79 | Array.prototype.forEach.call(scrollOptions, function (radio) { 80 | radio.addEventListener("change", onScrollOptionChange); 81 | }); 82 | 83 | //DarkMode 84 | Array.prototype.forEach.call(darkOptions, function (radio) { 85 | radio.addEventListener("change", onDarkOptionChange); 86 | }); 87 | 88 | //UI Tweaks 89 | Array.prototype.forEach.call(tweaksOptions, function (radio) { 90 | radio.addEventListener("change", onTweaksOptionChange); 91 | }); 92 | 93 | // Theater mode 94 | Array.prototype.forEach.call(theaterOptions, function (radio) { 95 | radio.addEventListener("change", onTheaterOptionChange); 96 | }); 97 | 98 | // Attention Lock 99 | Array.prototype.forEach.call(attentionOptions, function (radio) { 100 | radio.addEventListener("change", onAttentionOptionChange); 101 | }); 102 | 103 | // Analytics consent (Disabled for now) 104 | // Array.prototype.forEach.call(analyticsOptions, function (radio) { 105 | // radio.addEventListener("change", onAnalyticsOptionChange); 106 | // }); 107 | 108 | // Analytics consent 109 | Array.prototype.forEach.call(analyticsOptions, function (radio) { 110 | radio.addEventListener("change", onAnalyticsOptionChange); 111 | }); 112 | 113 | // PIP mode 114 | pipMode.addEventListener("click", enablePip); 115 | 116 | // download button 117 | // downloadBtn.addEventListener("click", () => downloadVideo(pageUrl)); 118 | 119 | } 120 | } 121 | 122 | /* 123 | * -------------------------- 124 | * | Option Change Handlers | 125 | * -------------------------- 126 | * 127 | */ 128 | 129 | 130 | // change video playback speed change handler 131 | async function onSpeedOptionChange() { 132 | //set speed 133 | setPlaybackSpeed(this.value); 134 | // save to storage 135 | await chrome.storage.sync.set({ "playbackSpeed": this.value }) 136 | } 137 | 138 | async function onButtonSpeedOptionChange() { 139 | //set speed 140 | setPlaybackSpeed(this.value); 141 | // 142 | document.getElementById("speedSlider").value = this.value 143 | onSpeedOptionChangeRealtime(this.value) 144 | // save to storage 145 | await chrome.storage.sync.set({ "playbackSpeed": this.value }) 146 | } 147 | 148 | async function onSpeedOptionChangeRealtime(customValue = 0) { 149 | let val = this.value || customValue 150 | let suffix = val > 5 ? "x🔥" : "x" 151 | document.getElementById("speedValue").innerHTML = `${Number(val).toFixed(2)}${suffix}` 152 | document.getElementById("blinker").style.animationDuration = `${Number((1 / val) / 2).toFixed(2)}s` 153 | 154 | let speedOptions = document.getElementsByName("speed") 155 | 156 | speedOptions.forEach(elm => { 157 | elm.checked = false 158 | }) 159 | } 160 | 161 | // change scroll behavior change handler 162 | async function onScrollOptionChange() { 163 | //set speed 164 | setScroll(this.value); 165 | if (this.value == 0) { 166 | // request page refresh 167 | document.getElementById("eduscope-mod-option-scroll-subtitle").innerHTML = "Page Refresh Required"; 168 | 169 | // TODO Find a way to remove event handlers with extensions 170 | } else { 171 | document.getElementById("eduscope-mod-option-scroll-subtitle").innerHTML = ""; 172 | } 173 | 174 | // save to storage 175 | await chrome.storage.sync.set({ "scroll": this.value }) 176 | } 177 | 178 | async function onDarkOptionChange() { 179 | setDark(this.value); 180 | // save to local storage 181 | await chrome.storage.sync.set({ "dark": this.value }) 182 | } 183 | 184 | async function onTweaksOptionChange() { 185 | setTweaks(this.value); 186 | 187 | if (this.value == 0) { 188 | // request page refresh 189 | document.getElementById("eduscope-mod-option-tweaks-subtitle").innerHTML = "Page Refresh Required"; 190 | 191 | // TODO Find a way to remove event handlers with extensions 192 | } else { 193 | document.getElementById("eduscope-mod-option-tweaks-subtitle").innerHTML = ""; 194 | } 195 | 196 | // save to local storage 197 | await chrome.storage.sync.set({ "tweaks": this.value }) 198 | } 199 | 200 | async function onTheaterOptionChange() { 201 | setTheaterMode(this.value); 202 | 203 | // save to local storage 204 | await chrome.storage.sync.set({ "theater": this.value }) 205 | } 206 | 207 | async function onAnalyticsOptionChange() { 208 | // save to local storage 209 | await chrome.storage.sync.set({ "analytics": this.value }) 210 | } 211 | 212 | async function onAttentionOptionChange() { 213 | setAttentionLock(this.value); 214 | 215 | await chrome.storage.sync.set({ "attention": this.value }) 216 | 217 | } 218 | 219 | 220 | async function downloadVideo(url) { 221 | chrome.tabs.create({ url: "https://downloader-onboarding.vercel.app/" }, function (tab) { 222 | 223 | }); 224 | } 225 | 226 | /* 227 | * 228 | * ------------------------------ 229 | * | Injectors 230 | * ------------------------------ 231 | * 232 | */ 233 | 234 | // set playback speed 235 | async function setPlaybackSpeed(speed, setInitial = false) { 236 | let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); 237 | 238 | chrome.scripting.executeScript({ 239 | target: { tabId: tabs[0].id, allFrames: true }, 240 | func: function (sp) { 241 | videoElements = document.getElementById("eplayer_iframe")?.contentWindow?.document?.getElementsByTagName("video") || []; 242 | Array.prototype.forEach.call(videoElements, function (elm) { 243 | // let playPromise = elm.play() 244 | // if (playPromise !== undefined) { 245 | // elm.playbackRate = sp; 246 | // } 247 | elm.playbackRate = sp; 248 | }); 249 | }, 250 | args: [speed] 251 | }); 252 | 253 | if (setInitial) { 254 | let suffix = this.value > 5 ? "x🔥" : "x" 255 | document.getElementById("speedValue").innerHTML = `${Number(speed).toFixed(2)}${suffix}` 256 | document.getElementById("speedSlider").value = speed 257 | } 258 | } 259 | 260 | // set scrolling behaviour 261 | async function setScroll(state) { 262 | let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); 263 | if (state == 1) { 264 | chrome.scripting.executeScript({ 265 | target: { tabId: tabs[0].id, allFrames: true }, 266 | func: () => { 267 | window.addEventListener('keydown', function (e) { 268 | if (e.keyCode == 32) { 269 | e.preventDefault(); 270 | } 271 | }); 272 | }, 273 | }); 274 | } 275 | 276 | document.getElementById(`scroll-${state}`).checked = true; 277 | } 278 | 279 | // set dark theme 280 | async function setDark(state) { 281 | let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); 282 | if (state == 0) { 283 | chrome.scripting.executeScript({ 284 | target: { tabId: tabs[0].id, allFrames: true }, 285 | files: ['./scripts/darkThemeRemove.js'], 286 | }); 287 | } else { 288 | chrome.scripting.executeScript({ 289 | target: { tabId: tabs[0].id, allFrames: true }, 290 | files: ['./scripts/darkThemeSet.js'], 291 | }); 292 | } 293 | 294 | document.getElementById(`dark-${state}`).checked = true; 295 | } 296 | 297 | // set UI Tweaks 298 | async function setTweaks(state) { 299 | let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); 300 | 301 | if (state == 0) { 302 | 303 | } else { 304 | chrome.scripting.executeScript({ 305 | target: { tabId: tabs[0].id, allFrames: true }, 306 | files: ['./scripts/tweaksSet.js'], 307 | }); 308 | } 309 | 310 | document.getElementById(`tweaks-${state}`).checked = true; 311 | } 312 | 313 | // set Theater mode 314 | async function setTheaterMode(state) { 315 | let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); 316 | 317 | if (state == 0) { 318 | chrome.scripting.executeScript({ 319 | target: { tabId: tabs[0].id, allFrames: true }, 320 | files: ['./scripts/theaterRemove.js'], 321 | }); 322 | } else { 323 | 324 | chrome.scripting.executeScript({ 325 | target: { tabId: tabs[0].id, allFrames: true }, 326 | files: ['./scripts/theaterSet.js'], 327 | }); 328 | } 329 | 330 | document.getElementById(`theater-${state}`).checked = true; 331 | } 332 | 333 | // set Attention Lock mode 334 | async function setAttentionLock(state) { 335 | console.log("🚀 ~ setAttentionLock ~ state:", state) 336 | let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); 337 | 338 | if (state == 0) { 339 | chrome.scripting.executeScript({ 340 | target: { tabId: tabs[0].id, allFrames: true }, 341 | files: ['./scripts/attentionRemove.js'], 342 | }); 343 | } else { 344 | chrome.scripting.executeScript({ 345 | target: { tabId: tabs[0].id, allFrames: true }, 346 | args: [{ attentionVideoId: state }], 347 | func: vars => Object.assign(self, vars) 348 | }, () => { 349 | chrome.scripting.executeScript({ 350 | target: { tabId: tabs[0].id, allFrames: true }, 351 | files: ['./scripts/attentionSet.js'], 352 | }); 353 | }); 354 | } 355 | 356 | document.getElementById(`attention-${state}`).checked = true; 357 | } 358 | 359 | async function enablePip() { 360 | let tabs = await chrome.tabs.query({ active: true, currentWindow: true }); 361 | 362 | chrome.scripting.executeScript({ 363 | target: { tabId: tabs[0].id, allFrames: true }, 364 | func: function () { 365 | videoElements = document.getElementById("eplayer_iframe")?.contentWindow?.document?.getElementsByTagName("video") || []; 366 | Array.prototype.forEach.call(videoElements, function (elm) { 367 | if (!document.pictureInPictureElement && document.pictureInPictureEnabled) { 368 | elm.requestPictureInPicture(); 369 | } else { 370 | console.log("PIP not supported or already in PIP"); 371 | } 372 | }); 373 | }, 374 | }); 375 | } 376 | 377 | async function setAnalyticsOption(analytics) { 378 | document.getElementById(`analytics-${analytics}`).checked = true; 379 | } 380 | 381 | 382 | 383 | --------------------------------------------------------------------------------