StreamingCommunity 2.9.4__py3-none-any.whl → 2.9.6__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/sweetpixel.py +49 -0
- StreamingCommunity/Api/Site/1337xx/__init__.py +26 -12
- StreamingCommunity/Api/Site/1337xx/site.py +5 -4
- StreamingCommunity/Api/Site/1337xx/title.py +4 -6
- StreamingCommunity/Api/Site/altadefinizione/__init__.py +64 -17
- StreamingCommunity/Api/Site/altadefinizione/film.py +32 -2
- StreamingCommunity/Api/Site/altadefinizione/series.py +54 -10
- StreamingCommunity/Api/Site/altadefinizione/site.py +25 -7
- StreamingCommunity/Api/Site/altadefinizione/util/ScrapeSerie.py +2 -2
- StreamingCommunity/Api/Site/animeunity/__init__.py +53 -32
- StreamingCommunity/Api/Site/animeunity/film_serie.py +8 -5
- StreamingCommunity/Api/Site/animeunity/site.py +4 -6
- StreamingCommunity/Api/Site/animeworld/__init__.py +71 -0
- StreamingCommunity/Api/Site/animeworld/serie.py +107 -0
- StreamingCommunity/Api/Site/animeworld/site.py +111 -0
- StreamingCommunity/Api/Site/animeworld/util/ScrapeSerie.py +79 -0
- StreamingCommunity/Api/Site/cb01new/__init__.py +26 -14
- StreamingCommunity/Api/Site/cb01new/film.py +1 -1
- StreamingCommunity/Api/Site/cb01new/site.py +9 -7
- StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +26 -15
- StreamingCommunity/Api/Site/ddlstreamitaly/series.py +2 -2
- StreamingCommunity/Api/Site/ddlstreamitaly/site.py +3 -3
- StreamingCommunity/Api/Site/guardaserie/__init__.py +23 -11
- StreamingCommunity/Api/Site/guardaserie/series.py +1 -1
- StreamingCommunity/Api/Site/guardaserie/site.py +5 -4
- StreamingCommunity/Api/Site/mostraguarda/__init__.py +27 -7
- StreamingCommunity/Api/Site/mostraguarda/film.py +1 -1
- StreamingCommunity/Api/Site/streamingcommunity/__init__.py +50 -27
- StreamingCommunity/Api/Site/streamingcommunity/film.py +1 -1
- StreamingCommunity/Api/Site/streamingcommunity/series.py +6 -3
- StreamingCommunity/Api/Site/streamingcommunity/site.py +7 -3
- StreamingCommunity/Lib/Downloader/HLS/segments.py +2 -4
- StreamingCommunity/Lib/Downloader/MP4/downloader.py +7 -6
- StreamingCommunity/Lib/Downloader/TOR/downloader.py +397 -227
- StreamingCommunity/Lib/FFmpeg/util.py +12 -0
- StreamingCommunity/Lib/M3U8/estimator.py +5 -8
- StreamingCommunity/Upload/version.py +1 -1
- StreamingCommunity/Util/config_json.py +2 -8
- StreamingCommunity/Util/table.py +12 -2
- StreamingCommunity/global_search.py +315 -0
- StreamingCommunity/run.py +39 -5
- {streamingcommunity-2.9.4.dist-info → streamingcommunity-2.9.6.dist-info}/METADATA +42 -15
- streamingcommunity-2.9.6.dist-info/RECORD +85 -0
- {streamingcommunity-2.9.4.dist-info → streamingcommunity-2.9.6.dist-info}/WHEEL +1 -1
- streamingcommunity-2.9.4.dist-info/RECORD +0 -79
- {streamingcommunity-2.9.4.dist-info → streamingcommunity-2.9.6.dist-info}/entry_points.txt +0 -0
- {streamingcommunity-2.9.4.dist-info → streamingcommunity-2.9.6.dist-info/licenses}/LICENSE +0 -0
- {streamingcommunity-2.9.4.dist-info → streamingcommunity-2.9.6.dist-info}/top_level.txt +0 -0
|
@@ -11,11 +11,12 @@ from rich.prompt import Prompt
|
|
|
11
11
|
|
|
12
12
|
# Internal utilities
|
|
13
13
|
from StreamingCommunity.Api.Template import get_select_title
|
|
14
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
15
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
14
16
|
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
# Logic class
|
|
18
|
-
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
19
20
|
from .site import title_search, media_search_manager, table_show_manager
|
|
20
21
|
from .film_serie import download_film, download_series
|
|
21
22
|
|
|
@@ -31,53 +32,73 @@ msg = Prompt()
|
|
|
31
32
|
console = Console()
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
def get_user_input(string_to_search: str = None):
|
|
36
|
+
"""
|
|
37
|
+
Asks the user to input a search term.
|
|
38
|
+
Handles both Telegram bot input and direct input.
|
|
39
|
+
"""
|
|
40
|
+
if string_to_search is None:
|
|
41
|
+
if site_constant.TELEGRAM_BOT:
|
|
42
|
+
bot = get_bot_instance()
|
|
42
43
|
string_to_search = bot.ask(
|
|
43
44
|
"key_search",
|
|
44
|
-
f"
|
|
45
|
+
f"Enter the search term\nor type 'back' to return to the menu: ",
|
|
45
46
|
None
|
|
46
47
|
)
|
|
47
48
|
|
|
48
49
|
if string_to_search == 'back':
|
|
49
|
-
|
|
50
|
-
#
|
|
50
|
+
|
|
51
|
+
# Restart the script
|
|
51
52
|
subprocess.Popen([sys.executable] + sys.argv)
|
|
52
53
|
sys.exit()
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
else:
|
|
55
|
+
string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
56
|
+
|
|
57
|
+
return string_to_search
|
|
58
|
+
|
|
59
|
+
def process_search_result(select_title):
|
|
60
|
+
"""
|
|
61
|
+
Handles the search result and initiates the download for either a film or series.
|
|
62
|
+
"""
|
|
63
|
+
download_series(select_title)
|
|
64
|
+
|
|
65
|
+
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
|
66
|
+
"""
|
|
67
|
+
Main function of the application for search film, series and anime.
|
|
68
|
+
|
|
69
|
+
Parameters:
|
|
70
|
+
string_to_search (str, optional): String to search for
|
|
71
|
+
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
72
|
+
direct_item (dict, optional): Direct item to process (bypass search)
|
|
73
|
+
"""
|
|
74
|
+
if direct_item:
|
|
75
|
+
select_title = MediaItem(**direct_item)
|
|
76
|
+
process_search_result(select_title)
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
# Get the user input for the search term
|
|
80
|
+
string_to_search = get_user_input(string_to_search)
|
|
81
|
+
|
|
82
|
+
# Perform the database search
|
|
59
83
|
len_database = title_search(string_to_search)
|
|
60
84
|
|
|
61
|
-
|
|
62
|
-
if
|
|
85
|
+
##If only the database is needed, return the manager
|
|
86
|
+
if get_onlyDatabase:
|
|
63
87
|
return media_search_manager
|
|
88
|
+
|
|
89
|
+
if site_constant.TELEGRAM_BOT:
|
|
90
|
+
bot = get_bot_instance()
|
|
64
91
|
|
|
65
92
|
if len_database > 0:
|
|
66
|
-
|
|
67
|
-
# Select title from list (type: TV \ Movie \ OVA)
|
|
68
93
|
select_title = get_select_title(table_show_manager, media_search_manager)
|
|
94
|
+
process_search_result(select_title)
|
|
69
95
|
|
|
70
|
-
if select_title.type == 'Movie' or select_title.type == 'OVA':
|
|
71
|
-
download_film(select_title)
|
|
72
|
-
|
|
73
|
-
else:
|
|
74
|
-
download_series(select_title)
|
|
75
|
-
|
|
76
96
|
else:
|
|
77
|
-
if site_constant.TELEGRAM_BOT:
|
|
78
|
-
bot.send_message(f"Nessun risultato trovato riprova", None)
|
|
79
|
-
|
|
80
97
|
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
81
98
|
|
|
82
|
-
|
|
99
|
+
if site_constant.TELEGRAM_BOT:
|
|
100
|
+
bot.send_message(f"No results found, please try again", None)
|
|
101
|
+
|
|
102
|
+
# If no results are found, ask again
|
|
103
|
+
string_to_search = get_user_input()
|
|
83
104
|
search()
|
|
@@ -55,8 +55,7 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so
|
|
|
55
55
|
if obj_episode is not None:
|
|
56
56
|
|
|
57
57
|
start_message()
|
|
58
|
-
console.print(f"[yellow]Download:
|
|
59
|
-
console.print("[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n")
|
|
58
|
+
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] ([cyan]E{obj_episode.number}[/cyan]) \n")
|
|
60
59
|
|
|
61
60
|
if site_constant.TELEGRAM_BOT:
|
|
62
61
|
bot.send_message(f"Download in corso:\nTitolo:{scrape_serie.series_name}\nEpisodio: {obj_episode.number}", None)
|
|
@@ -70,7 +69,7 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so
|
|
|
70
69
|
video_source.get_embed(obj_episode.id)
|
|
71
70
|
|
|
72
71
|
# Create output path
|
|
73
|
-
|
|
72
|
+
mp4_name = f"{scrape_serie.series_name}_EP_{dynamic_format_number(str(obj_episode.number))}.mp4"
|
|
74
73
|
|
|
75
74
|
if scrape_serie.is_series:
|
|
76
75
|
mp4_path = os_manager.get_sanitize_path(os.path.join(site_constant.ANIME_FOLDER, scrape_serie.series_name))
|
|
@@ -84,7 +83,7 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so
|
|
|
84
83
|
# Start downloading
|
|
85
84
|
path, kill_handler = MP4_downloader(
|
|
86
85
|
url=str(video_source.src_mp4).strip(),
|
|
87
|
-
path=os.path.join(mp4_path,
|
|
86
|
+
path=os.path.join(mp4_path, mp4_name)
|
|
88
87
|
)
|
|
89
88
|
|
|
90
89
|
return path, kill_handler
|
|
@@ -123,7 +122,11 @@ def download_series(select_title: MediaItem):
|
|
|
123
122
|
|
|
124
123
|
last_command = bot.ask(
|
|
125
124
|
"select_title",
|
|
126
|
-
|
|
125
|
+
"Menu di selezione degli episodi: \n\n"
|
|
126
|
+
"- Inserisci il numero dell'episodio (ad esempio, 1)\n"
|
|
127
|
+
"- Inserisci * per scaricare tutti gli episodi\n"
|
|
128
|
+
"- Inserisci un intervallo di episodi (ad esempio, 1-2) per scaricare da un episodio all'altro\n"
|
|
129
|
+
"- Inserisci (ad esempio, 3-*) per scaricare dall'episodio specificato fino alla fine della serie",
|
|
127
130
|
None
|
|
128
131
|
)
|
|
129
132
|
|
|
@@ -77,20 +77,18 @@ def get_real_title(record):
|
|
|
77
77
|
"""
|
|
78
78
|
if record['title_eng'] is not None:
|
|
79
79
|
return record['title_eng']
|
|
80
|
-
|
|
81
80
|
elif record['title'] is not None:
|
|
82
81
|
return record['title']
|
|
83
|
-
|
|
84
82
|
else:
|
|
85
83
|
return record['title_it']
|
|
86
84
|
|
|
87
85
|
|
|
88
|
-
def title_search(
|
|
86
|
+
def title_search(query: str) -> int:
|
|
89
87
|
"""
|
|
90
|
-
Function to perform an anime search using a provided
|
|
88
|
+
Function to perform an anime search using a provided query.
|
|
91
89
|
|
|
92
90
|
Parameters:
|
|
93
|
-
-
|
|
91
|
+
- query (str): The query to search for.
|
|
94
92
|
|
|
95
93
|
Returns:
|
|
96
94
|
- int: A number containing the length of media search manager.
|
|
@@ -108,7 +106,7 @@ def title_search(title: str) -> int:
|
|
|
108
106
|
'user-agent': get_userAgent(),
|
|
109
107
|
'x-csrf-token': data.get('csrf_token')
|
|
110
108
|
}
|
|
111
|
-
json_data = {'title':
|
|
109
|
+
json_data = {'title': query}
|
|
112
110
|
|
|
113
111
|
# Send a POST request to the API endpoint for live search
|
|
114
112
|
try:
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# 21.03.25
|
|
2
|
+
|
|
3
|
+
# External library
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.prompt import Prompt
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Internal utilities
|
|
9
|
+
from StreamingCommunity.Api.Template import get_select_title
|
|
10
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
11
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Logic class
|
|
15
|
+
from .site import title_search, media_search_manager, table_show_manager
|
|
16
|
+
from .serie import download_series
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Variable
|
|
20
|
+
indice = 8
|
|
21
|
+
_useFor = "anime"
|
|
22
|
+
_deprecate = False
|
|
23
|
+
_priority = 2
|
|
24
|
+
_engineDownload = "mp4"
|
|
25
|
+
|
|
26
|
+
msg = Prompt()
|
|
27
|
+
console = Console()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def process_search_result(select_title):
|
|
32
|
+
"""
|
|
33
|
+
Handles the search result and initiates the download for either a film or series.
|
|
34
|
+
"""
|
|
35
|
+
if select_title.type == "TV":
|
|
36
|
+
download_series(select_title)
|
|
37
|
+
|
|
38
|
+
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
|
39
|
+
"""
|
|
40
|
+
Main function of the application for search film, series and anime.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
string_to_search (str, optional): String to search for
|
|
44
|
+
get_onlyDatabase (bool, optional): If True, return only the database object
|
|
45
|
+
direct_item (dict, optional): Direct item to process (bypass search)
|
|
46
|
+
"""
|
|
47
|
+
if direct_item:
|
|
48
|
+
select_title = MediaItem(**direct_item)
|
|
49
|
+
process_search_result(select_title)
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
# Get the user input for the search term
|
|
53
|
+
string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
54
|
+
|
|
55
|
+
# Perform the database search
|
|
56
|
+
len_database = title_search(string_to_search)
|
|
57
|
+
|
|
58
|
+
##If only the database is needed, return the manager
|
|
59
|
+
if get_onlyDatabase:
|
|
60
|
+
return media_search_manager
|
|
61
|
+
|
|
62
|
+
if len_database > 0:
|
|
63
|
+
select_title = get_select_title(table_show_manager, media_search_manager)
|
|
64
|
+
process_search_result(select_title)
|
|
65
|
+
|
|
66
|
+
else:
|
|
67
|
+
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
68
|
+
|
|
69
|
+
# If no results are found, ask again
|
|
70
|
+
string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
71
|
+
search()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# 11.03.24
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Tuple
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# External library
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.prompt import Prompt
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Internal utilities
|
|
14
|
+
from StreamingCommunity.Util.os import os_manager
|
|
15
|
+
from StreamingCommunity.Util.message import start_message
|
|
16
|
+
from StreamingCommunity.Lib.Downloader import MP4_downloader
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Logic class
|
|
20
|
+
from .util.ScrapeSerie import ScrapSerie
|
|
21
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
22
|
+
from StreamingCommunity.Api.Template.Util import manage_selection, dynamic_format_number, map_episode_title
|
|
23
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Player
|
|
27
|
+
from StreamingCommunity.Api.Player.sweetpixel import AnimeWorldPlayer
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Variable
|
|
31
|
+
console = Console()
|
|
32
|
+
msg = Prompt()
|
|
33
|
+
KILL_HANDLER = bool(False)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def download_episode(index_select: int, scrape_serie: ScrapSerie, episodes) -> Tuple[str,bool]:
|
|
38
|
+
"""
|
|
39
|
+
Downloads the selected episode.
|
|
40
|
+
|
|
41
|
+
Parameters:
|
|
42
|
+
- index_select (int): Index of the episode to download.
|
|
43
|
+
|
|
44
|
+
Return:
|
|
45
|
+
- str: output path
|
|
46
|
+
- bool: kill handler status
|
|
47
|
+
"""
|
|
48
|
+
start_message()
|
|
49
|
+
|
|
50
|
+
# Get information about the selected episode
|
|
51
|
+
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] ([cyan]E{index_select+1}[/cyan]) \n")
|
|
52
|
+
|
|
53
|
+
# Define filename and path for the downloaded video
|
|
54
|
+
mp4_name = f"{scrape_serie.get_name()}_EP_{dynamic_format_number(str(index_select+1))}.mp4"
|
|
55
|
+
mp4_path = os.path.join(site_constant.ANIME_FOLDER, scrape_serie.get_name())
|
|
56
|
+
|
|
57
|
+
# Create output folder
|
|
58
|
+
os_manager.create_path(mp4_path)
|
|
59
|
+
|
|
60
|
+
# Collect mp4 link
|
|
61
|
+
video_source = AnimeWorldPlayer(site_constant.FULL_URL, episodes[index_select], scrape_serie.session_id, scrape_serie.csrf_token)
|
|
62
|
+
mp4_link = video_source.get_download_link()
|
|
63
|
+
|
|
64
|
+
# Start downloading
|
|
65
|
+
path, kill_handler = MP4_downloader(
|
|
66
|
+
url=str(mp4_link).strip(),
|
|
67
|
+
path=os.path.join(mp4_path, mp4_name)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return path, kill_handler
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def download_series(select_title: MediaItem):
|
|
74
|
+
"""
|
|
75
|
+
Function to download episodes of a TV series.
|
|
76
|
+
|
|
77
|
+
Parameters:
|
|
78
|
+
- tv_id (int): The ID of the TV series.
|
|
79
|
+
- tv_name (str): The name of the TV series.
|
|
80
|
+
"""
|
|
81
|
+
start_message()
|
|
82
|
+
|
|
83
|
+
scrape_serie = ScrapSerie(select_title.url, site_constant.FULL_URL)
|
|
84
|
+
|
|
85
|
+
# Get the count of episodes for the TV series
|
|
86
|
+
episodes = scrape_serie.get_episodes()
|
|
87
|
+
episoded_count = len(episodes)
|
|
88
|
+
console.print(f"[cyan]Episodes find: [red]{episoded_count}")
|
|
89
|
+
|
|
90
|
+
# Prompt user to select an episode index
|
|
91
|
+
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")
|
|
92
|
+
|
|
93
|
+
# Manage user selection
|
|
94
|
+
list_episode_select = manage_selection(last_command, episoded_count)
|
|
95
|
+
|
|
96
|
+
# Download selected episodes
|
|
97
|
+
if len(list_episode_select) == 1 and last_command != "*":
|
|
98
|
+
path, _ = download_episode(list_episode_select[0]-1, scrape_serie, episodes)
|
|
99
|
+
return path
|
|
100
|
+
|
|
101
|
+
# Download all other episodes selecter
|
|
102
|
+
else:
|
|
103
|
+
kill_handler = False
|
|
104
|
+
for i_episode in list_episode_select:
|
|
105
|
+
if kill_handler:
|
|
106
|
+
break
|
|
107
|
+
_, kill_handler = download_episode(i_episode-1, scrape_serie, episodes)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# 21.03.25
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
# External libraries
|
|
6
|
+
import httpx
|
|
7
|
+
from bs4 import BeautifulSoup
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Internal utilities
|
|
12
|
+
from StreamingCommunity.Util.config_json import config_manager
|
|
13
|
+
from StreamingCommunity.Util.headers import get_userAgent, get_headers
|
|
14
|
+
from StreamingCommunity.Util.table import TVShowManager
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Logic class
|
|
18
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
19
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Variable
|
|
23
|
+
console = Console()
|
|
24
|
+
media_search_manager = MediaManager()
|
|
25
|
+
table_show_manager = TVShowManager()
|
|
26
|
+
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_session_and_csrf() -> dict:
|
|
30
|
+
"""
|
|
31
|
+
Get the session ID and CSRF token from the website's cookies and HTML meta data.
|
|
32
|
+
"""
|
|
33
|
+
# Send an initial GET request to the website
|
|
34
|
+
response = httpx.get(site_constant.FULL_URL, headers=get_headers())
|
|
35
|
+
|
|
36
|
+
# Extract the sessionId from the cookies
|
|
37
|
+
session_id = response.cookies.get('sessionId')
|
|
38
|
+
logging.info(f"Session ID: {session_id}")
|
|
39
|
+
|
|
40
|
+
# Use BeautifulSoup to parse the HTML and extract the CSRF-Token
|
|
41
|
+
soup = BeautifulSoup(response.text, 'html.parser')
|
|
42
|
+
|
|
43
|
+
# Try to find the CSRF token in a meta tag or hidden input
|
|
44
|
+
csrf_token = None
|
|
45
|
+
meta_tag = soup.find('meta', {'name': 'csrf-token'})
|
|
46
|
+
if meta_tag:
|
|
47
|
+
csrf_token = meta_tag.get('content')
|
|
48
|
+
|
|
49
|
+
# If it's not in the meta tag, check for hidden input fields
|
|
50
|
+
if not csrf_token:
|
|
51
|
+
input_tag = soup.find('input', {'name': '_csrf'})
|
|
52
|
+
if input_tag:
|
|
53
|
+
csrf_token = input_tag.get('value')
|
|
54
|
+
|
|
55
|
+
logging.info(f"CSRF Token: {csrf_token}")
|
|
56
|
+
return session_id, csrf_token
|
|
57
|
+
|
|
58
|
+
def title_search(query: str) -> int:
|
|
59
|
+
"""
|
|
60
|
+
Function to perform an anime search using a provided title.
|
|
61
|
+
|
|
62
|
+
Parameters:
|
|
63
|
+
- query (str): The query to search for.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
- int: A number containing the length of media search manager.
|
|
67
|
+
"""
|
|
68
|
+
search_url = f"{site_constant.FULL_URL}/search?keyword={query}"
|
|
69
|
+
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
|
70
|
+
|
|
71
|
+
# Make the GET request
|
|
72
|
+
try:
|
|
73
|
+
response = httpx.get(search_url, headers={'User-Agent': get_userAgent()})
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
|
77
|
+
return 0
|
|
78
|
+
|
|
79
|
+
# Create soup istance
|
|
80
|
+
soup = BeautifulSoup(response.text, 'html.parser')
|
|
81
|
+
|
|
82
|
+
# Collect data from soup
|
|
83
|
+
for element in soup.find_all('a', class_='poster'):
|
|
84
|
+
try:
|
|
85
|
+
title = element.find('img').get('alt')
|
|
86
|
+
url = f"{site_constant.FULL_URL}{element.get('href')}"
|
|
87
|
+
status_div = element.find('div', class_='status')
|
|
88
|
+
is_dubbed = False
|
|
89
|
+
anime_type = 'TV'
|
|
90
|
+
|
|
91
|
+
if status_div:
|
|
92
|
+
if status_div.find('div', class_='dub'):
|
|
93
|
+
is_dubbed = True
|
|
94
|
+
|
|
95
|
+
if status_div.find('div', class_='movie'):
|
|
96
|
+
anime_type = 'Movie'
|
|
97
|
+
elif status_div.find('div', class_='ona'):
|
|
98
|
+
anime_type = 'ONA'
|
|
99
|
+
|
|
100
|
+
media_search_manager.add_media({
|
|
101
|
+
'name': title,
|
|
102
|
+
'type': anime_type,
|
|
103
|
+
'DUB': is_dubbed,
|
|
104
|
+
'url': url
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
print(f"Error parsing a film entry: {e}")
|
|
109
|
+
|
|
110
|
+
# Return the length of media search manager
|
|
111
|
+
return media_search_manager.get_length()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# 21.03.25
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# External libraries
|
|
5
|
+
import httpx
|
|
6
|
+
from bs4 import BeautifulSoup
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Internal utilities
|
|
10
|
+
from StreamingCommunity.Util.headers import get_userAgent
|
|
11
|
+
from StreamingCommunity.Util.config_json import config_manager
|
|
12
|
+
from StreamingCommunity.Util.os import os_manager
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Player
|
|
16
|
+
from ..site import get_session_and_csrf
|
|
17
|
+
from StreamingCommunity.Api.Player.sweetpixel import AnimeWorldPlayer
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Variable
|
|
21
|
+
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ScrapSerie:
|
|
25
|
+
def __init__(self, url, full_url):
|
|
26
|
+
"""Initialize the ScrapSerie object with the provided URL and setup the HTTP client."""
|
|
27
|
+
self.url = url
|
|
28
|
+
self.link = httpx.URL(url).path
|
|
29
|
+
self.session_id, self.csrf_token = get_session_and_csrf()
|
|
30
|
+
self.client = httpx.Client(
|
|
31
|
+
cookies={"sessionId": self.session_id},
|
|
32
|
+
headers={"User-Agent": get_userAgent(), "csrf-token": self.csrf_token},
|
|
33
|
+
base_url=full_url
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
self.response = self.client.get(self.link, timeout=max_timeout, follow_redirects=True)
|
|
38
|
+
self.response.raise_for_status()
|
|
39
|
+
|
|
40
|
+
except:
|
|
41
|
+
raise Exception(f"Failed to retrieve anime page.")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_name(self):
|
|
45
|
+
"""Extract and return the name of the anime series."""
|
|
46
|
+
soup = BeautifulSoup(self.response.content, "html.parser")
|
|
47
|
+
return os_manager.get_sanitize_file(soup.find("h1", {"id": "anime-title"}).get_text(strip=True))
|
|
48
|
+
|
|
49
|
+
def get_episodes(self, nums=None):
|
|
50
|
+
"""Fetch and return the list of episodes, optionally filtering by specific episode numbers."""
|
|
51
|
+
soup = BeautifulSoup(self.response.content, "html.parser")
|
|
52
|
+
|
|
53
|
+
raw_eps = {}
|
|
54
|
+
for data in soup.select('li.episode > a'):
|
|
55
|
+
epNum = data.get('data-episode-num')
|
|
56
|
+
epID = data.get('data-episode-id')
|
|
57
|
+
|
|
58
|
+
if nums and epNum not in nums:
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
if epID not in raw_eps:
|
|
62
|
+
raw_eps[epID] = {
|
|
63
|
+
'number': epNum,
|
|
64
|
+
'link': f"/api/download/{epID}"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
episodes = [episode_data for episode_data in raw_eps.values()]
|
|
68
|
+
return episodes
|
|
69
|
+
|
|
70
|
+
def get_episode(self, index):
|
|
71
|
+
"""Fetch a specific episode based on the index, and return an AnimeWorldPlayer instance."""
|
|
72
|
+
episodes = self.get_episodes()
|
|
73
|
+
|
|
74
|
+
if 0 <= index < len(episodes):
|
|
75
|
+
episode_data = episodes[index]
|
|
76
|
+
return AnimeWorldPlayer(episode_data, self.session_id, self.csrf_token)
|
|
77
|
+
|
|
78
|
+
else:
|
|
79
|
+
raise IndexError("Episode index out of range")
|
|
@@ -10,10 +10,11 @@ from rich.prompt import Prompt
|
|
|
10
10
|
|
|
11
11
|
# Internal utilities
|
|
12
12
|
from StreamingCommunity.Api.Template import get_select_title
|
|
13
|
+
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
14
|
+
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
# Logic class
|
|
16
|
-
from StreamingCommunity.Api.Template.config_loader import site_constant
|
|
17
18
|
from .site import title_search, media_search_manager, table_show_manager
|
|
18
19
|
from .film import download_film
|
|
19
20
|
|
|
@@ -29,10 +30,26 @@ msg = Prompt()
|
|
|
29
30
|
console = Console()
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
def
|
|
33
|
+
def process_search_result(select_title):
|
|
34
|
+
"""
|
|
35
|
+
Handles the search result and initiates the download for either a film or series.
|
|
36
|
+
"""
|
|
37
|
+
# !!! ADD TYPE DONT WORK FOR SERIE
|
|
38
|
+
download_film(select_title)
|
|
39
|
+
|
|
40
|
+
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
|
|
33
41
|
"""
|
|
34
|
-
Main function of the application for film and
|
|
42
|
+
Main function of the application for search film, series and anime.
|
|
43
|
+
|
|
44
|
+
Parameters:
|
|
45
|
+
string_to_search (str, optional): String to search for
|
|
46
|
+
get_onylDatabase (bool, optional): If True, return only the database object
|
|
47
|
+
direct_item (dict, optional): Direct item to process (bypass search)
|
|
35
48
|
"""
|
|
49
|
+
if direct_item:
|
|
50
|
+
select_title = MediaItem(**direct_item)
|
|
51
|
+
process_search_result(select_title)
|
|
52
|
+
return
|
|
36
53
|
|
|
37
54
|
if string_to_search is None:
|
|
38
55
|
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
|
|
@@ -40,21 +57,16 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
|
|
|
40
57
|
# Search on database
|
|
41
58
|
len_database = title_search(quote_plus(string_to_search))
|
|
42
59
|
|
|
43
|
-
|
|
44
|
-
if
|
|
60
|
+
## If only the database is needed, return the manager
|
|
61
|
+
if get_onlyDatabase:
|
|
45
62
|
return media_search_manager
|
|
46
63
|
|
|
47
64
|
if len_database > 0:
|
|
48
|
-
|
|
49
|
-
# Select title from list
|
|
50
65
|
select_title = get_select_title(table_show_manager, media_search_manager)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
download_film(select_title)
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
process_search_result(select_title)
|
|
67
|
+
|
|
56
68
|
else:
|
|
57
|
-
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
58
69
|
|
|
59
|
-
#
|
|
70
|
+
# If no results are found, ask again
|
|
71
|
+
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
|
60
72
|
search()
|
|
@@ -37,7 +37,7 @@ def download_film(select_title: MediaItem) -> str:
|
|
|
37
37
|
- str: output path
|
|
38
38
|
"""
|
|
39
39
|
start_message()
|
|
40
|
-
console.print(f"[yellow]Download:
|
|
40
|
+
console.print(f"[bold yellow]Download:[/bold yellow] [red]{site_constant.SITE_NAME}[/red] → [cyan]{select_title.name}[/cyan] \n")
|
|
41
41
|
|
|
42
42
|
# Setup api manger
|
|
43
43
|
video_source = VideoSource(select_title.url)
|