This commit is contained in:
Lovi 2025-01-04 10:40:08 +01:00
parent b40d202255
commit d03ece1cf2
33 changed files with 216 additions and 159 deletions

View File

@ -214,12 +214,14 @@ The configuration file is divided into several main sections:
"movie_folder_name": "Movie",
"serie_folder_name": "TV",
"map_episode_name": "%(tv_name)_S%(season)E%(episode)_%(episode_name)",
"add_siteName": false,
"disable_searchDomain": false,
"not_close": false
}
```
- `root_path`: Directory where all videos will be saved
### Path examples:
* Windows: `C:\\MyLibrary\\Folder` or `\\\\MyServer\\MyLibrary` (if you want to use a network folder)
* Linux/MacOS: `Desktop/MyLibrary/Folder`
@ -241,7 +243,10 @@ The configuration file is divided into several main sections:
* `%(episode_name)` : Is the name of the episode
`<br/><br/>`
- `not_close`: If true, continues running after downloading
- `add_siteName`: If set to true, appends the site_name to the root path before the movie and serie folders.
- `disable_searchDomain`: If set to true, disables the search for a new domain for all sites.
- `not_close`: If set to true, keeps the program running after the download is complete.
### qBittorrent Configuration
@ -258,7 +263,6 @@ The configuration file is divided into several main sections:
To enable qBittorrent integration, follow the setup guide [here](https://github.com/lgallard/qBittorrent-Controller/wiki/How-to-enable-the-qBittorrent-Web-UI).
<br>
## REQUESTS Settings
@ -272,7 +276,6 @@ The configuration file is divided into several main sections:
- `timeout`: Maximum timeout (in seconds) for each request
- `max_retry`: Number of retry attempts per segment during M3U8 index download
<br>
## M3U8_DOWNLOAD Settings
@ -293,10 +296,9 @@ The configuration file is divided into several main sections:
- `cleanup_tmp_folder`: Remove temporary .ts files after download
> [!IMPORTANT]
> Set `tqdm_use_large_bar` to `false` when using Termux or terminals with limited width to prevent display issues
> Set `tqdm_use_large_bar` to `false` when using Termux or terminals with limited width to prevent network monitoring issues
<br>
### Language Settings
@ -345,7 +347,6 @@ forced-ita hin - Hindi pol - Polish tur - Turkish
- `force_resolution`: Force specific resolution (-1 for best available, or specify 1080, 720, 360)
- `get_only_link`: Return M3U8 playlist/index URL instead of downloading
<br>
# COMMAND
@ -361,7 +362,6 @@ forced-ita hin - Hindi pol - Polish tur - Turkish
- 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.
<br>
# Docker

View File

@ -5,20 +5,14 @@ from typing import Dict, Any, List, Union
class Episode:
def __init__(self, data: Dict[str, Any]):
self.images = None
self.data = data
self.id: int = data.get('id')
self.scws_id: int = data.get('scws_id')
self.number: int = data.get('number')
self.name: str = data.get('name')
self.plot: str = data.get('plot')
self.duration: int = data.get('duration')
def collect_image(self, SITE_NAME, domain):
self.image = None
if len(self.data.get('images')) > 0:
self.image = f"https://cdn.{SITE_NAME}.{domain}/images/{self.data.get('images')[0]['filename']}"
self.id: int = data.get('id', 0)
self.scws_id: int = data.get('scws_id', 0)
self.number: int = data.get('number', 1)
self.name: str = data.get('name', '')
self.plot: str = data.get('plot', '')
self.duration: int = data.get('duration', 0)
def __str__(self):
return f"Episode(id={self.id}, number={self.number}, name='{self.name}', plot='{self.plot}', duration={self.duration} sec)"
@ -73,24 +67,19 @@ class EpisodeManager:
class Season:
def __init__(self, season_data: Dict[str, Union[int, str, None]]):
self.images = {}
self.season_data = season_data
self.id: int = season_data.get('id')
self.scws_id: int = season_data.get('scws_id')
self.imdb_id: int = season_data.get('imdb_id')
self.number: int = season_data.get('number')
self.name: str = season_data.get('name')
self.slug: str = season_data.get('slug')
self.plot: str = season_data.get('plot')
self.type: str = season_data.get('type')
self.seasons_count: int = season_data.get('seasons_count')
self.id: int = season_data.get('id', 0)
self.scws_id: int = season_data.get('scws_id', 0)
self.imdb_id: int = season_data.get('imdb_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.plot: str = season_data.get('plot', '')
self.type: str = season_data.get('type', '')
self.seasons_count: int = season_data.get('seasons_count', 0)
self.episodes: EpisodeManager = EpisodeManager()
def collect_images(self, SITE_NAME, domain):
for dict_image in self.season_data.get('images'):
self.images[dict_image.get('type')] = f"https://cdn.{SITE_NAME}.{domain}/images/{dict_image.get('filename')}"
class Stream:
def __init__(self, name: str, url: str, active: bool):

View File

@ -19,9 +19,11 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Variable
from .costant import SITE_NAME
from .costant import SITE_NAME, DOMAIN_NOW
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
def title_search(word_to_search: str) -> int:
@ -38,8 +40,10 @@ def title_search(word_to_search: str) -> int:
table_show_manager.clear()
# Find new domain if prev dont work
max_timeout = config_manager.get_int("REQUESTS", "timeout")
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
domain_to_use = DOMAIN_NOW
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}")
# Construct the full site URL and load the search page
try:

View File

@ -21,7 +21,7 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
# Config
from .costant import ROOT_PATH, DOMAIN_NOW, SITE_NAME, MOVIE_FOLDER
from .costant import DOMAIN_NOW, SITE_NAME, MOVIE_FOLDER
def download_title(select_title: MediaItem):
@ -39,7 +39,7 @@ def download_title(select_title: MediaItem):
# Define output path
title_name = os_manager.get_sanitize_file(select_title.name)
mp4_path = os_manager.get_sanitize_path(
os.path.join(ROOT_PATH, MOVIE_FOLDER, title_name.replace(".mp4", ""))
os.path.join(MOVIE_FOLDER, title_name.replace(".mp4", ""))
)
# Create output folder

View File

@ -11,5 +11,9 @@ SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
SERIES_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_folder_name'))
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))

View File

@ -22,7 +22,7 @@ from StreamingCommunity.Api.Player.supervideo import VideoSource
# Config
from .costant import ROOT_PATH, MOVIE_FOLDER
from .costant import MOVIE_FOLDER
def download_film(select_title: MediaItem) -> str:
@ -47,7 +47,7 @@ def download_film(select_title: MediaItem) -> str:
# Define output path
title_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
mp4_path = os_manager.get_sanitize_path(
os.path.join(ROOT_PATH, MOVIE_FOLDER, title_name.replace(".mp4", ""))
os.path.join(MOVIE_FOLDER, title_name.replace(".mp4", ""))
)
# Get m3u8 master playlist

View File

@ -19,9 +19,11 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Variable
from .costant import SITE_NAME
from .costant import SITE_NAME, DOMAIN_NOW
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
def title_search(title_search: str) -> int:
@ -38,9 +40,11 @@ def title_search(title_search: str) -> int:
table_show_manager.clear()
# Find new domain if prev dont work
max_timeout = config_manager.get_int("REQUESTS", "timeout")
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
domain_to_use = DOMAIN_NOW
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}")
# Send request to search for title
client = httpx.Client()

View File

@ -11,5 +11,9 @@ SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
SERIES_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_folder_name'))
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))

View File

@ -23,7 +23,7 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSourceAnime
# Variable
from .costant import ROOT_PATH, SITE_NAME, SERIES_FOLDER, MOVIE_FOLDER
from .costant import SITE_NAME, SERIES_FOLDER, MOVIE_FOLDER
@ -54,11 +54,11 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so
if scrape_serie.is_series:
mp4_path = os_manager.get_sanitize_path(
os.path.join(ROOT_PATH, SERIES_FOLDER, scrape_serie.series_name)
os.path.join(SERIES_FOLDER, scrape_serie.series_name)
)
else:
mp4_path = os_manager.get_sanitize_path(
os.path.join(ROOT_PATH, MOVIE_FOLDER, scrape_serie.series_name)
os.path.join(MOVIE_FOLDER, scrape_serie.series_name)
)
# Create output folder

View File

@ -21,10 +21,11 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Variable
from .costant import SITE_NAME
from .costant import SITE_NAME, DOMAIN_NOW
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
def get_token(site_name: str, domain: str) -> dict:
@ -40,7 +41,10 @@ def get_token(site_name: str, domain: str) -> dict:
"""
# Send a GET request to the specified URL composed of the site name and domain
response = httpx.get(f"https://www.{site_name}.{domain}")
response = httpx.get(
url=f"https://www.{site_name}.{domain}",
timeout=max_timeout
)
response.raise_for_status()
# Initialize variables to store CSRF token
@ -103,8 +107,10 @@ def title_search(title: str) -> int:
table_show_manager.clear()
# Get token and session value from configuration
max_timeout = config_manager.get_int("REQUESTS", "timeout")
domain_to_use, _ = search_domain(SITE_NAME, f"https://www.{SITE_NAME}")
domain_to_use = DOMAIN_NOW
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://www.{SITE_NAME}")
data = get_token(SITE_NAME, domain_to_use)

View File

@ -11,5 +11,9 @@ SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
SERIES_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_folder_name'))
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))

View File

@ -21,7 +21,7 @@ from StreamingCommunity.Api.Player.maxstream import VideoSource
# Config
from .costant import ROOT_PATH, MOVIE_FOLDER
from .costant import MOVIE_FOLDER
def download_film(select_title: MediaItem) -> str:
@ -46,7 +46,7 @@ def download_film(select_title: MediaItem) -> str:
# Define output path
title_name = os_manager.get_sanitize_file(select_title.name) +".mp4"
mp4_path = os_manager.get_sanitize_path(
os.path.join(ROOT_PATH, MOVIE_FOLDER, title_name.replace(".mp4", ""))
os.path.join(MOVIE_FOLDER, title_name.replace(".mp4", ""))
)
# Get m3u8 master playlist

View File

@ -18,9 +18,11 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Variable
from .costant import SITE_NAME
from .costant import SITE_NAME, DOMAIN_NOW
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
def title_search(word_to_search: str) -> int:
@ -37,8 +39,10 @@ def title_search(word_to_search: str) -> int:
table_show_manager.clear()
# Find new domain if prev dont work
max_timeout = config_manager.get_int("REQUESTS", "timeout")
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
domain_to_use = DOMAIN_NOW
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}")
response = httpx.get(
url=f"https://{SITE_NAME}.{domain_to_use}/?s={word_to_search}",

View File

@ -12,5 +12,9 @@ ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
COOKIE = config_manager.get_dict('SITE', SITE_NAME)['extra']
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
SERIES_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_folder_name'))
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))

View File

@ -24,7 +24,7 @@ from StreamingCommunity.Api.Player.ddl import VideoSource
# Variable
from .costant import ROOT_PATH, SERIES_FOLDER
from .costant import SERIES_FOLDER
@ -51,7 +51,7 @@ def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo,
title_name = os_manager.get_sanitize_file(
f"{map_episode_title(scape_info_serie.tv_name, None, index_episode_selected, obj_episode.get('name'))}.mp4"
)
mp4_path = os.path.join(ROOT_PATH, SERIES_FOLDER, scape_info_serie.tv_name)
mp4_path = os.path.join(SERIES_FOLDER, scape_info_serie.tv_name)
# Create output folder
os_manager.create_path(mp4_path)

View File

@ -22,9 +22,11 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Variable
from .costant import SITE_NAME
from .costant import SITE_NAME, DOMAIN_NOW
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
def title_search(word_to_search: str) -> int:
@ -41,8 +43,10 @@ def title_search(word_to_search: str) -> int:
table_show_manager.clear()
# Find new domain if prev dont work
max_timeout = config_manager.get_int("REQUESTS", "timeout")
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
domain_to_use = DOMAIN_NOW
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}")
# Send request to search for titles
try:

View File

@ -11,5 +11,9 @@ SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
SERIES_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_folder_name'))
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))

View File

@ -2,7 +2,6 @@
import os
import sys
import time
# Internal utilities
@ -24,7 +23,7 @@ from StreamingCommunity.Api.Player.supervideo import VideoSource
# Variable
from .costant import ROOT_PATH, SERIES_FOLDER
from .costant import SERIES_FOLDER
@ -50,7 +49,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap
# Define filename and path for the downloaded video
mp4_name = f"{map_episode_title(scape_info_serie.tv_name, index_season_selected, index_episode_selected, obj_episode.get('name'))}.mp4"
mp4_path = os.path.join(ROOT_PATH, SERIES_FOLDER, scape_info_serie.tv_name, f"S{index_season_selected}")
mp4_path = os.path.join(SERIES_FOLDER, scape_info_serie.tv_name, f"S{index_season_selected}")
# Setup video source
video_source = VideoSource(obj_episode.get('url'))

View File

@ -19,9 +19,11 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Variable
from .costant import SITE_NAME
from .costant import SITE_NAME, DOMAIN_NOW
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
def title_search(word_to_search: str) -> int:
@ -38,8 +40,10 @@ def title_search(word_to_search: str) -> int:
table_show_manager.clear()
# Find new domain if prev dont work
max_timeout = config_manager.get_int("REQUESTS", "timeout")
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
domain_to_use = DOMAIN_NOW
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}")
# Send request to search for titles
try:

View File

@ -11,5 +11,9 @@ SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
SERIES_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_folder_name'))
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))

View File

@ -2,6 +2,7 @@
# Internal utilities
from StreamingCommunity.Util._jsonConfig import config_manager
from StreamingCommunity.Util.table import TVShowManager
@ -13,9 +14,11 @@ from .util.ilCorsarScraper import IlCorsaroNeroScraper
# Variable
from .costant import SITE_NAME
from .costant import SITE_NAME, DOMAIN_NOW
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
async def title_search(word_to_search: str) -> int:
@ -32,7 +35,10 @@ async def title_search(word_to_search: str) -> int:
table_show_manager.clear()
# Find new domain if prev dont work
domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}")
domain_to_use = DOMAIN_NOW
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}")
# Create scraper and collect result
print("\n")

View File

@ -15,7 +15,7 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaItem
# Config
from .costant import ROOT_PATH, MOVIE_FOLDER
from .costant import MOVIE_FOLDER
def download_title(select_title: MediaItem):
@ -27,13 +27,13 @@ def download_title(select_title: MediaItem):
"""
start_message()
console.print(f"[yellow]Download: [red]{select_title.name} \n")
console.print(f"[yellow]Download: [red]{select_title.name} \n")
print()
# Define output path
title_name = os_manager.get_sanitize_file(select_title.name)
mp4_path = os_manager.get_sanitize_path(
os.path.join(ROOT_PATH, MOVIE_FOLDER, title_name.replace(".mp4", ""))
os.path.join(MOVIE_FOLDER, title_name.replace(".mp4", ""))
)
# Create output folder

View File

@ -11,5 +11,9 @@ SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
SERIES_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_folder_name'))
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))

View File

@ -33,7 +33,7 @@ from StreamingCommunity.Lib.TMBD import Json_film
# Config
from .costant import ROOT_PATH, SITE_NAME, DOMAIN_NOW, MOVIE_FOLDER
from .costant import SITE_NAME, DOMAIN_NOW, MOVIE_FOLDER
def download_film(movie_details: Json_film) -> str:
@ -75,7 +75,7 @@ def download_film(movie_details: Json_film) -> str:
# Define output path
title_name = os_manager.get_sanitize_file(movie_details.title) + ".mp4"
mp4_path = os.path.join(ROOT_PATH, MOVIE_FOLDER, title_name.replace(".mp4", ""))
mp4_path = os.path.join(MOVIE_FOLDER, title_name.replace(".mp4", ""))
# Get m3u8 master playlist
master_playlist = video_source.get_playlist()

View File

@ -9,7 +9,11 @@ from StreamingCommunity.Util._jsonConfig import config_manager
SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
DOMAIN_NOW = config_manager.get('SITE', SITE_NAME)
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
SERIES_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, config_manager.get('DEFAULT', 'movie_folder_name'))
if config_manager.get_bool("DEFAULT", "add_siteName"):
SERIES_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'serie_folder_name'))
MOVIE_FOLDER = os.path.join(ROOT_PATH, SITE_NAME, config_manager.get('DEFAULT', 'movie_folder_name'))

View File

@ -22,7 +22,7 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSource
# Variable
from .costant import ROOT_PATH, SITE_NAME, MOVIE_FOLDER
from .costant import SITE_NAME, MOVIE_FOLDER
def download_film(select_title: MediaItem) -> str:
@ -51,8 +51,8 @@ def download_film(select_title: MediaItem) -> str:
master_playlist = video_source.get_playlist()
# Define the filename and path for the downloaded film
title_name = os_manager.get_sanitize_file(select_title.slug) + ".mp4"
mp4_path = os.path.join(ROOT_PATH, MOVIE_FOLDER, select_title.slug)
title_name = os_manager.get_sanitize_file(select_title.name) + ".mp4"
mp4_path = os.path.join(MOVIE_FOLDER, select_title.name)
# Download the film using the m3u8 playlist, and output filename
r_proc = HLS_Downloader(

View File

@ -24,7 +24,7 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSource
# Variable
from .costant import ROOT_PATH, SITE_NAME, SERIES_FOLDER
from .costant import SITE_NAME, SERIES_FOLDER
@ -48,7 +48,7 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra
# Define filename and path for the downloaded video
mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
mp4_path = os.path.join(ROOT_PATH, SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}")
mp4_path = os.path.join(SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}")
# Retrieve scws and if available master playlist
video_source.get_iframe(obj_episode.id)

View File

@ -26,30 +26,36 @@ from StreamingCommunity.Api.Template.Class.SearchType import MediaManager
# Config
from .costant import SITE_NAME
from .costant import SITE_NAME, DOMAIN_NOW
# Variable
media_search_manager = MediaManager()
table_show_manager = TVShowManager()
max_timeout = config_manager.get_int("REQUESTS", "timeout")
disable_searchDomain = config_manager.get_bool("DEFAULT", "disable_searchDomain")
def get_version(text: str):
def get_version(domain: str):
"""
Extracts the version from the HTML text of a webpage.
Parameters:
- text (str): The HTML text of the webpage.
- domain (str): The domain of the site.
Returns:
str: The version extracted from the webpage.
list: Top 10 titles headlines for today.
"""
try:
response = httpx.get(
url=f"https://{SITE_NAME}.{domain}/",
headers={'User-Agent': get_headers()},
timeout=max_timeout
)
response.raise_for_status()
# Parse request to site
soup = BeautifulSoup(text, "html.parser")
soup = BeautifulSoup(response.text, "html.parser")
# Extract version
version = json.loads(soup.find("div", {"id": "app"}).get("data-page"))['version']
@ -72,22 +78,13 @@ def get_version_and_domain():
"""
# Find new domain if prev dont work
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}")
domain_to_use = DOMAIN_NOW
# Extract version from the response
try:
version = get_version(
httpx.get(
url=base_url,
headers={'User-Agent': get_headers()},
timeout=max_timeout
).text
)
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}")
version = get_version(domain_to_use)
except:
console.print("[green]Auto generate version ...")
version = secrets.token_hex(32 // 2)
return version, domain_to_use

View File

@ -1,10 +1,12 @@
# 01.03.24
import json
import logging
# External libraries
import httpx
from bs4 import BeautifulSoup
# Internal utilities
@ -56,33 +58,33 @@ class ScrapeSerie:
Raises:
Exception: If there's an error fetching season information
"""
self.headers = {
'user-agent': get_headers(),
'x-inertia': 'true',
'x-inertia-version': self.version,
}
try:
response = httpx.get(
url=f"https://{self.base_name}.{self.domain}/titles/{self.media_id}-{self.series_name}",
headers=self.headers,
url=f"https://{self.base_name}.{self.domain}/titles/{self.media_id}-{self.series_name}",
headers=self.headers,
timeout=max_timeout
)
response.raise_for_status()
# Extract seasons from JSON response
json_response = response.json().get('props')
soup = BeautifulSoup(response.text, "html.parser")
json_response = json.loads(soup.find("div", {"id": "app"}).get("data-page"))
"""
response = httpx.post(
url=f'https://{self.base_name}.{self.domain}/api/titles/preview/{self.media_id}',
headers={'User-Agent': get_headers()}
)
response.raise_for_status()
# Extract seasons from JSON response
json_response = response.json()
"""
# Collect info about season
self.season_manager = Season(json_response.get('title'))
self.season_manager.collect_images(self.base_name, self.domain)
self.season_manager = Season(json_response.get("props").get("title"))
# Collect first episode info
for i, ep in enumerate(json_response.get('loadedSeason').get('episodes')):
self.season_manager.episodes.add(ep)
self.season_manager.episodes.get(i).collect_image(self.base_name, self.domain)
except Exception as e:
logging.error(f"Error collecting season info: {e}")
raise
@ -97,16 +99,14 @@ class ScrapeSerie:
Raises:
Exception: If there's an error fetching episode information
"""
self.headers = {
'user-agent': get_headers(),
'x-inertia': 'true',
'x-inertia-version': self.version,
}
try:
response = httpx.get(
url=f'https://{self.base_name}.{self.domain}/titles/{self.media_id}-{self.series_name}/stagione-{number_season}',
headers=self.headers,
headers={
'User-Agent': get_headers(),
'x-inertia': 'true',
'x-inertia-version': self.version,
},
timeout=max_timeout
)
response.raise_for_status()

View File

@ -104,14 +104,15 @@ class HttpClient:
response = httpx.get(
url=url,
headers=self.headers,
timeout=max_timeout
timeout=max_timeout,
follow_redirects=True
)
response.raise_for_status()
return response.text
except Exception as e:
logging.info(f"Request to {url} failed with error: {e}")
console.print(f"Request to {url} failed with error: {e}")
return 404
def get_content(self, url):

View File

@ -194,23 +194,28 @@ class M3U8_Segments:
"""
if self.is_index_url:
# Send a GET request to retrieve the index M3U8 file
response = httpx.get(
self.url,
headers={'User-Agent': get_headers()},
timeout=max_timeout
)
response.raise_for_status()
try:
# Save the M3U8 file to the temporary folder
path_m3u8_file = os.path.join(self.tmp_folder, "playlist.m3u8")
open(path_m3u8_file, "w+").write(response.text)
# Send a GET request to retrieve the index M3U8 file
response = httpx.get(
self.url,
headers={'User-Agent': get_headers()},
timeout=max_timeout,
follow_redirects=True
)
response.raise_for_status()
# Parse the text from the M3U8 index file
self.parse_data(response.text)
# Save the M3U8 file to the temporary folder
path_m3u8_file = os.path.join(self.tmp_folder, "playlist.m3u8")
open(path_m3u8_file, "w+").write(response.text)
# Parse the text from the M3U8 index file
self.parse_data(response.text)
except Exception as e:
print(f"Error during M3U8 index request: {e}")
else:
# Parser data of content of index pass in input to class
self.parse_data(self.url)
@ -385,7 +390,7 @@ class M3U8_Segments:
buffer[index] = segment_content
except queue.Empty:
self.current_timeout = min(self.max_timeout, self.current_timeout * 1.5)
self.current_timeout = min(self.max_timeout, self.current_timeout * 1.25)
if self.stop_event.is_set():
break
@ -546,7 +551,7 @@ class M3U8_Segments:
raise Exception("Output file is empty")
# Display additional
if self.info_nRetry >= len(self.segments) * (1/3.33):
if self.info_nRetry >= len(self.segments) * 0.3:
# Get expected time
ex_hours, ex_minutes, ex_seconds = format_duration(self.expected_real_time_s)

View File

@ -1,5 +1,5 @@
__title__ = 'StreamingCommunity'
__version__ = '2.2.0'
__version__ = '2.3.0'
__author__ = 'Lovi-0'
__description__ = 'A command-line program to download film'
__copyright__ = 'Copyright 2024'

View File

@ -15,6 +15,8 @@
"user": "admin",
"pass": "adminadmin"
},
"add_siteName": true,
"disable_searchDomain": true,
"not_close": false
},
"REQUESTS": {
@ -24,7 +26,7 @@
"proxy_start_max": 0.5
},
"M3U8_DOWNLOAD": {
"tqdm_delay": 0.01,
"tqdm_delay": 0.12,
"tqdm_use_large_bar": true,
"default_video_workser": 12,
"default_audio_workser": 12,