StreamingCommunity 2.7.0__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.
- StreamingCommunity/Api/Player/ddl.py +2 -2
- StreamingCommunity/Api/Player/maxstream.py +7 -13
- StreamingCommunity/Api/Player/supervideo.py +7 -33
- StreamingCommunity/Api/Player/vixcloud.py +8 -80
- StreamingCommunity/Api/Site/1337xx/__init__.py +8 -1
- StreamingCommunity/Api/Site/1337xx/site.py +10 -16
- StreamingCommunity/Api/Site/1337xx/title.py +4 -1
- StreamingCommunity/Api/Site/animeunity/__init__.py +9 -2
- StreamingCommunity/Api/Site/animeunity/film_serie.py +7 -1
- StreamingCommunity/Api/Site/animeunity/site.py +8 -10
- StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +1 -1
- StreamingCommunity/Api/Site/cb01new/__init__.py +8 -1
- StreamingCommunity/Api/Site/cb01new/film.py +7 -1
- StreamingCommunity/Api/Site/cb01new/site.py +16 -15
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +9 -2
- StreamingCommunity/Api/Site/ddlstreamitaly/series.py +7 -1
- StreamingCommunity/Api/Site/ddlstreamitaly/site.py +10 -15
- StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/__init__.py +9 -2
- StreamingCommunity/Api/Site/guardaserie/series.py +9 -1
- StreamingCommunity/Api/Site/guardaserie/site.py +12 -17
- StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +1 -1
- StreamingCommunity/Api/Site/mostraguarda/__init__.py +6 -2
- StreamingCommunity/Api/Site/mostraguarda/film.py +7 -3
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +9 -2
- StreamingCommunity/Api/Site/streamingcommunity/film.py +8 -1
- StreamingCommunity/Api/Site/streamingcommunity/series.py +14 -5
- StreamingCommunity/Api/Site/streamingcommunity/site.py +10 -15
- StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +2 -2
- StreamingCommunity/Api/Template/Util/__init__.py +0 -1
- StreamingCommunity/Api/Template/Util/get_domain.py +24 -66
- StreamingCommunity/Api/Template/Util/manage_ep.py +10 -5
- StreamingCommunity/Api/Template/config_loader.py +8 -8
- StreamingCommunity/Api/Template/site.py +3 -6
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +10 -13
- StreamingCommunity/Lib/Downloader/HLS/segments.py +11 -31
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +12 -9
- StreamingCommunity/Lib/Downloader/TOR/downloader.py +109 -101
- StreamingCommunity/Lib/FFmpeg/__init__.py +1 -1
- StreamingCommunity/Lib/FFmpeg/capture.py +10 -12
- StreamingCommunity/Lib/FFmpeg/command.py +15 -14
- StreamingCommunity/Lib/FFmpeg/util.py +9 -38
- StreamingCommunity/Lib/M3U8/decryptor.py +72 -146
- StreamingCommunity/Lib/M3U8/estimator.py +8 -16
- StreamingCommunity/Lib/M3U8/parser.py +1 -17
- StreamingCommunity/Lib/M3U8/url_fixer.py +1 -4
- StreamingCommunity/Lib/TMBD/__init__.py +2 -0
- StreamingCommunity/Lib/TMBD/obj_tmbd.py +3 -17
- StreamingCommunity/Lib/TMBD/tmdb.py +4 -9
- StreamingCommunity/TelegramHelp/telegram_bot.py +50 -50
- StreamingCommunity/Upload/update.py +3 -2
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/color.py +1 -1
- StreamingCommunity/Util/{_jsonConfig.py → config_json.py} +148 -54
- StreamingCommunity/Util/headers.py +2 -38
- StreamingCommunity/Util/logger.py +72 -42
- StreamingCommunity/Util/message.py +8 -3
- StreamingCommunity/Util/os.py +41 -93
- StreamingCommunity/Util/table.py +8 -17
- StreamingCommunity/run.py +26 -34
- {StreamingCommunity-2.7.0.dist-info → StreamingCommunity-2.8.0.dist-info}/METADATA +165 -92
- StreamingCommunity-2.8.0.dist-info/RECORD +75 -0
- StreamingCommunity/Api/Template/Util/recall_search.py +0 -37
- StreamingCommunity/Lib/Downloader/HLS/proxyes.py +0 -110
- StreamingCommunity/Util/call_stack.py +0 -42
- StreamingCommunity/Util/console.py +0 -12
- StreamingCommunity-2.7.0.dist-info/RECORD +0 -79
- {StreamingCommunity-2.7.0.dist-info → StreamingCommunity-2.8.0.dist-info}/LICENSE +0 -0
- {StreamingCommunity-2.7.0.dist-info → StreamingCommunity-2.8.0.dist-info}/WHEEL +0 -0
- {StreamingCommunity-2.7.0.dist-info → StreamingCommunity-2.8.0.dist-info}/entry_points.txt +0 -0
- {StreamingCommunity-2.7.0.dist-info → StreamingCommunity-2.8.0.dist-info}/top_level.txt +0 -0
|
@@ -3,163 +3,89 @@
|
|
|
3
3
|
import sys
|
|
4
4
|
import time
|
|
5
5
|
import logging
|
|
6
|
-
import subprocess
|
|
7
6
|
import importlib.util
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
#
|
|
11
|
-
from
|
|
9
|
+
# External library
|
|
10
|
+
from rich.console import Console
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
# Check if
|
|
13
|
+
# Check if Cryptodome module is installed
|
|
14
|
+
console = Console()
|
|
15
15
|
crypto_spec = importlib.util.find_spec("Cryptodome")
|
|
16
16
|
crypto_installed = crypto_spec is not None
|
|
17
17
|
|
|
18
|
+
if not crypto_installed:
|
|
19
|
+
console.log("[red]pycryptodomex non è installato. Per favore installalo. Leggi readme.md [Requirement].")
|
|
20
|
+
sys.exit(0)
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
from Cryptodome.Util.Padding import unpad
|
|
22
|
+
logging.info("[cyan]Decrypy use: Cryptodomex")
|
|
23
|
+
from Cryptodome.Cipher import AES
|
|
24
|
+
from Cryptodome.Util.Padding import unpad
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class M3U8_Decryption:
|
|
29
|
+
"""
|
|
30
|
+
Class for decrypting M3U8 playlist content using AES with pycryptodomex.
|
|
31
|
+
"""
|
|
32
|
+
def __init__(self, key: bytes, iv: bytes, method: str) -> None:
|
|
25
33
|
"""
|
|
26
|
-
|
|
34
|
+
Initialize the M3U8_Decryption object.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
key (bytes): The encryption key.
|
|
38
|
+
iv (bytes): The initialization vector (IV).
|
|
39
|
+
method (str): The encryption method.
|
|
27
40
|
"""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
self.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
""
|
|
63
|
-
|
|
64
|
-
#logging.info(f"Ciphertext: {ciphertext}")
|
|
65
|
-
|
|
66
|
-
# Decrypt based on encryption method
|
|
67
|
-
if self.method in {"AES", "AES-128"}:
|
|
68
|
-
decrypted_data = self.cipher.decrypt(ciphertext)
|
|
69
|
-
decrypted_content = unpad(decrypted_data, AES.block_size)
|
|
70
|
-
|
|
71
|
-
elif self.method == "AES-128-CTR":
|
|
72
|
-
decrypted_content = self.cipher.decrypt(ciphertext)
|
|
73
|
-
else:
|
|
74
|
-
raise ValueError("Invalid or unsupported method")
|
|
75
|
-
|
|
76
|
-
end = time.perf_counter_ns()
|
|
77
|
-
|
|
78
|
-
# Calculate elapsed time with high precision
|
|
79
|
-
elapsed_nanoseconds = end - start
|
|
80
|
-
elapsed_milliseconds = elapsed_nanoseconds / 1_000_000
|
|
81
|
-
elapsed_seconds = elapsed_nanoseconds / 1_000_000_000
|
|
82
|
-
|
|
83
|
-
# Print performance metrics
|
|
84
|
-
logging.info(f"[Crypto Decryption Performance]")
|
|
85
|
-
logging.info(f"Method: {self.method}")
|
|
86
|
-
logging.info(f"Decryption Time: {elapsed_milliseconds:.4f} ms ({elapsed_seconds:.6f} s)")
|
|
87
|
-
logging.info(f"Decrypted Content Length: {len(decrypted_content)} bytes")
|
|
88
|
-
|
|
89
|
-
return decrypted_content
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
else:
|
|
93
|
-
|
|
94
|
-
# Check if openssl command is available
|
|
95
|
-
try:
|
|
96
|
-
openssl_available = subprocess.run(["openssl", "version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
|
|
97
|
-
logging.info("[cyan]Decrypy use: OPENSSL")
|
|
98
|
-
except:
|
|
99
|
-
openssl_available = False
|
|
100
|
-
|
|
101
|
-
if not openssl_available:
|
|
102
|
-
console.log("[red]Neither python library: pycryptodomex nor openssl software is installed. Please install either one of them. Read readme.md [Requirement].")
|
|
103
|
-
sys.exit(0)
|
|
104
|
-
|
|
105
|
-
class M3U8_Decryption:
|
|
41
|
+
self.key = key
|
|
42
|
+
self.iv = iv
|
|
43
|
+
if "0x" in str(iv):
|
|
44
|
+
self.iv = bytes.fromhex(iv.replace("0x", ""))
|
|
45
|
+
self.method = method
|
|
46
|
+
|
|
47
|
+
# Pre-create the cipher based on the encryption method
|
|
48
|
+
if self.method == "AES":
|
|
49
|
+
self.cipher = AES.new(self.key, AES.MODE_ECB)
|
|
50
|
+
elif self.method == "AES-128":
|
|
51
|
+
self.cipher = AES.new(self.key[:16], AES.MODE_CBC, iv=self.iv)
|
|
52
|
+
elif self.method == "AES-128-CTR":
|
|
53
|
+
self.cipher = AES.new(self.key[:16], AES.MODE_CTR, nonce=self.iv)
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError("Invalid or unsupported method")
|
|
56
|
+
|
|
57
|
+
def decrypt(self, ciphertext: bytes) -> bytes:
|
|
58
|
+
"""
|
|
59
|
+
Decrypt the ciphertext using the specified encryption method.
|
|
60
|
+
|
|
61
|
+
Parameters:
|
|
62
|
+
ciphertext (bytes): The encrypted content to decrypt.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
bytes: The decrypted content.
|
|
66
|
+
"""
|
|
67
|
+
#start = time.perf_counter_ns()
|
|
68
|
+
|
|
69
|
+
if self.method in {"AES", "AES-128"}:
|
|
70
|
+
decrypted_data = self.cipher.decrypt(ciphertext)
|
|
71
|
+
decrypted_content = unpad(decrypted_data, AES.block_size)
|
|
72
|
+
elif self.method == "AES-128-CTR":
|
|
73
|
+
decrypted_content = self.cipher.decrypt(ciphertext)
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError("Invalid or unsupported method")
|
|
76
|
+
|
|
106
77
|
"""
|
|
107
|
-
|
|
78
|
+
end = time.perf_counter_ns()
|
|
79
|
+
|
|
80
|
+
# Calculate the elapsed time with high precision
|
|
81
|
+
elapsed_nanoseconds = end - start
|
|
82
|
+
elapsed_milliseconds = elapsed_nanoseconds / 1_000_000
|
|
83
|
+
elapsed_seconds = elapsed_nanoseconds / 1_000_000_000
|
|
84
|
+
|
|
85
|
+
# Log performance metrics
|
|
86
|
+
logging.info("[Crypto Decryption Performance]")
|
|
87
|
+
logging.info(f"Method: {self.method}")
|
|
88
|
+
logging.info(f"Decryption Time: {elapsed_milliseconds:.4f} ms ({elapsed_seconds:.6f} s)")
|
|
89
|
+
logging.info(f"Decrypted Content Length: {len(decrypted_content)} bytes")
|
|
108
90
|
"""
|
|
109
|
-
|
|
110
|
-
"""
|
|
111
|
-
Initialize the M3U8_Decryption object.
|
|
112
|
-
|
|
113
|
-
Parameters:
|
|
114
|
-
- key (bytes): The encryption key.
|
|
115
|
-
- iv (bytes): The initialization vector (IV).
|
|
116
|
-
- method (str): The encryption method.
|
|
117
|
-
"""
|
|
118
|
-
self.key = key
|
|
119
|
-
self.iv = iv
|
|
120
|
-
if "0x" in str(iv):
|
|
121
|
-
self.iv = bytes.fromhex(iv.replace("0x", ""))
|
|
122
|
-
self.method = method
|
|
123
|
-
logging.info(f"Decrypt add: ('key': {self.key}, 'iv': {self.iv}, 'method': {self.method})")
|
|
124
|
-
|
|
125
|
-
def decrypt(self, ciphertext: bytes) -> bytes:
|
|
126
|
-
"""
|
|
127
|
-
Decrypt the ciphertext using the specified encryption method.
|
|
128
|
-
|
|
129
|
-
Parameters:
|
|
130
|
-
- ciphertext (bytes): The encrypted content to decrypt.
|
|
131
|
-
|
|
132
|
-
Returns:
|
|
133
|
-
bytes: The decrypted content.
|
|
134
|
-
"""
|
|
135
|
-
start = time.perf_counter_ns()
|
|
136
|
-
|
|
137
|
-
# Construct OpenSSL command based on encryption method
|
|
138
|
-
if self.method == "AES":
|
|
139
|
-
openssl_cmd = f'openssl enc -d -aes-256-ecb -K {self.key.hex()} -nosalt'
|
|
140
|
-
elif self.method == "AES-128":
|
|
141
|
-
openssl_cmd = f'openssl enc -d -aes-128-cbc -K {self.key[:16].hex()} -iv {self.iv.hex()}'
|
|
142
|
-
elif self.method == "AES-128-CTR":
|
|
143
|
-
openssl_cmd = f'openssl enc -d -aes-128-ctr -K {self.key[:16].hex()} -iv {self.iv.hex()}'
|
|
144
|
-
else:
|
|
145
|
-
raise ValueError("Invalid or unsupported method")
|
|
146
|
-
|
|
147
|
-
try:
|
|
148
|
-
decrypted_content = subprocess.check_output(openssl_cmd.split(), input=ciphertext, stderr=subprocess.STDOUT)
|
|
149
|
-
except subprocess.CalledProcessError as e:
|
|
150
|
-
raise ValueError(f"Decryption failed: {e.output.decode()}")
|
|
151
|
-
|
|
152
|
-
end = time.perf_counter_ns()
|
|
153
|
-
|
|
154
|
-
# Calculate elapsed time with high precision
|
|
155
|
-
elapsed_nanoseconds = end - start
|
|
156
|
-
elapsed_milliseconds = elapsed_nanoseconds / 1_000_000
|
|
157
|
-
elapsed_seconds = elapsed_nanoseconds / 1_000_000_000
|
|
158
|
-
|
|
159
|
-
# Print performance metrics
|
|
160
|
-
logging.info(f"[OpenSSL Decryption Performance]")
|
|
161
|
-
logging.info(f"Method: {self.method}")
|
|
162
|
-
logging.info(f"Decryption Time: {elapsed_milliseconds:.4f} ms ({elapsed_seconds:.6f} s)")
|
|
163
|
-
logging.info(f"Decrypted Content Length: {len(decrypted_content)} bytes")
|
|
164
|
-
|
|
165
|
-
return decrypted_content
|
|
91
|
+
return decrypted_content
|
|
@@ -14,13 +14,10 @@ from tqdm import tqdm
|
|
|
14
14
|
|
|
15
15
|
# Internal utilities
|
|
16
16
|
from StreamingCommunity.Util.color import Colors
|
|
17
|
+
from StreamingCommunity.Util.config_json import get_use_large_bar
|
|
17
18
|
from StreamingCommunity.Util.os import internet_manager
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
# Variable
|
|
21
|
-
USE_LARGE_BAR = not ("android" in sys.platform or "ios" in sys.platform)
|
|
22
|
-
|
|
23
|
-
|
|
24
21
|
class M3U8_Ts_Estimator:
|
|
25
22
|
def __init__(self, total_segments: int, segments_instance=None):
|
|
26
23
|
"""
|
|
@@ -36,7 +33,7 @@ class M3U8_Ts_Estimator:
|
|
|
36
33
|
self.lock = threading.Lock()
|
|
37
34
|
self.speed = {"upload": "N/A", "download": "N/A"}
|
|
38
35
|
|
|
39
|
-
if
|
|
36
|
+
if get_use_large_bar():
|
|
40
37
|
logging.debug("USE_LARGE_BAR is True, starting speed capture thread")
|
|
41
38
|
self.speed_thread = threading.Thread(target=self.capture_speed)
|
|
42
39
|
self.speed_thread.daemon = True
|
|
@@ -106,15 +103,11 @@ class M3U8_Ts_Estimator:
|
|
|
106
103
|
try:
|
|
107
104
|
self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration)
|
|
108
105
|
|
|
109
|
-
#downloaded_file_size_str = internet_manager.format_file_size(self.now_downloaded_size)
|
|
110
106
|
file_total_size = self.calculate_total_size()
|
|
111
|
-
|
|
112
|
-
#number_file_downloaded = downloaded_file_size_str.split(' ')[0]
|
|
113
107
|
number_file_total_size = file_total_size.split(' ')[0]
|
|
114
|
-
#units_file_downloaded = downloaded_file_size_str.split(' ')[1]
|
|
115
108
|
units_file_total_size = file_total_size.split(' ')[1]
|
|
116
109
|
|
|
117
|
-
if
|
|
110
|
+
if get_use_large_bar():
|
|
118
111
|
speed_data = self.speed['download'].split(" ")
|
|
119
112
|
|
|
120
113
|
if len(speed_data) >= 2:
|
|
@@ -126,17 +119,16 @@ class M3U8_Ts_Estimator:
|
|
|
126
119
|
|
|
127
120
|
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
|
128
121
|
progress_str = (
|
|
129
|
-
|
|
130
|
-
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
|
122
|
+
f"{Colors.GREEN}{number_file_total_size} {Colors.WHITE}< {Colors.RED}{units_file_total_size}"
|
|
131
123
|
f"{Colors.WHITE} {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
|
|
132
|
-
f"{Colors.WHITE} {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
|
124
|
+
f"{Colors.WHITE}, {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
|
133
125
|
)
|
|
126
|
+
|
|
134
127
|
else:
|
|
135
128
|
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
|
136
129
|
progress_str = (
|
|
137
|
-
|
|
138
|
-
f"{Colors.
|
|
139
|
-
f"{Colors.WHITE} {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
|
130
|
+
f"{Colors.GREEN}{number_file_total_size} {Colors.WHITE}< {Colors.RED}{units_file_total_size}"
|
|
131
|
+
f"{Colors.WHITE}, {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
|
140
132
|
)
|
|
141
133
|
|
|
142
134
|
progress_counter.set_postfix_str(progress_str)
|
|
@@ -9,10 +9,6 @@ from m3u8 import loads
|
|
|
9
9
|
from StreamingCommunity.Util.os import internet_manager
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
# External libraries
|
|
13
|
-
import httpx
|
|
14
|
-
|
|
15
|
-
|
|
16
12
|
# Costant
|
|
17
13
|
CODEC_MAPPINGS = {
|
|
18
14
|
"video": {
|
|
@@ -79,7 +75,6 @@ class M3U8_Codec:
|
|
|
79
75
|
Extracted codecs are set as attributes: audio_codec and video_codec.
|
|
80
76
|
"""
|
|
81
77
|
try:
|
|
82
|
-
# Split the codecs string by comma
|
|
83
78
|
codecs_list = self.codecs.split(',')
|
|
84
79
|
except Exception as e:
|
|
85
80
|
logging.error(f"Can't split codec list: {self.codecs} with error {e}")
|
|
@@ -407,10 +402,8 @@ class M3U8_Parser:
|
|
|
407
402
|
Parameters:
|
|
408
403
|
- m3u8_content (str): The content of the M3U8 file.
|
|
409
404
|
"""
|
|
410
|
-
|
|
411
|
-
# Get obj of the m3u8 text content download, dictionary with video, audio, segments, subtitles
|
|
412
405
|
m3u8_obj = loads(raw_content, uri)
|
|
413
|
-
|
|
406
|
+
|
|
414
407
|
self.__parse_video_info__(m3u8_obj)
|
|
415
408
|
self.__parse_subtitles_and_audio__(m3u8_obj)
|
|
416
409
|
self.__parse_segments__(m3u8_obj)
|
|
@@ -469,7 +462,6 @@ class M3U8_Parser:
|
|
|
469
462
|
Parameters:
|
|
470
463
|
- m3u8_obj: The M3U8 object containing video playlists.
|
|
471
464
|
"""
|
|
472
|
-
|
|
473
465
|
try:
|
|
474
466
|
for playlist in m3u8_obj.playlists:
|
|
475
467
|
|
|
@@ -569,7 +561,6 @@ class M3U8_Parser:
|
|
|
569
561
|
Parameters:
|
|
570
562
|
- m3u8_obj: The M3U8 object containing segment data.
|
|
571
563
|
"""
|
|
572
|
-
|
|
573
564
|
try:
|
|
574
565
|
for segment in m3u8_obj.segments:
|
|
575
566
|
|
|
@@ -606,13 +597,6 @@ class M3U8_Parser:
|
|
|
606
597
|
Returns:
|
|
607
598
|
- formatted_duration (str): Formatted duration string with hours, minutes, and seconds if return_string is True.
|
|
608
599
|
- duration_dict (dict): Dictionary with keys 'h', 'm', 's' representing hours, minutes, and seconds respectively if return_string is False.
|
|
609
|
-
|
|
610
|
-
Example usage:
|
|
611
|
-
>>> obj = YourClass(duration=3661)
|
|
612
|
-
>>> obj.get_duration()
|
|
613
|
-
'[yellow]1[red]h [yellow]1[red]m [yellow]1[red]s'
|
|
614
|
-
>>> obj.get_duration(return_string=False)
|
|
615
|
-
{'h': 1, 'm': 1, 's': 1}
|
|
616
600
|
"""
|
|
617
601
|
|
|
618
602
|
# Calculate hours, minutes, and remaining seconds
|
|
@@ -33,8 +33,6 @@ class M3U8_UrlFix:
|
|
|
33
33
|
Returns:
|
|
34
34
|
str: The full URL for the specified resource.
|
|
35
35
|
"""
|
|
36
|
-
|
|
37
|
-
# Check if m3u8 url playlist is present
|
|
38
36
|
if self.url_playlist == None:
|
|
39
37
|
logging.error("[M3U8_UrlFix] Cant generate full url, playlist not present")
|
|
40
38
|
raise
|
|
@@ -54,5 +52,4 @@ class M3U8_UrlFix:
|
|
|
54
52
|
"""
|
|
55
53
|
Reset the M3U8 playlist URL to its default state (None).
|
|
56
54
|
"""
|
|
57
|
-
self.url_playlist = None
|
|
58
|
-
|
|
55
|
+
self.url_playlist = None
|
|
@@ -5,35 +5,21 @@ from typing import Dict
|
|
|
5
5
|
|
|
6
6
|
class Json_film:
|
|
7
7
|
def __init__(self, data: Dict):
|
|
8
|
-
self.adult = data.get('adult', False)
|
|
9
|
-
self.backdrop_path = data.get('backdrop_path')
|
|
10
|
-
self.budget = data.get('budget', 0)
|
|
11
|
-
self.homepage = data.get('homepage')
|
|
12
8
|
self.id = data.get('id', 0)
|
|
13
9
|
self.imdb_id = data.get('imdb_id')
|
|
14
10
|
self.origin_country = data.get('origin_country', [])
|
|
15
11
|
self.original_language = data.get('original_language')
|
|
16
12
|
self.original_title = data.get('original_title')
|
|
17
|
-
self.overview = data.get('overview')
|
|
18
13
|
self.popularity = data.get('popularity', 0.0)
|
|
19
14
|
self.poster_path = data.get('poster_path')
|
|
20
15
|
self.release_date = data.get('release_date')
|
|
21
|
-
self.revenue = data.get('revenue', 0)
|
|
22
|
-
self.runtime = data.get('runtime', 0)
|
|
23
16
|
self.status = data.get('status')
|
|
24
|
-
self.tagline = data.get('tagline')
|
|
25
17
|
self.title = data.get('title')
|
|
26
|
-
self.video = data.get('video', False)
|
|
27
18
|
self.vote_average = data.get('vote_average', 0.0)
|
|
28
19
|
self.vote_count = data.get('vote_count', 0)
|
|
29
20
|
|
|
30
21
|
def __repr__(self):
|
|
31
|
-
return (f"
|
|
32
|
-
f"budget={self.budget}, "
|
|
33
|
-
f"homepage='{self.homepage}', id={self.id}, "
|
|
34
|
-
f"imdb_id='{self.imdb_id}', origin_country={self.origin_country}, "
|
|
22
|
+
return (f"Json_film(id={self.id}, imdb_id='{self.imdb_id}', origin_country={self.origin_country}, "
|
|
35
23
|
f"original_language='{self.original_language}', original_title='{self.original_title}', "
|
|
36
|
-
f"
|
|
37
|
-
f"
|
|
38
|
-
f"status='{self.status}', tagline='{self.tagline}', "
|
|
39
|
-
f"title='{self.title}', video={self.video}, vote_average={self.vote_average}, vote_count={self.vote_count})")
|
|
24
|
+
f"popularity={self.popularity}, poster_path='{self.poster_path}', release_date='{self.release_date}', "
|
|
25
|
+
f"status='{self.status}', title='{self.title}', vote_average={self.vote_average}, vote_count={self.vote_count})")
|
|
@@ -6,22 +6,22 @@ from typing import Dict
|
|
|
6
6
|
|
|
7
7
|
# External libraries
|
|
8
8
|
import httpx
|
|
9
|
+
from rich.console import Console
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
# Internal utilities
|
|
12
13
|
from .obj_tmbd import Json_film
|
|
13
|
-
from StreamingCommunity.Util.
|
|
14
|
-
from StreamingCommunity.Util._jsonConfig import config_manager
|
|
14
|
+
from StreamingCommunity.Util.config_json import config_manager
|
|
15
15
|
from StreamingCommunity.Util.table import TVShowManager
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
# Variable
|
|
19
|
+
console = Console()
|
|
19
20
|
table_show_manager = TVShowManager()
|
|
20
21
|
api_key = "a800ed6c93274fb857ea61bd9e7256c5"
|
|
21
22
|
MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
|
|
25
25
|
def get_select_title(table_show_manager, generic_obj):
|
|
26
26
|
"""
|
|
27
27
|
Display a selection of titles and prompt the user to choose one.
|
|
@@ -29,11 +29,6 @@ def get_select_title(table_show_manager, generic_obj):
|
|
|
29
29
|
Returns:
|
|
30
30
|
dict: The selected media item.
|
|
31
31
|
"""
|
|
32
|
-
|
|
33
|
-
# Set up table for displaying titles
|
|
34
|
-
table_show_manager.set_slice_end(10)
|
|
35
|
-
|
|
36
|
-
# Check if the generic_obj list is empty
|
|
37
32
|
if not generic_obj:
|
|
38
33
|
console.print("\n[red]No media items available.")
|
|
39
34
|
return None
|
|
@@ -158,7 +153,7 @@ class TheMovieDB:
|
|
|
158
153
|
|
|
159
154
|
# Join with colored arrows and print with proper category label
|
|
160
155
|
console.print(
|
|
161
|
-
f"[bold purple]{category}:[/] {' [red]
|
|
156
|
+
f"[bold purple]{category}:[/] {' [red]->[/] '.join(colored_items)}"
|
|
162
157
|
)
|
|
163
158
|
|
|
164
159
|
def display_trending_tv_shows(self):
|