├── .gitignore ├── pyproject.toml ├── setup.py ├── unimi_dl ├── __main__.py ├── __init__.py ├── platform │ ├── __init__.py │ ├── platform.py │ ├── getPlatform.py │ ├── ariel.py │ └── panopto.py ├── multi_select.py └── cmd.py ├── setup.cfg ├── CHANGELOG.md ├── README_EN.md ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /*.egg-info 2 | /dist/* 3 | __pycache__ 4 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 4 | # 5 | # This file is part of unimi-dl. 6 | # 7 | # unimi-dl is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # unimi-dl is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with unimi-dl. If not, see . 19 | 20 | 21 | from setuptools import setup 22 | 23 | setup() 24 | -------------------------------------------------------------------------------- /unimi_dl/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 2 | # 3 | # This file is part of unimi-dl. 4 | # 5 | # unimi-dl is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # unimi-dl is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with unimi-dl. If not, see . 17 | 18 | 19 | from .cmd import main 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /unimi_dl/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 2 | # 3 | # This file is part of unimi-dl. 4 | # 5 | # unimi-dl is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # unimi-dl is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with unimi-dl. If not, see . 17 | 18 | 19 | __version__ = "0.3.1" 20 | __license__ = "GPL v.3" 21 | 22 | import unimi_dl.platform 23 | 24 | __all__ = ["cmd"] 25 | -------------------------------------------------------------------------------- /unimi_dl/platform/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 2 | # 3 | # This file is part of unimi-dl. 4 | # 5 | # unimi-dl is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # unimi-dl is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with unimi-dl. If not, see . 17 | 18 | 19 | from .getPlatform import getPlatform 20 | from .ariel import Ariel 21 | from .panopto import Panopto 22 | 23 | __all__ = ["ariel", "getPlatform", "panopto", "platform"] 24 | -------------------------------------------------------------------------------- /unimi_dl/platform/platform.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 2 | # 3 | # This file is part of unimi-dl. 4 | # 5 | # unimi-dl is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # unimi-dl is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with unimi-dl. If not, see . 17 | 18 | 19 | from __future__ import annotations 20 | 21 | 22 | class Platform: 23 | def __init__(self, email: str, password: str) -> None: 24 | self.email = email 25 | self.password = password 26 | 27 | def get_manifests(self, url: str) -> dict[str, str]: 28 | """ Returns a list of couples, each one containing a filename and relative 29 | manifest, fetched from {url} """ 30 | 31 | raise NotImplementedError 32 | -------------------------------------------------------------------------------- /unimi_dl/multi_select.py: -------------------------------------------------------------------------------- 1 | class WrongSelectionError(Exception): 2 | pass 3 | 4 | 5 | def multi_select(entries: list, entries_text: list = None, selection_text: str = "\nYour selection: ") -> list: 6 | if not entries_text: 7 | entries_text = entries 8 | elif len(entries_text) != len(entries): 9 | raise ValueError("entries and entries_text must have the same length") 10 | 11 | for i, item in enumerate(entries_text): 12 | print("%d.\t%s" % (i+1, item)) 13 | menu_input = input(selection_text) 14 | 15 | ranges = menu_input.strip().split(",") 16 | sel_indexes = set() 17 | if len(ranges) == 1 and ranges[0] == "": 18 | return [] 19 | for rang in ranges: 20 | extremes = rang.split("-") 21 | if not 1 <= len(extremes) <= 2: 22 | raise WrongSelectionError 23 | try: 24 | extremes = [int(n)-1 for n in extremes] 25 | except ValueError: 26 | raise WrongSelectionError 27 | if len(extremes) == 1: 28 | extremes.append(extremes[0]) 29 | sel_indexes.update(range(extremes[0], extremes[1]+1)) 30 | 31 | try: 32 | return [entries[i] for i in sel_indexes] 33 | except IndexError: 34 | raise WrongSelectionError 35 | -------------------------------------------------------------------------------- /unimi_dl/platform/getPlatform.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 2 | # 3 | # This file is part of unimi-dl. 4 | # 5 | # unimi-dl is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # unimi-dl is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with unimi-dl. If not, see . 17 | 18 | from __future__ import annotations 19 | 20 | from .ariel import Ariel 21 | from .panopto import Panopto 22 | from .platform import Platform 23 | 24 | 25 | def getPlatform(email: str, password: str, platform: str) -> Platform: 26 | """ Factory method to create the appropriate Platform instance. """ 27 | 28 | if platform == 'ariel': 29 | return Ariel(email, password) 30 | if platform == 'panopto': 31 | return Panopto(email, password) 32 | 33 | raise NotImplementedError 34 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 2 | # 3 | # This file is part of unimi-dl. 4 | # 5 | # unimi-dl is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # unimi-dl is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with unimi-dl. If not, see . 17 | 18 | 19 | [metadata] 20 | name = unimi-dl 21 | version = attr: unimi_dl.__version__ 22 | author = Alessandro Clerici Lorenzini and Zhifan Chen. 23 | description = Script used for downloading videos from Unimi portals 24 | long_description = file: README.md 25 | long_description_content_type = text/markdown 26 | url = https://github.com/SimpoLab/unimi-dl 27 | project_urls = 28 | Bug Tracker = https://github.com/SimpoLab/unimi-dl/issues 29 | classifiers = 30 | Development Status :: 4 - Beta 31 | Environment :: Console 32 | License :: OSI Approved :: GNU General Public License v3 (GPLv3) 33 | Operating System :: MacOS 34 | Operating System :: Microsoft :: Windows 35 | Operating System :: POSIX :: Linux 36 | Programming Language :: Python 37 | Programming Language :: Python :: 3 38 | Topic :: Education 39 | Topic :: Multimedia 40 | license = attr: unimi_dl.__license__ 41 | license_file = LICENSE 42 | keywords = unimi, download, ariel, panopto, labonline 43 | scripts = unimi_dl/cmd.py 44 | 45 | 46 | [options] 47 | packages = 48 | unimi_dl 49 | unimi_dl.platform 50 | python_requires = >=3.8 51 | install_requires = 52 | requests 53 | youtube-dl 54 | 55 | 56 | [options.entry_points] 57 | console_scripts = 58 | unimi-dl = unimi_dl.cmd:main 59 | -------------------------------------------------------------------------------- /unimi_dl/platform/ariel.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 2 | # 3 | # This file is part of unimi-dl. 4 | # 5 | # unimi-dl is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # unimi-dl is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with unimi-dl. If not, see . 17 | 18 | 19 | from __future__ import annotations 20 | import logging 21 | import re 22 | import urllib.parse 23 | 24 | import requests 25 | 26 | from .platform import Platform 27 | 28 | 29 | def get_ariel_session(email: str, password: str) -> requests.Session: 30 | s = requests.Session() 31 | login_url = 'https://elearning.unimi.it/authentication/skin/portaleariel/login.aspx?url=https://ariel.unimi.it/' 32 | payload = {'hdnSilent': 'true', 33 | 'tbLogin': email, 34 | 'tbPassword': password} 35 | s.post(login_url, data=payload) 36 | return s 37 | 38 | 39 | class Ariel(Platform): 40 | def __init__(self, email: str, password: str) -> None: 41 | super().__init__(email, password) 42 | self.logger = logging.getLogger(__name__) 43 | self.logger.info("Logging in") 44 | self.session = get_ariel_session(email, password) 45 | 46 | def get_manifests(self, url: str) -> dict[str, str]: 47 | self.logger.info("Getting video page") 48 | video_page = self.session.get(url).text 49 | 50 | self.logger.info("Collecting manifests and video names") 51 | res = {} 52 | manifest_re = re.compile( 53 | r"https://.*?/mp4:.*?([^/]*?)\.(m4v|mp4)/manifest.m3u8") 54 | for i, manifest in enumerate(manifest_re.finditer(video_page)): 55 | title = urllib.parse.unquote( 56 | manifest[1]) if manifest[1] else urllib.parse.urlparse(url)[1]+str(i) 57 | while title in res: 58 | title += "_other" 59 | res[title] = manifest[0] 60 | return res 61 | -------------------------------------------------------------------------------- /unimi_dl/platform/panopto.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Alessandro Clerici Lorenzini and Zhifan Chen. 2 | # 3 | # This file is part of unimi-dl. 4 | # 5 | # unimi-dl is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # unimi-dl is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with unimi-dl. If not, see . 17 | 18 | 19 | from __future__ import annotations 20 | import logging 21 | import re 22 | 23 | import requests 24 | from urllib3 import disable_warnings 25 | import urllib.parse 26 | from urllib3.exceptions import InsecureRequestWarning 27 | 28 | from .ariel import get_ariel_session 29 | from .platform import Platform 30 | 31 | 32 | def get_panopto_session(email: str, password: str) -> requests.Session: 33 | s = get_ariel_session(email, password) 34 | auth_url = r"https://unimi.cloud.panopto.eu/Panopto/Pages/Auth/Login.aspx?instance=Labonline" 35 | disable_warnings(InsecureRequestWarning) 36 | s.get(auth_url, verify=False) 37 | return s 38 | 39 | 40 | class Panopto(Platform): 41 | def __init__(self, email: str, password: str) -> None: 42 | super().__init__(email, password) 43 | self.logger = logging.getLogger(__name__) 44 | self.logger.info("Logging in") 45 | self.session = get_panopto_session(email, password) 46 | 47 | def get_manifests(self, url: str) -> dict[str, str]: 48 | self.logger.info("Getting video page") 49 | video_page = self.session.get(url).text 50 | 51 | iframe_re = re.compile(r"