mirror of
https://github.com/neosubhamoy/pytubepp.git
synced 2026-02-05 02:32:23 +05:30
Compare commits
13 Commits
v1.1.7-sta
...
main
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@@ -1,7 +1,7 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*.*.*-*'
|
||||
|
||||
name: 🚀 Publish to PyPI
|
||||
jobs:
|
||||
|
||||
43
.github/workflows/release.yml
vendored
Normal file
43
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*-*'
|
||||
|
||||
name: 🚀 Release on GitHub
|
||||
jobs:
|
||||
release:
|
||||
name: 🎉 Release on GitHub
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🚚 Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🐍 Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: 📦 Install dependencies
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: 🛠️ Build package
|
||||
run: python3 -m build
|
||||
|
||||
- name: "✏️ Generate release changelog"
|
||||
id: gen-changelog
|
||||
uses: janheinrichmerker/action-github-changelog-generator@v2.3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: 🚀 Publish release to GitHub
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: ${{github.event.repository.name}}-${{github.ref_name}}
|
||||
body: ${{ steps.gen-changelog.outputs.changelog }}
|
||||
files: dist/*
|
||||
draft: false
|
||||
prerelease: false
|
||||
make_latest: true
|
||||
|
||||
60
README.md
60
README.md
@@ -9,13 +9,13 @@
|
||||
[](https://github.com/neosubhamoy/pytubepp/)
|
||||
[](https://github.com/neosubhamoy/pytubepp/)
|
||||
|
||||
😀 GOOD NEWS: If you are not a power user and don't want to bother remembering PytubePP Commands! (You are not familier with Command Line Tools). We recently released a Browser Extension that can auto detect YouTube Videos and You can download the Video in one click directly from the browser using PytubePP CLI. Install [PytubePP Helper](https://github.com/neosubhamoy/pytubepp-helper) app in your System and add [PytubePP Extension](https://github.com/neosubhamoy/pytubepp-extension) in your Browser to get started.
|
||||
😀 NEWS: If you are not a power user and don't want to bother remembering PytubePP Commands! (You are not familier with Command Line Tools). We recently released a Browser Extension that can auto detect YouTube Videos and You can download the Video in one click directly from the browser using PytubePP CLI. Install [PytubePP Helper](https://github.com/neosubhamoy/pytubepp-helper) app in your System and add [PytubePP Extension](https://github.com/neosubhamoy/pytubepp-extension) in your Browser to get started.
|
||||
|
||||
> **🥰 Liked this project? Please consider giving it a Star (🌟) on github to show us your appreciation and help the algorythm recommend this project to even more awesome people like you!**
|
||||
|
||||
### **💻 Supported Platforms**
|
||||
- Windows (10 / 11)
|
||||
- Linux (Debian, Fedora, Arch)
|
||||
- Windows
|
||||
- Linux
|
||||
- MacOS
|
||||
- Android (Termux)
|
||||
|
||||
@@ -30,19 +30,25 @@
|
||||
### **📎 Pre-Requirements**
|
||||
* [Python](https://www.python.org/downloads/) (>=3.8)
|
||||
* [FFmpeg](https://ffmpeg.org/)
|
||||
* [Node.js](https://nodejs.org/en/download/) (required for auto YT poToken genration which is currently not possible in Python environment)
|
||||
* [Node.js](https://nodejs.org/en/download/)
|
||||
|
||||
### **🧩 Python Dependencies**
|
||||
* [pytubefix](https://pypi.org/project/pytubefix/)
|
||||
* [ffmpy](https://pypi.org/project/ffmpy/)
|
||||
* [mutagen](https://pypi.org/project/mutagen/)
|
||||
* [tabulate](https://pypi.org/project/tabulate/)
|
||||
* [tqdm](https://pypi.org/project/tqdm/)
|
||||
* [appdirs](https://pypi.org/project/appdirs/)
|
||||
* [requests](https://pypi.org/project/requests/)
|
||||
* [setuptools](https://pypi.org/project/setuptools/)
|
||||
* [pytubefix](https://pypi.org/project/pytubefix/),
|
||||
[ffmpy](https://pypi.org/project/ffmpy/),
|
||||
[mutagen](https://pypi.org/project/mutagen/),
|
||||
[tabulate](https://pypi.org/project/tabulate/),
|
||||
[tqdm](https://pypi.org/project/tqdm/),
|
||||
[appdirs](https://pypi.org/project/appdirs/),
|
||||
[requests](https://pypi.org/project/requests/),
|
||||
[rich](https://pypi.org/project/rich/),
|
||||
[setuptools](https://pypi.org/project/setuptools/)
|
||||
|
||||
### **🛠️ Installation**
|
||||
|
||||
Open a Terminal/CMD (CLI) window and run the following commands one after one step by step (based on your OS) to install 'PytubePP' in your system!
|
||||
|
||||
> If you want to follow along make sure [WinGet](https://learn.microsoft.com/en-us/windows/package-manager/winget/#install-winget) is enabled if you are on Windows (verify using `winget --version`), install [Homebrew](https://brew.sh/) if you are on MacOS and install [Termux](https://termux.dev/) if you are on Android
|
||||
|
||||
1. Install Python and PIP
|
||||
- Linux (Debian): Python is pre-installed install PIP using `sudo apt install python3-pip`<br>
|
||||
- Linux (Fedora): Python is pre-installed install PIP using `sudo dnf install python3-pip`<br>
|
||||
@@ -79,12 +85,14 @@ pip install pytubepp
|
||||
|
||||
**UPDATE: Always make sure 'PytubePP' and 'Pytubefix' is on the latest version to avoid issues (update them at least once a week) (Use the command below to update)**
|
||||
|
||||
```
|
||||
```terminal
|
||||
pip install pytubefix pytubepp --upgrade
|
||||
```
|
||||
|
||||
> It is highly recommended to run the post install script once after updating 'PytubePP' using: `pytubepp --postinstall` command
|
||||
|
||||
**UNINSTALL: If you want to uninstall PytubePP (Use the command below to uninstall) NOTE: it will only remove the 'PytubePP' python package**
|
||||
```
|
||||
```terminal
|
||||
pip uninstall pytubepp -y
|
||||
```
|
||||
|
||||
@@ -114,7 +122,14 @@ pytubepp "https://youtube.com/watch?v=2lAe1cqCOXo" -s mp3
|
||||
```terminal
|
||||
pytubepp "https://youtube.com/watch?v=2lAe1cqCOXo" -i
|
||||
```
|
||||
* To cancel/stop an ongoing download press `CTRL` + `C` on keyboard (it is recommended to run the `-ct` flag once after canceling an ongoing download).
|
||||
* To cancel/stop an ongoing download press `CTRL` + `C` on keyboard (it is highly recommended to run the `pytubepp -ct` command once after canceling an ongoing download).
|
||||
|
||||
* To set default stream (suppose 1080p) use: `pytubepp -ds 1080p` command (This is useful when you always preffer to download this stream even if higher resolution stream is available. If You set default stream then next time when you download, You don't need to pass the `-s 1080p` flag, just pass the video url and it will auto select the `1080p` stream by default).
|
||||
|
||||
* To set default caption (suppose en - English) use: `pytubepp -dc en` command (Useful when you always preffer to embed caption in videos and in a specific language, If You set default caption, You don't need to pass `-c en` flag in next downloads just pass the video url).
|
||||
|
||||
* You can also view all these current configurations using: `pytubepp -sc` command and reset them using: `pytubepp -r` if needed.
|
||||
|
||||
* List of all available flags are given below:
|
||||
|
||||
| Short Flag | Flag | Usage | Requires Parameter | Requires URL | Parameters | Default |
|
||||
@@ -128,10 +143,10 @@ pytubepp "https://youtube.com/watch?v=2lAe1cqCOXo" -i
|
||||
| -ds | --default-stream | Set default download stream | YES | NO | `144p` `240p` `360p` `480p` `720p` `1080p` `1440p` `2160p` `4320p` `mp3` `max` (Pass any one of them) | `max` |
|
||||
| -dc | --default-caption | Set default caption | YES | NO | All [ISO 639-1 Language Codes](https://www.w3schools.com/tags/ref_language_codes.asp) + auto generated ones + `none` for No Caption (Pass any one of them) eg: `en` for English | `none` |
|
||||
| -df | --download-folder | Set custom download folder path | YES | NO | Use the full path excluding the last trailing slash within double quotes eg(in Linux): `"/path/to/folder"` (Make sure the folder path you enterted is already created and accessable) | Within `PytubePP Downloads` folder in your System's `Downloads` folder |
|
||||
| -r | --reset-default | Reset to default configuration (Download Folder, Default Stream) | NO | NO | No parameters | No default |
|
||||
| -r | --reset-default | Reset to default configuration (Download Folder, Default Stream, Default Caption) | NO | NO | No parameters | No default |
|
||||
| -sc | --show-config | Show all current user configurations | NO | NO | No parameters | No default |
|
||||
| -ct | --clear-temp | Clear temporary files (audio, video, thumbnail) of the failed, incomplete downloads | NO | NO | No parameters | No default |
|
||||
| -pi | --postinstall | Auto install all external dependencies (FFmpeg, Node.js) (in Windows, Linux - debian fedora arch, MacOS) | NO | NO | No parameters | No default |
|
||||
| -ct | --clear-temp | Clear temporary files (audio, video, thumbnail, caption) of the failed, incomplete downloads | NO | NO | No parameters | No default |
|
||||
| -pi | --postinstall | Auto install all external dependencies (FFmpeg, Node.js) (works in Windows, Linux - debian fedora arch, MacOS) | NO | NO | No parameters | No default |
|
||||
|
||||
### 🛠️ Contributing / Building from Source
|
||||
|
||||
@@ -144,19 +159,20 @@ Want to be part of this? Feel free to contribute...!! Pull Requests are always w
|
||||
|
||||
> Use `python3` and `pip3` commands instead of `python` and `pip` if you are on Linux or MacOS.
|
||||
|
||||
3. Install python dependencies
|
||||
3. Create a Python virtual environment (venv) and activate it (Optional)
|
||||
4. Install python dependencies
|
||||
|
||||
```terminal
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
4. build, install and test the module
|
||||
5. build, install and test the module
|
||||
|
||||
```terminal
|
||||
python -m build // build the module
|
||||
python -m build // build the module
|
||||
|
||||
pip install .\dist\pytubepp-<version>-py3-none-any.whl // install the module (give the path to the newly genrated whl file based on your OS path style and don't forget to replace the <version> with the actual version number)
|
||||
```
|
||||
5. Do the changes, Send a Pull Request with proper Description (NOTE: Pull Requests Without Proper Description will be Rejected)
|
||||
6. Do the changes, Send a Pull Request with proper Description (NOTE: Pull Requests Without Proper Description will be Rejected)
|
||||
|
||||
⭕ Noticed any Bugs? or Want to give me some suggetions? always feel free to open an issue...!!
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "pytubepp"
|
||||
version = "1.1.7"
|
||||
version = "1.1.9"
|
||||
authors = [
|
||||
{ name="Subhamoy Biswas", email="hey@neosubhamoy.com" },
|
||||
]
|
||||
@@ -43,6 +43,7 @@ dependencies = [
|
||||
"tabulate",
|
||||
"tqdm",
|
||||
"appdirs",
|
||||
"rich",
|
||||
"setuptools",
|
||||
]
|
||||
|
||||
|
||||
107
pytubepp/main.py
107
pytubepp/main.py
@@ -1,9 +1,10 @@
|
||||
from pytubefix import YouTube
|
||||
from tabulate import tabulate
|
||||
from rich import print as rprint
|
||||
from .config import get_temporary_directory, load_config, update_config, reset_config
|
||||
from .download import download_progressive, download_nonprogressive, download_audio, progress
|
||||
from .postprocess import merge_audio_video, convert_to_mp3
|
||||
from .utils import get_version, clear_temp_files, is_valid_url, network_available, ffmpeg_installed, nodejs_installed, unpack_caption
|
||||
from .utils import get_version, clear_temp_files, is_valid_url, network_available, ffmpeg_installed, nodejs_installed, unpack_caption, check_update
|
||||
from .postinstaller import postinstall
|
||||
import appdirs, os, re, sys, argparse, json
|
||||
|
||||
@@ -45,13 +46,18 @@ class YouTubeDownloader:
|
||||
sys.exit()
|
||||
|
||||
if not nodejs_installed():
|
||||
print("\nWarning: Node.js is not installed or not found in PATH!")
|
||||
rprint("\n[dark_orange]WARNING:[/dark_orange] Node.js is not installed or not found in PATH!")
|
||||
print("BotGuard poToken generation will not work properly without Node.js environment")
|
||||
print("Please install Node.js, by running: pytubepp --postinstall or read https://github.com/neosubhamoy/pytubepp#%EF%B8%8F-installation for manual instructions\n")
|
||||
rprint("Please install Node.js, by running: [green]pytubepp --postinstall[/green] or read [steel_blue3]https://github.com/neosubhamoy/pytubepp#%EF%B8%8F-installation[/steel_blue3] for manual instructions\n")
|
||||
|
||||
update = check_update()
|
||||
if update[0]:
|
||||
rprint(f'\n[blue]NOTE:[/blue] A newer version of pytubepp is available! ([dark_orange]v{update[1]}[/dark_orange] -> [light_green]v{update[2]}[/light_green])')
|
||||
rprint(f'Please upgrade to the latest version using: [green]{update[3]}[/green]')
|
||||
|
||||
if is_valid_url(link):
|
||||
link = is_valid_url(link).group(1)
|
||||
self.video = YouTube(link, 'WEB', on_progress_callback=progress)
|
||||
self.video = YouTube(link, on_progress_callback=progress)
|
||||
self.author = self.video.author
|
||||
self.title = re.sub(r'[\\/*?:"<>|]', '_', self.author + ' - ' + self.video.title)
|
||||
self.thumbnail = self.video.thumbnail_url
|
||||
@@ -159,7 +165,7 @@ class YouTubeDownloader:
|
||||
print('Sorry, No video streams found....!!!')
|
||||
sys.exit()
|
||||
|
||||
print(f'\nTitle: {self.video.title}\nAuthor: {self.author}\nPublished On: {self.video.publish_date.strftime("%d/%m/%Y")}\nDuration: {f"{self.video.length//3600:02}:{(self.video.length%3600)//60:02}:{self.video.length%60:02}" if self.video.length >= 3600 else f"{(self.video.length%3600)//60:02}:{self.video.length%60:02}"}\nViews: {self.views}\nCaptions: {"Available" if self.captions else "Unavailable"}')
|
||||
print(f'\nTitle: {self.video.title}\nAuthor: {self.author}\nPublished On: {self.video.publish_date.strftime("%d/%m/%Y") if self.video.publish_date else "Unknown"}\nDuration: {f"{self.video.length//3600:02}:{(self.video.length%3600)//60:02}:{self.video.length%60:02}" if self.video.length >= 3600 else f"{(self.video.length%3600)//60:02}:{self.video.length%60:02}"}\nViews: {self.views}\nCaptions: {"Available" if self.captions else "Unavailable"}')
|
||||
|
||||
print('\n')
|
||||
print(tabulate(table, headers=['Stream', 'Alias (for -s flag)', 'Format', 'Size', 'FrameRate', 'V-Codec', 'A-Codec', 'V-BitRate', 'A-BitRate']))
|
||||
@@ -237,7 +243,7 @@ class YouTubeDownloader:
|
||||
'author': self.author,
|
||||
'thumbnail_url': self.thumbnail,
|
||||
'views': self.video.views,
|
||||
'published_on': self.video.publish_date.strftime('%d/%m/%Y'),
|
||||
'published_on': self.video.publish_date.strftime('%d/%m/%Y') if self.video.publish_date else None,
|
||||
'duration': self.video.length,
|
||||
'streams': streams_list,
|
||||
'captions': captions_list or None
|
||||
@@ -271,11 +277,11 @@ class YouTubeDownloader:
|
||||
print(f'\nTitle: {self.title}')
|
||||
|
||||
if chosen_stream == 'mp3':
|
||||
print(f'Selected: Audio [128kbps (140)]')
|
||||
print(f'Selected: Audio [128kbps (140)] --> (MP3)')
|
||||
return
|
||||
|
||||
if chosen_stream in ['360', '360p']:
|
||||
print(f"Selected: Video [360p (18)] + Audio [96kbps (18)]{f' + Caption [{chosen_caption}]' if chosen_caption else ''}")
|
||||
print(f"Selected: Video [360p (18)] + Audio [96kbps (18)]{f' + Caption [{chosen_caption}]' if chosen_caption else ''} --> (MP4)")
|
||||
return
|
||||
|
||||
_select_suitable_audio_stream = lambda stream: 251 if stream.mime_type == 'video/webm' else 140
|
||||
@@ -288,15 +294,25 @@ class YouTubeDownloader:
|
||||
hdr_stream = self.stream.get_by_itag(hdr_itags.get(res))
|
||||
|
||||
matching_stream = hdr_stream if hdr_stream else self.stream.filter(res=res).first()
|
||||
|
||||
if res == '720p' and not hdr_stream:
|
||||
high_fps_stream = self.stream.get_by_itag(298)
|
||||
if high_fps_stream:
|
||||
matching_stream = high_fps_stream
|
||||
|
||||
audio_stream = self.stream.get_by_itag(_select_suitable_audio_stream(matching_stream))
|
||||
|
||||
print(f"Selected: Video [{res} ({matching_stream.itag})] + Audio [{audio_stream.abr} ({audio_stream.itag})]{f' + Caption [{chosen_caption}]' if chosen_caption else ''}")
|
||||
output_format = 'MP4'
|
||||
if matching_stream.mime_type == 'video/webm':
|
||||
output_format = 'WEBM'
|
||||
|
||||
print(f"Selected: Video [{res} ({matching_stream.itag})] + Audio [{audio_stream.abr} ({audio_stream.itag})]{f' + Caption [{chosen_caption}]' if chosen_caption else ''} --> ({output_format})")
|
||||
|
||||
def download_stream(self, link, chosen_stream, chosen_caption=None):
|
||||
if not ffmpeg_installed():
|
||||
print("\nWarning: FFmpeg is not installed or not found in PATH!")
|
||||
rprint("\n[dark_orange]WARNING:[/dark_orange] FFmpeg is not installed or not found in PATH!")
|
||||
print("Some core functionalities like video processing will not work properly without FFmpeg")
|
||||
print("Please install FFmpeg, by running: pytubepp --postinstall or read https://github.com/neosubhamoy/pytubepp#%EF%B8%8F-installation for manual instructions\n")
|
||||
rprint("Please install FFmpeg, by running: [green]pytubepp --postinstall[/green] or read [steel_blue3]https://github.com/neosubhamoy/pytubepp#%EF%B8%8F-installation[/steel_blue3] for manual instructions\n")
|
||||
sys.exit()
|
||||
|
||||
if self.set_video_info(link):
|
||||
@@ -421,10 +437,10 @@ def main():
|
||||
|
||||
# Handle info display flags
|
||||
if args.show_info:
|
||||
print('Loading...')
|
||||
rprint('Loading...')
|
||||
downloader.show_video_info(args.url)
|
||||
if args.list_stream:
|
||||
print('Loading...')
|
||||
rprint('Loading...')
|
||||
downloader.show_all_streams(args.url)
|
||||
if args.raw_info:
|
||||
downloader.show_raw_info(args.url, args.json_prettify)
|
||||
@@ -433,7 +449,7 @@ def main():
|
||||
|
||||
# Handle download cases
|
||||
if hasattr(args, 'stream') and hasattr(args, 'caption'):
|
||||
print('Loading...')
|
||||
rprint('Loading...')
|
||||
if downloader.set_video_info(args.url):
|
||||
if (args.caption not in downloader.captions.keys()) and (args.caption != 'none'):
|
||||
print('\nInvalid caption code or caption not available! Please choose a different caption...!! (use -i to see available captions)')
|
||||
@@ -442,10 +458,7 @@ def main():
|
||||
downloader.download_stream(args.url, args.stream)
|
||||
elif args.stream == 'mp3' and downloader.stream.get_by_itag(140):
|
||||
print(f'\nYou have chosen to download mp3 stream! ( Captioning audio files is not supported )')
|
||||
answer = input('Do you still want to continue downloading ? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you still want to continue downloading ? [yes/no]\n')
|
||||
answer = input('Do you still want to continue downloading? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, args.stream)
|
||||
else:
|
||||
@@ -453,16 +466,13 @@ def main():
|
||||
else:
|
||||
downloader.download_stream(args.url, args.stream, args.caption)
|
||||
elif hasattr(args, 'stream'):
|
||||
print('Loading...')
|
||||
rprint('Loading...')
|
||||
if downloader.set_video_info(args.url):
|
||||
if downloader.default_caption == 'none':
|
||||
downloader.download_stream(args.url, args.stream)
|
||||
elif args.stream == 'mp3' and downloader.stream.get_by_itag(140):
|
||||
print(f'\nYou have chosen to download mp3 stream! ( Captioning audio files is not supported )')
|
||||
answer = input('Do you still want to continue downloading ? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you still want to continue downloading ? [yes/no]\n')
|
||||
answer = input('Do you still want to continue downloading? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, args.stream)
|
||||
else:
|
||||
@@ -471,16 +481,13 @@ def main():
|
||||
downloader.download_stream(args.url, args.stream, downloader.default_caption)
|
||||
else:
|
||||
print(f'\nDefault caption not available! ( Default: {downloader.default_caption} | Available: {[caption.code for caption in downloader.captions.keys()] or "Nothing"} )')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/no]\n')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, args.stream)
|
||||
else:
|
||||
print('Download cancelled! exiting...!!')
|
||||
elif hasattr(args, 'caption'):
|
||||
print('Loading...')
|
||||
rprint('Loading...')
|
||||
if downloader.set_video_info(args.url):
|
||||
if (args.caption not in downloader.captions.keys()) and (args.caption != 'none'):
|
||||
print('\nInvalid caption code or caption not available! Please choose a different caption...!! (use -i to see available captions)')
|
||||
@@ -495,10 +502,7 @@ def main():
|
||||
else:
|
||||
if downloader.maxres:
|
||||
print(f'\nDefault stream not available! ( Default: {downloader.default_stream} | Available: {downloader.maxres} )')
|
||||
answer = input('Do you want to download the maximum available stream ? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you want to download the maximum available stream ? [yes/no]\n')
|
||||
answer = input('Do you want to download the maximum available stream? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, downloader.maxres)
|
||||
else:
|
||||
@@ -509,10 +513,7 @@ def main():
|
||||
downloader.download_stream(args.url, downloader.maxres, args.caption)
|
||||
elif downloader.default_stream == 'mp3' and downloader.stream.get_by_itag(140):
|
||||
print(f'\nDefault stream set to mp3! ( Captioning audio files is not supported )')
|
||||
answer = input('Do you still want to continue downloading ? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you still want to continue downloading ? [yes/no]\n')
|
||||
answer = input('Do you still want to continue downloading? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, downloader.default_stream)
|
||||
else:
|
||||
@@ -522,10 +523,7 @@ def main():
|
||||
else:
|
||||
if downloader.maxres:
|
||||
print(f'\nDefault stream not available! ( Default: {downloader.default_stream} | Available: {downloader.maxres} )')
|
||||
answer = input('Do you want to download the maximum available stream ? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you want to download the maximum available stream ? [yes/no]\n')
|
||||
answer = input('Do you want to download the maximum available stream? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, downloader.maxres, args.caption)
|
||||
else:
|
||||
@@ -533,7 +531,7 @@ def main():
|
||||
else:
|
||||
print('Sorry, No downloadable video stream found....!!!')
|
||||
elif not any([args.show_info, args.raw_info, args.json_prettify, args.list_stream]): # If no info flags are set
|
||||
print('Loading...')
|
||||
rprint('Loading...')
|
||||
if downloader.set_video_info(args.url):
|
||||
if downloader.default_stream == 'max' and downloader.maxres:
|
||||
if downloader.default_caption == 'none':
|
||||
@@ -542,10 +540,7 @@ def main():
|
||||
downloader.download_stream(args.url, downloader.maxres, downloader.default_caption)
|
||||
else:
|
||||
print(f'\nDefault caption not available! ( Default: {downloader.default_caption} | Available: {[caption.code for caption in downloader.captions.keys()] or "Nothing"} )')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/no]\n')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, downloader.maxres)
|
||||
else:
|
||||
@@ -555,10 +550,7 @@ def main():
|
||||
downloader.download_stream(args.url, downloader.default_stream)
|
||||
elif downloader.default_stream == 'mp3' and downloader.stream.get_by_itag(140):
|
||||
print(f'\nDefault stream set to mp3! ( Captioning audio files is not supported )')
|
||||
answer = input('Do you still want to continue downloading ? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you still want to continue downloading ? [yes/no]\n')
|
||||
answer = input('Do you still want to continue downloading? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, downloader.default_stream)
|
||||
else:
|
||||
@@ -567,10 +559,7 @@ def main():
|
||||
downloader.download_stream(args.url, downloader.default_stream, downloader.default_caption)
|
||||
else:
|
||||
print(f'\nDefault caption not available! ( Default: {downloader.default_caption} | Available: {[caption.code for caption in downloader.captions.keys()] or "Nothing"} )')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/no]\n')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, downloader.default_stream)
|
||||
else:
|
||||
@@ -578,10 +567,7 @@ def main():
|
||||
else:
|
||||
if downloader.maxres:
|
||||
print(f'\nDefault stream not available! ( Default: {downloader.default_stream} | Available: {downloader.maxres} )')
|
||||
answer = input('Do you want to download the maximum available stream ? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you want to download the maximum available stream ? [yes/no]\n')
|
||||
answer = input('Do you want to download the maximum available stream? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
if downloader.default_caption == 'none':
|
||||
downloader.download_stream(args.url, downloader.maxres)
|
||||
@@ -589,14 +575,13 @@ def main():
|
||||
downloader.download_stream(args.url, downloader.maxres, downloader.default_caption)
|
||||
else:
|
||||
print(f'\nDefault caption not available! ( Default: {downloader.default_caption} | Available: {[caption.code for caption in downloader.captions.keys()] or "Nothing"} )')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/no]\n')
|
||||
while answer not in ['yes', 'y', 'no', 'n']:
|
||||
print('Invalid answer! try again...!! answer with: [yes/y/no/n]')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/no]\n')
|
||||
answer = input('Do you still want to continue downloading without caption? [yes/No]: ').strip().lower()
|
||||
if answer in ['yes', 'y']:
|
||||
downloader.download_stream(args.url, downloader.maxres)
|
||||
else:
|
||||
print('Download cancelled! exiting...!!')
|
||||
else:
|
||||
print('Download cancelled! exiting...!!')
|
||||
else:
|
||||
print('Sorry, No downloadable video stream found....!!!')
|
||||
else:
|
||||
|
||||
@@ -21,8 +21,8 @@ def postinstall():
|
||||
package_manager = 'winget' # Windows Package Manager
|
||||
else:
|
||||
print("OS: Windows (winget not enabled)")
|
||||
user_input = input("WinGet is not available. Do you want to enable winget? (Make sure to login to Windows before enabling) [yes/no]: ").strip().lower()
|
||||
if user_input in ['yes', 'y']:
|
||||
user_input = input("WinGet is not available. Do you want to enable winget? (Make sure to login to Windows before enabling) [Yes/no]: ").strip().lower()
|
||||
if user_input in ['yes', 'y', '']:
|
||||
print("Enabling winget...")
|
||||
subprocess.run(['powershell', '-Command', 'Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe'])
|
||||
print("WinGet enabled successfully! Please restart your computer and re-run the post install script: pytubepp --postinstall")
|
||||
@@ -43,8 +43,8 @@ def postinstall():
|
||||
print("OS: Linux (dnf)")
|
||||
distro_id = subprocess.run(['grep', '^ID=', '/etc/os-release'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
||||
if distro_id.returncode == 0 and 'fedora' in distro_id.stdout.decode().strip() and ffmpeg_needed:
|
||||
user_input = input("Looks like you are using Fedora. Do you want to enable RPM Fusion free and nonfree repositories? (answer no if already enabled) [yes/no]: ").strip().lower()
|
||||
if user_input in ['yes', 'y']:
|
||||
user_input = input("Looks like you are using Fedora. Do you want to enable RPM Fusion free and nonfree repositories? (answer no if already enabled) [Yes/no]: ").strip().lower()
|
||||
if user_input in ['yes', 'y', '']:
|
||||
print("Enabling RPM Fusion repositories...")
|
||||
fedora_version = subprocess.run(['rpm', '-E', '%fedora'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
||||
if fedora_version.returncode == 0:
|
||||
@@ -70,8 +70,8 @@ def postinstall():
|
||||
package_manager = 'brew' # Homebrew for macOS
|
||||
else:
|
||||
print("OS: MacOS (brew not installed)")
|
||||
user_input = input("Homebrew is not installed. Do you want to install Homebrew? [yes/no]: ").strip().lower()
|
||||
if user_input in ['yes', 'y']:
|
||||
user_input = input("Homebrew is not installed. Do you want to install Homebrew? [Yes/no]: ").strip().lower()
|
||||
if user_input in ['yes', 'y', '']:
|
||||
print("Installing Homebrew...")
|
||||
subprocess.run('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', shell=True)
|
||||
print("Homebrew installation completed! Please restart your mac and re-run the post install script: pytubepp --postinstall")
|
||||
@@ -89,8 +89,8 @@ def postinstall():
|
||||
if nodejs_needed:
|
||||
print("- Node.js")
|
||||
|
||||
user_input = input("Do you want to proceed with the installation? [yes/no]: ").strip().lower()
|
||||
if user_input not in ['yes', 'y']:
|
||||
user_input = input("Do you want to proceed with the installation? [Yes/no]: ").strip().lower()
|
||||
if user_input not in ['yes', 'y', '']:
|
||||
print("Installation aborted! exiting...!!")
|
||||
return
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from importlib.metadata import version
|
||||
from .config import load_config, get_temporary_directory
|
||||
import os, re, subprocess, platform
|
||||
import os, re, subprocess, platform, requests
|
||||
|
||||
userConfig = load_config()
|
||||
downloadDIR = userConfig['downloadDIR']
|
||||
@@ -78,4 +78,33 @@ def clear_temp_files():
|
||||
except Exception as e:
|
||||
print(e)
|
||||
else:
|
||||
print('No temporary files found to clear...!')
|
||||
print('No temporary files found to clear...!')
|
||||
|
||||
def compare_versions(v1: str, v2: str):
|
||||
parts1 = list(map(int, v1.split('.')))
|
||||
parts2 = list(map(int, v2.split('.')))
|
||||
for i in range(max(len(parts1), len(parts2))):
|
||||
part1 = parts1[i] if i < len(parts1) else 0
|
||||
part2 = parts2[i] if i < len(parts2) else 0
|
||||
if part1 > part2:
|
||||
return 1
|
||||
if part1 < part2:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def get_platform_specific_upgrade_command():
|
||||
if platform.system().lower() == 'windows':
|
||||
return 'pip install pytubefix pytubepp --upgrade; pytubepp --postinstall'
|
||||
else:
|
||||
return 'pip3 install pytubefix pytubepp --upgrade && pytubepp --postinstall'
|
||||
|
||||
def check_update():
|
||||
try:
|
||||
response = requests.get('https://pypi.org/pypi/pytubepp/json')
|
||||
if response.status_code != 200:
|
||||
return False, None, None, None
|
||||
latest_version = response.json()['info']['version']
|
||||
current_version = get_version()
|
||||
return compare_versions(current_version, latest_version) == -1, current_version, latest_version, get_platform_specific_upgrade_command()
|
||||
except Exception as e:
|
||||
return False, None, None, None
|
||||
@@ -5,6 +5,7 @@ mutagen
|
||||
tabulate
|
||||
tqdm
|
||||
appdirs
|
||||
rich
|
||||
setuptools
|
||||
wheel
|
||||
twine
|
||||
|
||||
Reference in New Issue
Block a user