StreamingCommunity 2.6.1__py3-none-any.whl → 2.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of StreamingCommunity might be problematic. Click here for more details.

Files changed (76) hide show
  1. StreamingCommunity/Api/Player/ddl.py +4 -4
  2. StreamingCommunity/Api/Player/maxstream.py +10 -16
  3. StreamingCommunity/Api/Player/supervideo.py +9 -35
  4. StreamingCommunity/Api/Player/vixcloud.py +18 -92
  5. StreamingCommunity/Api/Site/1337xx/__init__.py +8 -1
  6. StreamingCommunity/Api/Site/1337xx/site.py +16 -15
  7. StreamingCommunity/Api/Site/1337xx/title.py +7 -5
  8. StreamingCommunity/Api/Site/animeunity/__init__.py +9 -2
  9. StreamingCommunity/Api/Site/animeunity/film_serie.py +12 -5
  10. StreamingCommunity/Api/Site/animeunity/site.py +14 -10
  11. StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +9 -10
  12. StreamingCommunity/Api/Site/cb01new/__init__.py +8 -1
  13. StreamingCommunity/Api/Site/cb01new/film.py +7 -1
  14. StreamingCommunity/Api/Site/cb01new/site.py +24 -15
  15. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +9 -2
  16. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +7 -1
  17. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +16 -15
  18. StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +3 -3
  19. StreamingCommunity/Api/Site/guardaserie/__init__.py +9 -2
  20. StreamingCommunity/Api/Site/guardaserie/series.py +9 -1
  21. StreamingCommunity/Api/Site/guardaserie/site.py +23 -22
  22. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +5 -4
  23. StreamingCommunity/Api/Site/mostraguarda/__init__.py +6 -2
  24. StreamingCommunity/Api/Site/mostraguarda/film.py +10 -6
  25. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +9 -2
  26. StreamingCommunity/Api/Site/streamingcommunity/film.py +9 -2
  27. StreamingCommunity/Api/Site/streamingcommunity/series.py +15 -6
  28. StreamingCommunity/Api/Site/streamingcommunity/site.py +16 -14
  29. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +10 -11
  30. StreamingCommunity/Api/Template/Util/__init__.py +0 -1
  31. StreamingCommunity/Api/Template/Util/get_domain.py +31 -134
  32. StreamingCommunity/Api/Template/Util/manage_ep.py +10 -5
  33. StreamingCommunity/Api/Template/config_loader.py +14 -10
  34. StreamingCommunity/Api/Template/site.py +3 -6
  35. StreamingCommunity/Lib/Downloader/HLS/downloader.py +12 -15
  36. StreamingCommunity/Lib/Downloader/HLS/segments.py +14 -34
  37. StreamingCommunity/Lib/Downloader/MP4/downloader.py +14 -11
  38. StreamingCommunity/Lib/Downloader/TOR/downloader.py +109 -101
  39. StreamingCommunity/Lib/FFmpeg/__init__.py +1 -1
  40. StreamingCommunity/Lib/FFmpeg/capture.py +10 -12
  41. StreamingCommunity/Lib/FFmpeg/command.py +15 -14
  42. StreamingCommunity/Lib/FFmpeg/util.py +9 -38
  43. StreamingCommunity/Lib/M3U8/decryptor.py +72 -146
  44. StreamingCommunity/Lib/M3U8/estimator.py +8 -16
  45. StreamingCommunity/Lib/M3U8/parser.py +1 -17
  46. StreamingCommunity/Lib/M3U8/url_fixer.py +1 -4
  47. StreamingCommunity/Lib/TMBD/__init__.py +2 -0
  48. StreamingCommunity/Lib/TMBD/obj_tmbd.py +3 -17
  49. StreamingCommunity/Lib/TMBD/tmdb.py +4 -9
  50. StreamingCommunity/TelegramHelp/telegram_bot.py +50 -50
  51. StreamingCommunity/Upload/update.py +6 -5
  52. StreamingCommunity/Upload/version.py +1 -1
  53. StreamingCommunity/Util/color.py +1 -1
  54. StreamingCommunity/Util/config_json.py +435 -0
  55. StreamingCommunity/Util/headers.py +7 -36
  56. StreamingCommunity/Util/logger.py +72 -42
  57. StreamingCommunity/Util/message.py +8 -3
  58. StreamingCommunity/Util/os.py +41 -93
  59. StreamingCommunity/Util/table.py +8 -17
  60. StreamingCommunity/run.py +39 -43
  61. {StreamingCommunity-2.6.1.dist-info → StreamingCommunity-2.8.0.dist-info}/METADATA +203 -114
  62. StreamingCommunity-2.8.0.dist-info/RECORD +75 -0
  63. StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +0 -53
  64. StreamingCommunity/Api/Site/ilcorsaronero/site.py +0 -64
  65. StreamingCommunity/Api/Site/ilcorsaronero/title.py +0 -42
  66. StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py +0 -149
  67. StreamingCommunity/Api/Template/Util/recall_search.py +0 -37
  68. StreamingCommunity/Lib/Downloader/HLS/proxyes.py +0 -110
  69. StreamingCommunity/Util/_jsonConfig.py +0 -241
  70. StreamingCommunity/Util/call_stack.py +0 -42
  71. StreamingCommunity/Util/console.py +0 -12
  72. StreamingCommunity-2.6.1.dist-info/RECORD +0 -83
  73. {StreamingCommunity-2.6.1.dist-info → StreamingCommunity-2.8.0.dist-info}/LICENSE +0 -0
  74. {StreamingCommunity-2.6.1.dist-info → StreamingCommunity-2.8.0.dist-info}/WHEEL +0 -0
  75. {StreamingCommunity-2.6.1.dist-info → StreamingCommunity-2.8.0.dist-info}/entry_points.txt +0 -0
  76. {StreamingCommunity-2.6.1.dist-info → StreamingCommunity-2.8.0.dist-info}/top_level.txt +0 -0
