1
1
mirror of https://github.com/neosubhamoy/pytubepp.git synced 2026-02-04 18:22:23 +05:30

6 Commits

7 changed files with 108 additions and 27 deletions

View File

@@ -1,7 +1,7 @@
on:
push:
branches:
- main
tags:
- 'v*.*.*-*'
name: 🚀 Publish to PyPI
jobs:

43
.github/workflows/release.yml vendored Normal file
View 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

View File

@@ -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**

View File

@@ -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",
]

View File

@@ -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':

View File

@@ -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

View File

@@ -5,6 +5,7 @@ mutagen
tabulate
tqdm
appdirs
rich
setuptools
wheel
twine