您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Calculate and display amount of time spent watching lectures and how much time remains to be spent watching lectures for the course.
// ==UserScript== // @name Teachable Time Helper // @namespace http://tampermonkey.net/ // @version 2024-05-05 // @description Calculate and display amount of time spent watching lectures and how much time remains to be spent watching lectures for the course. // @author Daniel Hillis (https://github.com/dhillis) // @match https://learn.cantrill.io/courses/enrolled/* // @icon https://www.google.com/s2/favicons?sz=64&domain=cantrill.io // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; class Section { constructor(sectionName){ this.name = sectionName; this.totalSeconds = 0; this.completedSeconds = 0; } get remainingSeconds(){ return this.totalSeconds - this.completedSeconds; } } function getDurationString(timeInSeconds) { let hours = Math.trunc(timeInSeconds/3600); let hoursRemainder = timeInSeconds%3600; let minutes = Math.trunc(hoursRemainder/60); let seconds = hoursRemainder%60; if (hours > 0){ return (hours.toString() .concat(':') .concat(String(minutes).padStart(2, '0')) .concat(':') .concat(String(seconds).padStart(2, '0'))); } else { return (String(minutes) .concat(':') .concat(String(seconds).padStart(2, '0'))); } } var sections = $('.course-mainbar div.row div.course-section'); var sectionCount = sections.length; var sectionCourses = $('.course-mainbar div.row div.course-section ul.section-list'); var lectureCount = $('span.lecture-name').length; const regexpCourseDurationMMSS = /([0-9]{1,2})(?=:):([0-9]{2})/; const sectionMap = new Map(); let overallDurationSeconds = 0; let overallDurationCompletedSeconds = 0; for(let sec of document.querySelectorAll(".course-mainbar div.row div.course-section")) { let currentSection = sec.querySelector("div.section-title"); let currentSectionTitle = currentSection.innerText; // store the map for this section let currentSectionCounts = new Section(currentSectionTitle); sectionMap.set(currentSectionTitle, currentSectionCounts); for(let c of sec.querySelectorAll("ul.section-list li.section-item")) { let courseName = c.querySelector("span.lecture-name").innerText; let completed = c.classList.contains("completed"); // courseName sample: // [ASSOCIATESHARED] [DEMO] Automated EC2 Control using Lambda and Events - PART2 (18:49 ) // get duration from the course title let duration = courseName.match(regexpCourseDurationMMSS); if (duration && duration.length === 3){ let minutes = parseInt(duration[1]); let seconds = parseInt(duration[2]); let courseTotalSeconds = seconds + ( minutes * 60 ); currentSectionCounts.totalSeconds+= courseTotalSeconds; overallDurationSeconds += courseTotalSeconds; if (completed){ currentSectionCounts.completedSeconds+= courseTotalSeconds; overallDurationCompletedSeconds+= courseTotalSeconds; } } } currentSection.innerText += ` (${getDurationString(currentSectionCounts.remainingSeconds)} remaining)`; } var tbl = document.createElement('table'); tbl.style.width = '100%'; tbl.setAttribute('border', '1'); var tbdy = document.createElement('tbody'); let playback_speeds = [0.50, 0.75, 1.00, 1.25, 1.50, 2.00]; const cellpadding = "10px"; const headfootbgcolor = "#5a1d82"; const headfootfontsize = "120%"; const cellhighlightcolor = "#e0d7e6"; const white = "#FFFFFF"; const speed1fontsize = "115%"; const speed1fontweight = "900"; let table = document.createElement('TABLE'); table.border = '1'; table.style.fontSize = "50%"; table.style.textAlign = "center"; let tableHead = document.createElement('THEAD'); tableHead.style.backgroundColor = headfootbgcolor; tableHead.style.color = white; let headrow1 = document.createElement('TR'); let headrow1_th1 = document.createElement('TH'); headrow1_th1.appendChild(document.createTextNode('Section')) headrow1_th1.style.textAlign = "center"; headrow1_th1.style.paddingLeft = cellpadding; headrow1_th1.style.paddingRight = cellpadding; headrow1_th1.rowSpan = 2; headrow1.appendChild(headrow1_th1); let headrow1_th2 = document.createElement('TH'); headrow1_th2.appendChild(document.createTextNode('Playback Speeds')) headrow1_th2.style.textAlign = "center"; headrow1_th2.style.paddingLeft = cellpadding; headrow1_th2.style.paddingRight = cellpadding; headrow1_th2.colSpan = playback_speeds.length; headrow1.appendChild(headrow1_th2); tableHead.appendChild(headrow1); let headrow2 = document.createElement('TR'); for(let speed of playback_speeds){ let th = document.createElement('TH'); th.appendChild(document.createTextNode(`${speed.toFixed(2)}X`)); th.style.textAlign = "center"; th.style.paddingLeft = cellpadding; th.style.paddingRight = cellpadding; if (speed == 1.0) { th.style.fontWeight = speed1fontweight; th.style.fontSize = speed1fontsize; } tableHead.style.fontSize = headfootfontsize; headrow2.appendChild(th); } tableHead.appendChild(headrow2); table.appendChild(tableHead); let tableBody = document.createElement('TBODY'); for (const [key, value] of sectionMap) { var sectionTR = document.createElement('TR'); let sectionNameTD = document.createElement('TD'); sectionNameTD.appendChild(document.createTextNode(key)); sectionNameTD.style.paddingLeft = cellpadding; sectionNameTD.style.paddingRight = cellpadding; sectionNameTD.style.textAlign = "center"; sectionTR.appendChild(sectionNameTD); for (let speed of playback_speeds) { var td = document.createElement('TD'); td.style.textAlign = "center"; td.style.paddingLeft = cellpadding; td.style.paddingRight = cellpadding; if (speed == 1.0) { td.style.backgroundColor = cellhighlightcolor; td.style.fontWeight = speed1fontweight; td.style.fontSize = speed1fontsize; } td.appendChild(document.createTextNode(getDurationString(Math.round(value.remainingSeconds / speed)))); sectionTR.appendChild(td); } tableBody.appendChild(sectionTR); } table.appendChild(tableBody); const overall_rt_secs = overallDurationSeconds - overallDurationCompletedSeconds; let tableFoot = document.createElement('TFOOT'); tableFoot.style.backgroundColor = headfootbgcolor; tableFoot.style.color = white; let tfth1 = document.createElement('TH'); tfth1.appendChild(document.createTextNode('Overall Remaining Time')); tfth1.style.textAlign = "center"; tfth1.style.paddingLeft = cellpadding; tfth1.style.paddingRight = cellpadding; tableFoot.appendChild(tfth1); for(let speed of playback_speeds){ let th = document.createElement('TH'); th.appendChild(document.createTextNode(`${getDurationString(Math.round(overall_rt_secs / speed))}`)); th.style.textAlign = "center"; th.style.paddingLeft = cellpadding; th.style.paddingRight = cellpadding; if (speed == 1.0) { th.style.fontWeight = speed1fontweight; } tableFoot.style.fontSize = headfootfontsize; tableFoot.appendChild(th); } table.appendChild(tableFoot); $('.course-mainbar .section-title').first().append(table); })();