├── .gitignore ├── LICENSE ├── README.md └── udemy_dl.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 atlonxp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Udemy Batch Downloader for Google Colab and Jupyter Notebook 2 | Run udemy-dl natively on Colab or Jupyter Notebook with a single file 3 | 4 | Thanks and credit to [r0oth3x49](https://github.com/r0oth3x49/) for such as great work! 5 | 6 | This is an alternative version to [r0oth3x49/udemy-dl](https://github.com/r0oth3x49/udemy-dl), that can be run on Google Colab with just a single file jupyter notebook. 7 | 8 | Features: 9 | 10 | - all features from [r0oth3x49/udemy-dl](https://github.com/r0oth3x49/udemy-dl) 11 | - batch downloader 12 | - download and save directly to your google drive 13 | - parallel downloader (coming soon) 14 | 15 | Changelogs: 16 | 17 | - fixed issue: udemy.course() 18 | -------------------------------------------------------------------------------- /udemy_dl.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "udemy-dl.ipynb", 7 | "provenance": [], 8 | "collapsed_sections": [], 9 | "mount_file_id": "1KdCQISbpoCWGk0yvEI-s46-iLE--qsTK", 10 | "authorship_tag": "ABX9TyOTB7rfOcmv84+rTGo+ZRD2", 11 | "include_colab_link": true 12 | }, 13 | "kernelspec": { 14 | "name": "python3", 15 | "display_name": "Python 3" 16 | } 17 | }, 18 | "cells": [ 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "id": "view-in-github", 23 | "colab_type": "text" 24 | }, 25 | "source": [ 26 | "\"Open" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "metadata": { 32 | "id": "saTRlbMA-YZs", 33 | "colab_type": "code", 34 | "cellView": "form", 35 | "colab": {} 36 | }, 37 | "source": [ 38 | "#@title Mount your drive with Google Colab\n", 39 | "from google.colab import drive\n", 40 | "drive.mount('/content/drive', force_remount=True)" 41 | ], 42 | "execution_count": 0, 43 | "outputs": [] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "metadata": { 48 | "id": "EgWNSd-G_AVk", 49 | "colab_type": "code", 50 | "cellView": "form", 51 | "colab": {} 52 | }, 53 | "source": [ 54 | "#@title Mount your drive with Google SDK\n", 55 | "!apt-get install -y -qq software-properties-common python-software-properties module-init-tools\n", 56 | "!add-apt-repository -y ppa:alessandro-strada/ppa 2>&1 > /dev/null\n", 57 | "!apt-get update -qq 2>&1 > /dev/null\n", 58 | "!apt-get -y install -qq google-drive-ocamlfuse fuse\n", 59 | "from google.colab import auth\n", 60 | "auth.authenticate_user()\n", 61 | "from oauth2client.client import GoogleCredentials\n", 62 | "creds = GoogleCredentials.get_application_default()\n", 63 | "import getpass\n", 64 | "!google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret} < /dev/null 2>&1 | grep URL\n", 65 | "vcode = getpass.getpass()\n", 66 | "!echo {vcode} | google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret}\n", 67 | "\n", 68 | "!mkdir -p mydrive\n", 69 | "!google-drive-ocamlfuse drive mydrive\n", 70 | "\n", 71 | "from IPython.display import HTML, clear_output\n", 72 | "\n", 73 | "clear_output()\n", 74 | "print(\"Installed Successfully\")" 75 | ], 76 | "execution_count": 0, 77 | "outputs": [] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "metadata": { 82 | "id": "lrZi4oVH4hZP", 83 | "colab_type": "code", 84 | "cellView": "form", 85 | "outputId": "2517c49e-61e9-4d05-8023-eadb8edc4789", 86 | "colab": { 87 | "base_uri": "https://localhost:8080/", 88 | "height": 34 89 | } 90 | }, 91 | "source": [ 92 | "#@title Initial Udemy Downloader\n", 93 | "\n", 94 | "#@markdown **Username/Password**\n", 95 | "username = \"\" #@param {type:\"string\"}\n", 96 | "password = \"\" #@param {type:\"string\", text:\"sss\"}\n", 97 | "\n", 98 | "#@markdown **Cookies** will be used in the authentication if provided\n", 99 | "cookies = \"access_token=\" #@param {type:\"string\"}\n", 100 | "\n", 101 | "#@markdown **Downloader Configurations**\n", 102 | "quality = \"720\" #@param ['2160', '1440', '1080', '720', '480', '360', '240']\n", 103 | "output = \"/content/drive/My Drive/\" #@param {type:\"string\"}\n", 104 | "\n", 105 | "!pip install requests[security] six colorama requests unidecode pyOpenSSL\n", 106 | "\n", 107 | "#@markdown \\ \n", 108 | "#@markdown **Remember**: Udemy courses you would like to download must be enrolled beforehand. Otherwise, you will not be able to download those courses. \n", 109 | "\n", 110 | "from __future__ import unicode_literals\n", 111 | "\n", 112 | "import codecs\n", 113 | "import json\n", 114 | "import itertools\n", 115 | "import os\n", 116 | "import re\n", 117 | "import requests\n", 118 | "import six\n", 119 | "import ssl\n", 120 | "import sys\n", 121 | "import time\n", 122 | "import termios\n", 123 | "import tty\n", 124 | "import unicodedata\n", 125 | "\n", 126 | "from colorama import init, Fore, Back, Style\n", 127 | "from unidecode import unidecode\n", 128 | "from IPython.display import clear_output\n", 129 | "\n", 130 | "# _compat.py\n", 131 | "# -------------------------------------------------------------------------------------\n", 132 | "import urllib.request as compat_urllib\n", 133 | "\n", 134 | "from urllib.error import HTTPError as compat_httperr\n", 135 | "from urllib.error import URLError as compat_urlerr\n", 136 | "from urllib.parse import urlparse as compat_urlparse\n", 137 | "from urllib.request import Request as compat_request\n", 138 | "from urllib.request import urlopen as compat_urlopen\n", 139 | "from urllib.request import build_opener as compat_opener\n", 140 | "from html.parser import HTMLParser as compat_HTMLParser\n", 141 | "from http.cookies import SimpleCookie as ParseCookie\n", 142 | "from requests.exceptions import ConnectionError as conn_error\n", 143 | "\n", 144 | "encoding, pyver = str, 3\n", 145 | "ssl._create_default_https_context = ssl._create_unverified_context\n", 146 | "\n", 147 | "NO_DEFAULT = object()\n", 148 | "LOGIN_URL = 'https://www.udemy.com/join/login-popup/?displayType=ajax&display_type=popup&showSkipButton=1&returnUrlAfterLogin=https'\n", 149 | " #'https://www.udemy.com/api-2.0/auth/udemy-auth/login/?fields[user]=access_token'\n", 150 | "LOGOUT_URL = 'https://www.udemy.com/user/logout'\n", 151 | "\n", 152 | "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\"\n", 153 | "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\"\n", 154 | "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\"\n", 155 | "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}\"\n", 156 | "COURSE_URL = 'https://{portal_name}.udemy.com/api-2.0/courses/{course_id}/cached-subscriber-curriculum-items?fields[asset]=results,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'\n", 157 | "HEADERS = {\n", 158 | " 'Origin': 'www.udemy.com',\n", 159 | " 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0',\n", 160 | " 'Referer': 'https://www.udemy.com/join/login-popup/',\n", 161 | " 'Accept': '*/*',\n", 162 | " 'Accept-Language': 'en-US,en;q=0.5',\n", 163 | " 'Accept-Encoding': 'gzip, deflate, br',\n", 164 | " 'Connection': 'keep-alive'\n", 165 | "}\n", 166 | "\n", 167 | "\n", 168 | "# _progress.py\n", 169 | "# -------------------------------------------------------------------------------------\n", 170 | "_spin = itertools.cycle(['-', '|', '/', '\\\\'])\n", 171 | "\n", 172 | "class ProgressBar(object):\n", 173 | "\n", 174 | " def _spinner(self, text):\n", 175 | " spin = _spin.next() if pyver == 2 else _spin.__next__()\n", 176 | " sys.stdout.write(text + spin)\n", 177 | " sys.stdout.flush()\n", 178 | " time.sleep(0.02)\n", 179 | "\n", 180 | " # thanks to https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console\n", 181 | " def _progress(self, iteration, total, prefix = '' , file_size='' , downloaded = '' , rate = '' ,suffix = '', bar_length = 30):\n", 182 | " filledLength = int(round(bar_length * iteration / float(total)))\n", 183 | " percents = format(100.00 * (iteration / float(total)), '.2f')\n", 184 | " bar = fc + sd + '#' * filledLength + fw + sd +'-' * (bar_length - filledLength)\n", 185 | " if '0.00' not in rate:\n", 186 | " sys.stdout.write('\\033[2K\\033[1G\\r\\r{}{}[{}{}*{}{}] : {}{}{}/{} {}% |{}{}{}| {} {}'.format(fc,sd,fm,sb,fc,sd,fg,sb,file_size,downloaded,percents,bar,fg,sb,rate,suffix))\n", 187 | " sys.stdout.flush()\n", 188 | "\n", 189 | " def show_progress(self, total, recvd, ratio, rate, eta):\n", 190 | " if total <= 1048576:\n", 191 | " _total_size = round(float(total) / 1024.00, 2)\n", 192 | " _receiving = round(float(recvd) / 1024.00, 2)\n", 193 | " _size = format(_total_size if _total_size < 1024.00 else _total_size/1024.00, '.2f')\n", 194 | " _received = format(_receiving if _receiving < 1024.00 else _receiving/1024.00,'.2f')\n", 195 | " suffix_size = 'KB' if _total_size < 1024.00 else 'MB'\n", 196 | " suffix_recvd = 'KB' if _receiving < 1024.00 else 'MB'\n", 197 | " else:\n", 198 | " _total_size = round(float(total) / 1048576, 2)\n", 199 | " _receiving = round(float(recvd) / 1048576, 2)\n", 200 | " _size = format(_total_size if _total_size < 1024.00 else _total_size/1024.00, '.2f')\n", 201 | " _received = format(_receiving if _receiving < 1024.00 else _receiving/1024.00,'.2f')\n", 202 | " suffix_size = 'MB' if _total_size < 1024.00 else 'GB'\n", 203 | " suffix_recvd = 'MB' if _receiving < 1024.00 else 'GB'\n", 204 | "\n", 205 | " _rate = round(float(rate) , 2)\n", 206 | " rate = format(_rate if _rate < 1024.00 else _rate/1024.00, '.2f')\n", 207 | " suffix_rate = 'kB/s' if _rate < 1024.00 else 'MB/s'\n", 208 | " (mins, secs) = divmod(eta, 60)\n", 209 | " (hours, mins) = divmod(mins, 60)\n", 210 | " if hours > 99:\n", 211 | " eta = \"--:--:--\"\n", 212 | " if hours == 0:\n", 213 | " eta = \"eta %02d:%02ds\" % (mins, secs)\n", 214 | " else:\n", 215 | " eta = \"eta %02d:%02d:%02ds\" % (hours, mins, secs)\n", 216 | " if secs == 0:\n", 217 | " eta = \"\\n\"\n", 218 | "\n", 219 | " self._progress(_receiving, _total_size, file_size = str(_size) + str(suffix_size) ,\\\n", 220 | " downloaded = str(_received) + str(suffix_recvd),\\\n", 221 | " rate = str(rate) + str(suffix_rate),\\\n", 222 | " suffix = str(eta),\\\n", 223 | " bar_length = 30)\n", 224 | "\n", 225 | "\n", 226 | "# _sanitize.py\n", 227 | "# -------------------------------------------------------------------------------------\n", 228 | "SLUG_OK = '-_~'\n", 229 | "\n", 230 | "def smart_text(s, encoding='utf-8', errors='strict'):\n", 231 | " if isinstance(s, six.text_type):\n", 232 | " return s\n", 233 | "\n", 234 | " if not isinstance(s, six.string_types):\n", 235 | " if six.PY3:\n", 236 | " if isinstance(s, bytes):\n", 237 | " s = six.text_type(s, encoding, errors)\n", 238 | " else:\n", 239 | " s = six.text_type(s)\n", 240 | " elif hasattr(s, '__unicode__'):\n", 241 | " s = six.text_type(s)\n", 242 | " else:\n", 243 | " s = six.text_type(bytes(s), encoding, errors)\n", 244 | " else:\n", 245 | " s = six.text_type(s)\n", 246 | " return s\n", 247 | "\n", 248 | "def slugify(s, ok=SLUG_OK, lower=True, spaces=False, only_ascii=False, space_replacement='-'):\n", 249 | " \"\"\"\n", 250 | " Creates a unicode slug for given string with several options.\n", 251 | "\n", 252 | " L and N signify letter/number.\n", 253 | " http://www.unicode.org/reports/tr44/tr44-4.html#GC_Values_Table\n", 254 | "\n", 255 | " :param s: Your unicode string.\n", 256 | " :param ok: Extra characters outside of alphanumerics to be allowed.\n", 257 | " Default is '-_~'\n", 258 | " :param lower: Lower the output string. \n", 259 | " Default is True\n", 260 | " :param spaces: True allows spaces, False replaces a space with the \"space_replacement\" param\n", 261 | " :param only_ascii: True to replace non-ASCII unicode characters with \n", 262 | " their ASCII representations.\n", 263 | " :param space_replacement: Char used to replace spaces if \"spaces\" is False. \n", 264 | " Default is dash (\"-\") or first char in ok if dash not allowed\n", 265 | " :type s: String\n", 266 | " :type ok: String\n", 267 | " :type lower: Bool\n", 268 | " :type spaces: Bool\n", 269 | " :type only_ascii: Bool\n", 270 | " :type space_replacement: String\n", 271 | " :return: Slugified unicode string\n", 272 | "\n", 273 | " \"\"\"\n", 274 | "\n", 275 | " if only_ascii and ok != SLUG_OK and hasattr(ok, 'decode'):\n", 276 | " try:\n", 277 | " ok.decode('ascii')\n", 278 | " except UnicodeEncodeError:\n", 279 | " raise ValueError(('You can not use \"only_ascii=True\" with '\n", 280 | " 'a non ascii available chars in \"ok\" (\"%s\" given)') % ok)\n", 281 | "\n", 282 | " rv = []\n", 283 | " for c in unicodedata.normalize('NFKC', smart_text(s)):\n", 284 | " cat = unicodedata.category(c)[0]\n", 285 | " if cat in 'LN' or c in ok:\n", 286 | " rv.append(c)\n", 287 | " elif cat == 'Z': # space\n", 288 | " rv.append(' ')\n", 289 | " new = ''.join(rv).strip()\n", 290 | "\n", 291 | " if only_ascii:\n", 292 | " new = unidecode(new)\n", 293 | " if not spaces:\n", 294 | " if space_replacement and space_replacement not in ok:\n", 295 | " space_replacement = ok[0] if ok else ''\n", 296 | " new = re.sub('[%s\\s]+' % space_replacement, space_replacement, new)\n", 297 | " if lower:\n", 298 | " new = new.lower()\n", 299 | "\n", 300 | " return new\n", 301 | "\n", 302 | "def sanitize(title):\n", 303 | " _locale = {\n", 304 | " '194': 'A',\n", 305 | " '199': 'C',\n", 306 | " '286': 'G',\n", 307 | " '304': 'I',\n", 308 | " '206': 'I',\n", 309 | " '214': 'O',\n", 310 | " '350': 'S',\n", 311 | " '219': 'U',\n", 312 | " '226': 'a',\n", 313 | " '231': 'c',\n", 314 | " '287': 'g',\n", 315 | " '305': 'i',\n", 316 | " '238': 'i',\n", 317 | " '246': 'o',\n", 318 | " '351': 's',\n", 319 | " '251': 'u',\n", 320 | " '191': '',\n", 321 | " '225': 'a',\n", 322 | " '233': 'e',\n", 323 | " '237': 'i',\n", 324 | " '243': 'o',\n", 325 | " '250': 'u',\n", 326 | " '252': 'u',\n", 327 | " '168u': 'u',\n", 328 | " '241': 'n',\n", 329 | " '193': 'A',\n", 330 | " '201': 'E',\n", 331 | " '205': 'I',\n", 332 | " '211': 'O',\n", 333 | " '218': 'U',\n", 334 | " '220': 'U',\n", 335 | " '168U': 'U',\n", 336 | " '209': 'N',\n", 337 | " '223': 'ss',\n", 338 | " }\n", 339 | " _temp = ''.join([str(ord(i)) if ord(i) > 128 else i for i in title])\n", 340 | " for _ascii, _char in _locale.items():\n", 341 | " if _ascii in _temp:\n", 342 | " _temp = _temp.replace(_ascii, _char)\n", 343 | "\n", 344 | " ok = re.compile(r'[^\\\\/:*?\"<>]')\n", 345 | " _title = ''.join(x if ok.match(x) else \"_\" for x in _temp)\n", 346 | " return _title\n", 347 | "\n", 348 | "\n", 349 | "# colors.py\n", 350 | "# -------------------------------------------------------------------------------------\n", 351 | "init(autoreset=True)\n", 352 | "# colors foreground text:\n", 353 | "fc = Fore.CYAN\n", 354 | "fg = Fore.GREEN\n", 355 | "fw = Fore.WHITE\n", 356 | "fr = Fore.RED\n", 357 | "fb = Fore.BLUE\n", 358 | "fy = Fore.YELLOW\n", 359 | "fm = Fore.MAGENTA\n", 360 | "\n", 361 | "# colors background text:\n", 362 | "bc = Back.CYAN\n", 363 | "bg = Back.GREEN\n", 364 | "bw = Back.WHITE\n", 365 | "br = Back.RED\n", 366 | "bb = Back.BLUE\n", 367 | "by = Fore.YELLOW\n", 368 | "bm = Fore.MAGENTA\n", 369 | "\n", 370 | "# colors style text:\n", 371 | "sd = Style.DIM\n", 372 | "sn = Style.NORMAL\n", 373 | "sb = Style.BRIGHT\n", 374 | "\n", 375 | "\n", 376 | "# banner.py\n", 377 | "# -------------------------------------------------------------------------------------\n", 378 | "def banner():\n", 379 | " banner = '''\n", 380 | " %s%s __ ____ \n", 381 | " %s%s __ ______/ /__ ____ ___ __ __ ____/ / / \n", 382 | " %s%s / / / / __ / _ \\/ __ `__ \\/ / / /_____/ __ / / \n", 383 | " %s%s / /_/ / /_/ / __/ / / / / / /_/ /_____/ /_/ / / \n", 384 | " %s%s \\__,_/\\__,_/\\___/_/ /_/ /_/\\__, / \\__,_/_/ \n", 385 | " %s%s /____/\n", 386 | " %s%sVersion : %s%s0.5\n", 387 | " %s%sAuthor : %s%sNasir Khan (r0ot h3x49)\n", 388 | " %s%sGithub : %s%shttps://github.com/r0oth3x49\n", 389 | "\n", 390 | " ''' % (fc, sb,fc, sb,fc, sb, fm, sb, fy, sb, fy, sb, fy,sd, fg, sd, fy, sd, fg, sd, fy, sd, fg, sd)\n", 391 | " return banner\n", 392 | "\n", 393 | "\n", 394 | "# _getpass.py\n", 395 | "# -------------------------------------------------------------------------------------\n", 396 | "class GetPass(object):\n", 397 | "\n", 398 | " def _unix_getch(self):\n", 399 | " fd = sys.stdin.fileno()\n", 400 | " old_settings = termios.tcgetattr(fd)\n", 401 | " try:\n", 402 | " tty.setraw(sys.stdin.fileno())\n", 403 | " ch = sys.stdin.read(1)\n", 404 | " finally:\n", 405 | " termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)\n", 406 | " return ch\n", 407 | " \n", 408 | " def getuser(self, prompt='Username : '):\n", 409 | " \"\"\"Prompt for Username \"\"\"\n", 410 | " if sys.version_info[:2] >= (3, 0):\n", 411 | " sys.stdout.write('{}'.format(prompt))\n", 412 | " sys.stdout.flush()\n", 413 | " username = input()\n", 414 | " else:\n", 415 | " sys.stdout.write('{}'.format(prompt))\n", 416 | " sys.stdout.flush()\n", 417 | " username = raw_input()\n", 418 | " return username\n", 419 | "\n", 420 | " def getpass(self, prompt='Password : '):\n", 421 | " \"\"\"Prompt for password and replace each character by asterik (*)\"\"\"\n", 422 | " sys.stdout.write('{}'.format(prompt))\n", 423 | " sys.stdout.flush()\n", 424 | " pw = \"\"\n", 425 | " while True:\n", 426 | " c = _win_getch() if os.name == 'nt' else self._unix_getch()\n", 427 | " if os.name == 'nt':\n", 428 | " if ord(c) == 13:\n", 429 | " break\n", 430 | " if ord(c) == 3:\n", 431 | " raise KeyboardInterrupt\n", 432 | " if ord(c) == 8:\n", 433 | " if len(pw) > 0:\n", 434 | " pw = pw[:-1]\n", 435 | " s = \"*\" * len(pw)\n", 436 | " sys.stdout.write('\\033[2K\\033[1G')\n", 437 | " sys.stdout.flush()\n", 438 | " sys.stdout.write('\\r\\r\\r{}{}'.format(prompt, s))\n", 439 | " sys.stdout.flush()\n", 440 | " else:\n", 441 | " pass\n", 442 | " else:\n", 443 | " if ord(c) == 27:\n", 444 | " pass\n", 445 | " elif ord(c) == 224:\n", 446 | " c = _win_getch()\n", 447 | " pass\n", 448 | " else:\n", 449 | " if sys.version_info[:2] >= (3, 0):\n", 450 | " pw = pw + c.decode('utf-8')\n", 451 | " else:\n", 452 | " pw = pw + c\n", 453 | " sys.stdout.write('*')\n", 454 | " sys.stdout.flush()\n", 455 | " else:\n", 456 | " if ord(c) == 13:\n", 457 | " break\n", 458 | " if ord(c) == 3:\n", 459 | " raise KeyboardInterrupt\n", 460 | " if ord(c) == 127:\n", 461 | " if len(pw) > 0:\n", 462 | " pw = pw[:-1]\n", 463 | " s = \"*\" * len(pw)\n", 464 | " sys.stdout.write('\\033[2K\\033[1G')\n", 465 | " sys.stdout.flush()\n", 466 | " sys.stdout.write('\\r\\r\\r{}{}'.format(prompt, s))\n", 467 | " sys.stdout.flush()\n", 468 | " else:\n", 469 | " pass\n", 470 | " else:\n", 471 | " if ord(c) == 27:\n", 472 | " pass\n", 473 | " elif ord(c) == 91 or ord(c) == 27:\n", 474 | " c = self._unix_getch()\n", 475 | " pass\n", 476 | " else:\n", 477 | " pw = pw + c\n", 478 | " sys.stdout.write(\"*\")\n", 479 | " sys.stdout.flush()\n", 480 | "\n", 481 | " return pw\n", 482 | "\n", 483 | "\n", 484 | "# _session.py\n", 485 | "# -------------------------------------------------------------------------------------\n", 486 | "class Session(object):\n", 487 | "\n", 488 | " def __init__(self):\n", 489 | " self._headers = HEADERS\n", 490 | " self._session = requests.sessions.Session()\n", 491 | "\n", 492 | " def _set_auth_headers(self, access_token='', client_id=''):\n", 493 | " self._headers['Authorization'] = \"Bearer {}\".format(access_token)\n", 494 | " self._headers['X-Udemy-Authorization'] = \"Bearer {}\".format(access_token)\n", 495 | "\n", 496 | " def _get(self, url):\n", 497 | " session = self._session.get(url, headers=self._headers)\n", 498 | " if session.ok or session.status_code in [502, 503]:\n", 499 | " return session\n", 500 | " if not session.ok:\n", 501 | " if session.status_code == 403:\n", 502 | " msg = {'detail': 'You should use cookie base method to authenticate or try again in few minutes'}\n", 503 | " else:\n", 504 | " msg = {'detail': ''}\n", 505 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Udemy Says : %s %s %s ...\\n\" % (session.status_code, session.reason, msg.get('detail', '')))\n", 506 | " time.sleep(0.8)\n", 507 | " sys.exit(0)\n", 508 | "\n", 509 | " def _post(self, url, data, redirect=True):\n", 510 | " session = self._session.post(url, data, headers=self._headers, allow_redirects=redirect)\n", 511 | " if session.ok:\n", 512 | " return session\n", 513 | " if not session.ok:\n", 514 | " if session.status_code == 403:\n", 515 | " msg = {'detail': 'You should use cookie base method to authenticate or try again in few minutes'}\n", 516 | " else:\n", 517 | " msg = {'detail': ''}\n", 518 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Udemy Says : %s %s %s ...\\n\" % (session.status_code, session.reason, msg.get('detail', '')))\n", 519 | " sys.stdout.flush()\n", 520 | " time.sleep(0.8)\n", 521 | " sys.exit(0)\n", 522 | "\n", 523 | " def terminate(self):\n", 524 | " self._set_auth_headers()\n", 525 | " return\n", 526 | "\n", 527 | "\n", 528 | "# _auth.py\n", 529 | "# -------------------------------------------------------------------------------------\n", 530 | "class UdemyAuth(object):\n", 531 | "\n", 532 | " def __init__(self, username='', password=''):\n", 533 | " self.username = username\n", 534 | " self.password = password\n", 535 | " self._session = Session()\n", 536 | "\n", 537 | " def _form_hidden_input(self, form_id):\n", 538 | " try:\n", 539 | " webpage = self._session._get(LOGIN_URL).text\n", 540 | " except conn_error as e:\n", 541 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Connection error : make sure your internet connection is working.\\n\")\n", 542 | " time.sleep(0.8)\n", 543 | " sys.exit(0)\n", 544 | " else:\n", 545 | " login_form = hidden_inputs(\n", 546 | " search_regex(\n", 547 | " r'(?is)]+?id=([\"\\'])%s\\1[^>]*>(?P
.+?)
' % form_id,\n", 548 | " webpage,\n", 549 | " '%s form' % form_id,\n", 550 | " group='form'\n", 551 | " )\n", 552 | " )\n", 553 | " login_form.update({\n", 554 | " 'email' : self.username,\n", 555 | " 'password' : self.password,\n", 556 | " })\n", 557 | " return login_form\n", 558 | "\n", 559 | " def authenticate(self, access_token='', client_id=''):\n", 560 | " if not access_token and not client_id:\n", 561 | " data = self._form_hidden_input(form_id='login-form')\n", 562 | " auth_response = self._session._post(LOGIN_URL, data=data, redirect=False)\n", 563 | " auth_cookies = auth_response.cookies\n", 564 | " \n", 565 | " access_token = auth_cookies.get('access_token', '')\n", 566 | " client_id = auth_cookies.get('client_id', '')\n", 567 | " \n", 568 | " if access_token:\n", 569 | " self._session._set_auth_headers(access_token=access_token, client_id=client_id)\n", 570 | " return self._session\n", 571 | " else:\n", 572 | " self._session._set_auth_headers()\n", 573 | " return None\n", 574 | "\n", 575 | "\n", 576 | "# _utils.py\n", 577 | "# -------------------------------------------------------------------------------------\n", 578 | "def cache_credentials(username, password, quality=\"\", output=\"\", language=\"\"):\n", 579 | " fname = \"configuration\"\n", 580 | " fmode = \"w\"\n", 581 | " creds = {\n", 582 | " \"username\" : username,\n", 583 | " \"password\" : password,\n", 584 | " \"quality\" : quality,\n", 585 | " \"output\" : output,\n", 586 | " \"language\" : language\n", 587 | " }\n", 588 | " fout = open(fname, fmode)\n", 589 | " json.dump(creds, fout, indent=4)\n", 590 | " fout.close()\n", 591 | " return \"cached\"\n", 592 | "\n", 593 | "def use_cached_credentials():\n", 594 | " fname = \"configuration\"\n", 595 | " try:\n", 596 | " fout = open(fname)\n", 597 | " except IOError as e:\n", 598 | " creds = ''\n", 599 | " return creds\n", 600 | " except Exception as e:\n", 601 | " creds = ''\n", 602 | " return creds\n", 603 | " else:\n", 604 | " creds = json.load(fout)\n", 605 | " fout.close()\n", 606 | " return creds\n", 607 | "\n", 608 | "class HTMLAttributeParser(compat_HTMLParser):\n", 609 | " \"\"\"Trivial HTML parser to gather the attributes for a single element\"\"\"\n", 610 | " def __init__(self):\n", 611 | " self.attrs = {}\n", 612 | " compat_HTMLParser.__init__(self)\n", 613 | "\n", 614 | " def handle_starttag(self, tag, attrs):\n", 615 | " self.attrs = dict(attrs)\n", 616 | "\n", 617 | "def unescapeHTML(s):\n", 618 | " clean = compat_HTMLParser()\n", 619 | " data = clean.unescape(s)\n", 620 | " return data\n", 621 | "\n", 622 | "def extract_attributes(html_element):\n", 623 | " \"\"\"Given a string for an HTML element such as\n", 624 | " \n", 629 | " Decode and return a dictionary of attributes.\n", 630 | " {\n", 631 | " 'a': 'foo', 'b': 'bar', c: 'baz', d: 'boz',\n", 632 | " 'empty': '', 'noval': None, 'entity': '&',\n", 633 | " 'sq': '\"', 'dq': '\\''\n", 634 | " }.\n", 635 | " NB HTMLParser is stricter in Python 2.6 & 3.2 than in later versions,\n", 636 | " but the cases in the unit test will work for all of 2.6, 2.7, 3.2-3.5.\n", 637 | " \"\"\"\n", 638 | " parser = HTMLAttributeParser()\n", 639 | " try:\n", 640 | " parser.feed(html_element)\n", 641 | " parser.close()\n", 642 | " except compat_HTMLParseError:\n", 643 | " pass\n", 644 | " return parser.attrs\n", 645 | "\n", 646 | "def hidden_inputs(html):\n", 647 | " html = re.sub(r'', '', html)\n", 648 | " hidden_inputs = {}\n", 649 | " for input in re.findall(r'(?i)(]+>)', html):\n", 650 | " attrs = extract_attributes(input)\n", 651 | " if not input:\n", 652 | " continue\n", 653 | " if attrs.get('type') not in ('hidden', 'submit'):\n", 654 | " continue\n", 655 | " name = attrs.get('name') or attrs.get('id')\n", 656 | " value = attrs.get('value')\n", 657 | " if name and value is not None:\n", 658 | " hidden_inputs[name] = value\n", 659 | " return hidden_inputs\n", 660 | "\n", 661 | "def search_regex(pattern, string, name, default=NO_DEFAULT, fatal=True, flags=0, group=None):\n", 662 | " \"\"\"\n", 663 | " Perform a regex search on the given string, using a single or a list of\n", 664 | " patterns returning the first matching group.\n", 665 | " In case of failure return a default value or raise a WARNING or a\n", 666 | " RegexNotFoundError, depending on fatal, specifying the field name.\n", 667 | " \"\"\"\n", 668 | " if isinstance(pattern, str):\n", 669 | " mobj = re.search(pattern, string, flags)\n", 670 | " else:\n", 671 | " for p in pattern:\n", 672 | " mobj = re.search(p, string, flags)\n", 673 | " if mobj:\n", 674 | " break\n", 675 | "\n", 676 | " _name = name\n", 677 | "\n", 678 | " if mobj:\n", 679 | " if group is None:\n", 680 | " # return the first matching group\n", 681 | " return next(g for g in mobj.groups() if g is not None)\n", 682 | " else:\n", 683 | " return mobj.group(group)\n", 684 | " elif default is not NO_DEFAULT:\n", 685 | " return default\n", 686 | " elif fatal:\n", 687 | " print('[-] Unable to extract %s' % _name)\n", 688 | " exit(0)\n", 689 | " else:\n", 690 | " print('[-] unable to extract %s' % _name)\n", 691 | " exit(0)\n", 692 | "\n", 693 | "def parse_json(json_string, video_id, transform_source=None, fatal=True):\n", 694 | " if transform_source:\n", 695 | " json_string = transform_source(json_string)\n", 696 | " try:\n", 697 | " return json.loads(json_string)\n", 698 | " except ValueError as ve:\n", 699 | " errmsg = '[-] %s: Failed to parse JSON ' % video_id\n", 700 | " if fatal:\n", 701 | " print(errmsg, ve)\n", 702 | " else:\n", 703 | " print(errmsg + str(ve))\n", 704 | "\n", 705 | "def js_to_json(code):\n", 706 | " COMMENT_RE = r'/\\*(?:(?!\\*/).)*?\\*/|//[^\\n]*'\n", 707 | " SKIP_RE = r'\\s*(?:{comment})?\\s*'.format(comment=COMMENT_RE)\n", 708 | " INTEGER_TABLE = (\n", 709 | " (r'(?s)^(0[xX][0-9a-fA-F]+){skip}:?$'.format(skip=SKIP_RE), 16),\n", 710 | " (r'(?s)^(0+[0-7]+){skip}:?$'.format(skip=SKIP_RE), 8),\n", 711 | " )\n", 712 | "\n", 713 | " def fix_kv(m):\n", 714 | " v = m.group(0)\n", 715 | " if v in ('true', 'false', 'null'):\n", 716 | " return v\n", 717 | " elif v.startswith('/*') or v.startswith('//') or v == ',':\n", 718 | " return \"\"\n", 719 | "\n", 720 | " if v[0] in (\"'\", '\"'):\n", 721 | " v = re.sub(r'(?s)\\\\.|\"', lambda m: {\n", 722 | " '\"': '\\\\\"',\n", 723 | " \"\\\\'\": \"'\",\n", 724 | " '\\\\\\n': '',\n", 725 | " '\\\\x': '\\\\u00',\n", 726 | " }.get(m.group(0), m.group(0)), v[1:-1])\n", 727 | "\n", 728 | " for regex, base in INTEGER_TABLE:\n", 729 | " im = re.match(regex, v)\n", 730 | " if im:\n", 731 | " i = int(im.group(1), base)\n", 732 | " return '\"%d\":' % i if v.endswith(':') else '%d' % i\n", 733 | "\n", 734 | " return '\"%s\"' % v\n", 735 | "\n", 736 | " return re.sub(r'''(?sx)\n", 737 | " \"(?:[^\"\\\\]*(?:\\\\\\\\|\\\\['\"nurtbfx/\\n]))*[^\"\\\\]*\"|\n", 738 | " '(?:[^'\\\\]*(?:\\\\\\\\|\\\\['\"nurtbfx/\\n]))*[^'\\\\]*'|\n", 739 | " {comment}|,(?={skip}[\\]}}])|\n", 740 | " [a-zA-Z_][.a-zA-Z_0-9]*|\n", 741 | " \\b(?:0[xX][0-9a-fA-F]+|0+[0-7]+)(?:{skip}:)?|\n", 742 | " [0-9]+(?={skip}:)\n", 743 | " '''.format(comment=COMMENT_RE, skip=SKIP_RE), fix_kv, code)\n", 744 | "\n", 745 | "\n", 746 | "# _extract.py\n", 747 | "# -------------------------------------------------------------------------------------\n", 748 | "class Udemy(ProgressBar):\n", 749 | "\n", 750 | " def __init__(self):\n", 751 | " self._session = ''\n", 752 | " self._cookies = ''\n", 753 | "\n", 754 | " def _clean(self, text):\n", 755 | " ok = re.compile(r'[^\\\\/:*?\"<>|]')\n", 756 | " text = \"\".join(x if ok.match(x) else \"_\" for x in text)\n", 757 | " text = re.sub(r'\\.+$', '', text.strip())\n", 758 | " return text\n", 759 | "\n", 760 | " def _course_name(self, url):\n", 761 | " mobj = re.search(r'(?i)(?://(?P.+?).udemy.com/(?:course(/draft)*/)?(?P[a-zA-Z0-9_-]+))', url)\n", 762 | " if mobj:\n", 763 | " return mobj.group('portal_name'), mobj.group('name_or_id')\n", 764 | "\n", 765 | " def _extract_cookie_string(self, raw_cookies):\n", 766 | " cookies = {}\n", 767 | " try:\n", 768 | " # client_id = re.search(r'(?i)(?:client_id=(?P\\w+))', raw_cookies)\n", 769 | " access_token = re.search(r'(?i)(?:access_token=(?P\\w+))', raw_cookies)\n", 770 | " except:\n", 771 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Cookies error, Request Headers is required.\\n\")\n", 772 | " sys.stdout.write(\n", 773 | " fc + sd + \"[\" + fm + sb + \"i\" + fc + sd + \"] : \" + fg + sb + \"Copy Request Headers for single request to a file, while you are logged in.\\n\")\n", 774 | " sys.exit(0)\n", 775 | " if not access_token:\n", 776 | " sys.stdout.write(\n", 777 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Cookies error, unable to find access_token, proper cookies required.\\n\")\n", 778 | " sys.stdout.flush()\n", 779 | " sys.exit(0)\n", 780 | " access_token = access_token.group('access_token')\n", 781 | " cookies.update({'access_token': access_token})\n", 782 | " # 'client_id': client_id.group('client_id'),\n", 783 | " return cookies\n", 784 | "\n", 785 | " def _sanitize(self, unsafetext):\n", 786 | " text = sanitize(slugify(unsafetext, lower=False, spaces=True, ok=SLUG_OK + '().[]'))\n", 787 | " return text\n", 788 | "\n", 789 | " def _login(self, username='', password='', cookies=''):\n", 790 | " if not cookies:\n", 791 | " auth = UdemyAuth(username=username, password=password)\n", 792 | " self._session = auth.authenticate()\n", 793 | " if cookies:\n", 794 | " self._cookies = self._extract_cookie_string(raw_cookies=cookies)\n", 795 | " access_token = self._cookies.get('access_token')\n", 796 | " client_id = self._cookies.get('client_id')\n", 797 | " time.sleep(0.3)\n", 798 | " auth = UdemyAuth()\n", 799 | " self._session = auth.authenticate(access_token=access_token, client_id=client_id)\n", 800 | " self._session._session.cookies.update(self._cookies)\n", 801 | " if self._session is not None:\n", 802 | " return {'login': 'successful'}\n", 803 | " else:\n", 804 | " return {'login': 'failed'}\n", 805 | "\n", 806 | " def _logout(self):\n", 807 | " return self._session.terminate()\n", 808 | "\n", 809 | " def _subscribed_courses(self, portal_name, course_name):\n", 810 | " results = []\n", 811 | " self._session._headers.update({\n", 812 | " 'Host': '{portal_name}.udemy.com'.format(portal_name=portal_name),\n", 813 | " 'Referer': 'https://{portal_name}.udemy.com/home/my-courses/search/?q={course_name}'.format(portal_name=portal_name,\n", 814 | " course_name=course_name)\n", 815 | " })\n", 816 | " url = COURSE_SEARCH.format(portal_name=portal_name, course_name=course_name)\n", 817 | " try:\n", 818 | " webpage = self._session._get(url).json()\n", 819 | " except conn_error as e:\n", 820 | " sys.stdout.write(\n", 821 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Connection error : make sure your internet connection is working.\\n\")\n", 822 | " time.sleep(0.8)\n", 823 | " sys.exit(0)\n", 824 | " except (ValueError, Exception) as e:\n", 825 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"%s.\\n\" % (e))\n", 826 | " time.sleep(0.8)\n", 827 | " sys.exit(0)\n", 828 | " else:\n", 829 | " results = webpage.get('results', [])\n", 830 | " return results\n", 831 | "\n", 832 | " def _my_courses(self, portal_name):\n", 833 | " results = []\n", 834 | " try:\n", 835 | " url = MY_COURSES_URL.format(portal_name=portal_name)\n", 836 | " webpage = self._session._get(url).json()\n", 837 | " except conn_error as e:\n", 838 | " sys.stdout.write(\n", 839 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Connection error : make sure your internet connection is working.\\n\")\n", 840 | " time.sleep(0.8)\n", 841 | " sys.exit(0)\n", 842 | " except (ValueError, Exception) as e:\n", 843 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"%s.\\n\" % (e))\n", 844 | " time.sleep(0.8)\n", 845 | " sys.exit(0)\n", 846 | " else:\n", 847 | " results = webpage.get('results', [])\n", 848 | " return results\n", 849 | "\n", 850 | " def _archived_courses(self, portal_name):\n", 851 | " results = []\n", 852 | " try:\n", 853 | " url = MY_COURSES_URL.format(portal_name=portal_name) + \"&is_archived=true\"\n", 854 | " webpage = self._session._get(url).json()\n", 855 | " except conn_error as e:\n", 856 | " sys.stdout.write(\n", 857 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Connection error : make sure your internet connection is working.\\n\")\n", 858 | " time.sleep(0.8)\n", 859 | " sys.exit(0)\n", 860 | " except (ValueError, Exception) as e:\n", 861 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"%s.\\n\" % (e))\n", 862 | " time.sleep(0.8)\n", 863 | " sys.exit(0)\n", 864 | " else:\n", 865 | " results = webpage.get('results', [])\n", 866 | " return results\n", 867 | "\n", 868 | " def _subscribed_collection_courses(self, portal_name):\n", 869 | " url = COLLECTION_URL.format(portal_name=portal_name)\n", 870 | " courses_lists = []\n", 871 | " try:\n", 872 | " webpage = self._session._get(url).json()\n", 873 | " except conn_error as e:\n", 874 | " sys.stdout.write(\n", 875 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Connection error : make sure your internet connection is working.\\n\")\n", 876 | " time.sleep(0.8)\n", 877 | " sys.exit(0)\n", 878 | " except (ValueError, Exception) as e:\n", 879 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"%s.\\n\" % (e))\n", 880 | " time.sleep(0.8)\n", 881 | " sys.exit(0)\n", 882 | " else:\n", 883 | " results = webpage.get('results', [])\n", 884 | " if results:\n", 885 | " [courses_lists.extend(courses.get('courses', [])) for courses in results if courses.get('courses', [])]\n", 886 | " return courses_lists\n", 887 | "\n", 888 | " def __extract_course(self, response, course_name):\n", 889 | " _temp = {}\n", 890 | " if response:\n", 891 | " for entry in response:\n", 892 | " course_id = str(entry.get('id'))\n", 893 | " published_title = entry.get('published_title')\n", 894 | " if course_name in (published_title, course_id):\n", 895 | " _temp = entry\n", 896 | " break\n", 897 | " return _temp\n", 898 | "\n", 899 | " def _extract_course_info(self, url):\n", 900 | " portal_name, course_name = self._course_name(url)\n", 901 | " course = {}\n", 902 | " results = self._subscribed_courses(portal_name=portal_name, course_name=course_name)\n", 903 | " course = self.__extract_course(response=results, course_name=course_name)\n", 904 | " if not course:\n", 905 | " results = self._my_courses(portal_name=portal_name)\n", 906 | " course = self.__extract_course(response=results, course_name=course_name)\n", 907 | " if not course:\n", 908 | " results = self._subscribed_collection_courses(portal_name=portal_name)\n", 909 | " course = self.__extract_course(response=results, course_name=course_name)\n", 910 | " if not course:\n", 911 | " results = self._archived_courses(portal_name=portal_name)\n", 912 | " course = self.__extract_course(response=results, course_name=course_name)\n", 913 | "\n", 914 | " if course:\n", 915 | " course.update({'portal_name': portal_name})\n", 916 | " return course.get('id'), course\n", 917 | " if not course:\n", 918 | " sys.stdout.write(\n", 919 | " '\\033[2K\\033[1G\\r\\r' + fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fg + sb + \"Downloading course information, course id not found .. (%s%sfailed%s%s)\\n\" % (\n", 920 | " fr, sb, fg, sb))\n", 921 | " sys.stdout.write(\n", 922 | " fc + sd + \"[\" + fw + sb + \"i\" + fc + sd + \"] : \" + fw + sb + \"It seems either you are not enrolled or you have to visit the course atleast once while you are logged in.\\n\")\n", 923 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to logout now...\\n\")\n", 924 | " if not self._cookies:\n", 925 | " self._logout()\n", 926 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sb + \"Logged out successfully.\\n\")\n", 927 | " sys.exit(0)\n", 928 | "\n", 929 | " def _extract_large_course_content(self, url):\n", 930 | " url = url.replace('10000', '300') if url.endswith('10000') else url\n", 931 | " try:\n", 932 | " data = self._session._get(url).json()\n", 933 | " except conn_error as e:\n", 934 | " sys.stdout.write(\n", 935 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Connection error : make sure your internet connection is working.\\n\")\n", 936 | " time.sleep(0.8)\n", 937 | " sys.exit(0)\n", 938 | " else:\n", 939 | " _next = data.get('next')\n", 940 | " while _next:\n", 941 | " try:\n", 942 | " resp = self._session._get(_next).json()\n", 943 | " except conn_error as e:\n", 944 | " sys.stdout.write(\n", 945 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Connection error : make sure your internet connection is working.\\n\")\n", 946 | " time.sleep(0.8)\n", 947 | " sys.exit(0)\n", 948 | " else:\n", 949 | " _next = resp.get('next')\n", 950 | " results = resp.get('results')\n", 951 | " if results and isinstance(results, list):\n", 952 | " for d in resp['results']:\n", 953 | " data['results'].append(d)\n", 954 | " return data\n", 955 | "\n", 956 | " def _extract_course_json(self, url, course_id, portal_name):\n", 957 | " self._session._headers.update({'Referer': url})\n", 958 | " url = COURSE_URL.format(portal_name=portal_name, course_id=course_id)\n", 959 | " try:\n", 960 | " resp = self._session._get(url)\n", 961 | " if resp.status_code in [502, 503]:\n", 962 | " resp = self._extract_large_course_content(url=url)\n", 963 | " else:\n", 964 | " resp = resp.json()\n", 965 | " except conn_error as e:\n", 966 | " sys.stdout.write(\n", 967 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Connection error : make sure your internet connection is working.\\n\")\n", 968 | " time.sleep(0.8)\n", 969 | " sys.exit(0)\n", 970 | " except (ValueError, Exception) as e:\n", 971 | " resp = self._extract_large_course_content(url=url)\n", 972 | " return resp\n", 973 | " else:\n", 974 | " return resp\n", 975 | "\n", 976 | " def _html_to_json(self, view_html, lecture_id):\n", 977 | " data = parse_json(\n", 978 | " search_regex(\n", 979 | " r'videojs-setup-data=([\"\\'])(?P{.+?})\\1',\n", 980 | " view_html,\n", 981 | " 'setup data',\n", 982 | " default='{}',\n", 983 | " group='data'),\n", 984 | " lecture_id,\n", 985 | " transform_source=unescapeHTML,\n", 986 | " fatal=False\n", 987 | " )\n", 988 | " text_tracks = parse_json(\n", 989 | " search_regex(\n", 990 | " r'text-tracks=([\"\\'])(?P\\[.+?\\])\\1',\n", 991 | " view_html,\n", 992 | " 'text tracks',\n", 993 | " default='{}',\n", 994 | " group='data'),\n", 995 | " lecture_id,\n", 996 | " transform_source=lambda s: js_to_json(unescapeHTML(s)),\n", 997 | " fatal=False\n", 998 | " )\n", 999 | " return data, text_tracks\n", 1000 | "\n", 1001 | " def _extract_ppt(self, assets):\n", 1002 | " _temp = []\n", 1003 | " download_urls = assets.get('download_urls')\n", 1004 | " slides_urls = assets.get('slide_urls')\n", 1005 | " filename = self._sanitize(assets.get('filename'))\n", 1006 | " if download_urls and isinstance(download_urls, dict):\n", 1007 | " extension = filename.rsplit('.', 1)[-1] if '.' in filename else ''\n", 1008 | " download_url = download_urls.get('Presentation', [])[0].get('file')\n", 1009 | " _temp.append({\n", 1010 | " 'type': 'presentation',\n", 1011 | " 'filename': filename,\n", 1012 | " 'extension': extension,\n", 1013 | " 'download_url': download_url\n", 1014 | " })\n", 1015 | " return _temp\n", 1016 | "\n", 1017 | " def _extract_file(self, assets):\n", 1018 | " _temp = []\n", 1019 | " download_urls = assets.get('download_urls')\n", 1020 | " filename = self._sanitize(assets.get('filename'))\n", 1021 | " if download_urls and isinstance(download_urls, dict):\n", 1022 | " extension = filename.rsplit('.', 1)[-1] if '.' in filename else ''\n", 1023 | " download_url = download_urls.get('File', [])[0].get('file')\n", 1024 | " _temp.append({\n", 1025 | " 'type': 'file',\n", 1026 | " 'filename': filename,\n", 1027 | " 'extension': extension,\n", 1028 | " 'download_url': download_url\n", 1029 | " })\n", 1030 | " return _temp\n", 1031 | "\n", 1032 | " def _extract_ebook(self, assets):\n", 1033 | " _temp = []\n", 1034 | " download_urls = assets.get('download_urls')\n", 1035 | " filename = self._sanitize(assets.get('filename'))\n", 1036 | " if download_urls and isinstance(download_urls, dict):\n", 1037 | " extension = filename.rsplit('.', 1)[-1] if '.' in filename else ''\n", 1038 | " download_url = download_urls.get('E-Book', [])[0].get('file')\n", 1039 | " _temp.append({\n", 1040 | " 'type': 'ebook',\n", 1041 | " 'filename': filename,\n", 1042 | " 'extension': extension,\n", 1043 | " 'download_url': download_url\n", 1044 | " })\n", 1045 | " return _temp\n", 1046 | "\n", 1047 | " def _extract_audio(self, assets):\n", 1048 | " _temp = []\n", 1049 | " download_urls = assets.get('download_urls')\n", 1050 | " filename = self._sanitize(assets.get('filename'))\n", 1051 | " if download_urls and isinstance(download_urls, dict):\n", 1052 | " extension = filename.rsplit('.', 1)[-1] if '.' in filename else ''\n", 1053 | " download_url = download_urls.get('Audio', [])[0].get('file')\n", 1054 | " _temp.append({\n", 1055 | " 'type': 'audio',\n", 1056 | " 'filename': filename,\n", 1057 | " 'extension': extension,\n", 1058 | " 'download_url': download_url\n", 1059 | " })\n", 1060 | " return _temp\n", 1061 | "\n", 1062 | " def _extract_sources(self, sources):\n", 1063 | " _temp = []\n", 1064 | " if sources and isinstance(sources, list):\n", 1065 | " for source in sources:\n", 1066 | " label = source.get('label')\n", 1067 | " download_url = source.get('file')\n", 1068 | " if not download_url:\n", 1069 | " continue\n", 1070 | " if label.lower() == 'audio':\n", 1071 | " continue\n", 1072 | " height = label if label else None\n", 1073 | " if height == \"2160\":\n", 1074 | " width = \"3840\"\n", 1075 | " elif height == \"1440\":\n", 1076 | " width = \"2560\"\n", 1077 | " elif height == \"1080\":\n", 1078 | " width = \"1920\"\n", 1079 | " elif height == \"720\":\n", 1080 | " width = \"1280\"\n", 1081 | " elif height == \"480\":\n", 1082 | " width = \"854\"\n", 1083 | " elif height == \"360\":\n", 1084 | " width = \"640\"\n", 1085 | " elif height == \"240\":\n", 1086 | " width = \"426\"\n", 1087 | " else:\n", 1088 | " width = \"256\"\n", 1089 | " if source.get('type') == 'application/x-mpegURL' or 'm3u8' in download_url:\n", 1090 | " continue\n", 1091 | " else:\n", 1092 | " _type = source.get('type')\n", 1093 | " _temp.append({\n", 1094 | " 'type': 'video',\n", 1095 | " 'height': height,\n", 1096 | " 'width': width,\n", 1097 | " 'extension': _type.replace('video/', ''),\n", 1098 | " 'download_url': download_url,\n", 1099 | " })\n", 1100 | " return _temp\n", 1101 | "\n", 1102 | " def _extract_subtitles(self, tracks):\n", 1103 | " _temp = []\n", 1104 | " if tracks and isinstance(tracks, list):\n", 1105 | " for track in tracks:\n", 1106 | " if not isinstance(track, dict):\n", 1107 | " continue\n", 1108 | " if track.get('_class') != 'caption':\n", 1109 | " continue\n", 1110 | " download_url = track.get('url')\n", 1111 | " if not download_url or not isinstance(download_url, encoding):\n", 1112 | " continue\n", 1113 | " lang = track.get('language') or track.get('srclang') or track.get('label') or track['locale_id'].split('_')[0]\n", 1114 | " ext = 'vtt' if 'vtt' in download_url.rsplit('.', 1)[-1] else 'srt'\n", 1115 | " _temp.append({\n", 1116 | " 'type': 'subtitle',\n", 1117 | " 'language': lang,\n", 1118 | " 'extension': ext,\n", 1119 | " 'download_url': download_url,\n", 1120 | " })\n", 1121 | " return _temp\n", 1122 | "\n", 1123 | " def _extract_supplementary_assets(self, supp_assets):\n", 1124 | " _temp = []\n", 1125 | " for entry in supp_assets:\n", 1126 | " file_id = entry.get('id')\n", 1127 | " filename = self._sanitize(entry.get('filename'))\n", 1128 | " download_urls = entry.get('download_urls')\n", 1129 | " external_url = entry.get('external_url')\n", 1130 | " slide_url = entry.get('slide_urls')\n", 1131 | " asset_type = entry.get('asset_type').lower()\n", 1132 | " if asset_type == 'file':\n", 1133 | " if download_urls and isinstance(download_urls, dict):\n", 1134 | " extension = filename.rsplit('.', 1)[-1] if '.' in filename else ''\n", 1135 | " download_url = download_urls.get('File', [])[0].get('file')\n", 1136 | " _temp.append({\n", 1137 | " 'type': 'file',\n", 1138 | " 'filename': filename,\n", 1139 | " 'extension': extension,\n", 1140 | " 'download_url': download_url,\n", 1141 | " })\n", 1142 | " elif asset_type == 'sourcecode':\n", 1143 | " if download_urls and isinstance(download_urls, dict):\n", 1144 | " extension = filename.rsplit('.', 1)[-1] if '.' in filename else ''\n", 1145 | " download_url = download_urls.get('SourceCode', [])[0].get('file')\n", 1146 | " _temp.append({\n", 1147 | " 'type': 'source_code',\n", 1148 | " 'filename': filename,\n", 1149 | " 'extension': extension,\n", 1150 | " 'download_url': download_url,\n", 1151 | " })\n", 1152 | " elif asset_type == 'externallink':\n", 1153 | " _temp.append({\n", 1154 | " 'type': 'external_link',\n", 1155 | " 'filename': filename,\n", 1156 | " 'extension': 'txt',\n", 1157 | " 'download_url': external_url,\n", 1158 | " })\n", 1159 | " return _temp\n", 1160 | "\n", 1161 | " def _real_extract(self, url=''):\n", 1162 | "\n", 1163 | " _udemy = {}\n", 1164 | " course_id, course_info = self._extract_course_info(url)\n", 1165 | "\n", 1166 | " if course_info and isinstance(course_info, dict):\n", 1167 | " course_title = course_info.get('published_title')\n", 1168 | " portal_name = course_info.get('portal_name')\n", 1169 | "\n", 1170 | " course_json = self._extract_course_json(url, course_id, portal_name)\n", 1171 | " course = course_json.get('results')\n", 1172 | " resource = course_json.get('detail')\n", 1173 | "\n", 1174 | " if resource:\n", 1175 | " if not self._cookies:\n", 1176 | " sys.stdout.write(\n", 1177 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Udemy Says : {}{}{} Run udemy-dl against course within few seconds.\\n\".format(\n", 1178 | " resource, fw, sb))\n", 1179 | " if self._cookies:\n", 1180 | " sys.stdout.write(\n", 1181 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Udemy Says : {}{}{} cookies seems to be expired.\\n\".format(resource,\n", 1182 | " fw, sb))\n", 1183 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to logout now...\\n\")\n", 1184 | " if not self._cookies:\n", 1185 | " self._logout()\n", 1186 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sb + \"Logged out successfully.\\n\")\n", 1187 | " sys.exit(0)\n", 1188 | "\n", 1189 | " _udemy['course_id'] = course_id\n", 1190 | " _udemy['course_title'] = course_title\n", 1191 | " _udemy['chapters'] = []\n", 1192 | "\n", 1193 | " counter = -1\n", 1194 | "\n", 1195 | " if course:\n", 1196 | " for entry in course:\n", 1197 | "\n", 1198 | " clazz = entry.get('_class')\n", 1199 | " asset = entry.get('asset')\n", 1200 | " supp_assets = entry.get('supplementary_assets')\n", 1201 | "\n", 1202 | " if clazz == 'chapter':\n", 1203 | " lectures = []\n", 1204 | " chapter_index = entry.get('object_index')\n", 1205 | " chapter_title = self._clean(self._sanitize(entry.get('title')))\n", 1206 | " chapter = \"{0:02d} {1!s}\".format(chapter_index, chapter_title)\n", 1207 | " unsafe_chapter = u'{0:02d} '.format(chapter_index) + self._clean(entry.get('title'))\n", 1208 | " if chapter not in _udemy['chapters']:\n", 1209 | " _udemy['chapters'].append({\n", 1210 | " 'chapter_title': chapter,\n", 1211 | " 'chapter_id': entry.get(\"id\"),\n", 1212 | " 'chapter_index': chapter_index,\n", 1213 | " 'unsafe_chapter': unsafe_chapter,\n", 1214 | " 'lectures': [],\n", 1215 | " })\n", 1216 | " counter += 1\n", 1217 | " elif clazz == 'lecture':\n", 1218 | "\n", 1219 | " lecture_id = entry.get(\"id\")\n", 1220 | " if len(_udemy['chapters']) == 0:\n", 1221 | " lectures = []\n", 1222 | " chapter_index = entry.get('object_index')\n", 1223 | " chapter_title = self._clean(self._sanitize(entry.get('title')))\n", 1224 | " chapter = \"{0:03d} {1!s}\".format(chapter_index, chapter_title)\n", 1225 | " unsafe_chapter = u'{0:02d} '.format(chapter_index) + self._clean(entry.get('title'))\n", 1226 | " if chapter not in _udemy['chapters']:\n", 1227 | " _udemy['chapters'].append({\n", 1228 | " 'chapter_title': chapter,\n", 1229 | " 'chapter_id': lecture_id,\n", 1230 | " 'chapter_index': chapter_index,\n", 1231 | " 'unsafe_chapter': unsafe_chapter,\n", 1232 | " 'lectures': [],\n", 1233 | " })\n", 1234 | " counter += 1\n", 1235 | "\n", 1236 | " if lecture_id:\n", 1237 | "\n", 1238 | " view_html = entry.get('view_html')\n", 1239 | " retVal = []\n", 1240 | "\n", 1241 | " if isinstance(asset, dict):\n", 1242 | " asset_type = asset.get('asset_type').lower() or asset.get('assetType').lower()\n", 1243 | " if asset_type == 'article':\n", 1244 | " if isinstance(supp_assets, list) and len(supp_assets) > 0:\n", 1245 | " retVal = self._extract_supplementary_assets(supp_assets)\n", 1246 | " elif asset_type == 'video':\n", 1247 | " if isinstance(supp_assets, list) and len(supp_assets) > 0:\n", 1248 | " retVal = self._extract_supplementary_assets(supp_assets)\n", 1249 | " elif asset_type == 'e-book':\n", 1250 | " retVal = self._extract_ebook(asset)\n", 1251 | " elif asset_type == 'file':\n", 1252 | " retVal = self._extract_file(asset)\n", 1253 | " elif asset_type == 'presentation':\n", 1254 | " retVal = self._extract_ppt(asset)\n", 1255 | " elif asset_type == 'audio':\n", 1256 | " retVal = self._extract_audio(asset)\n", 1257 | "\n", 1258 | " if view_html:\n", 1259 | " text = '\\r' + fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Downloading course information .. \"\n", 1260 | " self._spinner(text)\n", 1261 | " lecture_index = entry.get('object_index')\n", 1262 | " lecture_title = self._clean(self._sanitize(entry.get('title')))\n", 1263 | " lecture = \"{0:03d} {1!s}\".format(lecture_index, lecture_title)\n", 1264 | " unsafe_lecture = u'{0:03d} '.format(lecture_index) + entry.get('title')\n", 1265 | " data, subs = self._html_to_json(view_html, lecture_id)\n", 1266 | " if data and isinstance(data, dict):\n", 1267 | " sources = data.get('sources')\n", 1268 | " tracks = data.get('tracks') if isinstance(data.get('tracks'), list) else subs\n", 1269 | " duration = data.get('duration')\n", 1270 | " lectures.append({\n", 1271 | " 'lecture_index': lecture_index,\n", 1272 | " 'lectures_id': lecture_id,\n", 1273 | " 'lecture_title': lecture,\n", 1274 | " 'unsafe_lecture': unsafe_lecture,\n", 1275 | " 'duration': duration,\n", 1276 | " 'assets': retVal,\n", 1277 | " 'assets_count': len(retVal),\n", 1278 | " 'sources': self._extract_sources(sources),\n", 1279 | " 'subtitles': self._extract_subtitles(tracks),\n", 1280 | " 'subtitle_count': len(self._extract_subtitles(tracks)),\n", 1281 | " 'sources_count': len(self._extract_sources(sources)),\n", 1282 | " })\n", 1283 | " else:\n", 1284 | " lectures.append({\n", 1285 | " 'lecture_index': lecture_index,\n", 1286 | " 'lectures_id': lecture_id,\n", 1287 | " 'lecture_title': lecture,\n", 1288 | " 'unsafe_lecture': unsafe_lecture,\n", 1289 | " 'html_content': view_html,\n", 1290 | " 'extension': 'html',\n", 1291 | " 'assets': retVal,\n", 1292 | " 'assets_count': len(retVal),\n", 1293 | " 'subtitle_count': 0,\n", 1294 | " 'sources_count': 0,\n", 1295 | " })\n", 1296 | " if not view_html:\n", 1297 | " text = '\\r' + fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Downloading course information .. \"\n", 1298 | " self._spinner(text)\n", 1299 | " lecture_index = entry.get('object_index')\n", 1300 | " lecture_title = self._clean(self._sanitize(entry.get('title')))\n", 1301 | " lecture = \"{0:03d} {1!s}\".format(lecture_index, lecture_title)\n", 1302 | " unsafe_lecture = u'{0:03d} '.format(lecture_index) + self._clean(entry.get('title'))\n", 1303 | " data = asset.get('stream_urls')\n", 1304 | " if data and isinstance(data, dict):\n", 1305 | " sources = data.get('Video')\n", 1306 | " tracks = asset.get('captions')\n", 1307 | " duration = asset.get('time_estimation')\n", 1308 | " lectures.append({\n", 1309 | " 'lecture_index': lecture_index,\n", 1310 | " 'lectures_id': lecture_id,\n", 1311 | " 'lecture_title': lecture,\n", 1312 | " 'unsafe_lecture': unsafe_lecture,\n", 1313 | " 'duration': duration,\n", 1314 | " 'assets': retVal,\n", 1315 | " 'assets_count': len(retVal),\n", 1316 | " 'sources': self._extract_sources(sources),\n", 1317 | " 'subtitles': self._extract_subtitles(tracks),\n", 1318 | " 'subtitle_count': len(self._extract_subtitles(tracks)),\n", 1319 | " 'sources_count': len(self._extract_sources(sources)),\n", 1320 | " })\n", 1321 | " else:\n", 1322 | " lectures.append({\n", 1323 | " 'lecture_index': lecture_index,\n", 1324 | " 'lectures_id': lecture_id,\n", 1325 | " 'lecture_title': lecture,\n", 1326 | " 'unsafe_lecture': unsafe_lecture,\n", 1327 | " 'html_content': asset.get('body'),\n", 1328 | " 'extension': 'html',\n", 1329 | " 'assets': retVal,\n", 1330 | " 'assets_count': len(retVal),\n", 1331 | " 'subtitle_count': 0,\n", 1332 | " 'sources_count': 0,\n", 1333 | " })\n", 1334 | "\n", 1335 | " _udemy['chapters'][counter]['lectures'] = lectures\n", 1336 | " _udemy['chapters'][counter]['lectures_count'] = len(lectures)\n", 1337 | " elif clazz == 'quiz':\n", 1338 | " lecture_id = entry.get(\"id\")\n", 1339 | " if len(_udemy['chapters']) == 0:\n", 1340 | " lectures = []\n", 1341 | " chapter_index = entry.get('object_index')\n", 1342 | " chapter_title = self._clean(self._sanitize(entry.get('title')))\n", 1343 | " chapter = \"{0:03d} {1!s}\".format(chapter_index, chapter_title)\n", 1344 | " unsafe_chapter = u'{0:02d} '.format(chapter_index) + self._clean(entry.get('title'))\n", 1345 | " if chapter not in _udemy['chapters']:\n", 1346 | " _udemy['chapters'].append({\n", 1347 | " 'chapter_title': chapter,\n", 1348 | " 'unsafe_chapter': unsafe_chapter,\n", 1349 | " 'chapter_id': lecture_id,\n", 1350 | " 'chapter_index': chapter_index,\n", 1351 | " 'lectures': [],\n", 1352 | " })\n", 1353 | " counter += 1\n", 1354 | " _udemy['chapters'][counter]['lectures'] = lectures\n", 1355 | " _udemy['chapters'][counter]['lectures_count'] = len(lectures)\n", 1356 | " _udemy['total_chapters'] = len(_udemy['chapters'])\n", 1357 | " _udemy['total_lectures'] = sum([entry.get('lectures_count', 0) for entry in _udemy['chapters'] if entry])\n", 1358 | "\n", 1359 | " return _udemy\n", 1360 | "\n", 1361 | "\n", 1362 | "# _shared.py\n", 1363 | "# -------------------------------------------------------------------------------------\n", 1364 | "class Downloader(object):\n", 1365 | "\n", 1366 | " def __init__(self):\n", 1367 | " self._url = None\n", 1368 | " self._filename = None\n", 1369 | " self._mediatype = None\n", 1370 | " self._extension = None\n", 1371 | " self._sess = requests.session()\n", 1372 | "\n", 1373 | " @property\n", 1374 | " def url(self):\n", 1375 | " \"\"\"abac\"\"\"\n", 1376 | " return self._url\n", 1377 | "\n", 1378 | " @property\n", 1379 | " def mediatype(self):\n", 1380 | " return self._mediatype\n", 1381 | "\n", 1382 | " @property\n", 1383 | " def extension(self):\n", 1384 | " return self._extension\n", 1385 | "\n", 1386 | " @property\n", 1387 | " def filename(self):\n", 1388 | " if not self._filename:\n", 1389 | " self._filename = self._generate_filename()\n", 1390 | " return self._filename\n", 1391 | "\n", 1392 | " @property\n", 1393 | " def unsafe_filename(self):\n", 1394 | " if not self._filename:\n", 1395 | " self._filename = self._generate_unsafe_filename()\n", 1396 | " return self._filename\n", 1397 | "\n", 1398 | " def _generate_filename():\n", 1399 | " pass\n", 1400 | "\n", 1401 | " def _generate_unsafe_filename():\n", 1402 | " pass\n", 1403 | "\n", 1404 | " def _write_external_links(self, filepath, unsafe=False):\n", 1405 | " retVal = {}\n", 1406 | " savedirs, name = os.path.split(filepath)\n", 1407 | " filename = 'external-assets-links.txt' if not unsafe else u'external-assets-links.txt'\n", 1408 | " filename = os.path.join(savedirs, filename)\n", 1409 | "\n", 1410 | " file_data = []\n", 1411 | " if os.path.isfile(filename):\n", 1412 | " file_data = [i.strip().lower() for i in open(filename) if i]\n", 1413 | "\n", 1414 | " try:\n", 1415 | " f = codecs.open(filename, 'a', encoding='utf-8', errors='ignore')\n", 1416 | " data = '\\n{}\\n{}\\n'.format(name, self.url) if not unsafe else u'\\n{}\\n{}\\n'.format(\n", 1417 | " name, self.url)\n", 1418 | " if name.lower() not in file_data:\n", 1419 | " f.write(data)\n", 1420 | " except (OSError, Exception, UnicodeDecodeError) as e:\n", 1421 | " retVal = {'status' : 'False', 'msg' : '{}'.format(e)}\n", 1422 | " else:\n", 1423 | " retVal = {'status' : 'True', 'msg' : 'download'}\n", 1424 | " f.close()\n", 1425 | "\n", 1426 | " return retVal\n", 1427 | "\n", 1428 | " def download(self, filepath=\"\", unsafe=False, quiet=False, callback=lambda *x: None):\n", 1429 | " savedir = filename = \"\"\n", 1430 | " retVal = {}\n", 1431 | "\n", 1432 | " if filepath and os.path.isdir(filepath):\n", 1433 | " savedir, filename = filepath, self.filename if not unsafe else self.unsafe_filename\n", 1434 | "\n", 1435 | " elif filepath:\n", 1436 | " savedir, filename = os.path.split(filepath)\n", 1437 | "\n", 1438 | " else:\n", 1439 | " filename = self.filename if not unsafe else self.unsafe_filename\n", 1440 | "\n", 1441 | " filepath = os.path.join(savedir, filename)\n", 1442 | " if os.name == \"nt\" and len(filepath) > 250:\n", 1443 | " filepath = \"\\\\\\\\?\\\\{}\".format(filepath)\n", 1444 | "\n", 1445 | " if self.mediatype == 'external_link':\n", 1446 | " return self._write_external_links(filepath, unsafe)\n", 1447 | "\n", 1448 | " if filepath and filepath.endswith('.vtt'):\n", 1449 | " filepath_vtt2srt = filepath.replace('.vtt', '.srt')\n", 1450 | " if os.path.isfile(filepath_vtt2srt):\n", 1451 | " retVal = {\"status\" : \"True\", \"msg\" : \"already downloaded\"}\n", 1452 | " return retVal\n", 1453 | "\n", 1454 | " if os.path.isfile(filepath):\n", 1455 | " retVal = {\"status\": \"True\", \"msg\": \"already downloaded\"}\n", 1456 | " return retVal\n", 1457 | "\n", 1458 | " temp_filepath = filepath + \".part\"\n", 1459 | "\n", 1460 | " self._active = True\n", 1461 | " bytes_to_be_downloaded = 0\n", 1462 | " fmode, offset = \"wb\", 0\n", 1463 | " chunksize, bytesdone, t0 = 16384, 0, time.time()\n", 1464 | " headers = {'User-Agent': HEADERS.get('User-Agent')}\n", 1465 | " if os.path.exists(temp_filepath):\n", 1466 | " offset = os.stat(temp_filepath).st_size\n", 1467 | "\n", 1468 | " if offset:\n", 1469 | " offset_range = 'bytes={}-'.format(offset)\n", 1470 | " headers['Range'] = offset_range\n", 1471 | " bytesdone = offset\n", 1472 | " fmode = \"ab\"\n", 1473 | "\n", 1474 | " status_string = (' {:,} Bytes [{:.2%}] received. Rate: [{:4.0f} '\n", 1475 | " 'KB/s]. ETA: [{:.0f} secs]')\n", 1476 | "\n", 1477 | " try:\n", 1478 | " try:\n", 1479 | " response = self._sess.get(self.url, headers=headers, stream=True, timeout=10)\n", 1480 | " except conn_error as error:\n", 1481 | " return {'status': 'False', 'msg': 'ConnectionError: %s' % (str(error))}\n", 1482 | " if response.ok:\n", 1483 | " bytes_to_be_downloaded = total = int(response.headers.get('Content-Length'))\n", 1484 | " if bytesdone > 0:\n", 1485 | " bytes_to_be_downloaded = bytes_to_be_downloaded + bytesdone\n", 1486 | " total = bytes_to_be_downloaded\n", 1487 | " with open(temp_filepath, fmode) as media_file:\n", 1488 | " is_malformed = False\n", 1489 | " for chunk in response.iter_content(chunksize):\n", 1490 | " if not chunk:\n", 1491 | " break\n", 1492 | " media_file.write(chunk)\n", 1493 | " elapsed = time.time() - t0\n", 1494 | " bytesdone += len(chunk)\n", 1495 | " if elapsed:\n", 1496 | " try:\n", 1497 | " rate = ((float(bytesdone) - float(offset)) / 1024.0) / elapsed\n", 1498 | " eta = (total - bytesdone) / (rate * 1024.0)\n", 1499 | " except ZeroDivisionError:\n", 1500 | " is_malformed = True\n", 1501 | " try:\n", 1502 | " os.unlink(temp_filepath)\n", 1503 | " except Exception:\n", 1504 | " pass\n", 1505 | " retVal = {\"status\" : \"False\", \"msg\" : \"ZeroDivisionError : it seems, lecture has malfunction or is zero byte(s) ..\"}\n", 1506 | " break\n", 1507 | " else:\n", 1508 | " rate = 0\n", 1509 | " eta = 0\n", 1510 | "\n", 1511 | " if not is_malformed:\n", 1512 | " progress_stats = (\n", 1513 | " bytesdone, bytesdone * 1.0 / total, rate, eta)\n", 1514 | "\n", 1515 | " if not quiet:\n", 1516 | " status = status_string.format(*progress_stats)\n", 1517 | " sys.stdout.write(\n", 1518 | " \"\\r\" + status + ' ' * 4 + \"\\r\")\n", 1519 | " sys.stdout.flush()\n", 1520 | "\n", 1521 | " if callback:\n", 1522 | " callback(total, *progress_stats)\n", 1523 | " if not response.ok:\n", 1524 | " code = response.status_code\n", 1525 | " reason = response.reason\n", 1526 | " retVal = {\n", 1527 | " \"status\": \"False\", \"msg\": \"Udemy returned HTTP Code %s: %s\" % (code, reason)}\n", 1528 | " response.close()\n", 1529 | " except KeyboardInterrupt as error:\n", 1530 | " raise error\n", 1531 | " except Exception as error:\n", 1532 | " retVal = {\"status\": \"False\",\n", 1533 | " \"msg\": \"Reason : {}\".format(str(error))}\n", 1534 | " return retVal\n", 1535 | " # check if file is downloaded completely\n", 1536 | " if os.path.isfile(temp_filepath):\n", 1537 | " total_bytes_done = os.stat(temp_filepath).st_size\n", 1538 | " if total_bytes_done == bytes_to_be_downloaded:\n", 1539 | " self._active = False\n", 1540 | " if total_bytes_done < bytes_to_be_downloaded:\n", 1541 | " # set active to be True as remaining bytes to be downloaded\n", 1542 | " self._active = True\n", 1543 | " # try downloading back again remaining bytes until we download completely\n", 1544 | " self.download(filepath=filepath,\n", 1545 | " unsafe=unsafe,\n", 1546 | " quiet=quiet)\n", 1547 | "\n", 1548 | "\n", 1549 | " if not self._active:\n", 1550 | " os.rename(temp_filepath, filepath)\n", 1551 | " retVal = {\"status\": \"True\", \"msg\": \"download\"}\n", 1552 | "\n", 1553 | " return retVal\n", 1554 | "\n", 1555 | "class UdemyCourse(object):\n", 1556 | "\n", 1557 | " def __init__(self, url, username='', password='', cookies='', basic=True, callback=None):\n", 1558 | "\n", 1559 | " self._url = url\n", 1560 | " self._username = username\n", 1561 | " self._password = password\n", 1562 | " self._cookies = cookies \n", 1563 | " self._callback = callback or (lambda x: None)\n", 1564 | " self._have_basic = False\n", 1565 | "\n", 1566 | " self._id = None\n", 1567 | " self._title = None\n", 1568 | " self._chapters_count = None\n", 1569 | " self._total_lectures = None\n", 1570 | "\n", 1571 | " self._chapters = []\n", 1572 | "\n", 1573 | " if basic:\n", 1574 | " self._fetch_course()\n", 1575 | "\n", 1576 | " def _fetch_course(self):\n", 1577 | " raise NotImplementedError\n", 1578 | "\n", 1579 | " @property\n", 1580 | " def id(self):\n", 1581 | " if not self._id:\n", 1582 | " self._fetch_course()\n", 1583 | " return self._id\n", 1584 | "\n", 1585 | " @property\n", 1586 | " def title(self):\n", 1587 | " if not self._title:\n", 1588 | " self._fetch_course()\n", 1589 | " return self._title\n", 1590 | "\n", 1591 | " @property\n", 1592 | " def chapters(self):\n", 1593 | " if not self._chapters_count:\n", 1594 | " self._fetch_course()\n", 1595 | " return self._chapters_count\n", 1596 | "\n", 1597 | " @property\n", 1598 | " def lectures(self):\n", 1599 | " if not self._total_lectures:\n", 1600 | " self._fetch_course()\n", 1601 | " return self._total_lectures\n", 1602 | "\n", 1603 | " def get_chapters(self):\n", 1604 | " if not self._chapters:\n", 1605 | " self._fetch_course()\n", 1606 | " return self._chapters\n", 1607 | "\n", 1608 | "class UdemyChapters(object):\n", 1609 | "\n", 1610 | " def __init__(self):\n", 1611 | "\n", 1612 | " self._chapter_id = None\n", 1613 | " self._chapter_index = None\n", 1614 | " self._chapter_title = None\n", 1615 | " self._unsafe_title = None\n", 1616 | " self._lectures_count = None\n", 1617 | "\n", 1618 | " self._lectures = []\n", 1619 | "\n", 1620 | " def __repr__(self):\n", 1621 | " chapter = \"{title}\".format(title=self.title)\n", 1622 | " return chapter\n", 1623 | "\n", 1624 | " @property\n", 1625 | " def id(self):\n", 1626 | " return self._chapter_id\n", 1627 | "\n", 1628 | " @property\n", 1629 | " def index(self):\n", 1630 | " return self._chapter_index\n", 1631 | "\n", 1632 | " @property\n", 1633 | " def title(self):\n", 1634 | " return self._chapter_title\n", 1635 | "\n", 1636 | " @property\n", 1637 | " def unsafe_title(self):\n", 1638 | " return self._unsafe_title\n", 1639 | " \n", 1640 | " @property\n", 1641 | " def lectures(self):\n", 1642 | " return self._lectures_count\n", 1643 | "\n", 1644 | " def get_lectures(self):\n", 1645 | " return self._lectures\n", 1646 | "\n", 1647 | "class UdemyLectures(object):\n", 1648 | "\n", 1649 | " def __init__(self):\n", 1650 | "\n", 1651 | " self._best = None\n", 1652 | " self._duration = None\n", 1653 | " self._extension = None\n", 1654 | " self._lecture_id = None\n", 1655 | " self._lecture_title = None\n", 1656 | " self._unsafe_title = None\n", 1657 | " self._lecture_index = None\n", 1658 | " self._sources_count = None\n", 1659 | " self._assets_count = None\n", 1660 | " self._subtitles_count = None\n", 1661 | " self._html_content = None\n", 1662 | "\n", 1663 | " self._assets = []\n", 1664 | " self._streams = []\n", 1665 | " self._subtitles = []\n", 1666 | "\n", 1667 | " def __repr__(self):\n", 1668 | " lecture = \"{title}\".format(title=self.title)\n", 1669 | " return lecture\n", 1670 | " \n", 1671 | " @property\n", 1672 | " def id(self):\n", 1673 | " return self._lecture_id\n", 1674 | "\n", 1675 | " @property\n", 1676 | " def index(self):\n", 1677 | " return self._lecture_index\n", 1678 | "\n", 1679 | " @property\n", 1680 | " def title(self):\n", 1681 | " return self._lecture_title\n", 1682 | "\n", 1683 | " @property\n", 1684 | " def unsafe_title(self):\n", 1685 | " return self._unsafe_title\n", 1686 | "\n", 1687 | " @property\n", 1688 | " def html(self):\n", 1689 | " return self._html_content\n", 1690 | " \n", 1691 | " @property\n", 1692 | " def duration(self):\n", 1693 | " return self._duration\n", 1694 | "\n", 1695 | " @property\n", 1696 | " def extension(self):\n", 1697 | " return self._extension\n", 1698 | "\n", 1699 | " @property\n", 1700 | " def assets(self):\n", 1701 | " if not self._assets:\n", 1702 | " self._process_assets()\n", 1703 | " return self._assets\n", 1704 | "\n", 1705 | " @property\n", 1706 | " def streams(self):\n", 1707 | " if not self._streams:\n", 1708 | " self._process_streams()\n", 1709 | " return self._streams\n", 1710 | "\n", 1711 | " @property\n", 1712 | " def subtitles(self):\n", 1713 | " if not self._subtitles:\n", 1714 | " self._process_subtitles()\n", 1715 | " return self._subtitles\n", 1716 | "\n", 1717 | " def _getbest(self):\n", 1718 | " streams = self.streams\n", 1719 | " if not streams:\n", 1720 | " return None\n", 1721 | " def _sortkey(x, keyres=0, keyftype=0):\n", 1722 | " keyres = int(x.resolution.split('x')[0])\n", 1723 | " keyftype = x.extension\n", 1724 | " st = (keyftype, keyres)\n", 1725 | " return st\n", 1726 | " \n", 1727 | " self._best = max(streams, key=_sortkey)\n", 1728 | " return self._best\n", 1729 | "\n", 1730 | " def getbest(self):\n", 1731 | " return self._getbest()\n", 1732 | "\n", 1733 | " def dump(self, filepath, unsafe=False):\n", 1734 | " retVal = {}\n", 1735 | " data = '''\n", 1736 | " \n", 1737 | " \n", 1738 | " \n", 1739 | " %s\n", 1740 | " \n", 1741 | " \n", 1742 | "
\n", 1743 | "
\n", 1744 | "
\n", 1745 | "

