StreamingCommunity 3.2.8__py3-none-any.whl → 3.3.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/Helper/Vixcloud/util.py +2 -1
- StreamingCommunity/Api/Player/hdplayer.py +2 -2
- StreamingCommunity/Api/Player/sweetpixel.py +5 -8
- StreamingCommunity/Api/Site/altadefinizione/__init__.py +32 -15
- StreamingCommunity/Api/Site/altadefinizione/film.py +10 -8
- StreamingCommunity/Api/Site/altadefinizione/series.py +9 -7
- StreamingCommunity/Api/Site/altadefinizione/site.py +1 -1
- StreamingCommunity/Api/Site/animeunity/__init__.py +31 -15
- StreamingCommunity/Api/Site/animeunity/serie.py +2 -2
- StreamingCommunity/Api/Site/animeworld/__init__.py +33 -7
- StreamingCommunity/Api/Site/animeworld/site.py +3 -5
- StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +8 -10
- StreamingCommunity/Api/Site/crunchyroll/__init__.py +44 -12
- StreamingCommunity/Api/Site/crunchyroll/film.py +9 -7
- StreamingCommunity/Api/Site/crunchyroll/series.py +9 -7
- StreamingCommunity/Api/Site/crunchyroll/site.py +16 -1
- StreamingCommunity/Api/Site/guardaserie/__init__.py +36 -10
- StreamingCommunity/Api/Site/guardaserie/series.py +8 -6
- StreamingCommunity/Api/Site/guardaserie/site.py +0 -3
- StreamingCommunity/Api/Site/guardaserie/util/ScrapeSerie.py +1 -2
- StreamingCommunity/Api/Site/mediasetinfinity/__init__.py +37 -12
- StreamingCommunity/Api/Site/mediasetinfinity/film.py +10 -16
- StreamingCommunity/Api/Site/mediasetinfinity/series.py +12 -18
- StreamingCommunity/Api/Site/mediasetinfinity/site.py +18 -3
- StreamingCommunity/Api/Site/mediasetinfinity/util/ScrapeSerie.py +214 -180
- StreamingCommunity/Api/Site/mediasetinfinity/util/get_license.py +2 -31
- StreamingCommunity/Api/Site/raiplay/__init__.py +47 -12
- StreamingCommunity/Api/Site/raiplay/film.py +42 -10
- StreamingCommunity/Api/Site/raiplay/series.py +53 -11
- StreamingCommunity/Api/Site/raiplay/site.py +4 -1
- StreamingCommunity/Api/Site/raiplay/util/ScrapeSerie.py +2 -1
- StreamingCommunity/Api/Site/raiplay/util/get_license.py +40 -0
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +5 -8
- StreamingCommunity/Api/Site/streamingcommunity/film.py +7 -5
- StreamingCommunity/Api/Site/streamingcommunity/series.py +9 -7
- StreamingCommunity/Api/Site/streamingcommunity/site.py +8 -3
- StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py +5 -2
- StreamingCommunity/Api/Site/streamingwatch/__init__.py +43 -9
- StreamingCommunity/Api/Site/streamingwatch/film.py +7 -5
- StreamingCommunity/Api/Site/streamingwatch/series.py +8 -6
- StreamingCommunity/Api/Site/streamingwatch/site.py +3 -1
- StreamingCommunity/Api/Site/streamingwatch/util/ScrapeSerie.py +3 -3
- StreamingCommunity/Api/Template/Util/__init__.py +10 -1
- StreamingCommunity/Api/Template/Util/manage_ep.py +4 -4
- StreamingCommunity/Api/Template/__init__.py +5 -1
- StreamingCommunity/Api/Template/site.py +10 -6
- StreamingCommunity/Lib/Downloader/DASH/cdm_helpher.py +13 -12
- StreamingCommunity/Lib/Downloader/DASH/decrypt.py +1 -1
- StreamingCommunity/Lib/Downloader/DASH/downloader.py +24 -22
- StreamingCommunity/Lib/Downloader/DASH/parser.py +1 -1
- StreamingCommunity/Lib/Downloader/DASH/segments.py +4 -3
- StreamingCommunity/Lib/Downloader/HLS/downloader.py +11 -9
- StreamingCommunity/Lib/Downloader/HLS/segments.py +4 -9
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +25 -6
- StreamingCommunity/Lib/Downloader/TOR/downloader.py +3 -5
- StreamingCommunity/Lib/Downloader/__init__.py +9 -1
- StreamingCommunity/Lib/FFmpeg/__init__.py +10 -1
- StreamingCommunity/Lib/FFmpeg/command.py +4 -6
- StreamingCommunity/Lib/FFmpeg/util.py +1 -1
- StreamingCommunity/Lib/M3U8/__init__.py +9 -1
- StreamingCommunity/Lib/M3U8/decryptor.py +8 -4
- StreamingCommunity/Lib/M3U8/estimator.py +0 -6
- StreamingCommunity/Lib/M3U8/parser.py +1 -1
- StreamingCommunity/Lib/M3U8/url_fixer.py +1 -1
- StreamingCommunity/Lib/TMBD/__init__.py +6 -1
- StreamingCommunity/TelegramHelp/config.json +1 -5
- StreamingCommunity/TelegramHelp/telegram_bot.py +9 -10
- StreamingCommunity/Upload/update.py +2 -2
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/config_json.py +139 -59
- StreamingCommunity/Util/http_client.py +201 -0
- StreamingCommunity/Util/message.py +1 -1
- StreamingCommunity/Util/os.py +8 -5
- StreamingCommunity/Util/table.py +3 -3
- StreamingCommunity/__init__.py +9 -1
- StreamingCommunity/run.py +394 -258
- {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/METADATA +147 -45
- streamingcommunity-3.3.0.dist-info/RECORD +110 -0
- StreamingCommunity/Api/Site/cb01new/__init__.py +0 -72
- StreamingCommunity/Api/Site/cb01new/film.py +0 -62
- StreamingCommunity/Api/Site/cb01new/site.py +0 -78
- streamingcommunity-3.2.8.dist-info/RECORD +0 -111
- {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/WHEEL +0 -0
- {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.2.8.dist-info → streamingcommunity-3.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# 21.05.24
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import sys
|
|
4
5
|
from typing import Tuple
|
|
5
6
|
|
|
6
7
|
|
|
@@ -10,11 +11,15 @@ from rich.prompt import Prompt
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
# Internal utilities
|
|
14
|
+
from StreamingCommunity.Util.headers import get_headers
|
|
15
|
+
from StreamingCommunity.Util.os import get_wvd_path
|
|
13
16
|
from StreamingCommunity.Util.message import start_message
|
|
14
17
|
|
|
15
18
|
|
|
19
|
+
|
|
16
20
|
# Logic class
|
|
17
21
|
from .util.ScrapeSerie import GetSerieInfo
|
|
22
|
+
from .util.get_license import generate_license_url
|
|
18
23
|
from StreamingCommunity.Api.Template.Util import (
|
|
19
24
|
manage_selection,
|
|
20
25
|
map_episode_title,
|
|
@@ -27,7 +32,7 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
|
27
32
|
|
|
28
33
|
|
|
29
34
|
# Player
|
|
30
|
-
from StreamingCommunity import HLS_Downloader
|
|
35
|
+
from StreamingCommunity import HLS_Downloader, DASH_Downloader
|
|
31
36
|
from StreamingCommunity.Api.Player.mediapolisvod import VideoSource
|
|
32
37
|
|
|
33
38
|
|
|
@@ -55,22 +60,59 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
55
60
|
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
56
61
|
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")
|
|
57
62
|
|
|
58
|
-
# Get streaming URL
|
|
59
|
-
master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
|
|
60
|
-
|
|
61
63
|
# Define filename and path
|
|
62
64
|
mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
|
|
63
65
|
mp4_path = os.path.join(site_constant.SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}")
|
|
64
66
|
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
# Get streaming URL
|
|
68
|
+
master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
|
|
69
|
+
|
|
70
|
+
# HLS
|
|
71
|
+
if ".mpd" not in master_playlist:
|
|
72
|
+
r_proc = HLS_Downloader(
|
|
73
|
+
m3u8_url=master_playlist,
|
|
74
|
+
output_path=os.path.join(mp4_path, mp4_name)
|
|
75
|
+
).start()
|
|
76
|
+
# Get streaming URL
|
|
77
|
+
master_playlist = VideoSource.extract_m3u8_url(obj_episode.url)
|
|
78
|
+
|
|
79
|
+
# HLS
|
|
80
|
+
if ".mpd" not in master_playlist:
|
|
81
|
+
r_proc = HLS_Downloader(
|
|
82
|
+
m3u8_url=master_playlist,
|
|
83
|
+
output_path=os.path.join(mp4_path, mp4_name)
|
|
84
|
+
).start()
|
|
85
|
+
|
|
86
|
+
# MPD
|
|
87
|
+
else:
|
|
88
|
+
|
|
89
|
+
# Check CDM file before usage
|
|
90
|
+
cdm_device_path = get_wvd_path()
|
|
91
|
+
if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
|
|
92
|
+
console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
|
|
93
|
+
sys.exit(0)
|
|
94
|
+
|
|
95
|
+
license_url = generate_license_url(obj_episode.mpd_id)
|
|
96
|
+
|
|
97
|
+
dash_process = DASH_Downloader(
|
|
98
|
+
cdm_device=cdm_device_path,
|
|
99
|
+
license_url=license_url,
|
|
100
|
+
mpd_url=master_playlist,
|
|
101
|
+
output_path=os.path.join(mp4_path, mp4_name),
|
|
102
|
+
)
|
|
103
|
+
dash_process.parse_manifest(custom_headers=get_headers())
|
|
104
|
+
|
|
105
|
+
if dash_process.download_and_decrypt():
|
|
106
|
+
dash_process.finalize_output()
|
|
107
|
+
|
|
108
|
+
# Get final output path and status
|
|
109
|
+
r_proc = dash_process.get_status()
|
|
70
110
|
|
|
71
111
|
if r_proc['error'] is not None:
|
|
72
|
-
try:
|
|
73
|
-
|
|
112
|
+
try:
|
|
113
|
+
os.remove(r_proc['path'])
|
|
114
|
+
except Exception:
|
|
115
|
+
pass
|
|
74
116
|
|
|
75
117
|
return r_proc['path'], r_proc['stopped']
|
|
76
118
|
|
|
@@ -11,6 +11,9 @@ from StreamingCommunity.Util.headers import get_userAgent
|
|
|
11
11
|
from StreamingCommunity.Util.table import TVShowManager
|
|
12
12
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
13
13
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Logic Import
|
|
14
17
|
from .util.ScrapeSerie import GetSerieInfo
|
|
15
18
|
|
|
16
19
|
|
|
@@ -59,7 +62,7 @@ def title_search(query: str) -> int:
|
|
|
59
62
|
media_search_manager.clear()
|
|
60
63
|
table_show_manager.clear()
|
|
61
64
|
|
|
62
|
-
search_url =
|
|
65
|
+
search_url = "https://www.raiplay.it/atomatic/raiplay-search-service/api/v1/msearch"
|
|
63
66
|
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
64
67
|
|
|
65
68
|
json_data = {
|
|
@@ -101,7 +101,8 @@ class GetSerieInfo:
|
|
|
101
101
|
'number': ep.get('episode', ''),
|
|
102
102
|
'name': ep.get('episode_title', '') or ep.get('toptitle', ''),
|
|
103
103
|
'duration': ep.get('duration', ''),
|
|
104
|
-
'url': f"{self.base_url}{ep.get('weblink', '')}" if 'weblink' in ep else f"{self.base_url}{ep.get('url', '')}"
|
|
104
|
+
'url': f"{self.base_url}{ep.get('weblink', '')}" if 'weblink' in ep else f"{self.base_url}{ep.get('url', '')}",
|
|
105
|
+
'mpd_id': ep.get('video_url').split("=")[1].strip()
|
|
105
106
|
}
|
|
106
107
|
season.episodes.add(episode)
|
|
107
108
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 16.03.25
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# External library
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Internal utilities
|
|
9
|
+
from StreamingCommunity.Util.config_json import config_manager
|
|
10
|
+
from StreamingCommunity.Util.headers import get_headers
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Variable
|
|
14
|
+
MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_license_url(mpd_id: str):
|
|
19
|
+
"""
|
|
20
|
+
Generates the URL to obtain the Widevine license.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
mpd_id (str): The ID of the MPD (Media Presentation Description) file.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
str: The full license URL.
|
|
27
|
+
"""
|
|
28
|
+
params = {
|
|
29
|
+
'cont': mpd_id,
|
|
30
|
+
'output': '62',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
response = httpx.get('https://mediapolisvod.rai.it/relinker/relinkerServlet.htm', params=params, headers=get_headers(), timeout=MAX_TIMEOUT)
|
|
34
|
+
response.raise_for_status()
|
|
35
|
+
|
|
36
|
+
# Extract the license URL from the response in two lines
|
|
37
|
+
json_data = response.json()
|
|
38
|
+
license_url = json_data.get('licence_server_map').get('drmLicenseUrlValues')[0].get('licenceUrl')
|
|
39
|
+
|
|
40
|
+
return license_url
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
4
|
import subprocess
|
|
5
|
-
from urllib.parse import quote_plus
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
# External library
|
|
@@ -16,7 +15,6 @@ from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
|
16
15
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
17
16
|
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
|
|
18
17
|
|
|
19
|
-
|
|
20
18
|
# Logic class
|
|
21
19
|
from .site import title_search, table_show_manager, media_search_manager
|
|
22
20
|
from .film import download_film
|
|
@@ -25,7 +23,7 @@ from .series import download_series
|
|
|
25
23
|
|
|
26
24
|
# Variable
|
|
27
25
|
indice = 0
|
|
28
|
-
_useFor = "Film_&_Serie"
|
|
26
|
+
_useFor = "Film_&_Serie"
|
|
29
27
|
_priority = 0
|
|
30
28
|
_engineDownload = "hls"
|
|
31
29
|
_deprecate = False
|
|
@@ -126,9 +124,9 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
126
124
|
actual_search_query = get_user_input(string_to_search)
|
|
127
125
|
|
|
128
126
|
# Handle cases where user input is empty, or 'back' was handled (sys.exit or None return)
|
|
129
|
-
if not actual_search_query:
|
|
127
|
+
if not actual_search_query:
|
|
130
128
|
if bot:
|
|
131
|
-
|
|
129
|
+
if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
|
|
132
130
|
bot.send_message("Search term not provided or operation cancelled. Returning.", None)
|
|
133
131
|
return
|
|
134
132
|
|
|
@@ -137,16 +135,15 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
137
135
|
|
|
138
136
|
# If only the database object (media_search_manager populated by title_search) is needed
|
|
139
137
|
if get_onlyDatabase:
|
|
140
|
-
return media_search_manager
|
|
138
|
+
return media_search_manager
|
|
141
139
|
|
|
142
140
|
if len_database > 0:
|
|
143
141
|
select_title = get_select_title(table_show_manager, media_search_manager, len_database)
|
|
144
142
|
process_search_result(select_title, selections)
|
|
145
143
|
|
|
146
144
|
else:
|
|
147
|
-
no_results_message = f"No results found for: '{actual_search_query}'"
|
|
148
145
|
if bot:
|
|
149
|
-
bot.send_message(
|
|
146
|
+
bot.send_message(f"No results found for: '{actual_search_query}'", None)
|
|
150
147
|
else:
|
|
151
148
|
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
|
|
152
149
|
|
|
@@ -71,7 +71,7 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
71
71
|
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
|
|
72
72
|
|
|
73
73
|
# Download the film using the m3u8 playlist, and output filename
|
|
74
|
-
|
|
74
|
+
hls_process = HLS_Downloader(
|
|
75
75
|
m3u8_url=master_playlist,
|
|
76
76
|
output_path=os.path.join(mp4_path, title_name)
|
|
77
77
|
).start()
|
|
@@ -83,8 +83,10 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
83
83
|
if script_id != "unknown":
|
|
84
84
|
TelegramSession.deleteScriptId(script_id)
|
|
85
85
|
|
|
86
|
-
if
|
|
87
|
-
try:
|
|
88
|
-
|
|
86
|
+
if hls_process['error'] is not None:
|
|
87
|
+
try:
|
|
88
|
+
os.remove(hls_process['path'])
|
|
89
|
+
except Exception:
|
|
90
|
+
pass
|
|
89
91
|
|
|
90
|
-
return
|
|
92
|
+
return hls_process['path']
|
|
@@ -55,7 +55,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
55
55
|
|
|
56
56
|
# Get episode information
|
|
57
57
|
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
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")
|
|
58
|
+
console.print(f"[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")
|
|
59
59
|
|
|
60
60
|
if site_constant.TELEGRAM_BOT:
|
|
61
61
|
bot = get_bot_instance()
|
|
@@ -81,16 +81,18 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
81
81
|
master_playlist = video_source.get_playlist()
|
|
82
82
|
|
|
83
83
|
# Download the episode
|
|
84
|
-
|
|
84
|
+
hls_process = HLS_Downloader(
|
|
85
85
|
m3u8_url=master_playlist,
|
|
86
86
|
output_path=os.path.join(mp4_path, mp4_name)
|
|
87
87
|
).start()
|
|
88
88
|
|
|
89
|
-
if
|
|
90
|
-
try:
|
|
91
|
-
|
|
89
|
+
if hls_process['error'] is not None:
|
|
90
|
+
try:
|
|
91
|
+
os.remove(hls_process['path'])
|
|
92
|
+
except Exception:
|
|
93
|
+
pass
|
|
92
94
|
|
|
93
|
-
return
|
|
95
|
+
return hls_process['path'], hls_process['stopped']
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, video_source: VideoSource, download_all: bool = False, episode_selection: str = None) -> None:
|
|
@@ -214,7 +216,7 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
|
|
|
214
216
|
download_episode(season_number, scrape_serie, video_source, download_all=False, episode_selection=episode_selection)
|
|
215
217
|
|
|
216
218
|
if site_constant.TELEGRAM_BOT:
|
|
217
|
-
bot.send_message(
|
|
219
|
+
bot.send_message("Finito di scaricare tutte le serie e episodi", None)
|
|
218
220
|
|
|
219
221
|
# Get script_id
|
|
220
222
|
script_id = TelegramSession.get_session()
|
|
@@ -26,6 +26,7 @@ console = Console()
|
|
|
26
26
|
media_search_manager = MediaManager()
|
|
27
27
|
table_show_manager = TVShowManager()
|
|
28
28
|
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
29
|
+
ssl_verify = config_manager.get_bool("REQUESTS", "verify")
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
def title_search(query: str) -> int:
|
|
@@ -49,6 +50,7 @@ def title_search(query: str) -> int:
|
|
|
49
50
|
f"{site_constant.FULL_URL}/it",
|
|
50
51
|
headers={'user-agent': get_userAgent()},
|
|
51
52
|
timeout=max_timeout,
|
|
53
|
+
verify=ssl_verify,
|
|
52
54
|
follow_redirects=True
|
|
53
55
|
)
|
|
54
56
|
response.raise_for_status()
|
|
@@ -57,7 +59,9 @@ def title_search(query: str) -> int:
|
|
|
57
59
|
version = json.loads(soup.find('div', {'id': "app"}).get("data-page"))['version']
|
|
58
60
|
|
|
59
61
|
except Exception as e:
|
|
60
|
-
if "WinError" in str(e) or "Errno" in str(e):
|
|
62
|
+
if "WinError" in str(e) or "Errno" in str(e):
|
|
63
|
+
console.print("\n[bold yellow]Please make sure you have enabled and configured a valid proxy.[/bold yellow]")
|
|
64
|
+
|
|
61
65
|
console.print(f"[red]Site: {site_constant.SITE_NAME} version, request error: {e}")
|
|
62
66
|
return 0
|
|
63
67
|
|
|
@@ -73,7 +77,8 @@ def title_search(query: str) -> int:
|
|
|
73
77
|
'x-inertia': 'true',
|
|
74
78
|
'x-inertia-version': version
|
|
75
79
|
},
|
|
76
|
-
timeout=max_timeout
|
|
80
|
+
timeout=max_timeout,
|
|
81
|
+
verify=ssl_verify
|
|
77
82
|
)
|
|
78
83
|
response.raise_for_status()
|
|
79
84
|
|
|
@@ -116,7 +121,7 @@ def title_search(query: str) -> int:
|
|
|
116
121
|
|
|
117
122
|
if site_constant.TELEGRAM_BOT:
|
|
118
123
|
if choices:
|
|
119
|
-
bot.send_message(
|
|
124
|
+
bot.send_message("Lista dei risultati:", choices)
|
|
120
125
|
|
|
121
126
|
# Return the number of titles found
|
|
122
127
|
return media_search_manager.get_length()
|
|
@@ -17,6 +17,7 @@ from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
|
|
|
17
17
|
|
|
18
18
|
# Variable
|
|
19
19
|
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
20
|
+
ssl_verify = config_manager.get_bool("REQUESTS", "verify")
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class GetSerieInfo:
|
|
@@ -50,7 +51,8 @@ class GetSerieInfo:
|
|
|
50
51
|
response = httpx.get(
|
|
51
52
|
url=f"{self.url}/titles/{self.media_id}-{self.series_name}",
|
|
52
53
|
headers=self.headers,
|
|
53
|
-
timeout=max_timeout
|
|
54
|
+
timeout=max_timeout,
|
|
55
|
+
verify=ssl_verify
|
|
54
56
|
)
|
|
55
57
|
response.raise_for_status()
|
|
56
58
|
|
|
@@ -104,7 +106,8 @@ class GetSerieInfo:
|
|
|
104
106
|
'x-inertia': 'true',
|
|
105
107
|
'x-inertia-version': self.version,
|
|
106
108
|
},
|
|
107
|
-
timeout=max_timeout
|
|
109
|
+
timeout=max_timeout,
|
|
110
|
+
verify=ssl_verify
|
|
108
111
|
)
|
|
109
112
|
|
|
110
113
|
# Extract episodes from JSON response
|
|
@@ -9,6 +9,7 @@ from rich.prompt import Prompt
|
|
|
9
9
|
from StreamingCommunity.Api.Template import get_select_title
|
|
10
10
|
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
11
11
|
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
12
|
+
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
# Logic class
|
|
@@ -45,6 +46,13 @@ def process_search_result(select_title, selections=None):
|
|
|
45
46
|
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
46
47
|
{'season': season_selection, 'episode': episode_selection}
|
|
47
48
|
"""
|
|
49
|
+
if not select_title:
|
|
50
|
+
if site_constant.TELEGRAM_BOT:
|
|
51
|
+
bot = get_bot_instance()
|
|
52
|
+
bot.send_message("No title selected or selection cancelled.", None)
|
|
53
|
+
else:
|
|
54
|
+
console.print("[yellow]No title selected or selection cancelled.")
|
|
55
|
+
return
|
|
48
56
|
if select_title.type == 'tv':
|
|
49
57
|
season_selection = None
|
|
50
58
|
episode_selection = None
|
|
@@ -69,26 +77,52 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
69
77
|
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
70
78
|
{'season': season_selection, 'episode': episode_selection}
|
|
71
79
|
"""
|
|
80
|
+
"""
|
|
81
|
+
Main function of the application for search.
|
|
82
|
+
|
|
83
|
+
Parameters:
|
|
84
|
+
string_to_search (str, optional): String to search for
|
|
85
|
+
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
86
|
+
direct_item (dict, optional): Direct item to process (bypass search)
|
|
87
|
+
selections (dict, optional): Dictionary containing selection inputs that bypass manual input
|
|
88
|
+
{'season': season_selection, 'episode': episode_selection}
|
|
89
|
+
"""
|
|
90
|
+
bot = None
|
|
91
|
+
if site_constant.TELEGRAM_BOT:
|
|
92
|
+
bot = get_bot_instance()
|
|
93
|
+
|
|
72
94
|
if direct_item:
|
|
73
95
|
select_title = MediaItem(**direct_item)
|
|
74
96
|
process_search_result(select_title, selections)
|
|
75
97
|
return
|
|
76
98
|
|
|
77
|
-
|
|
78
|
-
|
|
99
|
+
# Get the user input for the search term
|
|
100
|
+
actual_search_query = get_user_input(string_to_search)
|
|
101
|
+
|
|
102
|
+
# Handle cases where user input is empty, or 'back' was handled (sys.exit or None return)
|
|
103
|
+
if not actual_search_query:
|
|
104
|
+
if bot:
|
|
105
|
+
if actual_search_query is None: # Specifically for timeout from bot.ask or failed restart
|
|
106
|
+
bot.send_message("Search term not provided or operation cancelled. Returning.", None)
|
|
107
|
+
return
|
|
79
108
|
|
|
80
|
-
# Perform
|
|
81
|
-
len_database = title_search(
|
|
109
|
+
# Perform the database search
|
|
110
|
+
len_database = title_search(actual_search_query)
|
|
82
111
|
|
|
83
112
|
# If only the database is needed, return the manager
|
|
84
113
|
if get_onlyDatabase:
|
|
85
114
|
return media_search_manager
|
|
86
|
-
|
|
115
|
+
|
|
87
116
|
if len_database > 0:
|
|
88
|
-
select_title = get_select_title(table_show_manager, media_search_manager,len_database)
|
|
117
|
+
select_title = get_select_title(table_show_manager, media_search_manager, len_database)
|
|
89
118
|
process_search_result(select_title, selections)
|
|
90
119
|
|
|
91
120
|
else:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
121
|
+
if bot:
|
|
122
|
+
bot.send_message(f"No results found for: '{actual_search_query}'", None)
|
|
123
|
+
else:
|
|
124
|
+
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{actual_search_query}")
|
|
125
|
+
|
|
126
|
+
# Do not call search() recursively here to avoid infinite loops on no results.
|
|
127
|
+
# The flow should return to the caller (e.g., main menu in run.py).
|
|
128
|
+
return
|
|
@@ -49,13 +49,15 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
49
49
|
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
|
|
50
50
|
|
|
51
51
|
# Download the film using the m3u8 playlist, and output filename
|
|
52
|
-
|
|
52
|
+
hls_process = HLS_Downloader(
|
|
53
53
|
m3u8_url=master_playlist,
|
|
54
54
|
output_path=os.path.join(mp4_path, title_name)
|
|
55
55
|
).start()
|
|
56
56
|
|
|
57
|
-
if
|
|
58
|
-
try:
|
|
59
|
-
|
|
57
|
+
if hls_process['error'] is not None:
|
|
58
|
+
try:
|
|
59
|
+
os.remove(hls_process['path'])
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
60
62
|
|
|
61
|
-
return
|
|
63
|
+
return hls_process['path']
|
|
@@ -53,7 +53,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
53
53
|
|
|
54
54
|
# Get episode information
|
|
55
55
|
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
56
|
-
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
|
+
console.print(f"[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")
|
|
57
57
|
|
|
58
58
|
# Define filename and path for the downloaded video
|
|
59
59
|
mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
|
|
@@ -64,16 +64,18 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
64
64
|
master_playlist = video_source.get_m3u8_url(obj_episode.url)
|
|
65
65
|
|
|
66
66
|
# Download the episode
|
|
67
|
-
|
|
67
|
+
hls_process = HLS_Downloader(
|
|
68
68
|
m3u8_url=master_playlist,
|
|
69
69
|
output_path=os.path.join(mp4_path, mp4_name)
|
|
70
70
|
).start()
|
|
71
71
|
|
|
72
|
-
if
|
|
73
|
-
try:
|
|
74
|
-
|
|
72
|
+
if hls_process['error'] is not None:
|
|
73
|
+
try:
|
|
74
|
+
os.remove(hls_process['path'])
|
|
75
|
+
except Exception:
|
|
76
|
+
pass
|
|
75
77
|
|
|
76
|
-
return
|
|
78
|
+
return hls_process['path'], hls_process['stopped']
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, download_all: bool = False, episode_selection: str = None) -> None:
|
|
@@ -86,7 +86,9 @@ def title_search(query: str) -> int:
|
|
|
86
86
|
soup = BeautifulSoup(response.text, 'html.parser')
|
|
87
87
|
|
|
88
88
|
except Exception as e:
|
|
89
|
-
if "WinError" in str(e) or "Errno" in str(e):
|
|
89
|
+
if "WinError" in str(e) or "Errno" in str(e):
|
|
90
|
+
console.print("\n[bold yellow]Please make sure you have enabled and configured a valid proxy.[/bold yellow]")
|
|
91
|
+
|
|
90
92
|
console.print(f"[red]Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
91
93
|
return 0
|
|
92
94
|
|
|
@@ -5,11 +5,12 @@ import logging
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
# External libraries
|
|
8
|
-
import httpx
|
|
9
8
|
from bs4 import BeautifulSoup
|
|
10
9
|
|
|
10
|
+
|
|
11
11
|
# Internal utilities
|
|
12
12
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
13
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
13
14
|
from StreamingCommunity.Util.config_json import config_manager
|
|
14
15
|
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager, Episode
|
|
15
16
|
|
|
@@ -24,8 +25,7 @@ class GetSerieInfo:
|
|
|
24
25
|
self.url = url
|
|
25
26
|
self.seasons_manager = SeasonManager()
|
|
26
27
|
self.series_name = None
|
|
27
|
-
|
|
28
|
-
self.client = httpx.Client(headers=self.headers, timeout=max_timeout)
|
|
28
|
+
self.client = create_client(headers=self.headers)
|
|
29
29
|
|
|
30
30
|
def collect_info_season(self) -> None:
|
|
31
31
|
"""
|
|
@@ -7,4 +7,13 @@ from .manage_ep import (
|
|
|
7
7
|
validate_selection,
|
|
8
8
|
dynamic_format_number,
|
|
9
9
|
display_episodes_list
|
|
10
|
-
)
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"manage_selection",
|
|
14
|
+
"map_episode_title",
|
|
15
|
+
"validate_episode_selection",
|
|
16
|
+
"validate_selection",
|
|
17
|
+
"dynamic_format_number",
|
|
18
|
+
"display_episodes_list"
|
|
19
|
+
]
|
|
@@ -125,20 +125,20 @@ def map_episode_title(tv_name: str, number_season: int, episode_number: int, epi
|
|
|
125
125
|
"""
|
|
126
126
|
map_episode_temp = MAP_EPISODE
|
|
127
127
|
|
|
128
|
-
if tv_name
|
|
128
|
+
if tv_name is not None:
|
|
129
129
|
map_episode_temp = map_episode_temp.replace("%(tv_name)", os_manager.get_sanitize_file(tv_name))
|
|
130
130
|
|
|
131
|
-
if number_season
|
|
131
|
+
if number_season is not None:
|
|
132
132
|
map_episode_temp = map_episode_temp.replace("%(season)", str(number_season))
|
|
133
133
|
else:
|
|
134
134
|
map_episode_temp = map_episode_temp.replace("%(season)", dynamic_format_number(str(0)))
|
|
135
135
|
|
|
136
|
-
if episode_number
|
|
136
|
+
if episode_number is not None:
|
|
137
137
|
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(str(episode_number)))
|
|
138
138
|
else:
|
|
139
139
|
map_episode_temp = map_episode_temp.replace("%(episode)", dynamic_format_number(str(0)))
|
|
140
140
|
|
|
141
|
-
if episode_name
|
|
141
|
+
if episode_name is not None:
|
|
142
142
|
map_episode_temp = map_episode_temp.replace("%(episode_name)", os_manager.get_sanitize_file(episode_name))
|
|
143
143
|
|
|
144
144
|
logging.info(f"Map episode string return: {map_episode_temp}")
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# 19.06.24
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
|
-
|
|
6
3
|
# External library
|
|
7
4
|
from rich.console import Console
|
|
8
5
|
|
|
@@ -83,12 +80,19 @@ def get_select_title(table_show_manager, media_search_manager, num_results_avail
|
|
|
83
80
|
|
|
84
81
|
color_index = 1
|
|
85
82
|
for key in first_media_item.__dict__.keys():
|
|
83
|
+
|
|
86
84
|
if key.capitalize() in column_to_hide:
|
|
87
85
|
continue
|
|
86
|
+
|
|
88
87
|
if key in ('id', 'type', 'name', 'score'):
|
|
89
|
-
if key == 'type':
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
if key == 'type':
|
|
89
|
+
column_info["Type"] = {'color': 'yellow'}
|
|
90
|
+
|
|
91
|
+
elif key == 'name':
|
|
92
|
+
column_info["Name"] = {'color': 'magenta'}
|
|
93
|
+
elif key == 'score':
|
|
94
|
+
column_info["Score"] = {'color': 'cyan'}
|
|
95
|
+
|
|
92
96
|
else:
|
|
93
97
|
column_info[key.capitalize()] = {'color': available_colors[color_index % len(available_colors)]}
|
|
94
98
|
color_index += 1
|