Skip to content

Instantly share code, notes, and snippets.

@jithurjacob
Last active March 4, 2024 16:37
Show Gist options
  • Save jithurjacob/4af5e9e8d231d79f17f6e553ba166864 to your computer and use it in GitHub Desktop.
Save jithurjacob/4af5e9e8d231d79f17f6e553ba166864 to your computer and use it in GitHub Desktop.
Script to find Udemy Course remaining time
//credits - https://greasyfork.org/en/scripts/28295-udemy-show-section-time
// greasyfonrk was throwing me an error so I created this gist
(function() {
$.getScript("https://greasyfork.org/scripts/5392-waitforkeyelements/code/WaitForKeyElements.js?version=115012", function(){
// waits for the cards to be loaded
waitForKeyElements(selectors.sectionCard, run, true);
});
var selectors = {
sectionCard: 'curriculum-navigation-section',
sectionName: '.curriculum-navigation__section__title',
// class needed by this user script
sectionTime: '.section-time',
lectureItem: '.lecture__item',
lectureTime: '.lecture__item__link__time',
lectureStatus: '.cur-status',
lectureCheck: '.udi-check',
lectureProgress : '#top-detail > div.detail__progress > div > div.fx',
};
// run();
/**
Gets total and remaining time for each section.
Displays these per section.
Display the total of all sections
*/
function run() {
var sections = $(selectors.sectionCard);
var totalLectureTime = 0;
var remainingLectureTime = 0;
$.each(sections, function(index, section) {
// remove previous time display
$(section).find(selectors.sectionTime).remove();
// get the section title
var title = $(section).find(selectors.sectionName).text();
// get the total times
var totalTimeTexts = getTimeTexts(section, false);
var totalTimeSeconds = textTimesToSeconds(totalTimeTexts);
var totalTime = secondsToTextTime(totalTimeSeconds);
// update the lecture time
totalLectureTime += totalTimeSeconds;
// initialize the text to display with the total time
var textToDisplay = totalTime;
// check if we need to display partial time
if (checkPartialTime(section)) {
// sum the partial times
var partialTimeTexts = getTimeTexts(section, true);
var partialTimeSeconds = textTimesToSeconds(partialTimeTexts);
var partialTime = secondsToTextTime(partialTimeSeconds);
// update the lecture time
remainingLectureTime += partialTimeSeconds;
// prepend partial time
textToDisplay = partialTime + ' / ' + textToDisplay;
}
// check if we need to add up the total time to remaining time
if (getRemainingParts(section) === 0) {
remainingLectureTime += totalTimeSeconds;
}
// display the section text
displaySectionTime(section, textToDisplay);
});
// display lecture totals
displayLectureTimeProgress(totalLectureTime, remainingLectureTime);
// stops the script from running continuously
return false;
}
function displayLectureTimeProgress(totalLectureTime, remainingLectureTime) {
// start with total
var displayText = secondsToTextTime(totalLectureTime);
// conditional add remaining
if (remainingLectureTime) {
displayText = secondsToTextTime(remainingLectureTime) + ' / ' + displayText;
}
// surround in parens
displayText = '(' + displayText + ')';
// add to DOM
var lectureProgressClass = 'lecture-progress-time';
var lectureProgressSpans = $(selectors.lectureProgress).find('.' + lectureProgressClass);
if (lectureProgressSpans.length > 0) {
$(lectureProgressSpans[0]).text(displayText);
} else {
$(selectors.lectureProgress + ' > div').before(
'<span'
+ ' class="'+ lectureProgressClass + '"'
+ ' style="margin-left: 1em;"'
+'">'
+ displayText + '</span>'
);
}
return displayText;
}
/*
Gets the section parts and checks if it's not 0 or the total parts
*/
function checkPartialTime(section) {
// get the section parts
var sectionParts = getSectionParts(section);
// determine what times to get
var totalSections = sectionParts[1];
var sectionsToGo = sectionParts[0];
return sectionsToGo !== 0 && sectionsToGo != totalSections;
}
function getRemainingParts(section) {
// get the section parts
var sectionParts = getSectionParts(section);
// determine what times to get
return sectionParts[0];
}
/**
Get the section's time. Whether partial or total is determined by the passed in
parameter.
*/
function getTimeTexts(section, isPartialTime) {
// get the times
var $lectures = $(section).find(selectors.lectureItem);
// Check for partial time
if (isPartialTime) {
// filter down to just the non-completed ones
$lectures = $lectures.filter(':not(.completed)');
}
// get the time spans
var timeSpans = $lectures.find(selectors.lectureTime);
// convert to timeTexts and return
return convertTimeSpansToTexts(timeSpans);
}
function displaySectionTime(section, displayText) {
var sectionTimeClass = 'section-time';
// prepend to lecture status
var totalTimeSpan = $(section).find(selectors.lectureStatus).find(sectionTimeClass);
// check to see if we've already added the time to the DOM
if (totalTimeSpan.length > 0) {
$(totalTimeSpan[0]).text(displayText);
} else {
// we haven't, so create the element and add it
$(section).find(selectors.lectureStatus)
.prepend('<span class="' + sectionTimeClass + '" style="margin-right:1em">'+ displayText + '</span>');
}
}
function getSectionParts(section) {
return $(section).find(selectors.lectureStatus).text()
// split up the parts by "/"
.split('/')
// trim up the space
.map(function(text) { return text.trim(); });
}
function convertTimeSpansToTexts(timeSpans) {
var timeTexts = [];
for (var i=0; i<timeSpans.length; i++) {
timeTexts.push($(timeSpans[i]).text());
}
return timeTexts;
}
function convertTextToSeconds(textTime) {
var timeParts = textTime.split(':');
var seconds = parseInt(timeParts[1]);
seconds += parseInt(timeParts[0]) * 60;
return seconds;
}
// get the summation of the times
function textTimesToSeconds(textTimes) {
var totalSeconds = 0;
// get total minutes
$.each(textTimes, function(index, textTime) {
totalSeconds += convertTextToSeconds(textTime);
});
return totalSeconds;
}
function secondsToTextTime(totalSeconds) {
// convert back to hh:mm
var hours = Math.floor(totalSeconds / 60 / 60);
var remainingTime = totalSeconds - hours * 60 * 60;
var minutes = Math.floor(remainingTime / 60);
remainingTime = remainingTime - minutes * 60;
var seconds = remainingTime;
return getTime(hours, minutes, seconds);
}
// get the time in the following format -> hh:mm:ss
function getTime(hours, minutes, seconds) {
var result = '';
// get the hours part
if (hours > 0) {
result += hours + ':';
result += timePad(minutes);
} else {
// we didn't have any hours
result += minutes;
}
// check for minutes
if (minutes > 0) {
result += ':' + timePad(seconds);
} else {
// we didn't have any minutes, but we should say 0
result += '0:' + timePad(seconds);
}
return result;
}
// time should be a length of 2, so prepend with 0
function timePad(timeSegment) {
var result = timeSegment + '';
while (result.length < 2) {
result = '0' + result;
}
return result;
}
})();
@ramialkawadri
Copy link

