Last active
April 16, 2020 14:42
-
-
Save Maxim-Mazurok/ab6d4cf847be1d3e2c83a57cdc7e73d2 to your computer and use it in GitHub Desktop.
Download all available Vue Mastery lessons
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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="[email protected]" 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