StreamingCommunity 2.5.6__py3-none-any.whl → 2.5.8__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 (65) hide show
  1. StreamingCommunity/Api/Player/ddl.py +2 -3
  2. StreamingCommunity/Api/Site/1337xx/__init__.py +5 -6
  3. StreamingCommunity/Api/Site/1337xx/site.py +7 -14
  4. StreamingCommunity/Api/Site/1337xx/title.py +3 -5
  5. StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py +7 -6
  6. StreamingCommunity/Api/Site/altadefinizionegratis/film.py +14 -19
  7. StreamingCommunity/Api/Site/altadefinizionegratis/site.py +6 -14
  8. StreamingCommunity/Api/Site/animeunity/__init__.py +7 -7
  9. StreamingCommunity/Api/Site/animeunity/film_serie.py +29 -31
  10. StreamingCommunity/Api/Site/animeunity/site.py +14 -22
  11. StreamingCommunity/Api/Site/cb01new/__init__.py +5 -4
  12. StreamingCommunity/Api/Site/cb01new/film.py +2 -5
  13. StreamingCommunity/Api/Site/cb01new/site.py +5 -13
  14. StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +5 -4
  15. StreamingCommunity/Api/Site/ddlstreamitaly/series.py +12 -49
  16. StreamingCommunity/Api/Site/ddlstreamitaly/site.py +6 -16
  17. StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +2 -3
  18. StreamingCommunity/Api/Site/guardaserie/__init__.py +5 -4
  19. StreamingCommunity/Api/Site/guardaserie/series.py +12 -46
  20. StreamingCommunity/Api/Site/guardaserie/site.py +5 -13
  21. StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +10 -14
  22. StreamingCommunity/Api/Site/ilcorsaronero/__init__.py +5 -4
  23. StreamingCommunity/Api/Site/ilcorsaronero/site.py +5 -13
  24. StreamingCommunity/Api/Site/ilcorsaronero/title.py +3 -5
  25. StreamingCommunity/Api/Site/mostraguarda/__init__.py +2 -2
  26. StreamingCommunity/Api/Site/mostraguarda/film.py +4 -8
  27. StreamingCommunity/Api/Site/streamingcommunity/__init__.py +8 -7
  28. StreamingCommunity/Api/Site/streamingcommunity/film.py +14 -18
  29. StreamingCommunity/Api/Site/streamingcommunity/series.py +25 -76
  30. StreamingCommunity/Api/Site/streamingcommunity/site.py +11 -23
  31. StreamingCommunity/Api/Template/Util/__init__.py +8 -1
  32. StreamingCommunity/Api/Template/Util/manage_ep.py +46 -2
  33. StreamingCommunity/Api/Template/config_loader.py +71 -0
  34. StreamingCommunity/Lib/Downloader/HLS/downloader.py +60 -60
  35. StreamingCommunity/Lib/Downloader/HLS/segments.py +40 -15
  36. StreamingCommunity/Lib/Downloader/MP4/downloader.py +47 -40
  37. StreamingCommunity/Lib/FFmpeg/command.py +59 -3
  38. StreamingCommunity/Lib/M3U8/estimator.py +10 -12
  39. StreamingCommunity/Lib/M3U8/parser.py +12 -51
  40. StreamingCommunity/Lib/TMBD/tmdb.py +66 -99
  41. StreamingCommunity/TelegramHelp/telegram_bot.py +222 -68
  42. StreamingCommunity/Util/_jsonConfig.py +14 -13
  43. StreamingCommunity/Util/ffmpeg_installer.py +70 -64
  44. StreamingCommunity/Util/headers.py +11 -122
  45. StreamingCommunity/Util/os.py +65 -56
  46. StreamingCommunity/Util/table.py +62 -108
  47. StreamingCommunity/run.py +16 -11
  48. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/METADATA +57 -23
  49. StreamingCommunity-2.5.8.dist-info/RECORD +86 -0
  50. StreamingCommunity/Api/Site/1337xx/costant.py +0 -15
  51. StreamingCommunity/Api/Site/altadefinizionegratis/costant.py +0 -21
  52. StreamingCommunity/Api/Site/animeunity/costant.py +0 -21
  53. StreamingCommunity/Api/Site/cb01new/costant.py +0 -19
  54. StreamingCommunity/Api/Site/ddlstreamitaly/costant.py +0 -20
  55. StreamingCommunity/Api/Site/guardaserie/costant.py +0 -19
  56. StreamingCommunity/Api/Site/ilcorsaronero/costant.py +0 -19
  57. StreamingCommunity/Api/Site/mostraguarda/costant.py +0 -19
  58. StreamingCommunity/Api/Site/streamingcommunity/costant.py +0 -21
  59. StreamingCommunity/TelegramHelp/request_manager.py +0 -82
  60. StreamingCommunity/TelegramHelp/session.py +0 -56
  61. StreamingCommunity-2.5.6.dist-info/RECORD +0 -96
  62. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/LICENSE +0 -0
  63. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/WHEEL +0 -0
  64. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/entry_points.txt +0 -0
  65. {StreamingCommunity-2.5.6.dist-info → StreamingCommunity-2.5.8.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@
3
3
  import os
4
4
  import re
5
5
  import sys
6
+ import time
6
7
  import signal
7
8
  import logging
8
9
  from functools import partial
@@ -39,21 +40,38 @@ TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
39
40
 
40
41
 
41
42
 
42
- def signal_handler(signum, frame, kill_handler):
43
- """Signal handler for graceful interruption"""
44
- kill_handler[0] = True
45
- print("\nReceived interrupt signal. Completing current download...")
43
+ class InterruptHandler:
44
+ def __init__(self):
45
+ self.interrupt_count = 0
46
+ self.last_interrupt_time = 0
47
+ self.kill_download = False
48
+ self.force_quit = False
46
49
 
50
+ def signal_handler(signum, frame, interrupt_handler, original_handler):
51
+ """Enhanced signal handler for multiple interrupt scenarios"""
52
+ current_time = time.time()
53
+
54
+ # Reset counter if more than 2 seconds have passed since last interrupt
55
+ if current_time - interrupt_handler.last_interrupt_time > 2:
56
+ interrupt_handler.interrupt_count = 0
57
+
58
+ interrupt_handler.interrupt_count += 1
59
+ interrupt_handler.last_interrupt_time = current_time
60
+
61
+ if interrupt_handler.interrupt_count == 1:
62
+ interrupt_handler.kill_download = True
63
+ console.print("\n[bold yellow]First interrupt received. Download will complete and save. Press Ctrl+C three times quickly to force quit.[/bold yellow]")
64
+
65
+ elif interrupt_handler.interrupt_count >= 3:
66
+ interrupt_handler.force_quit = True
67
+ console.print("\n[bold red]Force quit activated. Saving partial download...[/bold red]")
68
+ signal.signal(signum, original_handler)
47
69
 
48
70
  def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = None):
49
71
  """
50
- Downloads an MP4 video from a given URL with robust error handling and SSL bypass.
51
-
52
- Parameters:
53
- - url (str): The URL of the MP4 video to download.
54
- - path (str): The local path where the downloaded MP4 file will be saved.
55
- - referer (str, optional): The referer header value.
56
- - headers_ (dict, optional): Custom headers for the request.
72
+ Downloads an MP4 video with enhanced interrupt handling.
73
+ - Single Ctrl+C: Completes download gracefully
74
+ - Triple Ctrl+C: Saves partial download and exits
57
75
  """
58
76
  if TELEGRAM_BOT:
59
77
  bot = get_bot_instance()
@@ -65,23 +83,19 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
65
83
  bot.send_message(f"Contenuto già scaricato!", None)
66
84
  return 400
67
85
 
68
- # Early return for link-only mode
69
86
  if GET_ONLY_LINK:
70
87
  return {'path': path, 'url': url}
71
88
 
72
- # Validate URL
73
89
  if not (url.lower().startswith('http://') or url.lower().startswith('https://')):
74
90
  logging.error(f"Invalid URL: {url}")
75
91
  console.print(f"[bold red]Invalid URL: {url}[/bold red]")
76
92
  return None
77
93
 
78
- # Prepare headers
79
94
  try:
80
95
  headers = {}
81
96
  if referer:
82
97
  headers['Referer'] = referer
83
98
 
84
- # Use custom headers if provided, otherwise use default user agent
85
99
  if headers_:
86
100
  headers.update(headers_)
87
101
  else:
@@ -93,17 +107,12 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
93
107
  return None
94
108
 
95
109
  temp_path = f"{path}.temp"
96
- kill_handler = [False] # Using list for mutable state
97
- original_handler = signal.signal(signal.SIGINT, partial(signal_handler, kill_handler=kill_handler))
110
+ interrupt_handler = InterruptHandler()
111
+ original_handler = signal.signal(signal.SIGINT, partial(signal_handler, interrupt_handler=interrupt_handler, original_handler=signal.getsignal(signal.SIGINT)))
98
112
 
99
113
  try:
100
- # Create a custom transport that bypasses SSL verification
101
- transport = httpx.HTTPTransport(
102
- verify=False,
103
- http2=True
104
- )
114
+ transport = httpx.HTTPTransport(verify=False, http2=True)
105
115
 
106
- # Download with streaming and progress tracking
107
116
  with httpx.Client(transport=transport, timeout=httpx.Timeout(60)) as client:
108
117
  with client.stream("GET", url, headers=headers, timeout=REQUEST_TIMEOUT) as response:
109
118
  response.raise_for_status()
@@ -119,21 +128,22 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
119
128
  bar_format=f"{Colors.YELLOW}[MP4]{Colors.WHITE}: "
120
129
  f"{Colors.RED}{{percentage:.2f}}% {Colors.MAGENTA}{{bar}} {Colors.WHITE}[ "
121
130
  f"{Colors.YELLOW}{{n_fmt}}{Colors.WHITE} / {Colors.RED}{{total_fmt}} {Colors.WHITE}] "
122
- f"{Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}} {Colors.WHITE}| "
123
- f"{Colors.YELLOW}{{rate_fmt}}{{postfix}} {Colors.WHITE}]",
131
+ f"{Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}{Colors.WHITE}, "
132
+ f"{Colors.YELLOW}{{rate_fmt}}{{postfix}} ",
124
133
  unit='iB',
125
134
  unit_scale=True,
126
135
  desc='Downloading',
127
- mininterval=0.05
136
+ mininterval=0.05,
137
+ file=sys.stdout # Using file=sys.stdout to force in-place updates because sys.stderr may not support carriage returns in this environment.
128
138
  )
129
139
 
130
140
  downloaded = 0
131
141
  with open(temp_path, 'wb') as file, progress_bar as bar:
132
142
  try:
133
143
  for chunk in response.iter_bytes(chunk_size=1024):
134
- if kill_handler[0]:
135
- console.print("\n[bold yellow]Interrupting download...[/bold yellow]")
136
- return None, True
144
+ if interrupt_handler.force_quit:
145
+ console.print("\n[bold red]Force quitting... Saving partial download.[/bold red]")
146
+ break
137
147
 
138
148
  if chunk:
139
149
  size = file.write(chunk)
@@ -141,18 +151,15 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
141
151
  bar.update(size)
142
152
 
143
153
  except KeyboardInterrupt:
144
- console.print("\n[bold red]Download interrupted by user.[/bold red]")
145
- if os.path.exists(temp_path):
146
- os.remove(temp_path)
147
- return None, True
154
+ if not interrupt_handler.force_quit:
155
+ interrupt_handler.kill_download = True
148
156
 
149
- # Rename temp file to final file
150
157
  if os.path.exists(temp_path):
151
158
  os.rename(temp_path, path)
152
159
 
153
160
  if os.path.exists(path):
154
161
  console.print(Panel(
155
- f"[bold green]Download completed![/bold green]\n"
162
+ f"[bold green]Download completed{' (Partial)' if interrupt_handler.force_quit else ''}![/bold green]\n"
156
163
  f"[cyan]File size: [bold red]{internet_manager.format_file_size(os.path.getsize(path))}[/bold red]\n"
157
164
  f"[cyan]Duration: [bold]{print_duration_table(path, description=False, return_string=True)}[/bold]",
158
165
  title=f"{os.path.basename(path.replace('.mp4', ''))}",
@@ -160,22 +167,22 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
160
167
  ))
161
168
 
162
169
  if TELEGRAM_BOT:
163
- message = f"Download completato\nDimensione: {internet_manager.format_file_size(os.path.getsize(path))}\nDurata: {print_duration_table(path, description=False, return_string=True)}\nTitolo: {os.path.basename(path.replace('.mp4', ''))}"
170
+ message = f"Download completato{'(Parziale)' if interrupt_handler.force_quit else ''}\nDimensione: {internet_manager.format_file_size(os.path.getsize(path))}\nDurata: {print_duration_table(path, description=False, return_string=True)}\nTitolo: {os.path.basename(path.replace('.mp4', ''))}"
164
171
  clean_message = re.sub(r'\[[a-zA-Z]+\]', '', message)
165
172
  bot.send_message(clean_message, None)
166
173
 
167
- return path, kill_handler[0]
174
+ return path, interrupt_handler.kill_download
175
+
168
176
  else:
169
177
  console.print("[bold red]Download failed or file is empty.[/bold red]")
170
- return None, kill_handler[0]
178
+ return None, interrupt_handler.kill_download
171
179
 
172
180
  except Exception as e:
173
181
  logging.error(f"Unexpected error: {e}")
174
182
  console.print(f"[bold red]Unexpected Error: {e}[/bold red]")
175
183
  if os.path.exists(temp_path):
176
184
  os.remove(temp_path)
177
- return None, kill_handler[0]
185
+ return None, interrupt_handler.kill_download
178
186
 
179
187
  finally:
180
- # Restore original signal handler
181
188
  signal.signal(signal.SIGINT, original_handler)
@@ -3,7 +3,7 @@
3
3
  import sys
4
4
  import logging
5
5
  import subprocess
6
- from typing import List, Dict
6
+ from typing import List, Dict, Tuple, Optional
7
7
 
8
8
 
9
9
  # Internal utilities
@@ -34,6 +34,62 @@ USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
34
34
  FFMPEG_PATH = os_summary.ffmpeg_path
35
35
 
36
36
 
37
+
38
+ def check_subtitle_encoders() -> Tuple[Optional[bool], Optional[bool]]:
39
+ """
40
+ Executes 'ffmpeg -encoders' and checks if 'mov_text' and 'webvtt' encoders are available.
41
+
42
+ Returns:
43
+ Tuple[Optional[bool], Optional[bool]]: A tuple containing (mov_text_supported, webvtt_supported)
44
+ Returns (None, None) if the FFmpeg command fails
45
+ """
46
+ try:
47
+ result = subprocess.run(
48
+ [FFMPEG_PATH, '-encoders'],
49
+ capture_output=True,
50
+ text=True,
51
+ check=True
52
+ )
53
+
54
+ # Check for encoder presence in output
55
+ output = result.stdout
56
+ mov_text_supported = "mov_text" in output
57
+ webvtt_supported = "webvtt" in output
58
+
59
+ return mov_text_supported, webvtt_supported
60
+
61
+ except subprocess.CalledProcessError as e:
62
+ print(f"Error executing 'ffmpeg -encoders': {e}")
63
+ return None, None
64
+
65
+
66
+ def select_subtitle_encoder() -> Optional[str]:
67
+ """
68
+ Determines the best available subtitle encoder to use.
69
+ Prefers mov_text over webvtt if both are available.
70
+
71
+ Returns:
72
+ Optional[str]: Name of the best available encoder ('mov_text' or 'webvtt')
73
+ or None if no supported encoder is found
74
+ """
75
+ mov_text_supported, webvtt_supported = check_subtitle_encoders()
76
+
77
+ # Return early if check failed
78
+ if mov_text_supported is None:
79
+ return None
80
+
81
+ # Prioritize mov_text over webvtt
82
+ if mov_text_supported:
83
+ logging.info("Using 'mov_text' as the subtitle encoder.")
84
+ return "mov_text"
85
+ elif webvtt_supported:
86
+ logging.info("Using 'webvtt' as the subtitle encoder.")
87
+ return "webvtt"
88
+
89
+ logging.error("No supported subtitle encoder found.")
90
+ return None
91
+
92
+
37
93
  def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None):