%s

\n", 1746 | "
\n", 1747 | "
\n", 1748 | "
\n", 1749 | " \n", 1750 | " \n", 1751 | " \n", 1752 | " ''' % (self.title, self.html)\n", 1753 | " html = data.encode('utf-8').strip()\n", 1754 | " if not unsafe:\n", 1755 | " filename = \"%s\\\\%s\" % (filepath, self.title) if os.name == 'nt' else \"%s/%s\" % (filepath, self.title)\n", 1756 | " filename += \".html\"\n", 1757 | " if unsafe:\n", 1758 | " filename = u\"%s\\\\%s\" % (filepath, self.unsafe_title) if os.name == 'nt' else u\"%s/%s\" % (filepath, self.unsafe_title)\n", 1759 | " filename += \".html\"\n", 1760 | "\n", 1761 | " if os.path.isfile(filename):\n", 1762 | " retVal = {\"status\" : \"True\", \"msg\" : \"already downloaded\"}\n", 1763 | " return retVal\n", 1764 | " \n", 1765 | " try:\n", 1766 | " f = codecs.open(filename, 'wb', errors='ignore')\n", 1767 | " f.write(html)\n", 1768 | " except (OSError, Exception, UnicodeDecodeError) as e:\n", 1769 | " retVal = {'status' : 'False', 'msg' : '{}'.format(e)}\n", 1770 | " else:\n", 1771 | " retVal = {'status' : 'True', 'msg' : 'download'}\n", 1772 | " f.close()\n", 1773 | "\n", 1774 | " return retVal\n", 1775 | "\n", 1776 | "class UdemyLectureStream(Downloader):\n", 1777 | "\n", 1778 | "\n", 1779 | " def __init__(self, parent):\n", 1780 | "\n", 1781 | " self._mediatype = None\n", 1782 | " self._quality = None\n", 1783 | " self._resolution = None\n", 1784 | " self._dimention = None\n", 1785 | " self._extension = None\n", 1786 | " self._url = None\n", 1787 | "\n", 1788 | " self._parent = parent\n", 1789 | " self._filename = None\n", 1790 | " self._fsize = None\n", 1791 | " self._active = False\n", 1792 | "\n", 1793 | " Downloader.__init__(self)\n", 1794 | "\n", 1795 | " def __repr__(self):\n", 1796 | " out = \"%s:%s@%s\" % (self.mediatype, self.extension, self.quality)\n", 1797 | " return out\n", 1798 | "\n", 1799 | " def _generate_filename(self):\n", 1800 | " ok = re.compile(r'[^\\\\/:*?\"<>|]')\n", 1801 | " filename = \"\".join(x if ok.match(x) else \"_\" for x in self.title)\n", 1802 | " filename += \".\" + self.extension\n", 1803 | " return filename\n", 1804 | "\n", 1805 | " def _generate_unsafe_filename(self):\n", 1806 | " ok = re.compile(r'[^\\\\/:*?\"<>|]')\n", 1807 | " filename = \"\".join(x if ok.match(x) else \"_\" for x in self.unsafe_title)\n", 1808 | " filename += \".\" + self.extension\n", 1809 | " return filename\n", 1810 | "\n", 1811 | " @property\n", 1812 | " def resolution(self):\n", 1813 | " return self._resolution\n", 1814 | "\n", 1815 | " @property\n", 1816 | " def quality(self):\n", 1817 | " return self._quality\n", 1818 | "\n", 1819 | " @property\n", 1820 | " def url(self):\n", 1821 | " return self._url\n", 1822 | "\n", 1823 | " @property\n", 1824 | " def id(self):\n", 1825 | " return self._parent.id\n", 1826 | "\n", 1827 | " @property\n", 1828 | " def dimention(self):\n", 1829 | " return self._dimention\n", 1830 | "\n", 1831 | " @property\n", 1832 | " def extension(self):\n", 1833 | " return self._extension\n", 1834 | "\n", 1835 | " @property\n", 1836 | " def filename(self):\n", 1837 | " if not self._filename:\n", 1838 | " self._filename = self._generate_filename()\n", 1839 | " return self._filename\n", 1840 | "\n", 1841 | " @property\n", 1842 | " def title(self):\n", 1843 | " return self._parent.title\n", 1844 | "\n", 1845 | " @property\n", 1846 | " def unsafe_title(self):\n", 1847 | " return self._parent.unsafe_title\n", 1848 | "\n", 1849 | " @property\n", 1850 | " def unsafe_filename(self):\n", 1851 | " if not self._filename:\n", 1852 | " self._filename = self._generate_unsafe_filename()\n", 1853 | " return self._filename\n", 1854 | "\n", 1855 | " @property\n", 1856 | " def mediatype(self):\n", 1857 | " return self._mediatype\n", 1858 | "\n", 1859 | " def get_filesize(self):\n", 1860 | " if not self._fsize:\n", 1861 | " headers = {'User-Agent': HEADERS.get('User-Agent')}\n", 1862 | " try:\n", 1863 | " with requests.get(self.url, stream=True, headers=headers) as resp:\n", 1864 | " if resp.ok:\n", 1865 | " self._fsize = float(resp.headers.get('Content-Length', 0))\n", 1866 | " if not resp.ok:\n", 1867 | " self._fsize = 0\n", 1868 | " except conn_error:\n", 1869 | " self._fsize = 0\n", 1870 | " return self._fsize\n", 1871 | "\n", 1872 | "class UdemyLectureAssets(Downloader):\n", 1873 | "\n", 1874 | " def __init__(self, parent):\n", 1875 | "\n", 1876 | " self._extension = None\n", 1877 | " self._mediatype = None\n", 1878 | " self._url = None\n", 1879 | "\n", 1880 | " self._parent = parent\n", 1881 | " self._filename = None\n", 1882 | " self._fsize = None\n", 1883 | " self._active = False\n", 1884 | "\n", 1885 | " Downloader.__init__(self)\n", 1886 | "\n", 1887 | " def __repr__(self):\n", 1888 | " out = \"%s:%s@%s\" % (self.mediatype, self.extension, self.extension)\n", 1889 | " return out\n", 1890 | "\n", 1891 | " def _generate_filename(self):\n", 1892 | " ok = re.compile(r'[^\\\\/:*?\"<>|]')\n", 1893 | " filename = \"\".join(x if ok.match(x) else \"_\" for x in self.title)\n", 1894 | " filename += \".{}\".format(self.extension)\n", 1895 | " return filename\n", 1896 | "\n", 1897 | " def _generate_unsafe_filename(self):\n", 1898 | " ok = re.compile(r'[^\\\\/:*?\"<>|]')\n", 1899 | " filename = \"\".join(x if ok.match(x) else \"_\" for x in self.unsafe_title)\n", 1900 | " filename += \".{}\".format(self.extension)\n", 1901 | " return filename\n", 1902 | "\n", 1903 | " @property\n", 1904 | " def id(self):\n", 1905 | " return self._parent.id\n", 1906 | "\n", 1907 | " @property\n", 1908 | " def url(self):\n", 1909 | " return self._url\n", 1910 | "\n", 1911 | " @property\n", 1912 | " def extension(self):\n", 1913 | " return self._extension\n", 1914 | "\n", 1915 | " @property\n", 1916 | " def title(self):\n", 1917 | " return self._parent.title\n", 1918 | "\n", 1919 | " @property\n", 1920 | " def unsafe_title(self):\n", 1921 | " return self._parent.unsafe_title\n", 1922 | "\n", 1923 | " @property\n", 1924 | " def filename(self):\n", 1925 | " if not self._filename:\n", 1926 | " self._filename = self._generate_filename()\n", 1927 | " return self._filename\n", 1928 | "\n", 1929 | " @property\n", 1930 | " def unsafe_filename(self):\n", 1931 | " if not self._filename:\n", 1932 | " self._filename = self._generate_unsafe_filename()\n", 1933 | " return self._filename\n", 1934 | "\n", 1935 | " @property\n", 1936 | " def mediatype(self):\n", 1937 | " return self._mediatype\n", 1938 | "\n", 1939 | " def get_filesize(self):\n", 1940 | " if not self._fsize:\n", 1941 | " headers = {'User-Agent': HEADERS.get('User-Agent')}\n", 1942 | " try:\n", 1943 | " with requests.get(self.url, stream=True, headers=headers) as resp:\n", 1944 | " if resp.ok:\n", 1945 | " self._fsize = float(resp.headers.get('Content-Length', 0))\n", 1946 | " if not resp.ok:\n", 1947 | " self._fsize = 0\n", 1948 | " except conn_error:\n", 1949 | " self._fsize = 0\n", 1950 | " return self._fsize\n", 1951 | "\n", 1952 | "class UdemyLectureSubtitles(Downloader):\n", 1953 | "\n", 1954 | " def __init__(self, parent):\n", 1955 | "\n", 1956 | " self._mediatype = None\n", 1957 | " self._extension = None\n", 1958 | " self._language = None\n", 1959 | " self._url = None\n", 1960 | "\n", 1961 | " self._parent = parent\n", 1962 | " self._filename = None\n", 1963 | " self._fsize = None\n", 1964 | " self._active = False\n", 1965 | "\n", 1966 | " Downloader.__init__(self)\n", 1967 | "\n", 1968 | " def __repr__(self):\n", 1969 | " out = \"%s:%s@%s\" % (self.mediatype, self.language, self.extension)\n", 1970 | " return out\n", 1971 | "\n", 1972 | " def _generate_filename(self):\n", 1973 | " ok = re.compile(r'[^\\\\/:*?\"<>|]')\n", 1974 | " filename = \"\".join(x if ok.match(x) else \"_\" for x in self.title)\n", 1975 | " filename += \".{}.{}\".format(self.language, self.extension)\n", 1976 | " return filename\n", 1977 | "\n", 1978 | " def _generate_unsafe_filename(self):\n", 1979 | " ok = re.compile(r'[^\\\\/:*?\"<>|]')\n", 1980 | " filename = \"\".join(x if ok.match(x) else \"_\" for x in self.unsafe_title)\n", 1981 | " filename += \".{}.{}\".format(self.language, self.extension)\n", 1982 | " return filename\n", 1983 | "\n", 1984 | " @property\n", 1985 | " def id(self):\n", 1986 | " return self._parent.id\n", 1987 | " \n", 1988 | " @property\n", 1989 | " def url(self):\n", 1990 | " return self._url\n", 1991 | "\n", 1992 | " @property\n", 1993 | " def extension(self):\n", 1994 | " return self._extension\n", 1995 | "\n", 1996 | " @property\n", 1997 | " def language(self):\n", 1998 | " return self._language\n", 1999 | "\n", 2000 | " @property\n", 2001 | " def title(self):\n", 2002 | " return self._parent.title\n", 2003 | "\n", 2004 | " @property\n", 2005 | " def unsafe_title(self):\n", 2006 | " return self._parent.unsafe_title\n", 2007 | "\n", 2008 | " @property\n", 2009 | " def filename(self):\n", 2010 | " if not self._filename:\n", 2011 | " self._filename = self._generate_filename()\n", 2012 | " return self._filename\n", 2013 | "\n", 2014 | " @property\n", 2015 | " def unsafe_filename(self):\n", 2016 | " if not self._filename:\n", 2017 | " self._filename = self._generate_unsafe_filename()\n", 2018 | " return self._filename\n", 2019 | "\n", 2020 | " @property\n", 2021 | " def mediatype(self):\n", 2022 | " return self._mediatype\n", 2023 | "\n", 2024 | " def get_filesize(self):\n", 2025 | " if not self._fsize:\n", 2026 | " headers = {'User-Agent': HEADERS.get('User-Agent')}\n", 2027 | " try:\n", 2028 | " with requests.get(self.url, stream=True, headers=headers) as resp:\n", 2029 | " if resp.ok:\n", 2030 | " self._fsize = float(resp.headers.get('Content-Length', 0))\n", 2031 | " if not resp.ok:\n", 2032 | " self._fsize = 0\n", 2033 | " except conn_error:\n", 2034 | " self._fsize = 0\n", 2035 | " return self._fsize\n", 2036 | "\n", 2037 | "\n", 2038 | "# _internal.py\n", 2039 | "# -------------------------------------------------------------------------------------\n", 2040 | "class InternUdemyCourse(UdemyCourse, Udemy):\n", 2041 | " def __init__(self, *args, **kwargs):\n", 2042 | " self._info = ''\n", 2043 | " super(InternUdemyCourse, self).__init__(*args, **kwargs)\n", 2044 | "\n", 2045 | " def _fetch_course(self):\n", 2046 | " if self._have_basic:\n", 2047 | " return\n", 2048 | " if not self._cookies:\n", 2049 | " auth = self._login(username=self._username, password=self._password)\n", 2050 | " if self._cookies:\n", 2051 | " auth = self._login(cookies=self._cookies)\n", 2052 | " if auth.get('login') == 'successful':\n", 2053 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sb + \"Logged in successfully.\\n\")\n", 2054 | " sys.stdout.write('\\r' + fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Downloading course information .. \\r\")\n", 2055 | " self._info = self._real_extract(self._url)\n", 2056 | " time.sleep(1)\n", 2057 | " sys.stdout.write('\\r' + fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Downloaded course information .. (done)\\r\\n\")\n", 2058 | " self._id = self._info['course_id']\n", 2059 | " self._title = self._info['course_title']\n", 2060 | " self._chapters_count = self._info['total_chapters']\n", 2061 | " self._total_lectures = self._info['total_lectures']\n", 2062 | " self._chapters = [InternUdemyChapter(z) for z in self._info['chapters']]\n", 2063 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to logout now...\\n\")\n", 2064 | " if not self._cookies:\n", 2065 | " self._logout()\n", 2066 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sb + \"Logged out successfully.\\n\")\n", 2067 | " self._have_basic = True\n", 2068 | " if auth.get('login') == 'failed':\n", 2069 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Failed to login ..\\n\")\n", 2070 | " sys.exit(0)\n", 2071 | "\n", 2072 | "class InternUdemyChapter(UdemyChapters):\n", 2073 | " \n", 2074 | " def __init__(self, chapter):\n", 2075 | " super(InternUdemyChapter, self).__init__()\n", 2076 | "\n", 2077 | " self._chapter_id = chapter['chapter_id']\n", 2078 | " self._chapter_title = chapter['chapter_title']\n", 2079 | " self._unsafe_title = chapter['unsafe_chapter']\n", 2080 | " self._chapter_index = chapter['chapter_index']\n", 2081 | " self._lectures_count = chapter.get('lectures_count', 0)\n", 2082 | " self._lectures = [InternUdemyLecture(z) for z in chapter['lectures']] if self._lectures_count > 0 else []\n", 2083 | "\n", 2084 | "class InternUdemyLecture(UdemyLectures):\n", 2085 | "\n", 2086 | " def __init__(self, lectures):\n", 2087 | " super(InternUdemyLecture, self).__init__()\n", 2088 | " self._info = lectures\n", 2089 | "\n", 2090 | " self._lecture_id = self._info['lectures_id']\n", 2091 | " self._lecture_title = self._info['lecture_title']\n", 2092 | " self._unsafe_title = self._info['unsafe_lecture']\n", 2093 | " self._lecture_index = self._info['lecture_index']\n", 2094 | " \n", 2095 | " self._subtitles_count = self._info.get('subtitle_count', 0)\n", 2096 | " self._sources_count = self._info.get('sources_count', 0)\n", 2097 | " self._assets_count = self._info.get('assets_count', 0)\n", 2098 | " self._extension = self._info.get('extension')\n", 2099 | " self._html_content = self._info.get('html_content')\n", 2100 | " self._duration = self._info.get('duration')\n", 2101 | " if self._duration:\n", 2102 | " duration = int(self._duration)\n", 2103 | " (mins, secs) = divmod(duration, 60)\n", 2104 | " (hours, mins) = divmod(mins, 60)\n", 2105 | " if hours == 0:\n", 2106 | " self._duration = \"%02d:%02d\" % (mins, secs)\n", 2107 | " else:\n", 2108 | " self._duration = \"%02d:%02d:%02d\" % (hours, mins, secs)\n", 2109 | "\n", 2110 | "\n", 2111 | " def _process_streams(self):\n", 2112 | " streams = [InternUdemyLectureStream(z, self) for z in self._info['sources']] if self._sources_count > 0 else []\n", 2113 | " self._streams = streams\n", 2114 | "\n", 2115 | " def _process_assets(self):\n", 2116 | " assets = [InternUdemyLectureAssets(z, self) for z in self._info['assets']] if self._assets_count > 0 else []\n", 2117 | " self._assets = assets\n", 2118 | "\n", 2119 | " def _process_subtitles(self):\n", 2120 | " subtitles = [InternUdemyLectureSubtitles(z, self) for z in self._info['subtitles']] if self._subtitles_count > 0 else []\n", 2121 | " self._subtitles = subtitles\n", 2122 | " \n", 2123 | "class InternUdemyLectureStream(UdemyLectureStream):\n", 2124 | "\n", 2125 | " def __init__(self, sources, parent):\n", 2126 | " super(InternUdemyLectureStream, self).__init__(parent)\n", 2127 | "\n", 2128 | " self._mediatype = sources.get('type')\n", 2129 | " self._extension = sources.get('extension')\n", 2130 | " height = sources.get('height', 0)\n", 2131 | " width = sources.get('width', 0)\n", 2132 | " self._resolution = '%sx%s' % (width, height)\n", 2133 | " self._dimention = width, height\n", 2134 | " self._quality = self._resolution\n", 2135 | " self._url = sources.get('download_url')\n", 2136 | "\n", 2137 | "class InternUdemyLectureAssets(UdemyLectureAssets):\n", 2138 | "\n", 2139 | " def __init__(self, assets, parent):\n", 2140 | " super(InternUdemyLectureAssets, self).__init__(parent)\n", 2141 | "\n", 2142 | " self._mediatype = assets.get('type')\n", 2143 | " self._extension = assets.get('extension')\n", 2144 | " self._filename = '{0:03d} {1!s}'.format(parent._lecture_index, assets.get('filename'))\n", 2145 | " self._url = assets.get('download_url')\n", 2146 | "\n", 2147 | "class InternUdemyLectureSubtitles(UdemyLectureSubtitles):\n", 2148 | "\n", 2149 | " def __init__(self, subtitles, parent):\n", 2150 | " super(InternUdemyLectureSubtitles, self).__init__(parent)\n", 2151 | "\n", 2152 | " self._mediatype = subtitles.get('type')\n", 2153 | " self._extension = subtitles.get('extension')\n", 2154 | " self._language = subtitles.get('language')\n", 2155 | " self._url = subtitles.get('download_url')\n", 2156 | "\n", 2157 | "\n", 2158 | "# _vtt2srt.py\n", 2159 | "# -------------------------------------------------------------------------------------\n", 2160 | "class WebVtt2Srt(object):\n", 2161 | "\n", 2162 | " _TIMECODE_REGEX = r'(?i)(?P(?:(?:\\d{1,2}:)){1,2}\\d{2}[\\.,]\\d+)'\n", 2163 | " _TIMECODE = r'(?i)(?P(?:(?:\\d{1,2}:)){1,2}\\d{2}[\\.,]\\d+)\\s*-->\\s*(?i)(?P(?:(?:\\d{1,2}:)){1,2}\\d{2}[\\.,]\\d+)'\n", 2164 | "\n", 2165 | " def _vttcontents(self, fname):\n", 2166 | " try:\n", 2167 | " f = codecs.open(filename=fname, encoding='utf-8', errors='ignore')\n", 2168 | " except Exception as e:\n", 2169 | " return {'status' : 'False', 'msg' : 'failed to open file : file not found ..'}\n", 2170 | " content = [line for line in (l.strip() for l in f)]\n", 2171 | " f.close()\n", 2172 | " return content\n", 2173 | " \n", 2174 | "\n", 2175 | " def _write_srtcontent(self, fname, content):\n", 2176 | " with codecs.open(filename=fname, mode='a', encoding='utf-8') as fd:\n", 2177 | " fd.write(content)\n", 2178 | " fd.close()\n", 2179 | "\n", 2180 | " def _locate_timecode(self, content):\n", 2181 | " for (loc, line) in enumerate(content):\n", 2182 | " match = re.match(self._TIMECODE_REGEX, line, flags=re.U)\n", 2183 | " if match:\n", 2184 | " return {'status' : True, 'location' : loc}\n", 2185 | " return {'status' : False, 'location' : loc}\n", 2186 | "\n", 2187 | " def _is_timecode(self, timecode):\n", 2188 | " match = re.match(self._TIMECODE_REGEX, timecode, flags=re.U)\n", 2189 | " if match:\n", 2190 | " return True\n", 2191 | " return False\n", 2192 | "\n", 2193 | " def _fix_timecode(self, timecode):\n", 2194 | " _sdata = len(timecode.split(',')[0])\n", 2195 | " if _sdata == 5:\n", 2196 | " timecode = u'00:{code}'.format(code=timecode)\n", 2197 | " if _sdata == 7:\n", 2198 | " timecode = u'0{code}'.format(code=timecode)\n", 2199 | " return timecode\n", 2200 | "\n", 2201 | " def _generate_timecode(self, sequence, timecode):\n", 2202 | " match = re.match(self._TIMECODE, timecode, flags=re.U)\n", 2203 | " if match:\n", 2204 | " start, end = self._fix_timecode(timecode=re.sub(r'[\\.,]', ',', match.group('appeartime'))), self._fix_timecode(timecode=re.sub(r'[\\.,]', ',', match.group('disappertime')))\n", 2205 | " return u'{seq}\\r\\n{appeartime} --> {disappertime}\\r\\n'.format(seq=sequence, appeartime=start, disappertime=end)\n", 2206 | " return u''\n", 2207 | "\n", 2208 | " def convert(self, filename=None, remove_vtt=True):\n", 2209 | " if filename:\n", 2210 | " seq = 1\n", 2211 | " fname = filename.replace('.vtt', '.srt')\n", 2212 | " content = self._vttcontents(fname=filename)\n", 2213 | " if content and isinstance(content, list):\n", 2214 | " timecode_loc = self._locate_timecode(content)\n", 2215 | " if not timecode_loc.get('status'):\n", 2216 | " return {'status' : 'False', 'msg' : 'subtitle file seems to have malfunction skipping conversion ..'}\n", 2217 | " for line in content[timecode_loc.get('location'):]:\n", 2218 | " flag = self._is_timecode(timecode=line)\n", 2219 | " if flag:\n", 2220 | " timecode = self._generate_timecode(seq, unescapeHTML(line))\n", 2221 | " self._write_srtcontent(fname, timecode)\n", 2222 | " seq += 1\n", 2223 | " if not flag:\n", 2224 | " match = re.match('^([0-9]{1,3})$', line, flags=re.U)\n", 2225 | " if not match:\n", 2226 | " data = u'{content}\\r\\n'.format(content=line)\n", 2227 | " self._write_srtcontent(fname, data)\n", 2228 | " else:\n", 2229 | " return content\n", 2230 | " \n", 2231 | " if remove_vtt:\n", 2232 | " try:\n", 2233 | " os.unlink(filename)\n", 2234 | " except Exception as e:\n", 2235 | " pass\n", 2236 | " return {'status' : 'True', 'msg' : 'successfully generated subtitle in srt ...'}\n", 2237 | "\n", 2238 | "\n", 2239 | "# udemy-dl.py\n", 2240 | "# -------------------------------------------------------------------------------------\n", 2241 | "getpass = GetPass()\n", 2242 | "\n", 2243 | "def get_course(url, username='', password='', cookies='', basic=True, callback=None):\n", 2244 | " \"\"\"Returns udemy course instance.\n", 2245 | "\n", 2246 | " @params:\n", 2247 | " url : Udemy course url required : type (string).\n", 2248 | " username : Udemy email account required : type (string).\n", 2249 | " password : Udemy account password required : type (string)\n", 2250 | " cookies : Udemy account logged in browser cookies optional : type (string)\n", 2251 | " \"\"\"\n", 2252 | " return InternUdemyCourse(url, username, password, cookies, basic, callback)\n", 2253 | "\n", 2254 | "class Udemy(WebVtt2Srt, ProgressBar):\n", 2255 | "\n", 2256 | " def __init__(self, url, username='', password='', cookies=''):\n", 2257 | " self.url = url\n", 2258 | " self.username = username\n", 2259 | " self.password = password\n", 2260 | " self.cookies = cookies\n", 2261 | " super(Udemy, self).__init__()\n", 2262 | "\n", 2263 | " def _write_to_file(self, filepath='', lecture='', names_only=False, unsafe=False):\n", 2264 | " retVal = {}\n", 2265 | " filename = filepath\n", 2266 | " filename += '-names-only.txt' if names_only else \".txt\"\n", 2267 | " fmode = \"a\"\n", 2268 | " if not unsafe:\n", 2269 | " title = lecture.title\n", 2270 | " url = lecture.url\n", 2271 | " url_or_name = url if not names_only else title\n", 2272 | " url_or_name += \"n\"\n", 2273 | " if unsafe:\n", 2274 | " title = u'%s' % (lecture.unsafe_title)\n", 2275 | " url = lecture.url\n", 2276 | " url_or_name = url if not names_only else title\n", 2277 | " url_or_name += \"n\"\n", 2278 | "\n", 2279 | " try:\n", 2280 | " f = codecs.open(filename, fmode, encoding='utf-8', errors='ignore')\n", 2281 | " f.write(url_or_name)\n", 2282 | " except (OSError, Exception, UnicodeDecodeError) as e:\n", 2283 | " retVal = {'status': 'False', 'msg': '{}'.format(e)}\n", 2284 | " else:\n", 2285 | " retVal = {'status': 'True', 'msg': 'download'}\n", 2286 | " f.close()\n", 2287 | "\n", 2288 | " return retVal\n", 2289 | "\n", 2290 | " def course_save(self, path='', quality='', caption_only=False, skip_captions=False, names_only=False, unsafe=False):\n", 2291 | " if not self.cookies:\n", 2292 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to login as \" + fm + sb + \"(%s)\" % (\n", 2293 | " self.username) + fg + sb + \"...n\")\n", 2294 | " if self.cookies:\n", 2295 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to login using cookies ...n\")\n", 2296 | " course = get_course(url=self.url, username=self.username, password=self.password, cookies=self.cookies)\n", 2297 | " course_id = course.id\n", 2298 | " course_name = course.title\n", 2299 | " total_lectures = course.lectures\n", 2300 | " total_chapters = course.chapters\n", 2301 | " course_name = (course_name.lower()).replace(' ', '-')\n", 2302 | " chapters = course.get_chapters()\n", 2303 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Course \" + fb + sb + \"'%s'.n\" % (course_name))\n", 2304 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sd + \"Chapter(s) (%s).n\" % (total_chapters))\n", 2305 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (total_lectures))\n", 2306 | " if path:\n", 2307 | " if '~' in path:\n", 2308 | " path = os.path.expanduser(path)\n", 2309 | " course_path = \"%s%s\" % (path, course_name) if os.name == 'nt' else \"%s/%s\" % (path, course_name)\n", 2310 | " else:\n", 2311 | " path = os.getcwd()\n", 2312 | " course_path = \"%s%s\" % (path, course_name) if os.name == 'nt' else \"%s/%s\" % (path, course_name)\n", 2313 | " filepath = '%s.txt' % (course_path)\n", 2314 | " if os.path.isfile(filepath):\n", 2315 | " with open(filepath, 'w') as f:\n", 2316 | " f.close()\n", 2317 | " if not names_only:\n", 2318 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Writing course content(s) to '%s.txt'n\" % (course_name))\n", 2319 | " if names_only:\n", 2320 | " sys.stdout.write(\n", 2321 | " fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Writing course lecture names to '%s-names-only.txt'n\" % (course_name))\n", 2322 | " for chapter in chapters:\n", 2323 | " chapter_id = chapter.id\n", 2324 | " chapter_title = chapter.title\n", 2325 | " lectures = chapter.get_lectures()\n", 2326 | " lectures_count = chapter.lectures\n", 2327 | " for lecture in lectures:\n", 2328 | " lecture_id = lecture.id\n", 2329 | " lecture_streams = lecture.streams\n", 2330 | " lecture_best = lecture.getbest()\n", 2331 | " lecture_assets = lecture.assets\n", 2332 | " lecture_subtitles = lecture.subtitles\n", 2333 | " if quality:\n", 2334 | " index = 0\n", 2335 | " while index < len(lecture_streams):\n", 2336 | " dimension = int(lecture_streams[index].dimention[1])\n", 2337 | " if dimension == quality:\n", 2338 | " lecture_best = lecture_streams[index]\n", 2339 | " break\n", 2340 | " index += 1\n", 2341 | " if not lecture_best:\n", 2342 | " lecture_best = lecture_best\n", 2343 | " if caption_only and not skip_captions:\n", 2344 | " if lecture_subtitles:\n", 2345 | " for subtitle in lecture_subtitles:\n", 2346 | " self._write_to_file(filepath=course_path, lecture=subtitle, names_only=names_only, unsafe=unsafe)\n", 2347 | " if lecture_assets:\n", 2348 | " for asset in lecture_assets:\n", 2349 | " self._write_to_file(filepath=course_path, lecture=asset, names_only=names_only, unsafe=unsafe)\n", 2350 | " elif skip_captions and not caption_only:\n", 2351 | " if lecture_best:\n", 2352 | " self._write_to_file(filepath=course_path, lecture=lecture_best, names_only=names_only, unsafe=unsafe)\n", 2353 | " if lecture_assets:\n", 2354 | " for asset in lecture_assets:\n", 2355 | " self._write_to_file(filepath=course_path, lecture=asset, names_only=names_only, unsafe=unsafe)\n", 2356 | " else:\n", 2357 | " if lecture_best:\n", 2358 | " self._write_to_file(filepath=course_path, lecture=lecture_best, names_only=names_only, unsafe=unsafe)\n", 2359 | " if lecture_assets:\n", 2360 | " for asset in lecture_assets:\n", 2361 | " self._write_to_file(filepath=course_path, lecture=asset, names_only=names_only, unsafe=unsafe)\n", 2362 | " if lecture_subtitles:\n", 2363 | " for subtitle in lecture_subtitles:\n", 2364 | " self._write_to_file(filepath=course_path, lecture=subtitle, names_only=names_only, unsafe=unsafe)\n", 2365 | " if not names_only:\n", 2366 | " sys.stdout.write(\n", 2367 | " fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Written successfully under '{name}.txt'.n\".format(name=course_path))\n", 2368 | " if names_only:\n", 2369 | " sys.stdout.write(\n", 2370 | " fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Written successfully under '{name}-names-only.txt'.n\".format(\n", 2371 | " name=course_path))\n", 2372 | "\n", 2373 | " def course_list_down(self, chapter_number='', lecture_number='', unsafe=False):\n", 2374 | " if not self.cookies:\n", 2375 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to login as \" + fm + sb + \"(%s)\" % (\n", 2376 | " self.username) + fg + sb + \"...n\")\n", 2377 | " if self.cookies:\n", 2378 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to login using cookies ...n\")\n", 2379 | " course = udemy.course(url=self.url, username=self.username, password=self.password, cookies=self.cookies)\n", 2380 | " course_id = course.id\n", 2381 | " course_name = course.title\n", 2382 | " total_lectures = course.lectures\n", 2383 | " total_chapters = course.chapters\n", 2384 | " chapters = course.get_chapters()\n", 2385 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Course \" + fb + sb + \"'%s'.n\" % (course_name))\n", 2386 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sd + \"Chapter(s) (%s).n\" % (total_chapters))\n", 2387 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (total_lectures))\n", 2388 | " if chapter_number and chapter_number > 0 and chapter_number <= total_chapters:\n", 2389 | " chapter = chapters[chapter_number - 1]\n", 2390 | " chapter_id = chapter.id\n", 2391 | " chapter_title = chapter.title if not unsafe else \"%02d\" % (int(chapter.index))\n", 2392 | " lectures = chapter.get_lectures()\n", 2393 | " lectures_count = chapter.lectures\n", 2394 | " sys.stdout.write('n' + fc + sd + \"[\" + fw + sb + \"+\" + fc + sd + \"] : \" + fw + sd + \"Chapter (%s-%s)n\" % (chapter_title, chapter_id))\n", 2395 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (lectures_count))\n", 2396 | " if lecture_number and lecture_number > 0 and lecture_number <= lectures_count:\n", 2397 | " lecture = lectures[lecture_number - 1]\n", 2398 | " lecture_id = lecture.id\n", 2399 | " lecture_streams = lecture.streams\n", 2400 | " lecture_best = lecture.getbest()\n", 2401 | " lecture_assets = lecture.assets\n", 2402 | " lecture_subtitles = lecture.subtitles\n", 2403 | " if lecture_streams:\n", 2404 | " sys.stdout.write(fc + sd + \" - \" + fy + sb + \"duration : \" + fm + sb + str(lecture.duration) + fy + sb + \".n\")\n", 2405 | " sys.stdout.write(fc + sd + \" - \" + fy + sb + \"Lecture id : \" + fm + sb + str(lecture_id) + fy + sb + \".n\")\n", 2406 | " for stream in lecture_streams:\n", 2407 | " content_length = stream.get_filesize()\n", 2408 | " if content_length != 0:\n", 2409 | " if content_length <= 1048576.00:\n", 2410 | " size = round(float(content_length) / 1024.00, 2)\n", 2411 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2412 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2413 | " else:\n", 2414 | " size = round(float(content_length) / 1048576, 2)\n", 2415 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2416 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2417 | " if lecture_best.dimention[1] == stream.dimention[1]:\n", 2418 | " in_MB = in_MB + fc + sb + \"(Best)\" + fg + sd\n", 2419 | " sys.stdout.write(\n", 2420 | " 't- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(stream), stream.dimention[1] + 'p', sz, in_MB, fy, sb))\n", 2421 | " if lecture_assets:\n", 2422 | " for asset in lecture_assets:\n", 2423 | " if asset.mediatype != 'external_link':\n", 2424 | " content_length = asset.get_filesize()\n", 2425 | " if content_length != 0:\n", 2426 | " if content_length <= 1048576.00:\n", 2427 | " size = round(float(content_length) / 1024.00, 2)\n", 2428 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2429 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2430 | " else:\n", 2431 | " size = round(float(content_length) / 1048576, 2)\n", 2432 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2433 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2434 | " sys.stdout.write('t- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(asset), asset.extension, sz, in_MB, fy, sb))\n", 2435 | " if lecture_subtitles:\n", 2436 | " for subtitle in lecture_subtitles:\n", 2437 | " content_length = subtitle.get_filesize()\n", 2438 | " if content_length != 0:\n", 2439 | " if content_length <= 1048576.00:\n", 2440 | " size = round(float(content_length) / 1024.00, 2)\n", 2441 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2442 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2443 | " else:\n", 2444 | " size = round(float(content_length) / 1048576, 2)\n", 2445 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2446 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2447 | " sys.stdout.write('t- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(subtitle), subtitle.extension, sz, in_MB, fy, sb))\n", 2448 | " else:\n", 2449 | " for lecture in lectures:\n", 2450 | " lecture_id = lecture.id\n", 2451 | " lecture_streams = lecture.streams\n", 2452 | " lecture_best = lecture.getbest()\n", 2453 | " lecture_assets = lecture.assets\n", 2454 | " lecture_subtitles = lecture.subtitles\n", 2455 | " if lecture_streams:\n", 2456 | " sys.stdout.write(fc + sd + \" - \" + fy + sb + \"duration : \" + fm + sb + str(lecture.duration) + fy + sb + \".n\")\n", 2457 | " sys.stdout.write(fc + sd + \" - \" + fy + sb + \"Lecture id : \" + fm + sb + str(lecture_id) + fy + sb + \".n\")\n", 2458 | " for stream in lecture_streams:\n", 2459 | " content_length = stream.get_filesize()\n", 2460 | " if content_length != 0:\n", 2461 | " if content_length <= 1048576.00:\n", 2462 | " size = round(float(content_length) / 1024.00, 2)\n", 2463 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2464 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2465 | " else:\n", 2466 | " size = round(float(content_length) / 1048576, 2)\n", 2467 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2468 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2469 | " if lecture_best.dimention[1] == stream.dimention[1]:\n", 2470 | " in_MB = in_MB + fc + sb + \"(Best)\" + fg + sd\n", 2471 | " sys.stdout.write(\n", 2472 | " 't- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(stream), stream.dimention[1] + 'p', sz, in_MB, fy, sb))\n", 2473 | " if lecture_assets:\n", 2474 | " for asset in lecture_assets:\n", 2475 | " if asset.mediatype != 'external_link':\n", 2476 | " content_length = asset.get_filesize()\n", 2477 | " if content_length != 0:\n", 2478 | " if content_length <= 1048576.00:\n", 2479 | " size = round(float(content_length) / 1024.00, 2)\n", 2480 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2481 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2482 | " else:\n", 2483 | " size = round(float(content_length) / 1048576, 2)\n", 2484 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2485 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2486 | " sys.stdout.write('t- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(asset), asset.extension, sz, in_MB, fy, sb))\n", 2487 | " if lecture_subtitles:\n", 2488 | " for subtitle in lecture_subtitles:\n", 2489 | " content_length = subtitle.get_filesize()\n", 2490 | " if content_length != 0:\n", 2491 | " if content_length <= 1048576.00:\n", 2492 | " size = round(float(content_length) / 1024.00, 2)\n", 2493 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2494 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2495 | " else:\n", 2496 | " size = round(float(content_length) / 1048576, 2)\n", 2497 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2498 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2499 | " sys.stdout.write(\n", 2500 | " 't- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(subtitle), subtitle.extension, sz, in_MB, fy, sb))\n", 2501 | " else:\n", 2502 | " for chapter in chapters:\n", 2503 | " chapter_id = chapter.id\n", 2504 | " chapter_title = chapter.title if not unsafe else \"%02d\" % (int(chapter.index))\n", 2505 | " lectures = chapter.get_lectures()\n", 2506 | " lectures_count = chapter.lectures\n", 2507 | " sys.stdout.write('n' + fc + sd + \"[\" + fw + sb + \"+\" + fc + sd + \"] : \" + fw + sd + \"Chapter (%s-%s)n\" % (chapter_title, chapter_id))\n", 2508 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (lectures_count))\n", 2509 | " for lecture in lectures:\n", 2510 | " lecture_id = lecture.id\n", 2511 | " lecture_streams = lecture.streams\n", 2512 | " lecture_best = lecture.getbest()\n", 2513 | " lecture_assets = lecture.assets\n", 2514 | " lecture_subtitles = lecture.subtitles\n", 2515 | " if lecture_streams:\n", 2516 | " sys.stdout.write(fc + sd + \" - \" + fy + sb + \"duration : \" + fm + sb + str(lecture.duration) + fy + sb + \".n\")\n", 2517 | " sys.stdout.write(fc + sd + \" - \" + fy + sb + \"Lecture id : \" + fm + sb + str(lecture_id) + fy + sb + \".n\")\n", 2518 | " for stream in lecture_streams:\n", 2519 | " content_length = stream.get_filesize()\n", 2520 | " if content_length != 0:\n", 2521 | " if content_length <= 1048576.00:\n", 2522 | " size = round(float(content_length) / 1024.00, 2)\n", 2523 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2524 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2525 | " else:\n", 2526 | " size = round(float(content_length) / 1048576, 2)\n", 2527 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2528 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2529 | " if lecture_best.dimention[1] == stream.dimention[1]:\n", 2530 | " in_MB = in_MB + fc + sb + \"(Best)\" + fg + sd\n", 2531 | " sys.stdout.write(\n", 2532 | " 't- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(stream), stream.dimention[1] + 'p', sz, in_MB, fy, sb))\n", 2533 | " if lecture_assets:\n", 2534 | " for asset in lecture_assets:\n", 2535 | " if asset.mediatype != 'external_link':\n", 2536 | " content_length = asset.get_filesize()\n", 2537 | " if content_length != 0:\n", 2538 | " if content_length <= 1048576.00:\n", 2539 | " size = round(float(content_length) / 1024.00, 2)\n", 2540 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2541 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2542 | " else:\n", 2543 | " size = round(float(content_length) / 1048576, 2)\n", 2544 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2545 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2546 | " sys.stdout.write('t- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(asset), asset.extension, sz, in_MB, fy, sb))\n", 2547 | " if lecture_subtitles:\n", 2548 | " for subtitle in lecture_subtitles:\n", 2549 | " content_length = subtitle.get_filesize()\n", 2550 | " if content_length != 0:\n", 2551 | " if content_length <= 1048576.00:\n", 2552 | " size = round(float(content_length) / 1024.00, 2)\n", 2553 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2554 | " in_MB = 'KB' if size < 1024.00 else 'MB'\n", 2555 | " else:\n", 2556 | " size = round(float(content_length) / 1048576, 2)\n", 2557 | " sz = format(size if size < 1024.00 else size / 1024.00, '.2f')\n", 2558 | " in_MB = \"MB \" if size < 1024.00 else 'GB '\n", 2559 | " sys.stdout.write(\n", 2560 | " 't- ' + fg + sd + \"{:<22} {:<8}{}{}{}{}n\".format(str(subtitle), subtitle.extension, sz, in_MB, fy, sb))\n", 2561 | "\n", 2562 | " def download_assets(self, lecture_assets='', filepath='', unsafe=False):\n", 2563 | " if lecture_assets:\n", 2564 | " for assets in lecture_assets:\n", 2565 | " title = assets.filename if not unsafe else \"%s\" % (assets)\n", 2566 | " mediatype = assets.mediatype\n", 2567 | " if mediatype == \"external_link\":\n", 2568 | " assets.download(filepath=filepath, unsafe=unsafe, quiet=True, callback=self.show_progress)\n", 2569 | " else:\n", 2570 | " sys.stdout.write(fc + sd + \"n[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Downloading asset(s)n\")\n", 2571 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Downloading (%s)n\" % (title))\n", 2572 | " try:\n", 2573 | " retval = assets.download(filepath=filepath, unsafe=unsafe, quiet=True, callback=self.show_progress)\n", 2574 | " except KeyboardInterrupt:\n", 2575 | " sys.stdout.write(fc + sd + \"n[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sd + \"User Interrupted..n\")\n", 2576 | " sys.exit(0)\n", 2577 | " else:\n", 2578 | " msg = retval.get('msg')\n", 2579 | " if msg == 'already downloaded':\n", 2580 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Asset : '%s' \" % (\n", 2581 | " title) + fy + sb + \"(already downloaded).n\")\n", 2582 | " elif msg == 'download':\n", 2583 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sd + \"Downloaded (%s)n\" % (title))\n", 2584 | " else:\n", 2585 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Asset : '%s' \" % (\n", 2586 | " title) + fc + sb + \"(download skipped).n\")\n", 2587 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sd + \"{}n\".format(msg))\n", 2588 | "\n", 2589 | " def download_subtitles(self, lecture_subtitles='', language='', filepath='', unsafe=False):\n", 2590 | " if language:\n", 2591 | " _lecture_subtitles = [i for i in lecture_subtitles if i.language == language]\n", 2592 | " if _lecture_subtitles:\n", 2593 | " lecture_subtitles = _lecture_subtitles\n", 2594 | " if lecture_subtitles:\n", 2595 | " for subtitles in lecture_subtitles:\n", 2596 | " title = subtitles.title + '-' + subtitles.language if not unsafe else \"%s\" % (subtitles)\n", 2597 | " if not unsafe:\n", 2598 | " filename = \"%s%s\" % (filepath, subtitles.filename) if os.name == 'nt' else \"%s/%s\" % (filepath, subtitles.filename)\n", 2599 | " if unsafe:\n", 2600 | " filename = u\"%s%s\" % (filepath, subtitles.unsafe_filename) if os.name == 'nt' else u\"%s/%s\" % (\n", 2601 | " filepath, subtitles.unsafe_filename)\n", 2602 | "\n", 2603 | " sys.stdout.write(fc + sd + \"n[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Downloading subtitle(s)n\")\n", 2604 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Downloading (%s)n\" % (title))\n", 2605 | "\n", 2606 | " try:\n", 2607 | " retval = subtitles.download(filepath=filepath, unsafe=unsafe, quiet=True, callback=self.show_progress)\n", 2608 | " except KeyboardInterrupt:\n", 2609 | " sys.stdout.write(fc + sd + \"n[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sd + \"User Interrupted..n\")\n", 2610 | " sys.exit(0)\n", 2611 | " else:\n", 2612 | " msg = retval.get('msg')\n", 2613 | " if msg == 'already downloaded':\n", 2614 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Subtitle : '%s' \" % (\n", 2615 | " title) + fy + sb + \"(already downloaded).n\")\n", 2616 | " self.convert(filename=filename)\n", 2617 | " elif msg == 'download':\n", 2618 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sd + \"Downloaded (%s)n\" % (title))\n", 2619 | " self.convert(filename=filename)\n", 2620 | " else:\n", 2621 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Subtitle : '%s' \" % (\n", 2622 | " title) + fc + sb + \"(download skipped).n\")\n", 2623 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sd + \"{}n\".format(msg))\n", 2624 | "\n", 2625 | " def download_lectures(self, lecture_best='', lecture_title='', inner_index='', lectures_count='', filepath='', unsafe=False):\n", 2626 | " if lecture_best:\n", 2627 | " sys.stdout.write(\n", 2628 | " fc + sd + \"n[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) : ({index} of {total})n\".format(index=inner_index,\n", 2629 | " total=lectures_count))\n", 2630 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Downloading (%s)n\" % (lecture_title))\n", 2631 | " try:\n", 2632 | " retval = lecture_best.download(filepath=filepath, unsafe=unsafe, quiet=True, callback=self.show_progress)\n", 2633 | " except KeyboardInterrupt:\n", 2634 | " sys.stdout.write(fc + sd + \"n[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sd + \"User Interrupted..n\")\n", 2635 | " sys.exit(0)\n", 2636 | " else:\n", 2637 | " msg = retval.get('msg')\n", 2638 | " if msg == 'already downloaded':\n", 2639 | " if not unsafe:\n", 2640 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture : '%s' \" % (\n", 2641 | " lecture_title) + fy + sb + \"(already downloaded).n\")\n", 2642 | " if unsafe:\n", 2643 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"'%s' \" % (\n", 2644 | " lecture_title) + fy + sb + \"(already downloaded).n\")\n", 2645 | " elif msg == 'download':\n", 2646 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sd + \"Downloaded (%s)n\" % (lecture_title))\n", 2647 | " else:\n", 2648 | " if not unsafe:\n", 2649 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture : '%s' \" % (\n", 2650 | " lecture_title) + fc + sb + \"(download skipped).n\")\n", 2651 | " if unsafe:\n", 2652 | " sys.stdout.write(\n", 2653 | " fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"'%s' \" % (lecture_title) + fc + sb + \"(download skipped).n\")\n", 2654 | " sys.stdout.write(fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sd + \"{}n\".format(msg))\n", 2655 | "\n", 2656 | " def download_captions_only(self, lecture_subtitles='', language='', lecture_assets='', filepath='', unsafe=False):\n", 2657 | " if lecture_subtitles:\n", 2658 | " self.download_subtitles(lecture_subtitles=lecture_subtitles, language=language, filepath=filepath, unsafe=unsafe)\n", 2659 | " if lecture_assets:\n", 2660 | " self.download_assets(lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2661 | "\n", 2662 | " def download_lectures_only(self, lecture_best='', lecture_title='', inner_index='', lectures_count='', lecture_assets='', filepath='',\n", 2663 | " unsafe=False):\n", 2664 | " if lecture_best:\n", 2665 | " self.download_lectures(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=inner_index, lectures_count=lectures_count,\n", 2666 | " filepath=filepath, unsafe=unsafe)\n", 2667 | " if lecture_assets:\n", 2668 | " self.download_assets(lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2669 | "\n", 2670 | " def download_lectures_and_captions(self, lecture_best='', lecture_title='', inner_index='', lectures_count='', lecture_subtitles='', language='',\n", 2671 | " lecture_assets='', filepath='', unsafe=False):\n", 2672 | " if lecture_best:\n", 2673 | " self.download_lectures(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=inner_index, lectures_count=lectures_count,\n", 2674 | " filepath=filepath, unsafe=unsafe)\n", 2675 | " if lecture_subtitles:\n", 2676 | " self.download_subtitles(lecture_subtitles=lecture_subtitles, language=language, filepath=filepath, unsafe=unsafe)\n", 2677 | " if lecture_assets:\n", 2678 | " self.download_assets(lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2679 | "\n", 2680 | " def course_download(self, path='', quality='', language='', caption_only=False, skip_captions=False, unsafe=False):\n", 2681 | " print('\\bStarting...')\n", 2682 | " if not self.cookies:\n", 2683 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to login as \" + fm + sb + \"(%s)\" % (\n", 2684 | " self.username) + fg + sb + \"...n\")\n", 2685 | " if self.cookies:\n", 2686 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to login using cookies ...n\")\n", 2687 | " course = get_course(url=self.url, username=self.username, password=self.password, cookies=self.cookies)\n", 2688 | " course_id = course.id\n", 2689 | " course_name = course.title\n", 2690 | " print('-----------------------------------------------------')\n", 2691 | " print(course_id, course_name)\n", 2692 | " print('-----------------------------------------------------')\n", 2693 | " chapters = course.get_chapters()\n", 2694 | " total_lectures = course.lectures\n", 2695 | " total_chapters = course.chapters\n", 2696 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Course \" + fb + sb + \"'%s'.n\" % (course_name))\n", 2697 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sd + \"Chapter(s) (%s).n\" % (total_chapters))\n", 2698 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (total_lectures))\n", 2699 | " if path:\n", 2700 | " if '~' in path:\n", 2701 | " path = os.path.expanduser(path)\n", 2702 | " course_path = \"%s%s\" % (path, course_name) if os.name == 'nt' else \"%s/%s\" % (path, course_name)\n", 2703 | " else:\n", 2704 | " path = os.getcwd()\n", 2705 | " course_path = \"%s%s\" % (path, course_name) if os.name == 'nt' else \"%s/%s\" % (path, course_name)\n", 2706 | " for chapter in chapters:\n", 2707 | " chapter_id = chapter.id\n", 2708 | " chapter_index = chapter.index\n", 2709 | " chapter_title = chapter.title\n", 2710 | " lectures = chapter.get_lectures()\n", 2711 | " lectures_count = chapter.lectures\n", 2712 | " if unsafe:\n", 2713 | " filepath = u\"%s%s\" % (course_path, chapter.unsafe_title) if os.name == 'nt' else u\"%s/%s\" % (course_path, chapter.unsafe_title)\n", 2714 | " if not unsafe:\n", 2715 | " filepath = \"%s%s\" % (course_path, chapter_title) if os.name == 'nt' else \"%s/%s\" % (course_path, chapter_title)\n", 2716 | " try:\n", 2717 | " os.makedirs(filepath)\n", 2718 | " except Exception as e:\n", 2719 | " pass\n", 2720 | " sys.stdout.write(fc + sd + \"n[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fm + sb + \"Downloading chapter : ({index} of {total})n\".format(\n", 2721 | " index=chapter_index, total=total_chapters))\n", 2722 | " if not unsafe:\n", 2723 | " sys.stdout.write(fc + sd + \"[\" + fw + sb + \"+\" + fc + sd + \"] : \" + fw + sd + \"Chapter (%s)n\" % (chapter_title))\n", 2724 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Found (%s) lectures ...n\" % (lectures_count))\n", 2725 | " if unsafe:\n", 2726 | " sys.stdout.write(fc + sd + \"[\" + fw + sb + \"+\" + fc + sd + \"] : \" + fw + sd + \"Chapter (%02d-%s)n\" % (int(chapter_index), chapter_id))\n", 2727 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (lectures_count))\n", 2728 | " inner_index = 1\n", 2729 | " for lecture in lectures:\n", 2730 | " lecture_id = lecture.id\n", 2731 | " lecture_index = lecture.index\n", 2732 | " lecture_title = lecture.title if not unsafe else \"Lecture id : %s\" % (lecture_id)\n", 2733 | " lecture_assets = lecture.assets\n", 2734 | " lecture_subtitles = lecture.subtitles\n", 2735 | " lecture_best = lecture.getbest()\n", 2736 | " lecture_streams = lecture.streams\n", 2737 | " if caption_only and not skip_captions:\n", 2738 | " self.download_captions_only(lecture_subtitles=lecture_subtitles, language=language, lecture_assets=lecture_assets,\n", 2739 | " filepath=filepath, unsafe=unsafe)\n", 2740 | " elif skip_captions and not caption_only:\n", 2741 | " if quality:\n", 2742 | " index = 0\n", 2743 | " while index < len(lecture_streams):\n", 2744 | " dimension = int(lecture_streams[index].dimention[1])\n", 2745 | " if dimension == quality:\n", 2746 | " lecture_best = lecture_streams[index]\n", 2747 | " break\n", 2748 | " index += 1\n", 2749 | " if not lecture_best:\n", 2750 | " lecture_best = lecture_best\n", 2751 | " if lecture.html:\n", 2752 | " lecture.dump(filepath=filepath, unsafe=unsafe)\n", 2753 | " self.download_lectures_only(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=inner_index,\n", 2754 | " lectures_count=lectures_count, lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2755 | " else:\n", 2756 | " if quality:\n", 2757 | " index = 0\n", 2758 | " while index < len(lecture_streams):\n", 2759 | " dimension = int(lecture_streams[index].dimention[1])\n", 2760 | " if dimension == quality:\n", 2761 | " lecture_best = lecture_streams[index]\n", 2762 | " break\n", 2763 | " index += 1\n", 2764 | " if not lecture_best:\n", 2765 | " lecture_best = lecture_best\n", 2766 | " if lecture.html:\n", 2767 | " lecture.dump(filepath=filepath, unsafe=unsafe)\n", 2768 | " self.download_lectures_and_captions(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=inner_index,\n", 2769 | " lectures_count=lectures_count, lecture_subtitles=lecture_subtitles, language=language,\n", 2770 | " lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2771 | " inner_index += 1\n", 2772 | "\n", 2773 | " def chapter_download(self, chapter_number='', chapter_start='', chapter_end='', lecture_number='', lecture_start='', lecture_end='', path='',\n", 2774 | " quality='', language='', caption_only=False, skip_captions=False, unsafe=False):\n", 2775 | " if not self.cookies:\n", 2776 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to login as \" + fm + sb + \"(%s)\" % (\n", 2777 | " self.username) + fg + sb + \"...n\")\n", 2778 | " if self.cookies:\n", 2779 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Trying to login using cookies ...n\")\n", 2780 | " course = udemy.course(url=self.url, username=self.username, password=self.password, cookies=self.cookies)\n", 2781 | " course_id = course.id\n", 2782 | " course_name = course.title\n", 2783 | " chapters = course.get_chapters()\n", 2784 | " total_lectures = course.lectures\n", 2785 | " total_chapters = course.chapters\n", 2786 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sb + \"Course \" + fb + sb + \"'%s'.n\" % (course_name))\n", 2787 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"+\" + fc + sd + \"] : \" + fg + sd + \"Chapter(s) (%s).n\" % (total_chapters))\n", 2788 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (total_lectures))\n", 2789 | " if path:\n", 2790 | " if '~' in path:\n", 2791 | " path = os.path.expanduser(path)\n", 2792 | " course_path = \"%s%s\" % (path, course_name) if os.name == 'nt' else \"%s/%s\" % (path, course_name)\n", 2793 | " else:\n", 2794 | " path = os.getcwd()\n", 2795 | " course_path = \"%s%s\" % (path, course_name) if os.name == 'nt' else \"%s/%s\" % (path, course_name)\n", 2796 | " _lectures_start, _lectures_end = lecture_start, lecture_end\n", 2797 | " if chapter_start and not chapter_end:\n", 2798 | " chapter_end = total_chapters\n", 2799 | " if chapter_number and chapter_number > 0 and chapter_number <= total_chapters:\n", 2800 | " chapter = chapters[chapter_number - 1]\n", 2801 | " if chapter:\n", 2802 | " chapter_id = chapter.id\n", 2803 | " chapter_index = chapter.index\n", 2804 | " chapter_title = chapter.title\n", 2805 | " lectures = chapter.get_lectures()\n", 2806 | " lectures_count = chapter.lectures\n", 2807 | " if lecture_end and lecture_end > lectures_count:\n", 2808 | " lecture_end = lectures_count\n", 2809 | "\n", 2810 | " if unsafe:\n", 2811 | " filepath = u\"%s%s\" % (course_path, chapter.unsafe_title) if os.name == 'nt' else u\"%s/%s\" % (course_path, chapter.unsafe_title)\n", 2812 | " if not unsafe:\n", 2813 | " filepath = \"%s%s\" % (course_path, chapter_title) if os.name == 'nt' else \"%s/%s\" % (course_path, chapter_title)\n", 2814 | " try:\n", 2815 | " os.makedirs(filepath)\n", 2816 | " except Exception as e:\n", 2817 | " pass\n", 2818 | " sys.stdout.write(\n", 2819 | " fc + sd + \"n[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fm + sb + \"Downloading chapter : ({index})n\".format(index=chapter_index))\n", 2820 | " if not unsafe:\n", 2821 | " sys.stdout.write(fc + sd + \"[\" + fw + sb + \"+\" + fc + sd + \"] : \" + fw + sd + \"Chapter (%s)n\" % (chapter_title))\n", 2822 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Found (%s) lectures ...n\" % (lectures_count))\n", 2823 | " if unsafe:\n", 2824 | " sys.stdout.write(\n", 2825 | " fc + sd + \"[\" + fw + sb + \"+\" + fc + sd + \"] : \" + fw + sd + \"Chapter (%02d-%s)n\" % (int(chapter_index), chapter_id))\n", 2826 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (lectures_count))\n", 2827 | " lecture_start = _lectures_start\n", 2828 | " lecture_end = lectures_count if lecture_start and not lecture_end else _lectures_end\n", 2829 | " if lecture_number and lecture_number > 0 and lecture_number <= lectures_count:\n", 2830 | " lecture = lectures[lecture_number - 1]\n", 2831 | " lecture_id = lecture.id\n", 2832 | " lecture_index = lecture.index\n", 2833 | " lecture_title = lecture.title if not unsafe else \"Lecture id : %s\" % (lecture_id)\n", 2834 | " lecture_assets = lecture.assets\n", 2835 | " lecture_subtitles = lecture.subtitles\n", 2836 | " lecture_best = lecture.getbest()\n", 2837 | " lecture_streams = lecture.streams\n", 2838 | " if caption_only and not skip_captions:\n", 2839 | " self.download_captions_only(lecture_subtitles=lecture_subtitles, language=language, lecture_assets=lecture_assets,\n", 2840 | " filepath=filepath, unsafe=unsafe)\n", 2841 | " elif skip_captions and not caption_only:\n", 2842 | " if quality:\n", 2843 | " index = 0\n", 2844 | " while index < len(lecture_streams):\n", 2845 | " dimension = int(lecture_streams[index].dimention[1])\n", 2846 | " if dimension == quality:\n", 2847 | " lecture_best = lecture_streams[index]\n", 2848 | " break\n", 2849 | " index += 1\n", 2850 | " if not lecture_best:\n", 2851 | " lecture_best = lecture_best\n", 2852 | " if lecture.html:\n", 2853 | " lecture.dump(filepath=filepath)\n", 2854 | " self.download_lectures_only(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=lecture_number,\n", 2855 | " lectures_count=lectures_count, lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2856 | " else:\n", 2857 | " if quality:\n", 2858 | " index = 0\n", 2859 | " while index < len(lecture_streams):\n", 2860 | " dimension = int(lecture_streams[index].dimention[1])\n", 2861 | " if dimension == quality:\n", 2862 | " lecture_best = lecture_streams[index]\n", 2863 | " break\n", 2864 | " index += 1\n", 2865 | " if not lecture_best:\n", 2866 | " lecture_best = lecture_best\n", 2867 | " if lecture.html:\n", 2868 | " lecture.dump(filepath=filepath)\n", 2869 | " self.download_lectures_and_captions(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=lecture_number,\n", 2870 | " lectures_count=lectures_count, lecture_subtitles=lecture_subtitles, language=language,\n", 2871 | " lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2872 | " elif lecture_start and lecture_start > 0 and lecture_start <= lecture_end and lecture_end <= lectures_count:\n", 2873 | " while lecture_start <= lecture_end:\n", 2874 | " lecture = lectures[lecture_start - 1]\n", 2875 | " lecture_id = lecture.id\n", 2876 | " lecture_index = lecture.index\n", 2877 | " lecture_title = lecture.title if not unsafe else \"Lecture id : %s\" % (lecture_id)\n", 2878 | " lecture_assets = lecture.assets\n", 2879 | " lecture_subtitles = lecture.subtitles\n", 2880 | " lecture_best = lecture.getbest()\n", 2881 | " lecture_streams = lecture.streams\n", 2882 | " if caption_only and not skip_captions:\n", 2883 | " self.download_captions_only(lecture_subtitles=lecture_subtitles, language=language, lecture_assets=lecture_assets,\n", 2884 | " filepath=filepath, unsafe=unsafe)\n", 2885 | " elif skip_captions and not caption_only:\n", 2886 | " if quality:\n", 2887 | " index = 0\n", 2888 | " while index < len(lecture_streams):\n", 2889 | " dimension = int(lecture_streams[index].dimention[1])\n", 2890 | " if dimension == quality:\n", 2891 | " lecture_best = lecture_streams[index]\n", 2892 | " break\n", 2893 | " index += 1\n", 2894 | " if not lecture_best:\n", 2895 | " lecture_best = lecture_best\n", 2896 | " if lecture.html:\n", 2897 | " lecture.dump(filepath=filepath, unsafe=unsafe)\n", 2898 | " self.download_lectures_only(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=lecture_start,\n", 2899 | " lectures_count=lecture_end, lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2900 | " else:\n", 2901 | " if quality:\n", 2902 | " index = 0\n", 2903 | " while index < len(lecture_streams):\n", 2904 | " dimension = int(lecture_streams[index].dimention[1])\n", 2905 | " if dimension == quality:\n", 2906 | " lecture_best = lecture_streams[index]\n", 2907 | " break\n", 2908 | " index += 1\n", 2909 | " if not lecture_best:\n", 2910 | " lecture_best = lecture_best\n", 2911 | " if lecture.html:\n", 2912 | " lecture.dump(filepath=filepath, unsafe=unsafe)\n", 2913 | " self.download_lectures_and_captions(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=lecture_start,\n", 2914 | " lectures_count=lecture_end, lecture_subtitles=lecture_subtitles, language=language,\n", 2915 | " lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2916 | " lecture_start += 1\n", 2917 | " else:\n", 2918 | " inner_index = 1\n", 2919 | " for lecture in lectures:\n", 2920 | " lecture_id = lecture.id\n", 2921 | " lecture_index = lecture.index\n", 2922 | " lecture_title = lecture.title if not unsafe else \"Lecture id : %s\" % (lecture_id)\n", 2923 | " lecture_assets = lecture.assets\n", 2924 | " lecture_subtitles = lecture.subtitles\n", 2925 | " lecture_best = lecture.getbest()\n", 2926 | " lecture_streams = lecture.streams\n", 2927 | " if caption_only and not skip_captions:\n", 2928 | " self.download_captions_only(lecture_subtitles=lecture_subtitles, language=language, lecture_assets=lecture_assets,\n", 2929 | " filepath=filepath, unsafe=unsafe)\n", 2930 | " elif skip_captions and not caption_only:\n", 2931 | " if quality:\n", 2932 | " index = 0\n", 2933 | " while index < len(lecture_streams):\n", 2934 | " dimension = int(lecture_streams[index].dimention[1])\n", 2935 | " if dimension == quality:\n", 2936 | " lecture_best = lecture_streams[index]\n", 2937 | " break\n", 2938 | " index += 1\n", 2939 | " if not lecture_best:\n", 2940 | " lecture_best = lecture_best\n", 2941 | " if lecture.html:\n", 2942 | " lecture.dump(filepath=filepath, unsafe=unsafe)\n", 2943 | " self.download_lectures_only(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=inner_index,\n", 2944 | " lectures_count=lectures_count, lecture_assets=lecture_assets, filepath=filepath,\n", 2945 | " unsafe=unsafe)\n", 2946 | " else:\n", 2947 | " if quality:\n", 2948 | " index = 0\n", 2949 | " while index < len(lecture_streams):\n", 2950 | " dimension = int(lecture_streams[index].dimention[1])\n", 2951 | " if dimension == quality:\n", 2952 | " lecture_best = lecture_streams[index]\n", 2953 | " break\n", 2954 | " index += 1\n", 2955 | " if not lecture_best:\n", 2956 | " lecture_best = lecture_best\n", 2957 | " if lecture.html:\n", 2958 | " lecture.dump(filepath=filepath, unsafe=unsafe)\n", 2959 | " self.download_lectures_and_captions(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=inner_index,\n", 2960 | " lectures_count=lectures_count, lecture_subtitles=lecture_subtitles, language=language,\n", 2961 | " lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 2962 | " inner_index += 1\n", 2963 | " elif chapter_start and chapter_start > 0 and chapter_start <= chapter_end and chapter_end <= total_chapters:\n", 2964 | " while chapter_start <= chapter_end:\n", 2965 | " chapter = chapters[chapter_start - 1]\n", 2966 | " chapter_id = chapter.id\n", 2967 | " chapter_index = chapter.index\n", 2968 | " chapter_title = chapter.title\n", 2969 | " lectures = chapter.get_lectures()\n", 2970 | " lectures_count = chapter.lectures\n", 2971 | " if unsafe:\n", 2972 | " filepath = u\"%s%s\" % (course_path, chapter.unsafe_title) if os.name == 'nt' else u\"%s/%s\" % (course_path, chapter.unsafe_title)\n", 2973 | " if not unsafe:\n", 2974 | " filepath = \"%s%s\" % (course_path, chapter_title) if os.name == 'nt' else \"%s/%s\" % (course_path, chapter_title)\n", 2975 | " try:\n", 2976 | " os.makedirs(filepath)\n", 2977 | " except Exception as e:\n", 2978 | " pass\n", 2979 | " sys.stdout.write(fc + sd + \"n[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fm + sb + \"Downloading chapter : ({index} of {total})n\".format(\n", 2980 | " index=chapter_start, total=chapter_end))\n", 2981 | " if not unsafe:\n", 2982 | " sys.stdout.write(fc + sd + \"[\" + fw + sb + \"+\" + fc + sd + \"] : \" + fw + sd + \"Chapter (%s)n\" % (chapter_title))\n", 2983 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Found (%s) lectures ...n\" % (lectures_count))\n", 2984 | " if unsafe:\n", 2985 | " sys.stdout.write(\n", 2986 | " fc + sd + \"[\" + fw + sb + \"+\" + fc + sd + \"] : \" + fw + sd + \"Chapter (%02d-%s)n\" % (int(chapter_index), chapter_id))\n", 2987 | " sys.stdout.write(fc + sd + \"[\" + fm + sb + \"*\" + fc + sd + \"] : \" + fg + sd + \"Lecture(s) (%s).n\" % (lectures_count))\n", 2988 | " lecture_start = _lectures_start\n", 2989 | " lecture_end = lectures_count if lecture_start and not lecture_end else _lectures_end\n", 2990 | " if lecture_number and lecture_number > 0 and lecture_number <= lectures_count:\n", 2991 | " lecture = lectures[lecture_number - 1]\n", 2992 | " lecture_id = lecture.id\n", 2993 | " lecture_index = lecture.index\n", 2994 | " lecture_title = lecture.title if not unsafe else \"Lecture id : %s\" % (lecture_id)\n", 2995 | " lecture_assets = lecture.assets\n", 2996 | " lecture_subtitles = lecture.subtitles\n", 2997 | " lecture_best = lecture.getbest()\n", 2998 | " lecture_streams = lecture.streams\n", 2999 | " if caption_only and not skip_captions:\n", 3000 | " self.download_captions_only(lecture_subtitles=lecture_subtitles, language=language, lecture_assets=lecture_assets,\n", 3001 | " filepath=filepath, unsafe=unsafe)\n", 3002 | " elif skip_captions and not caption_only:\n", 3003 | " if quality:\n", 3004 | " index = 0\n", 3005 | " while index < len(lecture_streams):\n", 3006 | " dimension = int(lecture_streams[index].dimention[1])\n", 3007 | " if dimension == quality:\n", 3008 | " lecture_best = lecture_streams[index]\n", 3009 | " break\n", 3010 | " index += 1\n", 3011 | " if not lecture_best:\n", 3012 | " lecture_best = lecture_best\n", 3013 | " self.download_lectures_only(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=lecture_number,\n", 3014 | " lectures_count=lectures_count, lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 3015 | " else:\n", 3016 | " if quality:\n", 3017 | " index = 0\n", 3018 | " while index < len(lecture_streams):\n", 3019 | " dimension = int(lecture_streams[index].dimention[1])\n", 3020 | " if dimension == quality:\n", 3021 | " lecture_best = lecture_streams[index]\n", 3022 | " break\n", 3023 | " index += 1\n", 3024 | " if not lecture_best:\n", 3025 | " lecture_best = lecture_best\n", 3026 | " self.download_lectures_and_captions(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=lecture_number,\n", 3027 | " lectures_count=lectures_count, lecture_subtitles=lecture_subtitles, language=language,\n", 3028 | " lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 3029 | " elif lecture_start and lecture_start > 0 and lecture_start <= lecture_end and lecture_end <= lectures_count:\n", 3030 | " while lecture_start <= lecture_end:\n", 3031 | " lecture = lectures[lecture_start - 1]\n", 3032 | " lecture_id = lecture.id\n", 3033 | " lecture_index = lecture.index\n", 3034 | " lecture_title = lecture.title if not unsafe else \"Lecture id : %s\" % (lecture_id)\n", 3035 | " lecture_assets = lecture.assets\n", 3036 | " lecture_subtitles = lecture.subtitles\n", 3037 | " lecture_best = lecture.getbest()\n", 3038 | " lecture_streams = lecture.streams\n", 3039 | " if caption_only and not skip_captions:\n", 3040 | " self.download_captions_only(lecture_subtitles=lecture_subtitles, language=language, lecture_assets=lecture_assets,\n", 3041 | " filepath=filepath, unsafe=unsafe)\n", 3042 | " elif skip_captions and not caption_only:\n", 3043 | " if quality:\n", 3044 | " index = 0\n", 3045 | " while index < len(lecture_streams):\n", 3046 | " dimension = int(lecture_streams[index].dimention[1])\n", 3047 | " if dimension == quality:\n", 3048 | " lecture_best = lecture_streams[index]\n", 3049 | " break\n", 3050 | " index += 1\n", 3051 | " if not lecture_best:\n", 3052 | " lecture_best = lecture_best\n", 3053 | " self.download_lectures_only(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=lecture_start,\n", 3054 | " lectures_count=lecture_end, lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 3055 | " else:\n", 3056 | " if quality:\n", 3057 | " index = 0\n", 3058 | " while index < len(lecture_streams):\n", 3059 | " dimension = int(lecture_streams[index].dimention[1])\n", 3060 | " if dimension == quality:\n", 3061 | " lecture_best = lecture_streams[index]\n", 3062 | " break\n", 3063 | " index += 1\n", 3064 | " if not lecture_best:\n", 3065 | " lecture_best = lecture_best\n", 3066 | " self.download_lectures_and_captions(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=lecture_start,\n", 3067 | " lectures_count=lecture_end, lecture_subtitles=lecture_subtitles, language=language,\n", 3068 | " lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 3069 | " lecture_start += 1\n", 3070 | " else:\n", 3071 | " inner_index = 1\n", 3072 | " for lecture in lectures:\n", 3073 | " lecture_id = lecture.id\n", 3074 | " lecture_index = lecture.index\n", 3075 | " lecture_title = lecture.title if not unsafe else \"Lecture id : %s\" % (lecture_id)\n", 3076 | " lecture_assets = lecture.assets\n", 3077 | " lecture_subtitles = lecture.subtitles\n", 3078 | " lecture_best = lecture.getbest()\n", 3079 | " lecture_streams = lecture.streams\n", 3080 | " if caption_only and not skip_captions:\n", 3081 | " self.download_captions_only(lecture_subtitles=lecture_subtitles, language=language, lecture_assets=lecture_assets,\n", 3082 | " filepath=filepath, unsafe=unsafe)\n", 3083 | " elif skip_captions and not caption_only:\n", 3084 | " if quality:\n", 3085 | " index = 0\n", 3086 | " while index < len(lecture_streams):\n", 3087 | " dimension = int(lecture_streams[index].dimention[1])\n", 3088 | " if dimension == quality:\n", 3089 | " lecture_best = lecture_streams[index]\n", 3090 | " break\n", 3091 | " index += 1\n", 3092 | " if not lecture_best:\n", 3093 | " lecture_best = lecture_best\n", 3094 | " self.download_lectures_only(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=inner_index,\n", 3095 | " lectures_count=lectures_count, lecture_assets=lecture_assets, filepath=filepath,\n", 3096 | " unsafe=unsafe)\n", 3097 | " else:\n", 3098 | " if quality:\n", 3099 | " index = 0\n", 3100 | " while index < len(lecture_streams):\n", 3101 | " dimension = int(lecture_streams[index].dimention[1])\n", 3102 | " if dimension == quality:\n", 3103 | " lecture_best = lecture_streams[index]\n", 3104 | " break\n", 3105 | " index += 1\n", 3106 | " if not lecture_best:\n", 3107 | " lecture_best = lecture_best\n", 3108 | " self.download_lectures_and_captions(lecture_best=lecture_best, lecture_title=lecture_title, inner_index=inner_index,\n", 3109 | " lectures_count=lectures_count, lecture_subtitles=lecture_subtitles, language=language,\n", 3110 | " lecture_assets=lecture_assets, filepath=filepath, unsafe=unsafe)\n", 3111 | " inner_index += 1\n", 3112 | " chapter_start += 1\n", 3113 | " else:\n", 3114 | " if not chapter_end and not chapter_number and not chapter_start:\n", 3115 | " sys.stdout.write(\n", 3116 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Argument(s) are missing : Chapter(s) range or chapter(s) number is required.n\")\n", 3117 | " elif chapter_end and chapter_end > total_chapters or chapter_number and chapter_number > total_chapters:\n", 3118 | " sys.stdout.write(\n", 3119 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Chapter(s) Range exceeded : Chapter(s) ending or chapter(s) number is out of rangen\")\n", 3120 | " elif chapter_start and chapter_start > chapter_end:\n", 3121 | " sys.stdout.write(\n", 3122 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Chapter(s) Range exception : Chapter(s) starting point cannot be greater than chapter(s) ending pointn\")\n", 3123 | " elif chapter_end and not chapter_start:\n", 3124 | " sys.stdout.write(\n", 3125 | " fc + sd + \"[\" + fr + sb + \"-\" + fc + sd + \"] : \" + fr + sb + \"Argument(s) are missing : Chapter(s) range starting point is missing ..n\")\n", 3126 | " sys.stdout.write(\n", 3127 | " fc + sd + \"[\" + fy + sb + \"i\" + fc + sd + \"] : \" + fw + sb + \"Chapter(s) number or range should be in between ({start} to {end}).n\".format(\n", 3128 | " start=1, end=total_chapters))\n", 3129 | " sys.exit(0)\n", 3130 | "\n", 3131 | "clear_output()\n", 3132 | "print('prepration is complete')\n" 3133 | ], 3134 | "execution_count": 1, 3135 | "outputs": [ 3136 | { 3137 | "output_type": "stream", 3138 | "text": [ 3139 | "prepration is complete\n" 3140 | ], 3141 | "name": "stdout" 3142 | } 3143 | ] 3144 | }, 3145 | { 3146 | "cell_type": "code", 3147 | "metadata": { 3148 | "id": "Hl9cavOU9S2z", 3149 | "colab_type": "code", 3150 | "colab": {} 3151 | }, 3152 | "source": [ 3153 | "#@title Add Udemy links in the `courses` list\n", 3154 | "\n", 3155 | "courses = [\n", 3156 | " \"https://www.udemy.com/course/skillshare-course-marketing/\",\n", 3157 | "]\n", 3158 | "\n", 3159 | "language = '' # left blank if you want all language or put some e.g. en\n", 3160 | "\n", 3161 | "try:\n", 3162 | " for course in courses:\n", 3163 | " udemy = Udemy(url=course, username=username, password=password, cookies=cookies)\n", 3164 | " udemy.course_download(path=output, quality=quality, language=language)\n", 3165 | "except Exception as e:\n", 3166 | " print(e.args)" 3167 | ], 3168 | "execution_count": 0, 3169 | "outputs": [] 3170 | } 3171 | ] 3172 | } --------------------------------------------------------------------------------