mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-06 11:35:29 +00:00
Nuovo PC
This commit is contained in:
parent
cdbaae3c01
commit
590533a141
34
README.md
34
README.md
@ -82,18 +82,6 @@ You can change some behaviors by tweaking the configuration file.
|
||||
* **debug**: Enables or disables debug mode.
|
||||
- **Default Value**: `false`
|
||||
|
||||
* **log_file**: The file where logs will be written.
|
||||
- **Default Value**: `app.log`
|
||||
|
||||
* **log_to_file**: Whether to log messages to a file.
|
||||
- **Default Value**: `true`
|
||||
|
||||
* **show_message**: Whether to show messages.
|
||||
- **Default Value**: `false`
|
||||
|
||||
* **clean_console**: Clears the console before the script runs.
|
||||
- **Default Value**: `false`
|
||||
|
||||
* **root_path**: Path where the script will add movies and TV series folders (see [Path Examples](#Path-examples)).
|
||||
- **Default Value**: `Video`
|
||||
|
||||
@ -146,28 +134,6 @@ You can change some behaviors by tweaking the configuration file.
|
||||
* **cleanup_tmp_folder**: Upon final conversion, ensures the removal of all unformatted audio, video tracks, and subtitles from the temporary folder, thereby maintaining cleanliness and efficiency.
|
||||
- **Default Value**: `false`
|
||||
|
||||
* **create_report**: When enabled, saves the name of the series or movie being downloaded along with the date and file size in a CSV file, providing a log of downloaded content.
|
||||
- **Default Value**: `false`
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>M3U8_CONVERSION</strong></summary>
|
||||
|
||||
* **use_codec**: Whether to use a specific codec for processing.
|
||||
- **Default Value**: `false`
|
||||
- **Example Value**: `libx264`
|
||||
|
||||
* **use_gpu**: Whether to use GPU acceleration.
|
||||
- **Default Value**: `false`
|
||||
|
||||
* **default_preset**: The default preset for ffmpeg conversion.
|
||||
- **Default Value**: `ultrafast`
|
||||
- **Example Value**: `slow`
|
||||
|
||||
* **check_output_after_ffmpeg**: Verify if the conversion run by ffmpeg is free from corruption.
|
||||
- **Default Value**: `false`
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
@ -1,14 +1,12 @@
|
||||
# 18.06.24
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
# External libraries
|
||||
import httpx
|
||||
from googlesearch import search
|
||||
|
||||
|
||||
# Internal utilities
|
||||
@ -17,6 +15,46 @@ from Src.Util.console import console
|
||||
from Src.Util._jsonConfig import config_manager
|
||||
|
||||
|
||||
def google_search(query):
|
||||
"""
|
||||
Perform a Google search and return the first result.
|
||||
|
||||
Args:
|
||||
query (str): The search query to execute on Google.
|
||||
|
||||
Returns:
|
||||
str: The first URL result from the search, or None if no result is found.
|
||||
"""
|
||||
# Perform the search on Google and limit to 1 result
|
||||
search_results = search(query, num_results=1)
|
||||
|
||||
# Extract the first result
|
||||
first_result = next(search_results, None)
|
||||
|
||||
if not first_result:
|
||||
console.print("[red]No results found.[/red]")
|
||||
|
||||
return first_result
|
||||
|
||||
def get_final_redirect_url(initial_url):
|
||||
"""
|
||||
Follow redirects from the initial URL and return the final URL after all redirects.
|
||||
|
||||
Args:
|
||||
initial_url (str): The URL to start with and follow redirects.
|
||||
|
||||
Returns:
|
||||
str: The final URL after all redirects are followed.
|
||||
"""
|
||||
|
||||
# Create a client with redirects enabled
|
||||
with httpx.Client(follow_redirects=True) as client:
|
||||
response = client.get(initial_url)
|
||||
|
||||
# Capture the final URL after all redirects
|
||||
final_url = response.url
|
||||
|
||||
return final_url
|
||||
|
||||
def search_domain(site_name: str, base_url: str):
|
||||
"""
|
||||
@ -35,10 +73,27 @@ def search_domain(site_name: str, base_url: str):
|
||||
domain = str(config_manager.get_dict("SITE", site_name)['domain'])
|
||||
console.print(f"[cyan]Test site[white]: [red]{base_url}.{domain}")
|
||||
|
||||
# Test the current domain
|
||||
response_follow = httpx.get(f"{base_url}.{domain}", headers={'user-agent': get_headers()}, timeout=5, follow_redirects=True)
|
||||
#console.print(f"[cyan]Test response site[white]: [red]{response_follow.status_code}")
|
||||
response_follow.raise_for_status()
|
||||
try:
|
||||
|
||||
# Test the current domain
|
||||
response_follow = httpx.get(f"{base_url}.{domain}", headers={'user-agent': get_headers()}, timeout=2)
|
||||
console.print(f"[cyan]Response site[white]: [red]{response_follow.status_code}")
|
||||
response_follow.raise_for_status()
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[cyan]Change domain for site[white]: [red]{base_url}.{domain}, [cyan]error[white]: [red]{e}")
|
||||
|
||||
query = base_url.split("/")[-1]
|
||||
first_url = google_search(query)
|
||||
|
||||
if first_url:
|
||||
final_url = get_final_redirect_url(first_url)
|
||||
console.print(f"\n[bold yellow]Suggestion:[/bold yellow] [white](Experimental)\n"
|
||||
f"[cyan]New final URL[white]: [green]{final_url}")
|
||||
else:
|
||||
console.print("[bold red]No valid URL to follow redirects.[/bold red]")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Ensure the URL is in string format before parsing
|
||||
parsed_url = urlparse(str(response_follow.url))
|
||||
@ -52,5 +107,5 @@ def search_domain(site_name: str, base_url: str):
|
||||
config_manager.write_config()
|
||||
|
||||
# Return config domain
|
||||
console.print(f"[cyan]Use domain: [red]{tld} \n")
|
||||
console.print(f"[cyan]Return domain: [red]{tld} \n")
|
||||
return tld, f"{base_url}.{tld}"
|
||||
|
@ -37,54 +37,39 @@ def dynamic_format_number(n: int) -> str:
|
||||
|
||||
def manage_selection(cmd_insert: str, max_count: int) -> List[int]:
|
||||
"""
|
||||
Manage user selection for seasons to download.
|
||||
Manage user selection for seasons or episodes to download.
|
||||
|
||||
Parameters:
|
||||
- cmd_insert (str): User input for season selection.
|
||||
- max_count (int): Maximum count of seasons available.
|
||||
- cmd_insert (str): User input for selection.
|
||||
- max_count (int): Maximum count available.
|
||||
|
||||
Returns:
|
||||
list_season_select (List[int]): List of selected seasons.
|
||||
list_selection (List[int]): List of selected items.
|
||||
"""
|
||||
list_season_select = []
|
||||
list_selection = []
|
||||
logging.info(f"Command insert: {cmd_insert}, end index: {max_count + 1}")
|
||||
|
||||
# For a single number (e.g., '5')
|
||||
if cmd_insert.isnumeric():
|
||||
list_season_select.append(int(cmd_insert))
|
||||
list_selection.append(int(cmd_insert))
|
||||
|
||||
# For a range (e.g., '[5-12]')
|
||||
elif "[" in cmd_insert:
|
||||
|
||||
# Extract the start and end parts
|
||||
start, end = map(str.strip, cmd_insert[1:-1].split('-'))
|
||||
# For a range (e.g., '5-12')
|
||||
elif "-" in cmd_insert:
|
||||
start, end = map(str.strip, cmd_insert.split('-'))
|
||||
start = int(start)
|
||||
end = int(end) if end.isnumeric() else max_count
|
||||
|
||||
# If end is an integer, convert it
|
||||
try:
|
||||
end = int(end)
|
||||
|
||||
except ValueError:
|
||||
# end remains a string if conversion fails
|
||||
pass
|
||||
|
||||
# Generate the list_season_select based on the type of end
|
||||
if isinstance(end, int):
|
||||
list_season_select = list(range(start, end + 1))
|
||||
|
||||
elif end == "*":
|
||||
list_season_select = list(range(start, max_count + 1))
|
||||
|
||||
else:
|
||||
raise ValueError("Invalid end value")
|
||||
list_selection = list(range(start, end + 1))
|
||||
|
||||
# For all seasons
|
||||
# For all items ('*')
|
||||
elif cmd_insert == "*":
|
||||
list_season_select = list(range(1, max_count+1))
|
||||
list_selection = list(range(1, max_count + 1))
|
||||
|
||||
# Return list of selected seasons)
|
||||
logging.info(f"List return: {list_season_select}")
|
||||
return list_season_select
|
||||
else:
|
||||
raise ValueError("Invalid input format")
|
||||
|
||||
logging.info(f"List return: {list_selection}")
|
||||
return list_selection
|
||||
|
||||
|
||||
def map_episode_title(tv_name: str, number_season: int, episode_number: int, episode_name: str) -> str:
|
||||
@ -111,3 +96,51 @@ def map_episode_title(tv_name: str, number_season: int, episode_number: int, epi
|
||||
|
||||
logging.info(f"Map episode string return: {map_episode_temp}")
|
||||
return map_episode_temp
|
||||
|
||||
|
||||
# --> for season
|
||||
def validate_selection(list_season_select: List[int], seasons_count: int) -> List[int]:
|
||||
"""
|
||||
Validates and adjusts the selected seasons based on the available seasons.
|
||||
|
||||
Parameters:
|
||||
- list_season_select (List[int]): List of seasons selected by the user.
|
||||
- seasons_count (int): Total number of available seasons.
|
||||
|
||||
Returns:
|
||||
- List[int]: Adjusted list of valid season numbers.
|
||||
"""
|
||||
|
||||
# Remove any seasons greater than the available seasons
|
||||
valid_seasons = [season for season in list_season_select if 1 <= season <= seasons_count]
|
||||
|
||||
# If the list is empty, the input was completely invalid
|
||||
if not valid_seasons:
|
||||
print()
|
||||
raise ValueError(f"Invalid selection: The selected seasons are outside the available range (1-{seasons_count}).")
|
||||
|
||||
return valid_seasons
|
||||
|
||||
|
||||
# --> for episode
|
||||
def validate_episode_selection(list_episode_select: List[int], episodes_count: int) -> List[int]:
|
||||
"""
|
||||
Validates and adjusts the selected episodes based on the available episodes.
|
||||
|
||||
Parameters:
|
||||
- list_episode_select (List[int]): List of episodes selected by the user.
|
||||
- episodes_count (int): Total number of available episodes in the season.
|
||||
|
||||
Returns:
|
||||
- List[int]: Adjusted list of valid episode numbers.
|
||||
"""
|
||||
|
||||
# Remove any episodes greater than the available episodes
|
||||
valid_episodes = [episode for episode in list_episode_select if 1 <= episode <= episodes_count]
|
||||
|
||||
# If the list is empty, the input was completely invalid
|
||||
if not valid_episodes:
|
||||
print()
|
||||
raise ValueError(f"Invalid selection: The selected episodes are outside the available range (1-{episodes_count}).")
|
||||
|
||||
return valid_episodes
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
from .site import get_select_title
|
||||
from .Util.get_domain import search_domain
|
||||
from .Util.manage_ep import manage_selection, map_episode_title
|
||||
from .Util.manage_ep import manage_selection, map_episode_title, validate_episode_selection, validate_selection
|
@ -38,8 +38,8 @@ def title_search(title_search: str) -> int:
|
||||
# Find new domain if prev dont work
|
||||
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
|
||||
|
||||
# Send request to search for titles
|
||||
response = httpx.get(f"https://{SITE_NAME}.{domain_to_use}/page/1/?story={unidecode(title_search.replace(' ', '+'))}&do=search&subaction=search&titleonly=3", headers={'user-agent': get_headers()}, follow_redirects=True)
|
||||
# Send request to search for title
|
||||
response = httpx.get(f"https://{SITE_NAME}.{domain_to_use}/?story={unidecode(title_search.replace(' ', '+'))}&do=search&subaction=search&titleonly=3", headers={'user-agent': get_headers()})
|
||||
response.raise_for_status()
|
||||
|
||||
# Create soup and find table
|
||||
|
@ -36,34 +36,30 @@ def title_search(word_to_search: str) -> int:
|
||||
Returns:
|
||||
- int: The number of titles found.
|
||||
"""
|
||||
try:
|
||||
|
||||
# Find new domain if prev dont work
|
||||
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
|
||||
# Find new domain if prev dont work
|
||||
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
|
||||
|
||||
# Send request to search for titles
|
||||
response = httpx.get(f"https://{SITE_NAME}.{domain_to_use}/?s={unidecode(word_to_search)}", headers={'user-agent': get_headers()}, follow_redirects=True)
|
||||
response.raise_for_status()
|
||||
# Send request to search for titles
|
||||
response = httpx.get(f"https://{SITE_NAME}.{domain_to_use}/?s={unidecode(word_to_search)}", headers={'user-agent': get_headers()}, follow_redirects=True)
|
||||
response.raise_for_status()
|
||||
|
||||
# Create soup and find table
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
# Create soup and find table
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
|
||||
for div_title in soup.find_all("div", class_ = "card"):
|
||||
for div_title in soup.find_all("div", class_ = "card"):
|
||||
|
||||
url = div_title.find("h3").find("a").get("href")
|
||||
title = div_title.find("h3").find("a").get_text(strip=True)
|
||||
desc = div_title.find("p").find("strong").text
|
||||
url = div_title.find("h3").find("a").get("href")
|
||||
title = div_title.find("h3").find("a").get_text(strip=True)
|
||||
desc = div_title.find("p").find("strong").text
|
||||
|
||||
title_info = {
|
||||
'name': title,
|
||||
'desc': desc,
|
||||
'url': url
|
||||
}
|
||||
title_info = {
|
||||
'name': title,
|
||||
'desc': desc,
|
||||
'url': url
|
||||
}
|
||||
|
||||
media_search_manager.add_media(title_info)
|
||||
|
||||
except Exception as err:
|
||||
logging.error(f"An error occurred: {err}")
|
||||
media_search_manager.add_media(title_info)
|
||||
|
||||
# Return the number of titles found
|
||||
return media_search_manager.get_length()
|
||||
|
@ -12,7 +12,7 @@ from Src.Util.message import start_message
|
||||
from Src.Util.os import create_folder, can_create_file
|
||||
from Src.Util.table import TVShowManager
|
||||
from Src.Lib.Downloader import MP4_downloader
|
||||
from ..Template import manage_selection, map_episode_title
|
||||
from ..Template import manage_selection, map_episode_title, validate_episode_selection
|
||||
|
||||
|
||||
# Logic class
|
||||
@ -27,6 +27,7 @@ table_show_manager = TVShowManager()
|
||||
video_source = VideoSource()
|
||||
|
||||
|
||||
|
||||
def download_video(scape_info_serie: GetSerieInfo, index_episode_selected: int) -> None:
|
||||
"""
|
||||
Download a single episode video.
|
||||
@ -85,17 +86,19 @@ def download_thread(dict_serie: MediaItem):
|
||||
episodes_count = len(list_dict_episode)
|
||||
|
||||
# Display episodes list and manage user selection
|
||||
last_command = display_episodes_list(list_dict_episode)
|
||||
last_command = display_episodes_list()
|
||||
list_episode_select = manage_selection(last_command, episodes_count)
|
||||
|
||||
# Download selected episodes
|
||||
if len(list_episode_select) == 1 and last_command != "*":
|
||||
download_video(scape_info_serie, list_episode_select[0])
|
||||
try:
|
||||
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
||||
except ValueError as e:
|
||||
console.print(f"[red]{str(e)}")
|
||||
return
|
||||
|
||||
# Download selected episodes
|
||||
for i_episode in list_episode_select:
|
||||
download_video(scape_info_serie, i_episode)
|
||||
|
||||
# Download all other episodes selecter
|
||||
else:
|
||||
for i_episode in list_episode_select:
|
||||
download_video(scape_info_serie, i_episode)
|
||||
|
||||
|
||||
def display_episodes_list(obj_episode_manager) -> str:
|
||||
|
@ -36,46 +36,42 @@ def title_search(word_to_search: str) -> int:
|
||||
Returns:
|
||||
- int: The number of titles found.
|
||||
"""
|
||||
try:
|
||||
|
||||
# Find new domain if prev dont work
|
||||
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
|
||||
# Find new domain if prev dont work
|
||||
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
|
||||
|
||||
# Send request to search for titles
|
||||
response = httpx.get(f"https://{SITE_NAME}.{domain_to_use}/search/?&q={unidecode(word_to_search)}&quick=1&type=videobox_video&nodes=11", headers={'user-agent': get_headers()})
|
||||
response.raise_for_status()
|
||||
# Send request to search for titles
|
||||
response = httpx.get(f"https://{SITE_NAME}.{domain_to_use}/search/?&q={unidecode(word_to_search)}&quick=1&type=videobox_video&nodes=11", headers={'user-agent': get_headers()})
|
||||
response.raise_for_status()
|
||||
|
||||
# Create soup and find table
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
table_content = soup.find('ol', class_="ipsStream")
|
||||
# Create soup and find table
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
table_content = soup.find('ol', class_="ipsStream")
|
||||
|
||||
if table_content:
|
||||
for title_div in table_content.find_all('li', class_='ipsStreamItem'):
|
||||
try:
|
||||
if table_content:
|
||||
for title_div in table_content.find_all('li', class_='ipsStreamItem'):
|
||||
try:
|
||||
|
||||
title_type = title_div.find("p", class_="ipsType_reset").find_all("a")[-1].get_text(strip=True)
|
||||
name = title_div.find("span", class_="ipsContained").find("a").get_text(strip=True)
|
||||
link = title_div.find("span", class_="ipsContained").find("a").get("href")
|
||||
title_type = title_div.find("p", class_="ipsType_reset").find_all("a")[-1].get_text(strip=True)
|
||||
name = title_div.find("span", class_="ipsContained").find("a").get_text(strip=True)
|
||||
link = title_div.find("span", class_="ipsContained").find("a").get("href")
|
||||
|
||||
title_info = {
|
||||
'name': name,
|
||||
'url': link,
|
||||
'type': title_type
|
||||
}
|
||||
title_info = {
|
||||
'name': name,
|
||||
'url': link,
|
||||
'type': title_type
|
||||
}
|
||||
|
||||
media_search_manager.add_media(title_info)
|
||||
media_search_manager.add_media(title_info)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing title div: {e}")
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing title div: {e}")
|
||||
|
||||
return media_search_manager.get_length()
|
||||
|
||||
else:
|
||||
logging.error("No table content found.")
|
||||
return -999
|
||||
|
||||
except Exception as err:
|
||||
logging.error(f"An error occurred: {err}")
|
||||
return media_search_manager.get_length()
|
||||
|
||||
else:
|
||||
logging.error("No table content found.")
|
||||
return -999
|
||||
|
||||
return -9999
|
||||
|
||||
|
@ -11,7 +11,7 @@ from Src.Util.os import create_folder, can_create_file
|
||||
from Src.Util.message import start_message
|
||||
from Src.Util.table import TVShowManager
|
||||
from Src.Lib.Downloader import HLS_Downloader
|
||||
from ..Template import manage_selection, map_episode_title
|
||||
from ..Template import manage_selection, map_episode_title, validate_selection, validate_episode_selection
|
||||
|
||||
|
||||
# Logic class
|
||||
@ -26,6 +26,7 @@ table_show_manager = TVShowManager()
|
||||
video_source = VideoSource()
|
||||
|
||||
|
||||
|
||||
def download_video(scape_info_serie: GetSerieInfo, index_season_selected: int, index_episode_selected: int) -> None:
|
||||
"""
|
||||
Download a single episode video.
|
||||
@ -82,28 +83,28 @@ def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int,
|
||||
list_dict_episode = scape_info_serie.get_episode_number(index_season_selected)
|
||||
episodes_count = len(list_dict_episode)
|
||||
|
||||
# Download all episodes wihtout ask
|
||||
if download_all:
|
||||
for i_episode in range(1, episodes_count+1):
|
||||
|
||||
# Download all episodes without asking
|
||||
for i_episode in range(1, episodes_count + 1):
|
||||
download_video(scape_info_serie, index_season_selected, i_episode)
|
||||
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
||||
|
||||
console.print(f"\n[red]Download [yellow]season: [red]{index_season_selected}.")
|
||||
|
||||
# If not download all episode but a single season
|
||||
if not download_all:
|
||||
else:
|
||||
|
||||
# Display episodes list and manage user selection
|
||||
last_command = display_episodes_list(scape_info_serie.list_episodes)
|
||||
list_episode_select = manage_selection(last_command, episodes_count)
|
||||
|
||||
# Download selected episodes
|
||||
if len(list_episode_select) == 1 and last_command != "*":
|
||||
download_video(scape_info_serie, index_season_selected, list_episode_select[0])
|
||||
try:
|
||||
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
||||
except ValueError as e:
|
||||
console.print(f"[red]{str(e)}")
|
||||
return
|
||||
|
||||
# Download all other episodes selecter
|
||||
else:
|
||||
for i_episode in list_episode_select:
|
||||
download_video(scape_info_serie, index_season_selected, i_episode)
|
||||
# Download selected episodes
|
||||
for i_episode in list_episode_select:
|
||||
download_video(scape_info_serie, index_season_selected, i_episode)
|
||||
|
||||
|
||||
def download_series(dict_serie: MediaItem) -> None:
|
||||
@ -124,24 +125,31 @@ def download_series(dict_serie: MediaItem) -> None:
|
||||
seasons_count = scape_info_serie.get_seasons_number()
|
||||
|
||||
# Prompt user for season selection and download episodes
|
||||
console.print(f"\n[green]Season find: [red]{seasons_count}")
|
||||
index_season_selected = 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")
|
||||
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
||||
index_season_selected = msg.ask(
|
||||
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
||||
"[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"
|
||||
)
|
||||
|
||||
# Manage and validate the selection
|
||||
list_season_select = manage_selection(index_season_selected, seasons_count)
|
||||
|
||||
# Download selected episodes
|
||||
if len(list_season_select) == 1 and index_season_selected != "*":
|
||||
if 1 <= int(index_season_selected) <= seasons_count:
|
||||
download_episode(scape_info_serie, list_season_select[0])
|
||||
try:
|
||||
list_season_select = validate_selection(list_season_select, seasons_count)
|
||||
except ValueError as e:
|
||||
console.print(f"[red]{str(e)}")
|
||||
return
|
||||
|
||||
# Dowload all seasons and episodes
|
||||
elif index_season_selected == "*":
|
||||
for i_season in list_season_select:
|
||||
download_episode(scape_info_serie, i_season, True)
|
||||
# Loop through the selected seasons and download episodes
|
||||
for i_season in list_season_select:
|
||||
if len(list_season_select) > 1 or index_season_selected == "*":
|
||||
|
||||
# Download all other season selecter
|
||||
else:
|
||||
for i_season in list_season_select:
|
||||
download_episode(scape_info_serie, i_season)
|
||||
# Download all episodes if multiple seasons are selected or if '*' is used
|
||||
download_episode(scape_info_serie, i_season, download_all=True)
|
||||
else:
|
||||
|
||||
# Otherwise, let the user select specific episodes for the single season
|
||||
download_episode(scape_info_serie, i_season, download_all=False)
|
||||
|
||||
|
||||
def display_episodes_list(obj_episode_manager) -> str:
|
||||
|
36
Src/Api/mostraguarda/__init__.py
Normal file
36
Src/Api/mostraguarda/__init__.py
Normal file
@ -0,0 +1,36 @@
|
||||
# 26.05.24
|
||||
|
||||
# Internal utilities
|
||||
from Src.Util.console import console, msg
|
||||
|
||||
|
||||
# Logic class
|
||||
from Src.Lib.TMBD import tmdb, Json_film
|
||||
from .film import download_film
|
||||
|
||||
|
||||
# Variable
|
||||
indice = 9
|
||||
_deprecate = False
|
||||
|
||||
|
||||
def search():
|
||||
"""
|
||||
Main function of the application for film and series.
|
||||
"""
|
||||
|
||||
# Make request to site to get content that corrsisponde to that string
|
||||
string_to_search = msg.ask("\n[purple]Insert word to search in all site").strip()
|
||||
movie_id = tmdb.search_movie(string_to_search)
|
||||
|
||||
if movie_id:
|
||||
movie_details: Json_film = tmdb.get_movie_details(tmdb_id=movie_id)
|
||||
|
||||
# Download only film
|
||||
download_film(movie_details)
|
||||
|
||||
else:
|
||||
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
|
||||
|
||||
# Retry
|
||||
search()
|
15
Src/Api/mostraguarda/costant.py
Normal file
15
Src/Api/mostraguarda/costant.py
Normal file
@ -0,0 +1,15 @@
|
||||
# 26.05.24
|
||||
|
||||
import os
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from Src.Util._jsonConfig import config_manager
|
||||
|
||||
|
||||
SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
|
||||
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
||||
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
|
||||
|
||||
MOVIE_FOLDER = "Movie"
|
||||
SERIES_FOLDER= "Serie"
|
74
Src/Api/mostraguarda/film.py
Normal file
74
Src/Api/mostraguarda/film.py
Normal file
@ -0,0 +1,74 @@
|
||||
# 17.09.24
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
|
||||
# External libraries
|
||||
import httpx
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from Src.Util.message import start_message
|
||||
from Src.Util.console import console
|
||||
from Src.Util.os import create_folder, can_create_file, remove_special_characters
|
||||
from Src.Util.headers import get_headers
|
||||
from Src.Lib.Downloader import HLS_Downloader
|
||||
|
||||
|
||||
# Logic class
|
||||
from ..Template.Class.SearchType import MediaItem
|
||||
from ..guardaserie.Player.supervideo import VideoSource
|
||||
from Src.Lib.TMBD import Json_film
|
||||
|
||||
|
||||
# Config
|
||||
from .costant import ROOT_PATH, SITE_NAME, DOMAIN_NOW, MOVIE_FOLDER
|
||||
|
||||
|
||||
|
||||
def download_film(movie_details: Json_film):
|
||||
"""
|
||||
Downloads a film using the provided tmbd id.
|
||||
|
||||
Parameters:
|
||||
- movie_details (Json_film): Class with info about film title.
|
||||
"""
|
||||
|
||||
# Start message and display film information
|
||||
start_message()
|
||||
console.print(f"[yellow]Download: [red]{movie_details.title} \n")
|
||||
|
||||
# Make request to main site
|
||||
url = f"https://{SITE_NAME}.{DOMAIN_NOW}/set-movie-a/{movie_details.imdb_id}"
|
||||
response = httpx.get(url, headers={'User-Agent': get_headers()})
|
||||
response.raise_for_status()
|
||||
|
||||
# Extract supervideo url
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
player_links = soup.find("ul", class_ = "_player-mirrors").find_all("li")
|
||||
supervideo_url = "https:" + player_links[0].get("data-link")
|
||||
|
||||
# Set domain and media ID for the video source
|
||||
video_source = VideoSource()
|
||||
video_source.setup(supervideo_url)
|
||||
|
||||
# Define output path
|
||||
mp4_name = remove_special_characters(movie_details.title) + ".mp4"
|
||||
mp4_path = os.path.join(ROOT_PATH, SITE_NAME, MOVIE_FOLDER, remove_special_characters(movie_details.title))
|
||||
|
||||
# Check if the MP4 file can be created
|
||||
if not can_create_file(mp4_name):
|
||||
logging.error("Invalid mp4 name.")
|
||||
sys.exit(0)
|
||||
|
||||
# Get m3u8 master playlist
|
||||
master_playlist = video_source.get_playlist()
|
||||
|
||||
# Download the film using the m3u8 playlist, and output filename
|
||||
HLS_Downloader(
|
||||
m3u8_playlist = master_playlist,
|
||||
output_filename = os.path.join(mp4_path, mp4_name)
|
||||
).start()
|
@ -10,7 +10,7 @@ from Src.Util.console import console, msg
|
||||
from Src.Util.message import start_message
|
||||
from Src.Util.table import TVShowManager
|
||||
from Src.Lib.Downloader import HLS_Downloader
|
||||
from ..Template import manage_selection, map_episode_title
|
||||
from ..Template import manage_selection, map_episode_title, validate_selection, validate_episode_selection
|
||||
|
||||
|
||||
# Logic class
|
||||
@ -23,7 +23,8 @@ from .costant import ROOT_PATH, SITE_NAME, SERIES_FOLDER
|
||||
video_source = VideoSource()
|
||||
table_show_manager = TVShowManager()
|
||||
|
||||
# download_video
|
||||
|
||||
|
||||
def download_video(tv_name: str, index_season_selected: int, index_episode_selected: int) -> None:
|
||||
"""
|
||||
Download a single episode video.
|
||||
@ -59,54 +60,55 @@ def download_video(tv_name: str, index_season_selected: int, index_episode_selec
|
||||
|
||||
def download_episode(tv_name: str, index_season_selected: int, download_all: bool = False) -> None:
|
||||
"""
|
||||
Download all episodes of a season.
|
||||
Download episodes of a selected season.
|
||||
|
||||
Parameters:
|
||||
- tv_name (str): Name of the TV series.
|
||||
- index_season_selected (int): Index of the selected season.
|
||||
- download_all (bool): Download all seasons episodes
|
||||
- download_all (bool): Download all episodes in the season.
|
||||
"""
|
||||
|
||||
# Clean memory of all episodes and get the number of the season (some dont follow rule of [1,2,3,4,5] but [1,2,3,145,5,6,7]).
|
||||
# Clean memory of all episodes and get the number of the season
|
||||
video_source.obj_episode_manager.clear()
|
||||
season_number = (video_source.obj_season_manager.seasons[index_season_selected-1].number)
|
||||
season_number = video_source.obj_season_manager.seasons[index_season_selected - 1].number
|
||||
|
||||
# Start message and collect information about episodes
|
||||
start_message()
|
||||
video_source.collect_title_season(season_number)
|
||||
episodes_count = video_source.obj_episode_manager.get_length()
|
||||
|
||||
# Download all episodes wihtout ask
|
||||
if download_all:
|
||||
for i_episode in range(1, episodes_count+1):
|
||||
|
||||
# Download all episodes without asking
|
||||
for i_episode in range(1, episodes_count + 1):
|
||||
download_video(tv_name, index_season_selected, i_episode)
|
||||
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
||||
|
||||
console.print(f"\n[red]Download [yellow]season: [red]{index_season_selected}.")
|
||||
|
||||
# If not download all episode but a single season
|
||||
if not download_all:
|
||||
else:
|
||||
|
||||
# Display episodes list and manage user selection
|
||||
last_command = display_episodes_list()
|
||||
list_episode_select = manage_selection(last_command, episodes_count)
|
||||
|
||||
# Download selected episodes
|
||||
if len(list_episode_select) == 1 and last_command != "*":
|
||||
download_video(tv_name, index_season_selected, list_episode_select[0])
|
||||
try:
|
||||
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
||||
except ValueError as e:
|
||||
console.print(f"[red]{str(e)}")
|
||||
return
|
||||
|
||||
# Download all other episodes selecter
|
||||
else:
|
||||
for i_episode in list_episode_select:
|
||||
download_video(tv_name, index_season_selected, i_episode)
|
||||
# Download selected episodes
|
||||
for i_episode in list_episode_select:
|
||||
download_video(tv_name, index_season_selected, i_episode)
|
||||
|
||||
|
||||
def download_series(select_title: MediaItem, domain: str, version: str) -> None:
|
||||
"""
|
||||
Download all episodes of a TV series.
|
||||
Download episodes of a TV series based on user selection.
|
||||
|
||||
Parameters:
|
||||
- version (str): Version of site.
|
||||
- select_title (MediaItem): Selected media item (TV series).
|
||||
- domain (str): Domain from which to download.
|
||||
- version (str): Version of the site.
|
||||
"""
|
||||
|
||||
# Start message and set up video source
|
||||
@ -120,24 +122,31 @@ def download_series(select_title: MediaItem, domain: str, version: str) -> None:
|
||||
seasons_count = video_source.obj_season_manager.get_length()
|
||||
|
||||
# Prompt user for season selection and download episodes
|
||||
console.print(f"\n[green]Season find: [red]{seasons_count}")
|
||||
index_season_selected = 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")
|
||||
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
||||
index_season_selected = msg.ask(
|
||||
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
||||
"[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"
|
||||
)
|
||||
|
||||
# Manage and validate the selection
|
||||
list_season_select = manage_selection(index_season_selected, seasons_count)
|
||||
|
||||
# Download selected episodes
|
||||
if len(list_season_select) == 1 and index_season_selected != "*":
|
||||
if 1 <= int(index_season_selected) <= seasons_count:
|
||||
download_episode(select_title.slug, list_season_select[0])
|
||||
try:
|
||||
list_season_select = validate_selection(list_season_select, seasons_count)
|
||||
except ValueError as e:
|
||||
console.print(f"[red]{str(e)}")
|
||||
return
|
||||
|
||||
# Dowload all seasons and episodes
|
||||
elif index_season_selected == "*":
|
||||
for i_season in list_season_select:
|
||||
download_episode(select_title.slug, i_season, True)
|
||||
# Loop through the selected seasons and download episodes
|
||||
for i_season in list_season_select:
|
||||
if len(list_season_select) > 1 or index_season_selected == "*":
|
||||
|
||||
# Download all other season selecter
|
||||
else:
|
||||
for i_season in list_season_select:
|
||||
download_episode(select_title.slug, i_season)
|
||||
# Download all episodes if multiple seasons are selected or if '*' is used
|
||||
download_episode(select_title.slug, i_season, download_all=True)
|
||||
else:
|
||||
|
||||
# Otherwise, let the user select specific episodes for the single season
|
||||
download_episode(select_title.slug, i_season, download_all=False)
|
||||
|
||||
|
||||
def display_episodes_list() -> str:
|
||||
|
@ -12,7 +12,7 @@ from .serie import download_serie
|
||||
|
||||
# Variable
|
||||
indice = 6
|
||||
_deprecate = False
|
||||
_deprecate = True
|
||||
|
||||
|
||||
def search():
|
||||
|
@ -12,7 +12,7 @@ from Src.Util.message import start_message
|
||||
from Src.Util.os import create_folder, can_create_file
|
||||
from Src.Util.table import TVShowManager
|
||||
from Src.Lib.Downloader import MP4_downloader
|
||||
from ..Template import manage_selection, map_episode_title
|
||||
from ..Template import manage_selection, map_episode_title, validate_selection, validate_episode_selection
|
||||
|
||||
|
||||
# Logic class
|
||||
@ -27,6 +27,7 @@ from .costant import ROOT_PATH, SITE_NAME, SERIES_FOLDER
|
||||
table_show_manager = TVShowManager()
|
||||
|
||||
|
||||
|
||||
def download_video(api_manager: ApiManager, index_season_selected: int, index_episode_selected: int) -> None:
|
||||
"""
|
||||
Download a single episode video.
|
||||
@ -87,34 +88,32 @@ def download_episode(api_manager: ApiManager, index_season_selected: int, downlo
|
||||
season_name = api_manager.obj_season_manager.seasons[index_season_selected-1].name
|
||||
|
||||
# Collect all best episode
|
||||
start_message()
|
||||
api_manager.collect_episode(season_name)
|
||||
episodes_count = api_manager.obj_episode_manager.get_length()
|
||||
|
||||
# Start message
|
||||
start_message()
|
||||
|
||||
# Download all episodes wihtout ask
|
||||
if download_all:
|
||||
for i_episode in range(1, episodes_count+1):
|
||||
|
||||
# Download all episodes without asking
|
||||
for i_episode in range(1, episodes_count + 1):
|
||||
download_video(api_manager, index_season_selected, i_episode)
|
||||
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
||||
|
||||
console.print(f"\n[red]Download [yellow]season: [red]{index_season_selected}.")
|
||||
|
||||
# If not download all episode but a single season
|
||||
if not download_all:
|
||||
else:
|
||||
|
||||
# Display episodes list and manage user selection
|
||||
last_command = display_episodes_list(api_manager)
|
||||
last_command = display_episodes_list()
|
||||
list_episode_select = manage_selection(last_command, episodes_count)
|
||||
|
||||
# Download selected episodes
|
||||
if len(list_episode_select) == 1 and last_command != "*":
|
||||
download_video(api_manager, index_season_selected, list_episode_select[0])
|
||||
try:
|
||||
list_episode_select = validate_episode_selection(list_episode_select, episodes_count)
|
||||
except ValueError as e:
|
||||
console.print(f"[red]{str(e)}")
|
||||
return
|
||||
|
||||
# Download all other episodes selecter
|
||||
else:
|
||||
for i_episode in list_episode_select:
|
||||
download_video(api_manager, index_season_selected, i_episode)
|
||||
# Download selected episodes
|
||||
for i_episode in list_episode_select:
|
||||
download_video(api_manager, index_season_selected, i_episode)
|
||||
|
||||
|
||||
def download_serie(media: MediaItem):
|
||||
@ -137,25 +136,31 @@ def download_serie(media: MediaItem):
|
||||
if seasons_count > 0:
|
||||
|
||||
# Prompt user for season selection and download episodes
|
||||
console.print(f"\n[green]Season find: [red]{seasons_count}")
|
||||
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
||||
index_season_selected = msg.ask(
|
||||
"\n[cyan]Insert season number [yellow](e.g., 1), [red]* [cyan]to download all seasons, "
|
||||
"[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"
|
||||
)
|
||||
|
||||
index_season_selected = 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")
|
||||
# Manage and validate the selection
|
||||
list_season_select = manage_selection(index_season_selected, seasons_count)
|
||||
|
||||
# Download selected episodes
|
||||
if len(list_season_select) == 1 and index_season_selected != "*":
|
||||
if 1 <= int(index_season_selected) <= seasons_count:
|
||||
download_episode(api_manager, list_season_select[0])
|
||||
try:
|
||||
list_season_select = validate_selection(list_season_select, seasons_count)
|
||||
except ValueError as e:
|
||||
console.print(f"[red]{str(e)}")
|
||||
return
|
||||
|
||||
# Dowload all seasons and episodes
|
||||
elif index_season_selected == "*":
|
||||
for i_season in list_season_select:
|
||||
download_episode(api_manager, i_season, True)
|
||||
# Loop through the selected seasons and download episodes
|
||||
for i_season in list_season_select:
|
||||
if len(list_season_select) > 1 or index_season_selected == "*":
|
||||
|
||||
# Download all other season selecter
|
||||
else:
|
||||
for i_season in list_season_select:
|
||||
download_episode(api_manager, i_season)
|
||||
# Download all episodes if multiple seasons are selected or if '*' is used
|
||||
download_episode(api_manager, i_season, download_all=True)
|
||||
else:
|
||||
|
||||
# Otherwise, let the user select specific episodes for the single season
|
||||
download_episode(api_manager, i_season, download_all=False)
|
||||
|
||||
else:
|
||||
|
||||
|
@ -36,12 +36,6 @@ from ...M3U8 import (
|
||||
)
|
||||
from .proxyes import main_test_proxy
|
||||
|
||||
|
||||
# Warning
|
||||
import urllib3
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
|
||||
# Config
|
||||
TQDM_DELAY_WORKER = config_manager.get_float('M3U8_DOWNLOAD', 'tqdm_delay')
|
||||
TQDM_USE_LARGE_BAR = config_manager.get_int('M3U8_DOWNLOAD', 'tqdm_use_large_bar')
|
||||
@ -300,7 +294,7 @@ class M3U8_Segments:
|
||||
progress_bar.update(1)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"Failed to download '{ts_url}', status error: {e}.")
|
||||
console.print(f"Failed download: '{ts_url}' with error: {e}")
|
||||
|
||||
def write_segments_to_file(self):
|
||||
"""
|
||||
|
@ -254,7 +254,7 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat
|
||||
# Add subtitle maps and metadata
|
||||
for idx, subtitle in enumerate(subtitles_list):
|
||||
ffmpeg_cmd += ["-map", f"{idx + 1}:s"]
|
||||
ffmpeg_cmd += ["-metadata:s:s:{}".format(idx), "title={}".format(subtitle['name'])]
|
||||
ffmpeg_cmd += ["-metadata:s:s:{}".format(idx), "title={}".format(subtitle['language'])]
|
||||
|
||||
# Add output Parameters
|
||||
if USE_CODEC:
|
||||
|
2
Src/Lib/TMBD/__init__.py
Normal file
2
Src/Lib/TMBD/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .tmdb import tmdb
|
||||
from .obj_tmbd import Json_film
|
40
Src/Lib/TMBD/obj_tmbd.py
Normal file
40
Src/Lib/TMBD/obj_tmbd.py
Normal file
@ -0,0 +1,40 @@
|
||||
# 17.09.24
|
||||
|
||||
import json
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
|
||||
class Json_film:
|
||||
def __init__(self, data: Dict):
|
||||
self.adult = data.get('adult', False)
|
||||
self.backdrop_path = data.get('backdrop_path')
|
||||
self.budget = data.get('budget', 0)
|
||||
self.homepage = data.get('homepage')
|
||||
self.id = data.get('id', 0)
|
||||
self.imdb_id = data.get('imdb_id')
|
||||
self.origin_country = data.get('origin_country', [])
|
||||
self.original_language = data.get('original_language')
|
||||
self.original_title = data.get('original_title')
|
||||
self.overview = data.get('overview')
|
||||
self.popularity = data.get('popularity', 0.0)
|
||||
self.poster_path = data.get('poster_path')
|
||||
self.release_date = data.get('release_date')
|
||||
self.revenue = data.get('revenue', 0)
|
||||
self.runtime = data.get('runtime', 0)
|
||||
self.status = data.get('status')
|
||||
self.tagline = data.get('tagline')
|
||||
self.title = data.get('title')
|
||||
self.video = data.get('video', False)
|
||||
self.vote_average = data.get('vote_average', 0.0)
|
||||
self.vote_count = data.get('vote_count', 0)
|
||||
|
||||
def __repr__(self):
|
||||
return (f"Film(adult={self.adult}, backdrop_path='{self.backdrop_path}', "
|
||||
f"budget={self.budget}, "
|
||||
f"homepage='{self.homepage}', id={self.id}, "
|
||||
f"imdb_id='{self.imdb_id}', origin_country={self.origin_country}, "
|
||||
f"original_language='{self.original_language}', original_title='{self.original_title}', "
|
||||
f"overview='{self.overview}', popularity={self.popularity}, poster_path='{self.poster_path}', "
|
||||
f"release_date='{self.release_date}', revenue={self.revenue}, runtime={self.runtime}, "
|
||||
f"status='{self.status}', tagline='{self.tagline}', "
|
||||
f"title='{self.title}', video={self.video}, vote_average={self.vote_average}, vote_count={self.vote_count})")
|
@ -1,138 +0,0 @@
|
||||
# 24.08.24
|
||||
|
||||
from typing import Dict
|
||||
|
||||
|
||||
# External libraries
|
||||
import httpx
|
||||
from rich.console import Console
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from Src.Util.table import TVShowManager
|
||||
|
||||
|
||||
# Variable
|
||||
tv_show_manager = TVShowManager()
|
||||
api_key = "a800ed6c93274fb857ea61bd9e7256c5"
|
||||
|
||||
|
||||
class TheMovieDB:
|
||||
def __init__(self, api_key):
|
||||
"""
|
||||
Initialize the class with the API key and TV show manager.
|
||||
|
||||
Parameters:
|
||||
- api_key (str): The API key for authenticating requests to TheMovieDB.
|
||||
- tv_show_manager (TVShowManager): An instance of the TVShowManager for handling TV show items.
|
||||
"""
|
||||
self.api_key = api_key
|
||||
self.base_url = "https://api.themoviedb.org/3"
|
||||
self.console = Console()
|
||||
self.genres = self._fetch_genres()
|
||||
|
||||
def _make_request(self, endpoint, params=None):
|
||||
"""
|
||||
Make a request to the given API endpoint with optional parameters.
|
||||
|
||||
Parameters:
|
||||
- endpoint (str): The API endpoint to hit.
|
||||
- params (dict): Additional parameters for the request.
|
||||
|
||||
Returns:
|
||||
dict: JSON response as a dictionary.
|
||||
"""
|
||||
if params is None:
|
||||
params = {}
|
||||
|
||||
params['api_key'] = self.api_key
|
||||
url = f"{self.base_url}/{endpoint}"
|
||||
response = httpx.get(url, params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def _fetch_genres(self) -> Dict[int, str]:
|
||||
"""
|
||||
Fetch and return the genre names from TheMovieDB.
|
||||
|
||||
Returns:
|
||||
Dict[int, str]: A dictionary mapping genre IDs to genre names.
|
||||
"""
|
||||
genres = self._make_request("genre/tv/list")
|
||||
return {genre['id']: genre['name'] for genre in genres.get('genres', [])}
|
||||
|
||||
def _process_and_add_tv_shows(self, data, columns):
|
||||
"""
|
||||
Process TV show data and add it to the TV show manager.
|
||||
|
||||
Parameters:
|
||||
- data (list): List of dictionaries containing the data to process.
|
||||
- columns (list): A list of tuples, where each tuple contains the column name and the key to fetch the data from the dictionary.
|
||||
"""
|
||||
# Define column styles with colors
|
||||
tv_show_manager = TVShowManager()
|
||||
column_info = {
|
||||
col[0]: {'color': col[2] if len(col) > 2 else 'white'}
|
||||
for col in columns
|
||||
}
|
||||
tv_show_manager.add_column(column_info)
|
||||
|
||||
# Add each item to the TV show manager, including rank
|
||||
for index, item in enumerate(data):
|
||||
|
||||
# Convert genre IDs to genre names
|
||||
genre_names = [self.genres.get(genre_id, 'Unknown') for genre_id in item.get('genre_ids', [])]
|
||||
tv_show = {
|
||||
col[0]: str(item.get(col[1], 'N/A')) if col[1] != 'genre_ids' else ', '.join(genre_names)
|
||||
for col in columns
|
||||
}
|
||||
|
||||
tv_show_manager.add_tv_show(tv_show)
|
||||
|
||||
# Display the processed TV show data
|
||||
tv_show_manager.display_data(tv_show_manager.tv_shows[tv_show_manager.slice_start:tv_show_manager.slice_end])
|
||||
|
||||
def _display_with_title(self, title: str, data, columns):
|
||||
"""
|
||||
Display data with a title.
|
||||
|
||||
Parameters:
|
||||
- title (str): The title to display.
|
||||
- data (list): List of dictionaries containing the data to process.
|
||||
- columns (list): A list of tuples, where each tuple contains the column name and the key to fetch the data from the dictionary.
|
||||
"""
|
||||
self.console.print(f"\n{title}", style="bold underline")
|
||||
self._process_and_add_tv_shows(data, columns)
|
||||
|
||||
def display_trending_tv_shows(self):
|
||||
"""
|
||||
Fetch and display the trending TV shows of the week.
|
||||
"""
|
||||
data = self._make_request("trending/tv/week").get("results", [])
|
||||
columns = [
|
||||
("Title", "name", 'cyan'),
|
||||
("First Air Date", "first_air_date", 'green'),
|
||||
("Popularity", "popularity", 'magenta'),
|
||||
("Genres", "genre_ids", 'blue'),
|
||||
("Origin Country", "origin_country", 'red'),
|
||||
("Vote Average", "vote_average", 'yellow')
|
||||
]
|
||||
self._display_with_title("Trending TV Shows of the Week", data, columns)
|
||||
|
||||
def display_trending_films(self):
|
||||
"""
|
||||
Fetch and display the trending films of the week.
|
||||
"""
|
||||
data = self._make_request("trending/movie/week").get("results", [])
|
||||
columns = [
|
||||
("Title", "title", 'cyan'),
|
||||
("Release Date", "release_date", 'green'),
|
||||
("Popularity", "popularity", 'magenta'),
|
||||
("Genres", "genre_ids", 'blue'),
|
||||
("Vote Average", "vote_average", 'yellow')
|
||||
]
|
||||
self._display_with_title("Trending Films of the Week", data, columns)
|
||||
|
||||
# Output
|
||||
tmdb = TheMovieDB(api_key)
|
346
Src/Lib/TMBD/tmdb.py
Normal file
346
Src/Lib/TMBD/tmdb.py
Normal file
@ -0,0 +1,346 @@
|
||||
# 24.08.24
|
||||
|
||||
import sys
|
||||
from typing import Dict
|
||||
|
||||
|
||||
# External libraries
|
||||
import httpx
|
||||
from rich.console import Console
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from .obj_tmbd import Json_film
|
||||
from Src.Util.table import TVShowManager
|
||||
|
||||
|
||||
# Variable
|
||||
table_show_manager = TVShowManager()
|
||||
api_key = "a800ed6c93274fb857ea61bd9e7256c5"
|
||||
|
||||
|
||||
|
||||
def get_select_title(table_show_manager, generic_obj):
|
||||
"""
|
||||
Display a selection of titles and prompt the user to choose one.
|
||||
|
||||
Returns:
|
||||
dict: The selected media item.
|
||||
"""
|
||||
|
||||
# Set up table for displaying titles
|
||||
table_show_manager.set_slice_end(10)
|
||||
|
||||
# Check if the generic_obj list is empty
|
||||
if not generic_obj:
|
||||
Console.print("\n[red]No media items available.")
|
||||
return None
|
||||
|
||||
# Example of available colors for columns
|
||||
available_colors = ['red', 'magenta', 'yellow', 'cyan', 'green', 'blue', 'white']
|
||||
|
||||
# Retrieve the keys of the first item as column headers
|
||||
first_item = generic_obj[0]
|
||||
column_info = {"Index": {'color': available_colors[0]}} # Always include Index with a fixed color
|
||||
|
||||
# Assign colors to the remaining keys dynamically
|
||||
color_index = 1
|
||||
for key in first_item.keys():
|
||||
if key in ('name', 'date', 'number'): # Custom prioritization of colors
|
||||
if key == 'name':
|
||||
column_info["Name"] = {'color': 'magenta'}
|
||||
elif key == 'date':
|
||||
column_info["Date"] = {'color': 'cyan'}
|
||||
elif key == 'number':
|
||||
column_info["Number"] = {'color': 'yellow'}
|
||||
|
||||
else:
|
||||
column_info[key.capitalize()] = {'color': available_colors[color_index % len(available_colors)]}
|
||||
color_index += 1
|
||||
|
||||
table_show_manager.add_column(column_info)
|
||||
|
||||
# Populate the table with title information
|
||||
for i, item in enumerate(generic_obj):
|
||||
item_dict = {'Index': str(i)}
|
||||
|
||||
for key in item.keys():
|
||||
# Ensure all values are strings for rich add table
|
||||
item_dict[key.capitalize()] = str(item[key])
|
||||
|
||||
table_show_manager.add_tv_show(item_dict)
|
||||
|
||||
# Run the table and handle user input
|
||||
last_command = table_show_manager.run(force_int_input=True, max_int_input=len(generic_obj))
|
||||
table_show_manager.clear()
|
||||
|
||||
# Handle user's quit command
|
||||
if last_command == "q":
|
||||
Console.print("\n[red]Quit [white]...")
|
||||
sys.exit(0)
|
||||
|
||||
# Check if the selected index is within range
|
||||
if 0 <= int(last_command) < len(generic_obj):
|
||||
return generic_obj[int(last_command)]
|
||||
|
||||
else:
|
||||
Console.print("\n[red]Wrong index")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class TheMovieDB:
|
||||
def __init__(self, api_key):
|
||||
"""
|
||||
Initialize the class with the API key.
|
||||
|
||||
Parameters:
|
||||
- api_key (str): The API key for authenticating requests to TheMovieDB.
|
||||
"""
|
||||
self.api_key = api_key
|
||||
self.base_url = "https://api.themoviedb.org/3"
|
||||
self.console = Console()
|
||||
self.genres = self._fetch_genres()
|
||||
|
||||
def _make_request(self, endpoint, params=None):
|
||||
"""
|
||||
Make a request to the given API endpoint with optional parameters.
|
||||
|
||||
Parameters:
|
||||
- endpoint (str): The API endpoint to hit.
|
||||
- params (dict): Additional parameters for the request.
|
||||
|
||||
Returns:
|
||||
dict: JSON response as a dictionary.
|
||||
"""
|
||||
if params is None:
|
||||
params = {}
|
||||
|
||||
params['api_key'] = self.api_key
|
||||
url = f"{self.base_url}/{endpoint}"
|
||||
response = httpx.get(url, params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def _fetch_genres(self) -> Dict[int, str]:
|
||||
"""
|
||||
Fetch and return the genre names from TheMovieDB.
|
||||
|
||||
Returns:
|
||||
Dict[int, str]: A dictionary mapping genre IDs to genre names.
|
||||
"""
|
||||
genres = self._make_request("genre/movie/list")
|
||||
return {genre['id']: genre['name'] for genre in genres.get('genres', [])}
|
||||
|
||||
def _process_and_add_tv_shows(self, data, columns):
|
||||
"""
|
||||
Process TV show data and add it to the TV show manager.
|
||||
|
||||
Parameters:
|
||||
- data (list): List of dictionaries containing the data to process.
|
||||
- columns (list): A list of tuples, where each tuple contains the column name and the key to fetch the data from the dictionary.
|
||||
"""
|
||||
# Define column styles with colors
|
||||
tv_show_manager = TVShowManager()
|
||||
column_info = {
|
||||
col[0]: {'color': col[2] if len(col) > 2 else 'white'}
|
||||
for col in columns
|
||||
}
|
||||
tv_show_manager.add_column(column_info)
|
||||
|
||||
# Add each item to the TV show manager, including rank
|
||||
for index, item in enumerate(data):
|
||||
|
||||
# Convert genre IDs to genre names
|
||||
genre_names = [self.genres.get(genre_id, 'Unknown') for genre_id in item.get('genre_ids', [])]
|
||||
tv_show = {
|
||||
col[0]: str(item.get(col[1], 'N/A')) if col[1] != 'genre_ids' else ', '.join(genre_names)
|
||||
for col in columns
|
||||
}
|
||||
|
||||
tv_show_manager.add_tv_show(tv_show)
|
||||
|
||||
# Display the processed TV show data
|
||||
tv_show_manager.display_data(tv_show_manager.tv_shows[tv_show_manager.slice_start:tv_show_manager.slice_end])
|
||||
|
||||
def _display_with_title(self, title: str, data, columns):
|
||||
"""
|
||||
Display data with a title.
|
||||
|
||||
Parameters:
|
||||
- title (str): The title to display.
|
||||
- data (list): List of dictionaries containing the data to process.
|
||||
- columns (list): A list of tuples, where each tuple contains the column name and the key to fetch the data from the dictionary.
|
||||
"""
|
||||
self.console.print(f"\n{title}", style="bold underline")
|
||||
self._process_and_add_tv_shows(data, columns)
|
||||
|
||||
def display_trending_tv_shows(self):
|
||||
"""
|
||||
Fetch and display the trending TV shows of the week.
|
||||
"""
|
||||
data = self._make_request("trending/tv/week").get("results", [])
|
||||
columns = [
|
||||
("Title", "name", 'cyan'),
|
||||
("First Air Date", "first_air_date", 'green'),
|
||||
("Popularity", "popularity", 'magenta'),
|
||||
("Genres", "genre_ids", 'blue'),
|
||||
("Origin Country", "origin_country", 'red'),
|
||||
("Vote Average", "vote_average", 'yellow')
|
||||
]
|
||||
self._display_with_title("Trending TV Shows of the Week", data, columns)
|
||||
|
||||
def display_trending_films(self):
|
||||
"""
|
||||
Fetch and display the trending films of the week.
|
||||
"""
|
||||
data = self._make_request("trending/movie/week").get("results", [])
|
||||
columns = [
|
||||
("Title", "title", 'cyan'),
|
||||
("Release Date", "release_date", 'green'),
|
||||
("Popularity", "popularity", 'magenta'),
|
||||
("Genres", "genre_ids", 'blue'),
|
||||
("Vote Average", "vote_average", 'yellow')
|
||||
]
|
||||
self._display_with_title("Trending Films of the Week", data, columns)
|
||||
|
||||
def search_movie(self, movie_name: str):
|
||||
"""
|
||||
Search for a movie by name and return its TMDB ID.
|
||||
|
||||
Parameters:
|
||||
- movie_name (str): The name of the movie to search for.
|
||||
|
||||
Returns:
|
||||
int: The TMDB ID of the selected movie.
|
||||
"""
|
||||
generic_obj = []
|
||||
data = self._make_request("search/movie", {"query": movie_name}).get("results", [])
|
||||
if not data:
|
||||
self.console.print("No movies found with that name.", style="red")
|
||||
return None
|
||||
|
||||
self.console.print("\nSelect a Movie:")
|
||||
for i, movie in enumerate(data, start=1):
|
||||
generic_obj.append({
|
||||
'name': movie['title'],
|
||||
'date': movie.get('release_date', 'N/A'),
|
||||
'id': movie['id']
|
||||
})
|
||||
|
||||
choice = get_select_title(table_show_manager, generic_obj)
|
||||
return choice["id"]
|
||||
|
||||
def get_movie_details(self, tmdb_id: int) -> Json_film:
|
||||
"""
|
||||
Fetch and display details for a specific movie using its TMDB ID.
|
||||
|
||||
Parameters:
|
||||
- tmdb_id (int): The TMDB ID of the movie.
|
||||
|
||||
Returns:
|
||||
- Json_film: The movie details as a class.
|
||||
"""
|
||||
movie = self._make_request(f"movie/{tmdb_id}")
|
||||
if not movie:
|
||||
self.console.print("Movie not found.", style="red")
|
||||
return None
|
||||
|
||||
return Json_film(movie)
|
||||
|
||||
def search_tv_show(self, tv_name: str):
|
||||
"""
|
||||
Search for a TV show by name and return its TMDB ID.
|
||||
|
||||
Parameters:
|
||||
- tv_name (str): The name of the TV show to search for.
|
||||
|
||||
Returns:
|
||||
int: The TMDB ID of the selected TV show.
|
||||
"""
|
||||
data = self._make_request("search/tv", {"query": tv_name}).get("results", [])
|
||||
if not data:
|
||||
self.console.print("No TV shows found with that name.", style="red")
|
||||
return None
|
||||
|
||||
self.console.print("\nSelect a TV Show:")
|
||||
for i, show in enumerate(data, start=1):
|
||||
self.console.print(f"{i}. {show['name']} (First Air Date: {show.get('first_air_date', 'N/A')})")
|
||||
|
||||
choice = int(input("Enter the number of the show you want: ")) - 1
|
||||
selected_show = data[choice]
|
||||
return selected_show["id"] # Return the TMDB ID of the selected TV show
|
||||
|
||||
def get_seasons(self, tv_show_id: int):
|
||||
"""
|
||||
Get seasons for a given TV show.
|
||||
|
||||
Parameters:
|
||||
- tv_show_id (int): The TMDB ID of the TV show.
|
||||
|
||||
Returns:
|
||||
int: The season number selected by the user.
|
||||
"""
|
||||
data = self._make_request(f"tv/{tv_show_id}").get("seasons", [])
|
||||
if not data:
|
||||
self.console.print("No seasons found for this TV show.", style="red")
|
||||
return None
|
||||
|
||||
self.console.print("\nSelect a Season:")
|
||||
for i, season in enumerate(data, start=1):
|
||||
self.console.print(f"{i}. {season['name']} (Episodes: {season['episode_count']})")
|
||||
|
||||
choice = int(input("Enter the number of the season you want: ")) - 1
|
||||
return data[choice]["season_number"]
|
||||
|
||||
def get_episodes(self, tv_show_id: int, season_number: int):
|
||||
"""
|
||||
Get episodes for a given season of a TV show.
|
||||
|
||||
Parameters:
|
||||
- tv_show_id (int): The TMDB ID of the TV show.
|
||||
- season_number (int): The season number.
|
||||
|
||||
Returns:
|
||||
dict: The details of the selected episode.
|
||||
"""
|
||||
data = self._make_request(f"tv/{tv_show_id}/season/{season_number}").get("episodes", [])
|
||||
if not data:
|
||||
self.console.print("No episodes found for this season.", style="red")
|
||||
return None
|
||||
|
||||
self.console.print("\nSelect an Episode:")
|
||||
for i, episode in enumerate(data, start=1):
|
||||
self.console.print(f"{i}. {episode['name']} (Air Date: {episode.get('air_date', 'N/A')})")
|
||||
|
||||
choice = int(input("Enter the number of the episode you want: ")) - 1
|
||||
return data[choice]
|
||||
|
||||
|
||||
|
||||
# Output
|
||||
tmdb = TheMovieDB(api_key)
|
||||
|
||||
|
||||
"""
|
||||
Example:
|
||||
|
||||
|
||||
@ movie
|
||||
movie_name = "Interstellar"
|
||||
movie_id = tmdb.search_movie(movie_name)
|
||||
|
||||
if movie_id:
|
||||
movie_details = tmdb.get_movie_details(tmdb_id=movie_id)
|
||||
print(movie_details)
|
||||
|
||||
|
||||
@ series
|
||||
tv_name = "Game of Thrones"
|
||||
tv_show_id = tmdb.search_tv_show(tv_name)
|
||||
if tv_show_id:
|
||||
season_number = tmdb.get_seasons(tv_show_id=tv_show_id)
|
||||
if season_number:
|
||||
episode = tmdb.get_episodes(tv_show_id=tv_show_id, season_number=season_number)
|
||||
print(episode)
|
||||
"""
|
@ -62,3 +62,4 @@ def update():
|
||||
[cyan]Help the repository grow today by leaving a [yellow]star [cyan]and [yellow]sharing [cyan]it with others online!")
|
||||
|
||||
console.print("\n")
|
||||
time.sleep(4)
|
||||
|
@ -1,5 +1,5 @@
|
||||
__title__ = 'StreamingCommunity'
|
||||
__version__ = 'v1.3.0'
|
||||
__version__ = 'v1.4.0'
|
||||
__author__ = 'Lovi-0'
|
||||
__description__ = 'A command-line program to download film'
|
||||
__copyright__ = 'Copyright 2024'
|
||||
|
@ -6,11 +6,11 @@ import logging
|
||||
|
||||
|
||||
# External library
|
||||
import fake_useragent
|
||||
from fake_useragent import UserAgent
|
||||
|
||||
|
||||
# Variable
|
||||
useragent = fake_useragent.UserAgent()
|
||||
ua = UserAgent()
|
||||
|
||||
|
||||
def extract_versions(user_agent):
|
||||
@ -95,7 +95,7 @@ def random_headers(referer: str = None):
|
||||
Returns:
|
||||
dict: Generated HTTP headers.
|
||||
"""
|
||||
user_agent = useragent.random
|
||||
user_agent = ua.random
|
||||
versions = extract_versions(user_agent)
|
||||
platform = get_platform(user_agent)
|
||||
model = get_model(user_agent)
|
||||
@ -145,4 +145,4 @@ def get_headers() -> str:
|
||||
"""
|
||||
|
||||
# Get a random user agent string from the user agent rotator
|
||||
return useragent.random
|
||||
return str(ua.chrome)
|
||||
|
@ -75,11 +75,13 @@ class TVShowManager:
|
||||
|
||||
# Add rows dynamically based on available TV show data
|
||||
for entry in data_slice:
|
||||
row_data = [entry[col_name] for col_name in self.column_info.keys()]
|
||||
# Create row data while handling missing keys
|
||||
row_data = [entry.get(col_name, '') for col_name in self.column_info.keys()]
|
||||
table.add_row(*row_data)
|
||||
|
||||
self.console.print(table) # Use self.console.print instead of print
|
||||
|
||||
|
||||
def run(self, force_int_input: bool = False, max_int_input: int = 0) -> str:
|
||||
"""
|
||||
Run the TV show manager application.
|
||||
@ -105,7 +107,10 @@ class TVShowManager:
|
||||
self.console.print(f"\n\n[yellow][INFO] [green]Press [red]Enter [green]for next page, or [red]'q' [green]to quit.")
|
||||
|
||||
if not force_int_input:
|
||||
key = Prompt.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")
|
||||
key = Prompt.ask(
|
||||
"\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, "
|
||||
"[yellow](e.g., 1-2) [cyan]for a range of media, or [yellow](e.g., 3-*) [cyan]to download from a specific index to the end"
|
||||
)
|
||||
|
||||
else:
|
||||
choices = [str(i) for i in range(0, max_int_input)]
|
||||
@ -130,7 +135,11 @@ class TVShowManager:
|
||||
# Last slice, ensure all remaining items are shown
|
||||
self.console.print(f"\n\n[yellow][INFO] [red]You've reached the end. [green]Press [red]Enter [green]for next page, or [red]'q' [green]to quit.")
|
||||
if not force_int_input:
|
||||
key = Prompt.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")
|
||||
key = Prompt.ask(
|
||||
"\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, "
|
||||
"[yellow](e.g., 1-2) [cyan]for a range of media, or [yellow](e.g., 3-*) [cyan]to download from a specific index to the end"
|
||||
)
|
||||
|
||||
|
||||
else:
|
||||
choices = [str(i) for i in range(0, max_int_input)]
|
||||
|
17
config.json
17
config.json
@ -58,13 +58,11 @@
|
||||
},
|
||||
"SITE": {
|
||||
"streamingcommunity": {
|
||||
"video_workers": 2,
|
||||
"audio_workers": 2,
|
||||
"video_workers": 6,
|
||||
"audio_workers": 6,
|
||||
"domain": "buzz"
|
||||
},
|
||||
"animeunity": {
|
||||
"video_workers": 2,
|
||||
"audio_workers": 2,
|
||||
"domain": "to"
|
||||
},
|
||||
"altadefinizione": {
|
||||
@ -75,7 +73,7 @@
|
||||
"guardaserie": {
|
||||
"video_workers": -1,
|
||||
"audio_workers": -1,
|
||||
"domain": "my"
|
||||
"domain": "dev"
|
||||
},
|
||||
"ddlstreamitaly": {
|
||||
"domain": "co",
|
||||
@ -89,7 +87,7 @@
|
||||
"domain": "ru"
|
||||
},
|
||||
"uhdmovies": {
|
||||
"domain": "dad"
|
||||
"domain": "mov"
|
||||
},
|
||||
"bitsearch": {
|
||||
"domain": "to"
|
||||
@ -98,7 +96,12 @@
|
||||
"domain": "to"
|
||||
},
|
||||
"cb01": {
|
||||
"domain": "software"
|
||||
"domain": "broker"
|
||||
},
|
||||
"mostraguarda": {
|
||||
"video_workers": -1,
|
||||
"audio_workers": -1,
|
||||
"domain": "stream"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
urllib3
|
||||
certifi
|
||||
httpx
|
||||
httpx
|
||||
bs4
|
||||
rich
|
||||
tqdm
|
||||
@ -11,4 +9,5 @@ jsbeautifier
|
||||
seleniumbase
|
||||
fake-useragent
|
||||
qbittorrent-api
|
||||
python-qbittorrent
|
||||
python-qbittorrent
|
||||
googlesearch-python
|
2
run.py
2
run.py
@ -17,7 +17,7 @@ from Src.Util.console import console, msg
|
||||
from Src.Util._jsonConfig import config_manager
|
||||
from Src.Upload.update import update as git_update
|
||||
from Src.Util.os import get_system_summary, create_folder
|
||||
from Src.Lib.TMBD.tmbd import tmdb
|
||||
from Src.Lib.TMBD import tmdb
|
||||
from Src.Util.logger import Logger
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user