@@ -17,14 +17,13 @@ from typing import Dict
17
17
  # External libraries
18
18
  import httpx
19
19
  from tqdm import tqdm
20
+ from rich.console import Console
20
21
 
21
22
 
22
23
  # Internal utilities
23
24
  from StreamingCommunity.Util.color import Colors
24
- from StreamingCommunity.Util.console import console
25
- from StreamingCommunity.Util.headers import get_headers, random_headers
26
- from StreamingCommunity.Util._jsonConfig import config_manager
27
- from StreamingCommunity.Util.os import os_manager
25
+ from StreamingCommunity.Util.headers import get_userAgent
26
+ from StreamingCommunity.Util.config_json import config_manager, get_use_large_bar
28
27
 
29
28
 
30
29
  # Logic class
@@ -34,16 +33,11 @@ from ...M3U8 import (
34
33
  M3U8_Parser,
35
34
  M3U8_UrlFix
36
35
  )
37
- from .proxyes import main_test_proxy
38
36
 
39
37
  # Config
40
38
  TQDM_DELAY_WORKER = config_manager.get_float('M3U8_DOWNLOAD', 'tqdm_delay')
41
- USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
42
39
  REQUEST_MAX_RETRY = config_manager.get_int('REQUESTS', 'max_retry')
43
- REQUEST_VERIFY = False
44
- THERE_IS_PROXY_LIST = os_manager.check_file("list_proxy.txt")
45
- PROXY_START_MIN = config_manager.get_float('REQUESTS', 'proxy_start_min')
46
- PROXY_START_MAX = config_manager.get_float('REQUESTS', 'proxy_start_max')
40
+ REQUEST_VERIFY = config_manager.get_int('REQUESTS', 'verify')
47
41
  DEFAULT_VIDEO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_video_workser')
48
42
  DEFAULT_AUDIO_WORKERS = config_manager.get_int('M3U8_DOWNLOAD', 'default_audio_workser')
49
43
  MAX_TIMEOOUT = config_manager.get_int("REQUESTS", "timeout")
@@ -52,6 +46,9 @@ SEGMENT_MAX_TIMEOUT = config_manager.get_int("M3U8_DOWNLOAD", "segment_timeout")
52
46
  TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
53
47
 
54
48
 
49
+ # Variable
50
+ console = Console()
51
+
55
52
 
56
53
  class M3U8_Segments:
57
54
  def __init__(self, url: str, tmp_folder: str, is_index_url: bool = True):
@@ -102,7 +99,7 @@ class M3U8_Segments:
102
99
  self.key_base_url = f"{parsed_url.scheme}://{parsed_url.netloc}/"
103
100
 
104
101
  try:
105
- client_params = {'headers': {'User-Agent': get_headers()}, 'timeout': MAX_TIMEOOUT}
102
+ client_params = {'headers': {'User-Agent': get_userAgent()}, 'timeout': MAX_TIMEOOUT}
106
103
  response = httpx.get(url=key_uri, **client_params)
107
104
  response.raise_for_status()
108
105
 
@@ -133,19 +130,10 @@ class M3U8_Segments:
133
130
  ]
