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;
}
})();
@andyjf0
Copy link

andyjf0 commented Oct 13, 2020

This no longer works : (

@lundeen-bryan
Copy link

Can someone teach me how to fix this script? I would maintain it full-time.

@edenizk
Copy link

edenizk commented Jan 17, 2022

Tbh idk what does the above code do, but if someone wants to check how many mins left for their course
you can copy-paste the code below to your console in dev tools :)

function getLeftTime() {
  // Open all the sections
  console.log('Opening Sections...');
  document.querySelectorAll('.js-panel-toggler').forEach((item) => {
    const isExpanded = item.getAttribute('aria-expanded');
    if (!isExpanded) {
      item.click();
    }
  })
  console.log('Opening Sections DONE');

  console.log('Calculation Total ...')
  let total = 0;
  const items = document.querySelectorAll('.item-link.udlite-custom-focus-visible') // each item
  items.forEach((item) => {
    // console.log(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', '');
        // console.log('time', time);
        total+= parseInt(time);
      }
    }
  })
  console.log('Calculation Total DONE')

  console.log('left Time(Min): ', total);
}
getLeftTime()

@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