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
|
@@ -47,7 +47,6 @@ class GetSerieInfo:
|
|
|
47
47
|
Returns:
|
|
48
48
|
List[Dict[str, str]]: List of dictionaries containing episode information.
|
|
49
49
|
"""
|
|
50
|
-
|
|
51
50
|
try:
|
|
52
51
|
response = httpx.get(f"{self.url}?area=online", cookies=self.cookies, headers=self.headers, timeout=max_timeout)
|
|
53
52
|
response.raise_for_status()
|
|
@@ -81,4 +80,33 @@ class GetSerieInfo:
|
|
|
81
80
|
|
|
82
81
|
self.list_episodes = list_dict_episode
|
|
83
82
|
return list_dict_episode
|
|
84
|
-
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ------------- FOR GUI -------------
|
|
86
|
+
def getNumberSeason(self) -> int:
|
|
87
|
+
"""
|
|
88
|
+
Get the total number of seasons available for the series.
|
|
89
|
+
Note: DDLStreamItaly typically provides content organized as threads, not seasons.
|
|
90
|
+
"""
|
|
91
|
+
return 1
|
|
92
|
+
|
|
93
|
+
def getEpisodeSeasons(self, season_number: int = 1) -> list:
|
|
94
|
+
"""
|
|
95
|
+
Get all episodes for a specific season.
|
|
96
|
+
Note: For DDLStreamItaly, this returns all episodes as they're typically in one list.
|
|
97
|
+
"""
|
|
98
|
+
if not self.list_episodes:
|
|
99
|
+
self.list_episodes = self.get_episode_number()
|
|
100
|
+
|
|
101
|
+
return self.list_episodes
|
|
102
|
+
|
|
103
|
+
def selectEpisode(self, season_number: int = 1, episode_index: int = 0) -> dict:
|
|
104
|
+
"""
|
|
105
|
+
Get information for a specific episode.
|
|
106
|
+
"""
|
|
107
|
+
episodes = self.getEpisodeSeasons()
|
|
108
|
+
if not episodes or episode_index < 0 or episode_index >= len(episodes):
|
|
109
|
+
logging.error(f"Episode index {episode_index} is out of range")
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
return episodes[episode_index]
|
|
@@ -30,24 +30,38 @@ msg = Prompt()
|
|
|
30
30
|
console = Console()
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def process_search_result(select_title):
|
|
33
|
+
def process_search_result(select_title, selections=None):
|
|
34
34
|
"""
|
|
35
35
|
Handles the search result and initiates the download for either a film or series.
|
|
36
|
+
|
|
37
|
+
Parameters:
|
|
38
|
+
select_title (MediaItem): The selected media item
|
|
39
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
40
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
36
41
|
"""
|
|
37
|
-
|
|
42
|
+
season_selection = None
|
|
43
|
+
episode_selection = None
|
|
44
|
+
|
|
45
|
+
if selections:
|
|
46
|
+
season_selection = selections.get('season')
|
|
47
|
+
episode_selection = selections.get('episode')
|
|
48
|
+
|
|
49
|
+
download_series(select_title, season_selection, episode_selection)
|
|
38
50
|
|
|
39
|
-
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
|
51
|
+
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
|
|
40
52
|
"""
|
|
41
|
-
Main function of the application for search
|
|
53
|
+
Main function of the application for search.
|
|
42
54
|
|
|
43
55
|
Parameters:
|
|
44
56
|
string_to_search (str, optional): String to search for
|
|
45
|
-
|
|
57
|
+
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
46
58
|
direct_item (dict, optional): Direct item to process (bypass search)
|
|
59
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
60
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
47
61
|
"""
|
|
48
62
|
if direct_item:
|
|
49
63
|
select_title = MediaItem(**direct_item)
|
|
50
|
-
process_search_result(select_title)
|
|
64
|
+
process_search_result(select_title, selections)
|
|
51
65
|
return
|
|
52
66
|
|
|
53
67
|
if string_to_search is None:
|
|
@@ -62,7 +76,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
62
76
|
|
|
63
77
|
if len_database > 0:
|
|
64
78
|
select_title = get_select_title(table_show_manager, media_search_manager)
|
|
65
|
-
process_search_result(select_title)
|
|
79
|
+
process_search_result(select_title, selections)
|
|
66
80
|
|
|
67
81
|
else:
|
|
68
82
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# 13.06.24
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import logging
|
|
4
5
|
from typing import Tuple
|
|
5
6
|
|
|
6
7
|
|
|
@@ -39,22 +40,22 @@ console = Console()
|
|
|
39
40
|
|
|
40
41
|
def download_video(index_season_selected: int, index_episode_selected: int, scape_info_serie: GetSerieInfo) -> Tuple[str,bool]:
|
|
41
42
|
"""
|
|
42
|
-
|
|
43
|
+
Downloads a specific episode from a specified season.
|
|
43
44
|
|
|
44
45
|
Parameters:
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
46
|
+
- index_season_selected (int): Season number
|
|
47
|
+
- index_episode_selected (int): Episode index
|
|
48
|
+
- scape_info_serie (GetSerieInfo): Scraper object with series information
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
- str:
|
|
51
|
-
- bool:
|
|
50
|
+
Returns:
|
|
51
|
+
- str: Path to downloaded file
|
|
52
|
+
- bool: Whether download was stopped
|
|
52
53
|
"""
|
|
53
54
|
start_message()
|
|
54
|
-
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
55
55
|
|
|
56
|
-
# Get
|
|
57
|
-
obj_episode = scape_info_serie.
|
|
56
|
+
# Get episode information
|
|
57
|
+
obj_episode = scape_info_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
58
|
+
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
58
59
|
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [bold magenta]{obj_episode.get('name')}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
|
|
59
60
|
|
|
60
61
|
# Define filename and path for the downloaded video
|
|
@@ -80,24 +81,23 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
|
|
|
80
81
|
return r_proc['path'], r_proc['stopped']
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, download_all: bool = False) -> None:
|
|
84
|
+
def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, download_all: bool = False, episode_selection: str = None) -> None:
|
|
84
85
|
"""
|
|
85
|
-
|
|
86
|
+
Handle downloading episodes for a specific season.
|
|
86
87
|
|
|
87
88
|
Parameters:
|
|
88
|
-
-
|
|
89
|
-
- index_season_selected (int):
|
|
90
|
-
- download_all (bool):
|
|
89
|
+
- scape_info_serie (GetSerieInfo): Scraper object with series information
|
|
90
|
+
- index_season_selected (int): Season number
|
|
91
|
+
- download_all (bool): Whether to download all episodes
|
|
92
|
+
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
|
|
91
93
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
list_dict_episode = scape_info_serie.get_episode_number(index_season_selected)
|
|
96
|
-
episodes_count = len(list_dict_episode)
|
|
94
|
+
# Get episodes for the selected season
|
|
95
|
+
episodes = scape_info_serie.get_episode_number(index_season_selected)
|
|
96
|
+
episodes_count = len(episodes)
|
|
97
97
|
|
|
98
98
|
if download_all:
|
|
99
|
-
|
|
100
|
-
# Download all episodes
|
|
99
|
+
|
|
100
|
+
# Download all episodes in the season
|
|
101
101
|
for i_episode in range(1, episodes_count + 1):
|
|
102
102
|
path, stopped = download_video(index_season_selected, i_episode, scape_info_serie)
|
|
103
103
|
|
|
@@ -109,14 +109,15 @@ def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int,
|
|
|
109
109
|
else:
|
|
110
110
|
|
|
111
111
|
# Display episodes list and manage user selection
|
|
112
|
-
|
|
112
|
+
if episode_selection is None:
|
|
113
|
+
last_command = display_episodes_list(scape_info_serie.list_episodes)
|
|
114
|
+
else:
|
|
115
|
+
last_command = episode_selection
|
|
116
|
+
console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
|
|
117
|
+
|
|
118
|
+
# Validate the selection
|
|
113
119
|
list_episode_select = manage_selection(last_command, episodes_count)
|
|
114
|
-
|
|
115
|
-
try:
|
|
116
|
-
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
|
117
|
-
except ValueError as e:
|
|
118
|
-
console.print(f"[red]{str(e)}")
|
|
119
|
-
return
|
|
120
|
+
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
|
120
121
|
|
|
121
122
|
# Download selected episodes
|
|
122
123
|
for i_episode in list_episode_select:
|
|
@@ -126,46 +127,47 @@ def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int,
|
|
|
126
127
|
break
|
|
127
128
|
|
|
128
129
|
|
|
129
|
-
def download_series(dict_serie: MediaItem) -> None:
|
|
130
|
+
def download_series(dict_serie: MediaItem, season_selection: str = None, episode_selection: str = None) -> None:
|
|
130
131
|
"""
|
|
131
|
-
|
|
132
|
+
Handle downloading a complete series.
|
|
132
133
|
|
|
133
134
|
Parameters:
|
|
134
|
-
- dict_serie (MediaItem):
|
|
135
|
+
- dict_serie (MediaItem): Series metadata from search
|
|
136
|
+
- season_selection (str, optional): Pre-defined season selection that bypasses manual input
|
|
137
|
+
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
|
|
135
138
|
"""
|
|
136
|
-
|
|
137
|
-
# Start message and set up video source
|
|
138
139
|
start_message()
|
|
139
140
|
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
# Collect information about seasons
|
|
144
|
-
seasons_count = scape_info_serie.get_seasons_number()
|
|
141
|
+
# Create class
|
|
142
|
+
scrape_serie = GetSerieInfo(dict_serie)
|
|
145
143
|
|
|
144
|
+
# Get season count
|
|
145
|
+
seasons_count = scrape_serie.get_seasons_number()
|
|
146
|
+
|
|
146
147
|
# Prompt user for season selection and download episodes
|
|
147
148
|
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
|
148
|
-
index_season_selected = msg.ask(
|
|
149
|
-
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
|
150
|
-
"[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"
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
# Manage and validate the selection
|
|
154
|
-
list_season_select = manage_selection(index_season_selected, seasons_count)
|
|
155
149
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
150
|
+
# If season_selection is provided, use it instead of asking for input
|
|
151
|
+
if season_selection is None:
|
|
152
|
+
index_season_selected = msg.ask(
|
|
153
|
+
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
|
154
|
+
"[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"
|
|
155
|
+
)
|
|
156
|
+
else:
|
|
157
|
+
index_season_selected = season_selection
|
|
158
|
+
console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
|
|
159
|
+
|
|
160
|
+
# Validate the selection
|
|
161
|
+
list_season_select = manage_selection(index_season_selected, seasons_count)
|
|
162
|
+
list_season_select = validate_selection(list_season_select, seasons_count)
|
|
161
163
|
|
|
162
164
|
# Loop through the selected seasons and download episodes
|
|
163
165
|
for i_season in list_season_select:
|
|
164
166
|
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
165
167
|
|
|
166
168
|
# Download all episodes if multiple seasons are selected or if '*' is used
|
|
167
|
-
download_episode(
|
|
169
|
+
download_episode(scrape_serie, i_season, download_all=True)
|
|
168
170
|
else:
|
|
169
171
|
|
|
170
172
|
# Otherwise, let the user select specific episodes for the single season
|
|
171
|
-
download_episode(
|
|
173
|
+
download_episode(scrape_serie, i_season, download_all=False, episode_selection=episode_selection)
|
|
@@ -62,9 +62,10 @@ def title_search(query: str) -> int:
|
|
|
62
62
|
link = serie_div.find('a').get("href")
|
|
63
63
|
|
|
64
64
|
serie_info = {
|
|
65
|
-
'name': title,
|
|
65
|
+
'name': title.replace("streaming guardaserie", ""),
|
|
66
66
|
'url': link,
|
|
67
|
-
'type': 'tv'
|
|
67
|
+
'type': 'tv',
|
|
68
|
+
'image': f"{site_constant.FULL_URL}/{serie_div.find('img').get('src')}",
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
media_search_manager.add_media(serie_info)
|
|
@@ -104,4 +104,30 @@ class GetSerieInfo:
|
|
|
104
104
|
except Exception as e:
|
|
105
105
|
logging.error(f"Error parsing HTML page: {e}")
|
|
106
106
|
|
|
107
|
-
return []
|
|
107
|
+
return []
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ------------- FOR GUI -------------
|
|
111
|
+
def getNumberSeason(self) -> int:
|
|
112
|
+
"""
|
|
113
|
+
Get the total number of seasons available for the series.
|
|
114
|
+
"""
|
|
115
|
+
return self.get_seasons_number()
|
|
116
|
+
|
|
117
|
+
def getEpisodeSeasons(self, season_number: int) -> list:
|
|
118
|
+
"""
|
|
119
|
+
Get all episodes for a specific season.
|
|
120
|
+
"""
|
|
121
|
+
episodes = self.get_episode_number(season_number)
|
|
122
|
+
return episodes
|
|
123
|
+
|
|
124
|
+
def selectEpisode(self, season_number: int, episode_index: int) -> dict:
|
|
125
|
+
"""
|
|
126
|
+
Get information for a specific episode in a specific season.
|
|
127
|
+
"""
|
|
128
|
+
episodes = self.getEpisodeSeasons(season_number)
|
|
129
|
+
if not episodes or episode_index < 0 or episode_index >= len(episodes):
|
|
130
|
+
logging.error(f"Episode index {episode_index} is out of range for season {season_number}")
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
return episodes[episode_index]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# 21.05.24
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# External library
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.prompt import Prompt
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Internal utilities
|
|
10
|
+
from StreamingCommunity.Api.Template import get_select_title
|
|
11
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
12
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Logic class
|
|
16
|
+
from .site import title_search, table_show_manager, media_search_manager
|
|
17
|
+
from .series import download_series
|
|
18
|
+
from .film import download_film
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Variable
|
|
22
|
+
indice = 8
|
|
23
|
+
_useFor = "film_serie"
|
|
24
|
+
_deprecate = False
|
|
25
|
+
_priority = 3
|
|
26
|
+
_engineDownload = "hls"
|
|
27
|
+
|
|
28
|
+
msg = Prompt()
|
|
29
|
+
console = Console()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_user_input(string_to_search: str = None):
|
|
33
|
+
"""
|
|
34
|
+
Asks the user to input a search term.
|
|
35
|
+
"""
|
|
36
|
+
return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
37
|
+
|
|
38
|
+
def process_search_result(select_title, selections=None):
|
|
39
|
+
"""
|
|
40
|
+
Handles the search result and initiates the download for either a film or series.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
select_title (MediaItem): The selected media item
|
|
44
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
45
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
46
|
+
"""
|
|
47
|
+
if select_title.type == 'tv':
|
|
48
|
+
season_selection = None
|
|
49
|
+
episode_selection = None
|
|
50
|
+
|
|
51
|
+
if selections:
|
|
52
|
+
season_selection = selections.get('season')
|
|
53
|
+
episode_selection = selections.get('episode')
|
|
54
|
+
|
|
55
|
+
download_series(select_title, season_selection, episode_selection)
|
|
56
|
+
|
|
57
|
+
else:
|
|
58
|
+
download_film(select_title)
|
|
59
|
+
|
|
60
|
+
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
|
|
61
|
+
"""
|
|
62
|
+
Main function of the application for search.
|
|
63
|
+
|
|
64
|
+
Parameters:
|
|
65
|
+
string_to_search (str, optional): String to search for
|
|
66
|
+
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
67
|
+
direct_item (dict, optional): Direct item to process (bypass search)
|
|
68
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
69
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
70
|
+
"""
|
|
71
|
+
if direct_item:
|
|
72
|
+
select_title = MediaItem(**direct_item)
|
|
73
|
+
process_search_result(select_title, selections)
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
if string_to_search is None:
|
|
77
|
+
string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
78
|
+
|
|
79
|
+
# Search on database
|
|
80
|
+
len_database = title_search(string_to_search)
|
|
81
|
+
|
|
82
|
+
# If only the database is needed, return the manager
|
|
83
|
+
if get_onlyDatabase:
|
|
84
|
+
return media_search_manager
|
|
85
|
+
|
|
86
|
+
if len_database > 0:
|
|
87
|
+
select_title = get_select_title(table_show_manager, media_search_manager)
|
|
88
|
+
process_search_result(select_title, selections)
|
|
89
|
+
|
|
90
|
+
else:
|
|
91
|
+
# If no results are found, ask again
|
|
92
|
+
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
93
|
+
search()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# 3.12.23
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Tuple
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# External library
|
|
8
|
+
import httpx
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Internal utilities
|
|
13
|
+
from StreamingCommunity.Util.os import os_manager
|
|
14
|
+
from StreamingCommunity.Util.message import start_message
|
|
15
|
+
from StreamingCommunity.Lib.Downloader import HLS_Downloader
|
|
16
|
+
from StreamingCommunity.Util.headers import get_headers
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Logic class
|
|
20
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
21
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Player
|
|
25
|
+
from StreamingCommunity.Api.Player.mediapolisvod import VideoSource
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Variable
|
|
29
|
+
console = Console()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def download_film(select_title: MediaItem) -> Tuple[str, bool]:
|
|
33
|
+
"""
|
|
34
|
+
Downloads a film using the provided MediaItem information.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
- select_title (MediaItem): The media item containing film information
|
|
38
|
+
|
|
39
|
+
Return:
|
|
40
|
+
- str: Path to downloaded file
|
|
41
|
+
- bool: Whether download was stopped
|
|
42
|
+
"""
|
|
43
|
+
start_message()
|
|
44
|
+
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
|
|
45
|
+
|
|
46
|
+
# Extract m3u8 URL from the film's URL
|
|
47
|
+
response = httpx.get(select_title.url + ".json", headers=get_headers(), timeout=10)
|
|
48
|
+
first_item_path = "https://www.raiplay.it" + response.json().get("first_item_path")
|
|
49
|
+
master_playlist = VideoSource.extract_m3u8_url(first_item_path)
|
|
50
|
+
|
|
51
|
+
# Define the filename and path for the downloaded film
|
|
52
|
+
title_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
|
|
53
|
+
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
|
|
54
|
+
|
|
55
|
+
# Download the film using the m3u8 playlist, and output filename
|
|
56
|
+
r_proc = HLS_Downloader(
|
|
57
|
+
m3u8_url=master_playlist,
|
|
58
|
+
output_path=os.path.join(mp4_path, title_name)
|
|
59
|
+
).start()
|
|
60
|
+
|
|
61
|
+
if r_proc['error'] is not None:
|
|
62
|
+
try: os.remove(r_proc['path'])
|
|
63
|
+
except: pass
|
|
64
|
+
|
|
65
|
+
return r_proc['path'], r_proc['stopped']
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# 3.12.23
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Tuple
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# External library
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.prompt import Prompt
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Internal utilities
|
|
13
|
+
from StreamingCommunity.Util.message import start_message
|
|
14
|
+
from StreamingCommunity.Lib.Downloader import HLS_Downloader
|
|
15
|
+
|
|
16
|
+
# Logic class
|
|
17
|
+
from .util.ScrapeSerie import GetSerieInfo
|
|
18
|
+
from StreamingCommunity.Api.Template.Util import (
|
|
19
|
+
manage_selection,
|
|
20
|
+
map_episode_title,
|
|
21
|
+
validate_selection,
|
|
22
|
+
validate_episode_selection,
|
|
23
|
+
display_episodes_list
|
|
24
|
+
)
|
|
25
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
26
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Player
|
|
30
|
+
from StreamingCommunity.Api.Player.mediapolisvod import VideoSource
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Variable
|
|
34
|
+
msg = Prompt()
|
|
35
|
+
console = Console()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: GetSerieInfo) -> Tuple[str,bool]:
|
|
39
|
+
"""
|
|
40
|
+
Downloads a specific episode from the specified season.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
- index_season_selected (int): Season number
|
|
44
|
+
- index_episode_selected (int): Episode index
|
|
45
|
+
- scrape_serie (GetSerieInfo): Scraper object with series information
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
- str: Path to downloaded file
|
|
49
|
+
- bool: Whether download was stopped
|
|
50
|
+
"""
|
|
51
|
+
start_message()
|
|
52
|
+
|
|
53
|
+
# Get episode information
|
|
54
|
+
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
55
|
+
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")
|
|
56
|
+
|
|
57
|
+
# Get streaming URL
|
|
58
|
+
master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
|
|
59
|
+
|
|
60
|
+
# Define filename and path
|
|
61
|
+
mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
|
|
62
|
+
mp4_path = os.path.join(site_constant.SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}")
|
|
63
|
+
|
|
64
|
+
# Download the episode
|
|
65
|
+
r_proc = HLS_Downloader(
|
|
66
|
+
m3u8_url=master_playlist,
|
|
67
|
+
output_path=os.path.join(mp4_path, mp4_name)
|
|
68
|
+
).start()
|
|
69
|
+
|
|
70
|
+
if r_proc['error'] is not None:
|
|
71
|
+
try: os.remove(r_proc['path'])
|
|
72
|
+
except: pass
|
|
73
|
+
|
|
74
|
+
return r_proc['path'], r_proc['stopped']
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, download_all: bool = False, episode_selection: str = None) -> None:
|
|
78
|
+
"""
|
|
79
|
+
Handle downloading episodes for a specific season.
|
|
80
|
+
|
|
81
|
+
Parameters:
|
|
82
|
+
- index_season_selected (int): Season number
|
|
83
|
+
- scrape_serie (GetSerieInfo): Scraper object with series information
|
|
84
|
+
- download_all (bool): Whether to download all episodes
|
|
85
|
+
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
|
|
86
|
+
"""
|
|
87
|
+
# Get episodes for the selected season
|
|
88
|
+
episodes = scrape_serie.getEpisodeSeasons(index_season_selected)
|
|
89
|
+
episodes_count = len(episodes)
|
|
90
|
+
|
|
91
|
+
if download_all:
|
|
92
|
+
for i_episode in range(1, episodes_count + 1):
|
|
93
|
+
path, stopped = download_video(index_season_selected, i_episode, scrape_serie)
|
|
94
|
+
if stopped:
|
|
95
|
+
break
|
|
96
|
+
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
|
97
|
+
|
|
98
|
+
else:
|
|
99
|
+
# Display episodes list and manage user selection
|
|
100
|
+
if episode_selection is None:
|
|
101
|
+
last_command = display_episodes_list(episodes)
|
|
102
|
+
else:
|
|
103
|
+
last_command = episode_selection
|
|
104
|
+
console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
|
|
105
|
+
|
|
106
|
+
# Validate the selection
|
|
107
|
+
list_episode_select = manage_selection(last_command, episodes_count)
|
|
108
|
+
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
|
109
|
+
|
|
110
|
+
# Download selected episodes if not stopped
|
|
111
|
+
for i_episode in list_episode_select:
|
|
112
|
+
path, stopped = download_video(index_season_selected, i_episode, scrape_serie)
|
|
113
|
+
if stopped:
|
|
114
|
+
break
|
|
115
|
+
|
|
116
|
+
def download_series(select_season: MediaItem, season_selection: str = None, episode_selection: str = None) -> None:
|
|
117
|
+
"""
|
|
118
|
+
Handle downloading a complete series.
|
|
119
|
+
|
|
120
|
+
Parameters:
|
|
121
|
+
- select_season (MediaItem): Series metadata from search
|
|
122
|
+
- season_selection (str, optional): Pre-defined season selection that bypasses manual input
|
|
123
|
+
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
|
|
124
|
+
"""
|
|
125
|
+
start_message()
|
|
126
|
+
|
|
127
|
+
# Extract program name from path_id
|
|
128
|
+
program_name = None
|
|
129
|
+
if select_season.path_id:
|
|
130
|
+
parts = select_season.path_id.strip('/').split('/')
|
|
131
|
+
if len(parts) >= 2:
|
|
132
|
+
program_name = parts[-1].split('.')[0]
|
|
133
|
+
|
|
134
|
+
# Init scraper
|
|
135
|
+
scrape_serie = GetSerieInfo(program_name)
|
|
136
|
+
|
|
137
|
+
# Get seasons info
|
|
138
|
+
scrape_serie.collect_info_title()
|
|
139
|
+
seasons_count = len(scrape_serie.seasons_manager)
|
|
140
|
+
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
|
141
|
+
|
|
142
|
+
# If season_selection is provided, use it instead of asking for input
|
|
143
|
+
if season_selection is None:
|
|
144
|
+
index_season_selected = msg.ask(
|
|
145
|
+
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
|
146
|
+
"[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"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
else:
|
|
150
|
+
index_season_selected = season_selection
|
|
151
|
+
console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
|
|
152
|
+
|
|
153
|
+
# Validate the selection
|
|
154
|
+
list_season_select = manage_selection(index_season_selected, seasons_count)
|
|
155
|
+
list_season_select = validate_selection(list_season_select, seasons_count)
|
|
156
|
+
|
|
157
|
+
# Loop through the selected seasons and download episodes
|
|
158
|
+
for season_number in list_season_select:
|
|
159
|
+
if len(list_season_select) > 1 or index_season_selected == "*":
|
|
160
|
+
download_episode(season_number, scrape_serie, download_all=True)
|
|
161
|
+
else:
|
|
162
|
+
download_episode(season_number, scrape_serie, download_all=False, episode_selection=episode_selection)
|