From 1222b2d7fe3fc3e6c065e30061b4b4c34d4daa01 Mon Sep 17 00:00:00 2001 From: r0oth3x49 Date: Sun, 21 Feb 2021 17:56:25 +0500 Subject: [PATCH] updated udemy-dl, bumped version 1.1, fixed #612 (functionality to reset numbers for lectures for each chapter). added switch for session caching on demand --cache. --- README.md | 9 ++++++--- udemy-dl.py | 41 +++++++++++++++++++++++++++++---------- udemy/__init__.py | 16 +++++++-------- udemy/auth.py | 10 +++++----- udemy/colorized/banner.py | 5 +++-- udemy/compat.py | 10 +++++----- udemy/extract.py | 16 ++++++++++----- 7 files changed, 69 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 20316ed..3ff961f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![GitHub release](https://img.shields.io/badge/release-v1.0-brightgreen?style=flat-square)](https://github.com/r0oth3x49/udemy-dl/releases/tag/v1.0) +[![GitHub release](https://img.shields.io/badge/release-v1.1-brightgreen?style=flat-square)](https://github.com/r0oth3x49/udemy-dl/releases/tag/v1.0) [![GitHub stars](https://img.shields.io/github/stars/r0oth3x49/udemy-dl.svg?style=flat-square)](https://github.com/r0oth3x49/udemy-dl/stargazers) [![GitHub forks](https://img.shields.io/github/forks/r0oth3x49/udemy-dl.svg?style=flat-square)](https://github.com/r0oth3x49/udemy-dl/network) [![GitHub issues](https://img.shields.io/github/issues/r0oth3x49/udemy-dl.svg?style=flat-square)](https://github.com/r0oth3x49/udemy-dl/issues) @@ -7,7 +7,7 @@ # udemy-dl **A cross-platform python based utility to download courses from udemy for personal offline use.** -[![udemy-dl-v1-0.png](https://i.postimg.cc/4y0fH3Hq/udemy-dl-v1-0.png)](https://postimg.cc/Fkj52NZV) +[![udemy-dl-v1-1.png](https://i.postimg.cc/X7QpzY8q/udemy-dl-v1-1.png)](https://postimg.cc/zVHzL54Y) ### ***Important Note***: @@ -48,6 +48,8 @@ - Support multiple courses download from file. - Supports organization and individual udemy users both. - Added support to download hls based streams if available. +- Added functionality to reset lecture number to start from 1. +- Added switch for session caching on demand. (option: `--cache`) - Convert WebVTT to SRT but donot delete WebVTT. (option: `--keep-vtt`) - Skip fetching HLS streams, This will make the fetching fast. (option: `--skip-hls`) - List down course contents and video resolution, suggest the best resolution (option: `--info`). @@ -155,7 +157,7 @@ You can download the latest version of udemy-dl by cloning the GitHub repository

 Author: Nasir khan (r0ot h3x49)
 
-usage: udemy-dl.py [-h] [-v] [-u] [-p] [-k] [-o] [-q] [-c] [-l] [-s] [--chapter-start] [--chapter-end] [--lecture-start] [--lecture-end] [--info]
+usage: udemy-dl.py [-h] [-v] [-u] [-p] [-k] [-o] [-q] [-c] [-l] [-s] [--chapter-start] [--chapter-end] [--lecture-start] [--lecture-end] [--info] [--cache]
                    [--keep-vtt] [--sub-only] [--skip-sub] [--skip-hls] [--assets-only] [--skip-assets]
                    course
 
@@ -186,6 +188,7 @@ Advance:
 
 Others:
   --info            List all lectures with available resolution.
+  --cache           Cache your session to avoid providing again.
   --keep-vtt        Keep WebVTT caption(s).
   --sub-only        Download captions/subtitle only.
   --skip-sub        Download course but skip captions/subtitle.
