StreamingCommunity 2.9.7__py3-none-any.whl → 2.9.9__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 -10
- StreamingCommunity/Api/Player/mediapolisvod.py +64 -0
- StreamingCommunity/Api/Player/sweetpixel.py +3 -3
- StreamingCommunity/Api/Player/vixcloud.py +4 -9
- StreamingCommunity/Api/Site/1337xx/__init__.py +1 -1
- StreamingCommunity/Api/Site/altadefinizione/__init__.py +23 -7
- StreamingCommunity/Api/Site/altadefinizione/film.py +0 -1
- StreamingCommunity/Api/Site/altadefinizione/series.py +66 -70
- StreamingCommunity/Api/Site/altadefinizione/site.py +2 -1
- StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +37 -2
- StreamingCommunity/Api/Site/animeunity/__init__.py +29 -10
- StreamingCommunity/Api/Site/animeunity/film.py +40 -0
- StreamingCommunity/Api/Site/animeunity/serie.py +153 -0
- StreamingCommunity/Api/Site/animeunity/site.py +1 -2
- StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +15 -0
- StreamingCommunity/Api/Site/animeworld/__init__.py +25 -12
- StreamingCommunity/Api/Site/animeworld/film.py +63 -0
- StreamingCommunity/Api/Site/animeworld/serie.py +25 -22
- StreamingCommunity/Api/Site/animeworld/site.py +2 -1
- StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +32 -5
- StreamingCommunity/Api/Site/cb01new/__init__.py +1 -1
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +1 -1
- StreamingCommunity/Api/Site/ddlstreamitaly/series.py +31 -32
- StreamingCommunity/Api/Site/ddlstreamitaly/site.py +2 -2
- StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +30 -2
- StreamingCommunity/Api/Site/guardaserie/__init__.py +21 -7
- StreamingCommunity/Api/Site/guardaserie/series.py +55 -53
- StreamingCommunity/Api/Site/guardaserie/site.py +3 -2
- StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +27 -1
- StreamingCommunity/Api/Site/raiplay/__init__.py +93 -0
- StreamingCommunity/Api/Site/raiplay/film.py +65 -0
- StreamingCommunity/Api/Site/raiplay/series.py +162 -0
- StreamingCommunity/Api/Site/raiplay/site.py +166 -0
- StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +127 -0
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +29 -22
- StreamingCommunity/Api/Site/streamingcommunity/film.py +1 -2
- StreamingCommunity/Api/Site/streamingcommunity/series.py +76 -90
- StreamingCommunity/Api/Site/streamingcommunity/site.py +1 -3
- StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +41 -15
- StreamingCommunity/Api/Template/site.py +2 -2
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +1 -1
- StreamingCommunity/Lib/Downloader/HLS/segments.py +2 -3
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +2 -1
- StreamingCommunity/Lib/FFmpeg/util.py +47 -17
- StreamingCommunity/Lib/M3U8/estimator.py +50 -21
- StreamingCommunity/Lib/M3U8/parser.py +26 -6
- StreamingCommunity/Upload/update.py +22 -3
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/config_json.py +425 -274
- StreamingCommunity/Util/table.py +4 -2
- StreamingCommunity/run.py +1 -1
- {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/METADATA +1 -1
- streamingcommunity-2.9.9.dist-info/RECORD +91 -0
- {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/WHEEL +1 -1
- StreamingCommunity/Api/Site/animeunity/film_serie.py +0 -181
- StreamingCommunity/Api/Site/mostraguarda/__init__.py +0 -73
- StreamingCommunity/Api/Site/mostraguarda/film.py +0 -93
- streamingcommunity-2.9.7.dist-info/RECORD +0 -85
- {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-2.9.7.dist-info → streamingcommunity-2.9.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# 10.12.23
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
import queue
|
|
5
|
+
|
|
6
|
+
# External libraries
|
|
7
|
+
import httpx
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Internal utilities
|
|
12
|
+
from StreamingCommunity.Util.config_json import config_manager
|
|
13
|
+
from StreamingCommunity.Util.headers import get_userAgent
|
|
14
|
+
from StreamingCommunity.Util.table import TVShowManager
|
|
15
|
+
from StreamingCommunity.Lib.TMBD.tmdb import tmdb
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Logic class
|
|
19
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
20
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Variable
|
|
24
|
+
console = Console()
|
|
25
|
+
media_search_manager = MediaManager()
|
|
26
|
+
table_show_manager = TVShowManager()
|
|
27
|
+
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
28
|
+
MAX_THREADS = 4
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def determine_media_type(title):
|
|
32
|
+
"""
|
|
33
|
+
Use TMDB to determine if a title is a movie or TV show.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
# First search as a movie
|
|
37
|
+
movie_results = tmdb._make_request("search/movie", {"query": title})
|
|
38
|
+
movie_count = len(movie_results.get("results", []))
|
|
39
|
+
|
|
40
|
+
# Then search as a TV show
|
|
41
|
+
tv_results = tmdb._make_request("search/tv", {"query": title})
|
|
42
|
+
tv_count = len(tv_results.get("results", []))
|
|
43
|
+
|
|
44
|
+
# If results found in only one category, use that
|
|
45
|
+
if movie_count > 0 and tv_count == 0:
|
|
46
|
+
return "film"
|
|
47
|
+
elif tv_count > 0 and movie_count == 0:
|
|
48
|
+
return "tv"
|
|
49
|
+
|
|
50
|
+
# If both have results, compare popularity
|
|
51
|
+
if movie_count > 0 and tv_count > 0:
|
|
52
|
+
top_movie = movie_results["results"][0]
|
|
53
|
+
top_tv = tv_results["results"][0]
|
|
54
|
+
|
|
55
|
+
return "film" if top_movie.get("popularity", 0) > top_tv.get("popularity", 0) else "tv"
|
|
56
|
+
|
|
57
|
+
return "film"
|
|
58
|
+
|
|
59
|
+
except Exception as e:
|
|
60
|
+
console.log(f"Error determining media type with TMDB: {e}")
|
|
61
|
+
return "film"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def worker_determine_type(work_queue, result_dict, worker_id):
|
|
65
|
+
"""
|
|
66
|
+
Worker function to process items from queue and determine media types.
|
|
67
|
+
|
|
68
|
+
Parameters:
|
|
69
|
+
- work_queue: Queue containing items to process
|
|
70
|
+
- result_dict: Dictionary to store results
|
|
71
|
+
- worker_id: ID of the worker thread
|
|
72
|
+
"""
|
|
73
|
+
while not work_queue.empty():
|
|
74
|
+
try:
|
|
75
|
+
index, item = work_queue.get(block=False)
|
|
76
|
+
title = item.get('titolo', '')
|
|
77
|
+
media_type = determine_media_type(title)
|
|
78
|
+
|
|
79
|
+
result_dict[index] = {
|
|
80
|
+
'id': item.get('id', ''),
|
|
81
|
+
'name': title,
|
|
82
|
+
'type': media_type,
|
|
83
|
+
'path_id': item.get('path_id', ''),
|
|
84
|
+
'url': f"https://www.raiplay.it{item.get('url', '')}",
|
|
85
|
+
'image': f"https://www.raiplay.it{item.get('immagine', '')}",
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
work_queue.task_done()
|
|
89
|
+
|
|
90
|
+
except queue.Empty:
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
except Exception as e:
|
|
94
|
+
console.log(f"Worker {worker_id} error: {e}")
|
|
95
|
+
work_queue.task_done()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def title_search(query: str) -> int:
|
|
99
|
+
"""
|
|
100
|
+
Search for titles based on a search query.
|
|
101
|
+
|
|
102
|
+
Parameters:
|
|
103
|
+
- query (str): The query to search for.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
int: The number of titles found.
|
|
107
|
+
"""
|
|
108
|
+
media_search_manager.clear()
|
|
109
|
+
table_show_manager.clear()
|
|
110
|
+
|
|
111
|
+
search_url = f"https://www.raiplay.it/atomatic/raiplay-search-service/api/v1/msearch"
|
|
112
|
+
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
113
|
+
|
|
114
|
+
json_data = {
|
|
115
|
+
'templateIn': '6470a982e4e0301afe1f81f1',
|
|
116
|
+
'templateOut': '6516ac5d40da6c377b151642',
|
|
117
|
+
'params': {
|
|
118
|
+
'param': query,
|
|
119
|
+
'from': None,
|
|
120
|
+
'sort': 'relevance',
|
|
121
|
+
'onlyVideoQuery': False,
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
response = httpx.post(search_url, headers={'user-agent': get_userAgent()}, json=json_data, timeout=max_timeout, follow_redirects=True)
|
|
127
|
+
response.raise_for_status()
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
131
|
+
return 0
|
|
132
|
+
|
|
133
|
+
# Limit to only 15 results for performance
|
|
134
|
+
data = response.json().get('agg').get('titoli').get('cards')
|
|
135
|
+
data = data[:15] if len(data) > 15 else data
|
|
136
|
+
|
|
137
|
+
# Use multithreading to determine media types in parallel
|
|
138
|
+
work_queue = queue.Queue()
|
|
139
|
+
result_dict = {}
|
|
140
|
+
|
|
141
|
+
# Add items to the work queue
|
|
142
|
+
for i, item in enumerate(data):
|
|
143
|
+
work_queue.put((i, item))
|
|
144
|
+
|
|
145
|
+
# Create and start worker threads
|
|
146
|
+
threads = []
|
|
147
|
+
for i in range(min(MAX_THREADS, len(data))):
|
|
148
|
+
thread = threading.Thread(
|
|
149
|
+
target=worker_determine_type,
|
|
150
|
+
args=(work_queue, result_dict, i),
|
|
151
|
+
daemon=True
|
|
152
|
+
)
|
|
153
|
+
threads.append(thread)
|
|
154
|
+
thread.start()
|
|
155
|
+
|
|
156
|
+
# Wait for all threads to complete
|
|
157
|
+
for thread in threads:
|
|
158
|
+
thread.join()
|
|
159
|
+
|
|
160
|
+
# Add all results to media manager in correct order
|
|
161
|
+
for i in range(len(data)):
|
|
162
|
+
if i in result_dict:
|
|
163
|
+
media_search_manager.add_media(result_dict[i])
|
|
164
|
+
|
|
165
|
+
# Return the number of titles found
|
|
166
|
+
return media_search_manager.get_length()
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# 01.03.24
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# External libraries
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Internal utilities
|
|
11
|
+
from StreamingCommunity.Util.headers import get_headers
|
|
12
|
+
from StreamingCommunity.Util.config_json import config_manager
|
|
13
|
+
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Variable
|
|
17
|
+
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GetSerieInfo:
|
|
21
|
+
def __init__(self, program_name: str):
|
|
22
|
+
"""Initialize the GetSerieInfo class."""
|
|
23
|
+
self.base_url = "https://www.raiplay.it"
|
|
24
|
+
self.program_name = program_name
|
|
25
|
+
self.series_name = program_name
|
|
26
|
+
self.seasons_manager = SeasonManager()
|
|
27
|
+
|
|
28
|
+
def collect_info_title(self) -> None:
|
|
29
|
+
"""Get series info including seasons."""
|
|
30
|
+
try:
|
|
31
|
+
program_url = f"{self.base_url}/programmi/{self.program_name}.json"
|
|
32
|
+
response = httpx.get(url=program_url, headers=get_headers(), timeout=max_timeout)
|
|
33
|
+
response.raise_for_status()
|
|
34
|
+
|
|
35
|
+
json_data = response.json()
|
|
36
|
+
|
|
37
|
+
# Look for seasons in the 'blocks' property
|
|
38
|
+
for block in json_data.get('blocks'):
|
|
39
|
+
if block.get('type') == 'RaiPlay Multimedia Block' and block.get('name', '').lower() == 'episodi':
|
|
40
|
+
self.publishing_block_id = block.get('id')
|
|
41
|
+
|
|
42
|
+
# Extract seasons from sets array
|
|
43
|
+
for season_set in block.get('sets', []):
|
|
44
|
+
if 'stagione' in season_set.get('name', '').lower():
|
|
45
|
+
self.seasons_manager.add_season({
|
|
46
|
+
'id': season_set.get('id', ''),
|
|
47
|
+
'number': len(self.seasons_manager.seasons) + 1,
|
|
48
|
+
'name': season_set.get('name', ''),
|
|
49
|
+
'path': season_set.get('path_id', ''),
|
|
50
|
+
'episodes_count': season_set.get('episode_size', {}).get('number', 0)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logging.error(f"Error collecting series info: {e}")
|
|
55
|
+
|
|
56
|
+
def collect_info_season(self, number_season: int) -> None:
|
|
57
|
+
"""Get episodes for a specific season."""
|
|
58
|
+
try:
|
|
59
|
+
season = self.seasons_manager.get_season_by_number(number_season)
|
|
60
|
+
|
|
61
|
+
url = f"{self.base_url}/programmi/{self.program_name}/{self.publishing_block_id}/{season.id}/episodes.json"
|
|
62
|
+
response = httpx.get(url=url, headers=get_headers(), timeout=max_timeout)
|
|
63
|
+
response.raise_for_status()
|
|
64
|
+
|
|
65
|
+
episodes_data = response.json()
|
|
66
|
+
cards = []
|
|
67
|
+
|
|
68
|
+
# Extract episodes from different possible structures
|
|
69
|
+
if 'seasons' in episodes_data:
|
|
70
|
+
for season_data in episodes_data.get('seasons', []):
|
|
71
|
+
for episode_set in season_data.get('episodes', []):
|
|
72
|
+
cards.extend(episode_set.get('cards', []))
|
|
73
|
+
|
|
74
|
+
if not cards:
|
|
75
|
+
cards = episodes_data.get('cards', [])
|
|
76
|
+
|
|
77
|
+
# Add episodes to season
|
|
78
|
+
for ep in cards:
|
|
79
|
+
episode = {
|
|
80
|
+
'id': ep.get('id', ''),
|
|
81
|
+
'number': ep.get('episode', ''),
|
|
82
|
+
'name': ep.get('episode_title', '') or ep.get('toptitle', ''),
|
|
83
|
+
'duration': ep.get('duration', ''),
|
|
84
|
+
'url': f"{self.base_url}{ep.get('weblink', '')}" if 'weblink' in ep else f"{self.base_url}{ep.get('url', '')}"
|
|
85
|
+
}
|
|
86
|
+
season.episodes.add(episode)
|
|
87
|
+
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logging.error(f"Error collecting episodes for season {number_season}: {e}")
|
|
90
|
+
raise
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ------------- FOR GUI -------------
|
|
94
|
+
def getNumberSeason(self) -> int:
|
|
95
|
+
"""
|
|
96
|
+
Get the total number of seasons available for the series.
|
|
97
|
+
"""
|
|
98
|
+
if not self.seasons_manager.seasons:
|
|
99
|
+
self.collect_info_title()
|
|
100
|
+
|
|
101
|
+
return len(self.seasons_manager.seasons)
|
|
102
|
+
|
|
103
|
+
def getEpisodeSeasons(self, season_number: int) -> list:
|
|
104
|
+
"""
|
|
105
|
+
Get all episodes for a specific season.
|
|
106
|
+
"""
|
|
107
|
+
season = self.seasons_manager.get_season_by_number(season_number)
|
|
108
|
+
|
|
109
|
+
if not season:
|
|
110
|
+
logging.error(f"Season {season_number} not found")
|
|
111
|
+
return []
|
|
112
|
+
|
|
113
|
+
if not season.episodes.episodes:
|
|
114
|
+
self.collect_info_season(season_number)
|
|
115
|
+
|
|
116
|
+
return season.episodes.episodes
|
|
117
|
+
|
|
118
|
+
def selectEpisode(self, season_number: int, episode_index: int) -> dict:
|
|
119
|
+
"""
|
|
120
|
+
Get information for a specific episode in a specific season.
|
|
121
|
+
"""
|
|
122
|
+
episodes = self.getEpisodeSeasons(season_number)
|
|
123
|
+
if not episodes or episode_index < 0 or episode_index >= len(episodes):
|
|
124
|
+
logging.error(f"Episode index {episode_index} is out of range for season {season_number}")
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
return episodes[episode_index]
|
|
@@ -58,52 +58,59 @@ def get_user_input(string_to_search: str = None):
|
|
|
58
58
|
|
|
59
59
|
return string_to_search
|
|
60
60
|
|
|
61
|
-
def process_search_result(select_title):
|
|
61
|
+
def process_search_result(select_title, selections=None):
|
|
62
62
|
"""
|
|
63
63
|
Handles the search result and initiates the download for either a film or series.
|
|
64
|
+
|
|
65
|
+
Parameters:
|
|
66
|
+
select_title (MediaItem): The selected media item
|
|
67
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
68
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
64
69
|
"""
|
|
65
70
|
if select_title.type == 'tv':
|
|
66
|
-
|
|
71
|
+
season_selection = None
|
|
72
|
+
episode_selection = None
|
|
73
|
+
|
|
74
|
+
if selections:
|
|
75
|
+
season_selection = selections.get('season')
|
|
76
|
+
episode_selection = selections.get('episode')
|
|
77
|
+
|
|
78
|
+
download_series(select_title, season_selection, episode_selection)
|
|
79
|
+
|
|
67
80
|
else:
|
|
68
81
|
download_film(select_title)
|
|
69
82
|
|
|
70
|
-
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
|
83
|
+
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
|
|
71
84
|
"""
|
|
72
|
-
Main function of the application for search
|
|
85
|
+
Main function of the application for search.
|
|
73
86
|
|
|
74
87
|
Parameters:
|
|
75
88
|
string_to_search (str, optional): String to search for
|
|
76
|
-
|
|
89
|
+
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
77
90
|
direct_item (dict, optional): Direct item to process (bypass search)
|
|
91
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
92
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
78
93
|
"""
|
|
79
94
|
if direct_item:
|
|
80
95
|
select_title = MediaItem(**direct_item)
|
|
81
|
-
process_search_result(select_title)
|
|
96
|
+
process_search_result(select_title, selections)
|
|
82
97
|
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
#
|
|
88
|
-
len_database = title_search(
|
|
98
|
+
|
|
99
|
+
if string_to_search is None:
|
|
100
|
+
string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
101
|
+
|
|
102
|
+
# Search on database
|
|
103
|
+
len_database = title_search(string_to_search)
|
|
89
104
|
|
|
90
105
|
# If only the database is needed, return the manager
|
|
91
106
|
if get_onlyDatabase:
|
|
92
107
|
return media_search_manager
|
|
93
108
|
|
|
94
|
-
if site_constant.TELEGRAM_BOT:
|
|
95
|
-
bot = get_bot_instance()
|
|
96
|
-
|
|
97
109
|
if len_database > 0:
|
|
98
110
|
select_title = get_select_title(table_show_manager, media_search_manager)
|
|
99
|
-
process_search_result(select_title)
|
|
111
|
+
process_search_result(select_title, selections)
|
|
100
112
|
|
|
101
113
|
else:
|
|
102
|
-
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
103
|
-
|
|
104
|
-
if site_constant.TELEGRAM_BOT:
|
|
105
|
-
bot.send_message(f"No results found, please try again", None)
|
|
106
|
-
|
|
107
114
|
# If no results are found, ask again
|
|
108
|
-
|
|
115
|
+
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
109
116
|
search()
|
|
@@ -55,8 +55,7 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
55
55
|
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
|
|
56
56
|
|
|
57
57
|
# Init class
|
|
58
|
-
video_source = VideoSource(site_constant.FULL_URL, False)
|
|
59
|
-
video_source.setup(select_title.id)
|
|
58
|
+
video_source = VideoSource(site_constant.FULL_URL, False, select_title.id)
|
|
60
59
|
|
|
61
60
|
# Retrieve scws and if available master playlist
|
|
62
61
|
video_source.get_iframe(select_title.id)
|
|
@@ -39,28 +39,22 @@ console = Console()
|
|
|
39
39
|
|
|
40
40
|
def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: GetSerieInfo, video_source: VideoSource) -> Tuple[str,bool]:
|
|
41
41
|
"""
|
|
42
|
-
|
|
42
|
+
Downloads a specific episode from the specified season.
|
|
43
43
|
|
|
44
44
|
Parameters:
|
|
45
|
-
- index_season_selected (int):
|
|
46
|
-
- index_episode_selected (int):
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
- index_season_selected (int): Season number
|
|
46
|
+
- index_episode_selected (int): Episode index
|
|
47
|
+
- scrape_serie (GetSerieInfo): Scraper object with series information
|
|
48
|
+
- video_source (VideoSource): Video source handler
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
- str: Path to downloaded file
|
|
52
|
+
- bool: Whether download was stopped
|
|
51
53
|
"""
|
|
52
54
|
start_message()
|
|
53
|
-
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
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
|
|
63
|
-
obj_episode =
|
|
56
|
+
# Get episode information
|
|
57
|
+
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
64
58
|
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
|
|
65
59
|
|
|
66
60
|
if site_constant.TELEGRAM_BOT:
|
|
@@ -98,28 +92,28 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
98
92
|
|
|
99
93
|
return r_proc['path'], r_proc['stopped']
|
|
100
94
|
|
|
101
|
-
|
|
95
|
+
|
|
96
|
+
def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, video_source: VideoSource, download_all: bool = False, episode_selection: str = None) -> None:
|
|
102
97
|
"""
|
|
103
|
-
|
|
98
|
+
Handle downloading episodes for a specific season.
|
|
104
99
|
|
|
105
100
|
Parameters:
|
|
106
|
-
- index_season_selected (int):
|
|
107
|
-
-
|
|
101
|
+
- index_season_selected (int): Season number
|
|
102
|
+
- scrape_serie (GetSerieInfo): Scraper object with series information
|
|
103
|
+
- video_source (VideoSource): Video source object
|
|
104
|
+
- download_all (bool): Whether to download all episodes
|
|
105
|
+
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
|
|
108
106
|
"""
|
|
109
|
-
|
|
110
|
-
scrape_serie.
|
|
111
|
-
|
|
112
|
-
# SPECIAL: Get season number
|
|
113
|
-
season = None
|
|
114
|
-
for s in scrape_serie.seasons_manager.seasons:
|
|
115
|
-
if s.number == index_season_selected:
|
|
116
|
-
season = s
|
|
117
|
-
break
|
|
118
|
-
episodes_count = len(season.episodes.episodes)
|
|
107
|
+
# Get episodes for the selected season
|
|
108
|
+
episodes = scrape_serie.getEpisodeSeasons(index_season_selected)
|
|
109
|
+
episodes_count = len(episodes)
|
|
119
110
|
|
|
120
|
-
if
|
|
111
|
+
if episodes_count == 0:
|
|
112
|
+
console.print(f"[red]No episodes found for season {index_season_selected}")
|
|
113
|
+
return
|
|
121
114
|
|
|
122
|
-
|
|
115
|
+
if download_all:
|
|
116
|
+
# Download all episodes in the season
|
|
123
117
|
for i_episode in range(1, episodes_count + 1):
|
|
124
118
|
path, stopped = download_video(index_season_selected, i_episode, scrape_serie, video_source)
|
|
125
119
|
|
|
@@ -129,16 +123,16 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
|
|
|
129
123
|
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
|
130
124
|
|
|
131
125
|
else:
|
|
132
|
-
|
|
133
126
|
# Display episodes list and manage user selection
|
|
134
|
-
|
|
127
|
+
if episode_selection is None:
|
|
128
|
+
last_command = display_episodes_list(episodes)
|
|
129
|
+
else:
|
|
130
|
+
last_command = episode_selection
|
|
131
|
+
console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
|
|
132
|
+
|
|
133
|
+
# Validate the selection
|
|
135
134
|
list_episode_select = manage_selection(last_command, episodes_count)
|
|
136
|
-
|
|
137
|
-
try:
|
|
138
|
-
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
|
139
|
-
except ValueError as e:
|
|
140
|
-
console.print(f"[red]{str(e)}")
|
|
141
|
-
return
|
|
135
|
+
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
|
142
136
|
|
|
143
137
|
# Download selected episodes if not stopped
|
|
144
138
|
for i_episode in list_episode_select:
|
|
@@ -147,70 +141,65 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
|
|
|
147
141
|
if stopped:
|
|
148
142
|
break
|
|
149
143
|
|
|
150
|
-
|
|
144
|
+
|
|
145
|
+
def download_series(select_season: MediaItem, season_selection: str = None, episode_selection: str = None) -> None:
|
|
151
146
|
"""
|
|
152
|
-
|
|
147
|
+
Handle downloading a complete series.
|
|
153
148
|
|
|
154
149
|
Parameters:
|
|
155
|
-
- select_season (MediaItem):
|
|
156
|
-
-
|
|
150
|
+
- select_season (MediaItem): Series metadata from search
|
|
151
|
+
- season_selection (str, optional): Pre-defined season selection that bypasses manual input
|
|
152
|
+
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
|
|
157
153
|
"""
|
|
158
|
-
if site_constant.TELEGRAM_BOT:
|
|
159
|
-
bot = get_bot_instance()
|
|
160
|
-
|
|
161
|
-
# Start message and set up video source
|
|
162
154
|
start_message()
|
|
163
155
|
|
|
164
156
|
# Init class
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
# Setup video source
|
|
169
|
-
scrape_serie.setup(select_season.id, select_season.slug)
|
|
170
|
-
video_source.setup(select_season.id)
|
|
157
|
+
video_source = VideoSource(site_constant.FULL_URL, True, select_season.id)
|
|
158
|
+
scrape_serie = GetSerieInfo(site_constant.FULL_URL, select_season.id, select_season.name)
|
|
171
159
|
|
|
172
|
-
# Collect information about
|
|
173
|
-
scrape_serie.
|
|
160
|
+
# Collect information about season
|
|
161
|
+
scrape_serie.getNumberSeason()
|
|
174
162
|
seasons_count = len(scrape_serie.seasons_manager)
|
|
175
163
|
|
|
164
|
+
if site_constant.TELEGRAM_BOT:
|
|
165
|
+
bot = get_bot_instance()
|
|
166
|
+
|
|
176
167
|
# Prompt user for season selection and download episodes
|
|
177
168
|
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
|
178
169
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
170
|
+
# If season_selection is provided, use it instead of asking for input
|
|
171
|
+
if season_selection is None:
|
|
172
|
+
if site_constant.TELEGRAM_BOT:
|
|
173
|
+
console.print("\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
|
174
|
+
"[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end")
|
|
175
|
+
|
|
176
|
+
bot.send_message(f"Stagioni trovate: {seasons_count}", None)
|
|
177
|
+
|
|
178
|
+
index_season_selected = bot.ask(
|
|
179
|
+
"select_title_episode",
|
|
180
|
+
"Menu di selezione delle stagioni\n\n"
|
|
181
|
+
"- Inserisci il numero della stagione (ad esempio, 1)\n"
|
|
182
|
+
"- Inserisci * per scaricare tutte le stagioni\n"
|
|
183
|
+
"- Inserisci un intervallo di stagioni (ad esempio, 1-2) per scaricare da una stagione all'altra\n"
|
|
184
|
+
"- Inserisci (ad esempio, 3-*) per scaricare dalla stagione specificata fino alla fine della serie",
|
|
185
|
+
None
|
|
186
|
+
)
|
|
194
187
|
|
|
188
|
+
else:
|
|
189
|
+
index_season_selected = msg.ask(
|
|
190
|
+
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
|
191
|
+
"[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end"
|
|
192
|
+
)
|
|
195
193
|
else:
|
|
196
|
-
index_season_selected =
|
|
197
|
-
|
|
198
|
-
"[yellow](e.g., 1-2) [cyan]for a range of seasons, or [yellow](e.g., 3-*) [cyan]to download from a specific season to the end"
|
|
199
|
-
)
|
|
194
|
+
index_season_selected = season_selection
|
|
195
|
+
console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
|
|
200
196
|
|
|
201
|
-
#
|
|
197
|
+
# Validate the selection
|
|
202
198
|
list_season_select = manage_selection(index_season_selected, seasons_count)
|
|
203
|
-
|
|
204
|
-
try:
|
|
205
|
-
list_season_select = validate_selection(list_season_select, seasons_count)
|
|
206
|
-
except ValueError as e:
|
|
207
|
-
console.print(f"[red]{str(e)}")
|
|
208
|
-
return
|
|
199
|
+
list_season_select = validate_selection(list_season_select, seasons_count)
|
|
209
200
|
|
|
210
201
|
# Loop through the selected seasons and download episodes
|
|
211
202
|
for i_season in list_season_select:
|
|
212
|
-
|
|
213
|
-
# SPECIAL: Get season number
|
|
214
203
|
season = None
|
|
215
204
|
for s in scrape_serie.seasons_manager.seasons:
|
|
216
205
|
if s.number == i_season:
|
|
@@ -219,13 +208,10 @@ def download_series(select_season: MediaItem) -> None:
|
|
|
219
208
|
season_number = season.number
|
|
220
209
|
|
|
221
210
|
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
222
|
-
|
|
223
|
-
# Download all episodes if multiple seasons are selected or if '*' is used
|
|
224
211
|
download_episode(season_number, scrape_serie, video_source, download_all=True)
|
|
212
|
+
|
|
225
213
|
else:
|
|
226
|
-
|
|
227
|
-
# Otherwise, let the user select specific episodes for the single season
|
|
228
|
-
download_episode(season_number, scrape_serie, video_source, download_all=False)
|
|
214
|
+
download_episode(season_number, scrape_serie, video_source, download_all=False, episode_selection=episode_selection)
|
|
229
215
|
|
|
230
216
|
if site_constant.TELEGRAM_BOT:
|
|
231
217
|
bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# 10.12.23
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
3
|
|
|
6
4
|
# External libraries
|
|
7
5
|
import httpx
|
|
@@ -75,7 +73,7 @@ def title_search(query: str) -> int:
|
|
|
75
73
|
'name': dict_title.get('name'),
|
|
76
74
|
'type': dict_title.get('type'),
|
|
77
75
|
'date': dict_title.get('last_air_date'),
|
|
78
|
-
'
|
|
76
|
+
'image': f"{site_constant.FULL_URL.replace('stream', 'cdn.stream')}/images/{dict_title.get('images')[0].get('filename')}"
|
|
79
77
|
})
|
|
80
78
|
|
|
81
79
|
if site_constant.TELEGRAM_BOT:
|