134
131
  self.class_ts_estimator.total_segments = len(self.segments)
135
132
 
136
- # Proxy
137
- if THERE_IS_PROXY_LIST:
138
- console.log("[red]Start validation proxy.")
139
- self.valid_proxy = main_test_proxy(self.segments[0])
140
- console.log(f"[cyan]N. Valid ip: [red]{len(self.valid_proxy)}")
141
-
142
- if len(self.valid_proxy) == 0:
143
- sys.exit(0)
144
-
145
133
  def get_info(self) -> None:
146
134
  if self.is_index_url:
147
135
  try:
148
- client_params = {'headers': {'User-Agent': get_headers()}, 'timeout': MAX_TIMEOOUT}
136
+ client_params = {'headers': {'User-Agent': get_userAgent()}, 'timeout': MAX_TIMEOOUT}
149
137
  response = httpx.get(self.url, **client_params)
150
138
  response.raise_for_status()
151
139
 
@@ -184,18 +172,13 @@ class M3U8_Segments:
184
172
  else:
185
173
  print("Signal handler must be set in the main thread")
186
174
 
187
- def _get_http_client(self, index: int = None):
175
+ def _get_http_client(self):
188
176
  client_params = {
189
- #'headers': random_headers(self.key_base_url) if hasattr(self, 'key_base_url') else {'User-Agent': get_headers()},
190
- 'headers': {'User-Agent': get_headers()},
177
+ 'headers': {'User-Agent': get_userAgent()},
191
178
  'timeout': SEGMENT_MAX_TIMEOUT,
192
179
  'follow_redirects': True,
193
180
  'http2': False
194
181
  }
195
-
196
- if THERE_IS_PROXY_LIST and index is not None and hasattr(self, 'valid_proxy'):
197
- client_params['proxies'] = self.valid_proxy[index % len(self.valid_proxy)]
198
-
199
182
  return httpx.Client(**client_params)
200
183
 
201
184
  def download_segment(self, ts_url: str, index: int, progress_bar: tqdm, backoff_factor: float = 1.1) -> None:
@@ -213,7 +196,7 @@ class M3U8_Segments:
213
196
  return
214
197
 
215
198
  try:
216
- with self._get_http_client(index) as client:
199
+ with self._get_http_client() as client:
217
200
  start_time = time.time()
218
201
  response = client.get(ts_url)
219
202
 
@@ -350,7 +333,6 @@ class M3U8_Segments:
350
333
 
351
334
  # Configure workers and delay
352
335
  max_workers = self._get_worker_count(type)
353
- delay = max(PROXY_START_MIN, min(PROXY_START_MAX, 1 / (len(self.valid_proxy) + 1))) if THERE_IS_PROXY_LIST else TQDM_DELAY_WORKER
354
336
 
355
337
  # Download segments with completion verification
356
338
  with ThreadPoolExecutor(max_workers=max_workers) as executor:
@@ -361,7 +343,7 @@ class M3U8_Segments:
361
343
  if self.interrupt_flag.is_set():
362
344
  break
363
345
 
364
- time.sleep(delay)
346
+ time.sleep(TQDM_DELAY_WORKER)
365
347
  futures.append(executor.submit(self.download_segment, segment_url, index, progress_bar))
366
348
 
367
349
  # Wait for futures with interrupt handling
@@ -405,7 +387,7 @@ class M3U8_Segments:
405
387
  """
406
388
  Generate platform-appropriate progress bar format.
407
389
  """
408
- if not USE_LARGE_BAR:
390
+ if not get_use_large_bar():
409
391
  return (
410
392
  f"{Colors.YELLOW}Proc{Colors.WHITE}: "
411
393
  f"{Colors.RED}{{percentage:.2f}}% "
@@ -429,8 +411,6 @@ class M3U8_Segments:
429
411
  'audio': DEFAULT_AUDIO_WORKERS
430
412
  }.get(stream_type.lower(), 1)
431
413
 
432
- if THERE_IS_PROXY_LIST:
433
- return min(len(self.valid_proxy), base_workers * 2)
434
414
  return base_workers
435
415
 
436
416
  def _generate_results(self, stream_type: str) -> Dict:
@@ -12,13 +12,15 @@ from functools import partial
12
12
  # External libraries
13
13
  import httpx
14
14
  from tqdm import tqdm
15
+ from rich.console import Console
16
+ from rich.prompt import Prompt
17
+ from rich.panel import Panel
15
18
 
16
19
 
17
20
  # Internal utilities
