Skip to content

Instantly share code, notes, and snippets.

@Maxim-Mazurok
Last active April 16, 2020 14:42
Show Gist options
  • Save Maxim-Mazurok/ab6d4cf847be1d3e2c83a57cdc7e73d2 to your computer and use it in GitHub Desktop.
Save Maxim-Mazurok/ab6d4cf847be1d3e2c83a57cdc7e73d2 to your computer and use it in GitHub Desktop.
Download all available Vue Mastery lessons
/**
* Tested with "selenium-webdriver": "^4.0.0-alpha.7"
* and NodeJS v12.14.0
* on April 17, 2020
*
* Make sure to download Chrome selenium WebDriver for your version: https://chromedriver.chromium.org/downloads
* and put it in the same folder as this file or add to PATH
* Tested on Chrome 81
*
* This script downloads pages with embedded videos, lesson materials and links to challenges
* This will NOT download videos, only links to them (no auth required to view them if you know the link)
*
* Create "out" folder and run like this: `EMAIL="example@gmail.com" PASSWORD="yourPassword" node index.js`
*/
const fs = require("fs");
const chrome = require("selenium-webdriver/chrome");
const { Builder, By, Key, until } = require("selenium-webdriver");
const width = 1920;
const height = 1080;
const email = process.env.EMAIL;
const password = process.env.PASSWORD;
const x = {
loginButton:
'//*[@id="__layout"]/div/div/div/header/div/nav/div[2]/button[2]',
emailInput: '//*[@id="__layout"]/div/div[2]/div/div[2]/form/div[2]/input',
passwordInput: '//*[@id="__layout"]/div/div[2]/div/div[2]/form/div[3]/input',
courseCard:
'//*[@id="__layout"]/div/div/div/div[2]/div[2]/section/div/div/span/a[1]',
lessonTitle: '//*[@id="lessonContent"]/div/h1',
profilePicture: '//*[@id="__layout"]/div/div/div/header/div/nav/div[2]/a',
vimeoPlayer: '//*[@data-vimeo-initialized="true"]//iframe',
nextLesson: '//*[@id="__layout"]/div/div/div/div[2]/div/div[5]/button[2]',
};
const timeout = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
let driver;
(async () => {
try {
driver = await new Builder()
.forBrowser("chrome")
.setChromeOptions(
new chrome.Options().headless().windowSize({ width, height })
)
.build();
console.log("Logging in");
await driver.get("https://www.vuemastery.com");
await driver.wait(until.elementLocated(By.xpath(x.loginButton)));
await driver.findElement(By.xpath(x.loginButton)).click();
await driver.wait(until.elementLocated(By.xpath(x.emailInput)));
await driver.findElement(By.xpath(x.emailInput)).sendKeys(email);
await driver.wait(until.elementLocated(By.xpath(x.passwordInput)));
await driver
.findElement(By.xpath(x.passwordInput))
.sendKeys(password, Key.RETURN);
await driver.wait(until.elementLocated(By.xpath(x.profilePicture)));
console.log("Logged in, fetching courses");
await driver.navigate().to("https://www.vuemastery.com/courses");
await driver.wait(until.elementLocated(By.xpath(x.courseCard)));
const courses = await driver.findElements(By.className("course-card"));
const courseUrls = await Promise.all(
courses.map((course) => course.getAttribute("href"))
);
console.log("Courses fetched");
async function savePage(url, index) {
console.log(`saving ${url}`);
await Promise.all([
driver.wait(until.elementLocated(By.xpath(x.vimeoPlayer))),
driver.wait(until.elementLocated(By.xpath(x.lessonTitle))),
driver.wait(until.elementLocated(By.xpath(x.nextLesson))),
]);
const source = await driver.getPageSource();
const urlParts = new URL(url).pathname.split("/").splice(2);
const dirName = `${__dirname}/out/${urlParts[0]}`;
const fileName = `${index}_${urlParts[1]}.html`;
!fs.existsSync(dirName) && fs.mkdirSync(dirName);
fs.writeFileSync(`${dirName}/${fileName}`, source);
}
async function saveNextPage(index) {
console.log(`saving next page ${index}`);
const nextLessonButton = await driver.findElement(By.xpath(x.nextLesson));
if ((await nextLessonButton.getAttribute("disabled")) === null) {
await nextLessonButton.click();
await timeout(5 * 1000); // let new lesson load
const url = await driver.getCurrentUrl();
await savePage(url, index);
const newNextLessonButton = await driver.findElement(
By.xpath(x.nextLesson)
);
if ((await newNextLessonButton.getAttribute("disabled")) === null) {
await saveNextPage(index + 1);
}
}
}
for (courseUrl of courseUrls) {
console.log(`fetching course first page ${courseUrl}`);
await driver.navigate().to(courseUrl);
await savePage(courseUrl, 1);
await saveNextPage(2);
}
} catch (e) {
console.error(e);
} finally {
driver.quit();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment