├── .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 |
59 |
60 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
88 |
89 |
90 |
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 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
191 |
192 |
193 |
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 |
--------------------------------------------------------------------------------