18
- from StreamingCommunity.Util.headers import get_headers
21
+ from StreamingCommunity.Util.headers import get_userAgent
19
22
  from StreamingCommunity.Util.color import Colors
20
- from StreamingCommunity.Util.console import console, Panel
21
- from StreamingCommunity.Util._jsonConfig import config_manager
23
+ from StreamingCommunity.Util.config_json import config_manager
22
24
  from StreamingCommunity.Util.os import internet_manager
23
25
  from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
24
26
 
@@ -27,18 +29,17 @@ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
27
29
  from ...FFmpeg import print_duration_table
28
30
 
29
31
 
30
- # Suppress SSL warnings
31
- import urllib3
32
- urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
33
-
34
-
35
32
  # Config
33
+ REQUEST_VERIFY = config_manager.get_int('REQUESTS', 'verify')
36
34
  GET_ONLY_LINK = config_manager.get_bool('M3U8_PARSER', 'get_only_link')
37
35
  REQUEST_TIMEOUT = config_manager.get_float('REQUESTS', 'timeout')
38
-
39
36
  TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
40
37
 
41
38
 
39
+ # Variable
40
+ msg = Prompt()
41
+ console = Console()
42
+
42
43
 
43
44
  class InterruptHandler:
44
45
  def __init__(self):
@@ -47,6 +48,7 @@ class InterruptHandler:
47
48
  self.kill_download = False
48
49
  self.force_quit = False
49
50
 
51
+
50
52
  def signal_handler(signum, frame, interrupt_handler, original_handler):
51
53
  """Enhanced signal handler for multiple interrupt scenarios"""
52
54
  current_time = time.time()
@@ -67,6 +69,7 @@ def signal_handler(signum, frame, interrupt_handler, original_handler):
67
69
  console.print("\n[bold red]Force quit activated. Saving partial download...[/bold red]")
68
70
  signal.signal(signum, original_handler)
69
71
 
72
+
70
73
  def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = None):
71
74
  """
72
75
  Downloads an MP4 video with enhanced interrupt handling.
@@ -99,7 +102,7 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
99
102
  if headers_:
100
103
  headers.update(headers_)
101
104
  else:
102
- headers['User-Agent'] = get_headers()
105
+ headers['User-Agent'] = get_userAgent()
103
106
 
104
107
  except Exception as header_err:
105
108
  logging.error(f"Error preparing headers: {header_err}")
@@ -111,7 +114,7 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
111
114
  original_handler = signal.signal(signal.SIGINT, partial(signal_handler, interrupt_handler=interrupt_handler, original_handler=signal.getsignal(signal.SIGINT)))
112
115
 
113
116
  try:
114
- transport = httpx.HTTPTransport(verify=False, http2=True)
117
+ transport = httpx.HTTPTransport(verify=REQUEST_VERIFY, http2=True)
115
118
 
116
119
  with httpx.Client(transport=transport, timeout=httpx.Timeout(60)) as client:
117
120
  with client.stream("GET", url, headers=headers, timeout=REQUEST_TIMEOUT) as response:
@@ -9,47 +9,55 @@ import psutil
9
9
  import logging
10
10
 
11
11
 
12
+ # External library
13
+ from rich.console import Console
14
+
15
+
12
16
  # Internal utilities
13
17
  from StreamingCommunity.Util.color import Colors
14
18
  from StreamingCommunity.Util.os import internet_manager
15
- from StreamingCommunity.Util.console import console
16
- from StreamingCommunity.Util._jsonConfig import config_manager
19
+ from StreamingCommunity.Util.config_json import config_manager, get_use_large_bar
17
20
 
18
21
 
19
22
  # External libraries
20
23
  from tqdm import tqdm
21
- from qbittorrent import Client
24
+ import qbittorrentapi
22
25
 
23
26
 
24
27
  # Tor config
25
- HOST = str(config_manager.get_dict('DEFAULT', 'config_qbit_tor')['host'])
26
- PORT = str(config_manager.get_dict('DEFAULT', 'config_qbit_tor')['port'])
27
- USERNAME = str(config_manager.get_dict('DEFAULT', 'config_qbit_tor')['user'])
28
- PASSWORD = str(config_manager.get_dict('DEFAULT', 'config_qbit_tor')['pass'])
28
+ HOST = config_manager.get_dict('QBIT_CONFIG', 'host')
29
+ PORT = config_manager.get_dict('QBIT_CONFIG', 'port')
30
+ USERNAME = config_manager.get_dict('QBIT_CONFIG', 'user')
31
+ PASSWORD = config_manager.get_dict('QBIT_CONFIG', 'pass')
29
32
 
30
33
 
31
- # Config
32
- USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
34
+ # Variable
33
35
  REQUEST_TIMEOUT = config_manager.get_float('REQUESTS', 'timeout')
34
-
36
+ console = Console()
35
37
 
36
38
 
37
39
  class TOR_downloader:
38
40
  def __init__(self):
39
41
  """
40
42
  Initializes the TorrentManager instance.
41
-
43
+
42
44
  Parameters:
43
45
  - host (str): IP address or hostname of the qBittorrent Web UI.
44
- - port (int): Port number of the qBittorrent Web UI.
45
- - username (str): Username for logging into qBittorrent.
46
- - password (str): Password for logging into qBittorrent.
46
+ - port (int): Port of the qBittorrent Web UI.
47
+ - username (str): Username for accessing qBittorrent.
48
+ - password (str): Password for accessing qBittorrent.
47
49
  """
48
50
  try:
49
51
  console.print(f"[cyan]Connect to: [green]{HOST}:{PORT}")
50
- self.qb = Client(f'http://{HOST}:{PORT}/')
51
- except:
52
- logging.error("Start qbitorrent first.")
52
+ self.qb = qbittorrentapi.Client(
53
+ host=HOST,
54
+ port=PORT,
55
+ username=USERNAME,
56
+ password=PASSWORD
57
+ )
58
+
59
+ except:
60
+ logging.error("Start qbittorrent first.")
53
61
  sys.exit(0)
54
62
 
55
63
  self.username = USERNAME
@@ -65,7 +73,7 @@ class TOR_downloader:
65
73
  Logs into the qBittorrent Web UI.
66
74
  """
67
75
  try:
68
- self.qb.login(self.username, self.password)
76
+ self.qb.auth_log_in()
69
77
  self.logged_in = True
70
78
  logging.info("Successfully logged in to qBittorrent.")
71
79
 
@@ -74,96 +82,87 @@ class TOR_downloader:
74
82
  self.logged_in = False
75
83
 
76
84
  def delete_magnet(self, torrent_info):
77
-
78
- if (int(torrent_info.get('dl_speed')) == 0 and
79
- int(torrent_info.get('peers')) == 0 and
80
- int(torrent_info.get('seeds')) == 0):
81
-
82
- # Elimina il torrent appena aggiunto
83
- console.print(f"[bold red]⚠️ Torrent non scaricabile. Rimozione in corso...[/bold red]")
85
+ """
86
+ Deletes a torrent if it is not downloadable (no seeds/peers).
87
+
88
+ Parameters:
89
+ - torrent_info: Object containing torrent information obtained from the qBittorrent API.
90
+ """
91
+ if (int(torrent_info.dlspeed) == 0 and
92
+ int(torrent_info.num_leechs) == 0 and
93
+ int(torrent_info.num_seeds) == 0):
84
94
 
95
+ console.print(f"[bold red]Torrent not downloadable. Removing...[/bold red]")
85
96
  try:
86
- # Rimuovi il torrent
87
- self.qb.delete_permanently(torrent_info['hash'])
88
-
97
+ self.qb.torrents_delete(delete_files=True, torrent_hashes=torrent_info.hash)
89
98
  except Exception as delete_error:
90
- logging.error(f"Errore durante la rimozione del torrent: {delete_error}")
99
+ logging.error(f"Error while removing torrent: {delete_error}")
91
100
 
92
- # Resetta l'ultimo hash
93
101
  self.latest_torrent_hash = None
94
102
 
95
103
  def add_magnet_link(self, magnet_link):
96
104
  """
97
- Aggiunge un magnet link e recupera le informazioni dettagliate.
98
-
99
- Args:
100
- magnet_link (str): Magnet link da aggiungere
105
+ Adds a magnet link and retrieves detailed torrent information.
106
+
107
+ Arguments:
108
+ magnet_link (str): Magnet link to add.
101
109
 
102
110
  Returns:
103
- dict: Informazioni del torrent aggiunto, o None se fallisce
111
+ dict: Information about the added torrent, or None in case of error.
104
112
  """
105
-
106
- # Estrai l'hash dal magnet link
107
113
  magnet_hash_match = re.search(r'urn:btih:([0-9a-fA-F]+)', magnet_link)
108
-
109
114
  if not magnet_hash_match:
110
- raise ValueError("Hash del magnet link non trovato")
115
+ raise ValueError("Magnet link hash not found")
111
116
 
112
117
  magnet_hash = magnet_hash_match.group(1).lower()
113
118
 
114
- # Estrai il nome del file dal magnet link (se presente)
119
+ # Extract the torrent name, if available
115
120
  name_match = re.search(r'dn=([^&]+)', magnet_link)
116
- torrent_name = name_match.group(1).replace('+', ' ') if name_match else "Nome non disponibile"
121
+ torrent_name = name_match.group(1).replace('+', ' ') if name_match else "Name not available"
117
122
 
118
- # Salva il timestamp prima di aggiungere il torrent
123
+ # Save the timestamp before adding the torrent
119
124
  before_add_time = time.time()
120
125
 
121
- # Aggiungi il magnet link
122
- console.print(f"[cyan]Aggiunta magnet link[/cyan]: [red]{magnet_link}")
123
- self.qb.download_from_link(magnet_link)
126
+ console.print(f"[cyan]Adding magnet link ...")
127
+ self.qb.torrents_add(urls=magnet_link)
124
128
 
125
- # Aspetta un attimo per essere sicuri che il torrent sia stato aggiunto
126
129
  time.sleep(1)
127
130
 
128
- # Cerca il torrent
129
- torrents = self.qb.torrents()
131
+ torrents = self.qb.torrents_info()
130
132
  matching_torrents = [
131
133
  t for t in torrents
132
- if (t['hash'].lower() == magnet_hash) or (t.get('added_on', 0) > before_add_time)
134
+ if (t.hash.lower() == magnet_hash) or (getattr(t, 'added_on', 0) > before_add_time)
133
135
  ]
134
136
 
135
137
  if not matching_torrents:
136
- raise ValueError("Nessun torrent corrispondente trovato")
138
+ raise ValueError("No matching torrent found")
137
139
 
138
- # Prendi il primo torrent corrispondente
139
140
  torrent_info = matching_torrents[0]
140
141
 
141
- # Formatta e stampa le informazioni
142
- console.print("\n[bold green]🔗 Dettagli Torrent Aggiunto:[/bold green]")
143
- console.print(f"[yellow]Name:[/yellow] {torrent_info.get('name', torrent_name)}")
144
- console.print(f"[yellow]Hash:[/yellow] {torrent_info['hash']}")
142
+ console.print("\n[bold green]Added Torrent Details:[/bold green]")
143
+ console.print(f"[yellow]Name:[/yellow] {torrent_info.name or torrent_name}")
144
+ console.print(f"[yellow]Hash:[/yellow] {torrent_info.hash}")
145
145
  print()
146
146
 
147
- # Salva l'hash per usi successivi e il path
148
- self.latest_torrent_hash = torrent_info['hash']
149
- self.output_file = torrent_info['content_path']
150
- self.file_name = torrent_info['name']
147
+ self.latest_torrent_hash = torrent_info.hash
148
+ self.output_file = torrent_info.content_path
149
+ self.file_name = torrent_info.name
151
150
 
152
- # Controlla che sia possibile il download
151
+ # Wait and verify if the download is possible
153
152
  time.sleep(5)
154
- self.delete_magnet(self.qb.get_torrent(self.latest_torrent_hash))
153
+ self.delete_magnet(self.qb.torrents_info(torrent_hashes=self.latest_torrent_hash)[0])
155
154
 
156
155
  return torrent_info
157
156
 
158
157
  def start_download(self):
159
158
  """
160
- Starts downloading the latest added torrent and monitors progress.
161
- """
159
+ Starts downloading the added torrent and monitors its progress.
160
+ """
162
161
  if self.latest_torrent_hash is not None:
163
162
  try:
164
-
165
- # Custom bar for mobile and pc
166
- if USE_LARGE_BAR:
163
+
164
+ # Custom progress bar for mobile and PC
165
+ if get_use_large_bar():
167
166
  bar_format = (
168
167
  f"{Colors.YELLOW}[TOR] {Colors.WHITE}({Colors.CYAN}video{Colors.WHITE}): "
169
168
  f"{Colors.RED}{{percentage:.2f}}% {Colors.MAGENTA}{{bar}} {Colors.WHITE}[ "
@@ -189,35 +188,42 @@ class TOR_downloader:
189
188
  with progress_bar as pbar:
190
189
  while True:
191
190
 
192
- # Get variable from qtorrent
193
- torrent_info = self.qb.get_torrent(self.latest_torrent_hash)
194
- self.save_path = torrent_info['save_path']
195
- self.torrent_name = torrent_info['name']
191
+ torrent_info = self.qb.torrents_info(torrent_hashes=self.latest_torrent_hash)[0]
192
+ self.save_path = torrent_info.save_path
193
+ self.torrent_name = torrent_info.name
196
194
 
197
- # Fetch important variable
198
- pieces_have = torrent_info['pieces_have']
199
- pieces_num = torrent_info['pieces_num']
200
- progress = (pieces_have / pieces_num) * 100 if pieces_num else 0
195
+ progress = torrent_info.progress * 100
201
196
  pbar.n = progress
202
197
 
203
- download_speed = torrent_info['dl_speed']
204
- total_size = torrent_info['total_size']
205
- downloaded_size = torrent_info['total_downloaded']
198
+ download_speed = torrent_info.dlspeed
199
+ total_size = torrent_info.size
200
+ downloaded_size = torrent_info.downloaded
206
201
 
207
- # Format variable
202
+ # Format the downloaded size
208
203
  downloaded_size_str = internet_manager.format_file_size(downloaded_size)
209
204
  downloaded_size = downloaded_size_str.split(' ')[0]
210
205
 
206
+ # Safely format the total size
211
207
  total_size_str = internet_manager.format_file_size(total_size)
212
- total_size = total_size_str.split(' ')[0]
213
- total_size_unit = total_size_str.split(' ')[1]
208
+ total_size_parts = total_size_str.split(' ')
209
+ if len(total_size_parts) >= 2:
210
+ total_size = total_size_parts[0]
211
+ total_size_unit = total_size_parts[1]
212
+ else:
213
+ total_size = total_size_str
214
+ total_size_unit = ""
214
215
 
216
+ # Safely format the average download speed
215
217
  average_internet_str = internet_manager.format_transfer_speed(download_speed)
216
- average_internet = average_internet_str.split(' ')[0]
217
- average_internet_unit = average_internet_str.split(' ')[1]
218
+ average_internet_parts = average_internet_str.split(' ')
219
+ if len(average_internet_parts) >= 2:
220
+ average_internet = average_internet_parts[0]
221
+ average_internet_unit = average_internet_parts[1]
222
+ else:
223
+ average_internet = average_internet_str
224
+ average_internet_unit = ""
218
225
 
219
- # Update the progress bar's postfix
220
- if USE_LARGE_BAR:
226
+ if get_use_large_bar():
221
227
  pbar.set_postfix_str(
222
228
  f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_size} {Colors.WHITE}< {Colors.GREEN}{total_size} {Colors.RED}{total_size_unit} "
223
229
  f"{Colors.WHITE}| {Colors.CYAN}{average_internet} {Colors.RED}{average_internet_unit}"
@@ -231,7 +237,6 @@ class TOR_downloader:
231
237
  pbar.refresh()
232
238
  time.sleep(0.2)
233
239
 
234
- # Break at the end
235
240
  if int(progress) == 100:
236
241
  break
237
242
 
@@ -239,7 +244,15 @@ class TOR_downloader:
239
244
  logging.info("Download process interrupted.")
240
245
 
241
246
  def is_file_in_use(self, file_path: str) -> bool:
242
- """Check if a file is in use by any process."""
247
+ """
248
+ Checks if a file is being used by any process.
249
+
250
+ Parameters:
251
+ - file_path (str): The file path to check.
252
+
253
+ Returns:
254
+ - bool: True if the file is in use, False otherwise.
255
+ """
243
256
  for proc in psutil.process_iter(['open_files']):
244
257
  try:
245
258
  if any(file_path == f.path for f in proc.info['open_files'] or []):
@@ -251,21 +264,20 @@ class TOR_downloader:
251
264
 
252
265
  def move_downloaded_files(self, destination: str):
253
266
  """
254
- Moves downloaded files of the latest torrent to another location.
255
-
267
+ Moves the downloaded files of the most recent torrent to a new location.
268
+
256
269
  Parameters:
257
- - destination (str): Destination directory to move files.
258
-
270
+ - destination (str): Destination folder.
271
+
259
272
  Returns:
260
- - bool: True if files are moved successfully, False otherwise.
273
+ - bool: True if the move was successful, False otherwise.
261
274
  """
262
275
  console.print(f"[cyan]Destination folder: [red]{destination}")
263
276
 
264
277
  try:
265
-
266
- # Ensure the file is not in use
267
- timeout = 3
278
+ timeout = 5
268
279
  elapsed = 0
280
+
269
281
  while self.is_file_in_use(self.output_file) and elapsed < timeout:
270
282
  time.sleep(1)
271
283
  elapsed += 1
@@ -273,24 +285,20 @@ class TOR_downloader:
273
285
  if elapsed == timeout:
274
286
  raise Exception(f"File '{self.output_file}' is in use and could not be moved.")
275
287
 
276
- # Ensure destination directory exists
277
288
  os.makedirs(destination, exist_ok=True)
278
289
 
279
- # Perform the move operation
280
290
  try:
281
291
  shutil.move(self.output_file, destination)
282
-
283
292
  except OSError as e:
284
- if e.errno == 17: # Cross-disk move error
285
- # Perform copy and delete manually
293
+ if e.errno == 17: # Error when moving between different disks
286
294
  shutil.copy2(self.output_file, destination)
287
295
  os.remove(self.output_file)
288
296
  else:
289
297
  raise
290
298
 
291
- # Delete the torrent data
292
299
  time.sleep(5)
293
- self.qb.delete_permanently(self.qb.torrents()[-1]['hash'])
300
+ last_torrent = self.qb.torrents_info()[-1]
301
+ self.qb.torrents_delete(delete_files=True, torrent_hashes=last_torrent.hash)
294
302
  return True
295
303
 
296
304
  except Exception as e:
@@ -1,4 +1,4 @@
1
1
  # 18.04.24
2
2
 
3
3
  from .command import join_video, join_audios, join_subtitle
4
- from .util import print_duration_table, get_video_duration
4
+ from .util import print_duration_table, get_video_duration
@@ -6,12 +6,16 @@ import threading
6
6
  import subprocess
7
7
 
8
8
 
9
+ # External library
10
+ from rich.console import Console
11
+
12
+
9
13
  # Internal utilities
10
- from StreamingCommunity.Util.console import console
11
14
  from StreamingCommunity.Util.os import internet_manager
12
15
 
13
16
 
14
17
  # Variable
18
+ console = Console()
15
19
  terminate_flag = threading.Event()
16
20
 
17
21
 
@@ -24,8 +28,6 @@ def capture_output(process: subprocess.Popen, description: str) -> None:
24
28
  - description (str): Description of the command being executed.
25
29
  """
26
30
  try:
27
-
28
- # Variable to store the length of the longest progress string
29
31
  max_length = 0
30
32
 
31
33
  for line in iter(process.stdout.readline, ''):
@@ -59,7 +61,7 @@ def capture_output(process: subprocess.Popen, description: str) -> None:
59
61
 
60
62
 
61
63
  # Construct the progress string with formatted output information
62
- progress_string = (f" {description}[white]: "
64
+ progress_string = (f" {description}[white]: "
63
65
  f"([green]'speed': [yellow]{data.get('speed', 'N/A')}[white], "
64
66
  f"[green]'size': [yellow]{internet_manager.format_file_size(byte_size)}[white])")
65
67
  max_length = max(max_length, len(progress_string))
@@ -94,10 +96,7 @@ def parse_output_line(line: str) -> dict:
94
96
  dict: A dictionary containing parsed information.
95
97
  """
96
98
  try:
97
-
98
99
  data = {}
99
-
100
- # Split the line by whitespace and extract key-value pairs
101
100
  parts = line.replace(" ", "").replace("= ", "=").split()
102
101
 
103
102
  for part in parts:
@@ -123,7 +122,7 @@ def terminate_process(process):
123
122
  - process (subprocess.Popen): The subprocess to terminate.
124
123
  """
125
124
  try:
126
- if process.poll() is None: # Check if the process is still running
125
+ if process.poll() is None:
127
126
  process.kill()
128
127
  except Exception as e:
129
128
  logging.error(f"Failed to terminate process: {e}")
@@ -137,7 +136,6 @@ def capture_ffmpeg_real_time(ffmpeg_command: list, description: str) -> None:
137
136
  - ffmpeg_command (list): The command to execute ffmpeg.
138
137
  - description (str): Description of the command being executed.
139
138
  """
140
-
141
139
  global terminate_flag
142
140
 
143
141
  # Clear the terminate_flag before starting a new capture
@@ -163,8 +161,8 @@ def capture_ffmpeg_real_time(ffmpeg_command: list, description: str) -> None:
163
161
  logging.error(f"Error in ffmpeg process: {e}")
164
162
 
165
163
  finally:
166
- terminate_flag.set() # Signal the output capture thread to terminate
167
- output_thread.join() # Wait for the output capture thread to complete
164
+ terminate_flag.set()
165
+ output_thread.join()
168
166
 
169
167
  except Exception as e:
170
- logging.error(f"Failed to start ffmpeg process: {e}")
168
+ logging.error(f"Failed to start ffmpeg process: {e}")