StreamingCommunity 2.9.8__py3-none-any.whl → 3.0.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/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 +2 -3
- StreamingCommunity/Api/Site/1337xx/site.py +6 -1
- StreamingCommunity/Api/Site/altadefinizione/__init__.py +24 -9
- StreamingCommunity/Api/Site/altadefinizione/film.py +0 -1
- StreamingCommunity/Api/Site/altadefinizione/series.py +66 -70
- StreamingCommunity/Api/Site/altadefinizione/site.py +8 -2
- StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +37 -2
- StreamingCommunity/Api/Site/animeunity/__init__.py +30 -12
- StreamingCommunity/Api/Site/animeunity/film.py +40 -0
- StreamingCommunity/Api/Site/animeunity/serie.py +153 -0
- StreamingCommunity/Api/Site/animeunity/site.py +64 -37
- StreamingCommunity/Api/Site/animeunity/util/ScrapeSerie.py +41 -22
- StreamingCommunity/Api/Site/animeworld/__init__.py +26 -14
- StreamingCommunity/Api/Site/animeworld/film.py +63 -0
- StreamingCommunity/Api/Site/animeworld/serie.py +25 -22
- StreamingCommunity/Api/Site/animeworld/site.py +8 -2
- StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +32 -5
- StreamingCommunity/Api/Site/cb01new/__init__.py +2 -3
- StreamingCommunity/Api/Site/cb01new/site.py +7 -1
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +2 -3
- StreamingCommunity/Api/Site/ddlstreamitaly/series.py +31 -32
- StreamingCommunity/Api/Site/ddlstreamitaly/site.py +8 -3
- StreamingCommunity/Api/Site/ddlstreamitaly/util/ScrapeSerie.py +30 -2
- StreamingCommunity/Api/Site/guardaserie/__init__.py +22 -9
- StreamingCommunity/Api/Site/guardaserie/series.py +55 -53
- StreamingCommunity/Api/Site/guardaserie/site.py +10 -3
- StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +27 -1
- StreamingCommunity/Api/Site/raiplay/__init__.py +92 -0
- StreamingCommunity/Api/Site/raiplay/film.py +65 -0
- StreamingCommunity/Api/Site/raiplay/series.py +162 -0
- StreamingCommunity/Api/Site/raiplay/site.py +173 -0
- StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +127 -0
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +30 -24
- StreamingCommunity/Api/Site/streamingcommunity/film.py +1 -2
- StreamingCommunity/Api/Site/streamingcommunity/series.py +76 -90
- StreamingCommunity/Api/Site/streamingcommunity/site.py +8 -4
- 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 +9 -18
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +2 -1
- StreamingCommunity/Lib/Downloader/TOR/downloader.py +7 -14
- StreamingCommunity/Lib/FFmpeg/capture.py +1 -5
- StreamingCommunity/Lib/FFmpeg/util.py +57 -19
- StreamingCommunity/Lib/M3U8/estimator.py +57 -41
- StreamingCommunity/Lib/M3U8/parser.py +26 -6
- StreamingCommunity/Upload/update.py +22 -3
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/ffmpeg_installer.py +26 -1
- StreamingCommunity/Util/os.py +13 -15
- StreamingCommunity/Util/table.py +4 -2
- StreamingCommunity/global_search.py +1 -4
- StreamingCommunity/run.py +1 -4
- {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/METADATA +1 -1
- streamingcommunity-3.0.0.dist-info/RECORD +91 -0
- {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.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.8.dist-info/RECORD +0 -85
- {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-2.9.8.dist-info → streamingcommunity-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -19,12 +19,12 @@ from StreamingCommunity.Lib.Downloader import MP4_downloader
|
|
|
19
19
|
# Logic class
|
|
20
20
|
from .util.ScrapeSerie import ScrapSerie
|
|
21
21
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
22
|
-
from StreamingCommunity.Api.Template.Util import manage_selection, dynamic_format_number
|
|
22
|
+
from StreamingCommunity.Api.Template.Util import manage_selection, dynamic_format_number
|
|
23
23
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
# Player
|
|
27
|
-
from StreamingCommunity.Api.Player.sweetpixel import
|
|
27
|
+
from StreamingCommunity.Api.Player.sweetpixel import VideoSource
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
# Variable
|
|
@@ -33,8 +33,7 @@ msg = Prompt()
|
|
|
33
33
|
KILL_HANDLER = bool(False)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
def download_episode(index_select: int, scrape_serie: ScrapSerie, episodes) -> Tuple[str,bool]:
|
|
36
|
+
def download_episode(index_select: int, scrape_serie: ScrapSerie) -> Tuple[str,bool]:
|
|
38
37
|
"""
|
|
39
38
|
Downloads the selected episode.
|
|
40
39
|
|
|
@@ -47,7 +46,8 @@ def download_episode(index_select: int, scrape_serie: ScrapSerie, episodes) -> T
|
|
|
47
46
|
"""
|
|
48
47
|
start_message()
|
|
49
48
|
|
|
50
|
-
# Get information
|
|
49
|
+
# Get episode information
|
|
50
|
+
episode_data = scrape_serie.selectEpisode(1, index_select)
|
|
51
51
|
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] ([cyan]E{index_select+1}[/cyan]) \n")
|
|
52
52
|
|
|
53
53
|
# Define filename and path for the downloaded video
|
|
@@ -57,9 +57,9 @@ def download_episode(index_select: int, scrape_serie: ScrapSerie, episodes) -> T
|
|
|
57
57
|
# Create output folder
|
|
58
58
|
os_manager.create_path(mp4_path)
|
|
59
59
|
|
|
60
|
-
#
|
|
61
|
-
video_source =
|
|
62
|
-
mp4_link = video_source.
|
|
60
|
+
# Get video source for the episode
|
|
61
|
+
video_source = VideoSource(site_constant.FULL_URL, episode_data, scrape_serie.session_id, scrape_serie.csrf_token)
|
|
62
|
+
mp4_link = video_source.get_playlist()
|
|
63
63
|
|
|
64
64
|
# Start downloading
|
|
65
65
|
path, kill_handler = MP4_downloader(
|
|
@@ -70,38 +70,41 @@ def download_episode(index_select: int, scrape_serie: ScrapSerie, episodes) -> T
|
|
|
70
70
|
return path, kill_handler
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def download_series(select_title: MediaItem):
|
|
73
|
+
def download_series(select_title: MediaItem, episode_selection: str = None):
|
|
74
74
|
"""
|
|
75
75
|
Function to download episodes of a TV series.
|
|
76
76
|
|
|
77
77
|
Parameters:
|
|
78
|
-
-
|
|
79
|
-
-
|
|
78
|
+
- select_title (MediaItem): The selected media item
|
|
79
|
+
- episode_selection (str, optional): Episode selection input that bypasses manual input
|
|
80
80
|
"""
|
|
81
81
|
start_message()
|
|
82
82
|
|
|
83
|
+
# Create scrap instance
|
|
83
84
|
scrape_serie = ScrapSerie(select_title.url, site_constant.FULL_URL)
|
|
85
|
+
episodes = scrape_serie.get_episodes()
|
|
84
86
|
|
|
85
|
-
# Get
|
|
86
|
-
|
|
87
|
-
episoded_count = len(episodes)
|
|
88
|
-
console.print(f"[cyan]Episodes find: [red]{episoded_count}")
|
|
87
|
+
# Get episode count
|
|
88
|
+
console.print(f"[green]Episodes found:[/green] [red]{len(episodes)}[/red]")
|
|
89
89
|
|
|
90
|
-
#
|
|
91
|
-
|
|
90
|
+
# Display episodes list and get user selection
|
|
91
|
+
if episode_selection is None:
|
|
92
|
+
last_command = msg.ask("\n[cyan]Insert media [red]index [yellow]or [red]* [cyan]to download all media [yellow]or [red]1-2 [cyan]or [red]3-* [cyan]for a range of media")
|
|
93
|
+
else:
|
|
94
|
+
last_command = episode_selection
|
|
95
|
+
console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
list_episode_select = manage_selection(last_command, episoded_count)
|
|
97
|
+
list_episode_select = manage_selection(last_command, len(episodes))
|
|
95
98
|
|
|
96
99
|
# Download selected episodes
|
|
97
100
|
if len(list_episode_select) == 1 and last_command != "*":
|
|
98
|
-
path, _ = download_episode(list_episode_select[0]-1, scrape_serie
|
|
101
|
+
path, _ = download_episode(list_episode_select[0]-1, scrape_serie)
|
|
99
102
|
return path
|
|
100
103
|
|
|
101
|
-
# Download all
|
|
104
|
+
# Download all selected episodes
|
|
102
105
|
else:
|
|
103
106
|
kill_handler = False
|
|
104
107
|
for i_episode in list_episode_select:
|
|
105
108
|
if kill_handler:
|
|
106
109
|
break
|
|
107
|
-
_, kill_handler = download_episode(i_episode-1, scrape_serie
|
|
110
|
+
_, kill_handler = download_episode(i_episode-1, scrape_serie)
|
|
@@ -70,7 +70,12 @@ def title_search(query: str) -> int:
|
|
|
70
70
|
|
|
71
71
|
# Make the GET request
|
|
72
72
|
try:
|
|
73
|
-
response = httpx.get(
|
|
73
|
+
response = httpx.get(
|
|
74
|
+
search_url,
|
|
75
|
+
headers={'User-Agent': get_userAgent()},
|
|
76
|
+
timeout=max_timeout,
|
|
77
|
+
verify=False
|
|
78
|
+
)
|
|
74
79
|
|
|
75
80
|
except Exception as e:
|
|
76
81
|
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
@@ -101,7 +106,8 @@ def title_search(query: str) -> int:
|
|
|
101
106
|
'name': title,
|
|
102
107
|
'type': anime_type,
|
|
103
108
|
'DUB': is_dubbed,
|
|
104
|
-
'url': url
|
|
109
|
+
'url': url,
|
|
110
|
+
'image': element.find('img').get('src')
|
|
105
111
|
})
|
|
106
112
|
|
|
107
113
|
except Exception as e:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# 21.03.25
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
|
|
4
5
|
# External libraries
|
|
5
6
|
import httpx
|
|
@@ -14,7 +15,7 @@ from StreamingCommunity.Util.os import os_manager
|
|
|
14
15
|
|
|
15
16
|
# Player
|
|
16
17
|
from ..site import get_session_and_csrf
|
|
17
|
-
from StreamingCommunity.Api.Player.sweetpixel import
|
|
18
|
+
from StreamingCommunity.Api.Player.sweetpixel import VideoSource
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
# Variable
|
|
@@ -40,7 +41,6 @@ class ScrapSerie:
|
|
|
40
41
|
except:
|
|
41
42
|
raise Exception(f"Failed to retrieve anime page.")
|
|
42
43
|
|
|
43
|
-
|
|
44
44
|
def get_name(self):
|
|
45
45
|
"""Extract and return the name of the anime series."""
|
|
46
46
|
soup = BeautifulSoup(self.response.content, "html.parser")
|
|
@@ -68,12 +68,39 @@ class ScrapSerie:
|
|
|
68
68
|
return episodes
|
|
69
69
|
|
|
70
70
|
def get_episode(self, index):
|
|
71
|
-
"""Fetch a specific episode based on the index, and return an
|
|
71
|
+
"""Fetch a specific episode based on the index, and return an VideoSource instance."""
|
|
72
72
|
episodes = self.get_episodes()
|
|
73
73
|
|
|
74
74
|
if 0 <= index < len(episodes):
|
|
75
75
|
episode_data = episodes[index]
|
|
76
|
-
return
|
|
76
|
+
return VideoSource(episode_data, self.session_id, self.csrf_token)
|
|
77
77
|
|
|
78
78
|
else:
|
|
79
|
-
raise IndexError("Episode index out of range")
|
|
79
|
+
raise IndexError("Episode index out of range")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ------------- FOR GUI -------------
|
|
83
|
+
def getNumberSeason(self) -> int:
|
|
84
|
+
"""
|
|
85
|
+
Get the total number of seasons available for the anime.
|
|
86
|
+
Note: AnimeWorld typically doesn't have seasons, so returns 1.
|
|
87
|
+
"""
|
|
88
|
+
return 1
|
|
89
|
+
|
|
90
|
+
def getEpisodeSeasons(self, season_number: int = 1) -> list:
|
|
91
|
+
"""
|
|
92
|
+
Get all episodes for a specific season.
|
|
93
|
+
Note: For AnimeWorld, this returns all episodes as they're typically in one season.
|
|
94
|
+
"""
|
|
95
|
+
return self.get_episodes()
|
|
96
|
+
|
|
97
|
+
def selectEpisode(self, season_number: int = 1, episode_index: int = 0) -> dict:
|
|
98
|
+
"""
|
|
99
|
+
Get information for a specific episode.
|
|
100
|
+
"""
|
|
101
|
+
episodes = self.get_episodes()
|
|
102
|
+
if not episodes or episode_index < 0 or episode_index >= len(episodes):
|
|
103
|
+
logging.error(f"Episode index {episode_index} is out of range")
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
return episodes[episode_index]
|
|
@@ -22,8 +22,7 @@ from .film import download_film
|
|
|
22
22
|
# Variable
|
|
23
23
|
indice = 4
|
|
24
24
|
_useFor = "film"
|
|
25
|
-
|
|
26
|
-
_priority = 2
|
|
25
|
+
_priority = 0
|
|
27
26
|
_engineDownload = "mp4"
|
|
28
27
|
|
|
29
28
|
msg = Prompt()
|
|
@@ -39,7 +38,7 @@ def process_search_result(select_title):
|
|
|
39
38
|
|
|
40
39
|
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
|
41
40
|
"""
|
|
42
|
-
Main function of the application for search
|
|
41
|
+
Main function of the application for search.
|
|
43
42
|
|
|
44
43
|
Parameters:
|
|
45
44
|
string_to_search (str, optional): String to search for
|
|
@@ -44,7 +44,13 @@ def title_search(query: str) -> int:
|
|
|
44
44
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
45
45
|
|
|
46
46
|
try:
|
|
47
|
-
response = httpx.get(
|
|
47
|
+
response = httpx.get(
|
|
48
|
+
search_url,
|
|
49
|
+
headers={'user-agent': get_userAgent()},
|
|
50
|
+
timeout=max_timeout,
|
|
51
|
+
follow_redirects=True,
|
|
52
|
+
verify=False
|
|
53
|
+
)
|
|
48
54
|
response.raise_for_status()
|
|
49
55
|
|
|
50
56
|
except Exception as e:
|
|
@@ -23,8 +23,7 @@ from .series import download_thread
|
|
|
23
23
|
# Variable
|
|
24
24
|
indice = 6
|
|
25
25
|
_useFor = "serie"
|
|
26
|
-
|
|
27
|
-
_priority = 2
|
|
26
|
+
_priority = 0
|
|
28
27
|
_engineDownload = "mp4"
|
|
29
28
|
|
|
30
29
|
msg = Prompt()
|
|
@@ -42,7 +41,7 @@ def process_search_result(select_title):
|
|
|
42
41
|
|
|
43
42
|
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
|
44
43
|
"""
|
|
45
|
-
Main function of the application for search
|
|
44
|
+
Main function of the application for search.
|
|
46
45
|
|
|
47
46
|
Parameters:
|
|
48
47
|
string_to_search (str, optional): String to search for
|
|
@@ -35,22 +35,22 @@ from StreamingCommunity.Api.Player.ddl import VideoSource
|
|
|
35
35
|
console = Console()
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo
|
|
38
|
+
def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo) -> Tuple[str,bool]:
|
|
39
39
|
"""
|
|
40
|
-
|
|
40
|
+
Downloads a specific episode.
|
|
41
41
|
|
|
42
42
|
Parameters:
|
|
43
|
-
-
|
|
44
|
-
-
|
|
43
|
+
- index_episode_selected (int): Episode index
|
|
44
|
+
- scape_info_serie (GetSerieInfo): Scraper object with series information
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
- str:
|
|
48
|
-
- bool:
|
|
46
|
+
Returns:
|
|
47
|
+
- str: Path to downloaded file
|
|
48
|
+
- bool: Whether download was stopped
|
|
49
49
|
"""
|
|
50
50
|
start_message()
|
|
51
51
|
|
|
52
|
-
# Get
|
|
53
|
-
obj_episode = scape_info_serie.
|
|
52
|
+
# Get episode information
|
|
53
|
+
obj_episode = scape_info_serie.selectEpisode(1, index_episode_selected-1)
|
|
54
54
|
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [bold magenta]{obj_episode.get('name')}[/bold magenta] ([cyan]E{index_episode_selected}[/cyan]) \n")
|
|
55
55
|
|
|
56
56
|
# Define filename and path for the downloaded video
|
|
@@ -63,7 +63,7 @@ def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo,
|
|
|
63
63
|
os_manager.create_path(mp4_path)
|
|
64
64
|
|
|
65
65
|
# Setup video source
|
|
66
|
-
video_source.
|
|
66
|
+
video_source = VideoSource(site_constant.COOKIE, obj_episode.get('url'))
|
|
67
67
|
|
|
68
68
|
# Get m3u8 master playlist
|
|
69
69
|
master_playlist = video_source.get_playlist()
|
|
@@ -82,38 +82,37 @@ def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo,
|
|
|
82
82
|
console.print("[green]Result: ")
|
|
83
83
|
console.print(r_proc)
|
|
84
84
|
|
|
85
|
-
return os.path.join(mp4_path, title_name)
|
|
85
|
+
return os.path.join(mp4_path, title_name), False
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def download_thread(dict_serie: MediaItem):
|
|
88
|
+
def download_thread(dict_serie: MediaItem, episode_selection: str = None):
|
|
89
89
|
"""
|
|
90
90
|
Download all episode of a thread
|
|
91
|
+
|
|
92
|
+
Parameters:
|
|
93
|
+
dict_serie (MediaItem): The selected media item
|
|
94
|
+
episode_selection (str, optional): Episode selection input that bypasses manual input
|
|
91
95
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# Collect information about thread
|
|
99
|
-
list_dict_episode = scape_info_serie.get_episode_number()
|
|
100
|
-
episodes_count = len(list_dict_episode)
|
|
101
|
-
|
|
96
|
+
scrape_serie = GetSerieInfo(dict_serie, site_constant.COOKIE)
|
|
97
|
+
|
|
98
|
+
# Get episode list
|
|
99
|
+
episodes = scrape_serie.getEpisodeSeasons()
|
|
100
|
+
episodes_count = len(episodes)
|
|
101
|
+
|
|
102
102
|
# Display episodes list and manage user selection
|
|
103
|
-
|
|
103
|
+
if episode_selection is None:
|
|
104
|
+
last_command = display_episodes_list(scrape_serie.list_episodes)
|
|
105
|
+
else:
|
|
106
|
+
last_command = episode_selection
|
|
107
|
+
console.print(f"\n[cyan]Using provided episode selection: [yellow]{episode_selection}")
|
|
108
|
+
|
|
109
|
+
# Validate episode selection
|
|
104
110
|
list_episode_select = manage_selection(last_command, episodes_count)
|
|
105
|
-
|
|
106
|
-
try:
|
|
107
|
-
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
|
108
|
-
|
|
109
|
-
except ValueError as e:
|
|
110
|
-
console.print(f"[red]{str(e)}")
|
|
111
|
-
return
|
|
111
|
+
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
|
112
112
|
|
|
113
113
|
# Download selected episodes
|
|
114
114
|
kill_handler = bool(False)
|
|
115
115
|
for i_episode in list_episode_select:
|
|
116
116
|
if kill_handler:
|
|
117
117
|
break
|
|
118
|
-
|
|
119
|
-
kill_handler = download_video(i_episode, scape_info_serie, video_source)[1]
|
|
118
|
+
kill_handler = download_video(i_episode, scrape_serie)[1]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# 09.06.24
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
3
|
import logging
|
|
5
4
|
|
|
6
5
|
|
|
@@ -45,7 +44,12 @@ def title_search(query: str) -> int:
|
|
|
45
44
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
46
45
|
|
|
47
46
|
try:
|
|
48
|
-
response = httpx.get(
|
|
47
|
+
response = httpx.get(
|
|
48
|
+
search_url,
|
|
49
|
+
headers={'user-agent': get_userAgent()},
|
|
50
|
+
timeout=max_timeout,
|
|
51
|
+
follow_redirects=True
|
|
52
|
+
)
|
|
49
53
|
response.raise_for_status()
|
|
50
54
|
|
|
51
55
|
except Exception as e:
|
|
@@ -67,7 +71,8 @@ def title_search(query: str) -> int:
|
|
|
67
71
|
title_info = {
|
|
68
72
|
'name': name,
|
|
69
73
|
'url': link,
|
|
70
|
-
'type': title_type
|
|
74
|
+
'type': title_type,
|
|
75
|
+
'image': title_div.find("div", class_="ipsColumn").find("img").get("src")
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
media_search_manager.add_media(title_info)
|
|
@@ -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]
|
|
@@ -22,32 +22,45 @@ from .series import download_series
|
|
|
22
22
|
# Variable
|
|
23
23
|
indice = 5
|
|
24
24
|
_useFor = "serie"
|
|
25
|
-
|
|
26
|
-
_priority = 2
|
|
25
|
+
_priority = 0
|
|
27
26
|
_engineDownload = "hls"
|
|
28
27
|
|
|
29
28
|
msg = Prompt()
|
|
30
29
|
console = Console()
|
|
31
30
|
|
|
32
31
|
|
|
33
|
-
def process_search_result(select_title):
|
|
32
|
+
def process_search_result(select_title, selections=None):
|
|
34
33
|
"""
|
|
35
34
|
Handles the search result and initiates the download for either a film or series.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
select_title (MediaItem): The selected media item
|
|
38
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
39
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
36
40
|
"""
|
|
37
|
-
|
|
41
|
+
season_selection = None
|
|
42
|
+
episode_selection = None
|
|
43
|
+
|
|
44
|
+
if selections:
|
|
45
|
+
season_selection = selections.get('season')
|
|
46
|
+
episode_selection = selections.get('episode')
|
|
47
|
+
|
|
48
|
+
download_series(select_title, season_selection, episode_selection)
|
|
38
49
|
|
|
39
|
-
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
|
50
|
+
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None, selections: dict = None):
|
|
40
51
|
"""
|
|
41
|
-
Main function of the application for search
|
|
52
|
+
Main function of the application for search.
|
|
42
53
|
|
|
43
54
|
Parameters:
|
|
44
55
|
string_to_search (str, optional): String to search for
|
|
45
|
-
|
|
56
|
+
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
46
57
|
direct_item (dict, optional): Direct item to process (bypass search)
|
|
58
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
59
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
47
60
|
"""
|
|
48
61
|
if direct_item:
|
|
49
62
|
select_title = MediaItem(**direct_item)
|
|
50
|
-
process_search_result(select_title)
|
|
63
|
+
process_search_result(select_title, selections)
|
|
51
64
|
return
|
|
52
65
|
|
|
53
66
|
if string_to_search is None:
|
|
@@ -62,7 +75,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
62
75
|
|
|
63
76
|
if len_database > 0:
|
|
64
77
|
select_title = get_select_title(table_show_manager, media_search_manager)
|
|
65
|
-
process_search_result(select_title)
|
|
78
|
+
process_search_result(select_title, selections)
|
|
66
79
|
|
|
67
80
|
else:
|
|
68
81
|
|
|
@@ -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)
|