mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-03 10:00:10 +00:00
api: Add "altadefinizione"
This commit is contained in:
parent
6bea3c849b
commit
a63ee445dd
85
README.md
85
README.md
@ -79,9 +79,15 @@ Install directly from PyPI:
|
||||
pip install StreamingCommunity
|
||||
```
|
||||
|
||||
### Creating a Run Script
|
||||
Update to the latest version:
|
||||
|
||||
Create `run_streaming.py`:
|
||||
```bash
|
||||
pip install --upgrade StreamingCommunity
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Create a simple script (`run_streaming.py`) to launch the main application:
|
||||
|
||||
```python
|
||||
from StreamingCommunity.run import main
|
||||
@ -91,16 +97,85 @@ if __name__ == "__main__":
|
||||
```
|
||||
|
||||
Run the script:
|
||||
|
||||
```bash
|
||||
python run_streaming.py
|
||||
```
|
||||
|
||||
### Updating via PyPI
|
||||
## Modules
|
||||
|
||||
```bash
|
||||
pip install --upgrade StreamingCommunity
|
||||
### HLS Downloader
|
||||
|
||||
Download HTTP Live Streaming (HLS) content from m3u8 URLs.
|
||||
|
||||
```python
|
||||
from StreamingCommunity.Download import HLS_Downloader
|
||||
|
||||
# Initialize with m3u8 URL and optional output path
|
||||
downloader = HLS_Downloader(
|
||||
m3u8_url="https://example.com/stream.m3u8",
|
||||
output_path="/downloads/video.mp4" # Optional
|
||||
)
|
||||
|
||||
# Start the download
|
||||
downloader.download()
|
||||
```
|
||||
|
||||
See [HLS example](./Test/Download/HLS.py) for complete usage.
|
||||
|
||||
### MP4 Downloader
|
||||
|
||||
Direct MP4 file downloader with support for custom headers and referrer.
|
||||
|
||||
```python
|
||||
from StreamingCommunity.Download import MP4_downloader
|
||||
|
||||
# Basic usage
|
||||
downloader = MP4_downloader(
|
||||
url="https://example.com/video.mp4",
|
||||
path="/downloads/saved_video.mp4"
|
||||
)
|
||||
|
||||
# Advanced usage with custom headers and referrer
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
}
|
||||
downloader = MP4_downloader(
|
||||
url="https://example.com/video.mp4",
|
||||
path="/downloads/saved_video.mp4",
|
||||
referer="https://example.com",
|
||||
headers_=headers
|
||||
)
|
||||
|
||||
# Start download
|
||||
downloader.download()
|
||||
```
|
||||
|
||||
See [MP4 example](./Test/Download/MP4.py) for complete usage.
|
||||
|
||||
### Torrent Client
|
||||
|
||||
Download content via torrent magnet links.
|
||||
|
||||
```python
|
||||
from StreamingCommunity.Download import TOR_downloader
|
||||
|
||||
# Initialize torrent client
|
||||
client = TOR_downloader()
|
||||
|
||||
# Add magnet link
|
||||
client.add_magnet_link("magnet:?xt=urn:btih:example_hash&dn=example_name")
|
||||
|
||||
# Start download
|
||||
client.start_download()
|
||||
|
||||
# Move downloaded files to specific location
|
||||
client.move_downloaded_files("/downloads/torrents/")
|
||||
```
|
||||
|
||||
See [Torrent example](./Test/Download/TOR.py) for complete usage.
|
||||
|
||||
|
||||
## 2. Automatic Installation
|
||||
|
||||
### Supported Operating Systems 💿
|
||||
|
@ -11,6 +11,7 @@ class Episode:
|
||||
self.number: int = data.get('number', 1)
|
||||
self.name: str = data.get('name', '')
|
||||
self.duration: int = data.get('duration', 0)
|
||||
self.url: str = data.get('url', '')
|
||||
|
||||
def __str__(self):
|
||||
return f"Episode(id={self.id}, number={self.number}, name='{self.name}', duration={self.duration} sec)"
|
||||
@ -35,69 +36,70 @@ class EpisodeManager:
|
||||
|
||||
Parameters:
|
||||
- index (int): The zero-based index of the episode to retrieve.
|
||||
|
||||
Returns:
|
||||
Episode: The Episode object at the specified index.
|
||||
"""
|
||||
return self.episodes[index]
|
||||
|
||||
def length(self) -> int:
|
||||
"""
|
||||
Get the number of episodes in the manager.
|
||||
|
||||
Returns:
|
||||
int: Number of episodes.
|
||||
"""
|
||||
return len(self.episodes)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""
|
||||
This method clears the episodes list.
|
||||
|
||||
Parameters:
|
||||
- self: The object instance.
|
||||
"""
|
||||
self.episodes.clear()
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""
|
||||
Get the number of episodes in the manager.
|
||||
"""
|
||||
return len(self.episodes)
|
||||
|
||||
def __str__(self):
|
||||
return f"EpisodeManager(num_episodes={len(self.episodes)})"
|
||||
|
||||
|
||||
class SeasonData:
|
||||
class Season:
|
||||
def __init__(self, data: Dict[str, Any]):
|
||||
self.id: int = data.get('id', 0)
|
||||
self.number: int = data.get('number', 0)
|
||||
self.name: str = data.get('name', '')
|
||||
self.slug: str = data.get('slug', '')
|
||||
self.type: str = data.get('type', '')
|
||||
self.episodes: EpisodeManager = EpisodeManager()
|
||||
|
||||
def __str__(self):
|
||||
return f"SeasonData(id={self.id}, number={self.number}, name='{self.name}'"
|
||||
return f"Season(id={self.id}, number={self.number}, name='{self.name}', episodes={self.episodes.length()})"
|
||||
|
||||
|
||||
class SeasonManager:
|
||||
def __init__(self):
|
||||
self.seasons: List[SeasonData] = []
|
||||
self.seasons: List[Season] = []
|
||||
|
||||
def add_season(self, season_data):
|
||||
season = SeasonData(season_data)
|
||||
def add_season(self, season_data: Dict[str, Any]) -> Season:
|
||||
"""
|
||||
Add a new season to the manager and return it.
|
||||
|
||||
Parameters:
|
||||
- season_data (Dict[str, Any]): A dictionary containing data for the new season.
|
||||
"""
|
||||
season = Season(season_data)
|
||||
self.seasons.append(season)
|
||||
return season
|
||||
|
||||
def get_season_by_number(self, number: int) -> Optional[Dict]:
|
||||
return self.seasons[number]
|
||||
|
||||
class Season:
|
||||
def __init__(self, season_data: Dict[str, Union[int, str, None]]):
|
||||
self.season_data = season_data
|
||||
|
||||
self.id: int = season_data.get('id', 0)
|
||||
self.number: int = season_data.get('number', 0)
|
||||
self.name: str = season_data.get('name', '')
|
||||
self.slug: str = season_data.get('slug', '')
|
||||
self.type: str = season_data.get('type', '')
|
||||
self.seasons_count: int = season_data.get('seasons_count', 0)
|
||||
def get_season_by_number(self, number: int) -> Optional[Season]:
|
||||
"""
|
||||
Get a season by its number.
|
||||
|
||||
self.episodes: EpisodeManager = EpisodeManager()
|
||||
|
||||
self.seasonsData: SeasonManager = SeasonManager()
|
||||
for element in season_data['seasons']:
|
||||
self.seasonsData.add_season(element)
|
||||
Parameters:
|
||||
- number (int): The season number (1-based index)
|
||||
"""
|
||||
for season in self.seasons:
|
||||
if season.number == number:
|
||||
return season
|
||||
return None
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""
|
||||
Return the number of seasons managed.
|
||||
"""
|
||||
return len(self.seasons)
|
||||
|
||||
|
||||
class Stream:
|
||||
|
@ -14,15 +14,15 @@ from StreamingCommunity.Api.Template import get_select_title
|
||||
|
||||
# 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
|
||||
from .site import title_search, table_show_manager, media_search_manager
|
||||
from .film import download_film
|
||||
from .series import download_series
|
||||
|
||||
|
||||
# Variable
|
||||
indice = 4
|
||||
indice = 2
|
||||
_useFor = "film_serie"
|
||||
_deprecate = True
|
||||
_deprecate = False
|
||||
_priority = 1
|
||||
_engineDownload = "hls"
|
||||
|
||||
|
98
StreamingCommunity/Api/Site/altadefinizione/film.py
Normal file
98
StreamingCommunity/Api/Site/altadefinizione/film.py
Normal file
@ -0,0 +1,98 @@
|
||||
# 3.12.23
|
||||
|
||||
import os
|
||||
|
||||
|
||||
# External library
|
||||
import httpx
|
||||
from bs4 import BeautifulSoup
|
||||
from rich.console import Console
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# Logic class
|
||||
from StreamingCommunity.Api.Template.config_loader import site_constant
|
||||
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
||||
|
||||
|
||||
# Player
|
||||
from StreamingCommunity.Api.Player.supervideo import VideoSource
|
||||
|
||||
|
||||
# Variable
|
||||
console = Console()
|
||||
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
||||
|
||||
|
||||
def download_film(select_title: MediaItem) -> str:
|
||||
"""
|
||||
Downloads a film using the provided film ID, title name, and domain.
|
||||
|
||||
Parameters:
|
||||
- select_title (MediaItem): The selected media item.
|
||||
|
||||
Return:
|
||||
- str: output path if successful, otherwise None
|
||||
"""
|
||||
start_message()
|
||||
console.print(f"[yellow]Download: [red]{select_title.name} \n")
|
||||
|
||||
# Extract mostraguarda link
|
||||
try:
|
||||
response = httpx.get(select_title.url, headers=get_headers(), timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error fetching the page: {e}")
|
||||
return None
|
||||
|
||||
# Create mostraguarda url
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
iframe_tag = soup.find_all("iframe")
|
||||
url_mostraGuarda = iframe_tag[0].get('data-src')
|
||||
if not url_mostraGuarda:
|
||||
console.print("Error: data-src attribute not found in iframe.")
|
||||
|
||||
# Extract supervideo URL
|
||||
try:
|
||||
response = httpx.get(url_mostraGuarda, headers=get_headers(), timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error fetching mostraguarda link: {e}")
|
||||
return None
|
||||
|
||||
# Create supervio URL
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
player_links = soup.find("ul", class_="_player-mirrors")
|
||||
player_items = player_links.find_all("li")
|
||||
supervideo_url = "https:" + player_items[0].get("data-link")
|
||||
if not supervideo_url:
|
||||
return None
|
||||
|
||||
# Init class
|
||||
video_source = VideoSource(url=supervideo_url)
|
||||
master_playlist = video_source.get_playlist()
|
||||
|
||||
# Define the filename and path for the downloaded film
|
||||
title_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
|
||||
mp4_path = os.path.join(site_constant.MOVIE_FOLDER, title_name.replace(".mp4", ""))
|
||||
|
||||
# Download the film using the m3u8 playlist, and output filename
|
||||
r_proc = HLS_Downloader(
|
||||
m3u8_url=master_playlist,
|
||||
output_path=os.path.join(mp4_path, title_name)
|
||||
).start()
|
||||
|
||||
if r_proc['error'] is not None:
|
||||
try: os.remove(r_proc['path'])
|
||||
except: pass
|
||||
|
||||
return r_proc['path']
|
164
StreamingCommunity/Api/Site/altadefinizione/series.py
Normal file
164
StreamingCommunity/Api/Site/altadefinizione/series.py
Normal file
@ -0,0 +1,164 @@
|
||||
# 3.12.23
|
||||
|
||||
import os
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
# External library
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from StreamingCommunity.Util.message import start_message
|
||||
from StreamingCommunity.Lib.Downloader import HLS_Downloader
|
||||
|
||||
# Logic class
|
||||
from .util.ScrapeSerie import GetSerieInfo
|
||||
from StreamingCommunity.Api.Template.Util import (
|
||||
manage_selection,
|
||||
map_episode_title,
|
||||
dynamic_format_number,
|
||||
validate_selection,
|
||||
validate_episode_selection,
|
||||
display_episodes_list
|
||||
)
|
||||
from StreamingCommunity.Api.Template.config_loader import site_constant
|
||||
from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
|
||||
|
||||
|
||||
# Player
|
||||
from StreamingCommunity.Api.Player.supervideo import VideoSource
|
||||
|
||||
|
||||
# Variable
|
||||
msg = Prompt()
|
||||
console = Console()
|
||||
|
||||
|
||||
def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: GetSerieInfo) -> Tuple[str,bool]:
|
||||
"""
|
||||
Download a single episode video.
|
||||
|
||||
Parameters:
|
||||
- index_season_selected (int): Index of the selected season.
|
||||
- index_episode_selected (int): Index of the selected episode.
|
||||
|
||||
Return:
|
||||
- str: output path
|
||||
- bool: kill handler status
|
||||
"""
|
||||
start_message()
|
||||
index_season_selected = dynamic_format_number(str(index_season_selected))
|
||||
|
||||
# Get info about episode
|
||||
obj_episode = scrape_serie.seasons_manager.get_season_by_number(int(index_season_selected)).episodes.get(index_episode_selected-1)
|
||||
console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}")
|
||||
print()
|
||||
|
||||
# Define filename and path for the downloaded video
|
||||
mp4_name = f"{map_episode_title(scrape_serie.serie_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
|
||||
mp4_path = os.path.join(site_constant.SERIES_FOLDER, scrape_serie.serie_name, f"S{index_season_selected}")
|
||||
|
||||
# Retrieve scws and if available master playlist
|
||||
video_source = VideoSource(obj_episode.url)
|
||||
video_source.make_request(obj_episode.url)
|
||||
master_playlist = video_source.get_playlist()
|
||||
|
||||
# Download the episode
|
||||
r_proc = HLS_Downloader(
|
||||
m3u8_url=master_playlist,
|
||||
output_path=os.path.join(mp4_path, mp4_name)
|
||||
).start()
|
||||
|
||||
if r_proc['error'] is not None:
|
||||
try: os.remove(r_proc['path'])
|
||||
except: pass
|
||||
|
||||
return r_proc['path'], r_proc['stopped']
|
||||
|
||||
|
||||
def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, download_all: bool = False) -> None:
|
||||
"""
|
||||
Download episodes of a selected season.
|
||||
|
||||
Parameters:
|
||||
- index_season_selected (int): Index of the selected season.
|
||||
- download_all (bool): Download all episodes in the season.
|
||||
"""
|
||||
start_message()
|
||||
obj_episodes = scrape_serie.seasons_manager.get_season_by_number(index_season_selected).episodes
|
||||
episodes_count = len(obj_episodes.episodes)
|
||||
|
||||
if download_all:
|
||||
|
||||
# Download all episodes without asking
|
||||
for i_episode in range(1, episodes_count + 1):
|
||||
path, stopped = download_video(index_season_selected, i_episode, scrape_serie)
|
||||
|
||||
if stopped:
|
||||
break
|
||||
|
||||
console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.")
|
||||
|
||||
else:
|
||||
|
||||
# Display episodes list and manage user selection
|
||||
last_command = display_episodes_list(obj_episodes.episodes)
|
||||
list_episode_select = manage_selection(last_command, episodes_count)
|
||||
|
||||
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 if not stopped
|
||||
for i_episode in list_episode_select:
|
||||
path, stopped = download_video(index_season_selected, i_episode, scrape_serie)
|
||||
|
||||
if stopped:
|
||||
break
|
||||
|
||||
def download_series(select_season: MediaItem) -> None:
|
||||
"""
|
||||
Download episodes of a TV series based on user selection.
|
||||
|
||||
Parameters:
|
||||
- select_season (MediaItem): Selected media item (TV series).
|
||||
"""
|
||||
start_message()
|
||||
|
||||
# Init class
|
||||
scrape_serie = GetSerieInfo(select_season.url)
|
||||
|
||||
# Collect information about seasons
|
||||
scrape_serie.collect_season()
|
||||
seasons_count = len(scrape_serie.seasons_manager)
|
||||
|
||||
# Prompt user for season selection and download episodes
|
||||
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)
|
||||
|
||||
try:
|
||||
list_season_select = validate_selection(list_season_select, seasons_count)
|
||||
except ValueError as e:
|
||||
console.print(f"[red]{str(e)}")
|
||||
return
|
||||
|
||||
# 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 episodes if multiple seasons are selected or if '*' is used
|
||||
download_episode(i_season, scrape_serie, download_all=True)
|
||||
else:
|
||||
|
||||
# Otherwise, let the user select specific episodes for the single season
|
||||
download_episode(i_season, scrape_serie, download_all=False)
|
75
StreamingCommunity/Api/Site/altadefinizione/site.py
Normal file
75
StreamingCommunity/Api/Site/altadefinizione/site.py
Normal file
@ -0,0 +1,75 @@
|
||||
# 10.12.23
|
||||
|
||||
|
||||
# External libraries
|
||||
import httpx
|
||||
from bs4 import BeautifulSoup
|
||||
from rich.console import Console
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from StreamingCommunity.Util.config_json import config_manager
|
||||
from StreamingCommunity.Util.headers import get_userAgent
|
||||
from StreamingCommunity.Util.table import TVShowManager
|
||||
|
||||
|
||||
# Logic class
|
||||
from StreamingCommunity.Api.Template.config_loader import site_constant
|
||||
from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
|
||||
|
||||
|
||||
# Variable
|
||||
console = Console()
|
||||
media_search_manager = MediaManager()
|
||||
table_show_manager = TVShowManager()
|
||||
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
||||
|
||||
|
||||
def title_search(title_search: str) -> int:
|
||||
"""
|
||||
Search for titles based on a search query.
|
||||
|
||||
Parameters:
|
||||
- title_search (str): The title to search for.
|
||||
|
||||
Returns:
|
||||
int: The number of titles found.
|
||||
"""
|
||||
media_search_manager.clear()
|
||||
table_show_manager.clear()
|
||||
|
||||
search_url = f"{site_constant.FULL_URL}/?story={title_search}&do=search&subaction=search"
|
||||
console.print(f"[cyan]Search url: [yellow]{search_url}")
|
||||
|
||||
try:
|
||||
response = httpx.post(search_url, headers={'user-agent': get_userAgent()}, timeout=max_timeout, follow_redirects=True)
|
||||
response.raise_for_status()
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"Site: {site_constant.SITE_NAME}, request search error: {e}")
|
||||
return 0
|
||||
|
||||
# Create soup istance
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
|
||||
# Collect data from soup
|
||||
for movie_div in soup.find_all("div", class_="movie"):
|
||||
|
||||
title_tag = movie_div.find("h2", class_="movie-title")
|
||||
title = title_tag.find("a").get_text(strip=True)
|
||||
url = title_tag.find("a").get("href")
|
||||
|
||||
# Define typo
|
||||
if "/serie-tv/" in url:
|
||||
tipo = "tv"
|
||||
else:
|
||||
tipo = "film"
|
||||
|
||||
media_search_manager.add_media({
|
||||
'url': url,
|
||||
'name': title,
|
||||
'type': tipo
|
||||
})
|
||||
|
||||
# Return the number of titles found
|
||||
return media_search_manager.get_length()
|
@ -0,0 +1,72 @@
|
||||
# 01.03.24
|
||||
|
||||
# External libraries
|
||||
import httpx
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
# Internal utilities
|
||||
from StreamingCommunity.Util.headers import get_userAgent
|
||||
from StreamingCommunity.Util.config_json import config_manager
|
||||
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
|
||||
|
||||
|
||||
# Variable
|
||||
max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
||||
|
||||
|
||||
|
||||
class GetSerieInfo:
|
||||
def __init__(self, url):
|
||||
"""
|
||||
Initialize the GetSerieInfo class for scraping TV series information.
|
||||
|
||||
Args:
|
||||
- url (str): The URL of the streaming site.
|
||||
"""
|
||||
self.headers = {'user-agent': get_userAgent()}
|
||||
self.url = url
|
||||
self.seasons_manager = SeasonManager()
|
||||
|
||||
def collect_season(self) -> None:
|
||||
"""
|
||||
Retrieve all episodes for all seasons
|
||||
"""
|
||||
response = httpx.get(self.url, headers=self.headers)
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
self.serie_name = soup.find("title").get_text(strip=True).split(" - ")[0]
|
||||
|
||||
# Process all seasons
|
||||
season_items = soup.find_all('div', class_='accordion-item')
|
||||
|
||||
for season_idx, season_item in enumerate(season_items, 1):
|
||||
season_header = season_item.find('div', class_='accordion-header')
|
||||
if not season_header:
|
||||
continue
|
||||
|
||||
season_name = season_header.get_text(strip=True)
|
||||
|
||||
# Create a new season and get a reference to it
|
||||
current_season = self.seasons_manager.add_season({
|
||||
'number': season_idx,
|
||||
'name': season_name
|
||||
})
|
||||
|
||||
# Find episodes for this season
|
||||
episode_divs = season_item.find_all('div', class_='down-episode')
|
||||
for ep_idx, ep_div in enumerate(episode_divs, 1):
|
||||
episode_name_tag = ep_div.find('b')
|
||||
if not episode_name_tag:
|
||||
continue
|
||||
|
||||
episode_name = episode_name_tag.get_text(strip=True)
|
||||
link_tag = ep_div.find('a', string=lambda text: text and "Supervideo" in text)
|
||||
episode_url = link_tag['href'] if link_tag else None
|
||||
|
||||
# Add episode to the season
|
||||
if current_season:
|
||||
current_season.episodes.add({
|
||||
'number': ep_idx,
|
||||
'name': episode_name,
|
||||
'url': episode_url
|
||||
})
|
@ -52,8 +52,15 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
|
||||
start_message()
|
||||
index_season_selected = dynamic_format_number(str(index_season_selected))
|
||||
|
||||
# SPECIAL: Get season number
|
||||
season = None
|
||||
for s in scrape_serie.seasons_manager.seasons:
|
||||
if s.number == int(index_season_selected):
|
||||
season = s
|
||||
break
|
||||
|
||||
# Get info about episode
|
||||
obj_episode = scrape_serie.episode_manager.get(index_episode_selected - 1)
|
||||
obj_episode = season.episodes.get(index_episode_selected - 1)
|
||||
console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}")
|
||||
print()
|
||||
|
||||
@ -100,14 +107,16 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
|
||||
- index_season_selected (int): Index of the selected season.
|
||||
- download_all (bool): Download all episodes in the season.
|
||||
"""
|
||||
|
||||
# Clean memory of all episodes and get the number of the season
|
||||
scrape_serie.episode_manager.clear()
|
||||
|
||||
# Start message and collect information about episodes
|
||||
start_message()
|
||||
scrape_serie.collect_info_season(index_season_selected)
|
||||
episodes_count = scrape_serie.episode_manager.length()
|
||||
|
||||
# SPECIAL: Get season number
|
||||
season = None
|
||||
for s in scrape_serie.seasons_manager.seasons:
|
||||
if s.number == index_season_selected:
|
||||
season = s
|
||||
break
|
||||
episodes_count = len(season.episodes.episodes)
|
||||
|
||||
if download_all:
|
||||
|
||||
@ -123,7 +132,7 @@ def download_episode(index_season_selected: int, scrape_serie: GetSerieInfo, vid
|
||||
else:
|
||||
|
||||
# Display episodes list and manage user selection
|
||||
last_command = display_episodes_list(scrape_serie.episode_manager.episodes)
|
||||
last_command = display_episodes_list(season.episodes.episodes)
|
||||
list_episode_select = manage_selection(last_command, episodes_count)
|
||||
|
||||
try:
|
||||
@ -163,7 +172,7 @@ def download_series(select_season: MediaItem) -> None:
|
||||
|
||||
# Collect information about seasons
|
||||
scrape_serie.collect_info_title()
|
||||
seasons_count = scrape_serie.season_manager.seasons_count
|
||||
seasons_count = len(scrape_serie.seasons_manager)
|
||||
|
||||
# Prompt user for season selection and download episodes
|
||||
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
||||
@ -197,14 +206,23 @@ def download_series(select_season: MediaItem) -> None:
|
||||
|
||||
# Loop through the selected seasons and download episodes
|
||||
for i_season in list_season_select:
|
||||
|
||||
# SPECIAL: Get season number
|
||||
season = None
|
||||
for s in scrape_serie.seasons_manager.seasons:
|
||||
if s.number == i_season:
|
||||
season = s
|
||||
break
|
||||
season_number = season.number
|
||||
|
||||
if len(list_season_select) > 1 or index_season_selected == "*":
|
||||
|
||||
# Download all episodes if multiple seasons are selected or if '*' is used
|
||||
download_episode(scrape_serie.season_manager.seasonsData.get_season_by_number(i_season-1).number, scrape_serie, video_source, download_all=True)
|
||||
download_episode(season_number, scrape_serie, video_source, download_all=True)
|
||||
else:
|
||||
|
||||
# Otherwise, let the user select specific episodes for the single season
|
||||
download_episode(scrape_serie.season_manager.seasonsData.get_season_by_number(i_season-1).number, scrape_serie, video_source, download_all=False)
|
||||
download_episode(season_number, scrape_serie, video_source, download_all=False)
|
||||
|
||||
if site_constant.TELEGRAM_BOT:
|
||||
bot.send_message(f"Finito di scaricare tutte le serie e episodi", None)
|
||||
|
@ -12,7 +12,7 @@ from bs4 import BeautifulSoup
|
||||
# Internal utilities
|
||||
from StreamingCommunity.Util.headers import get_userAgent
|
||||
from StreamingCommunity.Util.config_json import config_manager
|
||||
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import Season, EpisodeManager
|
||||
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager
|
||||
|
||||
|
||||
# Variable
|
||||
@ -22,7 +22,7 @@ max_timeout = config_manager.get_int("REQUESTS", "timeout")
|
||||
class GetSerieInfo:
|
||||
def __init__(self, url):
|
||||
"""
|
||||
Initialize the ScrapeSerie class for scraping TV series information.
|
||||
Initialize the GetSerieInfo class for scraping TV series information.
|
||||
|
||||
Args:
|
||||
- url (str): The URL of the streaming site.
|
||||
@ -31,6 +31,9 @@ class GetSerieInfo:
|
||||
self.headers = {'user-agent': get_userAgent()}
|
||||
self.url = url
|
||||
|
||||
# Initialize the SeasonManager
|
||||
self.seasons_manager = SeasonManager()
|
||||
|
||||
def setup(self, media_id: int = None, series_name: str = None):
|
||||
"""
|
||||
Set up the scraper with specific media details.
|
||||
@ -41,19 +44,17 @@ class GetSerieInfo:
|
||||
"""
|
||||
self.media_id = media_id
|
||||
|
||||
# If series name is provided, initialize series-specific managers
|
||||
# If series name is provided, initialize series-specific properties
|
||||
if series_name is not None:
|
||||
self.is_series = True
|
||||
self.series_name = series_name
|
||||
self.season_manager = None
|
||||
self.episode_manager: EpisodeManager = EpisodeManager()
|
||||
|
||||
def collect_info_title(self) -> None:
|
||||
"""
|
||||
Retrieve season information for a TV series from the streaming site.
|
||||
Retrieve general information about the TV series from the streaming site.
|
||||
|
||||
Raises:
|
||||
Exception: If there's an error fetching season information
|
||||
Exception: If there's an error fetching series information
|
||||
"""
|
||||
try:
|
||||
response = httpx.get(
|
||||
@ -63,16 +64,30 @@ class GetSerieInfo:
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# Extract seasons from JSON response
|
||||
# Extract series info from JSON response
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
json_response = json.loads(soup.find("div", {"id": "app"}).get("data-page"))
|
||||
self.version = json_response['version']
|
||||
|
||||
# Collect info about season
|
||||
self.season_manager = Season(json_response.get("props").get("title"))
|
||||
|
||||
# Extract information about available seasons
|
||||
title_data = json_response.get("props", {}).get("title", {})
|
||||
|
||||
# Save general series information
|
||||
self.title_info = title_data
|
||||
|
||||
# Extract available seasons and add them to SeasonManager
|
||||
seasons_data = title_data.get("seasons", [])
|
||||
for season_data in seasons_data:
|
||||
self.seasons_manager.add_season({
|
||||
'id': season_data.get('id', 0),
|
||||
'number': season_data.get('number', 0),
|
||||
'name': f"Season {season_data.get('number', 0)}",
|
||||
'slug': season_data.get('slug', ''),
|
||||
'type': title_data.get('type', '')
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error collecting season info: {e}")
|
||||
logging.error(f"Error collecting series info: {e}")
|
||||
raise
|
||||
|
||||
def collect_info_season(self, number_season: int) -> None:
|
||||
@ -86,6 +101,12 @@ class GetSerieInfo:
|
||||
Exception: If there's an error fetching episode information
|
||||
"""
|
||||
try:
|
||||
# Get the season object from SeasonManager
|
||||
season = self.seasons_manager.get_season_by_number(number_season)
|
||||
if not season:
|
||||
logging.error(f"Season {number_season} not found")
|
||||
return
|
||||
|
||||
response = httpx.get(
|
||||
url=f'{self.url}/titles/{self.media_id}-{self.series_name}/stagione-{number_season}',
|
||||
headers={
|
||||
@ -98,12 +119,12 @@ class GetSerieInfo:
|
||||
response.raise_for_status()
|
||||
|
||||
# Extract episodes from JSON response
|
||||
json_response = response.json().get('props').get('loadedSeason').get('episodes')
|
||||
json_response = response.json().get('props', {}).get('loadedSeason', {}).get('episodes', [])
|
||||
|
||||
# Add each episode to the episode manager
|
||||
# Add each episode to the corresponding season's episode manager
|
||||
for dict_episode in json_response:
|
||||
self.episode_manager.add(dict_episode)
|
||||
season.episodes.add(dict_episode)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error collecting title season info: {e}")
|
||||
raise
|
||||
logging.error(f"Error collecting episodes for season {number_season}: {e}")
|
||||
raise
|
@ -31,7 +31,7 @@ from ...FFmpeg import print_duration_table
|
||||
|
||||
# Config
|
||||
REQUEST_VERIFY = config_manager.get_bool('REQUESTS', 'verify')
|
||||
REQUEST_HTTP2 = config_manager.get_bool('REQUEST', 'http2')
|
||||
REQUEST_HTTP2 = config_manager.get_bool('REQUESTS', 'http2')
|
||||
GET_ONLY_LINK = config_manager.get_bool('M3U8_PARSER', 'get_only_link')
|
||||
REQUEST_TIMEOUT = config_manager.get_float('REQUESTS', 'timeout')
|
||||
TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
|
||||
|
@ -119,15 +119,15 @@ class M3U8_Ts_Estimator:
|
||||
|
||||
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
||||
progress_str = (
|
||||
f"{Colors.GREEN}{number_file_total_size} {Colors.WHITE}< {Colors.RED}{units_file_total_size}"
|
||||
f"{Colors.WHITE} {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
|
||||
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
||||
f"{Colors.WHITE}, {Colors.CYAN}{average_internet_speed} {Colors.RED}{average_internet_unit}"
|
||||
f"{Colors.WHITE}, {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
||||
)
|
||||
|
||||
else:
|
||||
retry_count = self.segments_instance.active_retries if self.segments_instance else 0
|
||||
progress_str = (
|
||||
f"{Colors.GREEN}{number_file_total_size} {Colors.WHITE}< {Colors.RED}{units_file_total_size}"
|
||||
f"{Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size}"
|
||||
f"{Colors.WHITE}, {Colors.GREEN}CRR {Colors.RED}{retry_count} "
|
||||
)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
__title__ = 'StreamingCommunity'
|
||||
__version__ = '2.9.3'
|
||||
__version__ = '2.9.4'
|
||||
__author__ = 'Arrowar'
|
||||
__description__ = 'A command-line program to download film'
|
||||
__copyright__ = 'Copyright 2024'
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 11.03.25
|
||||
|
||||
from .run import main
|
||||
from .Lib.Downloader.HLS import downloader as HLS_Downloader
|
||||
from .Lib.Downloader.MP4 import downloader as MP4_Downloader
|
||||
from .Lib.Downloader.TOR import downloader as TOR_Downloader
|
||||
from .Lib.Downloader.HLS.downloader import HLS_Downloader
|
||||
from .Lib.Downloader.MP4.downloader import MP4_downloader
|
||||
from .Lib.Downloader.TOR.downloader import TOR_downloader
|
Loading…
x
Reference in New Issue
Block a user