StreamingCommunity 3.2.8__py3-none-any.whl → 3.3.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 (86) hide show
  1. StreamingCommunity/Api/Player/Helper/Vixcloud/util.py +2 -1
  2. StreamingCommunity/Api/Player/hdplayer.py +2 -2
  3. StreamingCommunity/Api/Player/sweetpixel.py +5 -8
  4. StreamingCommunity/Api/Site/altadefinizione/__init__.py +32 -15
  5. StreamingCommunity/Api/Site/altadefinizione/film.py +10 -8
  6. StreamingCommunity/Api/Site/altadefinizione/series.py +9 -7
  7. StreamingCommunity/Api/Site/altadefinizione/site.py +1 -1
  8. StreamingCommunity/Api/Site/animeunity/__init__.py +31 -15
  9. StreamingCommunity/Api/Site/animeunity/serie.py +2 -2
  10. StreamingCommunity/Api/Site/animeworld/__init__.py +33 -7
  11. StreamingCommunity/Api/Site/animeworld/site.py +3 -5
  12. StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +8 -10
  13. StreamingCommunity/Api/Site/crunchyroll/__init__.py +44 -12
  14. StreamingCommunity/Api/Site/crunchyroll/film.py +9 -7
  15. StreamingCommunity/Api/Site/crunchyroll/series.py +9 -7
  16. StreamingCommunity/Api/Site/crunchyroll/site.py +16 -1
  17. StreamingCommunity/Api/Site/guardaserie/__init__.py +36 -10
  18. StreamingCommunity/Api/Site/guardaserie/series.py +8 -6
  19. StreamingCommunity/Api/Site/guardaserie/site.py +0 -3
  20. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +1 -2
  21. StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +37 -12
  22. StreamingCommunity/Api/Site/mediasetinfinity/film.py +10 -16
  23. StreamingCommunity/Api/Site/mediasetinfinity/series.py +12 -18
  24. StreamingCommunity/Api/Site/mediasetinfinity/site.py +18 -3
  25. StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +214 -180
  26. StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +2 -31
  27. StreamingCommunity/Api/Site/raiplay/__init__.py +47 -12
  28. StreamingCommunity/Api/Site/raiplay/film.py +42 -10
  29. StreamingCommunity/Api/Site/raiplay/series.py +53 -11
  30. StreamingCommunity/Api/Site/raiplay/site.py +4 -1
  31. StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +2 -1
  32. StreamingCommunity/Api/Site/raiplay/util/get_license.py +40 -0
  33. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +5 -8
  34. StreamingCommunity/Api/Site/streamingcommunity/film.py +7 -5
  35. StreamingCommunity/Api/Site/streamingcommunity/series.py +9 -7
  36. StreamingCommunity/Api/Site/streamingcommunity/site.py +8 -3
  37. StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +5 -2
  38. StreamingCommunity/Api/Site/streamingwatch/__init__.py +43 -9
  39. StreamingCommunity/Api/Site/streamingwatch/film.py +7 -5
  40. StreamingCommunity/Api/Site/streamingwatch/series.py +8 -6
  41. StreamingCommunity/Api/Site/streamingwatch/site.py +3 -1
  42. StreamingCommunity/Api/Site/streamingwatch/util/ScrapeSerie.py +3 -3
  43. StreamingCommunity/Api/Template/Util/__init__.py +10 -1
  44. StreamingCommunity/Api/Template/Util/manage_ep.py +4 -4
  45. StreamingCommunity/Api/Template/__init__.py +5 -1
  46. StreamingCommunity/Api/Template/site.py +10 -6
  47. StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +13 -12
  48. StreamingCommunity/Lib/Downloader/DASH/decrypt.py +1 -1
  49. StreamingCommunity/Lib/Downloader/DASH/downloader.py +24 -22
  50. StreamingCommunity/Lib/Downloader/DASH/parser.py +1 -1
  51. StreamingCommunity/Lib/Downloader/DASH/segments.py +4 -3
  52. StreamingCommunity/Lib/Downloader/HLS/downloader.py +11 -9
  53. StreamingCommunity/Lib/Downloader/HLS/segments.py +4 -9
  54. StreamingCommunity/Lib/Downloader/MP4/downloader.py +25 -6
  55. StreamingCommunity/Lib/Downloader/TOR/downloader.py +3 -5
  56. StreamingCommunity/Lib/Downloader/__init__.py +9 -1
  57. StreamingCommunity/Lib/FFmpeg/__init__.py +10 -1
  58. StreamingCommunity/Lib/FFmpeg/command.py +4 -6
  59. StreamingCommunity/Lib/FFmpeg/util.py +1 -1
  60. StreamingCommunity/Lib/M3U8/__init__.py +9 -1
  61. StreamingCommunity/Lib/M3U8/decryptor.py +8 -4
  62. StreamingCommunity/Lib/M3U8/estimator.py +0 -6
  63. StreamingCommunity/Lib/M3U8/parser.py +1 -1
  64. StreamingCommunity/Lib/M3U8/url_fixer.py +1 -1
  65. StreamingCommunity/Lib/TMBD/__init__.py +6 -1
  66. StreamingCommunity/TelegramHelp/config.json +1 -5
  67. StreamingCommunity/TelegramHelp/telegram_bot.py +9 -10
  68. StreamingCommunity/Upload/update.py +2 -2
  69. StreamingCommunity/Upload/version.py +1 -1
  70. StreamingCommunity/Util/config_json.py +139 -59
  71. StreamingCommunity/Util/http_client.py +201 -0
  72. StreamingCommunity/Util/message.py +1 -1
  73. StreamingCommunity/Util/os.py +8 -5
  74. StreamingCommunity/Util/table.py +3 -3
  75. StreamingCommunity/__init__.py +9 -1
  76. StreamingCommunity/run.py +394 -258
  77. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/METADATA +147 -45
  78. streamingcommunity-3.3.0.dist-info/RECORD +110 -0
  79. StreamingCommunity/Api/Site/cb01new/__init__.py +0 -72
  80. StreamingCommunity/Api/Site/cb01new/film.py +0 -62
  81. StreamingCommunity/Api/Site/cb01new/site.py +0 -78
  82. streamingcommunity-3.2.8.dist-info/RECORD +0 -111
  83. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/WHEEL +0 -0
  84. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/entry_points.txt +0 -0
  85. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/licenses/LICENSE +0 -0
  86. {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,6 @@
2
2
 
3
3
  import base64
4
4
  import logging
5
- import os
6
5
 
7
6
 
8
7
  # External libraries
@@ -31,16 +30,11 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
31
30
  list: List of dicts {'kid': ..., 'key': ...} (only CONTENT keys) or None if error.
32
31
  """
33
32
 
34
- # Check if CDM file exists
35
- if not os.path.isfile(cdm_device_path):
36
- console.print(f"[bold red] CDM file not found: {cdm_device_path}[/bold red]")
37
- return None
38
-
39
33
  # Check if PSSH is a valid base64 string
40
34
  try:
41
35
  base64.b64decode(pssh)
42
36
  except Exception:
43
- console.print(f"[bold red] Invalid PSSH base64 string.[/bold red]")
37
+ console.print("[bold red] Invalid PSSH base64 string.[/bold red]")
44
38
  return None
45
39
 
46
40
  try:
@@ -67,7 +61,15 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
67
61
  response = httpx.post(license_url, data=challenge, headers=req_headers, content=payload)
68
62
 
69
63
  if response.status_code != 200:
70
- console.print(f"[bold red]License error:[/bold red] {response.status_code}")
64
+ console.print(f"[bold red]License error:[/bold red] {response.status_code}, {response.text}")
65
+ console.print({
66
+ "url": license_url,
67
+ "headers": req_headers,
68
+ "content": payload,
69
+ "session_id": session_id.hex(),
70
+ "pssh": pssh
71
+ })
72
+
71
73
  return None
72
74
 
73
75
  # Handle (JSON) or classic (binary) license response
@@ -77,18 +79,17 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
77
79
 
78
80
  # Check if license_data is empty
79
81
  if not license_data:
80
- console.print(f"[bold red]License response is empty.[/bold red]")
82
+ console.print("[bold red]License response is empty.[/bold red]")
81
83
  return None
82
84
 
83
85
  if "application/json" in content_type:
84
86
  try:
85
87
 
86
88
  # Try to decode as JSON only if plausible
87
- text = response.text
88
89
  data = None
89
90
  try:
90
91
  data = response.json()
91
- except Exception as e:
92
+ except Exception:
92
93
  data = None
93
94
 
94
95
  if data and "license" in data:
@@ -118,7 +119,7 @@ def get_widevine_keys(pssh, license_url, cdm_device_path, headers=None, payload=
118
119
 
119
120
  # Check if content_keys list is empty
120
121
  if not content_keys:
121
- console.print(f"[bold yellow]⚠️ No CONTENT keys found in license.[/bold yellow]")
122
+ console.print("[bold yellow]⚠️ No CONTENT keys found in license.[/bold yellow]")
122
123
  return None
123
124
 
124
125
  return content_keys
@@ -38,7 +38,7 @@ def decrypt_with_mp4decrypt(encrypted_path, kid, key, output_path=None, cleanup=
38
38
  bytes.fromhex(kid)
39
39
  bytes.fromhex(key)
40
40
  except Exception:
41
- console.print(f"[bold red] Invalid KID or KEY (not hex).[/bold red]")
41
+ console.print("[bold red] Invalid KID or KEY (not hex).[/bold red]")
42
42
  return None
43
43
 
44
44
  if not output_path:
@@ -22,7 +22,7 @@ from .cdm_helpher import get_widevine_keys
22
22
 
23
23
  # Config
24
24
  DOWNLOAD_SPECIFIC_AUDIO = config_manager.get_list('M3U8_DOWNLOAD', 'specific_list_audio')
25
- FILTER_CUSTOM_REOLUTION = str(config_manager.get('M3U8_PARSER', 'force_resolution')).strip().lower()
25
+ FILTER_CUSTOM_REOLUTION = str(config_manager.get('M3U8_CONVERSION', 'force_resolution')).strip().lower()
26
26
  CLEANUP_TMP = config_manager.get_bool('M3U8_DOWNLOAD', 'cleanup_tmp_folder')
27
27
 
28
28
 
@@ -99,28 +99,30 @@ class DASH_Downloader:
99
99
  if rep:
100
100
  encrypted_path = os.path.join(self.encrypted_dir, f"{rep['id']}_encrypted.m4s")
101
101
 
102
- downloader = MPD_Segments(
103
- tmp_folder=self.encrypted_dir,
104
- representation=rep,
105
- pssh=self.parser.pssh
106
- )
107
-
108
- try:
109
- result = downloader.download_streams()
110
-
111
- # Check for interruption or failure
112
- if result.get("stopped"):
113
- self.stopped = True
114
- self.error = "Download interrupted"
102
+ # If m4s file doesn't exist start downloading
103
+ if not os.path.exists(encrypted_path):
104
+ downloader = MPD_Segments(
105
+ tmp_folder=self.encrypted_dir,
106
+ representation=rep,
107
+ pssh=self.parser.pssh
108
+ )
109
+
110
+ try:
111
+ result = downloader.download_streams()
112
+
113
+ # Check for interruption or failure
114
+ if result.get("stopped"):
115
+ self.stopped = True
116
+ self.error = "Download interrupted"
117
+ return False
118
+
119
+ if result.get("nFailed", 0) > 0:
120
+ self.error = f"Failed segments: {result['nFailed']}"
121
+ return False
122
+
123
+ except Exception as ex:
124
+ self.error = str(ex)
115
125
  return False
116
-
117
- if result.get("nFailed", 0) > 0:
118
- self.error = f"Failed segments: {result['nFailed']}"
119
- return False
120
-
121
- except Exception as ex:
122
- self.error = str(ex)
123
- return False
124
126
 
125
127
  if not self.parser.pssh:
126
128
  print("No PSSH found: segments are not encrypted, skipping decryption.")
@@ -61,7 +61,7 @@ class MPDParser:
61
61
  self.base_url = mpd_url.rsplit('/', 1)[0] + '/'
62
62
 
63
63
  def parse(self, custom_headers):
64
- response = httpx.get(self.mpd_url, headers=custom_headers, timeout=max_timeout)
64
+ response = httpx.get(self.mpd_url, headers=custom_headers, timeout=max_timeout, follow_redirects=True)
65
65
  response.raise_for_status()
66
66
 
67
67
  root = ET.fromstring(response.content)
@@ -134,7 +134,7 @@ class MPD_Segments:
134
134
 
135
135
  try:
136
136
  headers = {'User-Agent': get_userAgent()}
137
- response = await client.get(init_url, headers=headers)
137
+ response = await client.get(init_url, headers=headers, follow_redirects=True)
138
138
 
139
139
  with open(concat_path, 'wb') as outfile:
140
140
  if response.status_code == 200:
@@ -160,7 +160,8 @@ class MPD_Segments:
160
160
  headers = {'User-Agent': get_userAgent()}
161
161
  for attempt in range(max_retry):
162
162
  try:
163
- resp = await client.get(url, headers=headers)
163
+ resp = await client.get(url, headers=headers, follow_redirects=True)
164
+
164
165
  if resp.status_code == 200:
165
166
  return idx, resp.content, attempt
166
167
  else:
@@ -214,7 +215,7 @@ class MPD_Segments:
214
215
  for attempt in range(max_retry):
215
216
  try:
216
217
  resp = await client.get(url, headers=headers)
217
-
218
+
218
219
  if resp.status_code == 200:
219
220
  return idx, resp.content, attempt
220
221
  else:
@@ -17,6 +17,7 @@ from rich.panel import Panel
17
17
  # Internal utilities
18
18
  from StreamingCommunity.Util.config_json import config_manager
19
19
  from StreamingCommunity.Util.headers import get_userAgent
20
+ from StreamingCommunity.Util.http_client import create_client
20
21
  from StreamingCommunity.Util.os import os_manager, internet_manager
21
22
  from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
22
23
 
@@ -38,7 +39,7 @@ DOWNLOAD_SPECIFIC_AUDIO = config_manager.get_list('M3U8_DOWNLOAD', 'specific_lis
38
39
  DOWNLOAD_SPECIFIC_SUBTITLE = config_manager.get_list('M3U8_DOWNLOAD', 'specific_list_subtitles')
39
40
  MERGE_SUBTITLE = config_manager.get_bool('M3U8_DOWNLOAD', 'merge_subs')
40
41
  CLEANUP_TMP = config_manager.get_bool('M3U8_DOWNLOAD', 'cleanup_tmp_folder')
41
- FILTER_CUSTOM_REOLUTION = str(config_manager.get('M3U8_PARSER', 'force_resolution')).strip().lower()
42
+ FILTER_CUSTOM_RESOLUTION = str(config_manager.get('M3U8_CONVERSION', 'force_resolution')).strip().lower()
42
43
  RETRY_LIMIT = config_manager.get_int('REQUESTS', 'max_retry')
43
44
  MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
44
45
  TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
@@ -62,7 +63,8 @@ class HLSClient:
62
63
  Returns:
63
64
  Response content/text or None if all retries fail
64
65
  """
65
- client = httpx.Client(headers=self.headers, timeout=MAX_TIMEOUT, follow_redirects=True)
66
+ # Use unified HTTP client (inherits timeout/verify/proxy from config)
67
+ client = create_client(headers=self.headers)
66
68
 
67
69
  for attempt in range(RETRY_LIMIT):
68
70
  try:
@@ -154,12 +156,12 @@ class M3U8Manager:
154
156
  self.sub_streams = []
155
157
 
156
158
  else:
157
- if str(FILTER_CUSTOM_REOLUTION) == "best":
159
+ if str(FILTER_CUSTOM_RESOLUTION) == "best":
158
160
  self.video_url, self.video_res = self.parser._video.get_best_uri()
159
- elif str(FILTER_CUSTOM_REOLUTION) == "worst":
161
+ elif str(FILTER_CUSTOM_RESOLUTION) == "worst":
160
162
  self.video_url, self.video_res = self.parser._video.get_worst_uri()
161
- elif str(FILTER_CUSTOM_REOLUTION).replace("p", "").replace("px", "").isdigit():
162
- resolution_value = int(str(FILTER_CUSTOM_REOLUTION).replace("p", "").replace("px", ""))
163
+ elif str(FILTER_CUSTOM_RESOLUTION).replace("p", "").replace("px", "").isdigit():
164
+ resolution_value = int(str(FILTER_CUSTOM_RESOLUTION).replace("p", "").replace("px", ""))
163
165
  self.video_url, self.video_res = self.parser._video.get_custom_uri(resolution_value)
164
166
  else:
165
167
  logging.error("Resolution not recognized.")
@@ -188,7 +190,7 @@ class M3U8Manager:
188
190
 
189
191
  console.print(
190
192
  f"[cyan bold]Video [/cyan bold] [green]Available:[/green] [purple]{', '.join(list_available_resolution)}[/purple] | "
191
- f"[red]Set:[/red] [purple]{FILTER_CUSTOM_REOLUTION}[/purple] | "
193
+ f"[red]Set:[/red] [purple]{FILTER_CUSTOM_RESOLUTION}[/purple] | "
192
194
  f"[yellow]Downloadable:[/yellow] [purple]{self.video_res[0]}x{self.video_res[1]}[/purple]"
193
195
  )
194
196
 
@@ -418,7 +420,7 @@ class HLS_Downloader:
418
420
  - is_master: Whether the M3U8 was a master playlist
419
421
  Or raises an exception if there's an error
420
422
  """
421
- console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n")
423
+ console.print("[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n")
422
424
 
423
425
  if TELEGRAM_BOT:
424
426
  bot = get_bot_instance()
@@ -435,7 +437,7 @@ class HLS_Downloader:
435
437
  'stopped': False
436
438
  }
437
439
  if TELEGRAM_BOT:
438
- bot.send_message(f"Contenuto già scaricato!", None)
440
+ bot.send_message("Contenuto già scaricato!", None)
439
441
  return response
440
442
 
441
443
  self.path_manager.setup_directories()
@@ -23,6 +23,7 @@ from rich.console import Console
23
23
  # Internal utilities
24
24
  from StreamingCommunity.Util.color import Colors
25
25
  from StreamingCommunity.Util.headers import get_userAgent
26
+ from StreamingCommunity.Util.http_client import create_client
26
27
  from StreamingCommunity.Util.config_json import config_manager
27
28
 
28
29
 
@@ -198,14 +199,7 @@ class M3U8_Segments:
198
199
  print("Signal handler must be set in the main thread")
199
200
 
200
201
  def _get_http_client(self):
201
- client_params = {
202
- 'headers': {'User-Agent': get_userAgent()},
203
- 'timeout': SEGMENT_MAX_TIMEOUT,
204
- 'follow_redirects': True,
205
- 'http2': False,
206
- 'verify': REQUEST_VERIFY
207
- }
208
- return httpx.Client(**client_params)
202
+ return create_client(headers={'User-Agent': get_userAgent()}, follow_redirects=True)
209
203
 
210
204
  def download_segment(self, ts_url: str, index: int, progress_bar: tqdm, backoff_factor: float = 1.1) -> None:
211
205
  """
@@ -264,7 +258,8 @@ class M3U8_Segments:
264
258
  with self.active_retries_lock:
265
259
  self.active_retries += 1
266
260
 
267
- sleep_time = backoff_factor * (2 ** attempt)
261
+ #sleep_time = backoff_factor * (2 ** attempt)
262
+ sleep_time = backoff_factor * (attempt + 1)
268
263
  logging.info(f"Retrying segment {index} in {sleep_time} seconds...")
269
264
  time.sleep(sleep_time)
270
265
 
@@ -7,10 +7,10 @@ import time
7
7
  import signal
8
8
  import logging
9
9
  from functools import partial
10
+ import threading
10
11
 
11
12
 
12
13
  # External libraries
13
- import httpx
14
14
  from tqdm import tqdm
15
15
  from rich.console import Console
16
16
  from rich.prompt import Prompt
@@ -19,6 +19,7 @@ from rich.panel import Panel
19
19
 
20
20
  # Internal utilities
21
21
  from StreamingCommunity.Util.headers import get_userAgent
22
+ from StreamingCommunity.Util.http_client import create_client
22
23
  from StreamingCommunity.Util.color import Colors
23
24
  from StreamingCommunity.Util.config_json import config_manager
24
25
  from StreamingCommunity.Util.os import internet_manager, os_manager
@@ -83,7 +84,7 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
83
84
  if os.path.exists(path):
84
85
  console.log("[red]Output file already exists.")
85
86
  if TELEGRAM_BOT:
86
- bot.send_message(f"Contenuto già scaricato!", None)
87
+ bot.send_message("Contenuto già scaricato!", None)
87
88
  return None, False
88
89
 
89
90
  if not (url.lower().startswith('http://') or url.lower().startswith('https://')):
@@ -101,16 +102,30 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
101
102
  else:
102
103
  headers['User-Agent'] = get_userAgent()
103
104
 
104
- # Set interrupt handler
105
+ # Set interrupt handler (only in main thread). In background threads (e.g., Django), skip custom signal handling.
105
106
  temp_path = f"{path}.temp"
106
107
  interrupt_handler = InterruptHandler()
107
- original_handler = signal.signal(signal.SIGINT, partial(signal_handler, interrupt_handler=interrupt_handler, original_handler=signal.getsignal(signal.SIGINT)))
108
+ original_handler = None
109
+ try:
110
+ if threading.current_thread() is threading.main_thread():
111
+ original_handler = signal.signal(
112
+ signal.SIGINT,
113
+ partial(
114
+ signal_handler,
115
+ interrupt_handler=interrupt_handler,
116
+ original_handler=signal.getsignal(signal.SIGINT),
117
+ ),
118
+ )
119
+ except Exception:
120
+ # If setting signal handler fails (non-main thread), continue without it
121
+ original_handler = None
108
122
 
109
123
  # Ensure the output directory exists
110
124
  os.makedirs(os.path.dirname(path), exist_ok=True)
111
125
 
112
126
  try:
113
- with httpx.Client(verify=REQUEST_VERIFY) as client:
127
+ # Use unified HTTP client (verify/timeout/proxy from config)
128
+ with create_client() as client:
114
129
  with client.stream("GET", url, headers=headers) as response:
115
130
  response.raise_for_status()
116
131
  total = int(response.headers.get('content-length', 0))
@@ -183,4 +198,8 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
183
198
  return None, interrupt_handler.kill_download
184
199
 
185
200
  finally:
186
- signal.signal(signal.SIGINT, original_handler)
201
+ if original_handler is not None:
202
+ try:
203
+ signal.signal(signal.SIGINT, original_handler)
204
+ except Exception:
205
+ pass
@@ -229,7 +229,7 @@ class TOR_downloader:
229
229
  torrent_info.num_seeds == 0 and
230
230
  torrent_info.state in ('stalledDL', 'missingFiles', 'error')):
231
231
 
232
- self.console.print(f"[bold red]Torrent not downloadable. No seeds or peers available. Removing...[/bold red]")
232
+ self.console.print("[bold red]Torrent not downloadable. No seeds or peers available. Removing...[/bold red]")
233
233
  self._remove_torrent(self.latest_torrent_hash)
234
234
  self.latest_torrent_hash = None
235
235
  return False
@@ -250,7 +250,7 @@ class TOR_downloader:
250
250
  """
251
251
  try:
252
252
  self.qb.torrents_delete(delete_files=delete_files, torrent_hashes=torrent_hash)
253
- self.console.print(f"[yellow]Torrent removed from client[/yellow]")
253
+ self.console.print("[yellow]Torrent removed from client[/yellow]")
254
254
  except Exception as e:
255
255
  logging.error(f"Error removing torrent: {str(e)}")
256
256
 
@@ -356,10 +356,8 @@ class TOR_downloader:
356
356
 
357
357
  # Get download statistics
358
358
  download_speed = torrent_info.dlspeed
359
- upload_speed = torrent_info.upspeed
360
359
  total_size = torrent_info.size
361
360
  downloaded_size = torrent_info.downloaded
362
- eta = torrent_info.eta # eta in seconds
363
361
 
364
362
  # Format sizes and speeds using the existing functions without modification
365
363
  downloaded_size_str = internet_manager.format_file_size(downloaded_size)
@@ -465,5 +463,5 @@ class TOR_downloader:
465
463
 
466
464
  try:
467
465
  self.qb.auth_log_out()
468
- except:
466
+ except Exception:
469
467
  pass
@@ -2,4 +2,12 @@
2
2
 
3
3
  from .HLS.downloader import HLS_Downloader
4
4
  from .MP4.downloader import MP4_downloader
5
- from .TOR.downloader import TOR_downloader
5
+ from .TOR.downloader import TOR_downloader
6
+ from .DASH.downloader import DASH_Downloader
7
+
8
+ __all__ = [
9
+ "HLS_Downloader",
10
+ "MP4_downloader",
11
+ "TOR_downloader",
12
+ "DASH_Downloader"
13
+ ]
@@ -1,4 +1,13 @@
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
5
+
6
+
7
+ __all__ = [
8
+ "join_video",
9
+ "join_audios",
10
+ "join_subtitle",
11
+ "print_duration_table",
12
+ "get_video_duration",
13
+ ]
@@ -1,6 +1,5 @@
1
1
  # 31.01.24
2
2
 
3
- import sys
4
3
  import logging
5
4
  import subprocess
6
5
  from typing import List, Dict, Tuple, Optional
@@ -110,13 +109,12 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
110
109
  if need_to_force_to_ts(video_path):
111
110
  #console.log("[red]Force input file to 'mpegts'.")
112
111
  ffmpeg_cmd.extend(['-f', 'mpegts'])
113
- vcodec = "libx264"
114
112
 
115
113
  # Insert input video path
116
114
  ffmpeg_cmd.extend(['-i', video_path])
117
115
 
118
116
  # Add output Parameters
119
- if USE_CODEC and codec != None:
117
+ if USE_CODEC and codec is not None:
120
118
  if USE_VCODEC:
121
119
  if codec.video_codec_name:
122
120
  if not USE_GPU:
@@ -162,7 +160,7 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
162
160
  print()
163
161
 
164
162
  else:
165
- console.log(f"[purple]FFmpeg [white][[cyan]Join video[white]] ...")
163
+ console.log("[purple]FFmpeg [white][[cyan]Join video[white]] ...")
166
164
  with suppress_output():
167
165
  capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video")
168
166
  print()
@@ -258,7 +256,7 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s
258
256
  print()
259
257
 
260
258
  else:
261
- console.log(f"[purple]FFmpeg [white][[cyan]Join audio[white]] ...")
259
+ console.log("[purple]FFmpeg [white][[cyan]Join audio[white]] ...")
262
260
  with suppress_output():
263
261
  capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio")
264
262
  print()
@@ -313,7 +311,7 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
313
311
  print()
314
312
 
315
313
  else:
316
- console.log(f"[purple]FFmpeg [white][[cyan]Join subtitle[white]] ...")
314
+ console.log("[purple]FFmpeg [white][[cyan]Join subtitle[white]] ...")
317
315
  with suppress_output():
318
316
  capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join subtitle")
319
317
  print()
@@ -76,7 +76,7 @@ def get_video_duration(file_path: str) -> float:
76
76
  try:
77
77
  return float(probe_result['format']['duration'])
78
78
 
79
- except:
79
+ except Exception:
80
80
  return 1
81
81
 
82
82
  except Exception as e:
@@ -3,4 +3,12 @@
3
3
  from .decryptor import M3U8_Decryption
4
4
  from .estimator import M3U8_Ts_Estimator
5
5
  from .parser import M3U8_Parser, M3U8_Codec
6
- from .url_fixer import M3U8_UrlFix
6
+ from .url_fixer import M3U8_UrlFix
7
+
8
+ __all__ = [
9
+ "M3U8_Decryption",
10
+ "M3U8_Ts_Estimator",
11
+ "M3U8_Parser",
12
+ "M3U8_Codec",
13
+ "M3U8_UrlFix"
14
+ ]
@@ -1,7 +1,6 @@
1
1
  # 03.04.24
2
2
 
3
3
  import sys
4
- import time
5
4
  import logging
6
5
  import importlib.util
7
6
 
@@ -10,18 +9,23 @@ import importlib.util
10
9
  from rich.console import Console
11
10
 
12
11
 
12
+ # Cryptodome imports
13
+ from Cryptodome.Cipher import AES
14
+ from Cryptodome.Util.Padding import unpad
15
+
16
+
13
17
  # Check if Cryptodome module is installed
14
18
  console = Console()
15
19
  crypto_spec = importlib.util.find_spec("Cryptodome")
16
20
  crypto_installed = crypto_spec is not None
17
21
 
22
+
18
23
  if not crypto_installed:
19
24
  console.log("[red]pycryptodomex non è installato. Per favore installalo. Leggi readme.md [Requirement].")
20
25
  sys.exit(0)
21
26
 
22
- logging.info("[cyan]Decrypy use: Cryptodomex")
23
- from Cryptodome.Cipher import AES
24
- from Cryptodome.Util.Padding import unpad
27
+
28
+ logging.info("[cyan]Decryption use: Cryptodomex")
25
29
 
26
30
 
27
31
 
@@ -121,12 +121,6 @@ class M3U8_Ts_Estimator:
121
121
  number_file_total_size = file_total_size.split(' ')[0]
122
122
  units_file_total_size = file_total_size.split(' ')[1]
123
123
 
124
- # Reduce lock contention by acquiring data with minimal synchronization
125
- retry_count = 0
126
- if self.segments_instance:
127
- with self.segments_instance.active_retries_lock:
128
- retry_count = self.segments_instance.active_retries
129
-
130
124
  # Get speed data outside of any locks
131
125
  speed_data = ["N/A", ""]
132
126
  with self.lock:
@@ -485,7 +485,7 @@ class M3U8_Parser:
485
485
  try:
486
486
  for playlist in m3u8_obj.playlists:
487
487
 
488
- there_is_codec = not playlist.stream_info.codecs is None
488
+ there_is_codec = playlist.stream_info.codecs is not None
489
489
  logging.info(f"There is coded: {there_is_codec}")
490
490
 
491
491
  if there_is_codec:
@@ -33,7 +33,7 @@ class M3U8_UrlFix:
33
33
  Returns:
34
34
  str: The full URL for the specified resource.
35
35
  """
36
- if self.url_playlist == None:
36
+ if self.url_playlist is None:
37
37
  logging.error("[M3U8_UrlFix] Cant generate full url, playlist not present")
38
38
  raise
39
39
 
@@ -1,4 +1,9 @@
1
1
  # 17.09.24
2
2
 
3
3
  from .tmdb import tmdb
4
- from .obj_tmbd import Json_film
4
+ from .obj_tmbd import Json_film
5
+
6
+ __all__ = [
7
+ "tmdb",
8
+ "Json_film"
9
+ ]
@@ -2,10 +2,8 @@
2
2
  "DEFAULT": {
3
3
  "debug": false,
4
4
  "show_message": true,
5
- "clean_console": true,
6
5
  "show_trending": true,
7
6
  "use_api": true,
8
- "not_close": false,
9
7
  "telegram_bot": true,
10
8
  "download_site_data": true,
11
9
  "validate_github_config": true
@@ -45,9 +43,7 @@
45
43
  "use_acodec": true,
46
44
  "use_bitrate": true,
47
45
  "use_gpu": false,
48
- "default_preset": "ultrafast"
49
- },
50
- "M3U8_PARSER": {
46
+ "default_preset": "ultrafast",
51
47
  "force_resolution": "Best"
52
48
  },
53
49
  "REQUESTS": {