mirror of
https://github.com/neosubhamoy/pytubepp.git
synced 2026-02-04 18:22:23 +05:30
Compare commits
6 Commits
v1.1.8-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
|
||||
|
||||
17
README.md
17
README.md
@@ -33,14 +33,15 @@
|
||||
* [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**
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "pytubepp"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
authors = [
|
||||
{ name="Subhamoy Biswas", email="hey@neosubhamoy.com" },
|
||||
]
|
||||
@@ -43,6 +43,7 @@ dependencies = [
|
||||
"tabulate",
|
||||
"tqdm",
|
||||
"appdirs",
|
||||
"rich",
|
||||
"setuptools",
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -304,9 +310,9 @@ class YouTubeDownloader:
|
||||
|
||||
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):
|
||||
@@ -431,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)
|
||||
@@ -443,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)')
|
||||
@@ -460,7 +466,7 @@ 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)
|
||||
@@ -481,7 +487,7 @@ def main():
|
||||
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)')
|
||||
@@ -525,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':
|
||||
|
||||
@@ -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