Spaces:
Running
Running
Update cli.py
Browse files
cli.py
CHANGED
@@ -1,37 +1,15 @@
|
|
1 |
import threading
|
2 |
import time
|
3 |
import traceback
|
4 |
-
import
|
5 |
-
|
6 |
-
import requests
|
7 |
-
import cloudscraper # Required for Udemy session handling
|
8 |
from base import VERSION, LoginException, Scraper, Udemy, scraper_dict
|
9 |
from colors import bw, by, fb, fg, fr
|
10 |
-
from requests.utils import cookiejar_from_dict, dict_from_cookiejar
|
11 |
|
12 |
# DUCE-CLI
|
13 |
|
14 |
-
COOKIE_FILE = "udemy_cookies.pkl" # File to store cookies
|
15 |
-
|
16 |
-
|
17 |
-
def save_cookies(session):
|
18 |
-
"""Save session cookies as a dictionary"""
|
19 |
-
with open(COOKIE_FILE, "wb") as f:
|
20 |
-
pickle.dump(dict_from_cookiejar(session.cookies), f) # Convert cookies to a dictionary
|
21 |
-
|
22 |
-
|
23 |
-
def load_cookies():
|
24 |
-
"""Load cookies from a file if it exists and return as a dictionary"""
|
25 |
-
if os.path.exists(COOKIE_FILE):
|
26 |
-
with open(COOKIE_FILE, "rb") as f:
|
27 |
-
cookies = pickle.load(f)
|
28 |
-
if isinstance(cookies, dict): # Ensure cookies are stored as a dictionary
|
29 |
-
return cookies
|
30 |
-
return None
|
31 |
-
|
32 |
-
|
33 |
def create_scraping_thread(site: str):
|
34 |
-
"""Creates a separate thread
|
35 |
code_name = scraper_dict[site]
|
36 |
try:
|
37 |
t = threading.Thread(target=getattr(scraper, code_name), daemon=True)
|
@@ -43,98 +21,113 @@ def create_scraping_thread(site: str):
|
|
43 |
if getattr(scraper, f"{code_name}_length") == -1:
|
44 |
raise Exception(f"Error in: {site}")
|
45 |
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
except Exception as e:
|
49 |
-
error = traceback.format_exc()
|
50 |
-
print(f"
|
|
|
51 |
|
|
|
52 |
|
53 |
-
############## MAIN #############
|
54 |
udemy = Udemy("cli")
|
55 |
udemy.load_settings()
|
56 |
login_title, main_title = udemy.check_for_update()
|
57 |
|
58 |
if "Update" in login_title:
|
59 |
-
print(login_title)
|
|
|
60 |
|
61 |
-
|
62 |
-
session = cloudscraper.create_scraper() # Use cloudscraper instead of requests.Session()
|
63 |
|
64 |
-
|
65 |
-
|
66 |
-
if cookies:
|
67 |
-
print("Trying to log in using saved cookies...")
|
68 |
try:
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
udemy.
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
email, password = udemy.settings["email"], udemy.settings["password"]
|
85 |
-
print(f"Trying to log in using saved credentials: {email}")
|
86 |
-
else:
|
87 |
-
email = input("Email: ")
|
88 |
-
password = input("Password: ")
|
89 |
-
|
90 |
udemy.manual_login(email, password)
|
91 |
-
udemy.get_session_info() # Ensure login was successful
|
92 |
|
93 |
-
|
|
|
94 |
udemy.settings["email"], udemy.settings["password"] = email, password
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
print(
|
101 |
-
|
|
|
|
|
102 |
|
103 |
-
|
104 |
-
print(f"Login Failed: {e}")
|
105 |
-
if "Browser" in login_title:
|
106 |
-
print("Can't login using cookies")
|
107 |
-
os.remove(COOKIE_FILE) # Delete invalid cookies
|
108 |
-
elif "Email" in login_title:
|
109 |
-
udemy.settings["email"], udemy.settings["password"] = "", ""
|
110 |
-
udemy.save_settings()
|
111 |
|
112 |
print(fg + f"Logged in as {udemy.display_name}")
|
|
|
|
|
|
|
113 |
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
exit()
|
118 |
|
119 |
scraper = Scraper(udemy.sites)
|
120 |
|
121 |
try:
|
122 |
-
|
123 |
-
|
|
|
124 |
udemy.scraped_data = scraper.get_scraped_courses(create_scraping_thread)
|
125 |
time.sleep(0.5)
|
126 |
-
print("\nScraping completed. Enrolling in courses...\n")
|
127 |
|
128 |
-
|
|
|
|
|
129 |
udemy.start_enrolling()
|
130 |
|
131 |
-
print(
|
132 |
-
|
133 |
-
|
134 |
-
print(
|
135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
|
137 |
except Exception as e:
|
138 |
-
|
|
|
|
|
139 |
|
140 |
-
input("Press Enter to exit...")
|
|
|
|
|
|
1 |
import threading
|
2 |
import time
|
3 |
import traceback
|
4 |
+
import sys
|
5 |
+
|
|
|
|
|
6 |
from base import VERSION, LoginException, Scraper, Udemy, scraper_dict
|
7 |
from colors import bw, by, fb, fg, fr
|
|
|
8 |
|
9 |
# DUCE-CLI
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
def create_scraping_thread(site: str):
|
12 |
+
"""Creates a separate thread for scraping each site."""
|
13 |
code_name = scraper_dict[site]
|
14 |
try:
|
15 |
t = threading.Thread(target=getattr(scraper, code_name), daemon=True)
|
|
|
21 |
if getattr(scraper, f"{code_name}_length") == -1:
|
22 |
raise Exception(f"Error in: {site}")
|
23 |
|
24 |
+
prev_progress = 0
|
25 |
+
total = getattr(scraper, f"{code_name}_length")
|
26 |
+
|
27 |
+
while not getattr(scraper, f"{code_name}_done"):
|
28 |
+
time.sleep(0.5)
|
29 |
+
current_progress = getattr(scraper, f"{code_name}_progress")
|
30 |
+
percent = (current_progress / total) * 100 if total else 0
|
31 |
+
print(f"[{site}] Progress: {percent:.2f}%")
|
32 |
+
sys.stdout.flush()
|
33 |
+
|
34 |
+
print(f"[{site}] Scraping Completed β
")
|
35 |
+
sys.stdout.flush()
|
36 |
|
37 |
except Exception as e:
|
38 |
+
error = getattr(scraper, f"{code_name}_error", traceback.format_exc())
|
39 |
+
print(f"[ERROR] {site}: {error}")
|
40 |
+
sys.stdout.flush()
|
41 |
|
42 |
+
##########################################
|
43 |
|
|
|
44 |
udemy = Udemy("cli")
|
45 |
udemy.load_settings()
|
46 |
login_title, main_title = udemy.check_for_update()
|
47 |
|
48 |
if "Update" in login_title:
|
49 |
+
print(by + fr + login_title)
|
50 |
+
sys.stdout.flush()
|
51 |
|
52 |
+
############## MAIN #############
|
|
|
53 |
|
54 |
+
login_successful = False
|
55 |
+
while not login_successful:
|
|
|
|
|
56 |
try:
|
57 |
+
if udemy.settings["use_browser_cookies"]:
|
58 |
+
udemy.fetch_cookies()
|
59 |
+
login_method = "Browser Cookies"
|
60 |
+
elif udemy.settings["email"] and udemy.settings["password"]:
|
61 |
+
email, password = udemy.settings["email"], udemy.settings["password"]
|
62 |
+
login_method = "Saved Email and Password"
|
63 |
+
else:
|
64 |
+
email = input("Email: ")
|
65 |
+
password = input("Password: ")
|
66 |
+
login_method = "Email and Password"
|
67 |
+
|
68 |
+
print(fb + f"Trying to login using {login_method}")
|
69 |
+
sys.stdout.flush()
|
70 |
+
|
71 |
+
if "Email" in login_method:
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
udemy.manual_login(email, password)
|
|
|
73 |
|
74 |
+
udemy.get_session_info()
|
75 |
+
if "Email" in login_method:
|
76 |
udemy.settings["email"], udemy.settings["password"] = email, password
|
77 |
+
login_successful = True
|
78 |
+
except LoginException as e:
|
79 |
+
print(fr + f"Login Failed: {e}")
|
80 |
+
sys.stdout.flush()
|
81 |
+
if "Browser" in login_method:
|
82 |
+
print("Can't login using cookies. Switching to manual login.")
|
83 |
+
udemy.settings["use_browser_cookies"] = False
|
84 |
+
elif "Email" in login_method:
|
85 |
+
udemy.settings["email"], udemy.settings["password"] = "", ""
|
86 |
|
87 |
+
udemy.save_settings()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
|
89 |
print(fg + f"Logged in as {udemy.display_name}")
|
90 |
+
sys.stdout.flush()
|
91 |
+
|
92 |
+
user_dumb = udemy.is_user_dumb()
|
93 |
|
94 |
+
if user_dumb:
|
95 |
+
print(bw + fr + "Invalid user settings. Exiting...")
|
96 |
+
sys.stdout.flush()
|
97 |
exit()
|
98 |
|
99 |
scraper = Scraper(udemy.sites)
|
100 |
|
101 |
try:
|
102 |
+
print("π Starting Course Scraping...")
|
103 |
+
sys.stdout.flush()
|
104 |
+
|
105 |
udemy.scraped_data = scraper.get_scraped_courses(create_scraping_thread)
|
106 |
time.sleep(0.5)
|
|
|
107 |
|
108 |
+
print("\nβ
Scraping Completed. Starting Enrollment...\n")
|
109 |
+
sys.stdout.flush()
|
110 |
+
|
111 |
udemy.start_enrolling()
|
112 |
|
113 |
+
udemy.print(
|
114 |
+
f"\nβ Successfully Enrolled: {udemy.successfully_enrolled_c}", color="green"
|
115 |
+
)
|
116 |
+
udemy.print(
|
117 |
+
f"π° Amount Saved: {round(udemy.amount_saved_c,2)} {udemy.currency.upper()}",
|
118 |
+
color="light green",
|
119 |
+
)
|
120 |
+
udemy.print(f"π΅ Already Enrolled: {udemy.already_enrolled_c}", color="blue")
|
121 |
+
udemy.print(f"β οΈ Excluded Courses: {udemy.excluded_c}", color="yellow")
|
122 |
+
udemy.print(f"β Expired Courses: {udemy.expired_c}", color="red")
|
123 |
+
|
124 |
+
sys.stdout.flush()
|
125 |
|
126 |
except Exception as e:
|
127 |
+
error = traceback.format_exc()
|
128 |
+
print(f"\n[ERROR] {error}\n")
|
129 |
+
sys.stdout.flush()
|
130 |
|
131 |
+
# β
Remove `input("Press Enter to exit...")` to prevent blocking in Flask
|
132 |
+
print("β
Process Completed!")
|
133 |
+
sys.stdout.flush()
|