Merge branch 'Arrowar:main' into main

This commit is contained in:
Francesco Grazioso 2025-02-25 14:31:42 +01:00 committed by GitHub
commit b3a89b19cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 184 additions and 141 deletions

View File

@ -13,11 +13,11 @@ on:
- 'false' - 'false'
push: push:
tags: tags:
- "*" - "v*.*"
jobs: jobs:
publish: publish:
if: github.event.inputs.publish_pypi == 'true' if: startsWith(github.ref_name, 'v') && github.event.inputs.publish_pypi == 'true'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -39,6 +39,7 @@ jobs:
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
python -m pip install setuptools wheel twine python -m pip install setuptools wheel twine
- name: Build package - name: Build package
run: python setup.py sdist bdist_wheel run: python setup.py sdist bdist_wheel
@ -49,7 +50,7 @@ jobs:
run: twine upload dist/* run: twine upload dist/*
build: build:
if: github.event.inputs.publish_pypi == 'false' if: startsWith(github.ref_name, 'v') && github.event.inputs.publish_pypi == 'false'
strategy: strategy:
matrix: matrix:
os: [windows-latest, ubuntu-latest, macos-latest] os: [windows-latest, ubuntu-latest, macos-latest]
@ -87,7 +88,7 @@ jobs:
--hidden-import=qbittorrentapi --hidden-import=qbittorrent ` --hidden-import=qbittorrentapi --hidden-import=qbittorrent `
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm ` --hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm `
--hidden-import=m3u8 --hidden-import=psutil --hidden-import=unidecode ` --hidden-import=m3u8 --hidden-import=psutil --hidden-import=unidecode `
--hidden-import=jsbeautifier --hidden-import=pathvalidate ` --hidden-import=jsbeautifier --hidden-import=six --hidden-import=pathvalidate `
--hidden-import=Cryptodome.Cipher --hidden-import=Cryptodome.Cipher.AES ` --hidden-import=Cryptodome.Cipher --hidden-import=Cryptodome.Cipher.AES `
--hidden-import=Cryptodome.Util --hidden-import=Cryptodome.Util.Padding ` --hidden-import=Cryptodome.Util --hidden-import=Cryptodome.Util.Padding `
--hidden-import=Cryptodome.Random --hidden-import=Pillow ` --hidden-import=Cryptodome.Random --hidden-import=Pillow `
@ -102,7 +103,7 @@ jobs:
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \ --hidden-import=qbittorrentapi --hidden-import=qbittorrent \
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \ --hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
--hidden-import=m3u8 --hidden-import=psutil --hidden-import=unidecode \ --hidden-import=m3u8 --hidden-import=psutil --hidden-import=unidecode \
--hidden-import=jsbeautifier --hidden-import=pathvalidate \ --hidden-import=jsbeautifier --hidden-import=six --hidden-import=pathvalidate \
--hidden-import=Cryptodome.Cipher --hidden-import=Cryptodome.Cipher.AES \ --hidden-import=Cryptodome.Cipher --hidden-import=Cryptodome.Cipher.AES \
--hidden-import=Cryptodome.Util --hidden-import=Cryptodome.Util.Padding \ --hidden-import=Cryptodome.Util --hidden-import=Cryptodome.Util.Padding \
--hidden-import=Cryptodome.Random --hidden-import=Pillow \ --hidden-import=Cryptodome.Random --hidden-import=Pillow \
@ -117,7 +118,7 @@ jobs:
--hidden-import=qbittorrentapi --hidden-import=qbittorrent \ --hidden-import=qbittorrentapi --hidden-import=qbittorrent \
--hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \ --hidden-import=bs4 --hidden-import=httpx --hidden-import=rich --hidden-import=tqdm \
--hidden-import=m3u8 --hidden-import=psutil --hidden-import=unidecode \ --hidden-import=m3u8 --hidden-import=psutil --hidden-import=unidecode \
--hidden-import=jsbeautifier --hidden-import=pathvalidate \ --hidden-import=jsbeautifier --hidden-import=six --hidden-import=pathvalidate \
--hidden-import=Cryptodome.Cipher --hidden-import=Cryptodome.Cipher.AES \ --hidden-import=Cryptodome.Cipher --hidden-import=Cryptodome.Cipher.AES \
--hidden-import=Cryptodome.Util --hidden-import=Cryptodome.Util.Padding \ --hidden-import=Cryptodome.Util --hidden-import=Cryptodome.Util.Padding \
--hidden-import=Cryptodome.Random --hidden-import=Pillow \ --hidden-import=Cryptodome.Random --hidden-import=Pillow \

View File

@ -51,13 +51,16 @@ def title_search(word_to_search: str) -> int:
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]") console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
sys.exit(1) sys.exit(1)
# Construct the full site URL and load the search page search_url = f"{site_constant.FULL_URL}/search/{word_to_search}/1/"
console.print(f"[cyan]Search url: [yellow]{search_url}")
try: try:
response = httpx.get( response = httpx.get(
url=f"{site_constant.FULL_URL}/search/{word_to_search}/1/", url=search_url,
headers={'user-agent': get_userAgent()}, headers={'user-agent': get_userAgent()},
follow_redirects=True, timeout=max_timeout,
timeout=max_timeout verify=site_constant.VERIFY,
follow_redirects=True
) )
response.raise_for_status() response.raise_for_status()

View File

@ -148,7 +148,8 @@ def title_search(title: str) -> int:
cookies=cookies, cookies=cookies,
headers=headers, headers=headers,
json=json_data, json=json_data,
timeout=max_timeout timeout=max_timeout,
verify=site_constant.VERIFY
) )
response.raise_for_status() response.raise_for_status()

View File

@ -51,13 +51,22 @@ def title_search(word_to_search: str) -> int:
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]") console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
sys.exit(1) sys.exit(1)
search_url = f"{site_constant.FULL_URL}/?s={word_to_search}"
console.print(f"[cyan]Search url: [yellow]{search_url}")
try:
response = httpx.get( response = httpx.get(
url=f"{site_constant.FULL_URL}/?s={word_to_search}", url=search_url,
headers={'user-agent': get_userAgent()}, headers={'user-agent': get_userAgent()},
timeout=max_timeout timeout=max_timeout,
verify=site_constant.VERIFY,
follow_redirects=True
) )
response.raise_for_status() response.raise_for_status()
except Exception as e:
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
# Create soup and find table # Create soup and find table
soup = BeautifulSoup(response.text, "html.parser") soup = BeautifulSoup(response.text, "html.parser")

View File

@ -53,12 +53,16 @@ def title_search(word_to_search: str) -> int:
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]") console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
sys.exit(1) sys.exit(1)
# Send request to search for titles search_url = f"{site_constant.FULL_URL}/search/?&q={word_to_search}&quick=1&type=videobox_video&nodes=11"
console.print(f"[cyan]Search url: [yellow]{search_url}")
try: try:
response = httpx.get( response = httpx.get(
url=f"{site_constant.FULL_URL}/search/?&q={word_to_search}&quick=1&type=videobox_video&nodes=11", url=search_url,
headers={'user-agent': get_userAgent()}, headers={'user-agent': get_userAgent()},
timeout=max_timeout timeout=max_timeout,
verify=site_constant.VERIFY,
follow_redirects=True
) )
response.raise_for_status() response.raise_for_status()

View File

@ -51,13 +51,16 @@ def title_search(word_to_search: str) -> int:
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]") console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
sys.exit(1) sys.exit(1)
# Send request to search for titles search_url = f"{site_constant.FULL_URL}/?story={word_to_search}&do=search&subaction=search"
print(f"{site_constant.FULL_URL}/?story={word_to_search}&do=search&subaction=search") console.print(f"[cyan]Search url: [yellow]{search_url}")
try: try:
response = httpx.get( response = httpx.get(
url=f"{site_constant.FULL_URL}/?story={word_to_search}&do=search&subaction=search", url=search_url,
headers={'user-agent': get_userAgent()}, headers={'user-agent': get_userAgent()},
timeout=max_timeout timeout=max_timeout,
verify=site_constant.VERIFY,
follow_redirects=True
) )
response.raise_for_status() response.raise_for_status()

View File

@ -55,11 +55,16 @@ def title_search(title_search: str) -> int:
media_search_manager.clear() media_search_manager.clear()
table_show_manager.clear() table_show_manager.clear()
search_url = f"{site_constant.FULL_URL}/api/search?q={title_search}"
console.print(f"[cyan]Search url: [yellow]{search_url}")
try: try:
response = httpx.get( response = httpx.get(
url=f"{site_constant.FULL_URL}/api/search?q={title_search.replace(' ', '+')}", url=search_url,
headers={'user-agent': get_userAgent()}, headers={'user-agent': get_userAgent()},
timeout=max_timeout timeout=max_timeout,
verify=site_constant.VERIFY,
follow_redirects=True
) )
response.raise_for_status() response.raise_for_status()

View File

@ -15,6 +15,9 @@ from StreamingCommunity.Util.console import console
from StreamingCommunity.Util._jsonConfig import config_manager from StreamingCommunity.Util._jsonConfig import config_manager
# Variable
VERIFY = config_manager.get("REQUESTS", "verify")
def get_tld(url_str): def get_tld(url_str):
"""Extract the TLD (Top-Level Domain) from the URL.""" """Extract the TLD (Top-Level Domain) from the URL."""
@ -79,7 +82,7 @@ def validate_url(url, base_url, max_timeout, max_retries=2, sleep=1):
return False, None return False, None
client = httpx.Client( client = httpx.Client(
verify=False, verify=VERIFY,
headers=get_headers(), headers=get_headers(),
timeout=max_timeout timeout=max_timeout
) )

View File

@ -31,6 +31,10 @@ class SiteConstant:
def ROOT_PATH(self): def ROOT_PATH(self):
return config_manager.get('DEFAULT', 'root_path') return config_manager.get('DEFAULT', 'root_path')
@property
def VERIFY(self):
return config_manager.get('REQUESTS', 'verify')
@property @property
def DOMAIN_NOW(self): def DOMAIN_NOW(self):
return config_manager.get_site(self.SITE_NAME, 'domain') return config_manager.get_site(self.SITE_NAME, 'domain')

View File

@ -18,7 +18,7 @@ from StreamingCommunity.Util._jsonConfig import config_manager
# External libraries # External libraries
from tqdm import tqdm from tqdm import tqdm
from qbittorrent import Client import qbittorrentapi
# Tor config # Tor config
@ -41,15 +41,21 @@ class TOR_downloader:
Parameters: Parameters:
- host (str): IP address or hostname of the qBittorrent Web UI. - host (str): IP address or hostname of the qBittorrent Web UI.
- port (int): Port number of the qBittorrent Web UI. - port (int): Port of the qBittorrent Web UI.
- username (str): Username for logging into qBittorrent. - username (str): Username for accessing qBittorrent.
- password (str): Password for logging into qBittorrent. - password (str): Password for accessing qBittorrent.
""" """
try: try:
console.print(f"[cyan]Connect to: [green]{HOST}:{PORT}") console.print(f"[cyan]Connect to: [green]{HOST}:{PORT}")
self.qb = Client(f'http://{HOST}:{PORT}/') self.qb = qbittorrentapi.Client(
host=HOST,
port=PORT,
username=USERNAME,
password=PASSWORD
)
except: except:
logging.error("Start qbitorrent first.") logging.error("Start qbittorrent first.")
sys.exit(0) sys.exit(0)
self.username = USERNAME self.username = USERNAME
@ -65,7 +71,7 @@ class TOR_downloader:
Logs into the qBittorrent Web UI. Logs into the qBittorrent Web UI.
""" """
try: try:
self.qb.login(self.username, self.password) self.qb.auth_log_in()
self.logged_in = True self.logged_in = True
logging.info("Successfully logged in to qBittorrent.") logging.info("Successfully logged in to qBittorrent.")
@ -74,95 +80,86 @@ class TOR_downloader:
self.logged_in = False self.logged_in = False
def delete_magnet(self, torrent_info): def delete_magnet(self, torrent_info):
"""
Deletes a torrent if it is not downloadable (no seeds/peers).
if (int(torrent_info.get('dl_speed')) == 0 and Parameters:
int(torrent_info.get('peers')) == 0 and - torrent_info: Object containing torrent information obtained from the qBittorrent API.
int(torrent_info.get('seeds')) == 0): """
if (int(torrent_info.dlspeed) == 0 and
# Elimina il torrent appena aggiunto int(torrent_info.num_leechs) == 0 and
console.print(f"[bold red]⚠️ Torrent non scaricabile. Rimozione in corso...[/bold red]") int(torrent_info.num_seeds) == 0):
console.print(f"[bold red]⚠️ Torrent not downloadable. Removing...[/bold red]")
try: try:
# Rimuovi il torrent self.qb.torrents_delete(delete_files=True, torrent_hashes=torrent_info.hash)
self.qb.delete_permanently(torrent_info['hash'])
except Exception as delete_error: except Exception as delete_error:
logging.error(f"Errore durante la rimozione del torrent: {delete_error}") logging.error(f"Error while removing torrent: {delete_error}")
# Resetta l'ultimo hash
self.latest_torrent_hash = None self.latest_torrent_hash = None
def add_magnet_link(self, magnet_link): def add_magnet_link(self, magnet_link):
""" """
Aggiunge un magnet link e recupera le informazioni dettagliate. Adds a magnet link and retrieves detailed torrent information.
Args: Arguments:
magnet_link (str): Magnet link da aggiungere magnet_link (str): Magnet link to add.
Returns: Returns:
dict: Informazioni del torrent aggiunto, o None se fallisce dict: Information about the added torrent, or None in case of error.
""" """
# Estrai l'hash dal magnet link
magnet_hash_match = re.search(r'urn:btih:([0-9a-fA-F]+)', magnet_link) magnet_hash_match = re.search(r'urn:btih:([0-9a-fA-F]+)', magnet_link)
if not magnet_hash_match: if not magnet_hash_match:
raise ValueError("Hash del magnet link non trovato") raise ValueError("Magnet link hash not found")
magnet_hash = magnet_hash_match.group(1).lower() magnet_hash = magnet_hash_match.group(1).lower()
# Estrai il nome del file dal magnet link (se presente) # Extract the torrent name, if available
name_match = re.search(r'dn=([^&]+)', magnet_link) name_match = re.search(r'dn=([^&]+)', magnet_link)
torrent_name = name_match.group(1).replace('+', ' ') if name_match else "Nome non disponibile" torrent_name = name_match.group(1).replace('+', ' ') if name_match else "Name not available"
# Salva il timestamp prima di aggiungere il torrent # Save the timestamp before adding the torrent
before_add_time = time.time() before_add_time = time.time()
# Aggiungi il magnet link console.print(f"[cyan]Adding magnet link ...")
console.print(f"[cyan]Aggiunta magnet link[/cyan]: [red]{magnet_link}") self.qb.torrents_add(urls=magnet_link)
self.qb.download_from_link(magnet_link)
# Aspetta un attimo per essere sicuri che il torrent sia stato aggiunto
time.sleep(1) time.sleep(1)
# Cerca il torrent torrents = self.qb.torrents_info()
torrents = self.qb.torrents()
matching_torrents = [ matching_torrents = [
t for t in torrents t for t in torrents
if (t['hash'].lower() == magnet_hash) or (t.get('added_on', 0) > before_add_time) if (t.hash.lower() == magnet_hash) or (getattr(t, 'added_on', 0) > before_add_time)
] ]
if not matching_torrents: if not matching_torrents:
raise ValueError("Nessun torrent corrispondente trovato") raise ValueError("No matching torrent found")
# Prendi il primo torrent corrispondente
torrent_info = matching_torrents[0] torrent_info = matching_torrents[0]
# Formatta e stampa le informazioni console.print("\n[bold green]🔗 Added Torrent Details:[/bold green]")
console.print("\n[bold green]🔗 Dettagli Torrent Aggiunto:[/bold green]") console.print(f"[yellow]Name:[/yellow] {torrent_info.name or torrent_name}")
console.print(f"[yellow]Name:[/yellow] {torrent_info.get('name', torrent_name)}") console.print(f"[yellow]Hash:[/yellow] {torrent_info.hash}")
console.print(f"[yellow]Hash:[/yellow] {torrent_info['hash']}")
print() print()
# Salva l'hash per usi successivi e il path self.latest_torrent_hash = torrent_info.hash
self.latest_torrent_hash = torrent_info['hash'] self.output_file = torrent_info.content_path
self.output_file = torrent_info['content_path'] self.file_name = torrent_info.name
self.file_name = torrent_info['name']
# Controlla che sia possibile il download # Wait and verify if the download is possible
time.sleep(5) time.sleep(5)
self.delete_magnet(self.qb.get_torrent(self.latest_torrent_hash)) self.delete_magnet(self.qb.torrents_info(torrent_hashes=self.latest_torrent_hash)[0])
return torrent_info return torrent_info
def start_download(self): def start_download(self):
""" """
Starts downloading the latest added torrent and monitors progress. Starts downloading the added torrent and monitors its progress.
""" """
if self.latest_torrent_hash is not None: if self.latest_torrent_hash is not None:
try: try:
# Custom bar for mobile and pc # Custom progress bar for mobile and PC
if USE_LARGE_BAR: if USE_LARGE_BAR:
bar_format = ( bar_format = (
f"{Colors.YELLOW}[TOR] {Colors.WHITE}({Colors.CYAN}video{Colors.WHITE}): " f"{Colors.YELLOW}[TOR] {Colors.WHITE}({Colors.CYAN}video{Colors.WHITE}): "
@ -189,34 +186,41 @@ class TOR_downloader:
with progress_bar as pbar: with progress_bar as pbar:
while True: while True:
# Get variable from qtorrent torrent_info = self.qb.torrents_info(torrent_hashes=self.latest_torrent_hash)[0]
torrent_info = self.qb.get_torrent(self.latest_torrent_hash) self.save_path = torrent_info.save_path
self.save_path = torrent_info['save_path'] self.torrent_name = torrent_info.name
self.torrent_name = torrent_info['name']
# Fetch important variable progress = torrent_info.progress * 100
pieces_have = torrent_info['pieces_have']
pieces_num = torrent_info['pieces_num']
progress = (pieces_have / pieces_num) * 100 if pieces_num else 0
pbar.n = progress pbar.n = progress
download_speed = torrent_info['dl_speed'] download_speed = torrent_info.dlspeed
total_size = torrent_info['total_size'] total_size = torrent_info.size
downloaded_size = torrent_info['total_downloaded'] downloaded_size = torrent_info.downloaded
# Format variable # Format the downloaded size
downloaded_size_str = internet_manager.format_file_size(downloaded_size) downloaded_size_str = internet_manager.format_file_size(downloaded_size)
downloaded_size = downloaded_size_str.split(' ')[0] downloaded_size = downloaded_size_str.split(' ')[0]
# Safely format the total size
total_size_str = internet_manager.format_file_size(total_size) total_size_str = internet_manager.format_file_size(total_size)
total_size = total_size_str.split(' ')[0] total_size_parts = total_size_str.split(' ')
total_size_unit = total_size_str.split(' ')[1] if len(total_size_parts) >= 2:
total_size = total_size_parts[0]
total_size_unit = total_size_parts[1]
else:
total_size = total_size_str
total_size_unit = ""
# Safely format the average download speed
average_internet_str = internet_manager.format_transfer_speed(download_speed) average_internet_str = internet_manager.format_transfer_speed(download_speed)
average_internet = average_internet_str.split(' ')[0] average_internet_parts = average_internet_str.split(' ')
average_internet_unit = average_internet_str.split(' ')[1] if len(average_internet_parts) >= 2:
average_internet = average_internet_parts[0]
average_internet_unit = average_internet_parts[1]
else:
average_internet = average_internet_str
average_internet_unit = ""
# Update the progress bar's postfix
if USE_LARGE_BAR: if USE_LARGE_BAR:
pbar.set_postfix_str( pbar.set_postfix_str(
f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_size} {Colors.WHITE}< {Colors.GREEN}{total_size} {Colors.RED}{total_size_unit} " f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_size} {Colors.WHITE}< {Colors.GREEN}{total_size} {Colors.RED}{total_size_unit} "
@ -231,7 +235,6 @@ class TOR_downloader:
pbar.refresh() pbar.refresh()
time.sleep(0.2) time.sleep(0.2)
# Break at the end
if int(progress) == 100: if int(progress) == 100:
break break
@ -239,7 +242,15 @@ class TOR_downloader:
logging.info("Download process interrupted.") logging.info("Download process interrupted.")
def is_file_in_use(self, file_path: str) -> bool: def is_file_in_use(self, file_path: str) -> bool:
"""Check if a file is in use by any process.""" """
Checks if a file is being used by any process.
Parameters:
- file_path (str): The file path to check.
Returns:
- bool: True if the file is in use, False otherwise.
"""
for proc in psutil.process_iter(['open_files']): for proc in psutil.process_iter(['open_files']):
try: try:
if any(file_path == f.path for f in proc.info['open_files'] or []): if any(file_path == f.path for f in proc.info['open_files'] or []):
@ -251,21 +262,20 @@ class TOR_downloader:
def move_downloaded_files(self, destination: str): def move_downloaded_files(self, destination: str):
""" """
Moves downloaded files of the latest torrent to another location. Moves the downloaded files of the most recent torrent to a new location.
Parameters: Parameters:
- destination (str): Destination directory to move files. - destination (str): Destination folder.
Returns: Returns:
- bool: True if files are moved successfully, False otherwise. - bool: True if the move was successful, False otherwise.
""" """
console.print(f"[cyan]Destination folder: [red]{destination}") console.print(f"[cyan]Destination folder: [red]{destination}")
try: try:
timeout = 5
# Ensure the file is not in use
timeout = 3
elapsed = 0 elapsed = 0
while self.is_file_in_use(self.output_file) and elapsed < timeout: while self.is_file_in_use(self.output_file) and elapsed < timeout:
time.sleep(1) time.sleep(1)
elapsed += 1 elapsed += 1
@ -273,24 +283,20 @@ class TOR_downloader:
if elapsed == timeout: if elapsed == timeout:
raise Exception(f"File '{self.output_file}' is in use and could not be moved.") raise Exception(f"File '{self.output_file}' is in use and could not be moved.")
# Ensure destination directory exists
os.makedirs(destination, exist_ok=True) os.makedirs(destination, exist_ok=True)
# Perform the move operation
try: try:
shutil.move(self.output_file, destination) shutil.move(self.output_file, destination)
except OSError as e: except OSError as e:
if e.errno == 17: # Cross-disk move error if e.errno == 17: # Error when moving between different disks
# Perform copy and delete manually
shutil.copy2(self.output_file, destination) shutil.copy2(self.output_file, destination)
os.remove(self.output_file) os.remove(self.output_file)
else: else:
raise raise
# Delete the torrent data
time.sleep(5) time.sleep(5)
self.qb.delete_permanently(self.qb.torrents()[-1]['hash']) last_torrent = self.qb.torrents_info()[-1]
self.qb.torrents_delete(delete_files=True, torrent_hashes=last_torrent.hash)
return True return True
except Exception as e: except Exception as e:

View File

@ -1,5 +1,5 @@
__title__ = 'StreamingCommunity' __title__ = 'StreamingCommunity'
__version__ = '2.6.0' __version__ = '2.7.0'
__author__ = 'Arrowar' __author__ = 'Arrowar'
__description__ = 'A command-line program to download film' __description__ = 'A command-line program to download film'
__copyright__ = 'Copyright 2024' __copyright__ = 'Copyright 2024'

View File

@ -24,6 +24,7 @@
"telegram_bot": false "telegram_bot": false
}, },
"REQUESTS": { "REQUESTS": {
"verify": false,
"timeout": 20, "timeout": 20,
"max_retry": 8, "max_retry": 8,
"proxy_start_min": 0.1, "proxy_start_min": 0.1,

View File

@ -3,6 +3,7 @@ bs4
rich rich
tqdm tqdm
m3u8 m3u8
certifi
psutil psutil
unidecode unidecode
jsbeautifier jsbeautifier
@ -10,6 +11,4 @@ pathvalidate
pycryptodomex pycryptodomex
ua-generator ua-generator
qbittorrent-api qbittorrent-api
python-qbittorrent
Pillow
pyTelegramBotAPI pyTelegramBotAPI

View File

@ -10,7 +10,7 @@ with open("requirements.txt", "r", encoding="utf-8-sig") as f:
setup( setup(
name="StreamingCommunity", name="StreamingCommunity",
version="2.6.1", version="2.7.0",
long_description=read_readme(), long_description=read_readme(),
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
author="Lovi-0", author="Lovi-0",

View File

@ -46,33 +46,37 @@ def move_content(source: str, destination: str):
def keep_specific_items(directory: str, keep_folder: str, keep_file: str): def keep_specific_items(directory: str, keep_folder: str, keep_file: str):
""" """
Delete all items in the directory except for the specified folder and file. Deletes all items in the given directory except for the specified folder,
the specified file, and the '.git' directory.
Parameters: Parameters:
- directory (str): The path to the directory. - directory (str): The path to the directory.
- keep_folder (str): The name of the folder to keep. - keep_folder (str): The name of the folder to keep.
- keep_file (str): The name of the file to keep. - keep_file (str): The name of the file to keep.
""" """
try:
if not os.path.exists(directory) or not os.path.isdir(directory): if not os.path.exists(directory) or not os.path.isdir(directory):
raise ValueError(f"Error: '{directory}' is not a valid directory.") console.print(f"[red]Error: '{directory}' is not a valid directory.")
return
# Define folders and files to skip
skip_folders = {keep_folder, ".git"}
skip_files = {keep_file}
# Iterate through items in the directory # Iterate through items in the directory
for item in os.listdir(directory): for item in os.listdir(directory):
if item in skip_folders or item in skip_files:
continue
item_path = os.path.join(directory, item) item_path = os.path.join(directory, item)
try:
# Check if the item is the specified folder or file if os.path.isdir(item_path):
if os.path.isdir(item_path) and item != keep_folder:
shutil.rmtree(item_path) shutil.rmtree(item_path)
console.log(f"[green]Removed directory: {item_path}")
elif os.path.isfile(item_path) and item != keep_file: elif os.path.isfile(item_path):
os.remove(item_path) os.remove(item_path)
console.log(f"[green]Removed file: {item_path}")
except PermissionError as pe:
console.print(f"[red]PermissionError: {pe}. Check permissions and try again.")
except Exception as e: except Exception as e:
console.print(f"[red]Error: {e}") console.log(f"[yellow]Skipping {item_path} due to error: {e}")
def print_commit_info(commit_info: dict): def print_commit_info(commit_info: dict):
@ -177,14 +181,14 @@ def main_upload():
Main function to upload the latest commit of a GitHub repository. Main function to upload the latest commit of a GitHub repository.
""" """
cmd_insert = Prompt.ask( cmd_insert = Prompt.ask(
"[bold red]Are you sure you want to delete all files? (Only 'Video' folder and 'update_version.py' will remain)", "[bold red]Are you sure you want to delete all files? (Only 'Video' folder and 'update.py' will remain)",
choices=['y', 'n'], choices=['y', 'n'],
default='y', default='y',
show_choices=True show_choices=True
) )
if cmd_insert.lower().strip() == 'y' or cmd_insert.lower().strip() == 'yes': if cmd_insert.lower().strip() == 'y' or cmd_insert.lower().strip() == 'yes':
console.print("[red]Deleting all files except 'Video' folder and 'update_version.py'...") console.print("[red]Deleting all files except 'Video' folder and 'update.py'...")
keep_specific_items(".", "Video", "upload.py") keep_specific_items(".", "Video", "upload.py")
download_and_extract_latest_commit() download_and_extract_latest_commit()
else: else: