Skip to content

Commit e47f786

Browse files
authored
Bump v3.4.6
* Fix issue #446 * Add dmax, realtime free version * Fix mpd parser * Bump v3.4.6
1 parent 8fd82bb commit e47f786

File tree

31 files changed

+1766
-140
lines changed

31 files changed

+1766
-140
lines changed

StreamingCommunity/Api/Site/altadefinizione/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def get_user_input(string_to_search: str = None):
7171
else:
7272
return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
7373

74+
7475
def process_search_result(select_title, selections=None):
7576
"""
7677
Handles the search result and initiates the download for either a film or series.
@@ -109,6 +110,7 @@ def process_search_result(select_title, selections=None):
109110
table_show_manager.clear()
110111
return True
111112

113+
112114
# search("Game of Thrones", selections={"season": "1", "episode": "1-3"})
113115
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
114116
"""

StreamingCommunity/Api/Site/animeunity/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def get_user_input(string_to_search: str = None):
7171
else:
7272
return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
7373

74+
7475
def process_search_result(select_title, selections=None):
7576
"""
7677
Handles the search result and initiates the download for either a film or series.
@@ -108,6 +109,7 @@ def process_search_result(select_title, selections=None):
108109
table_show_manager.clear()
109110
return True
110111

112+
111113
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
112114
"""
113115
Main function of the application for search.

StreamingCommunity/Api/Site/animeworld/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def get_user_input(string_to_search: str = None):
7171
else:
7272
return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
7373

74+
7475
def process_search_result(select_title, selections=None):
7576
"""
7677
Handles the search result and initiates the download for either a film or series.
@@ -106,6 +107,7 @@ def process_search_result(select_title, selections=None):
106107
table_show_manager.clear()
107108
return True
108109

110+
109111
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
110112
"""
111113
Main function of the application for search.

StreamingCommunity/Api/Site/crunchyroll/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def get_user_input(string_to_search: str = None):
7171
else:
7272
return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
7373

