Преглед изворни кода

Add 'pluralsight.py'

Rape me senpai - Pluralsight probably
master
CanWePlsRapeTheShitOuttaPluralsight пре 2 година
родитељ
комит
9a98977f66
  1. 353
      pluralsight.py

353
pluralsight.py

@ -0,0 +1,353 @@ @@ -0,0 +1,353 @@
import os
import random
import re
import secrets
import string
import time
from sys import platform
import requests
import youtube_dl
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
# region Global Constant(s) and Readonly Variable(s)
# True/False to determine whether selenium instances will be visible or not (headless)
HIDE_SELENIUM_INSTANCES = False
# The maximum number of courses to download from a single account
MAX_COURSE_DOWNLOAD_COUNT = 5
# Denotes Time.Sleep() duration in seconds
SLEEP_DURATION = 5
# Master Directory Path
MASTER_DIRECTORY = os.path.join(os.path.expanduser("~/Desktop"), "Pluralsight")
# Path of the text file where pluralsight account details will be stored
ACCOUNT_FILE_PATH = os.path.join(MASTER_DIRECTORY, "ps.txt")
# Path of the text file where pluralsight courses to be downloaded will be stored
COURSE_LINKS_FILE_PATH = os.path.join(MASTER_DIRECTORY, "c.txt")
# Path of the directory where downloaded courses will be saved
SAVE_DIRECTORY_PATH = os.path.join(MASTER_DIRECTORY, "Courses")
# Path of the archive text file used by Youtube-dl to keep track of downloaded videos
ARCHIVE_FILE_PATH = os.path.join(MASTER_DIRECTORY, "archive.txt")
# Options for youtube-dl. For a complete list of options, check https://github.com/ytdl-org/youtube-dl/blob/3e4cedf9e8cd3157df2457df7274d0c842421945/youtube_dl/YoutubeDL.py#L137-L312
ydl_options = {
'writesubtitles': True,
'nooverwrites': True,
'download_archive': ARCHIVE_FILE_PATH,
'sleep_interval': 20,
'max_sleep_interval': 40,
# 'outtmpl': f"{SAVE_DIRECTORY_PATH}/%(playlist)s/%(chapter_number)s - %(chapter)s/%(playlist_index)s - %(title)s.%(ext)s"
# Windows Users should comment out the previous outtmpl and uncomment the following
# 'outtmpl': f"{save_directory_path}\\%(playlist)s\\%(chapter_number)s - %(chapter)s\\%(playlist_index)s - %(title)s.%(ext)s"
}
if platform.startswith("win"):
ydl_options[
'outtmpl'] = f"{SAVE_DIRECTORY_PATH}\\%(playlist)s\\%(chapter_number)s - %(chapter)s\\%(playlist_index)s - %(title)s.%(ext)s"
else:
ydl_options[
'outtmpl'] = f"{SAVE_DIRECTORY_PATH}/%(playlist)s/%(chapter_number)s - %(chapter)s/%(playlist_index)s - %(title)s.%(ext)s"
# endregion
class TempGmail:
"""
This class is used to generate random disposable gmails from https://freetempemails.com
and use them for registration purpose
"""
def __init__(self, email_address: str):
self.email_address = email_address
def get_email_id(self) -> object:
post_url = "https://gmailnator.com/mailbox/mailboxquery"
post_data = {
'action': 'LoadMailList',
'Email_address': self.email_address
}
while True:
try:
time.sleep(1)
response_text = requests.post(post_url, post_data).json()[0]['content']
result = re.findall('#(.*)\\">', response_text)
mail_id = result[0]
return mail_id
except Exception as e:
pass
def get_verification_link(self) -> str:
post_url = "https://gmailnator.com/mailbox/get_single_message/"
post_data = {
'action': 'LoadMailList',
'message_id': self.get_email_id(),
'email': self.email_address.split("+")[0]
}
response_data = requests.post(post_url, post_data).text
soup = BeautifulSoup(response_data)
for link in soup.findAll('a', href=True):
if "https://app.pluralsight.com/id/forgotpassword/reset?token" in link['href']:
return link['href']
class Pluralsight:
"""
This class handles the registration, verification and bootstrapping of new Pluralsight accounts
"""
def __init__(self, email: str, password: str, is_headless: bool = True):
if is_headless:
options = Options()
options.add_argument("--headless")
self.driver = webdriver.Firefox(options=options)
else:
self.driver = webdriver.Firefox()
self.email = email
self.password = password
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.driver.quit()
@staticmethod
def get_name() -> str:
"""
Generate a random string to be used as first or last name
Returns:
str: Generated string
"""
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for _ in range(random.randint(5, 15)))
def register(self) -> None:
"""
Registers new Pluralsight account
"""
self.driver.get("https://www.pluralsight.com/offer/2020/free-april-month")
time.sleep(SLEEP_DURATION)
accept_cookie_button_element = self.driver.find_element_by_class_name("cookie_notification--opt_in")
accept_cookie_button_element.click()
time.sleep(1)
sign_up_now_button_element = self.driver.find_element_by_xpath('//a[@data-aa-title="Free-April-Start-Now"]')
sign_up_now_button_element.click()
time.sleep(1)
email_input_element = self.driver.find_element_by_name("email")
firstname_input_element = self.driver.find_element_by_name("firstname")
lastname_input_element = self.driver.find_element_by_name("lastname")
tos_checkbox_element = self.driver.find_element_by_name("optInBox")
email_input_element.send_keys(self.email)
firstname_input_element.send_keys(self.get_name())
lastname_input_element.send_keys(self.get_name())
tos_checkbox_element.click()
time.sleep(SLEEP_DURATION)
create_account_button_element = self.driver.find_element_by_xpath(
"//*[contains(text(), 'I agree, activate benefit')]")
create_account_button_element.click()
time.sleep(30)
cancel_button_element = self.driver.find_element_by_class_name("cancelButton---CKAut")
cancel_button_element.click()
def set_password(self, verification_link: str) -> None:
"""
Sets password in the given verification link
Args:
verification_link: The verification link (as string) to set up password
"""
self.driver.get(verification_link)
time.sleep(SLEEP_DURATION)
password_input_element = self.driver.find_element_by_id("Password")
password_confirm_input_element = self.driver.find_element_by_id("PasswordConfirmation")
save_button_element = self.driver.find_element_by_class_name("psds-button--appearance-primary")
password_input_element.send_keys(self.password)
password_confirm_input_element.send_keys(self.password)
time.sleep(1)
save_button_element.click()
time.sleep(SLEEP_DURATION)
def bootstrap(self):
"""
Bootstraps newly registered accounts to prevent 403 errors in youtube-dl
"""
username_input_element = self.driver.find_element_by_id("Username")
password_input_element = self.driver.find_element_by_id("Password")
login_button_element = self.driver.find_element_by_id("login")
username_input_element.send_keys(self.email)
password_input_element.send_keys(self.password)
time.sleep(1)
login_button_element.click()
time.sleep(SLEEP_DURATION)
cancel_button_element = self.driver.find_element_by_class_name("cancelButton---CKAut")
cancel_button_element.click()
def get_password(length: int = 30) -> str:
"""
Generates a random password using ascii letters and numerical digits
Args:
length: Length of the password, default is 30
Returns: Generated password as string
"""
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for _ in range(length))
return password
def generate_email(is_gmail: bool = True) -> str:
"""
Generates a new email
Returns: Generated email string
"""
gmailnator_gen_url = "https://gmailnator.com/index/indexquery"
post_data = {
'action': 'GenerateEmail'
}
email = requests.post(gmailnator_gen_url, post_data).text
return email
def create_pluralsight_account() -> None:
"""
Creates new Pluralsight account using Pluralsight and TempMail
"""
try:
email = generate_email()
password = get_password()
with Pluralsight(email=email, password=password, is_headless=HIDE_SELENIUM_INSTANCES) as ps:
ps.register()
verification_link = TempGmail(email_address=email).get_verification_link()
ps.set_password(verification_link=verification_link)
time.sleep(SLEEP_DURATION)
with open(ACCOUNT_FILE_PATH, 'w+') as account_file:
account_file.write(f"{email}\n")
account_file.write(f"{password}\n")
except Exception as e:
print(f"ERROR OCCURRED!!\n\nDETAILS: {e.__str__()} | {e.__context__}")
def download_course(course_link: str, username: str, password: str) -> bool:
"""
Download the given course using the provided credential
Args:
course_link: The link of the course to download
username: Username (Email) of the Pluralsight account to be used for download
password: Password of the Pluralsight account to be used for download
Returns: True/False bool value denoting the success status of the download
"""
try:
ydl_options['username'] = username
ydl_options['password'] = password
with youtube_dl.YoutubeDL(ydl_options) as ydl:
ydl.download([course_link])
return True
except Exception as exception:
return False
def main():
if not os.path.exists(COURSE_LINKS_FILE_PATH):
print(f"{COURSE_LINKS_FILE_PATH} NOT FOUND!")
return
with open(COURSE_LINKS_FILE_PATH, 'r') as course_file:
course_list = [course.rstrip() for course in course_file.readlines()]
download_count = 0
for course in course_list:
try:
while True:
if not os.path.exists(ACCOUNT_FILE_PATH) or download_count > 5:
print("CREATING NEW PLURALSIGHT ACCOUNT")
create_pluralsight_account()
download_count = 0
print("SUCCESS! NEW PLURALSIGHT ACCOUNT CREATED.")
with open(ACCOUNT_FILE_PATH, 'r') as account_file:
lines = account_file.readlines()
email = lines[0].rstrip()
password = lines[1].rstrip()
print(f"[{email}] DOWNLOADING COURSE: {course}")
is_download_success = download_course(course, username=email, password=password)
if not is_download_success:
os.remove(ACCOUNT_FILE_PATH)
continue
print(f"[{email}] SUCCESSFULLY DOWNLOADED COURSE: {course}")
break
except Exception as e:
print(e)
finally:
download_count += 1
if __name__ == '__main__':
main()
Loading…
Откажи
Сачувај