Bump v2.9.5

This commit is contained in:
None 2025-03-17 16:15:01 +01:00
parent 3e5b126c07
commit 64e382c32e
22 changed files with 685 additions and 296 deletions

View File

@ -45,6 +45,7 @@
- 📥 [Download](#m3u8_download-settings)
- 🔍 [Parser](#m3u8_parser-settings)
- 📝 [Command](#command)
- 🔍 [Global search](#global-search)
- 💻 [Examples of terminal](#examples-of-terminal-usage)
- 🔧 [Manual domain configuration](#update-domains)
- 🐳 [Docker](#docker)
@ -637,19 +638,41 @@ Note: If `use_api` is set to `false` and no `domains.json` file is found, the sc
#### 💡 Adding a New Site to the Legacy API
If you want to add a new site to the legacy API, just message me on the Discord server, and I'll add it!
# COMMAND
# Global Search
- Download a specific season by entering its number.
* **Example:** `1` will download *Season 1* only.
You can now search across multiple streaming sites at once using the Global Search feature. This allows you to find content more efficiently without having to search each site individually.
- Use the wildcard `*` to download every available season.
* **Example:** `*` will download all seasons in the series.
## Using Global Search
- Specify a range of seasons using a hyphen `-`.
* **Example:** `1-2` will download *Seasons 1 and 2*.
The Global Search feature provides a unified interface to search across all supported sites:
- Enter a season number followed by `-*` to download from that season to the end.
* **Example:** `3-*` will download from *Season 3* to the final season.
## Search Options
When using Global Search, you have three ways to select which sites to search:
1. **Search all sites** - Searches across all available streaming sites
2. **Search by category** - Group sites by their categories (movies, series, anime, etc.)
3. **Select specific sites** - Choose individual sites to include in your search
## Navigation and Selection
After performing a search:
1. Results are displayed in a consolidated table showing:
- Title
- Media type (movie, TV series, etc.)
- Source site
2. Select an item by number to view details or download
3. The system will automatically use the appropriate site's API to handle the download
## Command Line Arguments
The Global Search can be configured from the command line:
- `--global` - Perform a global search across multiple sites.
- `-s`, `--search` - Specify the search terms.
# Examples of terminal usage
@ -662,6 +685,9 @@ python test_run.py --specific_list_audio ita,eng --specific_list_subtitles eng,s
# Keep console open after download
python test_run.py --not_close true
# Use global search
python test_run.py --global -s "cars"
```
# Docker

View File

@ -10,10 +10,11 @@ from rich.prompt import Prompt
# Internal utilities
from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
# Logic class
from StreamingCommunity.Api.Template.config_loader import site_constant
from .site import title_search, media_search_manager, table_show_manager
from .title import download_title
@ -29,30 +30,43 @@ console = Console()
msg = Prompt()
def search(string_to_search: str = None, get_onylDatabase: bool = False):
def process_search_result(select_title):
"""
Main function of the application for film and series.
Handles the search result and initiates the download for either a film or series.
"""
download_title(select_title)
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
"""
Main function of the application for search film, series and anime.
Parameters:
string_to_search (str, optional): String to search for
get_onylDatabase (bool, optional): If True, return only the database object
direct_item (dict, optional): Direct item to process (bypass search)
"""
if direct_item:
select_title = MediaItem(**direct_item)
process_search_result(select_title)
return
if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
# Search on database
# Perform the database search
len_database = title_search(quote_plus(string_to_search))
# Return list of elements
if get_onylDatabase:
# If only the database is needed, return the manager
if get_onlyDatabase:
return media_search_manager
if len_database > 0:
# Select title from list
select_title = get_select_title(table_show_manager, media_search_manager)
# Download title
download_title(select_title)
else:
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
# Retry
# If no results are found, ask again
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
search()

View File

@ -62,7 +62,8 @@ def title_search(word_to_search: str) -> int:
'seader': tr.find_all("td")[-5].get_text(strip=True),
'leacher': tr.find_all("td")[-4].get_text(strip=True),
'date': tr.find_all("td")[-3].get_text(strip=True).replace("'", ""),
'size': tr.find_all("td")[-2].get_text(strip=True)
'size': tr.find_all("td")[-2].get_text(strip=True),
'type': 'torrent'
}
media_search_manager.add_media(title_info)

View File

@ -2,19 +2,22 @@
import sys
import subprocess
from urllib.parse import quote_plus
# External library
from rich.console import Console
from rich.prompt import Prompt
# Internal utilities
from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
from .site import title_search, table_show_manager, media_search_manager
from .film import download_film
from .series import download_series
@ -30,51 +33,76 @@ msg = Prompt()
console = Console()
def search(string_to_search: str = None, get_onylDatabase: bool = False):
def get_user_input(string_to_search: str = None):
"""
Main function of the application for film and series.
Asks the user to input a search term.
Handles both Telegram bot input and direct input.
"""
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
if string_to_search is None:
# Chiedi la scelta all'utente con il bot Telegram
if string_to_search is None:
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
string_to_search = bot.ask(
"key_search",
f"Inserisci la parola da cercare\noppure back per tornare alla scelta: ",
f"Enter the search term\nor type 'back' to return to the menu: ",
None
)
if string_to_search == 'back':
# Riavvia lo script
# Chiude il processo attuale e avvia una nuova istanza dello script
# Restart the script
subprocess.Popen([sys.executable] + sys.argv)
sys.exit()
else:
if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
else:
string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
return string_to_search
def process_search_result(select_title):
"""
Handles the search result and initiates the download for either a film or series.
"""
if select_title.type == 'tv':
download_series(select_title)
else:
download_film(select_title)
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
"""
Main function of the application for search film, series and anime.
Parameters:
string_to_search (str, optional): String to search for
get_onylDatabase (bool, optional): If True, return only the database object
direct_item (dict, optional): Direct item to process (bypass search)
"""
if direct_item:
select_title = MediaItem(**direct_item)
process_search_result(select_title)
return
# Get the user input for the search term
string_to_search = get_user_input(string_to_search)
# Perform the database search
len_database = title_search(quote_plus(string_to_search))
# Return list of elements
if get_onylDatabase:
# If only the database is needed, return the manager
if get_onlyDatabase:
return media_search_manager
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
if len_database > 0:
# Select title from list
select_title = get_select_title(table_show_manager, media_search_manager)
if select_title.type == 'tv':
download_series(select_title)
else:
download_film(select_title)
process_search_result(select_title)
else:
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
# Retry
search()
if site_constant.TELEGRAM_BOT:
bot.send_message(f"No results found, please try again", None)
# If no results are found, ask again
string_to_search = get_user_input()
search()

View File

@ -1,4 +1,4 @@
# 3.12.23
# 16.03.25
import os
@ -8,13 +8,14 @@ import httpx
from bs4 import BeautifulSoup
from rich.console import Console
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance, TelegramSession
# Internal utilities
from StreamingCommunity.Util.os import os_manager
from StreamingCommunity.Util.message import start_message
from StreamingCommunity.Util.headers import get_headers
from StreamingCommunity.Util.config_json import config_manager
from StreamingCommunity.Lib.Downloader import HLS_Downloader
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance, TelegramSession
# Logic class

View File

@ -1,4 +1,4 @@
# 3.12.23
# 16.03.25
import os
from typing import Tuple

View File

@ -1,4 +1,4 @@
# 10.12.23
# 16.03.25
# External libraries
@ -6,11 +6,12 @@ import httpx
from bs4 import BeautifulSoup
from rich.console import Console
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Internal utilities
from StreamingCommunity.Util.config_json import config_manager
from StreamingCommunity.Util.headers import get_userAgent
from StreamingCommunity.Util.table import TVShowManager
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
@ -61,10 +62,8 @@ def title_search(title_search: str) -> int:
# Create soup istance
soup = BeautifulSoup(response.text, "html.parser")
i = 0
# Collect data from soup
for movie_div in soup.find_all("div", class_="movie"):
for i, movie_div in enumerate(soup.find_all("div", class_="movie")):
title_tag = movie_div.find("h2", class_="movie-title")
title = title_tag.find("a").get_text(strip=True)
@ -86,8 +85,6 @@ def title_search(title_search: str) -> int:
choice_text = f"{i} - {title} ({tipo})"
choices.append(choice_text)
i += 1
if site_constant.TELEGRAM_BOT:
if choices:
bot.send_message(f"Lista dei risultati:", choices)

View File

@ -1,4 +1,4 @@
# 01.03.24
# 16.03.25
# External libraries
import httpx

View File

@ -11,11 +11,12 @@ from rich.prompt import Prompt
# Internal utilities
from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
from StreamingCommunity.Api.Template.config_loader import site_constant
from .site import title_search, media_search_manager, table_show_manager
from .film_serie import download_film, download_series
@ -31,53 +32,73 @@ msg = Prompt()
console = Console()
def search(string_to_search: str = None, get_onylDatabase: bool = False):
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
if string_to_search is None:
# Chiedi la scelta all'utente con il bot Telegram
def get_user_input(string_to_search: str = None):
"""
Asks the user to input a search term.
Handles both Telegram bot input and direct input.
"""
if string_to_search is None:
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
string_to_search = bot.ask(
"key_search",
f"Inserisci la parola da cercare\noppure back per tornare alla scelta: ",
f"Enter the search term\nor type 'back' to return to the menu: ",
None
)
if string_to_search == 'back':
# Riavvia lo script
# Chiude il processo attuale e avvia una nuova istanza dello script
# Restart the script
subprocess.Popen([sys.executable] + sys.argv)
sys.exit()
else:
string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
else:
if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
return string_to_search
# Search on database
def process_search_result(select_title):
"""
Handles the search result and initiates the download for either a film or series.
"""
download_series(select_title)
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
"""
Main function of the application for search film, series and anime.
Parameters:
string_to_search (str, optional): String to search for
get_onlyDatabase (bool, optional): If True, return only the database object
direct_item (dict, optional): Direct item to process (bypass search)
"""
if direct_item:
select_title = MediaItem(**direct_item)
process_search_result(select_title)
return
# Get the user input for the search term
string_to_search = get_user_input(string_to_search)
# Perform the database search
len_database = title_search(string_to_search)
# Return list of elements
if get_onylDatabase:
##If only the database is needed, return the manager
if get_onlyDatabase:
return media_search_manager
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
if len_database > 0:
# Select title from list (type: TV \ Movie \ OVA)
select_title = get_select_title(table_show_manager, media_search_manager)
process_search_result(select_title)
if select_title.type == 'Movie' or select_title.type == 'OVA':
download_film(select_title)
else:
download_series(select_title)
else:
if site_constant.TELEGRAM_BOT:
bot.send_message(f"Nessun risultato trovato riprova", None)
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
# Retry
if site_constant.TELEGRAM_BOT:
bot.send_message(f"No results found, please try again", None)
# If no results are found, ask again
string_to_search = get_user_input()
search()

View File

@ -77,10 +77,8 @@ def get_real_title(record):
"""
if record['title_eng'] is not None:
return record['title_eng']
elif record['title'] is not None:
return record['title']
else:
return record['title_it']

View File

@ -10,10 +10,11 @@ from rich.prompt import Prompt
# Internal utilities
from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
# Logic class
from StreamingCommunity.Api.Template.config_loader import site_constant
from .site import title_search, media_search_manager, table_show_manager
from .film import download_film
@ -29,10 +30,26 @@ msg = Prompt()
console = Console()
def search(string_to_search: str = None, get_onylDatabase: bool = False):
def process_search_result(select_title):
"""
Main function of the application for film and series.
Handles the search result and initiates the download for either a film or series.
"""
# !!! ADD TYPE DONT WORK FOR SERIE
download_film(select_title)
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
"""
Main function of the application for search film, series and anime.
Parameters:
string_to_search (str, optional): String to search for
get_onylDatabase (bool, optional): If True, return only the database object
direct_item (dict, optional): Direct item to process (bypass search)
"""
if direct_item:
select_title = MediaItem(**direct_item)
process_search_result(select_title)
return
if string_to_search is None:
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):
# Search on database
len_database = title_search(quote_plus(string_to_search))
# Return list of elements
if get_onylDatabase:
## If only the database is needed, return the manager
if get_onlyDatabase:
return media_search_manager
if len_database > 0:
# Select title from list
select_title = get_select_title(table_show_manager, media_search_manager)
# !!! ADD TYPE DONT WORK FOR SERIE
download_film(select_title)
process_search_result(select_title)
else:
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
# Retry
# If no results are found, ask again
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
search()

View File

@ -62,7 +62,8 @@ def title_search(word_to_search: str) -> int:
title_info = {
'name': title,
'url': url
'url': url,
'type': 'film'
}
media_search_manager.add_media(title_info)

View File

@ -12,6 +12,7 @@ from rich.prompt import Prompt
# Internal utilities
from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
# Logic class
@ -30,10 +31,28 @@ msg = Prompt()
console = Console()
def search(string_to_search: str = None, get_onylDatabase: bool = False):
def process_search_result(select_title):
"""
Main function of the application for film and series.
Handles the search result and initiates the download for either a film or series.
"""
if "Serie TV" in str(select_title.type):
download_thread(select_title)
else:
logging.error(f"Not supported: {select_title.type}")
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
"""
Main function of the application for search film, series and anime.
Parameters:
string_to_search (str, optional): String to search for
get_onylDatabase (bool, optional): If True, return only the database object
direct_item (dict, optional): Direct item to process (bypass search)
"""
if direct_item:
select_title = MediaItem(**direct_item)
process_search_result(select_title)
return
if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
@ -41,24 +60,16 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
# Search on database
len_database = title_search(quote_plus(string_to_search))
# Return list of elements
if get_onylDatabase:
# If only the database is needed, return the manager
if get_onlyDatabase:
return media_search_manager
if len_database > 0:
# Select title from list
select_title = get_select_title(table_show_manager, media_search_manager)
# Download only film
if "Serie TV" in str(select_title.type):
download_thread(select_title)
else:
logging.error(f"Not supported: {select_title.type}")
process_search_result(select_title)
else:
# If no results are found, ask again
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
# Retry
search()

View File

@ -11,6 +11,7 @@ from rich.prompt import Prompt
# Internal utilities
from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
# Logic class
@ -29,10 +30,25 @@ msg = Prompt()
console = Console()
def search(string_to_search: str = None, get_onylDatabase: bool = False):
def process_search_result(select_title):
"""
Main function of the application for film and series.
Handles the search result and initiates the download for either a film or series.
"""
download_series(select_title)
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
"""
Main function of the application for search film, series and anime.
Parameters:
string_to_search (str, optional): String to search for
get_onylDatabase (bool, optional): If True, return only the database object
direct_item (dict, optional): Direct item to process (bypass search)
"""
if direct_item:
select_title = MediaItem(**direct_item)
process_search_result(select_title)
return
if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
@ -40,20 +56,16 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
# Search on database
len_database = title_search(quote_plus(string_to_search))
# Return list of elements
if get_onylDatabase:
# If only the database is needed, return the manager
if get_onlyDatabase:
return media_search_manager
if len_database > 0:
# Select title from list
select_title = get_select_title(table_show_manager, media_search_manager)
# Download only film
download_series(select_title)
process_search_result(select_title)
else:
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
# Retry
# If no results are found, ask again
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
search()

View File

@ -63,7 +63,8 @@ def title_search(word_to_search: str) -> int:
serie_info = {
'name': title,
'url': link
'url': link,
'type': 'tv'
}
media_search_manager.add_media(serie_info)

View File

@ -5,12 +5,16 @@ from urllib.parse import quote_plus
# External library
from rich.console import Console
from rich.prompt import Prompt, Confirm
from rich.prompt import Prompt
# Internal utilities
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
from StreamingCommunity.Lib.TMBD import tmdb, Json_film
# Logic class
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Lib.TMBD import tmdb, Json_film
from .film import download_film
@ -25,16 +29,32 @@ msg = Prompt()
console = Console()
def search(string_to_search: str = None, get_onylDatabase: bool = False):
def process_search_result(select_title):
"""
Main function of the application for film and series.
Handles the search result and initiates the download for either a film or series.
"""
download_film(select_title)
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
"""
Main function of the application for search film, series and anime.
Parameters:
string_to_search (str, optional): String to search for
get_onylDatabase (bool, optional): If True, return only the database object
direct_item (dict, optional): Direct item to process (bypass search)
"""
if direct_item:
select_title = MediaItem(**direct_item)
process_search_result(select_title)
return
if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
# Not available for the moment
if get_onylDatabase:
if get_onlyDatabase:
return 0
# Search on database
@ -47,7 +67,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False):
download_film(movie_details)
else:
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
# Retry
# If no results are found, ask again
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
search()

View File

@ -12,11 +12,12 @@ from rich.prompt import Prompt
# Internal utilities
from StreamingCommunity.Api.Template import get_select_title
from StreamingCommunity.Api.Template.config_loader import site_constant
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
from StreamingCommunity.TelegramHelp.telegram_bot import get_bot_instance
# Logic class
from StreamingCommunity.Api.Template.config_loader import site_constant
from .site import title_search, table_show_manager, media_search_manager
from .film import download_film
from .series import download_series
@ -33,54 +34,76 @@ msg = Prompt()
console = Console()
def search(string_to_search: str = None, get_onylDatabase: bool = False):
def get_user_input(string_to_search: str = None):
"""
Main function of the application for film and series.
Asks the user to input a search term.
Handles both Telegram bot input and direct input.
"""
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
if string_to_search is None:
# Chiedi la scelta all'utente con il bot Telegram
if string_to_search is None:
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
string_to_search = bot.ask(
"key_search",
f"Inserisci la parola da cercare\noppure back per tornare alla scelta: ",
f"Enter the search term\nor type 'back' to return to the menu: ",
None
)
if string_to_search == 'back':
# Riavvia lo script
# Chiude il processo attuale e avvia una nuova istanza dello script
# Restart the script
subprocess.Popen([sys.executable] + sys.argv)
sys.exit()
else:
if string_to_search is None:
string_to_search = msg.ask(f"\n[purple]Insert word to search in [green]{site_constant.SITE_NAME}").strip()
else:
string_to_search = msg.ask(f"\n[purple]Insert a word to search in [green]{site_constant.SITE_NAME}").strip()
return string_to_search
def process_search_result(select_title):
"""
Handles the search result and initiates the download for either a film or series.
"""
if select_title.type == 'tv':
download_series(select_title)
else:
download_film(select_title)
def search(string_to_search: str = None, get_onlyDatabase: bool = False, direct_item: dict = None):
"""
Main function of the application for search film, series and anime.
Parameters:
string_to_search (str, optional): String to search for
get_onylDatabase (bool, optional): If True, return only the database object
direct_item (dict, optional): Direct item to process (bypass search)
"""
if direct_item:
select_title = MediaItem(**direct_item)
process_search_result(select_title)
return
# Get the user input for the search term
string_to_search = get_user_input(string_to_search)
# Perform the database search
len_database = title_search(quote_plus(string_to_search))
# Return list of elements
if get_onylDatabase:
# If only the database is needed, return the manager
if get_onlyDatabase:
return media_search_manager
if site_constant.TELEGRAM_BOT:
bot = get_bot_instance()
if len_database > 0:
# Select title from list
select_title = get_select_title(table_show_manager, media_search_manager)
if select_title.type == 'tv':
download_series(select_title)
else:
download_film(select_title)
process_search_result(select_title)
else:
console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}")
if site_constant.TELEGRAM_BOT:
bot.send_message(f"Nessun risultato trovato riprova", None)
bot.send_message(f"No results found, please try again", None)
# Retry
# If no results are found, ask again
string_to_search = get_user_input()
search()

View File

@ -1,5 +1,5 @@
__title__ = 'StreamingCommunity'
__version__ = '2.9.4'
__version__ = '2.9.5'
__author__ = 'Arrowar'
__description__ = 'A command-line program to download film'
__copyright__ = 'Copyright 2024'

View File

@ -0,0 +1,315 @@
# 17.03.25
import os
import sys
import time
import glob
import logging
import importlib
# External library
from rich.console import Console
from rich.prompt import Prompt
from rich.table import Table
from rich.progress import Progress
# Internal utilities
from StreamingCommunity.Util.message import start_message
# Variable
console = Console()
msg = Prompt()
# !!! DA METTERE IN COMUNE CON QUELLA DI RUN
def load_search_functions():
modules = []
loaded_functions = {}
excluded_sites = set()
# Find api home directory
if getattr(sys, 'frozen', False): # Modalità PyInstaller
base_path = os.path.join(sys._MEIPASS, "StreamingCommunity")
else:
base_path = os.path.dirname(__file__)
api_dir = os.path.join(base_path, 'Api', 'Site')
init_files = glob.glob(os.path.join(api_dir, '*', '__init__.py'))
# Retrieve modules and their indices
for init_file in init_files:
# Get folder name as module name
module_name = os.path.basename(os.path.dirname(init_file))
# Se il modulo è nella lista da escludere, saltalo
if module_name in excluded_sites:
continue
logging.info(f"Load module name: {module_name}")
try:
# Dynamically import the module
mod = importlib.import_module(f'StreamingCommunity.Api.Site.{module_name}')
# Get 'indice' from the module
indice = getattr(mod, 'indice', 0)
is_deprecate = bool(getattr(mod, '_deprecate', True))
use_for = getattr(mod, '_useFor', 'other')
if not is_deprecate:
modules.append((module_name, indice, use_for))
except Exception as e:
console.print(f"[red]Failed to import module {module_name}: {str(e)}")
# Sort modules by 'indice'
modules.sort(key=lambda x: x[1])
# Load search functions in the sorted order
for module_name, _, use_for in modules:
# Construct a unique alias for the module
module_alias = f'{module_name}_search'
try:
# Dynamically import the module
mod = importlib.import_module(f'StreamingCommunity.Api.Site.{module_name}')
# Get the search function from the module (assuming the function is named 'search' and defined in __init__.py)
search_function = getattr(mod, 'search')
# Add the function to the loaded functions dictionary
loaded_functions[module_alias] = (search_function, use_for)
except Exception as e:
console.print(f"[red]Failed to load search function from module {module_name}: {str(e)}")
return loaded_functions
def global_search(search_terms: str = None, selected_sites: list = None):
"""
Perform a search across multiple sites based on selection.
Parameters:
search_terms (str, optional): The terms to search for. If None, will prompt the user.
selected_sites (list, optional): List of site aliases to search. If None, will search all sites.
Returns:
dict: Consolidated search results from all searched sites.
"""
search_functions = load_search_functions()
all_results = {}
if search_terms is None:
search_terms = msg.ask("\n[purple]Enter search terms for global search: ").strip()
# Organize sites by category for better display
sites_by_category = {}
for alias, (func, category) in search_functions.items():
if category not in sites_by_category:
sites_by_category[category] = []
sites_by_category[category].append((alias, func))
# If no sites are specifically selected, prompt the user
if selected_sites is None:
console.print("\n[bold green]Select sites to search:[/bold green]")
console.print("[bold cyan]1.[/bold cyan] Search all sites")
console.print("[bold cyan]2.[/bold cyan] Search by category")
console.print("[bold cyan]3.[/bold cyan] Select specific sites")
choice = msg.ask("[green]Enter your choice (1-3)", choices=["1", "2", "3"], default="1")
if choice == "1":
# Search all sites
selected_sites = list(search_functions.keys())
elif choice == "2":
# Search by category
console.print("\n[bold green]Select categories to search:[/bold green]")
for i, category in enumerate(sites_by_category.keys(), 1):
console.print(f"[bold cyan]{i}.[/bold cyan] {category.capitalize()}")
category_choices = msg.ask("[green]Enter category numbers separated by commas", default="1")
selected_categories = [list(sites_by_category.keys())[int(c.strip())-1] for c in category_choices.split(",")]
selected_sites = []
for category in selected_categories:
for alias, _ in sites_by_category.get(category, []):
selected_sites.append(alias)
else:
# Select specific sites
console.print("\n[bold green]Select specific sites to search:[/bold green]")
for i, (alias, _) in enumerate(search_functions.items(), 1):
site_name = alias.split("_")[0].capitalize()
console.print(f"[bold cyan]{i}.[/bold cyan] {site_name}")
site_choices = msg.ask("[green]Enter site numbers separated by commas", default="1")
selected_indices = [int(c.strip())-1 for c in site_choices.split(",")]
selected_sites = [list(search_functions.keys())[i] for i in selected_indices if i < len(search_functions)]
# Display progress information
console.print(f"\n[bold green]Searching for:[/bold green] [yellow]{search_terms}[/yellow]")
console.print(f"[bold green]Searching across:[/bold green] {len(selected_sites)} sites")
with Progress() as progress:
search_task = progress.add_task("[cyan]Searching...", total=len(selected_sites))
# Search each selected site
for alias in selected_sites:
site_name = alias.split("_")[0].capitalize()
progress.update(search_task, description=f"[cyan]Searching {site_name}...")
func, _ = search_functions[alias]
try:
# Call the search function with get_onlyDatabase=True to get database object
database = func(search_terms, get_onlyDatabase=True)
# Check if database has media_list attribute and it's not empty
if database and hasattr(database, 'media_list') and len(database.media_list) > 0:
# Store media_list items with additional source information
all_results[alias] = []
for element in database.media_list:
# Convert element to dictionary if it's an object
if hasattr(element, '__dict__'):
item_dict = element.__dict__.copy()
else:
item_dict = {} # Fallback for non-object items
# Add source information
item_dict['source'] = site_name
item_dict['source_alias'] = alias
all_results[alias].append(item_dict)
console.print(f"[green]Found {len(database.media_list)} results from {site_name}")
except Exception as e:
console.print(f"[bold red]Error searching {site_name}:[/bold red] {str(e)}")
progress.update(search_task, advance=1)
# Display the consolidated results
if all_results:
all_media_items = []
for alias, results in all_results.items():
for item in results:
all_media_items.append(item)
# Display consolidated results
display_consolidated_results(all_media_items, search_terms)
# Allow user to select an item
selected_item = select_from_consolidated_results(all_media_items)
if selected_item:
# Process the selected item - download or further actions
process_selected_item(selected_item, search_functions)
else:
console.print(f"\n[bold red]No results found for:[/bold red] [yellow]{search_terms}[/yellow]")
# Optionally offer to search again or return to main menu
if msg.ask("[green]Search again? (y/n)", choices=["y", "n"], default="y") == "y":
global_search()
return all_results
def display_consolidated_results(all_media_items, search_terms):
"""
Display consolidated search results from multiple sites.
Parameters:
all_media_items (list): List of media items from all searched sites.
search_terms (str): The search terms used.
"""
time.sleep(1)
start_message()
console.print(f"\n[bold green]Search results for:[/bold green] [yellow]{search_terms}[/yellow] \n")
table = Table(show_header=True, header_style="bold cyan")
table.add_column("#", style="dim", width=4)
table.add_column("Title", min_width=20)
table.add_column("Type", width=15)
table.add_column("Source", width=25)
for i, item in enumerate(all_media_items, 1):
# Extract values from item dict, with fallbacks if keys don't exist
title = item.get('title', item.get('name', 'Unknown'))
media_type = item.get('type', item.get('media_type', 'Unknown'))
source = item.get('source', 'Unknown')
table.add_row(
str(i),
str(title),
str(media_type),
str(source),
)
console.print(table)
def select_from_consolidated_results(all_media_items):
"""
Allow user to select an item from consolidated results.
Parameters:
all_media_items (list): List of media items from all searched sites.
Returns:
dict: The selected media item or None if no selection was made.
"""
if not all_media_items:
return None
max_index = len(all_media_items)
choice = msg.ask(
f"[green]Select item # (1-{max_index}) or 0 to cancel",
choices=[str(i) for i in range(max_index + 1)],
default="1",
show_choices=False
)
if choice == "0":
return None
return all_media_items[int(choice) - 1]
def process_selected_item(selected_item, search_functions):
"""
Process the selected item - download the media using the appropriate site API.
Parameters:
selected_item (dict): The selected media item.
search_functions (dict): Dictionary of search functions by alias.
"""
source_alias = selected_item.get('source_alias')
if not source_alias or source_alias not in search_functions:
console.print("[bold red]Error: Cannot process this item - source information missing.[/bold red]")
return
# Get the appropriate search function for this source
func, _ = search_functions[source_alias]
console.print(f"\n[bold green]Processing selection from:[/bold green] {selected_item.get('source')}")
# Extract necessary information to pass to the site's search function
item_id = selected_item.get('id', selected_item.get('media_id'))
item_type = selected_item.get('type', selected_item.get('media_type', 'unknown'))
item_title = selected_item.get('title', selected_item.get('name', 'Unknown'))
if item_id:
console.print(f"[bold green]Selected item:[/bold green] {item_title} (ID: {item_id}, Type: {item_type})")
# Call the site's search function with direct_item parameter to process download
try:
func(direct_item=selected_item)
except Exception as e:
console.print(f"[bold red]Error processing download:[/bold red] {str(e)}")
else:
console.print("[bold red]Error: Item ID not found.[/bold red]")

View File

@ -18,6 +18,7 @@ from rich.prompt import Prompt
# Internal utilities
from .global_search import global_search
from StreamingCommunity.Util.message import start_message
from StreamingCommunity.Util.config_json import config_manager
from StreamingCommunity.Util.os import os_summary
@ -54,6 +55,7 @@ def run_function(func: Callable[..., None], close_console: bool = False, search_
func(search_terms)
# !!! DA METTERE IN COMUNE CON QUELLA DI GLOBAL
def load_search_functions():
modules = []
loaded_functions = {}
@ -237,6 +239,11 @@ def main(script_id = 0):
'--specific_list_subtitles', type=str, help='Comma-separated list of specific subtitle languages to download (e.g., eng,spa).'
)
# Add global search option
parser.add_argument(
'--global', action='store_true', help='Perform a global search across multiple sites.'
)
# Add arguments for search functions
color_map = {
"anime": "red",
@ -253,6 +260,7 @@ def main(script_id = 0):
parser.add_argument(f'-{short_option}', f'--{long_option}', action='store_true', help=f'Search for {alias.split("_")[0]} on streaming platforms.')
parser.add_argument('-s', '--search', default=None, help='Search terms')
# Parse command-line arguments
args = parser.parse_args()
@ -280,6 +288,11 @@ def main(script_id = 0):
config_manager.write_config()
# Check if global search is requested
if getattr(args, 'global'):
global_search(search_terms)
return
# Map command-line arguments to functions
arg_to_function = {alias: func for alias, (func, _) in search_functions.items()}
@ -295,13 +308,18 @@ def main(script_id = 0):
# Create dynamic prompt message and choices
choice_labels = {str(i): (alias.split("_")[0].capitalize(), use_for) for i, (alias, (_, use_for)) in enumerate(search_functions.items())}
# Add global search option to the menu
#global_search_key = str(len(choice_labels))
#choice_labels[global_search_key] = ("Global Search", "all")
#input_to_function[global_search_key] = global_search
# Display the category legend in a single line
legend_text = " | ".join([f"[{color}]{category.capitalize()}[/{color}]" for category, color in color_map.items()])
console.print(f"\n[bold green]Category Legend:[/bold green] {legend_text}")
# Construct the prompt message with color-coded site names
prompt_message = "[green]Insert category [white](" + ", ".join(
[f"{key}: [{color_map[label[1]]}]{label[0]}[/{color_map[label[1]]}]" for key, label in choice_labels.items()]
[f"{key}: [{color_map.get(label[1], 'white')}]{label[0]}[/{color_map.get(label[1], 'white')}]" for key, label in choice_labels.items()]
) + "[white])"
if TELEGRAM_BOT:
@ -330,10 +348,16 @@ def main(script_id = 0):
# Run the corresponding function based on user input
if category in input_to_function:
run_function(input_to_function[category], search_terms = args.search)
"""if category == global_search_key:
# Run global search
run_function(input_to_function[category], search_terms=search_terms)
else:"""
# Run normal site-specific search
run_function(input_to_function[category], search_terms=search_terms)
else:
if TELEGRAM_BOT:
bot.send_message(f"Categoria non valida", None)

View File

@ -1,116 +0,0 @@
# 12.11.24
# Fix import
import os
import sys
src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.append(src_path)
# Other
import glob
import logging
import importlib
from rich.console import Console
# Other import
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Variable
console = Console()
def load_search_functions():
modules = []
loaded_functions = {}
# Traverse the Api directory
api_dir = os.path.join(os.path.dirname(__file__), '..', 'StreamingCommunity', 'Api', 'Site')
init_files = glob.glob(os.path.join(api_dir, '*', '__init__.py'))
logging.info(f"Base folder path: {api_dir}")
logging.info(f"Api module path: {init_files}")
# Retrieve modules and their indices
for init_file in init_files:
# Get folder name as module name
module_name = os.path.basename(os.path.dirname(init_file))
logging.info(f"Load module name: {module_name}")
try:
# Dynamically import the module
mod = importlib.import_module(f'StreamingCommunity.Api.Site.{module_name}')
# Get 'indice' from the module
indice = getattr(mod, 'indice', 0)
is_deprecate = bool(getattr(mod, '_deprecate', True))
use_for = getattr(mod, '_useFor', 'other')
if not is_deprecate:
modules.append((module_name, indice, use_for))
except Exception as e:
console.print(f"[red]Failed to import module {module_name}: {str(e)}")
# Sort modules by 'indice'
modules.sort(key=lambda x: x[1])
# Load search functions in the sorted order
for module_name, _, use_for in modules:
# Construct a unique alias for the module
module_alias = f'{module_name}_search'
logging.info(f"Module alias: {module_alias}")
try:
# Dynamically import the module
mod = importlib.import_module(f'StreamingCommunity.Api.Site.{module_name}')
# Get the search function from the module (assuming the function is named 'search' and defined in __init__.py)
search_function = getattr(mod, 'search')
# Add the function to the loaded functions dictionary
loaded_functions[module_alias] = (search_function, use_for)
except Exception as e:
console.print(f"[red]Failed to load search function from module {module_name}: {str(e)}")
return loaded_functions
def search_all_sites(loaded_functions, search_string, max_sites=10):
total_len_database = 0
site_count = 0
for module_alias, (search_function, use_for) in loaded_functions.items():
if max_sites is not None and site_count >= max_sites:
break
console.print(f"\n[blue]Searching in module: {module_alias} [white](Use for: {use_for})")
try:
database: MediaManager = search_function(search_string, get_onylDatabase=True)
len_database = len(database.media_list)
for element in database.media_list:
print(element.__dict__)
console.print(f"[green]Database length for {module_alias}: {len_database}")
total_len_database += len_database
site_count += 1
except Exception as e:
console.print(f"[red]Error while executing search function for {module_alias}: {str(e)}")
return total_len_database
# Main
search_string = "cars"
loaded_functions = load_search_functions()
total_len = search_all_sites(loaded_functions, search_string)
console.print(f"\n[cyan]Total number of results from all sites: {total_len}")

View File

@ -10,7 +10,7 @@ with open(os.path.join(os.path.dirname(__file__), "requirements.txt"), "r", enco
setup(
name="StreamingCommunity",
version="2.9.4",
version="2.9.5",
long_description=read_readme(),
long_description_content_type="text/markdown",
author="Lovi-0",