diff --git a/README.md b/README.md index 60704b6..b328964 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,16 @@ CLI to download videos from https://xvideos.com +
中文文档
+ +## Features + +-[X] Download a single video (requires the URL of the video playback page) +-[X] Download all videos in the favorites (requires the URL of the favorite page) +-[X] Download all videos uploaded by the user (requires the URL of the user's homepage) +-[X] Download all videos published by the channel (requires the URL of the channel homepage) +-[X] Segmented high-speed download, breakpoint download, progress and status display + ## Usage - Install xvideos-dl @@ -30,20 +40,29 @@ pip install -U xvideos-dl xvideos-dl --help ``` -- Download video +- Download single / favorites / uploaded / published videos in one command: ```bash -xvideos-dl https://www.xvideos.com/video37177493/asian_webcam_2_camsex4u.life +xvideos-dl https://www.xvideos.com/video37177493/asian_webcam_2_camsex4u.life https://www.xvideos.com/favorite/71879935/_ https://www.xvideos.com/profiles/mypornstation https://www.xvideos.com/channels/av69tv ``` ## Release History +### 1.1.0 + +New Features: + +- Download all videos uploaded by users. +- Download all videos posted by the channel. +- Download single, playlist, user uploaded and channel posted videos in one command. +- Optimize download status display. + ### 1.0.1 -New features: +New Features: -- Download videos from playlist -- Show download speed +- Download videos from playlist. +- Show download speed. ### 1.0.0 diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 0000000..8be2bd5 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,73 @@ +# xvideos-dl + +
+ +[![Build status](https://github.com/lonsty/xvideos-dl/workflows/build/badge.svg?branch=master&event=push)](https://github.com/lonsty/xvideos-dl/actions?query=workflow%3Abuild) +[![Python Version](https://img.shields.io/pypi/pyversions/xvideos-dl.svg)](https://pypi.org/project/xvideos-dl/) +[![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/lonsty/xvideos-dl/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot) + +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Security: bandit](https://img.shields.io/badge/security-bandit-green.svg)](https://github.com/PyCQA/bandit) +[![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/lonsty/xvideos-dl/blob/master/.pre-commit-config.yaml) +[![Semantic Versions](https://img.shields.io/badge/%F0%9F%9A%80-semantic%20versions-informational.svg)](https://github.com/lonsty/xvideos-dl/releases) +[![License](https://img.shields.io/github/license/lonsty/xvideos-dl)](https://github.com/lonsty/xvideos-dl/blob/master/LICENSE) + +https://xvideos.com H 片命令行下载工具 + +
+ +## 工具特点 + +- [X] 下载单个视频(需视频播放页的 URL) +- [X] 下载收藏夹中的所有视频(需收藏夹页的 URL) +- [X] 下载用户上传的所有视频(需用户首页的 URL) +- [X] 下载频道发布的所有视频(需频道首页的 URL) +- [X] 分段高速下载,断点下载,进度与状态显示 + +## 使用说明 + +### 安装工具 xvideos-dl + +```bash +pip install -U xvideos-dl +``` + +### 获取工具使用帮助 + +```bash +xvideos-dl --help +``` + +### 下载视频 + +*注意:首此使用时,程序会出现提示语要求输入 Cookie,用个人账号登录 https://xvideos.com 获取 Cookie 粘贴到终端,即可继续运行。Cookie 可以长期使用。* + +只需一行命令,下载单个 / 收藏夹 / 用户上传 / 频道发布 的所有视频 + +```bash +xvideos-dl https://www.xvideos.com/video37177493/asian_webcam_2_camsex4u.life https://www.xvideos.com/favorite/71879935/_ https://www.xvideos.com/profiles/mypornstation https://www.xvideos.com/channels/av69tv +``` + +![示例](demo_1.PNG) + +## Release History + +### 1.1.0 + +New Features: + +- Download all videos uploaded by users. +- Download all videos posted by the channel. +- Download single, playlist, user uploaded and channel posted videos in one command. +- Optimize download status display. + +### 1.0.1 + +New Features: + +- Download all videos in playlists. +- Show download speed. + +### 1.0.0 + +Initial release on PyPY. diff --git a/demo_1.PNG b/demo_1.PNG new file mode 100644 index 0000000..1ecce42 Binary files /dev/null and b/demo_1.PNG differ diff --git a/poetry.lock b/poetry.lock index db262c5..0d1d003 100644 --- a/poetry.lock +++ b/poetry.lock @@ -56,6 +56,21 @@ PyYAML = ">=5.3.1" six = ">=1.10.0" stevedore = ">=1.20.0" +[[package]] +name = "beautifulsoup4" +version = "4.9.3" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""} + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "black" version = "20.8b1" @@ -437,7 +452,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm [[package]] name = "pyupgrade" -version = "2.10.0" +version = "2.10.1" description = "A tool to automatically upgrade syntax for newer versions." category = "dev" optional = false @@ -543,6 +558,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "soupsieve" +version = "2.2" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "stevedore" version = "3.3.0" @@ -608,20 +631,20 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.26.3" +version = "1.26.4" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] -brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotlipy (>=0.6.0)"] [[package]] name = "virtualenv" -version = "20.4.2" +version = "20.4.3" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -661,7 +684,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "cd5784c574938e4efe848f1add7f9a3d62e0eb7854ed8a5b6ef9d22d9d33de82" +content-hash = "64bef34a8fd6a1622bd647b96e3796db239fcb4e9042afe983e541a6580b2735" [metadata.files] appdirs = [ @@ -684,6 +707,11 @@ bandit = [ {file = "bandit-1.7.0-py3-none-any.whl", hash = "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07"}, {file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"}, ] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, + {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, + {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, +] black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] @@ -857,8 +885,8 @@ pytest = [ {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"}, ] pyupgrade = [ - {file = "pyupgrade-2.10.0-py2.py3-none-any.whl", hash = "sha256:b26a00db6e2d745fe5a949e1fd02c5286c3999edaf804f746c69d559c8f8b365"}, - {file = "pyupgrade-2.10.0.tar.gz", hash = "sha256:601427033f280d50b5b102fed1013b96f91244777772114aeb7e191762cd6050"}, + {file = "pyupgrade-2.10.1-py2.py3-none-any.whl", hash = "sha256:3c9902865055af0211a312180fbbf7858bbd6cc51e414c0085b8b759b5bab0ed"}, + {file = "pyupgrade-2.10.1.tar.gz", hash = "sha256:6ecf88976084de9bac0041fb74b0319e1866b70fd110189937563e19186aa48c"}, ] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, @@ -962,6 +990,10 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] +soupsieve = [ + {file = "soupsieve-2.2-py3-none-any.whl", hash = "sha256:d3a5ea5b350423f47d07639f74475afedad48cf41c0ad7a82ca13a3928af34f6"}, + {file = "soupsieve-2.2.tar.gz", hash = "sha256:407fa1e8eb3458d1b5614df51d9651a1180ea5fedf07feb46e45d7e25e6d6cdd"}, +] stevedore = [ {file = "stevedore-3.3.0-py3-none-any.whl", hash = "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"}, {file = "stevedore-3.3.0.tar.gz", hash = "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee"}, @@ -1016,12 +1048,12 @@ typing-extensions = [ {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] urllib3 = [ - {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, - {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, + {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, + {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, ] virtualenv = [ - {file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"}, - {file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"}, + {file = "virtualenv-20.4.3-py2.py3-none-any.whl", hash = "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c"}, + {file = "virtualenv-20.4.3.tar.gz", hash = "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107"}, ] wrapt = [ {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, diff --git a/pyproject.toml b/pyproject.toml index a1f5e94..9ffb61b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "xvideos-dl" -version = "1.0.1" +version = "1.1.0" description = "CLI to download videos from https://xvideos.com" readme = "README.md" authors = [ @@ -36,6 +36,7 @@ python = "^3.7" importlib_metadata = {version = "^1.6.0", python = "<3.8"} typer = {extras = ["all"], version = "^0.3.2"} rich = "^9.8.2" +beautifulsoup4 = "^4.9.0" cursor = "^1.3.4" requests = "^2.25.0" diff --git a/xvideos_dl/__main__.py b/xvideos_dl/__main__.py index 8de4d22..6a264a0 100644 --- a/xvideos_dl/__main__.py +++ b/xvideos_dl/__main__.py @@ -7,7 +7,16 @@ from cursor import HiddenCursor from rich.console import Console from xvideos_dl import __version__ -from xvideos_dl.xvideos_dl import download, get_videos_by_playlist_id, get_videos_from_play_page, parse_playlist_id +from xvideos_dl.xvideos_dl import ( + Process, + download, + get_videos_by_playlist_id, + get_videos_from_play_page, + get_videos_from_user_page, + parse_playlist_id, +) + +from . import constant as c app = typer.Typer( name="xvideos-dl", @@ -24,18 +33,19 @@ def version_callback(value: bool): raise typer.Exit() -@app.command(name="") +@app.command(name="CLI to download videos from https://xvideos.com") def main( - url: List[str] = typer.Argument(..., help="URL of the video web page."), - playlist: bool = typer.Option(False, "-p", "--playlist", help="Download videos from playlist web page."), + urls: List[str] = typer.Argument(..., help="URL of the video web page."), dest: str = typer.Option( "./xvideos", "-d", "--destination", help="Destination to save the downloaded videos.", ), + max: int = typer.Option(None, "-n", "--maximum", help="Maximum videos to download."), + reversed: bool = typer.Option(False, "-r", "--reversed", help="Download videos in reverse order."), low: bool = typer.Option(False, "-l", "--low-definition", help="Download low definition videos."), - overwrite: bool = typer.Option(False, "-O", "--overwrite", help="Overwrite the exist video files."), + overwrite: bool = typer.Option(False, "-o", "--overwrite", help="Overwrite the exist video files."), version: bool = typer.Option( None, "-v", @@ -46,19 +56,35 @@ def main( ), ): """CLI to download videos from https://xvideos.com""" - videos = [] - if playlist: - pids = [parse_playlist_id(u) for u in url] - for pid in pids: - vs = get_videos_by_playlist_id(pid) - videos.extend(vs) - else: - for page_url in url: - videos.append(get_videos_from_play_page(page_url)) + videos_to_download = [] + for url in urls: + if "/profiles/" in url: + videos = [] + videos = get_videos_from_user_page(url, "0", c.USER_UPLOAD_API, videos) + videos_to_download.extend(videos) + elif "/channels/" in url: + videos = [] + videos = get_videos_from_user_page(url, "0", c.CHANNEL_API, videos) + videos_to_download.extend(videos) + elif "/favorite/" in url: + pid = parse_playlist_id(url) + videos = get_videos_by_playlist_id(pid) + videos_to_download.extend(videos) + else: + video = get_videos_from_play_page(url) + videos_to_download.append(video) + + if reversed: + videos_to_download = videos_to_download[::-1] + if max: + videos_to_download = videos_to_download[:max] - for video in videos: + total = len(videos_to_download) + for idx, video in enumerate(videos_to_download): try: with HiddenCursor(): + process = Process(idx + 1, total) + console.print(f"Downloading: [cyan]{process.status()}[/]") download(video, dest, low, overwrite) except Exception as e: console.print(f"[red]{e}[/]") diff --git a/xvideos_dl/constant.py b/xvideos_dl/constant.py index f97662c..d5dccee 100644 --- a/xvideos_dl/constant.py +++ b/xvideos_dl/constant.py @@ -1,7 +1,40 @@ APP_NAME = "xvideos-dl" FAVORITE_API = "https://www.xvideos.com/api/playlists/alpha" +USER_UPLOAD_API = "https://www.xvideos.com/profiles/{u}/activity/{aid}" PLAYLIST_API = "https://www.xvideos.com/api/playlists/list/{pid}" +CHANNEL_API = "https://www.xvideos.com/channels/{u}/activity/straight/{aid}" VIDEO_API = "https://www.xvideos.com/video-download/{vid}/" -TIMEOUT = 10 # seconds +TIMEOUT = 15 # seconds FRAGMENT_SIZE = 1024 ** 2 * 16 # 16MB CHUNK_SIZE = 1024 * 4 # 4KB + +SMILE_EMOJIS = [ + "😁", + "😀", + "😂", + "🤣", + "😃", + "😄", + "😅", + "😆", + "😉", + "😊", + "😋", + "😎", + "😍", + "😘", + "😗", + "😙", + "😚", + "🙂", + "🤗", + "🤔", + "😐", + "😑", + "😏", + "😛", + "😜", + "😝", + "🤤", + # "☯", +] diff --git a/xvideos_dl/xvideos_dl.py b/xvideos_dl/xvideos_dl.py index 7d78f33..5abc823 100644 --- a/xvideos_dl/xvideos_dl.py +++ b/xvideos_dl/xvideos_dl.py @@ -1,12 +1,16 @@ from typing import Any, Dict, List import html +import random import re import time from collections import namedtuple +from dataclasses import dataclass +from functools import wraps from pathlib import Path -from requests import Session +from bs4 import BeautifulSoup +from requests import Response, Session from requests.cookies import cookiejar_from_dict from rich.console import Console @@ -14,7 +18,46 @@ console = Console() session = Session() -Video = namedtuple("Video", "vid vname pname") +Video = namedtuple("Video", "vid vname pname uname") + + +@dataclass +class Process: + now: int = 1 + total: int = 1 + + def status(self): + if self.total > 1: + return f"{self.now}/{self.total} ({self.now / self.total * 100:.0f}%)" + # return "☯" + return "⚓" + + +def retry(exceptions=Exception, tries=3, delay=1, backoff=2): + def deco_retry(f): + @wraps(f) + def f_retry(*args, **kwargs): + mtries, mdelay = tries, delay + while mtries > 1: + try: + return f(*args, **kwargs) + except exceptions as e: + console.print(f"[red]{e}, Retrying in {mdelay} seconds...[/]") + time.sleep(mdelay) + mtries -= 1 + mdelay *= backoff + return f(*args, **kwargs) + + return f_retry + + return deco_retry + + +@retry() +def session_request(method: str, url: str, **kwargs) -> Response: + resp = session.request(method, url, **kwargs) + resp.raise_for_status() + return resp def parse_cookies(cookie: str) -> Dict[str, str]: @@ -27,7 +70,7 @@ def parse_cookies(cookie: str) -> Dict[str, str]: def save_cookie(cookie: str) -> None: cache_dir = Path.home() / f".{c.APP_NAME}" - cache_dir.mkdir(exist_ok=True) + cache_dir.mkdir(parents=True, exist_ok=True) with open(cache_dir / "cookie", "w") as f: f.write(cookie) @@ -49,15 +92,19 @@ def find_from_string(pattern: str, string: str) -> str: def parse_video_id(index: str) -> str: - return find_from_string(r"(?<=video)\d+(?=/)", index) + return find_from_string(r"(?<=video)\d+(?=/)", index.strip()) def parse_video_name(index: str) -> str: - return find_from_string(r"(?<=\d/).+(?=[/])*", index) + return find_from_string(r"(?<=\d/).+(?=[/])*", index.strip()) + + +def parse_username(index: str) -> str: + return find_from_string(r"(?<=profiles/|channels/)\w*(?=/*)", index.strip()) def parse_playlist_id(index: str) -> str: - return find_from_string(r"(?<=/favorite/)\d+(?=/)", index) + return find_from_string(r"(?<=/favorite/)\d+(?=/)", index.strip()) def safe_filename(filename: str) -> str: @@ -65,7 +112,7 @@ def safe_filename(filename: str) -> str: def get_video_full_name(index: str) -> str: - resp = session.get(index, timeout=c.TIMEOUT) + resp = session_request("GET", index, timeout=c.TIMEOUT) resp.raise_for_status() title_tab = re.search(r'(?<=)', resp.text) if title_tab: @@ -79,7 +126,7 @@ def request_with_cookie(method: str, url: str, return_when: str) -> Dict[str, An while 1: session.cookies = cookiejar_from_dict(cookies) - resp = session.request(method, url, timeout=c.TIMEOUT) + resp = session_request(method, url, timeout=c.TIMEOUT) resp.raise_for_status() data = resp.json() keys = return_when.split(".") @@ -112,7 +159,25 @@ def get_video_url(vid: str, low: bool = False) -> str: def get_videos_from_play_page(page_url: str) -> Video: vid = parse_video_id(page_url) vname = get_video_full_name(page_url) or parse_video_name(page_url) - return Video(vid=vid, vname=vname, pname="") + return Video(vid=vid, vname=vname, pname="", uname="") + + +def get_videos_from_user_page(page_url: str, aid: str, base_api: str, videos: List[Video]) -> List[Video]: + username = parse_username(page_url) + start_page = base_api.format(u=username, aid=aid) + resp = session_request("POST", start_page, timeout=c.TIMEOUT) + resp.raise_for_status() + next_aid = find_from_string(r"\d+", resp.text.splitlines()[0]) + + bs = BeautifulSoup(resp.text, "html.parser") + blocks = bs.find_all("div", class_="thumb-block") + for block in blocks: + vid = block.attrs.get("data-id") + vname = block.find("p", class_="title").find("a").attrs.get("title") + videos.append(Video(vid=vid, vname=vname, pname="", uname=username)) + if int(next_aid) > 0: + return get_videos_from_user_page(page_url, next_aid, base_api, videos) + return videos def get_videos_by_playlist_id(pid: str) -> List[Video]: @@ -122,7 +187,7 @@ def get_videos_by_playlist_id(pid: str) -> List[Video]: videos_info = data.get("list", {}).get("videos") videos = [] for v in videos_info: - videos.append(Video(vid=v.get("id"), vname=v.get("tf"), pname=playlist_name)) + videos.append(Video(vid=v.get("id"), vname=v.get("tf"), pname=playlist_name, uname="")) return videos @@ -130,13 +195,13 @@ def get_videos_by_playlist_id(pid: str) -> List[Video]: def download(video: Video, dest: str, low: bool, overwrite: bool) -> None: url = get_video_url(video.vid, low) - save_dir = Path(dest) / video.pname - save_dir.mkdir(exist_ok=True) + save_dir = Path(dest) / (video.pname or video.uname) + save_dir.mkdir(parents=True, exist_ok=True) save_name = save_dir / f"{video.vname}(#{video.vid}).mp4" - head = session.head(url, stream=True) + head = session_request("HEAD", url, stream=True) size = int(head.headers["Content-Length"].strip()) - console.print(f"Video ID : [cyan]{video.vid}[/]") + console.print(f"Video ID : [white]{video.vid}[/]") console.print(f"Video Name : [yellow]{video.vname}[/]") console.print(f"Video Link : [underline]{url}[/]") console.print(f"Video Size : [white]{size / 1024 ** 2:.2f}[/] MB") @@ -145,7 +210,7 @@ def download(video: Video, dest: str, low: bool, overwrite: bool) -> None: done = 0 if save_name.is_file(): if overwrite: - save_name.unlink(missing_ok=True) + save_name.unlink() else: done = save_name.stat().st_size @@ -154,6 +219,7 @@ def download(video: Video, dest: str, low: bool, overwrite: bool) -> None: show_process_bar = True print() + emoji = random.choice(c.SMILE_EMOJIS) while done < size: time_start = time.time() start = done @@ -162,7 +228,7 @@ def download(video: Video, dest: str, low: bool, overwrite: bool) -> None: done = size end = done headers = {"Range": f"bytes={start}-{end - 1}"} - resp = session.get(url, stream=True, headers=headers, timeout=c.TIMEOUT) + resp = session_request("GET", url, stream=True, headers=headers, timeout=c.TIMEOUT) with open(save_name, "ab") as f: write = start @@ -172,7 +238,9 @@ def download(video: Video, dest: str, low: bool, overwrite: bool) -> None: write += c.CHUNK_SIZE percent_done = int(min(write, size) / size * 1000) / 10 bar_done = int(percent_done * 0.6) - console.print(f"|{'█' * bar_done}{' ' * (60 - bar_done)}| [green]{percent_done:5.1f}%[/]", end="\r") + console.print( + f"{emoji} |{'█' * bar_done}{' ' * (60 - bar_done)}| [green]{percent_done:5.1f}%[/]", end="\r" + ) # Download speed speed = (end - start) / (time.time() - time_start) if speed < 1024: # 1KB @@ -182,7 +250,7 @@ def download(video: Video, dest: str, low: bool, overwrite: bool) -> None: else: speed_text = f"{speed / 1048576:7.2f}MB/s" console.print( - f"|{'█' * bar_done}{' ' * (60 - bar_done)}| [green]{percent_done:5.1f}% {speed_text}[/]", end="\r" + f"{emoji} |{'█' * bar_done}{' ' * (60 - bar_done)}| [green]{percent_done:5.1f}% {speed_text}[/]", end="\r" ) if show_process_bar: