StreamingCommunity 1.7.6__py3-none-any.whl → 1.9.1__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 (101) hide show
  1. StreamingCommunity/{Src/Api → Api}/Player/Helper/Vixcloud/js_parser.py +4 -1
  2. StreamingCommunity/{Src/Api → Api}/Player/Helper/Vixcloud/util.py +166 -166
  3. StreamingCommunity/{Src/Api → Api}/Player/ddl.py +89 -89
  4. StreamingCommunity/{Src/Api → Api}/Player/maxstream.py +151 -151
  5. StreamingCommunity/{Src/Api → Api}/Player/supervideo.py +193 -193
  6. StreamingCommunity/{Src/Api → Api}/Player/vixcloud.py +224 -212
  7. StreamingCommunity/{Src/Api → Api}/Site/1337xx/__init__.py +50 -50
  8. StreamingCommunity/{Src/Api → Api}/Site/1337xx/costant.py +15 -15
  9. StreamingCommunity/{Src/Api → Api}/Site/1337xx/site.py +83 -83
  10. StreamingCommunity/{Src/Api → Api}/Site/1337xx/title.py +66 -66
  11. StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/__init__.py +50 -50
  12. StreamingCommunity/{Src/Api/Site/mostraguarda → Api/Site/altadefinizione}/costant.py +15 -15
  13. StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/film.py +69 -69
  14. StreamingCommunity/{Src/Api → Api}/Site/altadefinizione/site.py +86 -86
  15. StreamingCommunity/{Src/Api → Api}/Site/animeunity/__init__.py +50 -50
  16. StreamingCommunity/{Src/Api/Site/altadefinizione → Api/Site/animeunity}/costant.py +15 -15
  17. StreamingCommunity/{Src/Api → Api}/Site/animeunity/film_serie.py +130 -131
  18. StreamingCommunity/{Src/Api → Api}/Site/animeunity/site.py +164 -164
  19. StreamingCommunity/{Src/Api → Api}/Site/animeunity/util/ScrapeSerie.py +3 -3
  20. StreamingCommunity/{Src/Api → Api}/Site/bitsearch/__init__.py +51 -51
  21. StreamingCommunity/{Src/Api → Api}/Site/bitsearch/costant.py +15 -15
  22. StreamingCommunity/{Src/Api → Api}/Site/bitsearch/site.py +84 -84
  23. StreamingCommunity/{Src/Api → Api}/Site/bitsearch/title.py +47 -47
  24. StreamingCommunity/{Src/Api → Api}/Site/cb01new/__init__.py +51 -51
  25. StreamingCommunity/{Src/Api → Api}/Site/cb01new/costant.py +15 -15
  26. StreamingCommunity/{Src/Api → Api}/Site/cb01new/film.py +69 -69
  27. StreamingCommunity/{Src/Api → Api}/Site/cb01new/site.py +74 -74
  28. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/__init__.py +57 -57
  29. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/costant.py +16 -16
  30. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/series.py +141 -142
  31. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/site.py +92 -92
  32. StreamingCommunity/{Src/Api → Api}/Site/ddlstreamitaly/util/ScrapeSerie.py +84 -82
  33. StreamingCommunity/{Src/Api → Api}/Site/guardaserie/__init__.py +52 -52
  34. StreamingCommunity/{Src/Api/Site/piratebays → Api/Site/guardaserie}/costant.py +15 -15
  35. StreamingCommunity/{Src/Api → Api}/Site/guardaserie/series.py +195 -195
  36. StreamingCommunity/{Src/Api → Api}/Site/guardaserie/site.py +84 -84
  37. StreamingCommunity/{Src/Api → Api}/Site/guardaserie/util/ScrapeSerie.py +110 -110
  38. StreamingCommunity/{Src/Api → Api}/Site/mostraguarda/__init__.py +48 -48
  39. StreamingCommunity/{Src/Api/Site/animeunity → Api/Site/mostraguarda}/costant.py +15 -15
  40. StreamingCommunity/{Src/Api → Api}/Site/mostraguarda/film.py +94 -94
  41. StreamingCommunity/{Src/Api → Api}/Site/piratebays/__init__.py +50 -50
  42. StreamingCommunity/{Src/Api/Site/guardaserie → Api/Site/piratebays}/costant.py +15 -15
  43. StreamingCommunity/{Src/Api → Api}/Site/piratebays/site.py +88 -88
  44. StreamingCommunity/{Src/Api → Api}/Site/piratebays/title.py +45 -45
  45. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/__init__.py +55 -55
  46. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/costant.py +15 -15
  47. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/film.py +70 -70
  48. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/series.py +205 -203
  49. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/site.py +125 -125
  50. StreamingCommunity/{Src/Api → Api}/Site/streamingcommunity/util/ScrapeSerie.py +3 -3
  51. StreamingCommunity/{Src/Api → Api}/Template/Class/SearchType.py +101 -101
  52. StreamingCommunity/{Src/Api → Api}/Template/Util/__init__.py +4 -4
  53. StreamingCommunity/{Src/Api → Api}/Template/Util/get_domain.py +137 -137
  54. StreamingCommunity/{Src/Api → Api}/Template/Util/manage_ep.py +153 -153
  55. StreamingCommunity/{Src/Api → Api}/Template/Util/recall_search.py +37 -37
  56. StreamingCommunity/Api/Template/__init__.py +3 -0
  57. StreamingCommunity/{Src/Api → Api}/Template/site.py +87 -87
  58. StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/downloader.py +968 -968
  59. StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/proxyes.py +110 -110
  60. StreamingCommunity/{Src/Lib → Lib}/Downloader/HLS/segments.py +538 -540
  61. StreamingCommunity/{Src/Lib → Lib}/Downloader/MP4/downloader.py +156 -156
  62. StreamingCommunity/{Src/Lib → Lib}/Downloader/TOR/downloader.py +222 -222
  63. StreamingCommunity/{Src/Lib → Lib}/Downloader/__init__.py +4 -4
  64. StreamingCommunity/{Src/Lib → Lib}/Driver/driver_1.py +76 -76
  65. StreamingCommunity/{Src/Lib → Lib}/FFmpeg/__init__.py +4 -4
  66. StreamingCommunity/{Src/Lib → Lib}/FFmpeg/capture.py +170 -170
  67. StreamingCommunity/{Src/Lib → Lib}/FFmpeg/command.py +292 -292
  68. StreamingCommunity/{Src/Lib → Lib}/FFmpeg/util.py +241 -241
  69. StreamingCommunity/{Src/Lib → Lib}/M3U8/__init__.py +5 -5
  70. StreamingCommunity/{Src/Lib → Lib}/M3U8/decryptor.py +164 -129
  71. StreamingCommunity/{Src/Lib → Lib}/M3U8/estimator.py +175 -172
  72. StreamingCommunity/{Src/Lib → Lib}/M3U8/parser.py +666 -666
  73. StreamingCommunity/{Src/Lib → Lib}/M3U8/url_fixer.py +51 -51
  74. StreamingCommunity/Lib/TMBD/__init__.py +2 -0
  75. StreamingCommunity/{Src/Lib → Lib}/TMBD/obj_tmbd.py +39 -39
  76. StreamingCommunity/{Src/Lib → Lib}/TMBD/tmdb.py +345 -345
  77. StreamingCommunity/{Src/Upload → Upload}/update.py +68 -64
  78. StreamingCommunity/{Src/Upload → Upload}/version.py +5 -5
  79. StreamingCommunity/{Src/Util → Util}/_jsonConfig.py +204 -204
  80. StreamingCommunity/{Src/Util → Util}/call_stack.py +42 -42
  81. StreamingCommunity/{Src/Util → Util}/color.py +20 -20
  82. StreamingCommunity/{Src/Util → Util}/console.py +12 -12
  83. StreamingCommunity/Util/ffmpeg_installer.py +275 -0
  84. StreamingCommunity/{Src/Util → Util}/headers.py +147 -147
  85. StreamingCommunity/{Src/Util → Util}/logger.py +53 -53
  86. StreamingCommunity/{Src/Util → Util}/message.py +46 -46
  87. StreamingCommunity/{Src/Util → Util}/os.py +514 -417
  88. StreamingCommunity/{Src/Util → Util}/table.py +163 -163
  89. StreamingCommunity/run.py +202 -196
  90. {StreamingCommunity-1.7.6.dist-info → StreamingCommunity-1.9.1.dist-info}/METADATA +126 -60
  91. StreamingCommunity-1.9.1.dist-info/RECORD +95 -0
  92. {StreamingCommunity-1.7.6.dist-info → StreamingCommunity-1.9.1.dist-info}/WHEEL +1 -1
  93. StreamingCommunity/Src/Api/Site/animeunity/anime.py +0 -126
  94. StreamingCommunity/Src/Api/Site/ddlstreamitaly/Player/ScrapeSerie.py +0 -83
  95. StreamingCommunity/Src/Api/Site/guardaserie/Player/ScrapeSerie.py +0 -110
  96. StreamingCommunity/Src/Api/Template/__init__.py +0 -3
  97. StreamingCommunity/Src/Lib/TMBD/__init__.py +0 -2
  98. StreamingCommunity-1.7.6.dist-info/RECORD +0 -97
  99. {StreamingCommunity-1.7.6.dist-info → StreamingCommunity-1.9.1.dist-info}/LICENSE +0 -0
  100. {StreamingCommunity-1.7.6.dist-info → StreamingCommunity-1.9.1.dist-info}/entry_points.txt +0 -0
  101. {StreamingCommunity-1.7.6.dist-info → StreamingCommunity-1.9.1.dist-info}/top_level.txt +0 -0
@@ -1,129 +1,164 @@
1
- # 03.04.24
2
-
3
- import sys
4
- import logging
5
- import subprocess
6
- import importlib.util
7
-
8
-
9
- # Internal utilities
10
- from StreamingCommunity.Src.Util.console import console
11
-
12
-
13
- # Check if Crypto module is installed
14
- crypto_spec = importlib.util.find_spec("Crypto")
15
- crypto_installed = crypto_spec is not None
16
-
17
-
18
- if crypto_installed:
19
- logging.info("Decrypy use: Crypto")
20
- from Crypto.Cipher import AES # type: ignore
21
- from Crypto.Util.Padding import unpad # type: ignore
22
-
23
- class M3U8_Decryption:
24
- """
25
- Class for decrypting M3U8 playlist content using AES encryption when the Crypto module is available.
26
- """
27
- def __init__(self, key: bytes, iv: bytes, method: str) -> None:
28
- """
29
- Initialize the M3U8_Decryption object.
30
-
31
- Parameters:
32
- - key (bytes): The encryption key.
33
- - iv (bytes): The initialization vector (IV).
34
- - method (str): The encryption method.
35
- """
36
- self.key = key
37
- if "0x" in str(iv):
38
- self.iv = bytes.fromhex(iv.replace("0x", ""))
39
- else:
40
- self.iv = iv
41
- self.method = method
42
- logging.info(f"Decrypt add: ('key': {self.key}, 'iv': {self.iv}, 'method': {self.method})")
43
-
44
- def decrypt(self, ciphertext: bytes) -> bytes:
45
- """
46
- Decrypt the ciphertext using the specified encryption method.
47
-
48
- Parameters:
49
- - ciphertext (bytes): The encrypted content to decrypt.
50
-
51
- Returns:
52
- bytes: The decrypted content.
53
- """
54
- if self.method == "AES":
55
- cipher = AES.new(self.key, AES.MODE_ECB)
56
- decrypted_data = cipher.decrypt(ciphertext)
57
- return unpad(decrypted_data, AES.block_size)
58
-
59
- elif self.method == "AES-128":
60
- cipher = AES.new(self.key[:16], AES.MODE_CBC, iv=self.iv)
61
- decrypted_data = cipher.decrypt(ciphertext)
62
- return unpad(decrypted_data, AES.block_size)
63
-
64
- elif self.method == "AES-128-CTR":
65
- cipher = AES.new(self.key[:16], AES.MODE_CTR, nonce=self.iv)
66
- return cipher.decrypt(ciphertext)
67
-
68
- else:
69
- raise ValueError("Invalid or unsupported method")
70
-
71
- else:
72
-
73
- # Check if openssl command is available
74
- try:
75
- openssl_available = subprocess.run(["openssl", "version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
76
- logging.info("Decrypy use: OPENSSL")
77
- except:
78
- openssl_available = False
79
-
80
- if not openssl_available:
81
- console.log("[red]Neither python library: pycryptodome nor openssl software is installed. Please install either one of them. Read readme.md [Requirement].")
82
- sys.exit(0)
83
-
84
- class M3U8_Decryption:
85
- """
86
- Class for decrypting M3U8 playlist content using OpenSSL when the Crypto module is not available.
87
- """
88
- def __init__(self, key: bytes, iv: bytes, method: str) -> None:
89
- """
90
- Initialize the M3U8_Decryption object.
91
-
92
- Parameters:
93
- - key (bytes): The encryption key.
94
- - iv (bytes): The initialization vector (IV).
95
- - method (str): The encryption method.
96
- """
97
- self.key = key
98
- if "0x" in str(iv):
99
- self.iv = bytes.fromhex(iv.replace("0x", ""))
100
- else:
101
- self.iv = iv
102
- self.method = method
103
- logging.info(f"Decrypt add: ('key': {self.key}, 'iv': {self.iv}, 'method': {self.method})")
104
-
105
- def decrypt(self, ciphertext: bytes) -> bytes:
106
- """
107
- Decrypt the ciphertext using the specified encryption method.
108
-
109
- Parameters:
110
- - ciphertext (bytes): The encrypted content to decrypt.
111
-
112
- Returns:
113
- bytes: The decrypted content.
114
- """
115
- if self.method == "AES":
116
- openssl_cmd = f'openssl enc -d -aes-256-ecb -K {self.key.hex()} -nosalt'
117
- elif self.method == "AES-128":
118
- openssl_cmd = f'openssl enc -d -aes-128-cbc -K {self.key[:16].hex()} -iv {self.iv.hex()}'
119
- elif self.method == "AES-128-CTR":
120
- openssl_cmd = f'openssl enc -d -aes-128-ctr -K {self.key[:16].hex()} -iv {self.iv.hex()}'
121
- else:
122
- raise ValueError("Invalid or unsupported method")
123
-
124
- try:
125
- decrypted_data = subprocess.check_output(openssl_cmd.split(), input=ciphertext, stderr=subprocess.STDOUT)
126
- except subprocess.CalledProcessError as e:
127
- raise ValueError(f"Decryption failed: {e.output.decode()}")
128
-
129
- return decrypted_data
1
+ # 03.04.24
2
+
3
+ import sys
4
+ import time
5
+ import logging
6
+ import subprocess
7
+ import importlib.util
8
+
9
+
10
+ # Internal utilities
11
+ from StreamingCommunity.Util.console import console
12
+
13
+
14
+ # Check if Crypto module is installed
15
+ crypto_spec = importlib.util.find_spec("Crypto")
16
+ crypto_installed = crypto_spec is not None
17
+
18
+
19
+ if crypto_installed:
20
+ logging.info("Decrypy use: Crypto")
21
+ from Crypto.Cipher import AES # type: ignore
22
+ from Crypto.Util.Padding import unpad # type: ignore
23
+
24
+ class M3U8_Decryption:
25
+ """
26
+ Class for decrypting M3U8 playlist content using AES encryption when the Crypto module is available.
27
+ """
28
+ def __init__(self, key: bytes, iv: bytes, method: str) -> None:
29
+ """
30
+ Initialize the M3U8_Decryption object.
31
+
32
+ Parameters:
33
+ - key (bytes): The encryption key.
34
+ - iv (bytes): The initialization vector (IV).
35
+ - method (str): The encryption method.
36
+ """
37
+ self.key = key
38
+ self.iv = iv
39
+ if "0x" in str(iv):
40
+ self.iv = bytes.fromhex(iv.replace("0x", ""))
41
+ self.method = method
42
+
43
+ # Precreate cipher based on encryption method
44
+ if self.method == "AES":
45
+ self.cipher = AES.new(self.key, AES.MODE_ECB)
46
+ elif self.method == "AES-128":
47
+ self.cipher = AES.new(self.key[:16], AES.MODE_CBC, iv=self.iv)
48
+ elif self.method == "AES-128-CTR":
49
+ self.cipher = AES.new(self.key[:16], AES.MODE_CTR, nonce=self.iv)
50
+ else:
51
+ raise ValueError("Invalid or unsupported method")
52
+
53
+ def decrypt(self, ciphertext: bytes) -> bytes:
54
+ """
55
+ Decrypt the ciphertext using the specified encryption method.
56
+
57
+ Parameters:
58
+ - ciphertext (bytes): The encrypted content to decrypt.
59
+
60
+ Returns:
61
+ bytes: The decrypted content.
62
+ """
63
+ start = time.perf_counter_ns()
64
+
65
+ # Decrypt based on encryption method
66
+ if self.method in {"AES", "AES-128"}:
67
+ decrypted_data = self.cipher.decrypt(ciphertext)
68
+ decrypted_content = unpad(decrypted_data, AES.block_size)
69
+
70
+ elif self.method == "AES-128-CTR":
71
+ decrypted_content = self.cipher.decrypt(ciphertext)
72
+ else:
73
+ raise ValueError("Invalid or unsupported method")
74
+
75
+ end = time.perf_counter_ns()
76
+
77
+ # Calculate elapsed time with high precision
78
+ elapsed_nanoseconds = end - start
79
+ elapsed_milliseconds = elapsed_nanoseconds / 1_000_000
80
+ elapsed_seconds = elapsed_nanoseconds / 1_000_000_000
81
+
82
+ # Print performance metrics
83
+ logging.info(f"[Crypto Decryption Performance]")
84
+ logging.info(f"Method: {self.method}")
85
+ logging.info(f"Decryption Time: {elapsed_milliseconds:.4f} ms ({elapsed_seconds:.6f} s)")
86
+ logging.info(f"Decrypted Content Length: {len(decrypted_content)} bytes")
87
+
88
+ return decrypted_content
89
+
90
+
91
+ else:
92
+
93
+ # Check if openssl command is available
94
+ try:
95
+ openssl_available = subprocess.run(["openssl", "version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
96
+ logging.info("Decrypy use: OPENSSL")
97
+ except:
98
+ openssl_available = False
99
+
100
+ if not openssl_available:
101
+ console.log("[red]Neither python library: pycryptodome nor openssl software is installed. Please install either one of them. Read readme.md [Requirement].")
102
+ sys.exit(0)
103
+
104
+ class M3U8_Decryption:
105
+ """
106
+ Class for decrypting M3U8 playlist content using OpenSSL when the Crypto module is not available.
107
+ """
108
+ def __init__(self, key: bytes, iv: bytes, method: str) -> None:
109
+ """
110
+ Initialize the M3U8_Decryption object.
111
+
112
+ Parameters:
113
+ - key (bytes): The encryption key.
114
+ - iv (bytes): The initialization vector (IV).
115
+ - method (str): The encryption method.
116
+ """
117
+ self.key = key
118
+ self.iv = iv
119
+ if "0x" in str(iv):
120
+ self.iv = bytes.fromhex(iv.replace("0x", ""))
121
+ self.method = method
122
+ logging.info(f"Decrypt add: ('key': {self.key}, 'iv': {self.iv}, 'method': {self.method})")
123
+
124
+ def decrypt(self, ciphertext: bytes) -> bytes:
125
+ """
126
+ Decrypt the ciphertext using the specified encryption method.
127
+
128
+ Parameters:
129
+ - ciphertext (bytes): The encrypted content to decrypt.
130
+
131
+ Returns:
132
+ bytes: The decrypted content.
133
+ """
134
+ start = time.perf_counter_ns()
135
+
136
+ # Construct OpenSSL command based on encryption method
137
+ if self.method == "AES":
138
+ openssl_cmd = f'openssl enc -d -aes-256-ecb -K {self.key.hex()} -nosalt'
139
+ elif self.method == "AES-128":
140
+ openssl_cmd = f'openssl enc -d -aes-128-cbc -K {self.key[:16].hex()} -iv {self.iv.hex()}'
141
+ elif self.method == "AES-128-CTR":
142
+ openssl_cmd = f'openssl enc -d -aes-128-ctr -K {self.key[:16].hex()} -iv {self.iv.hex()}'
143
+ else:
144
+ raise ValueError("Invalid or unsupported method")
145
+
146
+ try:
147
+ decrypted_content = subprocess.check_output(openssl_cmd.split(), input=ciphertext, stderr=subprocess.STDOUT)
148
+ except subprocess.CalledProcessError as e:
149
+ raise ValueError(f"Decryption failed: {e.output.decode()}")
150
+
151
+ end = time.perf_counter_ns()
152
+
153
+ # Calculate elapsed time with high precision
154
+ elapsed_nanoseconds = end - start
155
+ elapsed_milliseconds = elapsed_nanoseconds / 1_000_000
156
+ elapsed_seconds = elapsed_nanoseconds / 1_000_000_000
157
+
158
+ # Print performance metrics
159
+ logging.info(f"[OpenSSL Decryption Performance]")
160
+ logging.info(f"Method: {self.method}")
161
+ logging.info(f"Decryption Time: {elapsed_milliseconds:.4f} ms ({elapsed_seconds:.6f} s)")
162
+ logging.info(f"Decrypted Content Length: {len(decrypted_content)} bytes")
163
+
164
+ return decrypted_content
@@ -1,173 +1,176 @@
1
- # 20.02.24
2
-
3
- import os
4
- import time
5
- import logging
6
- import threading
7
- from collections import deque
8
-
9
-
10
- # External libraries
11
- import psutil
12
- from tqdm import tqdm
13
-
14
-
15
- # Internal utilities
16
- from StreamingCommunity.Src.Util.color import Colors
17
- from StreamingCommunity.Src.Util.os import internet_manager
18
- from StreamingCommunity.Src.Util._jsonConfig import config_manager
19
-
20
-
21
- # Variable
22
- TQDM_USE_LARGE_BAR = config_manager.get_int('M3U8_DOWNLOAD', 'tqdm_use_large_bar')
23
-
24
-
25
- class M3U8_Ts_Estimator:
26
- def __init__(self, total_segments: int):
27
- """
28
- Initialize the M3U8_Ts_Estimator object.
29
-
30
- Parameters:
31
- - total_segments (int): Length of total segments to download.
32
- """
33
- self.ts_file_sizes = []
34
- self.now_downloaded_size = 0
35
- self.total_segments = total_segments
36
- self.lock = threading.Lock()
37
- self.speed = {"upload": "N/A", "download": "N/A"} # Default to N/A
38
- self.speed_thread = threading.Thread(target=self.capture_speed)
39
- self.speed_thread.daemon = True
40
- self.speed_thread.start()
41
-
42
- def add_ts_file(self, size: int, size_download: int, duration: float):
43
- """
44
- Add a file size to the list of file sizes.
45
-
46
- Parameters:
47
- - size (int): The size of the ts file to be added.
48
- - size_download (int): Single size of the ts file.
49
- - duration (float): Time to download segment file.
50
- """
51
- if size <= 0 or size_download <= 0 or duration <= 0:
52
- logging.error("Invalid input values: size=%d, size_download=%d, duration=%f", size, size_download, duration)
53
- return
54
-
55
- # Add total size bytes
56
- self.ts_file_sizes.append(size)
57
- self.now_downloaded_size += size_download
58
-
59
- def capture_speed(self, interval: float = 1):
60
- """
61
- Capture the internet speed periodically and store the values.
62
- """
63
- def get_network_io():
64
- """Get network I/O counters, handle missing psutil gracefully."""
65
- try:
66
- io_counters = psutil.net_io_counters()
67
- return io_counters
68
- except Exception as e:
69
- logging.warning(f"Unable to access network I/O counters: {e}")
70
- return None
71
-
72
- while True:
73
- old_value = get_network_io()
74
- if old_value is None: # If psutil is not available, continue with default values
75
- time.sleep(interval)
76
- continue
77
-
78
- time.sleep(interval)
79
- new_value = get_network_io()
80
- if new_value is None: # Handle again if psutil fails in the next call
81
- time.sleep(interval)
82
- continue
83
-
84
- with self.lock:
85
- upload_speed = (new_value.bytes_sent - old_value.bytes_sent) / interval
86
- download_speed = (new_value.bytes_recv - old_value.bytes_recv) / interval
87
-
88
- self.speed = {
89
- "upload": internet_manager.format_transfer_speed(upload_speed),
90
- "download": internet_manager.format_transfer_speed(download_speed)
91
- }
92
-
93
- def get_average_speed(self) -> float:
94
- """
95
- Calculate the average internet speed.
96
-
97
- Returns:
98
- float: The average internet speed in Mbps.
99
- """
100
- with self.lock:
101
- return self.speed['download'].split(" ")
102
-
103
- def calculate_total_size(self) -> str:
104
- """
105
- Calculate the total size of the files.
106
-
107
- Returns:
108
- str: The mean size of the files in a human-readable format.
109
- """
110
- try:
111
- if len(self.ts_file_sizes) == 0:
112
- raise ValueError("No file sizes available to calculate total size.")
113
-
114
- total_size = sum(self.ts_file_sizes)
115
- mean_size = total_size / len(self.ts_file_sizes)
116
-
117
- # Return formatted mean size
118
- return internet_manager.format_file_size(mean_size)
119
-
120
- except ZeroDivisionError as e:
121
- logging.error("Division by zero error occurred: %s", e)
122
- return "0B"
123
-
124
- except Exception as e:
125
- logging.error("An unexpected error occurred: %s", e)
126
- return "Error"
127
-
128
- def get_downloaded_size(self) -> str:
129
- """
130
- Get the total downloaded size formatted as a human-readable string.
131
-
132
- Returns:
133
- str: The total downloaded size as a human-readable string.
134
- """
135
- return internet_manager.format_file_size(self.now_downloaded_size)
136
-
137
- def update_progress_bar(self, total_downloaded: int, duration: float, progress_counter: tqdm) -> None:
138
- """
139
- Updates the progress bar with information about the TS segment download.
140
-
141
- Parameters:
142
- total_downloaded (int): The length of the content of the downloaded TS segment.
143
- duration (float): The duration of the segment download in seconds.
144
- progress_counter (tqdm): The tqdm object representing the progress bar.
145
- """
146
- # Add the size of the downloaded segment to the estimator
147
- self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
148
-
149
- # Get downloaded size and total estimated size
150
- downloaded_file_size_str = self.get_downloaded_size()
151
- file_total_size = self.calculate_total_size()
152
-
153
- # Fix parameter for prefix
154
- number_file_downloaded = downloaded_file_size_str.split(' ')[0]
155
- number_file_total_size = file_total_size.split(' ')[0]
156
- units_file_downloaded = downloaded_file_size_str.split(' ')[1]
157
- units_file_total_size = file_total_size.split(' ')[1]
158
-
159
- average_internet_speed = self.get_average_speed()[0]
160
- average_internet_unit = self.get_average_speed()[1]
161
-
162
- # Update the progress bar's postfix
163
- if TQDM_USE_LARGE_BAR:
164
- progress_counter.set_postfix_str(
165
- f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< {Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
166
- f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
167
- )
168
- else:
169
- progress_counter.set_postfix_str(
170
- f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded}{Colors.RED} {units_file_downloaded} "
171
- f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
172
- )
1
+ # 20.02.24
2
+
3
+ import os
4
+ import time
5
+ import logging
6
+ import threading
7
+ from collections import deque
8
+
9
+
10
+ # External libraries
11
+ import psutil
12
+ from tqdm import tqdm
13
+
14
+
15
+ # Internal utilities
16
+ from StreamingCommunity.Util.color import Colors
17
+ from StreamingCommunity.Util.os import internet_manager
18
+ from StreamingCommunity.Util._jsonConfig import config_manager
19
+
20
+
21
+ # Variable
22
+ TQDM_USE_LARGE_BAR = config_manager.get_int('M3U8_DOWNLOAD', 'tqdm_use_large_bar')
23
+
24
+
25
+ class M3U8_Ts_Estimator:
26
+ def __init__(self, total_segments: int):
27
+ """
28
+ Initialize the M3U8_Ts_Estimator object.
29
+
30
+ Parameters:
31
+ - total_segments (int): Length of total segments to download.
32
+ """
33
+ self.ts_file_sizes = []
34
+ self.now_downloaded_size = 0
35
+ self.total_segments = total_segments
36
+ self.lock = threading.Lock()
37
+ self.speed = {"upload": "N/A", "download": "N/A"} # Default to N/A
38
+ self.speed_thread = threading.Thread(target=self.capture_speed)
39
+ self.speed_thread.daemon = True
40
+ self.speed_thread.start()
41
+
42
+ def add_ts_file(self, size: int, size_download: int, duration: float):
43
+ """
44
+ Add a file size to the list of file sizes.
45
+
46
+ Parameters:
47
+ - size (int): The size of the ts file to be added.
48
+ - size_download (int): Single size of the ts file.
49
+ - duration (float): Time to download segment file.
50
+ """
51
+ if size <= 0 or size_download <= 0 or duration <= 0:
52
+ logging.error("Invalid input values: size=%d, size_download=%d, duration=%f", size, size_download, duration)
53
+ return
54
+
55
+ # Add total size bytes
56
+ self.ts_file_sizes.append(size)
57
+ self.now_downloaded_size += size_download
58
+
59
+ def capture_speed(self, interval: float = 1):
60
+ """
61
+ Capture the internet speed periodically and store the values.
62
+ """
63
+ def get_network_io():
64
+ """Get network I/O counters, handle missing psutil gracefully."""
65
+ try:
66
+ io_counters = psutil.net_io_counters()
67
+ return io_counters
68
+
69
+ except Exception as e:
70
+ logging.warning(f"Unable to access network I/O counters: {e}")
71
+ return None
72
+
73
+ while True:
74
+ old_value = get_network_io()
75
+
76
+ if old_value is None: # If psutil is not available, continue with default values
77
+ time.sleep(interval)
78
+ continue
79
+
80
+ time.sleep(interval)
81
+ new_value = get_network_io()
82
+
83
+ if new_value is None: # Handle again if psutil fails in the next call
84
+ time.sleep(interval)
85
+ continue
86
+
87
+ with self.lock:
88
+ upload_speed = (new_value.bytes_sent - old_value.bytes_sent) / interval
89
+ download_speed = (new_value.bytes_recv - old_value.bytes_recv) / interval
90
+
91
+ self.speed = {
92
+ "upload": internet_manager.format_transfer_speed(upload_speed),
93
+ "download": internet_manager.format_transfer_speed(download_speed)
94
+ }
95
+
96
+ def get_average_speed(self) -> float:
97
+ """
98
+ Calculate the average internet speed.
99
+
100
+ Returns:
101
+ float: The average internet speed in Mbps.
102
+ """
103
+ with self.lock:
104
+ return self.speed['download'].split(" ")
105
+
106
+ def calculate_total_size(self) -> str:
107
+ """
108
+ Calculate the total size of the files.
109
+
110
+ Returns:
111
+ str: The mean size of the files in a human-readable format.
112
+ """
113
+ try:
114
+ if len(self.ts_file_sizes) == 0:
115
+ raise ValueError("No file sizes available to calculate total size.")
116
+
117
+ total_size = sum(self.ts_file_sizes)
118
+ mean_size = total_size / len(self.ts_file_sizes)
119
+
120
+ # Return formatted mean size
121
+ return internet_manager.format_file_size(mean_size)
122
+
123
+ except ZeroDivisionError as e:
124
+ logging.error("Division by zero error occurred: %s", e)
125
+ return "0B"
126
+
127
+ except Exception as e:
128
+ logging.error("An unexpected error occurred: %s", e)
129
+ return "Error"
130
+
131
+ def get_downloaded_size(self) -> str:
132
+ """
133
+ Get the total downloaded size formatted as a human-readable string.
134
+
135
+ Returns:
136
+ str: The total downloaded size as a human-readable string.
137
+ """
138
+ return internet_manager.format_file_size(self.now_downloaded_size)
139
+
140
+ def update_progress_bar(self, total_downloaded: int, duration: float, progress_counter: tqdm) -> None:
141
+ """
142
+ Updates the progress bar with information about the TS segment download.
143
+
144
+ Parameters:
145
+ total_downloaded (int): The length of the content of the downloaded TS segment.
146
+ duration (float): The duration of the segment download in seconds.
147
+ progress_counter (tqdm): The tqdm object representing the progress bar.
148
+ """
149
+ # Add the size of the downloaded segment to the estimator
150
+ self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
151
+
152
+ # Get downloaded size and total estimated size
153
+ downloaded_file_size_str = self.get_downloaded_size()
154
+ file_total_size = self.calculate_total_size()
155
+
156
+ # Fix parameter for prefix
157
+ number_file_downloaded = downloaded_file_size_str.split(' ')[0]
158
+ number_file_total_size = file_total_size.split(' ')[0]
159
+ units_file_downloaded = downloaded_file_size_str.split(' ')[1]
160
+ units_file_total_size = file_total_size.split(' ')[1]
161
+
162
+ average_internet_speed = self.get_average_speed()[0]
163
+ average_internet_unit = self.get_average_speed()[1]
164
+
165
+ # Update the progress bar's postfix
166
+ if TQDM_USE_LARGE_BAR:
167
+ progress_counter.set_postfix_str(
168
+ f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded} {Colors.WHITE}< {Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} "
169
+ f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
170
+ )
171
+ else:
172
+ progress_counter.set_postfix_str(
173
+ f"{Colors.WHITE}[ {Colors.GREEN}{number_file_downloaded}{Colors.RED} {units_file_downloaded} "
174
+ f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
175
+ )
173
176