74+
7475
def process_search_result(select_title, selections=None):
7576
"""
7677
Handles the search result and initiates the download for either a film or series.
@@ -109,6 +110,7 @@ def process_search_result(select_title, selections=None):
109110
table_show_manager.clear()
110111
return True
111112

113+
112114
# search("Game of Thrones", selections={"season": "1", "episode": "1-3"})
113115
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
114116
"""
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# 26.11.2025
2+
3+
# External library
4+
from rich.console import Console
5+
from rich.prompt import Prompt
6+
7+
8+
# Internal utilities
9+
from StreamingCommunity.Api.Template import get_select_title
10+
from StreamingCommunity.Api.Template.config_loader import site_constant
11+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
12+
13+
14+
# Logic class
15+
from .site import title_search, table_show_manager, media_search_manager
16+
from .series import download_series
17+
18+
19+
# Variable
20+
indice = 10
21+
_useFor = "Serie"
22+
_priority = 0
23+
_engineDownload = "hls"
24+
_deprecate = False
25+
26+
msg = Prompt()
27+
console = Console()
28+
29+
30+
def get_user_input(string_to_search: str = None):
31+
"""
32+
Asks the user to input a search term.
33+
Handles both Telegram bot input and direct input.
34+
If string_to_search is provided, it's returned directly (after stripping).
35+
"""
36+
if string_to_search is not None:
37+
return string_to_search.strip()
38+
else:
39+
return msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
40+
41+
42+
def process_search_result(select_title, selections=None):
43+
"""
44+
Handles the search result and initiates the download for either a film or series.
45+
46+
Parameters:
47+
select_title (MediaItem): The selected media item. Can be None if selection fails.
48+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
49+
e.g., {'season': season_selection, 'episode': episode_selection}
50+
Returns:
51+
bool: True if processing was successful, False otherwise
52+
"""
53+
if not select_title:
54+
return False
55+
56+
if select_title.type == 'tv':
57+
season_selection = None
58+
episode_selection = None
59+
60+
if selections:
61+
season_selection = selections.get('season')
62+
episode_selection = selections.get('episode')
63+
64+
download_series(select_title, season_selection, episode_selection)
65+
media_search_manager.clear()
66+
table_show_manager.clear()
67+
return True
68+
69+
70+
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
71+
"""
72+
Main function of the application for search.
73+
74+
Parameters:
75+
string_to_search (str, optional): String to search for. Can be passed from run.py.
76+
If 'back', special handling might occur in get_user_input.
77+
get_onlyDatabase (bool, optional): If True, return only the database search manager object.
78+
direct_item (dict, optional): Direct item to process (bypasses search).
79+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
80+
for series (season/episode).
81+
"""
82+
if direct_item:
83+
select_title = MediaItem(**direct_item)
84+
result = process_search_result(select_title, selections)
85+
return result
86+
87+
# Get the user input for the search term
88+
actual_search_query = get_user_input(string_to_search)
89+
90+
# Handle empty input
91+
if not actual_search_query:
92+
return False
93+
94+
# Search on database
95+
len_database = title_search(actual_search_query)
96+
97+
# If only the database is needed, return the manager
98+
if get_onlyDatabase:
99+
return media_search_manager
100+
101+
if len_database > 0:
102+
select_title = get_select_title(table_show_manager, media_search_manager, len_database)
103+
result = process_search_result(select_title, selections)
104+
return result
105+
106+
else:
107+
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
108+
return False
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# 26.11.2025
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.Util.config_json import config_manager
15+
16+
17+
# Logic class
18+
from ..realtime.util.ScrapeSerie import GetSerieInfo
19+
from StreamingCommunity.Api.Template.Util import (
20+
manage_selection,
21+
map_episode_title,
22+
validate_selection,
23+
validate_episode_selection,
24+
display_episodes_list,
25+
display_seasons_list
26+
)
27+
from StreamingCommunity.Api.Template.config_loader import site_constant
28+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
29+
30+
31+
# Player
32+
from StreamingCommunity import HLS_Downloader
33+
from ..realtime.util.get_license import get_bearer_token, get_playback_url
34+
35+
36+
# Variable
37+
msg = Prompt()
38+
console = Console()
39+
extension_output = config_manager.get("M3U8_CONVERSION", "extension")
40+
41+
42+
def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: GetSerieInfo) -> Tuple[str,bool]:
43+
"""
44+
Downloads a specific episode from the specified season.
45+
46+
Parameters:
47+
- index_season_selected (int): Season number
48+
- index_episode_selected (int): Episode index
49+
- scrape_serie (GetSerieInfo): Scraper object with series information
50+
51+
Returns:
52+
- str: Path to downloaded file
53+
- bool: Whether download was stopped
54+
"""
55+
start_message()
56+
57+
# Get episode information
58+
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
59+
console.print(f"\n[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scrape_serie.series_name}[/cyan] \\ [bold magenta]{obj_episode.name}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
60+
61+
# Define filename and path for the downloaded video
62+
mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.{extension_output}"
63+
mp4_path = os.path.join(site_constant.SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}")
64+
65+
# Get m3u8 playlist
66+
bearer_token = get_bearer_token()
67+
master_playlist = get_playback_url(obj_episode.id, bearer_token)
68+
69+
# Download the episode
70+
hls_process = HLS_Downloader(
71+
m3u8_url=master_playlist,
72+
output_path=os.path.join(mp4_path, mp4_name)
73+
).start()
74+
75+
if hls_process['error'] is not None:
76+
try:
77+
os.remove(hls_process['path'])
78+
except Exception:
79+
pass
80+
81+
return hls_process['path'], hls_process['stopped']
82+
83+
84+
def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, download_all: bool = False, episode_selection: str = None) -> None:
85+
"""
86+
Handle downloading episodes for a specific season.
87+
88+
Parameters:
89+
- index_season_selected (int): Season number
90+
- scrape_serie (GetSerieInfo): Scraper object with series information
91+
- download_all (bool): Whether to download all episodes
92+
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
93+
"""
94+
# Get episodes for the selected season
95+
episodes = scrape_serie.getEpisodeSeasons(index_season_selected)
96+
episodes_count = len(episodes)
97+
98+
if episodes_count == 0:
99+
console.print(f"[red]No episodes found for season {index_season_selected}")
100+
return
101+
102+
if download_all:
103+
# Download all episodes in the season
104+
for i_episode in range(1, episodes_count + 1):
105+
path, stopped = download_video(index_season_selected, i_episode, scrape_serie)
106+
107+
if stopped:
108+
break
109+
110+
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
111+
112+
else:
113+
# Display episodes list and manage user selection
114+
if episode_selection is None:
115+
last_command = display_episodes_list(episodes)
116+
else:
117+
last_command = episode_selection
118+
console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
119+
120+
# Validate the selection
121+
list_episode_select = manage_selection(last_command, episodes_count)
122+
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
123+
124+
# Download selected episodes if not stopped
125+
for i_episode in list_episode_select:
126+
path, stopped = download_video(index_season_selected, i_episode, scrape_serie)
127+
128+
if stopped:
129+
break
130+
131+
132+
def download_series(select_season: MediaItem, season_selection: str = None, episode_selection: str = None) -> None:
133+
"""
134+
Handle downloading a complete series.
135+
136+
Parameters:
137+
- select_season (MediaItem): Series metadata from search
138+
- season_selection (str, optional): Pre-defined season selection that bypasses manual input
139+
- episode_selection (str, optional): Pre-defined episode selection that bypasses manual input
140+
"""
141+
start_message()
142+
143+
# Init class
144+
scrape_serie = GetSerieInfo(select_season.url)
145+
146+
# Collect information about season
147+
scrape_serie.getNumberSeason()
148+
seasons_count = len(scrape_serie.seasons_manager)
149+
150+
# If season_selection is provided, use it instead of asking for input
151+
if season_selection is None:
152+
index_season_selected = display_seasons_list(scrape_serie.seasons_manager)
153+
else:
154+
index_season_selected = season_selection
155+
console.print(f"\n[cyan]Using provided season selection: [yellow]{season_selection}")
156+
157+
# Validate the selection
158+
list_season_select = manage_selection(index_season_selected, seasons_count)
159+
list_season_select = validate_selection(list_season_select, seasons_count)
160+
161+
# Loop through the selected seasons and download episodes
162+
for i_season in list_season_select:
163+
try:
164+
season = scrape_serie.seasons_manager.seasons[i_season - 1]
165+
except IndexError:
166+
console.print(f"[red]Season index {i_season} not found! Available seasons: {[s.number for s in scrape_serie.seasons_manager.seasons]}")
167+
continue
168+
169+
season_number = season.number
170+
171+
if len(list_season_select) > 1 or index_season_selected == "*":
172+
download_episode(season_number, scrape_serie, download_all=True)
173+
else:
174+
download_episode(season_number, scrape_serie, download_all=False, episode_selection=episode_selection)

0 commit comments

Comments
 (0)