api: Add "altadefinizione"

This commit is contained in:
Lovi 2025-03-15 15:43:11 +01:00
parent 6bea3c849b
commit a63ee445dd
14 changed files with 610 additions and 85 deletions

View File

@ -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 💿

View File

@ -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:

View File

@ -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"

View 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']

View 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)

View 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()

View File

@ -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
})

View File

@ -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)

View File

@ -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

View File

@ -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')

View File

@ -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} "
)

View File

@ -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'

View File

@ -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

View File

@ -10,7 +10,7 @@ with open("requirements.txt", "r", encoding="utf-8-sig") as f:
setup(
name="StreamingCommunity",
version="2.9.3",
version="2.9.4",
long_description=read_readme(),
long_description_content_type="text/markdown",
author="Lovi-0",