StreamingCommunity 3.2.7__py3-none-any.whl → 3.2.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/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 +2 -2
- 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 +2 -2
- StreamingCommunity/Api/Site/animeunity/serie.py +2 -2
- StreamingCommunity/Api/Site/animeworld/site.py +3 -5
- StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +8 -10
- StreamingCommunity/Api/Site/cb01new/film.py +7 -5
- StreamingCommunity/Api/Site/crunchyroll/__init__.py +1 -3
- StreamingCommunity/Api/Site/crunchyroll/film.py +9 -7
- StreamingCommunity/Api/Site/crunchyroll/series.py +9 -7
- StreamingCommunity/Api/Site/crunchyroll/site.py +10 -1
- 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 +1 -1
- StreamingCommunity/Api/Site/mediasetinfinity/film.py +10 -16
- StreamingCommunity/Api/Site/mediasetinfinity/series.py +12 -18
- StreamingCommunity/Api/Site/mediasetinfinity/site.py +11 -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 +1 -1
- StreamingCommunity/Api/Site/raiplay/film.py +41 -10
- StreamingCommunity/Api/Site/raiplay/series.py +44 -12
- 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 +0 -1
- StreamingCommunity/Api/Site/streamingcommunity/film.py +7 -5
- StreamingCommunity/Api/Site/streamingcommunity/series.py +9 -7
- StreamingCommunity/Api/Site/streamingcommunity/site.py +4 -2
- 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 +5 -12
- StreamingCommunity/Lib/Downloader/DASH/decrypt.py +1 -1
- StreamingCommunity/Lib/Downloader/DASH/downloader.py +1 -1
- 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 +253 -144
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +4 -3
- 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/version.py +2 -2
- StreamingCommunity/Util/config_json.py +139 -59
- StreamingCommunity/Util/http_client.py +201 -0
- StreamingCommunity/Util/message.py +1 -1
- StreamingCommunity/Util/os.py +5 -5
- StreamingCommunity/Util/table.py +3 -3
- StreamingCommunity/__init__.py +9 -1
- StreamingCommunity/run.py +396 -260
- {streamingcommunity-3.2.7.dist-info → streamingcommunity-3.2.9.dist-info}/METADATA +143 -45
- streamingcommunity-3.2.9.dist-info/RECORD +113 -0
- streamingcommunity-3.2.7.dist-info/RECORD +0 -111
- {streamingcommunity-3.2.7.dist-info → streamingcommunity-3.2.9.dist-info}/WHEEL +0 -0
- {streamingcommunity-3.2.7.dist-info → streamingcommunity-3.2.9.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-3.2.7.dist-info → streamingcommunity-3.2.9.dist-info}/licenses/LICENSE +0 -0
- {streamingcommunity-3.2.7.dist-info → streamingcommunity-3.2.9.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 23.11.24
|
|
2
2
|
|
|
3
|
-
from typing import Dict, Any, List,
|
|
3
|
+
from typing import Dict, Any, List, Optional
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class Episode:
|
|
@@ -12,6 +12,7 @@ class Episode:
|
|
|
12
12
|
self.name: str = data.get('name', '')
|
|
13
13
|
self.duration: int = data.get('duration', 0)
|
|
14
14
|
self.url: str = data.get('url', '')
|
|
15
|
+
self.mpd_id: str = data.get('mpd_id', '')
|
|
15
16
|
|
|
16
17
|
def __str__(self):
|
|
17
18
|
return f"Episode(id={self.id}, number={self.number}, name='{self.name}', duration={self.duration} sec)"
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
5
|
# External library
|
|
6
|
-
import httpx
|
|
7
6
|
from bs4 import BeautifulSoup
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
# Internal utilities
|
|
11
10
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
11
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
12
12
|
from StreamingCommunity.Util.config_json import config_manager
|
|
13
13
|
|
|
14
14
|
|
|
@@ -19,7 +19,7 @@ REQUEST_VERIFY = config_manager.get_bool('REQUESTS', 'verify')
|
|
|
19
19
|
|
|
20
20
|
class VideoSource:
|
|
21
21
|
def __init__(self):
|
|
22
|
-
self.client =
|
|
22
|
+
self.client = create_client(headers={'user-agent': get_userAgent()})
|
|
23
23
|
|
|
24
24
|
def extractLinkHdPlayer(self, response):
|
|
25
25
|
"""Extract iframe source from the page."""
|
|
@@ -4,12 +4,12 @@ import logging
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
# External libraries
|
|
7
|
-
import httpx
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
# Internal utilities
|
|
11
10
|
from StreamingCommunity.Util.config_json import config_manager
|
|
12
11
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
12
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# Variable
|
|
@@ -17,21 +17,18 @@ MAX_TIMEOUT = config_manager.get_int("REQUESTS", "timeout")
|
|
|
17
17
|
REQUEST_VERIFY = config_manager.get_bool('REQUESTS', 'verify')
|
|
18
18
|
|
|
19
19
|
class VideoSource:
|
|
20
|
-
def __init__(self,
|
|
20
|
+
def __init__(self, site_url, episode_data, session_id, csrf_token):
|
|
21
21
|
"""Initialize the VideoSource with session details, episode data, and URL."""
|
|
22
22
|
self.session_id = session_id
|
|
23
23
|
self.csrf_token = csrf_token
|
|
24
24
|
self.episode_data = episode_data
|
|
25
25
|
self.number = episode_data['number']
|
|
26
|
-
self.link = episode_data['link']
|
|
26
|
+
self.link = site_url + episode_data['link']
|
|
27
27
|
|
|
28
28
|
# Create an HTTP client with session cookies, headers, and base URL.
|
|
29
|
-
self.client =
|
|
29
|
+
self.client = create_client(
|
|
30
30
|
cookies={"sessionId": session_id},
|
|
31
|
-
headers={"User-Agent": get_userAgent(), "csrf-token": csrf_token}
|
|
32
|
-
base_url=full_url,
|
|
33
|
-
timeout=MAX_TIMEOUT,
|
|
34
|
-
verify=REQUEST_VERIFY
|
|
31
|
+
headers={"User-Agent": get_userAgent(), "csrf-token": csrf_token}
|
|
35
32
|
)
|
|
36
33
|
|
|
37
34
|
def get_playlist(self):
|
|
@@ -43,7 +43,7 @@ def get_user_input(string_to_search: str = None):
|
|
|
43
43
|
bot = get_bot_instance()
|
|
44
44
|
string_to_search = bot.ask(
|
|
45
45
|
"key_search",
|
|
46
|
-
|
|
46
|
+
"Enter the search term\nor type 'back' to return to the menu: ",
|
|
47
47
|
None
|
|
48
48
|
)
|
|
49
49
|
|
|
@@ -117,7 +117,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
117
117
|
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
118
118
|
|
|
119
119
|
if site_constant.TELEGRAM_BOT:
|
|
120
|
-
bot.send_message(
|
|
120
|
+
bot.send_message("No results found, please try again", None)
|
|
121
121
|
|
|
122
122
|
# If no results are found, ask again
|
|
123
123
|
string_to_search = get_user_input()
|
|
@@ -5,7 +5,6 @@ import re
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
# External library
|
|
8
|
-
import httpx
|
|
9
8
|
from bs4 import BeautifulSoup
|
|
10
9
|
from rich.console import Console
|
|
11
10
|
|
|
@@ -13,6 +12,7 @@ from rich.console import Console
|
|
|
13
12
|
# Internal utilities
|
|
14
13
|
from StreamingCommunity.Util.os import os_manager
|
|
15
14
|
from StreamingCommunity.Util.headers import get_headers
|
|
15
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
16
16
|
from StreamingCommunity.Util.message import start_message
|
|
17
17
|
from StreamingCommunity.Util.config_json import config_manager
|
|
18
18
|
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance, TelegramSession
|
|
@@ -60,7 +60,7 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
60
60
|
|
|
61
61
|
# Extract mostraguarda URL
|
|
62
62
|
try:
|
|
63
|
-
response =
|
|
63
|
+
response = create_client(headers=get_headers()).get(select_title.url)
|
|
64
64
|
response.raise_for_status()
|
|
65
65
|
|
|
66
66
|
soup = BeautifulSoup(response.text, 'html.parser')
|
|
@@ -74,7 +74,7 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
74
74
|
# Extract supervideo URL
|
|
75
75
|
supervideo_url = None
|
|
76
76
|
try:
|
|
77
|
-
response =
|
|
77
|
+
response = create_client(headers=get_headers()).get(mostraguarda)
|
|
78
78
|
response.raise_for_status()
|
|
79
79
|
|
|
80
80
|
soup = BeautifulSoup(response.text, 'html.parser')
|
|
@@ -96,7 +96,7 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
96
96
|
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
|
|
97
97
|
|
|
98
98
|
# Download the film using the m3u8 playlist, and output filename
|
|
99
|
-
|
|
99
|
+
hls_process = HLS_Downloader(
|
|
100
100
|
m3u8_url=master_playlist,
|
|
101
101
|
output_path=os.path.join(mp4_path, title_name)
|
|
102
102
|
).start()
|
|
@@ -108,8 +108,10 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
108
108
|
if script_id != "unknown":
|
|
109
109
|
TelegramSession.deleteScriptId(script_id)
|
|
110
110
|
|
|
111
|
-
if
|
|
112
|
-
try:
|
|
113
|
-
|
|
111
|
+
if hls_process['error'] is not None:
|
|
112
|
+
try:
|
|
113
|
+
os.remove(hls_process['path'])
|
|
114
|
+
except Exception:
|
|
115
|
+
pass
|
|
114
116
|
|
|
115
|
-
return
|
|
117
|
+
return hls_process['path']
|
|
@@ -54,7 +54,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
54
54
|
|
|
55
55
|
# Get episode information
|
|
56
56
|
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
57
|
-
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
|
+
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")
|
|
58
58
|
|
|
59
59
|
# Telegram integration
|
|
60
60
|
if site_constant.TELEGRAM_BOT:
|
|
@@ -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, download_all: bool = False, episode_selection: str = None) -> None:
|
|
@@ -196,7 +198,7 @@ def download_series(select_season: MediaItem, season_selection: str = None, epis
|
|
|
196
198
|
download_episode(i_season, scrape_serie, download_all=False, episode_selection=episode_selection)
|
|
197
199
|
|
|
198
200
|
if site_constant.TELEGRAM_BOT:
|
|
199
|
-
bot.send_message(
|
|
201
|
+
bot.send_message("Finito di scaricare tutte le serie e episodi", None)
|
|
200
202
|
|
|
201
203
|
# Get script_id
|
|
202
204
|
script_id = TelegramSession.get_session()
|
|
@@ -93,7 +93,7 @@ def title_search(query: str) -> int:
|
|
|
93
93
|
|
|
94
94
|
if site_constant.TELEGRAM_BOT:
|
|
95
95
|
if choices:
|
|
96
|
-
bot.send_message(
|
|
96
|
+
bot.send_message("Lista dei risultati:", choices)
|
|
97
97
|
|
|
98
98
|
# Return the number of titles found
|
|
99
99
|
return media_search_manager.get_length()
|
|
@@ -43,7 +43,7 @@ def get_user_input(string_to_search: str = None):
|
|
|
43
43
|
bot = get_bot_instance()
|
|
44
44
|
string_to_search = bot.ask(
|
|
45
45
|
"key_search",
|
|
46
|
-
|
|
46
|
+
"Enter the search term\nor type 'back' to return to the menu: ",
|
|
47
47
|
None
|
|
48
48
|
)
|
|
49
49
|
|
|
@@ -116,7 +116,7 @@ def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_
|
|
|
116
116
|
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
117
117
|
|
|
118
118
|
if site_constant.TELEGRAM_BOT:
|
|
119
|
-
bot.send_message(
|
|
119
|
+
bot.send_message("No results found, please try again", None)
|
|
120
120
|
|
|
121
121
|
# If no results are found, ask again
|
|
122
122
|
string_to_search = get_user_input()
|
|
@@ -109,7 +109,7 @@ def download_series(select_title: MediaItem, season_selection: str = None, episo
|
|
|
109
109
|
# Telegram bot integration
|
|
110
110
|
if episode_selection is None:
|
|
111
111
|
if site_constant.TELEGRAM_BOT:
|
|
112
|
-
console.print(
|
|
112
|
+
console.print("\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")
|
|
113
113
|
bot.send_message(f"Episodi trovati: {episoded_count}", None)
|
|
114
114
|
|
|
115
115
|
last_command = bot.ask(
|
|
@@ -145,7 +145,7 @@ def download_series(select_title: MediaItem, season_selection: str = None, episo
|
|
|
145
145
|
_, kill_handler = download_episode(i_episode-1, scrape_serie, video_source)
|
|
146
146
|
|
|
147
147
|
if site_constant.TELEGRAM_BOT:
|
|
148
|
-
bot.send_message(
|
|
148
|
+
bot.send_message("Finito di scaricare tutte le serie e episodi", None)
|
|
149
149
|
|
|
150
150
|
# Get script_id
|
|
151
151
|
script_id = TelegramSession.get_session()
|
|
@@ -11,6 +11,7 @@ from rich.console import Console
|
|
|
11
11
|
# Internal utilities
|
|
12
12
|
from StreamingCommunity.Util.config_json import config_manager
|
|
13
13
|
from StreamingCommunity.Util.headers import get_userAgent, get_headers
|
|
14
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
14
15
|
from StreamingCommunity.Util.table import TVShowManager
|
|
15
16
|
|
|
16
17
|
|
|
@@ -31,11 +32,8 @@ def get_session_and_csrf() -> dict:
|
|
|
31
32
|
Get the session ID and CSRF token from the website's cookies and HTML meta data.
|
|
32
33
|
"""
|
|
33
34
|
# Send an initial GET request to the website
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
headers=get_headers(),
|
|
37
|
-
verify=False
|
|
38
|
-
)
|
|
35
|
+
client = create_client(headers=get_headers())
|
|
36
|
+
response = client.get(site_constant.FULL_URL)
|
|
39
37
|
|
|
40
38
|
# Extract the sessionId from the cookies
|
|
41
39
|
session_id = response.cookies.get('sessionId')
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
# External libraries
|
|
6
|
-
import httpx
|
|
7
7
|
from bs4 import BeautifulSoup
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
# Internal utilities
|
|
11
11
|
from StreamingCommunity.Util.headers import get_userAgent
|
|
12
|
+
from StreamingCommunity.Util.http_client import create_client
|
|
12
13
|
from StreamingCommunity.Util.config_json import config_manager
|
|
13
14
|
from StreamingCommunity.Util.os import os_manager
|
|
14
15
|
|
|
@@ -23,24 +24,21 @@ max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class ScrapSerie:
|
|
26
|
-
def __init__(self, url,
|
|
27
|
+
def __init__(self, url, site_url):
|
|
27
28
|
"""Initialize the ScrapSerie object with the provided URL and setup the HTTP client."""
|
|
28
29
|
self.url = url
|
|
29
|
-
self.link = httpx.URL(url).path
|
|
30
30
|
self.session_id, self.csrf_token = get_session_and_csrf()
|
|
31
|
-
self.client =
|
|
31
|
+
self.client = create_client(
|
|
32
32
|
cookies={"sessionId": self.session_id},
|
|
33
|
-
headers={"User-Agent": get_userAgent(), "csrf-token": self.csrf_token}
|
|
34
|
-
base_url=full_url,
|
|
35
|
-
verify=False
|
|
33
|
+
headers={"User-Agent": get_userAgent(), "csrf-token": self.csrf_token}
|
|
36
34
|
)
|
|
37
35
|
|
|
38
36
|
try:
|
|
39
|
-
self.response = self.client.get(self.
|
|
37
|
+
self.response = self.client.get(self.url, timeout=max_timeout, follow_redirects=True)
|
|
40
38
|
self.response.raise_for_status()
|
|
41
39
|
|
|
42
|
-
except:
|
|
43
|
-
raise Exception(f"Failed to retrieve anime page
|
|
40
|
+
except Exception as e:
|
|
41
|
+
raise Exception(f"Failed to retrieve anime page: {str(e)}")
|
|
44
42
|
|
|
45
43
|
def get_name(self):
|
|
46
44
|
"""Extract and return the name of the anime series."""
|
|
@@ -50,13 +50,15 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
50
50
|
master_playlist = video_source.get_playlist()
|
|
51
51
|
|
|
52
52
|
# Download the film using the m3u8 playlist, and output filename
|
|
53
|
-
|
|
53
|
+
hls_process = HLS_Downloader(
|
|
54
54
|
m3u8_url=master_playlist,
|
|
55
55
|
output_path=os.path.join(mp4_path, title_name)
|
|
56
56
|
).start()
|
|
57
57
|
|
|
58
|
-
if
|
|
59
|
-
try:
|
|
60
|
-
|
|
58
|
+
if hls_process['error'] is not None:
|
|
59
|
+
try:
|
|
60
|
+
os.remove(hls_process['path'])
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
61
63
|
|
|
62
|
-
return
|
|
64
|
+
return hls_process['path']
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# 16.03.25
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
|
-
import subprocess
|
|
5
3
|
from urllib.parse import quote_plus
|
|
6
4
|
|
|
7
5
|
|
|
@@ -25,7 +23,7 @@ from .series import download_series
|
|
|
25
23
|
indice = 8
|
|
26
24
|
_useFor = "Anime"
|
|
27
25
|
_priority = 0
|
|
28
|
-
_engineDownload = "
|
|
26
|
+
_engineDownload = "dash"
|
|
29
27
|
_deprecate = False
|
|
30
28
|
|
|
31
29
|
msg = Prompt()
|
|
@@ -54,13 +54,13 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
54
54
|
query_params = parse_qs(parsed_url.query)
|
|
55
55
|
|
|
56
56
|
# Download the episode
|
|
57
|
-
|
|
57
|
+
dash_process = DASH_Downloader(
|
|
58
58
|
cdm_device=get_wvd_path(),
|
|
59
59
|
license_url='https://www.crunchyroll.com/license/v1/license/widevine',
|
|
60
60
|
mpd_url=mpd_url,
|
|
61
61
|
output_path=os.path.join(mp4_path, mp4_name),
|
|
62
62
|
)
|
|
63
|
-
|
|
63
|
+
dash_process.parse_manifest(custom_headers=mpd_headers)
|
|
64
64
|
|
|
65
65
|
# Create headers for license request
|
|
66
66
|
license_headers = mpd_headers.copy()
|
|
@@ -69,14 +69,16 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
69
69
|
"x-cr-video-token": query_params['playbackGuid'][0],
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
if
|
|
73
|
-
|
|
72
|
+
if dash_process.download_and_decrypt(custom_headers=license_headers):
|
|
73
|
+
dash_process.finalize_output()
|
|
74
74
|
|
|
75
75
|
# Get final output path and status
|
|
76
|
-
status =
|
|
76
|
+
status = dash_process.get_status()
|
|
77
77
|
|
|
78
78
|
if status['error'] is not None and status['path']:
|
|
79
|
-
try:
|
|
80
|
-
|
|
79
|
+
try:
|
|
80
|
+
os.remove(status['path'])
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
81
83
|
|
|
82
84
|
return status['path'], status['stopped']
|
|
@@ -71,13 +71,13 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
71
71
|
query_params = parse_qs(parsed_url.query)
|
|
72
72
|
|
|
73
73
|
# Download the episode
|
|
74
|
-
|
|
74
|
+
dash_process = DASH_Downloader(
|
|
75
75
|
cdm_device=get_wvd_path(),
|
|
76
76
|
license_url='https://www.crunchyroll.com/license/v1/license/widevine',
|
|
77
77
|
mpd_url=mpd_url,
|
|
78
78
|
output_path=os.path.join(mp4_path, mp4_name),
|
|
79
79
|
)
|
|
80
|
-
|
|
80
|
+
dash_process.parse_manifest(custom_headers=mpd_headers)
|
|
81
81
|
|
|
82
82
|
# Create headers for license request
|
|
83
83
|
license_headers = mpd_headers.copy()
|
|
@@ -86,15 +86,17 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
86
86
|
"x-cr-video-token": query_params['playbackGuid'][0],
|
|
87
87
|
})
|
|
88
88
|
|
|
89
|
-
if
|
|
90
|
-
|
|
89
|
+
if dash_process.download_and_decrypt(custom_headers=license_headers):
|
|
90
|
+
dash_process.finalize_output()
|
|
91
91
|
|
|
92
92
|
# Get final output path and status
|
|
93
|
-
status =
|
|
93
|
+
status = dash_process.get_status()
|
|
94
94
|
|
|
95
95
|
if status['error'] is not None and status['path']:
|
|
96
|
-
try:
|
|
97
|
-
|
|
96
|
+
try:
|
|
97
|
+
os.remove(status['path'])
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
98
100
|
|
|
99
101
|
# Delete episode stream
|
|
100
102
|
delete_stream_episode(url_id, query_params['playbackGuid'][0], mpd_headers)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# 16.03.25
|
|
2
2
|
|
|
3
|
+
import os
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
# External libraries
|
|
5
7
|
from curl_cffi import requests
|
|
@@ -8,6 +10,7 @@ from rich.console import Console
|
|
|
8
10
|
|
|
9
11
|
# Internal utilities
|
|
10
12
|
from StreamingCommunity.Util.config_json import config_manager
|
|
13
|
+
from StreamingCommunity.Util.os import get_wvd_path
|
|
11
14
|
from StreamingCommunity.Util.headers import get_headers
|
|
12
15
|
from StreamingCommunity.Util.table import TVShowManager
|
|
13
16
|
|
|
@@ -38,8 +41,14 @@ def title_search(query: str) -> int:
|
|
|
38
41
|
media_search_manager.clear()
|
|
39
42
|
table_show_manager.clear()
|
|
40
43
|
|
|
44
|
+
# Check CDM file before usage
|
|
45
|
+
cdm_device_path = get_wvd_path()
|
|
46
|
+
if not cdm_device_path or not isinstance(cdm_device_path, (str, bytes, os.PathLike)) or not os.path.isfile(cdm_device_path):
|
|
47
|
+
console.print(f"[bold red] CDM file not found or invalid path: {cdm_device_path}[/bold red]")
|
|
48
|
+
return None
|
|
49
|
+
|
|
41
50
|
# Build new Crunchyroll API search URL
|
|
42
|
-
api_url =
|
|
51
|
+
api_url = "https://www.crunchyroll.com/content/v2/discover/search"
|
|
43
52
|
|
|
44
53
|
params = {
|
|
45
54
|
"q": query,
|
|
@@ -55,7 +55,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
|
|
|
55
55
|
# Get episode information
|
|
56
56
|
obj_episode = scape_info_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
57
57
|
index_season_selected = dynamic_format_number(str(index_season_selected))
|
|
58
|
-
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")
|
|
58
|
+
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{scape_info_serie.tv_name}[/cyan] \\ [bold magenta]{obj_episode.get('name')}[/bold magenta] ([cyan]S{index_season_selected}E{index_episode_selected}[/cyan]) \n")
|
|
59
59
|
|
|
60
60
|
# Define filename and path for the downloaded video
|
|
61
61
|
mp4_name = f"{map_episode_title(scape_info_serie.tv_name, index_season_selected, index_episode_selected, obj_episode.get('name'))}.mp4"
|
|
@@ -68,16 +68,18 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
|
|
|
68
68
|
master_playlist = video_source.get_playlist()
|
|
69
69
|
|
|
70
70
|
# Download the film using the m3u8 playlist, and output filename
|
|
71
|
-
|
|
71
|
+
hls_process = HLS_Downloader(
|
|
72
72
|
m3u8_url=master_playlist,
|
|
73
73
|
output_path=os.path.join(mp4_path, mp4_name)
|
|
74
74
|
).start()
|
|
75
75
|
|
|
76
|
-
if
|
|
77
|
-
try:
|
|
78
|
-
|
|
76
|
+
if hls_process['error'] is not None:
|
|
77
|
+
try:
|
|
78
|
+
os.remove(hls_process['path'])
|
|
79
|
+
except Exception:
|
|
80
|
+
pass
|
|
79
81
|
|
|
80
|
-
return
|
|
82
|
+
return hls_process['path'], hls_process['stopped']
|
|
81
83
|
|
|
82
84
|
|
|
83
85
|
def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, download_all: bool = False, episode_selection: str = None) -> None:
|
|
@@ -53,8 +53,7 @@ class GetSerieInfo:
|
|
|
53
53
|
seasons_number = len(table_content.find_all("li"))
|
|
54
54
|
|
|
55
55
|
# Try to get the title, with fallback
|
|
56
|
-
|
|
57
|
-
self.tv_name = title_element.get_text(strip=True) if title_element else "Unknown Title"
|
|
56
|
+
self.tv_name = soup.find('h1', class_="front_title").get_text(strip=True) if soup.find('h1', class_="front_title") else "Unknown Series"
|
|
58
57
|
|
|
59
58
|
return seasons_number
|
|
60
59
|
|
|
@@ -49,37 +49,31 @@ def download_film(select_title: MediaItem) -> Tuple[str, bool]:
|
|
|
49
49
|
# Generate mpd and license URLs
|
|
50
50
|
bearer = get_bearer_token()
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
episode_id = select_title.url.split('_')[-1]
|
|
54
|
-
if "http" in episode_id:
|
|
55
|
-
try: episode_id = select_title.url.split('/')[-1]
|
|
56
|
-
except Exception:
|
|
57
|
-
console.print(f"[red]Error:[/red] Failed to parse episode ID from URL: {select_title.url}")
|
|
58
|
-
return None, True
|
|
59
|
-
|
|
60
|
-
playback_json = get_playback_url(bearer, episode_id)
|
|
52
|
+
playback_json = get_playback_url(bearer, select_title.id)
|
|
61
53
|
tracking_info = get_tracking_info(bearer, playback_json)[0]
|
|
62
54
|
|
|
63
55
|
license_url = generate_license_url(bearer, tracking_info)
|
|
64
56
|
mpd_url = get_manifest(tracking_info['video_src'])
|
|
65
57
|
|
|
66
58
|
# Download the episode
|
|
67
|
-
|
|
59
|
+
dash_process = DASH_Downloader(
|
|
68
60
|
cdm_device=get_wvd_path(),
|
|
69
61
|
license_url=license_url,
|
|
70
62
|
mpd_url=mpd_url,
|
|
71
63
|
output_path=mp4_path,
|
|
72
64
|
)
|
|
73
|
-
|
|
65
|
+
dash_process.parse_manifest(custom_headers=get_headers())
|
|
74
66
|
|
|
75
|
-
if
|
|
76
|
-
|
|
67
|
+
if dash_process.download_and_decrypt():
|
|
68
|
+
dash_process.finalize_output()
|
|
77
69
|
|
|
78
70
|
# Get final output path and status
|
|
79
|
-
status =
|
|
71
|
+
status = dash_process.get_status()
|
|
80
72
|
|
|
81
73
|
if status['error'] is not None and status['path']:
|
|
82
|
-
try:
|
|
83
|
-
|
|
74
|
+
try:
|
|
75
|
+
os.remove(status['path'])
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
84
78
|
|
|
85
79
|
return status['path'], status['stopped']
|
|
@@ -56,7 +56,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
56
56
|
|
|
57
57
|
# Get episode information
|
|
58
58
|
obj_episode = scrape_serie.selectEpisode(index_season_selected, index_episode_selected-1)
|
|
59
|
-
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")
|
|
59
|
+
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")
|
|
60
60
|
|
|
61
61
|
# Define filename and path for the downloaded video
|
|
62
62
|
mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
|
|
@@ -64,39 +64,33 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
|
|
64
64
|
|
|
65
65
|
# Generate mpd and license URLs
|
|
66
66
|
bearer = get_bearer_token()
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
episode_id = obj_episode.url.split('_')[-1]
|
|
70
|
-
if "http" in episode_id:
|
|
71
|
-
try: episode_id = obj_episode.url.split('/')[-1]
|
|
72
|
-
except Exception:
|
|
73
|
-
console.print(f"[red]Error:[/red] Failed to parse episode ID from URL: {obj_episode.url}")
|
|
74
|
-
return None, True
|
|
75
|
-
|
|
76
|
-
playback_json = get_playback_url(bearer, episode_id)
|
|
67
|
+
|
|
68
|
+
playback_json = get_playback_url(bearer, obj_episode.id)
|
|
77
69
|
tracking_info = get_tracking_info(bearer, playback_json)[0]
|
|
78
70
|
|
|
79
71
|
license_url = generate_license_url(bearer, tracking_info)
|
|
80
72
|
mpd_url = get_manifest(tracking_info['video_src'])
|
|
81
73
|
|
|
82
74
|
# Download the episode
|
|
83
|
-
|
|
75
|
+
dash_process = DASH_Downloader(
|
|
84
76
|
cdm_device=get_wvd_path(),
|
|
85
77
|
license_url=license_url,
|
|
86
78
|
mpd_url=mpd_url,
|
|
87
79
|
output_path=os.path.join(mp4_path, mp4_name),
|
|
88
80
|
)
|
|
89
|
-
|
|
81
|
+
dash_process.parse_manifest(custom_headers=get_headers())
|
|
90
82
|
|
|
91
|
-
if
|
|
92
|
-
|
|
83
|
+
if dash_process.download_and_decrypt():
|
|
84
|
+
dash_process.finalize_output()
|
|
93
85
|
|
|
94
86
|
# Get final output path and status
|
|
95
|
-
status =
|
|
87
|
+
status = dash_process.get_status()
|
|
96
88
|
|
|
97
89
|
if status['error'] is not None and status['path']:
|
|
98
|
-
try:
|
|
99
|
-
|
|
90
|
+
try:
|
|
91
|
+
os.remove(status['path'])
|
|
92
|
+
except Exception:
|
|
93
|
+
pass
|
|
100
94
|
|
|
101
95
|
return status['path'], status['stopped']
|
|
102
96
|
|