An updated version that works:

(function getLeftTime() {
    const scrollTopValue = window.scrollY;

    // Opening unopened sections
    const unopened = [];
    document
        .querySelectorAll('.section--section--BukKG > span')
        .forEach((item) => {
            const isExpanded = item.getAttribute('data-checked') === 'checked';
            if (!isExpanded) {
                item.parentNode
                    .querySelector('.udlite-accordion-panel-title')
                    .click();
                unopened.push(item);
            }
        });

    // Calculating total
    let total = 0;
    const items = document.querySelectorAll(
        '.item-link.udlite-custom-focus-visible'
    );
    items.forEach((item) => {
        const isChecked = item.querySelector(
            '.udlite-real-toggle-input'
        ).checked;

        if (!isChecked) {
            let timer = item.querySelector('.udlite-text-xs span');
            if (timer) {
                time = timer.innerText.replace('min', '');
                total += parseInt(time);
            }
        }
    });

    unopened.forEach((item) =>
        item.parentNode.querySelector('.udlite-accordion-panel-title').click()
    );
    window.scrollTo({ top: scrollTopValue });

    const hourString =
        total / 60.0 < 10
            ? '0' + String(Math.trunc(total / 60.0))
            : String(Math.trunc(total / 60.0));
    const minuteString =
        total % 60.0 < 10
            ? '0' + String(Math.trunc(total % 60.0))
            : String(Math.trunc(total % 60.0));

    console.log(`Time left (HH:MM): ${hourString}:${minuteString}`);
})();

@lundeen-bryan
Copy link

lundeen-bryan commented Aug 29, 2023

I created a gist with updated working code here:
https://gist.github.com/lundeen-bryan/3fc513ce89564af0904c053b56aba36f

I will try to keep it updated for everyone, but it's pretty straight forward and has good comments to document what each block of js is doing.

@edenizk
Copy link

edenizk commented Mar 4, 2024

You can find my repo here for it : https://github.com/edenizk/udemyRemainingTimerExtention

Also I've added as an extension in extension store:
https://chromewebstore.google.com/detail/udemy-remaining-time-calc/klpecghaflmikhifcnphellhoacmnibg

If anyone needs any new feature just leave it in issue section, or create a PR 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment