Switch from python-qbittorrent to qbittorrent-api

This commit is contained in:
Dark1291 2025-02-24 15:40:15 +01:00
parent bdbe0afc93
commit 938e2838e4
2 changed files with 96 additions and 91 deletions

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

@ -11,5 +11,4 @@ pathvalidate
pycryptodomex pycryptodomex
ua-generator ua-generator
qbittorrent-api qbittorrent-api
python-qbittorrent
pyTelegramBotAPI pyTelegramBotAPI