StreamingCommunity 2.9.3__py3-none-any.whl → 2.9.4__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/Helper/Vixcloud/util.py +40 -38
- StreamingCommunity/Api/Player/maxstream.py +3 -11
- StreamingCommunity/Api/Site/1337xx/site.py +1 -9
- StreamingCommunity/Api/Site/altadefinizione/__init__.py +61 -0
- StreamingCommunity/Api/Site/altadefinizione/film.py +98 -0
- StreamingCommunity/Api/Site/altadefinizione/series.py +164 -0
- StreamingCommunity/Api/Site/altadefinizione/site.py +75 -0
- StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +72 -0
- StreamingCommunity/Api/Site/animeunity/film_serie.py +1 -1
- StreamingCommunity/Api/Site/animeunity/site.py +1 -9
- StreamingCommunity/Api/Site/cb01new/site.py +5 -16
- StreamingCommunity/Api/Site/ddlstreamitaly/site.py +1 -9
- StreamingCommunity/Api/Site/guardaserie/site.py +1 -9
- StreamingCommunity/Api/Site/streamingcommunity/series.py +29 -11
- StreamingCommunity/Api/Site/streamingcommunity/site.py +10 -10
- StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +38 -17
- StreamingCommunity/Api/Template/Class/SearchType.py +1 -1
- StreamingCommunity/Api/Template/Util/__init__.py +0 -1
- StreamingCommunity/Api/Template/Util/manage_ep.py +1 -0
- StreamingCommunity/Api/Template/config_loader.py +0 -4
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +2 -2
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +3 -2
- StreamingCommunity/Lib/M3U8/estimator.py +3 -3
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/config_json.py +0 -3
- StreamingCommunity/__init__.py +6 -0
- {StreamingCommunity-2.9.3.dist-info → streamingcommunity-2.9.4.dist-info}/METADATA +90 -7
- {StreamingCommunity-2.9.3.dist-info → streamingcommunity-2.9.4.dist-info}/RECORD +32 -28
- {StreamingCommunity-2.9.3.dist-info → streamingcommunity-2.9.4.dist-info}/WHEEL +1 -1
- StreamingCommunity/Api/Template/Util/get_domain.py +0 -100
- {StreamingCommunity-2.9.3.dist-info → streamingcommunity-2.9.4.dist-info}/LICENSE +0 -0
- {StreamingCommunity-2.9.3.dist-info → streamingcommunity-2.9.4.dist-info}/entry_points.txt +0 -0
- {StreamingCommunity-2.9.3.dist-info → streamingcommunity-2.9.4.dist-info}/top_level.txt +0 -0
|
@@ -17,7 +17,6 @@ from StreamingCommunity.Util.table import TVShowManager
|
|
|
17
17
|
|
|
18
18
|
# Logic class
|
|
19
19
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
20
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
21
20
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
22
21
|
|
|
23
22
|
|
|
@@ -41,15 +40,7 @@ def title_search(word_to_search: str) -> int:
|
|
|
41
40
|
media_search_manager.clear()
|
|
42
41
|
table_show_manager.clear()
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
domain_to_use, base_url = search_domain(site_constant.FULL_URL)
|
|
46
|
-
|
|
47
|
-
if domain_to_use is None or base_url is None:
|
|
48
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
49
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
50
|
-
sys.exit(1)
|
|
51
|
-
|
|
52
|
-
search_url = f"{site_constant.FULL_URL}/?s={word_to_search}"
|
|
43
|
+
search_url = f"{site_constant.FULL_URL}/?story={word_to_search}&do=search&subaction=search"
|
|
53
44
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
54
45
|
|
|
55
46
|
try:
|
|
@@ -58,20 +49,18 @@ def title_search(word_to_search: str) -> int:
|
|
|
58
49
|
|
|
59
50
|
except Exception as e:
|
|
60
51
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
52
|
+
return 0
|
|
61
53
|
|
|
62
54
|
# Create soup and find table
|
|
63
55
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
64
56
|
|
|
65
|
-
for div in soup.find_all("div", class_ = "
|
|
57
|
+
for div in soup.find_all("div", class_ = "short-main"):
|
|
66
58
|
try:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
title = div.find("h3").find("a").get_text(strip=True)
|
|
70
|
-
desc = div.find("p").find("strong").text
|
|
59
|
+
url = div.find("a").get("href")
|
|
60
|
+
title = div.find("a").get_text(strip=True)
|
|
71
61
|
|
|
72
62
|
title_info = {
|
|
73
63
|
'name': title,
|
|
74
|
-
'desc': desc,
|
|
75
64
|
'url': url
|
|
76
65
|
}
|
|
77
66
|
|
|
@@ -18,7 +18,6 @@ from StreamingCommunity.Util.table import TVShowManager
|
|
|
18
18
|
|
|
19
19
|
# Logic class
|
|
20
20
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
21
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
22
21
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
23
22
|
|
|
24
23
|
|
|
@@ -42,14 +41,6 @@ def title_search(word_to_search: str) -> int:
|
|
|
42
41
|
media_search_manager.clear()
|
|
43
42
|
table_show_manager.clear()
|
|
44
43
|
|
|
45
|
-
# Check if domain is working
|
|
46
|
-
domain_to_use, base_url = search_domain(site_constant.FULL_URL)
|
|
47
|
-
|
|
48
|
-
if domain_to_use is None or base_url is None:
|
|
49
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
50
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
51
|
-
sys.exit(1)
|
|
52
|
-
|
|
53
44
|
search_url = f"{site_constant.FULL_URL}/search/?&q={word_to_search}&quick=1&type=videobox_video&nodes=11"
|
|
54
45
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
55
46
|
|
|
@@ -59,6 +50,7 @@ def title_search(word_to_search: str) -> int:
|
|
|
59
50
|
|
|
60
51
|
except Exception as e:
|
|
61
52
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
53
|
+
return 0
|
|
62
54
|
|
|
63
55
|
# Create soup and find table
|
|
64
56
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
@@ -16,7 +16,6 @@ from StreamingCommunity.Util.table import TVShowManager
|
|
|
16
16
|
|
|
17
17
|
# Logic class
|
|
18
18
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
19
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
20
19
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
21
20
|
|
|
22
21
|
|
|
@@ -41,14 +40,6 @@ def title_search(word_to_search: str) -> int:
|
|
|
41
40
|
media_search_manager.clear()
|
|
42
41
|
table_show_manager.clear()
|
|
43
42
|
|
|
44
|
-
# Check if domain is working
|
|
45
|
-
domain_to_use, base_url = search_domain(site_constant.FULL_URL)
|
|
46
|
-
|
|
47
|
-
if domain_to_use is None or base_url is None:
|
|
48
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
49
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
50
|
-
sys.exit(1)
|
|
51
|
-
|
|
52
43
|
search_url = f"{site_constant.FULL_URL}/?story={word_to_search}&do=search&subaction=search"
|
|
53
44
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
54
45
|
|
|
@@ -58,6 +49,7 @@ def title_search(word_to_search: str) -> int:
|
|
|
58
49
|
|
|
59
50
|
except Exception as e:
|
|
60
51
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
52
|
+
return 0
|
|
61
53
|
|
|
62
54
|
# Create soup and find table
|
|
63
55
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
@@ -52,8 +52,15 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
52
52
|
start_message()
|
|
53
53
|
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
54
54
|
|
|
55
|
+
# SPECIAL: Get season number
|
|
56
|
+
season = None
|
|
57
|
+
for s in scrape_serie.seasons_manager.seasons:
|
|
58
|
+
if s.number == int(index_season_selected):
|
|
59
|
+
season = s
|
|
60
|
+
break
|
|
61
|
+
|
|
55
62
|
# Get info about episode
|
|
56
|
-
obj_episode =
|
|
63
|
+
obj_episode = season.episodes.get(index_episode_selected - 1)
|
|
57
64
|
console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}")
|
|
58
65
|
print()
|
|
59
66
|
|
|
@@ -100,14 +107,16 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
|
|
|
100
107
|
- index_season_selected (int): Index of the selected season.
|
|
101
108
|
- download_all (bool): Download all episodes in the season.
|
|
102
109
|
"""
|
|
103
|
-
|
|
104
|
-
# Clean memory of all episodes and get the number of the season
|
|
105
|
-
scrape_serie.episode_manager.clear()
|
|
106
|
-
|
|
107
|
-
# Start message and collect information about episodes
|
|
108
110
|
start_message()
|
|
109
111
|
scrape_serie.collect_info_season(index_season_selected)
|
|
110
|
-
|
|
112
|
+
|
|
113
|
+
# SPECIAL: Get season number
|
|
114
|
+
season = None
|
|
115
|
+
for s in scrape_serie.seasons_manager.seasons:
|
|
116
|
+
if s.number == index_season_selected:
|
|
117
|
+
season = s
|
|
118
|
+
break
|
|
119
|
+
episodes_count = len(season.episodes.episodes)
|
|
111
120
|
|
|
112
121
|
if download_all:
|
|
113
122
|
|
|
@@ -123,7 +132,7 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
|
|
|
123
132
|
else:
|
|
124
133
|
|
|
125
134
|
# Display episodes list and manage user selection
|
|
126
|
-
last_command = display_episodes_list(
|
|
135
|
+
last_command = display_episodes_list(season.episodes.episodes)
|
|
127
136
|
list_episode_select = manage_selection(last_command, episodes_count)
|
|
128
137
|
|
|
129
138
|
try:
|
|
@@ -163,7 +172,7 @@ def download_series(select_season: MediaItem) -> None:
|
|
|
163
172
|
|
|
164
173
|
# Collect information about seasons
|
|
165
174
|
scrape_serie.collect_info_title()
|
|
166
|
-
seasons_count = scrape_serie.
|
|
175
|
+
seasons_count = len(scrape_serie.seasons_manager)
|
|
167
176
|
|
|
168
177
|
# Prompt user for season selection and download episodes
|
|
169
178
|
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
|
@@ -197,14 +206,23 @@ def download_series(select_season: MediaItem) -> None:
|
|
|
197
206
|
|
|
198
207
|
# Loop through the selected seasons and download episodes
|
|
199
208
|
for i_season in list_season_select:
|
|
209
|
+
|
|
210
|
+
# SPECIAL: Get season number
|
|
211
|
+
season = None
|
|
212
|
+
for s in scrape_serie.seasons_manager.seasons:
|
|
213
|
+
if s.number == i_season:
|
|
214
|
+
season = s
|
|
215
|
+
break
|
|
216
|
+
season_number = season.number
|
|
217
|
+
|
|
200
218
|
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
201
219
|
|
|
202
220
|
# Download all episodes if multiple seasons are selected or if '*' is used
|
|
203
|
-
download_episode(
|
|
221
|
+
download_episode(season_number, scrape_serie, video_source, download_all=True)
|
|
204
222
|
else:
|
|
205
223
|
|
|
206
224
|
# Otherwise, let the user select specific episodes for the single season
|
|
207
|
-
download_episode(
|
|
225
|
+
download_episode(season_number, scrape_serie, video_source, download_all=False)
|
|
208
226
|
|
|
209
227
|
if site_constant.TELEGRAM_BOT:
|
|
210
228
|
bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
|
|
@@ -17,7 +17,6 @@ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
|
|
|
17
17
|
|
|
18
18
|
# Logic class
|
|
19
19
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
20
|
-
from StreamingCommunity.Api.Template.Util import search_domain
|
|
21
20
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
22
21
|
|
|
23
22
|
|
|
@@ -38,13 +37,6 @@ def title_search(title_search: str) -> int:
|
|
|
38
37
|
Returns:
|
|
39
38
|
int: The number of titles found.
|
|
40
39
|
"""
|
|
41
|
-
domain_to_use, base_url = search_domain(site_constant.FULL_URL)
|
|
42
|
-
|
|
43
|
-
if domain_to_use is None or base_url is None:
|
|
44
|
-
console.print("[bold red]Error: Unable to determine valid domain or base URL.[/bold red]")
|
|
45
|
-
console.print("[yellow]The service might be temporarily unavailable or the domain may have changed.[/yellow]")
|
|
46
|
-
sys.exit(1)
|
|
47
|
-
|
|
48
40
|
if site_constant.TELEGRAM_BOT:
|
|
49
41
|
bot = get_bot_instance()
|
|
50
42
|
|
|
@@ -60,12 +52,20 @@ def title_search(title_search: str) -> int:
|
|
|
60
52
|
|
|
61
53
|
except Exception as e:
|
|
62
54
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
55
|
+
return 0
|
|
63
56
|
|
|
64
57
|
# Prepara le scelte per l'utente
|
|
65
58
|
if site_constant.TELEGRAM_BOT:
|
|
66
59
|
choices = []
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
|
|
61
|
+
# Collect json data
|
|
62
|
+
try:
|
|
63
|
+
data = response.json().get('data', [])
|
|
64
|
+
except Exception as e:
|
|
65
|
+
console.log(f"Error parsing JSON response: {e}")
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
for i, dict_title in enumerate(data):
|
|
69
69
|
try:
|
|
70
70
|
media_search_manager.add_media({
|
|
71
71
|
'id': dict_title.get('id'),
|
|
@@ -12,7 +12,7 @@ from bs4 import BeautifulSoup
|
|
|
12
12
|
# Internal utilities
|
|
13
13
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
14
14
|
from StreamingCommunity.Util.config_json import config_manager
|
|
15
|
-
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import
|
|
15
|
+
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
# Variable
|
|
@@ -22,7 +22,7 @@ max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
|
22
22
|
class GetSerieInfo:
|
|
23
23
|
def __init__(self, url):
|
|
24
24
|
"""
|
|
25
|
-
Initialize the
|
|
25
|
+
Initialize the GetSerieInfo class for scraping TV series information.
|
|
26
26
|
|
|
27
27
|
Args:
|
|
28
28
|
- url (str): The URL of the streaming site.
|
|
@@ -31,6 +31,9 @@ class GetSerieInfo:
|
|
|
31
31
|
self.headers = {'user-agent': get_userAgent()}
|
|
32
32
|
self.url = url
|
|
33
33
|
|
|
34
|
+
# Initialize the SeasonManager
|
|
35
|
+
self.seasons_manager = SeasonManager()
|
|
36
|
+
|
|
34
37
|
def setup(self, media_id: int = None, series_name: str = None):
|
|
35
38
|
"""
|
|
36
39
|
Set up the scraper with specific media details.
|
|
@@ -41,19 +44,17 @@ class GetSerieInfo:
|
|
|
41
44
|
"""
|
|
42
45
|
self.media_id = media_id
|
|
43
46
|
|
|
44
|
-
# If series name is provided, initialize series-specific
|
|
47
|
+
# If series name is provided, initialize series-specific properties
|
|
45
48
|
if series_name is not None:
|
|
46
49
|
self.is_series = True
|
|
47
50
|
self.series_name = series_name
|
|
48
|
-
self.season_manager = None
|
|
49
|
-
self.episode_manager: EpisodeManager = EpisodeManager()
|
|
50
51
|
|
|
51
52
|
def collect_info_title(self) -> None:
|
|
52
53
|
"""
|
|
53
|
-
Retrieve
|
|
54
|
+
Retrieve general information about the TV series from the streaming site.
|
|
54
55
|
|
|
55
56
|
Raises:
|
|
56
|
-
Exception: If there's an error fetching
|
|
57
|
+
Exception: If there's an error fetching series information
|
|
57
58
|
"""
|
|
58
59
|
try:
|
|
59
60
|
response = httpx.get(
|
|
@@ -63,16 +64,30 @@ class GetSerieInfo:
|
|
|
63
64
|
)
|
|
64
65
|
response.raise_for_status()
|
|
65
66
|
|
|
66
|
-
# Extract
|
|
67
|
+
# Extract series info from JSON response
|
|
67
68
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
68
69
|
json_response = json.loads(soup.find("div", {"id": "app"}).get("data-page"))
|
|
69
70
|
self.version = json_response['version']
|
|
70
|
-
|
|
71
|
-
#
|
|
72
|
-
|
|
71
|
+
|
|
72
|
+
# Extract information about available seasons
|
|
73
|
+
title_data = json_response.get("props", {}).get("title", {})
|
|
74
|
+
|
|
75
|
+
# Save general series information
|
|
76
|
+
self.title_info = title_data
|
|
77
|
+
|
|
78
|
+
# Extract available seasons and add them to SeasonManager
|
|
79
|
+
seasons_data = title_data.get("seasons", [])
|
|
80
|
+
for season_data in seasons_data:
|
|
81
|
+
self.seasons_manager.add_season({
|
|
82
|
+
'id': season_data.get('id', 0),
|
|
83
|
+
'number': season_data.get('number', 0),
|
|
84
|
+
'name': f"Season {season_data.get('number', 0)}",
|
|
85
|
+
'slug': season_data.get('slug', ''),
|
|
86
|
+
'type': title_data.get('type', '')
|
|
87
|
+
})
|
|
73
88
|
|
|
74
89
|
except Exception as e:
|
|
75
|
-
logging.error(f"Error collecting
|
|
90
|
+
logging.error(f"Error collecting series info: {e}")
|
|
76
91
|
raise
|
|
77
92
|
|
|
78
93
|
def collect_info_season(self, number_season: int) -> None:
|
|
@@ -86,6 +101,12 @@ class GetSerieInfo:
|
|
|
86
101
|
Exception: If there's an error fetching episode information
|
|
87
102
|
"""
|
|
88
103
|
try:
|
|
104
|
+
# Get the season object from SeasonManager
|
|
105
|
+
season = self.seasons_manager.get_season_by_number(number_season)
|
|
106
|
+
if not season:
|
|
107
|
+
logging.error(f"Season {number_season} not found")
|
|
108
|
+
return
|
|
109
|
+
|
|
89
110
|
response = httpx.get(
|
|
90
111
|
url=f'{self.url}/titles/{self.media_id}-{self.series_name}/stagione-{number_season}',
|
|
91
112
|
headers={
|
|
@@ -98,12 +119,12 @@ class GetSerieInfo:
|
|
|
98
119
|
response.raise_for_status()
|
|
99
120
|
|
|
100
121
|
# Extract episodes from JSON response
|
|
101
|
-
json_response = response.json().get('props').get('loadedSeason').get('episodes')
|
|
122
|
+
json_response = response.json().get('props', {}).get('loadedSeason', {}).get('episodes', [])
|
|
102
123
|
|
|
103
|
-
# Add each episode to the episode manager
|
|
124
|
+
# Add each episode to the corresponding season's episode manager
|
|
104
125
|
for dict_episode in json_response:
|
|
105
|
-
|
|
126
|
+
season.episodes.add(dict_episode)
|
|
106
127
|
|
|
107
128
|
except Exception as e:
|
|
108
|
-
logging.error(f"Error collecting
|
|
109
|
-
raise
|
|
129
|
+
logging.error(f"Error collecting episodes for season {number_season}: {e}")
|
|
130
|
+
raise
|
|
@@ -218,6 +218,7 @@ def validate_episode_selection(list_episode_select: List[int], episodes_count: i
|
|
|
218
218
|
input_episodes = input(f"Enter valid episode numbers (1-{episodes_count}): ")
|
|
219
219
|
list_episode_select = list(map(int, input_episodes.split(',')))
|
|
220
220
|
|
|
221
|
+
|
|
221
222
|
def display_episodes_list(episodes_manager) -> str:
|
|
222
223
|
"""
|
|
223
224
|
Display episodes list and handle user input.
|
|
@@ -31,10 +31,6 @@ class SiteConstant:
|
|
|
31
31
|
def ROOT_PATH(self):
|
|
32
32
|
return config_manager.get('OUT_FOLDER', 'root_path')
|
|
33
33
|
|
|
34
|
-
@property
|
|
35
|
-
def DOMAIN_NOW(self):
|
|
36
|
-
return config_manager.get_site(self.SITE_NAME, 'domain')
|
|
37
|
-
|
|
38
34
|
@property
|
|
39
35
|
def FULL_URL(self):
|
|
40
36
|
return config_manager.get_site(self.SITE_NAME, 'full_url').rstrip('/')
|
|
@@ -438,13 +438,13 @@ class HLS_Downloader:
|
|
|
438
438
|
return response
|
|
439
439
|
|
|
440
440
|
if GET_ONLY_LINK:
|
|
441
|
-
console.print(f"URL: {self.m3u8_url}[/bold red]")
|
|
441
|
+
console.print(f"URL: [bold red]{self.m3u8_url}[/bold red]")
|
|
442
442
|
return {
|
|
443
443
|
'path': None,
|
|
444
444
|
'url': self.m3u8_url,
|
|
445
445
|
'is_master': getattr(self.m3u8_manager, 'is_master', None),
|
|
446
446
|
'msg': None,
|
|
447
|
-
'error':
|
|
447
|
+
'error': None,
|
|
448
448
|
'stopped': True
|
|
449
449
|
}
|
|
450
450
|
|
|
@@ -30,7 +30,8 @@ from ...FFmpeg import print_duration_table
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
# Config
|
|
33
|
-
REQUEST_VERIFY = config_manager.
|
|
33
|
+
REQUEST_VERIFY = config_manager.get_bool('REQUESTS', 'verify')
|
|
34
|
+
REQUEST_HTTP2 = config_manager.get_bool('REQUESTS', 'http2')
|
|
34
35
|
GET_ONLY_LINK = config_manager.get_bool('M3U8_PARSER', 'get_only_link')
|
|
35
36
|
REQUEST_TIMEOUT = config_manager.get_float('REQUESTS', 'timeout')
|
|
36
37
|
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
|
|
@@ -111,7 +112,7 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
|
|
|
111
112
|
original_handler = signal.signal(signal.SIGINT, partial(signal_handler, interrupt_handler=interrupt_handler, original_handler=signal.getsignal(signal.SIGINT)))
|
|
112
113
|
|
|
113
114
|
try:
|
|
114
|
-
transport = httpx.HTTPTransport(verify=REQUEST_VERIFY, http2=
|
|
115
|
+
transport = httpx.HTTPTransport(verify=REQUEST_VERIFY, http2=REQUEST_HTTP2)
|
|
115
116
|
|
|
116
117
|
with httpx.Client(transport=transport, timeout=httpx.Timeout(60)) as client:
|
|
117
118
|
with client.stream("GET", url, headers=headers, timeout=REQUEST_TIMEOUT) as response:
|
|
@@ -119,15 +119,15 @@ class M3U8_Ts_Estimator:
|
|
|
119
119
|
|
|
120
120
|
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
|
121
121
|
progress_str = (
|
|
122
|
-
f"{Colors.GREEN}{number_file_total_size} {Colors.
|
|
123
|
-
f"{Colors.WHITE} {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
|
|
122
|
+
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
|
123
|
+
f"{Colors.WHITE}, {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
|
|
124
124
|
f"{Colors.WHITE}, {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
|
125
125
|
)
|
|
126
126
|
|
|
127
127
|
else:
|
|
128
128
|
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
|
129
129
|
progress_str = (
|
|
130
|
-
f"{Colors.GREEN}{number_file_total_size} {Colors.
|
|
130
|
+
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
|
131
131
|
f"{Colors.WHITE}, {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
|
132
132
|
)
|
|
133
133
|
|
StreamingCommunity/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: StreamingCommunity
|
|
3
|
-
Version: 2.9.
|
|
3
|
+
Version: 2.9.4
|
|
4
4
|
Home-page: https://github.com/Lovi-0/StreamingCommunity
|
|
5
5
|
Author: Lovi-0
|
|
6
6
|
Project-URL: Bug Reports, https://github.com/Lovi-0/StreamingCommunity/issues
|
|
@@ -23,6 +23,14 @@ Requires-Dist: pycryptodomex
|
|
|
23
23
|
Requires-Dist: ua-generator
|
|
24
24
|
Requires-Dist: qbittorrent-api
|
|
25
25
|
Requires-Dist: pyTelegramBotAPI
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: description-content-type
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: keywords
|
|
31
|
+
Dynamic: project-url
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
26
34
|
|
|
27
35
|
<p align="center">
|
|
28
36
|
<img src="https://i.ibb.co/v6RnT0wY/s2.jpg" alt="Project Logo" width="700"/>
|
|
@@ -105,9 +113,15 @@ Install directly from PyPI:
|
|
|
105
113
|
pip install StreamingCommunity
|
|
106
114
|
```
|
|
107
115
|
|
|
108
|
-
|
|
116
|
+
Update to the latest version:
|
|
109
117
|
|
|
110
|
-
|
|
118
|
+
```bash
|
|
119
|
+
pip install --upgrade StreamingCommunity
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Quick Start
|
|
123
|
+
|
|
124
|
+
Create a simple script (`run_streaming.py`) to launch the main application:
|
|
111
125
|
|
|
112
126
|
```python
|
|
113
127
|
from StreamingCommunity.run import main
|
|
@@ -117,16 +131,85 @@ if __name__ == "__main__":
|
|
|
117
131
|
```
|
|
118
132
|
|
|
119
133
|
Run the script:
|
|
134
|
+
|
|
120
135
|
```bash
|
|
121
136
|
python run_streaming.py
|
|
122
137
|
```
|
|
123
138
|
|
|
124
|
-
|
|
139
|
+
## Modules
|
|
125
140
|
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
### HLS Downloader
|
|
142
|
+
|
|
143
|
+
Download HTTP Live Streaming (HLS) content from m3u8 URLs.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from StreamingCommunity.Download import HLS_Downloader
|
|
147
|
+
|
|
148
|
+
# Initialize with m3u8 URL and optional output path
|
|
149
|
+
downloader = HLS_Downloader(
|
|
150
|
+
m3u8_url="https://example.com/stream.m3u8",
|
|
151
|
+
output_path="/downloads/video.mp4" # Optional
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Start the download
|
|
155
|
+
downloader.download()
|
|
128
156
|
```
|
|
129
157
|
|
|
158
|
+
See [HLS example](./Test/Download/HLS.py) for complete usage.
|
|
159
|
+
|
|
160
|
+
### MP4 Downloader
|
|
161
|
+
|
|
162
|
+
Direct MP4 file downloader with support for custom headers and referrer.
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from StreamingCommunity.Download import MP4_downloader
|
|
166
|
+
|
|
167
|
+
# Basic usage
|
|
168
|
+
downloader = MP4_downloader(
|
|
169
|
+
url="https://example.com/video.mp4",
|
|
170
|
+
path="/downloads/saved_video.mp4"
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Advanced usage with custom headers and referrer
|
|
174
|
+
headers = {
|
|
175
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
176
|
+
}
|
|
177
|
+
downloader = MP4_downloader(
|
|
178
|
+
url="https://example.com/video.mp4",
|
|
179
|
+
path="/downloads/saved_video.mp4",
|
|
180
|
+
referer="https://example.com",
|
|
181
|
+
headers_=headers
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Start download
|
|
185
|
+
downloader.download()
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
See [MP4 example](./Test/Download/MP4.py) for complete usage.
|
|
189
|
+
|
|
190
|
+
### Torrent Client
|
|
191
|
+
|
|
192
|
+
Download content via torrent magnet links.
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from StreamingCommunity.Download import TOR_downloader
|
|
196
|
+
|
|
197
|
+
# Initialize torrent client
|
|
198
|
+
client = TOR_downloader()
|
|
199
|
+
|
|
200
|
+
# Add magnet link
|
|
201
|
+
client.add_magnet_link("magnet:?xt=urn:btih:example_hash&dn=example_name")
|
|
202
|
+
|
|
203
|
+
# Start download
|
|
204
|
+
client.start_download()
|
|
205
|
+
|
|
206
|
+
# Move downloaded files to specific location
|
|
207
|
+
client.move_downloaded_files("/downloads/torrents/")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
See [Torrent example](./Test/Download/TOR.py) for complete usage.
|
|
211
|
+
|
|
212
|
+
|
|
130
213
|
## 2. Automatic Installation
|
|
131
214
|
|
|
132
215
|
### Supported Operating Systems 💿
|