├── .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 | "
"
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)
' % 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 | }
--------------------------------------------------------------------------------