38
94
  """
39
95
  Joins single ts video file to mp4
@@ -238,9 +294,9 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
238
294
 
239
295
  # Add output Parameters
240
296
  if USE_CODEC:
241
- ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy', '-c:s', 'mov_text'])
297
+ ffmpeg_cmd.extend(['-c:v', 'copy', '-c:a', 'copy', '-c:s', select_subtitle_encoder()])
242
298
  else:
243
- ffmpeg_cmd.extend(['-c', 'copy', '-c:s', 'mov_text'])
299
+ ffmpeg_cmd.extend(['-c', 'copy', '-c:s', select_subtitle_encoder()])
244
300
 
245
301
  # Overwrite
246
302
  ffmpeg_cmd += [out_path, "-y"]
@@ -106,12 +106,12 @@ class M3U8_Ts_Estimator:
106
106
  try:
107
107
  self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
108
108
 
109
- downloaded_file_size_str = internet_manager.format_file_size(self.now_downloaded_size)
109
+ #downloaded_file_size_str = internet_manager.format_file_size(self.now_downloaded_size)
110
110
  file_total_size = self.calculate_total_size()
111
111
 
112
- number_file_downloaded = downloaded_file_size_str.split(' ')[0]
112
+ #number_file_downloaded = downloaded_file_size_str.split(' ')[0]
113
113
  number_file_total_size = file_total_size.split(' ')[0]
114
- units_file_downloaded = downloaded_file_size_str.split(' ')[1]
114
+ #units_file_downloaded = downloaded_file_size_str.split(' ')[1]
115
115
  units_file_total_size = file_total_size.split(' ')[1]
116
116
 
117
117
  if USE_LARGE_BAR:
@@ -124,21 +124,19 @@ class M3U8_Ts_Estimator:
124
124
  average_internet_speed = "N/A"
125
125
  average_internet_unit = ""
126
126
 
127
- # Retrieve retry count from segments_instance
128
127
  retry_count = self.segments_instance.active_retries if self.segments_instance else 0
129
128
  progress_str = (
130
- f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
131
- f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
132
- f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit} "
133
- f"{Colors.WHITE}| {Colors.GREEN}CRR {Colors.RED}{retry_count}"
129
+ #f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
130
+ f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
131
+ f"{Colors.WHITE} {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
132
+ f"{Colors.WHITE} {Colors.GREEN}CRR {Colors.RED}{retry_count} "
134
133
  )
135
134
  else:
136
- # Retrieve retry count from segments_instance
137
135
  retry_count = self.segments_instance.active_retries if self.segments_instance else 0
138
136
  progress_str = (
139
- f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
140
- f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
141
- f"{Colors.WHITE}| {Colors.GREEN}CRR {Colors.RED}{retry_count}"
137
+ #f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< "
138
+ f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
139
+ f"{Colors.WHITE} {Colors.GREEN}CRR {Colors.RED}{retry_count} "
142
140
  )
143
141
 
144
142
  progress_counter.set_postfix_str(progress_str)
@@ -39,12 +39,17 @@ CODEC_MAPPINGS = {
39
39
  }
40
40
 
41
41
  RESOLUTIONS = [
42
- (7680, 4320),
43
- (3840, 2160),
44
- (2560, 1440),
45
- (1920, 1080),
46
- (1280, 720),
47
- (640, 480)
42
+ (7680, 4320), # 8K
43
+ (3840, 2160), # 4K
44
+ (2560, 1440), # 1440p
45
+ (1920, 1080), # 1080p
46
+ (1280, 720), # 720p
47
+ (640, 480), # VGA
48
+ (640, 360), # 360p
49
+ (480, 320), # HVGA
50
+ (426, 240), # 240p
51
+ (320, 240), # QVGA
52
+ (256, 144), # 144p
48
53
  ]
49
54
 
50
55
 
@@ -377,47 +382,6 @@ class M3U8_Subtitle:
377
382
  return subtitle
378
383
  return None
379
384
 
380
- def download_all(self, custom_subtitle):
381
- """
382
- Download all subtitles listed in the object's attributes, filtering based on a provided list of custom subtitles.
383
-
384
- Parameters:
385
- - custom_subtitle (list): A list of custom subtitles to download.
386
-
387
- Returns:
388
- list: A list containing dictionaries with subtitle information including name, language, and URI.
389
- """
390
-
391
- output = [] # Initialize an empty list to store subtitle information
392
-
393
- # Iterate through all available subtitles
394
- for obj_subtitle in self.subtitle_get_all_uris_and_names():
395
-
396
- # Check if the subtitle name is not in the list of custom subtitles, and skip if not found
397
- if obj_subtitle.get('name') not in custom_subtitle:
398
- continue
399
-
400
- # Send a request to retrieve the subtitle content
401
- logging.info(f"Download subtitle: {obj_subtitle.get('name')}")
402
- response_subitle = httpx.get(obj_subtitle.get('uri'))
403
-
404
- try:
405
- # Try to extract the VTT URL from the subtitle content
406
- sub_parse = M3U8_Parser()
407
- sub_parse.parse_data(obj_subtitle.get('uri'), response_subitle.text)
408
- url_subititle = sub_parse.subtitle[0]
409
-
410
- output.append({
411
- 'name': obj_subtitle.get('name'),
412
- 'language': obj_subtitle.get('language'),
413
- 'uri': url_subititle
414
- })
415
-
416
- except Exception as e:
417
- logging.error(f"Cant download: {obj_subtitle.get('name')}, error: {e}")
418
-
419
- return output
420
-
421
385
 
422
386
  class M3U8_Parser:
423
387
  def __init__(self):
@@ -555,7 +519,6 @@ class M3U8_Parser:
555
519
  - m3u8_obj: The M3U8 object containing encryption keys.
556
520
  """
557
521
  try:
558
-
559
522
  if m3u8_obj.key is not None:
560
523
  if self.keys is None:
561
524
  self.keys = {
@@ -564,7 +527,6 @@ class M3U8_Parser:
564
527
  'uri': m3u8_obj.key.uri
565
528
  }
566
529
 
567
-
568
530
  except Exception as e:
569
531
  logging.error(f"Error parsing encryption keys: {e}")
570
532
  sys.exit(0)
@@ -629,7 +591,6 @@ class M3U8_Parser:
629
591
  """
630
592
  Initialize variables for video, audio, and subtitle playlists.
631
593
  """
632
-
633
594
  self._video = M3U8_Video(self.video_playlist)
634
595
  self._audio = M3U8_Audio(self.audio_playlist)
635
596
  self._subtitle = M3U8_Subtitle(self.subtitle_playlist)
@@ -663,4 +624,4 @@ class M3U8_Parser:
663
624
  if return_string:
664
625
  return f"[yellow]{int(hours)}[red]h [yellow]{int(minutes)}[red]m [yellow]{int(seconds)}[red]s"
665
626
  else:
666
- return {'h': int(hours), 'm': int(minutes), 's': int(seconds)}
627
+ return {'h': int(hours), 'm': int(minutes), 's': int(seconds)}