diff --git a/udemy-dl.py b/udemy-dl.py
index 12fa70f..8e3007f 100644
--- a/udemy-dl.py
+++ b/udemy-dl.py
@@ -385,7 +385,7 @@ def course_download(
 def main():
     """main function"""
     sys.stdout.write(banner())
-    version = "%(prog)s {version}".format(version="1.0")
+    version = "%(prog)s {version}".format(version=udemy.__version__)
     description = "A cross-platform python based utility to download courses from udemy for personal offline use."
     parser = argparse.ArgumentParser(
         description=description, conflict_handler="resolve"
@@ -502,6 +502,12 @@ def main():
         action="store_true",
         help="List all lectures with available resolution.",
     )
+    other.add_argument(
+        "--cache",
+        dest="cache_session",
+        action="store_true",
+        help="Cache your session to avoid providing again.",
+    )
     other.add_argument(
         "--keep-vtt",
         dest="keep_vtt",
@@ -546,11 +552,19 @@ def main():
             cookies = "\n".join([line for line in (l.strip() for l in f_in) if line])
         args.cookies = cookies
     if not args.username and not args.password and not args.cookies:
+        # check if we already have a session..
         configs = load_configs()
         if not configs:
+            # if not ask user for user/pass or access token (cookie)
             args.username = getpass.getuser(prompt="Username : ")
             args.password = getpass.getpass(prompt="Password : ")
-            print("\n")
+            if args.username and args.password:
+                print("\n")
+            if not args.username and not args.password:
+                print("")
+                args.cookies = getpass.get_access_token(prompt="Access Token : ")
+                if args.cookies:
+                    print("\n")
         if configs:
             cookies = configs.get("cookies")
             if not cookies:
@@ -562,6 +576,12 @@ def main():
             args.output = args.output if args.output else configs.get("output")
             args.language = args.language if args.language else configs.get("language")
     url_or_courses = extract_url_or_courses(args.course)
+    if not args.username and not args.password and not args.cookies:
+        print("\n")
+        logger.error(
+            msg=f"You should either provide fresh access token or username/password for udemy.."
+        )
+        sys.exit(0)
     udemy_obj = Udemy(
         url_or_courses=url_or_courses,
         username=args.username,
@@ -569,14 +589,15 @@ def main():
         cookies=args.cookies,
     )
     # setting the caching default so that we can avoid future login attemps.
-    _ = to_configs(
-        username=args.username,
-        password=args.password,
-        cookies=args.cookies,
-        quality=args.quality,
-        output=args.output,
-        language=args.language,
-    )
+    if args.cache_session:
+        _ = to_configs(
+            username=args.username,
+            password=args.password,
+            cookies=args.cookies,
+            quality=args.quality,
+            output=args.output,
+            language=args.language,
+        )
     dl_assets = dl_lecture = dl_subtitles = True
     if args.assets_only:
         dl_lecture = False
diff --git a/udemy/__init__.py b/udemy/__init__.py
index 7508061..a069c1b 100644
--- a/udemy/__init__.py
+++ b/udemy/__init__.py
@@ -2,10 +2,10 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
-'''
+"""
 
-Author 	: Nasir Khan (r0ot h3x49)
-Github 	: https://github.com/r0oth3x49
+Author  : Nasir Khan (r0ot h3x49)
+Github  : https://github.com/r0oth3x49
 License : MIT
 
 
@@ -22,11 +22,11 @@
 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 
 THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-'''
+"""
 
-__version__ = "1.0"
-__author__  = "Nasir Khan (r0ot h3x49)"
-__license__ = 'MIT'
-__copyright__ = 'Copyright (c) 2018 Nasir Khan (r0ot h3x49)'
+__version__ = "1.1"
+__author__ = "Nasir Khan (r0ot h3x49)"
+__license__ = "MIT"
+__copyright__ = "Copyright (c) 2018 Nasir Khan (r0ot h3x49)"
 
 from udemy.udemy import course, fetch_enrolled_courses
diff --git a/udemy/auth.py b/udemy/auth.py
index d27cd97..86ddd7e 100644
--- a/udemy/auth.py
+++ b/udemy/auth.py
@@ -103,11 +103,11 @@ def authenticate(self, access_token="", client_id=""):
 
         if access_token:
             # dump cookies to configs
-            _ = to_configs(
-                username=self.username,
-                password=self.password,
-                cookies=f"access_token={access_token}",
-            )
+            # _ = to_configs(
+            #     username=self.username,
+            #     password=self.password,
+            #     cookies=f"access_token={access_token}",
+            # )
             self._session._set_auth_headers(  # pylint: disable=W
                 access_token=access_token, client_id=client_id
             )
diff --git a/udemy/colorized/banner.py b/udemy/colorized/banner.py
index e20856b..278f332 100644
--- a/udemy/colorized/banner.py
+++ b/udemy/colorized/banner.py
@@ -23,16 +23,17 @@
 '''
 
 from .colors import *
+from udemy import __version__
 
 def banner():
-    banner = '''
+    banner = f'''
 %s%s              __                               ____  
 %s%s   __  ______/ /__  ____ ___  __  __      ____/ / /  
 %s%s  / / / / __  / _ \/ __ `__ \/ / / /_____/ __  / /   
 %s%s / /_/ / /_/ /  __/ / / / / / /_/ /_____/ /_/ / /    
 %s%s \__,_/\__,_/\___/_/ /_/ /_/\__, /      \__,_/_/     
 %s%s                           /____/
-                                 %s%sVersion : %s%s1.0
+                                 %s%sVersion : %s%s{__version__}
                                  %s%sAuthor  : %s%sNasir Khan (r0ot h3x49)
                                  %s%sGithub  : %s%shttps://github.com/r0oth3x49
 
diff --git a/udemy/compat.py b/udemy/compat.py
index 9eb9e96..08b44ab 100644
--- a/udemy/compat.py
+++ b/udemy/compat.py
@@ -45,14 +45,14 @@
 LOGOUT_URL = "https://www.udemy.com/user/logout"
 
 WISHLIST_URL = "https://{portal_name}.udemy.com/api-2.0/users/me/wishlisted-courses?fields[course]=id,url,published_title&ordering=-access_time&page=1&page_size=1000"
-COLLECTION_URL = "https://{portal_name}.udemy.com/api-2.0/users/me/subscribed-courses-collections/?collection_has_courses=True&course_limit=20&fields[course]=last_accessed_time,published_title&fields[user_has_subscribed_courses_collection]=@all&page=1&page_size=1000"
-MY_COURSES_URL = "https://{portal_name}.udemy.com/api-2.0/users/me/subscribed-courses?fields[course]=id,url,published_title&ordering=-last_accessed,-access_time&page=1&page_size=10000"
-COURSE_SEARCH = "https://{portal_name}.udemy.com/api-2.0/users/me/subscribed-courses?fields[course]=id,url,published_title&page=1&page_size=1000&ordering=-last_accessed,-access_time&search={course_name}"
+COLLECTION_URL = "https://{portal_name}.udemy.com/api-2.0/users/me/subscribed-courses-collections/?collection_has_courses=True&course_limit=20&fields[course]=last_accessed_time,title,published_title&fields[user_has_subscribed_courses_collection]=@all&page=1&page_size=1000"
+MY_COURSES_URL = "https://{portal_name}.udemy.com/api-2.0/users/me/subscribed-courses?fields[course]=id,url,title,published_title&ordering=-last_accessed,-access_time&page=1&page_size=10000"
+COURSE_SEARCH = "https://{portal_name}.udemy.com/api-2.0/users/me/subscribed-courses?fields[course]=id,url,title,published_title&page=1&page_size=1000&ordering=-last_accessed,-access_time&search={course_name}"
 COURSE_URL = "https://{portal_name}.udemy.com/api-2.0/courses/{course_id}/cached-subscriber-curriculum-items?fields[asset]=results,title,external_url,time_estimation,download_urls,slide_urls,filename,asset_type,captions,stream_urls,body&fields[chapter]=object_index,title,sort_order&fields[lecture]=id,title,object_index,asset,supplementary_assets,view_html&page_size=10000"
-SUBSCRIBED_COURSES = "https://www.udemy.com/api-2.0/users/me/subscribed-courses/?ordering=-last_accessed&fields[course]=id,url&page=1&page_size=12"
+SUBSCRIBED_COURSES = "https://www.udemy.com/api-2.0/users/me/subscribed-courses/?ordering=-last_accessed&fields[course]=id,title,url&page=1&page_size=12"
 HEADERS = {
     "Origin": "www.udemy.com",
-    "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0",
+    "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0",
     # "Referer": "https://www.udemy.com/join/login-popup/",
     "Accept": "*/*",
     # "Accept-Language": "en-US,en;q=0.5",
diff --git a/udemy/extract.py b/udemy/extract.py
index d6d0cea..8abc2ae 100644
--- a/udemy/extract.py
+++ b/udemy/extract.py
@@ -618,6 +618,7 @@ def _real_extract(self, url="", skip_hls_stream=False):
         course_id, course_info = self._extract_course_info(url)
 
         if course_info and isinstance(course_info, dict):
+            title = self._clean(course_info.get("title"))
             course_title = course_info.get("published_title")
             portal_name = course_info.get("portal_name")
 
@@ -648,19 +649,21 @@ def _real_extract(self, url="", skip_hls_stream=False):
 
         _udemy["access_token"] = self._access_token
         _udemy["course_id"] = course_id
+        _udemy["title"] = title
         _udemy["course_title"] = course_title
         _udemy["chapters"] = []
 
         counter = -1
 
         if course:
+            lecture_counter = 0
             for entry in course:
-
                 clazz = entry.get("_class")
                 asset = entry.get("asset")
                 supp_assets = entry.get("supplementary_assets")
 
                 if clazz == "chapter":
+                    lecture_counter = 0
                     lectures = []
                     chapter_index = entry.get("object_index")
                     chapter_title = "{0:02d} ".format(chapter_index) + self._clean(
@@ -677,7 +680,7 @@ def _real_extract(self, url="", skip_hls_stream=False):
                         )
                         counter += 1
                 elif clazz == "lecture":
-
+                    lecture_counter += 1
                     lecture_id = entry.get("id")
                     if len(_udemy["chapters"]) == 0:
                         lectures = []
@@ -732,9 +735,9 @@ def _real_extract(self, url="", skip_hls_stream=False):
 
                         logger.progress(msg="Downloading course information .. ")
                         lecture_index = entry.get("object_index")
-                        lecture_title = "{0:03d} ".format(lecture_index) + self._clean(
-                            entry.get("title")
-                        )
+                        lecture_title = "{0:03d} ".format(
+                            lecture_counter
+                        ) + self._clean(entry.get("title"))
                         data = asset.get("stream_urls")
                         if data and isinstance(data, dict):
                             sources = data.get("Video")
@@ -748,6 +751,7 @@ def _real_extract(self, url="", skip_hls_stream=False):
                             subtitle_count = len(subtitles)
                             lectures.append(
                                 {
+                                    "index": lecture_counter,
                                     "lecture_index": lecture_index,
                                     "lectures_id": lecture_id,
                                     "lecture_title": lecture_title,
@@ -763,6 +767,7 @@ def _real_extract(self, url="", skip_hls_stream=False):
                         else:
                             lectures.append(
                                 {
+                                    "index": lecture_counter,
                                     "lecture_index": lecture_index,
                                     "lectures_id": lecture_id,
                                     "lecture_title": lecture_title,
@@ -786,6 +791,7 @@ def _real_extract(self, url="", skip_hls_stream=False):
                             entry.get("title")
                         )
                         if chapter_title not in _udemy["chapters"]:
+                            lecture_counter = 0
                             _udemy["chapters"].append(
                                 {
                                     "chapter_title": chapter_title,