StreamingCommunity 3.0.8__py3-none-any.whl → 3.1.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/supervideo.py +5 -4
- StreamingCommunity/Api/Player/vixcloud.py +14 -9
- StreamingCommunity/Api/Site/altadefinizione/film.py +9 -1
- StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +35 -21
- StreamingCommunity/Api/Site/animeunity/site.py +43 -71
- StreamingCommunity/Api/Site/streamingcommunity/film.py +4 -0
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +17 -8
- StreamingCommunity/Upload/update.py +7 -2
- StreamingCommunity/Upload/version.py +2 -2
- StreamingCommunity/Util/config_json.py +16 -17
- StreamingCommunity/Util/message.py +1 -3
- StreamingCommunity/Util/os.py +52 -25
- StreamingCommunity/global_search.py +2 -2
- StreamingCommunity/run.py +72 -26
- {streamingcommunity-3.0.8.dist-info → streamingcommunity-3.1.0.dist-info}/METADATA +43 -58
- {streamingcommunity-3.0.8.dist-info → streamingcommunity-3.1.0.dist-info}/RECORD +20 -20
- {streamingcommunity-3.0.8.dist-info → streamingcommunity-3.1.0.dist-info}/WHEEL +1 -1
- {streamingcommunity-3.0.8.dist-info → streamingcommunity-3.1.0.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.0.8.dist-info → streamingcommunity-3.1.0.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.0.8.dist-info → streamingcommunity-3.1.0.dist-info}/top_level.txt +0 -0
|
@@ -5,9 +5,9 @@ import logging
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
# External libraries
|
|
8
|
-
import httpx
|
|
9
8
|
import jsbeautifier
|
|
10
9
|
from bs4 import BeautifulSoup
|
|
10
|
+
from curl_cffi import requests
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
# Internal utilities
|
|
@@ -28,7 +28,6 @@ class VideoSource:
|
|
|
28
28
|
- url (str): The URL of the video source.
|
|
29
29
|
"""
|
|
30
30
|
self.headers = get_headers()
|
|
31
|
-
self.client = httpx.Client()
|
|
32
31
|
self.url = url
|
|
33
32
|
|
|
34
33
|
def make_request(self, url: str) -> str:
|
|
@@ -42,8 +41,10 @@ class VideoSource:
|
|
|
42
41
|
- str: The response content if successful, None otherwise.
|
|
43
42
|
"""
|
|
44
43
|
try:
|
|
45
|
-
response =
|
|
46
|
-
response.
|
|
44
|
+
response = requests.get(url, headers=self.headers, timeout=MAX_TIMEOUT, impersonate="chrome110")
|
|
45
|
+
if response.status_code >= 400:
|
|
46
|
+
logging.error(f"Request failed with status code: {response.status_code}")
|
|
47
|
+
return None
|
|
47
48
|
return response.text
|
|
48
49
|
|
|
49
50
|
except Exception as e:
|
|
@@ -39,6 +39,7 @@ class VideoSource:
|
|
|
39
39
|
self.is_series = is_series
|
|
40
40
|
self.media_id = media_id
|
|
41
41
|
self.iframe_src = None
|
|
42
|
+
self.window_parameter = None
|
|
42
43
|
|
|
43
44
|
def get_iframe(self, episode_id: int) -> None:
|
|
44
45
|
"""
|
|
@@ -109,41 +110,45 @@ class VideoSource:
|
|
|
109
110
|
# Parse script to get video information
|
|
110
111
|
self.parse_script(script_text=script)
|
|
111
112
|
|
|
113
|
+
except httpx.HTTPStatusError as e:
|
|
114
|
+
if e.response.status_code == 404:
|
|
115
|
+
console.print("[yellow]This content will be available soon![/yellow]")
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
logging.error(f"Error getting content: {e}")
|
|
119
|
+
raise
|
|
120
|
+
|
|
112
121
|
except Exception as e:
|
|
113
122
|
logging.error(f"Error getting content: {e}")
|
|
114
123
|
raise
|
|
115
124
|
|
|
116
|
-
def get_playlist(self) -> str:
|
|
125
|
+
def get_playlist(self) -> str | None:
|
|
117
126
|
"""
|
|
118
127
|
Generate authenticated playlist URL.
|
|
119
128
|
|
|
120
129
|
Returns:
|
|
121
|
-
str: Fully constructed playlist URL with authentication parameters
|
|
130
|
+
str | None: Fully constructed playlist URL with authentication parameters, or None if content unavailable
|
|
122
131
|
"""
|
|
132
|
+
if not self.window_parameter:
|
|
133
|
+
return None
|
|
134
|
+
|
|
123
135
|
params = {}
|
|
124
136
|
|
|
125
|
-
# Add 'h' parameter if video quality is 1080p
|
|
126
137
|
if self.canPlayFHD:
|
|
127
138
|
params['h'] = 1
|
|
128
139
|
|
|
129
|
-
# Parse the original URL
|
|
130
140
|
parsed_url = urlparse(self.window_parameter.url)
|
|
131
141
|
query_params = parse_qs(parsed_url.query)
|
|
132
142
|
|
|
133
|
-
# Check specifically for 'b=1' in the query parameters
|
|
134
143
|
if 'b' in query_params and query_params['b'] == ['1']:
|
|
135
144
|
params['b'] = 1
|
|
136
145
|
|
|
137
|
-
# Add authentication parameters (token and expiration)
|
|
138
146
|
params.update({
|
|
139
147
|
"token": self.window_parameter.token,
|
|
140
148
|
"expires": self.window_parameter.expires
|
|
141
149
|
})
|
|
142
150
|
|
|
143
|
-
# Build the updated query string
|
|
144
151
|
query_string = urlencode(params)
|
|
145
|
-
|
|
146
|
-
# Construct the new URL with updated query parameters
|
|
147
152
|
return urlunparse(parsed_url._replace(query=query_string))
|
|
148
153
|
|
|
149
154
|
|
|
@@ -61,16 +61,22 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
61
61
|
# Extract mostraguarda URL
|
|
62
62
|
try:
|
|
63
63
|
response = httpx.get(select_title.url, headers=get_headers(), timeout=10)
|
|
64
|
+
response.raise_for_status()
|
|
65
|
+
|
|
64
66
|
soup = BeautifulSoup(response.text, 'html.parser')
|
|
65
67
|
iframes = soup.find_all('iframe')
|
|
66
68
|
mostraguarda = iframes[0]['src']
|
|
67
69
|
|
|
68
70
|
except Exception as e:
|
|
69
71
|
console.print(f"[red]Site: {site_constant.SITE_NAME}, request error: {e}, get mostraguarda")
|
|
72
|
+
return None
|
|
70
73
|
|
|
71
74
|
# Extract supervideo URL
|
|
75
|
+
supervideo_url = None
|
|
72
76
|
try:
|
|
73
77
|
response = httpx.get(mostraguarda, headers=get_headers(), timeout=10)
|
|
78
|
+
response.raise_for_status()
|
|
79
|
+
|
|
74
80
|
soup = BeautifulSoup(response.text, 'html.parser')
|
|
75
81
|
pattern = r'//supervideo\.[^/]+/[a-z]/[a-zA-Z0-9]+'
|
|
76
82
|
supervideo_match = re.search(pattern, response.text)
|
|
@@ -78,7 +84,9 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
78
84
|
|
|
79
85
|
except Exception as e:
|
|
80
86
|
console.print(f"[red]Site: {site_constant.SITE_NAME}, request error: {e}, get supervideo URL")
|
|
81
|
-
|
|
87
|
+
console.print("[yellow]This content will be available soon![/yellow]")
|
|
88
|
+
return None
|
|
89
|
+
|
|
82
90
|
# Init class
|
|
83
91
|
video_source = VideoSource(supervideo_url)
|
|
84
92
|
master_playlist = video_source.get_playlist()
|
|
@@ -38,38 +38,52 @@ class GetSerieInfo:
|
|
|
38
38
|
soup = BeautifulSoup(response.text, "html.parser")
|
|
39
39
|
self.series_name = soup.find("title").get_text(strip=True).split(" - ")[0]
|
|
40
40
|
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
# Find all season dropdowns
|
|
42
|
+
seasons_dropdown = soup.find('div', class_='dropdown seasons')
|
|
43
|
+
if not seasons_dropdown:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# Get all season items
|
|
47
|
+
season_items = seasons_dropdown.find_all('span', {'data-season': True})
|
|
48
|
+
|
|
49
|
+
for season_item in season_items:
|
|
50
|
+
season_num = int(season_item['data-season'])
|
|
51
|
+
season_name = season_item.get_text(strip=True)
|
|
50
52
|
|
|
51
|
-
# Create a new season
|
|
53
|
+
# Create a new season
|
|
52
54
|
current_season = self.seasons_manager.add_season({
|
|
53
|
-
'number':
|
|
55
|
+
'number': season_num,
|
|
54
56
|
'name': season_name
|
|
55
57
|
})
|
|
56
58
|
|
|
57
|
-
# Find episodes for this season
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
# Find all episodes for this season
|
|
60
|
+
episodes_container = soup.find('div', {'class': 'dropdown mirrors', 'data-season': str(season_num)})
|
|
61
|
+
if not episodes_container:
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
# Get all episode mirrors for this season
|
|
65
|
+
episode_mirrors = soup.find_all('div', {'class': 'dropdown mirrors',
|
|
66
|
+
'data-season': str(season_num)})
|
|
67
|
+
|
|
68
|
+
for mirror in episode_mirrors:
|
|
69
|
+
episode_data = mirror.get('data-episode', '').split('-')
|
|
70
|
+
if len(episode_data) != 2:
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
ep_num = int(episode_data[1])
|
|
74
|
+
|
|
75
|
+
# Find supervideo link
|
|
76
|
+
supervideo_span = mirror.find('span', {'data-id': 'supervideo'})
|
|
77
|
+
if not supervideo_span:
|
|
62
78
|
continue
|
|
63
79
|
|
|
64
|
-
|
|
65
|
-
link_tag = ep_div.find('a', string=lambda text: text and "Supervideo" in text)
|
|
66
|
-
episode_url = link_tag['href'] if link_tag else None
|
|
80
|
+
episode_url = supervideo_span.get('data-link', '')
|
|
67
81
|
|
|
68
82
|
# Add episode to the season
|
|
69
83
|
if current_season:
|
|
70
84
|
current_season.episodes.add({
|
|
71
|
-
'number':
|
|
72
|
-
'name':
|
|
85
|
+
'number': ep_num,
|
|
86
|
+
'name': f"Episodio {ep_num}",
|
|
73
87
|
'url': episode_url
|
|
74
88
|
})
|
|
75
89
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# 10.12.23
|
|
2
2
|
|
|
3
|
-
import logging
|
|
4
|
-
|
|
5
|
-
|
|
6
3
|
# External libraries
|
|
4
|
+
import urllib.parse
|
|
7
5
|
import httpx
|
|
8
|
-
from
|
|
6
|
+
from curl_cffi import requests
|
|
9
7
|
from rich.console import Console
|
|
10
8
|
|
|
11
9
|
|
|
@@ -20,92 +18,67 @@ from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
|
|
|
20
18
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
21
19
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
# Variable
|
|
25
21
|
console = Console()
|
|
26
22
|
media_search_manager = MediaManager()
|
|
27
23
|
table_show_manager = TVShowManager()
|
|
28
24
|
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
29
25
|
|
|
30
26
|
|
|
31
|
-
def get_token() -> dict:
|
|
27
|
+
def get_token(user_agent: str) -> dict:
|
|
32
28
|
"""
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Parameters:
|
|
36
|
-
- site_name (str): The name of the site.
|
|
37
|
-
- domain (str): The domain of the site.
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
- dict: A dictionary containing session tokens. The keys are 'XSRF_TOKEN', 'animeunity_session', and 'csrf_token'.
|
|
29
|
+
Retrieve session cookies from the site.
|
|
41
30
|
"""
|
|
42
|
-
response =
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
response = requests.get(
|
|
32
|
+
site_constant.FULL_URL,
|
|
33
|
+
headers={'user-agent': user_agent},
|
|
34
|
+
impersonate="chrome120"
|
|
45
35
|
)
|
|
46
36
|
response.raise_for_status()
|
|
37
|
+
all_cookies = {name: value for name, value in response.cookies.items()}
|
|
47
38
|
|
|
48
|
-
|
|
49
|
-
find_csrf_token = None
|
|
50
|
-
soup = BeautifulSoup(response.text, "html.parser")
|
|
51
|
-
|
|
52
|
-
for html_meta in soup.find_all("meta"):
|
|
53
|
-
if html_meta.get('name') == "csrf-token":
|
|
54
|
-
find_csrf_token = html_meta.get('content')
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
'animeunity_session': response.cookies['animeunity_session'],
|
|
58
|
-
'csrf_token': find_csrf_token
|
|
59
|
-
}
|
|
39
|
+
return {k: urllib.parse.unquote(v) for k, v in all_cookies.items()}
|
|
60
40
|
|
|
61
41
|
|
|
62
|
-
def get_real_title(record):
|
|
42
|
+
def get_real_title(record: dict) -> str:
|
|
63
43
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
Parameters:
|
|
67
|
-
- record (dict): A dictionary representing a row of JSON data.
|
|
68
|
-
|
|
69
|
-
Returns:
|
|
70
|
-
- str: The title found in the record. If no title is found, returns None.
|
|
44
|
+
Return the most appropriate title from the record.
|
|
71
45
|
"""
|
|
72
|
-
if record
|
|
46
|
+
if record.get('title_eng'):
|
|
73
47
|
return record['title_eng']
|
|
74
|
-
elif record
|
|
48
|
+
elif record.get('title'):
|
|
75
49
|
return record['title']
|
|
76
50
|
else:
|
|
77
|
-
return record
|
|
51
|
+
return record.get('title_it', '')
|
|
78
52
|
|
|
79
53
|
|
|
80
54
|
def title_search(query: str) -> int:
|
|
81
55
|
"""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Parameters:
|
|
85
|
-
- query (str): The query to search for.
|
|
86
|
-
|
|
87
|
-
Returns:
|
|
88
|
-
- int: A number containing the length of media search manager.
|
|
56
|
+
Perform anime search on animeunity.so.
|
|
89
57
|
"""
|
|
90
|
-
if site_constant.TELEGRAM_BOT:
|
|
58
|
+
if site_constant.TELEGRAM_BOT:
|
|
91
59
|
bot = get_bot_instance()
|
|
92
|
-
|
|
60
|
+
|
|
93
61
|
media_search_manager.clear()
|
|
94
62
|
table_show_manager.clear()
|
|
95
63
|
seen_titles = set()
|
|
96
64
|
choices = [] if site_constant.TELEGRAM_BOT else None
|
|
97
65
|
|
|
98
|
-
|
|
99
|
-
data = get_token()
|
|
66
|
+
user_agent = get_userAgent()
|
|
67
|
+
data = get_token(user_agent)
|
|
68
|
+
|
|
100
69
|
cookies = {
|
|
101
|
-
'
|
|
70
|
+
'XSRF-TOKEN': data.get('XSRF-TOKEN', ''),
|
|
71
|
+
'animeunity_session': data.get('animeunity_session', ''),
|
|
102
72
|
}
|
|
73
|
+
|
|
103
74
|
headers = {
|
|
104
|
-
'
|
|
105
|
-
'
|
|
75
|
+
'origin': site_constant.FULL_URL,
|
|
76
|
+
'referer': f"{site_constant.FULL_URL}/",
|
|
77
|
+
'user-agent': user_agent,
|
|
78
|
+
'x-xsrf-token': data.get('XSRF-TOKEN', ''),
|
|
106
79
|
}
|
|
107
80
|
|
|
108
|
-
# First
|
|
81
|
+
# First call: /livesearch
|
|
109
82
|
try:
|
|
110
83
|
response1 = httpx.post(
|
|
111
84
|
f'{site_constant.FULL_URL}/livesearch',
|
|
@@ -114,15 +87,14 @@ def title_search(query: str) -> int:
|
|
|
114
87
|
json={'title': query},
|
|
115
88
|
timeout=max_timeout
|
|
116
89
|
)
|
|
117
|
-
|
|
118
90
|
response1.raise_for_status()
|
|
119
|
-
process_results(response1.json()
|
|
91
|
+
process_results(response1.json().get('records', []), seen_titles, media_search_manager, choices)
|
|
120
92
|
|
|
121
93
|
except Exception as e:
|
|
122
94
|
console.print(f"[red]Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
123
95
|
return 0
|
|
124
96
|
|
|
125
|
-
# Second
|
|
97
|
+
# Second call: /archivio/get-animes
|
|
126
98
|
try:
|
|
127
99
|
json_data = {
|
|
128
100
|
'title': query,
|
|
@@ -135,7 +107,6 @@ def title_search(query: str) -> int:
|
|
|
135
107
|
'dubbed': False,
|
|
136
108
|
'season': False
|
|
137
109
|
}
|
|
138
|
-
|
|
139
110
|
response2 = httpx.post(
|
|
140
111
|
f'{site_constant.FULL_URL}/archivio/get-animes',
|
|
141
112
|
cookies=cookies,
|
|
@@ -143,30 +114,32 @@ def title_search(query: str) -> int:
|
|
|
143
114
|
json=json_data,
|
|
144
115
|
timeout=max_timeout
|
|
145
116
|
)
|
|
146
|
-
|
|
147
117
|
response2.raise_for_status()
|
|
148
|
-
process_results(response2.json()
|
|
118
|
+
process_results(response2.json().get('records', []), seen_titles, media_search_manager, choices)
|
|
149
119
|
|
|
150
120
|
except Exception as e:
|
|
151
121
|
console.print(f"Site: {site_constant.SITE_NAME}, archivio search error: {e}")
|
|
152
122
|
|
|
153
123
|
if site_constant.TELEGRAM_BOT and choices and len(choices) > 0:
|
|
154
|
-
bot.send_message(
|
|
155
|
-
|
|
124
|
+
bot.send_message("List of results:", choices)
|
|
125
|
+
|
|
156
126
|
result_count = media_search_manager.get_length()
|
|
157
127
|
if result_count == 0:
|
|
158
128
|
console.print(f"Nothing matching was found for: {query}")
|
|
159
|
-
|
|
129
|
+
|
|
160
130
|
return result_count
|
|
161
131
|
|
|
132
|
+
|
|
162
133
|
def process_results(records: list, seen_titles: set, media_manager: MediaManager, choices: list = None) -> None:
|
|
163
|
-
"""
|
|
134
|
+
"""
|
|
135
|
+
Add unique results to the media manager and to choices.
|
|
136
|
+
"""
|
|
164
137
|
for dict_title in records:
|
|
165
138
|
try:
|
|
166
139
|
title_id = dict_title.get('id')
|
|
167
140
|
if title_id in seen_titles:
|
|
168
141
|
continue
|
|
169
|
-
|
|
142
|
+
|
|
170
143
|
seen_titles.add(title_id)
|
|
171
144
|
dict_title['name'] = get_real_title(dict_title)
|
|
172
145
|
|
|
@@ -179,10 +152,9 @@ def process_results(records: list, seen_titles: set, media_manager: MediaManager
|
|
|
179
152
|
'episodes_count': dict_title.get('episodes_count'),
|
|
180
153
|
'image': dict_title.get('imageurl')
|
|
181
154
|
})
|
|
182
|
-
|
|
155
|
+
|
|
183
156
|
if choices is not None:
|
|
184
|
-
choice_text = f"{len(choices)} - {dict_title.get('name')} ({dict_title.get('type')}) -
|
|
157
|
+
choice_text = f"{len(choices)} - {dict_title.get('name')} ({dict_title.get('type')}) - Episodes: {dict_title.get('episodes_count')}"
|
|
185
158
|
choices.append(choice_text)
|
|
186
|
-
|
|
187
159
|
except Exception as e:
|
|
188
|
-
print(f"Error parsing a title entry: {e}")
|
|
160
|
+
print(f"Error parsing a title entry: {e}")
|
|
@@ -62,6 +62,10 @@ def download_film(select_title: MediaItem, proxy: str = None) -> str:
|
|
|
62
62
|
video_source.get_content()
|
|
63
63
|
master_playlist = video_source.get_playlist()
|
|
64
64
|
|
|
65
|
+
if master_playlist is None:
|
|
66
|
+
console.print(f"[red]Site: {site_constant.SITE_NAME}, error: No master playlist found[/red]")
|
|
67
|
+
return None
|
|
68
|
+
|
|
65
69
|
# Define the filename and path for the downloaded film
|
|
66
70
|
title_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
|
|
67
71
|
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
|
|
@@ -156,7 +156,7 @@ class M3U8Manager:
|
|
|
156
156
|
If it's a master playlist, only selects video stream.
|
|
157
157
|
"""
|
|
158
158
|
if not self.is_master:
|
|
159
|
-
self.video_url, self.video_res = self.m3u8_url, "
|
|
159
|
+
self.video_url, self.video_res = self.m3u8_url, "undefined"
|
|
160
160
|
self.audio_streams = []
|
|
161
161
|
self.sub_streams = []
|
|
162
162
|
|
|
@@ -165,8 +165,9 @@ class M3U8Manager:
|
|
|
165
165
|
self.video_url, self.video_res = self.parser._video.get_best_uri()
|
|
166
166
|
elif str(FILTER_CUSTOM_REOLUTION) == "worst":
|
|
167
167
|
self.video_url, self.video_res = self.parser._video.get_worst_uri()
|
|
168
|
-
elif "p"
|
|
169
|
-
|
|
168
|
+
elif str(FILTER_CUSTOM_REOLUTION).replace("p", "").replace("px", "").isdigit():
|
|
169
|
+
resolution_value = int(str(FILTER_CUSTOM_REOLUTION).replace("p", "").replace("px", ""))
|
|
170
|
+
self.video_url, self.video_res = self.parser._video.get_custom_uri(resolution_value)
|
|
170
171
|
else:
|
|
171
172
|
logging.error("Resolution not recognized.")
|
|
172
173
|
self.video_url, self.video_res = self.parser._video.get_best_uri()
|
|
@@ -180,10 +181,14 @@ class M3U8Manager:
|
|
|
180
181
|
|
|
181
182
|
self.sub_streams = []
|
|
182
183
|
if ENABLE_SUBTITLE:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
if "*" in DOWNLOAD_SPECIFIC_SUBTITLE:
|
|
185
|
+
self.sub_streams = self.parser._subtitle.get_all_uris_and_names() or []
|
|
186
|
+
|
|
187
|
+
else:
|
|
188
|
+
self.sub_streams = [
|
|
189
|
+
s for s in (self.parser._subtitle.get_all_uris_and_names() or [])
|
|
190
|
+
if s.get('language') in DOWNLOAD_SPECIFIC_SUBTITLE
|
|
191
|
+
]
|
|
187
192
|
|
|
188
193
|
def log_selection(self):
|
|
189
194
|
tuple_available_resolution = self.parser._video.get_list_resolution()
|
|
@@ -209,9 +214,13 @@ class M3U8Manager:
|
|
|
209
214
|
f"[red]Set:[/red] {set_codec_info}"
|
|
210
215
|
)
|
|
211
216
|
|
|
217
|
+
# Get available subtitles and their languages
|
|
212
218
|
available_subtitles = self.parser._subtitle.get_all_uris_and_names() or []
|
|
213
219
|
available_sub_languages = [sub.get('language') for sub in available_subtitles]
|
|
214
|
-
|
|
220
|
+
|
|
221
|
+
# If "*" is in DOWNLOAD_SPECIFIC_SUBTITLE, all languages are downloadable
|
|
222
|
+
downloadable_sub_languages = available_sub_languages if "*" in DOWNLOAD_SPECIFIC_SUBTITLE else list(set(available_sub_languages) & set(DOWNLOAD_SPECIFIC_SUBTITLE))
|
|
223
|
+
|
|
215
224
|
if available_sub_languages:
|
|
216
225
|
console.print(
|
|
217
226
|
f"[cyan bold]Subtitle [/cyan bold] [green]Available:[/green] [purple]{', '.join(available_sub_languages)}[/purple] | "
|
|
@@ -4,6 +4,7 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
6
|
import asyncio
|
|
7
|
+
import importlib.metadata
|
|
7
8
|
|
|
8
9
|
# External library
|
|
9
10
|
import httpx
|
|
@@ -11,7 +12,7 @@ from rich.console import Console
|
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
# Internal utilities
|
|
14
|
-
from .version import __version__, __author__, __title__
|
|
15
|
+
from .version import __version__ as source_code_version, __author__, __title__
|
|
15
16
|
from StreamingCommunity.Util.config_json import config_manager
|
|
16
17
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
17
18
|
|
|
@@ -75,7 +76,11 @@ def update():
|
|
|
75
76
|
percentual_stars = 0
|
|
76
77
|
|
|
77
78
|
# Get the current version (installed version)
|
|
78
|
-
|
|
79
|
+
try:
|
|
80
|
+
current_version = importlib.metadata.version(__title__)
|
|
81
|
+
except importlib.metadata.PackageNotFoundError:
|
|
82
|
+
#console.print(f"[yellow]Warning: Could not determine installed version for '{__title__}' via importlib.metadata. Falling back to source version.[/yellow]")
|
|
83
|
+
current_version = source_code_version
|
|
79
84
|
|
|
80
85
|
# Get commit details
|
|
81
86
|
latest_commit = response_commits[0] if response_commits else None
|
|
@@ -268,33 +268,32 @@ class ConfigManager:
|
|
|
268
268
|
self._load_site_data_from_file()
|
|
269
269
|
|
|
270
270
|
def _load_site_data_from_api(self) -> None:
|
|
271
|
-
"""Load site data from
|
|
271
|
+
"""Load site data from GitHub."""
|
|
272
|
+
domains_github_url = "https://raw.githubusercontent.com/Arrowar/StreamingCommunity/refs/heads/main/.github/.domain/domains.json"
|
|
272
273
|
headers = {
|
|
273
|
-
"
|
|
274
|
-
"Authorization": f"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inp2Zm5ncG94d3Jnc3duenl0YWRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDAxNTIxNjMsImV4cCI6MjA1NTcyODE2M30.FNTCCMwi0QaKjOu8gtZsT5yQttUW8QiDDGXmzkn89QE",
|
|
275
|
-
"Content-Type": "application/json",
|
|
276
|
-
"User-Agent": get_userAgent()
|
|
274
|
+
"User-Agent": get_userAgent()
|
|
277
275
|
}
|
|
278
276
|
|
|
279
277
|
try:
|
|
280
|
-
console.print("[bold cyan]Retrieving site data from
|
|
281
|
-
response = requests.get(
|
|
278
|
+
console.print(f"[bold cyan]Retrieving site data from GitHub:[/bold cyan]")
|
|
279
|
+
response = requests.get(domains_github_url, timeout=8, headers=headers)
|
|
282
280
|
|
|
283
281
|
if response.ok:
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
else:
|
|
291
|
-
console.print("[bold yellow]API returned an empty data set[/bold yellow]")
|
|
282
|
+
self.configSite = response.json()
|
|
283
|
+
|
|
284
|
+
site_count = len(self.configSite) if isinstance(self.configSite, dict) else 0
|
|
285
|
+
console.print(f"[bold green]Site data loaded from GitHub:[/bold green] {site_count} streaming services found.")
|
|
286
|
+
|
|
292
287
|
else:
|
|
293
|
-
console.print(f"[bold red]
|
|
288
|
+
console.print(f"[bold red]GitHub request failed:[/bold red] HTTP {response.status_code}, {response.text[:100]}")
|
|
294
289
|
self._handle_site_data_fallback()
|
|
295
290
|
|
|
291
|
+
except json.JSONDecodeError as e:
|
|
292
|
+
console.print(f"[bold red]Error parsing JSON from GitHub:[/bold red] {str(e)}")
|
|
293
|
+
self._handle_site_data_fallback()
|
|
294
|
+
|
|
296
295
|
except Exception as e:
|
|
297
|
-
console.print(f"[bold red]
|
|
296
|
+
console.print(f"[bold red]GitHub connection error:[/bold red] {str(e)}")
|
|
298
297
|
self._handle_site_data_fallback()
|
|
299
298
|
|
|
300
299
|
def _load_site_data_from_file(self) -> None:
|
|
@@ -35,7 +35,5 @@ def start_message():
|
|
|
35
35
|
|
|
36
36
|
if SHOW:
|
|
37
37
|
console.print(f"[purple]{msg}")
|
|
38
|
-
|
|
39
|
-
# Print a decorative separator line using asterisks
|
|
40
|
-
separator = "_" * (console.width - 2) # Ridotto di 2 per il padding
|
|
38
|
+
separator = "_" * (console.width - 2)
|
|
41
39
|
console.print(f"[cyan]{separator}[/cyan]\n")
|
StreamingCommunity/Util/os.py
CHANGED
|
@@ -12,7 +12,7 @@ import inspect
|
|
|
12
12
|
import subprocess
|
|
13
13
|
import contextlib
|
|
14
14
|
import importlib.metadata
|
|
15
|
-
|
|
15
|
+
import socket
|
|
16
16
|
|
|
17
17
|
# External library
|
|
18
18
|
from unidecode import unidecode
|
|
@@ -283,38 +283,65 @@ class InternManager():
|
|
|
283
283
|
else:
|
|
284
284
|
return f"{bytes / (1024 * 1024):.2f} MB/s"
|
|
285
285
|
|
|
286
|
-
def check_dns_provider(self):
|
|
286
|
+
# def check_dns_provider(self):
|
|
287
|
+
# """
|
|
288
|
+
# Check if the system's current DNS server matches any known DNS providers.
|
|
289
|
+
|
|
290
|
+
# Returns:
|
|
291
|
+
# bool: True if the current DNS server matches a known provider,
|
|
292
|
+
# False if no match is found or in case of errors
|
|
293
|
+
# """
|
|
294
|
+
# dns_providers = {
|
|
295
|
+
# "Cloudflare": ["1.1.1.1", "1.0.0.1"],
|
|
296
|
+
# "Google": ["8.8.8.8", "8.8.4.4"],
|
|
297
|
+
# "OpenDNS": ["208.67.222.222", "208.67.220.220"],
|
|
298
|
+
# "Quad9": ["9.9.9.9", "149.112.112.112"],
|
|
299
|
+
# "AdGuard": ["94.140.14.14", "94.140.15.15"],
|
|
300
|
+
# "Comodo": ["8.26.56.26", "8.20.247.20"],
|
|
301
|
+
# "Level3": ["209.244.0.3", "209.244.0.4"],
|
|
302
|
+
# "Norton": ["199.85.126.10", "199.85.127.10"],
|
|
303
|
+
# "CleanBrowsing": ["185.228.168.9", "185.228.169.9"],
|
|
304
|
+
# "Yandex": ["77.88.8.8", "77.88.8.1"]
|
|
305
|
+
# }
|
|
306
|
+
|
|
307
|
+
# try:
|
|
308
|
+
# resolver = dns.resolver.Resolver()
|
|
309
|
+
# nameservers = resolver.nameservers
|
|
310
|
+
|
|
311
|
+
# if not nameservers:
|
|
312
|
+
# return False
|
|
313
|
+
|
|
314
|
+
# for server in nameservers:
|
|
315
|
+
# for provider, ips in dns_providers.items():
|
|
316
|
+
# if server in ips:
|
|
317
|
+
# return True
|
|
318
|
+
# return False
|
|
319
|
+
|
|
320
|
+
# except Exception:
|
|
321
|
+
# return False
|
|
322
|
+
|
|
323
|
+
def check_dns_resolve(self, domains_list: list = None):
|
|
287
324
|
"""
|
|
288
|
-
Check if the system's current DNS server
|
|
325
|
+
Check if the system's current DNS server can resolve a domain name.
|
|
326
|
+
Works on both Windows and Unix-like systems.
|
|
289
327
|
|
|
328
|
+
Args:
|
|
329
|
+
domains_list (list, optional): List of domains to test. Defaults to common domains.
|
|
330
|
+
|
|
290
331
|
Returns:
|
|
291
|
-
bool: True if the current DNS server
|
|
292
|
-
|
|
332
|
+
bool: True if the current DNS server can resolve a domain name,
|
|
333
|
+
False if can't resolve or in case of errors
|
|
293
334
|
"""
|
|
294
|
-
|
|
295
|
-
"Cloudflare": ["1.1.1.1", "1.0.0.1"],
|
|
296
|
-
"Google": ["8.8.8.8", "8.8.4.4"],
|
|
297
|
-
"OpenDNS": ["208.67.222.222", "208.67.220.220"],
|
|
298
|
-
"Quad9": ["9.9.9.9", "149.112.112.112"],
|
|
299
|
-
}
|
|
335
|
+
test_domains = domains_list or ["github.com", "google.com", "microsoft.com", "amazon.com"]
|
|
300
336
|
|
|
301
337
|
try:
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
for server in nameservers:
|
|
309
|
-
for provider, ips in dns_providers.items():
|
|
310
|
-
if server in ips:
|
|
311
|
-
return True
|
|
312
|
-
return False
|
|
313
|
-
|
|
314
|
-
except Exception:
|
|
338
|
+
for domain in test_domains:
|
|
339
|
+
# socket.gethostbyname() works consistently across all platforms
|
|
340
|
+
socket.gethostbyname(domain)
|
|
341
|
+
return True
|
|
342
|
+
except (socket.gaierror, socket.error):
|
|
315
343
|
return False
|
|
316
344
|
|
|
317
|
-
|
|
318
345
|
class OsSummary:
|
|
319
346
|
def __init__(self):
|
|
320
347
|
self.ffmpeg_path = None
|
|
@@ -157,7 +157,7 @@ def global_search(search_terms: str = None, selected_sites: list = None):
|
|
|
157
157
|
|
|
158
158
|
# Display progress information
|
|
159
159
|
console.print(f"\n[bold green]Searching for:[/bold green] [yellow]{search_terms}[/yellow]")
|
|
160
|
-
console.print(f"[bold green]Searching across:[/bold green] {len(selected_sites)} sites")
|
|
160
|
+
console.print(f"[bold green]Searching across:[/bold green] {len(selected_sites)} sites \n")
|
|
161
161
|
|
|
162
162
|
with Progress() as progress:
|
|
163
163
|
search_task = progress.add_task("[cyan]Searching...", total=len(selected_sites))
|
|
@@ -188,7 +188,7 @@ def global_search(search_terms: str = None, selected_sites: list = None):
|
|
|
188
188
|
item_dict['source_alias'] = alias
|
|
189
189
|
all_results[alias].append(item_dict)
|
|
190
190
|
|
|
191
|
-
console.print(f"[green]Found {len(database.media_list)} results from {site_name}")
|
|
191
|
+
console.print(f"\n[green]Found {len(database.media_list)} results from {site_name}")
|
|
192
192
|
|
|
193
193
|
except Exception as e:
|
|
194
194
|
console.print(f"[bold red]Error searching {site_name}:[/bold red] {str(e)}")
|
StreamingCommunity/run.py
CHANGED
|
@@ -9,6 +9,7 @@ import platform
|
|
|
9
9
|
import argparse
|
|
10
10
|
import importlib
|
|
11
11
|
import threading, asyncio
|
|
12
|
+
from urllib.parse import urlparse
|
|
12
13
|
from typing import Callable
|
|
13
14
|
|
|
14
15
|
|
|
@@ -153,6 +154,7 @@ def initialize():
|
|
|
153
154
|
except:
|
|
154
155
|
console.log("[red]Error with loading github.")
|
|
155
156
|
|
|
157
|
+
|
|
156
158
|
def restart_script():
|
|
157
159
|
"""Riavvia lo script con gli stessi argomenti della riga di comando."""
|
|
158
160
|
print("\nRiavvio dello script...\n")
|
|
@@ -191,6 +193,11 @@ def force_exit():
|
|
|
191
193
|
os._exit(0)
|
|
192
194
|
|
|
193
195
|
|
|
196
|
+
def _extract_hostname(url_string: str) -> str:
|
|
197
|
+
"""Safely extracts the hostname from a URL string."""
|
|
198
|
+
return urlparse(url_string).hostname
|
|
199
|
+
|
|
200
|
+
|
|
194
201
|
def main(script_id = 0):
|
|
195
202
|
|
|
196
203
|
color_map = {
|
|
@@ -200,6 +207,13 @@ def main(script_id = 0):
|
|
|
200
207
|
"torrent": "white"
|
|
201
208
|
}
|
|
202
209
|
|
|
210
|
+
category_map = {
|
|
211
|
+
1: "anime",
|
|
212
|
+
2: "film_&_serie",
|
|
213
|
+
3: "serie",
|
|
214
|
+
4: "torrent"
|
|
215
|
+
}
|
|
216
|
+
|
|
203
217
|
if TELEGRAM_BOT:
|
|
204
218
|
bot = get_bot_instance()
|
|
205
219
|
bot.send_message(f"Avviato script {script_id}", None)
|
|
@@ -209,8 +223,11 @@ def main(script_id = 0):
|
|
|
209
223
|
# Create logger
|
|
210
224
|
log_not = Logger()
|
|
211
225
|
initialize()
|
|
212
|
-
|
|
213
|
-
|
|
226
|
+
|
|
227
|
+
# Get all site hostname
|
|
228
|
+
hostname_list = [hostname for site_info in config_manager.configSite.values() if (hostname := _extract_hostname(site_info.get('full_url')))]
|
|
229
|
+
|
|
230
|
+
if not internet_manager.check_dns_resolve(hostname_list):
|
|
214
231
|
print()
|
|
215
232
|
console.print("[red]❌ ERROR: DNS configuration is required!")
|
|
216
233
|
console.print("[red]The program cannot function correctly without proper DNS settings.")
|
|
@@ -219,8 +236,7 @@ def main(script_id = 0):
|
|
|
219
236
|
console.print("[blue]• Quad9 (9.9.9.9) 'https://docs.quad9.net/Setup_Guides/Windows/Windows_10/'")
|
|
220
237
|
console.print("\n[yellow]⚠️ The program will not work until you configure your DNS settings.")
|
|
221
238
|
|
|
222
|
-
|
|
223
|
-
msg.ask("[yellow]Press Enter to continue ...")
|
|
239
|
+
os._exit(0)
|
|
224
240
|
|
|
225
241
|
# Load search functions
|
|
226
242
|
search_functions = load_search_functions()
|
|
@@ -262,6 +278,11 @@ def main(script_id = 0):
|
|
|
262
278
|
'--global', action='store_true', help='Perform a global search across multiple sites.'
|
|
263
279
|
)
|
|
264
280
|
|
|
281
|
+
# Add category selection argument
|
|
282
|
+
parser.add_argument(
|
|
283
|
+
'--category', type=int, help='Select category directly (1: anime, 2: film_&_serie, 3: serie, 4: torrent).'
|
|
284
|
+
)
|
|
285
|
+
|
|
265
286
|
# Add arguments for search functions
|
|
266
287
|
parser.add_argument('-s', '--search', default=None, help='Search terms')
|
|
267
288
|
|
|
@@ -311,35 +332,60 @@ def main(script_id = 0):
|
|
|
311
332
|
except Exception as e:
|
|
312
333
|
console.print(f"[red]Error mapping module {module_name}: {str(e)}")
|
|
313
334
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
335
|
+
if args.category:
|
|
336
|
+
selected_category = category_map.get(args.category)
|
|
337
|
+
category_sites = []
|
|
338
|
+
for key, label in choice_labels.items():
|
|
339
|
+
if label[1] == selected_category:
|
|
340
|
+
category_sites.append((key, label[0]))
|
|
317
341
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
for key, label in choice_labels.items()]
|
|
322
|
-
) + "[white])"
|
|
342
|
+
if len(category_sites) == 1:
|
|
343
|
+
category = category_sites[0][0]
|
|
344
|
+
console.print(f"[green]Selezionato automaticamente: {category_sites[0][1]}[/green]")
|
|
323
345
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
346
|
+
else:
|
|
347
|
+
sito_prompt_items = [f"[{color_map.get(selected_category, 'white')}]({k}) {v}[/{color_map.get(selected_category, 'white')}]"
|
|
348
|
+
for k, v in category_sites]
|
|
349
|
+
sito_prompt_line = ", ".join(sito_prompt_items)
|
|
350
|
+
|
|
351
|
+
if TELEGRAM_BOT:
|
|
352
|
+
console.print(f"\nInsert site: {sito_prompt_line}")
|
|
353
|
+
category = bot.ask(
|
|
354
|
+
"select_site",
|
|
355
|
+
f"Insert site: {sito_prompt_line}",
|
|
356
|
+
None
|
|
357
|
+
)
|
|
358
|
+
else:
|
|
359
|
+
category = msg.ask(f"\n[cyan]Insert site: {sito_prompt_line}", choices=[k for k, _ in category_sites], show_choices=False)
|
|
328
360
|
|
|
329
|
-
|
|
330
|
-
|
|
361
|
+
else:
|
|
362
|
+
legend_text = " | ".join([f"[{color}]{category.capitalize()}[/{color}]" for category, color in color_map.items()])
|
|
363
|
+
console.print(f"\n[bold cyan]Category Legend:[/bold cyan] {legend_text}")
|
|
364
|
+
|
|
365
|
+
prompt_message = "[cyan]Insert site: " + ", ".join(
|
|
366
|
+
[f"[{color_map.get(label[1], 'white')}]({key}) {label[0]}[/{color_map.get(label[1], 'white')}]"
|
|
367
|
+
for key, label in choice_labels.items()]
|
|
331
368
|
)
|
|
332
369
|
|
|
333
|
-
|
|
370
|
+
if TELEGRAM_BOT:
|
|
371
|
+
category_legend_str = "Categorie: \n" + " | ".join([
|
|
372
|
+
f"{category.capitalize()}" for category in color_map.keys()
|
|
373
|
+
])
|
|
334
374
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
None
|
|
339
|
-
)
|
|
375
|
+
prompt_message_telegram = "Inserisci il sito:\n" + "\n".join(
|
|
376
|
+
[f"{key}: {label[0]}" for key, label in choice_labels.items()]
|
|
377
|
+
)
|
|
340
378
|
|
|
341
|
-
|
|
342
|
-
|
|
379
|
+
console.print(f"\n{prompt_message_telegram}")
|
|
380
|
+
|
|
381
|
+
category = bot.ask(
|
|
382
|
+
"select_provider",
|
|
383
|
+
f"{category_legend_str}\n\n{prompt_message_telegram}",
|
|
384
|
+
None
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
else:
|
|
388
|
+
category = msg.ask(prompt_message, choices=list(choice_labels.keys()), default="0", show_choices=False, show_default=False)
|
|
343
389
|
|
|
344
390
|
# Run the corresponding function based on user input
|
|
345
391
|
if category in input_to_function:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: StreamingCommunity
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.1.0
|
|
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
|
|
@@ -17,6 +17,7 @@ Requires-Dist: m3u8
|
|
|
17
17
|
Requires-Dist: certifi
|
|
18
18
|
Requires-Dist: psutil
|
|
19
19
|
Requires-Dist: unidecode
|
|
20
|
+
Requires-Dist: curl_cffi
|
|
20
21
|
Requires-Dist: dnspython
|
|
21
22
|
Requires-Dist: jsbeautifier
|
|
22
23
|
Requires-Dist: pathvalidate
|
|
@@ -24,6 +25,7 @@ Requires-Dist: pycryptodomex
|
|
|
24
25
|
Requires-Dist: ua-generator
|
|
25
26
|
Requires-Dist: qbittorrent-api
|
|
26
27
|
Requires-Dist: pyTelegramBotAPI
|
|
28
|
+
Requires-Dist: beautifulsoup4
|
|
27
29
|
Dynamic: author
|
|
28
30
|
Dynamic: description
|
|
29
31
|
Dynamic: description-content-type
|
|
@@ -34,36 +36,32 @@ Dynamic: project-url
|
|
|
34
36
|
Dynamic: requires-dist
|
|
35
37
|
Dynamic: requires-python
|
|
36
38
|
|
|
37
|
-
<
|
|
38
|
-
|
|
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
|
-
<a href="https://github.com/Arrowar/StreamingCommunity">
|
|
64
|
-
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/Arrowar/StreamingCommunity/main/.github/media/loc-badge.json&style=for-the-badge" alt="Lines of Code"/>
|
|
65
|
-
</a>
|
|
66
|
-
</p>
|
|
39
|
+
<div align="center">
|
|
40
|
+
|
|
41
|
+
## 📊 Project Status & Info
|
|
42
|
+
|
|
43
|
+
[](https://pypi.org/project/streamingcommunity)
|
|
44
|
+
[](https://pypi.org/project/streamingcommunity)
|
|
45
|
+
[](https://github.com/Arrowar/StreamingCommunity/blob/main/LICENSE)
|
|
46
|
+
|
|
47
|
+
[](https://github.com/Arrowar/StreamingCommunity)
|
|
48
|
+
[](https://github.com/Arrowar/StreamingCommunity/commits)
|
|
49
|
+
[](https://github.com/Arrowar/StreamingCommunity/issues)
|
|
50
|
+
|
|
51
|
+
## 💝 Support the Project
|
|
52
|
+
|
|
53
|
+
[](https://www.paypal.com/donate/?hosted_button_id=UXTWMT8P6HE2C)
|
|
54
|
+
## 🚀 Download & Install
|
|
55
|
+
|
|
56
|
+
[](https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_win.exe)
|
|
57
|
+
[](https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_mac)
|
|
58
|
+
[](https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_linux_latest)
|
|
59
|
+
[](https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_linux_previous)
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
*⚡ **Quick Start:** `pip install streamingcommunity` or download the executable for your platform above*
|
|
63
|
+
|
|
64
|
+
</div>
|
|
67
65
|
|
|
68
66
|
# 📋 Table of Contents
|
|
69
67
|
|
|
@@ -112,24 +110,6 @@ Dynamic: requires-python
|
|
|
112
110
|
|
|
113
111
|
# Installation
|
|
114
112
|
|
|
115
|
-
<p align="center">
|
|
116
|
-
<a href="https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_win.exe">
|
|
117
|
-
<img src="https://img.shields.io/badge/-Windows-blue.svg?style=for-the-badge&logo=windows" alt="Windows">
|
|
118
|
-
</a>
|
|
119
|
-
<a href="https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_mac">
|
|
120
|
-
<img src="https://img.shields.io/badge/-macOS-black.svg?style=for-the-badge&logo=apple" alt="macOS">
|
|
121
|
-
</a>
|
|
122
|
-
<a href="https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_linux">
|
|
123
|
-
<img src="https://img.shields.io/badge/-Linux-orange.svg?style=for-the-badge&logo=linux" alt="Linux">
|
|
124
|
-
</a>
|
|
125
|
-
<a href="https://github.com/Arrowar/StreamingCommunity/releases/latest/download/StreamingCommunity_linux_previous">
|
|
126
|
-
<img src="https://img.shields.io/badge/-Linux Previous-gray.svg?style=for-the-badge&logo=linux" alt="Linux Previous">
|
|
127
|
-
</a>
|
|
128
|
-
<a href="https://github.com/Arrowar/StreamingCommunity/releases">
|
|
129
|
-
<img src="https://img.shields.io/badge/-All Versions-lightgrey.svg?style=for-the-badge&logo=github" alt="All Versions">
|
|
130
|
-
</a>
|
|
131
|
-
</p>
|
|
132
|
-
|
|
133
113
|
## 1. PyPI Installation
|
|
134
114
|
|
|
135
115
|
Install directly from PyPI:
|
|
@@ -554,7 +534,7 @@ To enable qBittorrent integration, follow the setup guide [here](https://github.
|
|
|
554
534
|
"download_subtitle": true,
|
|
555
535
|
"merge_subs": true,
|
|
556
536
|
"specific_list_subtitles": [
|
|
557
|
-
"ita",
|
|
537
|
+
"ita", // Specify language codes or use ["*"] to download all available subtitles
|
|
558
538
|
"eng"
|
|
559
539
|
],
|
|
560
540
|
"cleanup_tmp_folder": true
|
|
@@ -580,6 +560,8 @@ To enable qBittorrent integration, follow the setup guide [here](https://github.
|
|
|
580
560
|
- `download_subtitle`: Whether to download subtitles
|
|
581
561
|
- `merge_subs`: Whether to merge subtitles with video
|
|
582
562
|
- `specific_list_subtitles`: List of subtitle languages to download
|
|
563
|
+
* Use `["*"]` to download all available subtitles
|
|
564
|
+
* Or specify individual languages like `["ita", "eng"]`
|
|
583
565
|
* Can be changed with `--specific_list_subtitles ita,eng`
|
|
584
566
|
|
|
585
567
|
#### Cleanup
|
|
@@ -731,8 +713,14 @@ python test_run.py --specific_list_audio ita,eng --specific_list_subtitles eng,s
|
|
|
731
713
|
# Keep console open after download
|
|
732
714
|
python test_run.py --not_close true
|
|
733
715
|
|
|
734
|
-
# Use global
|
|
716
|
+
# Use global searchAdd commentMore actions
|
|
735
717
|
python test_run.py --global -s "cars"
|
|
718
|
+
|
|
719
|
+
# Select specific category
|
|
720
|
+
python test_run.py --category 1 # Search in anime category
|
|
721
|
+
python test_run.py --category 2 # Search in movies & series
|
|
722
|
+
python test_run.py --category 3 # Search in series
|
|
723
|
+
python test_run.py --category 4 # Search in torrent category
|
|
736
724
|
```
|
|
737
725
|
|
|
738
726
|
# Docker
|
|
@@ -826,10 +814,10 @@ python3 telegram_bot.py
|
|
|
826
814
|
|
|
827
815
|
# Tutorials
|
|
828
816
|
|
|
829
|
-
- [Windows
|
|
830
|
-
- [Linux
|
|
831
|
-
- [Pypy
|
|
832
|
-
- [Compiled
|
|
817
|
+
- [Windows](https://www.youtube.com/watch?v=mZGqK4wdN-k)
|
|
818
|
+
- [Linux](https://www.youtube.com/watch?v=0qUNXPE_mTg)
|
|
819
|
+
- [Pypy](https://www.youtube.com/watch?v=C6m9ZKOK0p4)
|
|
820
|
+
- [Compiled](https://www.youtube.com/watch?v=pm4lqsxkTVo)
|
|
833
821
|
|
|
834
822
|
# To Do
|
|
835
823
|
|
|
@@ -848,9 +836,6 @@ Addon per Stremio che consente lo streaming HTTPS di film, serie, anime e TV in
|
|
|
848
836
|
### 🧩 [streamingcommunity-unofficialapi](https://github.com/Blu-Tiger/streamingcommunity-unofficialapi)
|
|
849
837
|
API non ufficiale per accedere ai contenuti del sito italiano StreamingCommunity.
|
|
850
838
|
|
|
851
|
-
### 🎥 [stream-buddy](https://github.com/Bbalduzz/stream-buddy)
|
|
852
|
-
Tool per guardare o scaricare film dalla piattaforma StreamingCommunity.
|
|
853
|
-
|
|
854
839
|
# Disclaimer
|
|
855
840
|
|
|
856
841
|
This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
StreamingCommunity/__init__.py,sha256=Cw-N0VCg7sef1WqdtvVwrhs1zc4LoUhs5C8k7vpM1lQ,207
|
|
2
|
-
StreamingCommunity/global_search.py,sha256=
|
|
3
|
-
StreamingCommunity/run.py,sha256=
|
|
2
|
+
StreamingCommunity/global_search.py,sha256=ip3D0OKNRTXHpe_Wy7v2KM-yNZdnnbG5FOBYSSk6wB8,12401
|
|
3
|
+
StreamingCommunity/run.py,sha256=I-FXtuLuBOmn963vs9hkvURLRI6CdVhCsIuuoY8ALF4,14146
|
|
4
4
|
StreamingCommunity/Api/Player/ddl.py,sha256=S3UZFonJl3d3xU1fQrosRFXFhwAm8hGVQ8Ff8g-6xSI,2071
|
|
5
5
|
StreamingCommunity/Api/Player/hdplayer.py,sha256=zfPcmtt8f-NfH9yapwwWpVSts-7s47vJ4_XHKJFg0i8,1875
|
|
6
6
|
StreamingCommunity/Api/Player/maxstream.py,sha256=6y2h7cMSA_kmaeiOWqqyMVBMrtX6HTt2WT0QXxirCxg,4839
|
|
7
7
|
StreamingCommunity/Api/Player/mediapolisvod.py,sha256=OcdnE1BMSwPZM-nw74GXNJ44E9RYwGnc_kFEA-G8XyY,2294
|
|
8
8
|
StreamingCommunity/Api/Player/mixdrop.py,sha256=B5KEv-S0xg8b8X2doSxPVcjgwDIlB5TP3m35zfn3v5w,4968
|
|
9
|
-
StreamingCommunity/Api/Player/supervideo.py,sha256=
|
|
9
|
+
StreamingCommunity/Api/Player/supervideo.py,sha256=8Gqn3rwlOT-_J9jTkpryo-ZI_hsaEbQg6UoYzg-k0GY,5301
|
|
10
10
|
StreamingCommunity/Api/Player/sweetpixel.py,sha256=gJSe1fop5J216CB3u8vstxLPP5YbcyoGUH4y3X3-JaQ,1643
|
|
11
|
-
StreamingCommunity/Api/Player/vixcloud.py,sha256=
|
|
11
|
+
StreamingCommunity/Api/Player/vixcloud.py,sha256=boMyliNNrwVcvbLxVoBq1pBqcMwaCPIQjSzCv_ilSoQ,6531
|
|
12
12
|
StreamingCommunity/Api/Player/Helper/Vixcloud/js_parser.py,sha256=U-8QlD5kGzIk3-4t4D6QyYmiDe8UBrSuVi1YHRQb7AU,4295
|
|
13
13
|
StreamingCommunity/Api/Player/Helper/Vixcloud/util.py,sha256=QLUgbwQrpuPIVNzdBlAiEJXnd-eCj_JQFckZZEEL55w,5214
|
|
14
14
|
StreamingCommunity/Api/Site/1337xx/__init__.py,sha256=-lZenAXIv69E38G1AssvRbdQ5ghZS-DjV-SifsLLpn8,2032
|
|
15
15
|
StreamingCommunity/Api/Site/1337xx/site.py,sha256=5XVUMTQn1UqMYgo7tPAw7bGMA-tqhQnfeOGKkgGh9OA,2349
|
|
16
16
|
StreamingCommunity/Api/Site/1337xx/title.py,sha256=8T3cVRb-Mt9QdOtKWVVFHz8iOHqspf7iw28E7bfTV78,1865
|
|
17
17
|
StreamingCommunity/Api/Site/altadefinizione/__init__.py,sha256=6CF9MCh3AstetNlTqG_uzRUQEmMR4iix2vPzGbl3EWM,4180
|
|
18
|
-
StreamingCommunity/Api/Site/altadefinizione/film.py,sha256=
|
|
18
|
+
StreamingCommunity/Api/Site/altadefinizione/film.py,sha256=sEPIZFU0Tvvgkk3-KD6yypmvISqLuQWNzl6Q2F76a00,3716
|
|
19
19
|
StreamingCommunity/Api/Site/altadefinizione/series.py,sha256=-rCYx-Fa7aZiYepcIne7OdH1aaUFZZAPX-ToBv6mxFs,8192
|
|
20
20
|
StreamingCommunity/Api/Site/altadefinizione/site.py,sha256=2kUNQ8ebYlX5dkSql-CvEhU01TOTNtuyEMIAD6SC3lg,2865
|
|
21
|
-
StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py,sha256=
|
|
21
|
+
StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py,sha256=9iulNlnNAhTfI5iKNW3I6pZqYeYwovAswa13L3LPGDM,4251
|
|
22
22
|
StreamingCommunity/Api/Site/animeunity/__init__.py,sha256=3T-F9Hsq1wlELOAHqYWCxNYHfDeYsQLRYifmQATjh0A,4063
|
|
23
23
|
StreamingCommunity/Api/Site/animeunity/film.py,sha256=Vqg6yag2siR-Y3ougBsV8mzdQXChxg6ghz_KVXFQ3pE,998
|
|
24
24
|
StreamingCommunity/Api/Site/animeunity/serie.py,sha256=ib86sLXYsYbrvrFNbzKdhlwMUO3DT7JS5yTTrrSr2jk,5711
|
|
25
|
-
StreamingCommunity/Api/Site/animeunity/site.py,sha256=
|
|
25
|
+
StreamingCommunity/Api/Site/animeunity/site.py,sha256=GLULPQATMHcXiH99d772v1ICH-PnnZgSM3q5__eN-gs,4977
|
|
26
26
|
StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py,sha256=UladSvOlTEVLiV0-rAz45zrET5qRHMuTGuKEpeQoumU,3872
|
|
27
27
|
StreamingCommunity/Api/Site/animeworld/__init__.py,sha256=fjStJyOl6VBVDgoi6wr2yA0CQ_UQ4atTBzal0gOBgRM,2822
|
|
28
28
|
StreamingCommunity/Api/Site/animeworld/film.py,sha256=W9KOS9Wvx3Mlqx5WojR-NgnF9WX8mI79JZPS7UwG-dc,1763
|
|
@@ -42,7 +42,7 @@ StreamingCommunity/Api/Site/raiplay/series.py,sha256=uQVbeA_g3Z1Ciqeq99gsY2F8mC5
|
|
|
42
42
|
StreamingCommunity/Api/Site/raiplay/site.py,sha256=kWo2YPcUOPgsThJU_RahnDwJP4QpkmgzdzRqtoubR5U,3166
|
|
43
43
|
StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py,sha256=V7fFP9KLuMXitRsHtInxUJBcGClfSk_tOUNWFC8pG5A,5807
|
|
44
44
|
StreamingCommunity/Api/Site/streamingcommunity/__init__.py,sha256=Qgy8nNtC05uwpSF6e0EE0WvsZkLKSKuqjWD1kligaPg,5977
|
|
45
|
-
StreamingCommunity/Api/Site/streamingcommunity/film.py,sha256=
|
|
45
|
+
StreamingCommunity/Api/Site/streamingcommunity/film.py,sha256=bt6g0c20eQux_cAz-FfobvkIF0wnFhVlBreRxhzV17E,2764
|
|
46
46
|
StreamingCommunity/Api/Site/streamingcommunity/series.py,sha256=W1KHSrF_rVl757dtLugvqVm7HCjovCHaHZXoGSRjOoI,8903
|
|
47
47
|
StreamingCommunity/Api/Site/streamingcommunity/site.py,sha256=cqaVM4cNXVqN_5ImeS_c7FI6go3TZPKkDi3XbD93MSQ,4019
|
|
48
48
|
StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py,sha256=Q05e8k2xvWd66jv3jlBtvpTau2J1C0zXw5uHvcimzxY,5664
|
|
@@ -58,7 +58,7 @@ StreamingCommunity/Api/Template/Class/SearchType.py,sha256=LOlE8UgraEM0UAVeNCThD
|
|
|
58
58
|
StreamingCommunity/Api/Template/Util/__init__.py,sha256=ZWQQd6iymNFDol9HaKPhVBoRX1W-xHJZgU_mZvLVdsM,196
|
|
59
59
|
StreamingCommunity/Api/Template/Util/manage_ep.py,sha256=FYe2DC9SXIXzlRYI7fW4ieBpfrxYzsUgt2C47tYRk7U,9252
|
|
60
60
|
StreamingCommunity/Lib/Downloader/__init__.py,sha256=JhbBh5hOnSM7VmtkxJ7zZ_FtWEC1JdnKThsSBjLV5FY,140
|
|
61
|
-
StreamingCommunity/Lib/Downloader/HLS/downloader.py,sha256=
|
|
61
|
+
StreamingCommunity/Lib/Downloader/HLS/downloader.py,sha256=kU7DN7cqdU5Uwo8d3TGXMIdep7QQhtU71Oe0s-Kks1k,22117
|
|
62
62
|
StreamingCommunity/Lib/Downloader/HLS/segments.py,sha256=Q_YZxkWfUqd6TRkHoYt58Iy-xMWVLH6fCnBnQImQK2I,18059
|
|
63
63
|
StreamingCommunity/Lib/Downloader/MP4/downloader.py,sha256=L03fpjaJ2pvZ_McaOShv3iCsdbefCGW3WM1QIa2TGJo,7514
|
|
64
64
|
StreamingCommunity/Lib/Downloader/TOR/downloader.py,sha256=CrRGdLGI_45AnhtTZm8r7KO7uGmU9k6pywy-qO18LG8,19242
|
|
@@ -78,19 +78,19 @@ StreamingCommunity/Lib/TMBD/tmdb.py,sha256=byg0EFnlmd9JeLvn1N9K3QkB1KEfeMuFa7OVf
|
|
|
78
78
|
StreamingCommunity/TelegramHelp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
79
|
StreamingCommunity/TelegramHelp/config.json,sha256=x1rtclDIrlnFcmiAmCQnV3WpnlTl5ds1LRRqECIF7xk,1581
|
|
80
80
|
StreamingCommunity/TelegramHelp/telegram_bot.py,sha256=zCqj7xBofh9FYfEYl55mgT945jqtKo7qJhn-SMLvAvA,26455
|
|
81
|
-
StreamingCommunity/Upload/update.py,sha256=
|
|
82
|
-
StreamingCommunity/Upload/version.py,sha256=
|
|
81
|
+
StreamingCommunity/Upload/update.py,sha256=V1JrW666IdVKr9hQasGqSGiYYBdCR04sF8EkyMHWweE,3813
|
|
82
|
+
StreamingCommunity/Upload/version.py,sha256=yR3Xi2ywfo47V5pjiyGyHAqv8S3LrLalAH9XqxDkC0Y,170
|
|
83
83
|
StreamingCommunity/Util/color.py,sha256=NvD0Eni-25oOOkY-szCEoc0lGvzQxyL7xhM0RE4EvUM,458
|
|
84
|
-
StreamingCommunity/Util/config_json.py,sha256=
|
|
84
|
+
StreamingCommunity/Util/config_json.py,sha256=l-NJhmIwSdqenZ3CZFOgWfbLYyEdzfk_PPl5p6VuLYo,24285
|
|
85
85
|
StreamingCommunity/Util/ffmpeg_installer.py,sha256=yRVIPwbh05tZ-duZmXkH0qasLNxaQCAT_E4cTP79Z3c,14890
|
|
86
86
|
StreamingCommunity/Util/headers.py,sha256=TItkaFMx1GqsVNEIS3Tr0BGU5EHyF-HkZVliHORT3P8,308
|
|
87
87
|
StreamingCommunity/Util/logger.py,sha256=9kGD6GmWj2pM8ADpJc85o7jm8DD0c5Aguqnq-9kmxos,3314
|
|
88
|
-
StreamingCommunity/Util/message.py,sha256=
|
|
89
|
-
StreamingCommunity/Util/os.py,sha256=
|
|
88
|
+
StreamingCommunity/Util/message.py,sha256=81vPmsGBusovIhheIO4Ec6p7BYvMj1wE_CthtRyp6OM,1333
|
|
89
|
+
StreamingCommunity/Util/os.py,sha256=8mAZhYsFJ9ZDt9hdFcDqBzAZ0Fv8tM3aqRkdiewK9ok,17330
|
|
90
90
|
StreamingCommunity/Util/table.py,sha256=Nw5PlsvfEIOQZWy5VhsU5OK3heuBXGwsqmLl0k8yQzc,9813
|
|
91
|
-
streamingcommunity-3.0.
|
|
92
|
-
streamingcommunity-3.0.
|
|
93
|
-
streamingcommunity-3.0.
|
|
94
|
-
streamingcommunity-3.0.
|
|
95
|
-
streamingcommunity-3.0.
|
|
96
|
-
streamingcommunity-3.0.
|
|
91
|
+
streamingcommunity-3.1.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
92
|
+
streamingcommunity-3.1.0.dist-info/METADATA,sha256=sfoMIrQBFMzlTFMNmefin7oNpG0AkKJCSVXhNBLFe5w,25190
|
|
93
|
+
streamingcommunity-3.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
94
|
+
streamingcommunity-3.1.0.dist-info/entry_points.txt,sha256=Qph9XYfDC8n4LfDLOSl6gJGlkb9eFb5f-JOr_Wb_5rk,67
|
|
95
|
+
streamingcommunity-3.1.0.dist-info/top_level.txt,sha256=YsOcxKP-WOhWpIWgBlh0coll9XUx7aqmRPT7kmt3fH0,19
|
|
96
|
+
streamingcommunity-3.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|