mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-07-22 18:10:02 +00:00
Improve site SC
This commit is contained in:
parent
f5ad9b7187
commit
0bedb1bf05
26
README.md
26
README.md
@ -314,6 +314,24 @@ forced-ita hin - Hindi pol - Polish tur - Turkish
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
# COMMAND
|
||||||
|
|
||||||
|
|
||||||
|
- Download a specific season by entering its number.
|
||||||
|
* **Example:** `1` will download *Season 1* only.
|
||||||
|
|
||||||
|
- Use the wildcard `*` to download every available season.
|
||||||
|
* **Example:** `*` will download all seasons in the series.
|
||||||
|
|
||||||
|
- Specify a range of seasons using a hyphen `-`.
|
||||||
|
* **Example:** `1-2` will download *Seasons 1 and 2*.
|
||||||
|
|
||||||
|
- 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
|
# Docker
|
||||||
|
|
||||||
You can run the script in a docker container, to build the image just run
|
You can run the script in a docker container, to build the image just run
|
||||||
@ -371,7 +389,13 @@ The `run-container` command mounts also the `config.json` file, so any change to
|
|||||||
|
|
||||||
# To Do
|
# To Do
|
||||||
|
|
||||||
- Create website API
|
- Create website API -> https://github.com/Lovi-0/StreamingCommunity/tree/test_gui_1
|
||||||
|
|
||||||
|
# SUPPORT
|
||||||
|
|
||||||
|
If you'd like to support this project, consider making a donation!
|
||||||
|
|
||||||
|
[](https://www.paypal.com/donate/?hosted_button_id=UXTWMT8P6HE2C)
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
# 23.11.24
|
# 23.11.24
|
||||||
|
|
||||||
import re
|
|
||||||
import logging
|
|
||||||
from typing import Dict, Any, List, Union
|
from typing import Dict, Any, List, Union
|
||||||
|
|
||||||
|
|
||||||
class Episode:
|
class Episode:
|
||||||
def __init__(self, data: Dict[str, Any]):
|
def __init__(self, data: Dict[str, Any]):
|
||||||
self.id: int = data.get('id', '')
|
self.images = None
|
||||||
self.number: int = data.get('number', '')
|
self.data = data
|
||||||
self.name: str = data.get('name', '')
|
|
||||||
self.plot: str = data.get('plot', '')
|
self.id: int = data.get('id')
|
||||||
self.duration: int = data.get('duration', '')
|
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 = f"https://cdn.{SITE_NAME}.{domain}/images/{self.data.get('images')[0]['filename']}"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Episode(id={self.id}, number={self.number}, name='{self.name}', plot='{self.plot}', duration={self.duration} sec)"
|
return f"Episode(id={self.id}, number={self.number}, name='{self.name}', plot='{self.plot}', duration={self.duration} sec)"
|
||||||
@ -20,7 +25,7 @@ class EpisodeManager:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.episodes: List[Episode] = []
|
self.episodes: List[Episode] = []
|
||||||
|
|
||||||
def add_episode(self, episode_data: Dict[str, Any]):
|
def add(self, episode_data: Dict[str, Any]):
|
||||||
"""
|
"""
|
||||||
Add a new episode to the manager.
|
Add a new episode to the manager.
|
||||||
|
|
||||||
@ -29,8 +34,20 @@ class EpisodeManager:
|
|||||||
"""
|
"""
|
||||||
episode = Episode(episode_data)
|
episode = Episode(episode_data)
|
||||||
self.episodes.append(episode)
|
self.episodes.append(episode)
|
||||||
|
|
||||||
|
def get(self, index: int) -> Episode:
|
||||||
|
"""
|
||||||
|
Retrieve an episode by its index in the episodes list.
|
||||||
|
|
||||||
|
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 get_length(self) -> int:
|
def length(self) -> int:
|
||||||
"""
|
"""
|
||||||
Get the number of episodes in the manager.
|
Get the number of episodes in the manager.
|
||||||
|
|
||||||
@ -54,61 +71,23 @@ class EpisodeManager:
|
|||||||
|
|
||||||
class Season:
|
class Season:
|
||||||
def __init__(self, season_data: Dict[str, Union[int, str, None]]):
|
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.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.number: int = season_data.get('number')
|
||||||
self.name: str = season_data.get('name')
|
self.name: str = season_data.get('name')
|
||||||
|
self.slug: str = season_data.get('slug')
|
||||||
self.plot: str = season_data.get('plot')
|
self.plot: str = season_data.get('plot')
|
||||||
self.episodes_count: int = season_data.get('episodes_count')
|
self.type: str = season_data.get('type')
|
||||||
|
self.seasons_count: int = season_data.get('seasons_count')
|
||||||
def __str__(self):
|
self.episodes: EpisodeManager = EpisodeManager()
|
||||||
return f"Season(id={self.id}, number={self.number}, name='{self.name}', plot='{self.plot}', episodes_count={self.episodes_count})"
|
|
||||||
|
def collect_images(self, SITE_NAME, domain):
|
||||||
class SeasonManager:
|
for dict_image in self.season_data.get('images'):
|
||||||
def __init__(self):
|
self.images[dict_image.get('type')] = f"https://cdn.{SITE_NAME}.{domain}/images/{dict_image.get('filename')}"
|
||||||
self.seasons: List[Season] = []
|
|
||||||
|
|
||||||
def add_season(self, season_data: Dict[str, Union[int, str, None]]):
|
|
||||||
"""
|
|
||||||
Add a new season to the manager.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
season_data (Dict[str, Union[int, str, None]]): A dictionary containing data for the new season.
|
|
||||||
"""
|
|
||||||
season = Season(season_data)
|
|
||||||
self.seasons.append(season)
|
|
||||||
|
|
||||||
def get(self, index: int) -> Season:
|
|
||||||
"""
|
|
||||||
Get a season item from the list by index.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
index (int): The index of the seasons item to retrieve.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Season: The media item at the specified index.
|
|
||||||
"""
|
|
||||||
return self.media_list[index]
|
|
||||||
|
|
||||||
def get_length(self) -> int:
|
|
||||||
"""
|
|
||||||
Get the number of seasons in the manager.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: Number of seasons.
|
|
||||||
"""
|
|
||||||
return len(self.seasons)
|
|
||||||
|
|
||||||
def clear(self) -> None:
|
|
||||||
"""
|
|
||||||
This method clears the seasons list.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
self: The object instance.
|
|
||||||
"""
|
|
||||||
self.seasons.clear()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"SeasonManager(num_seasons={len(self.seasons)})"
|
|
||||||
|
|
||||||
|
|
||||||
class Stream:
|
class Stream:
|
||||||
|
@ -169,6 +169,56 @@ class VideoSource:
|
|||||||
# Construct the new URL with updated query parameters
|
# Construct the new URL with updated query parameters
|
||||||
return urlunparse(parsed_url._replace(query=query_string))
|
return urlunparse(parsed_url._replace(query=query_string))
|
||||||
|
|
||||||
|
def get_mp4(self, url_to_download: str, scws_id: str) -> list:
|
||||||
|
"""
|
||||||
|
Generate download links for the specified resolutions from StreamingCommunity.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url_to_download (str): URL of the video page.
|
||||||
|
scws_id (str): SCWS ID of the title.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: A list of video download URLs.
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
'referer': url_to_download,
|
||||||
|
'user-agent': get_headers(),
|
||||||
|
}
|
||||||
|
|
||||||
|
# API request to get video details
|
||||||
|
video_api_url = f'https://{self.base_name}.{self.domain}/api/video/{scws_id}'
|
||||||
|
response = httpx.get(video_api_url, headers=headers)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
video_tracks = response_json.get('video_tracks', [])
|
||||||
|
track = video_tracks[-1]
|
||||||
|
console.print(f"[cyan]Available resolutions: [red]{[str(track['quality']) for track in video_tracks]}")
|
||||||
|
|
||||||
|
# Request download link generation for each track
|
||||||
|
download_response = httpx.post(
|
||||||
|
url=f'https://{self.base_name}.{self.domain}/api/download/generate_link?scws_id={track["video_id"]}&rendition={track["quality"]}',
|
||||||
|
headers={
|
||||||
|
'referer': url_to_download,
|
||||||
|
'user-agent': get_headers(),
|
||||||
|
'x-xsrf-token': config_manager.get("SITE", self.base_name)['extra']['x-xsrf-token']
|
||||||
|
},
|
||||||
|
cookies={
|
||||||
|
'streamingcommunity_session': config_manager.get("SITE", self.base_name)['extra']['streamingcommunity_session']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if download_response.status_code == 200:
|
||||||
|
return {'url': download_response.text, 'quality': track["quality"]}
|
||||||
|
|
||||||
|
else:
|
||||||
|
logging.error(f"Failed to generate link for resolution {track['quality']} (HTTP {download_response.status_code}).")
|
||||||
|
|
||||||
|
else:
|
||||||
|
logging.error(f"Error fetching video API URL (HTTP {response.status_code}).")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class VideoSourceAnime(VideoSource):
|
class VideoSourceAnime(VideoSource):
|
||||||
def __init__(self, site_name: str):
|
def __init__(self, site_name: str):
|
||||||
@ -221,4 +271,4 @@ class VideoSourceAnime(VideoSource):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error fetching embed URL: {e}")
|
logging.error(f"Error fetching embed URL: {e}")
|
||||||
return None
|
return None
|
@ -10,7 +10,7 @@ from StreamingCommunity.Util._jsonConfig import config_manager
|
|||||||
SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
|
SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
|
||||||
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
ROOT_PATH = config_manager.get('DEFAULT', 'root_path')
|
||||||
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
|
DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain']
|
||||||
COOKIE = config_manager.get_dict('SITE', SITE_NAME)['cookie']
|
COOKIE = config_manager.get_dict('SITE', SITE_NAME)['extra']
|
||||||
|
|
||||||
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
|
SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name')
|
||||||
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
|
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
|
||||||
|
@ -42,13 +42,13 @@ def download_video(tv_name: str, index_season_selected: int, index_episode_selec
|
|||||||
start_message()
|
start_message()
|
||||||
|
|
||||||
# Get info about episode
|
# Get info about episode
|
||||||
obj_episode = scrape_serie.obj_episode_manager.episodes[index_episode_selected - 1]
|
obj_episode = scrape_serie.episode_manager.get(index_episode_selected - 1)
|
||||||
console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}")
|
console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
# Define filename and path for the downloaded video
|
# Define filename and path for the downloaded video
|
||||||
mp4_name = f"{map_episode_title(tv_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
|
mp4_name = f"{map_episode_title(tv_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4"
|
||||||
mp4_path = os.path.join(ROOT_PATH, SITE_NAME, SERIES_FOLDER, tv_name, f"S{index_season_selected}")
|
mp4_path = os.path.join(ROOT_PATH, SITE_NAME, SERIES_FOLDER, tv_name, f"S{index_season_selected}")
|
||||||
|
|
||||||
# Retrieve scws and if available master playlist
|
# Retrieve scws and if available master playlist
|
||||||
video_source.get_iframe(obj_episode.id)
|
video_source.get_iframe(obj_episode.id)
|
||||||
@ -84,13 +84,12 @@ def download_episode(tv_name: str, index_season_selected: int, scrape_serie: Scr
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Clean memory of all episodes and get the number of the season
|
# Clean memory of all episodes and get the number of the season
|
||||||
scrape_serie.obj_episode_manager.clear()
|
scrape_serie.episode_manager.clear()
|
||||||
season_number = scrape_serie.obj_season_manager.seasons[index_season_selected - 1].number
|
|
||||||
|
|
||||||
# Start message and collect information about episodes
|
# Start message and collect information about episodes
|
||||||
start_message()
|
start_message()
|
||||||
scrape_serie.collect_title_season(season_number)
|
scrape_serie.collect_info_season(index_season_selected)
|
||||||
episodes_count = scrape_serie.obj_episode_manager.get_length()
|
episodes_count = scrape_serie.episode_manager.length()
|
||||||
|
|
||||||
if download_all:
|
if download_all:
|
||||||
|
|
||||||
@ -137,8 +136,8 @@ def download_series(select_season: MediaItem, version: str) -> None:
|
|||||||
video_source.setup(select_season.id)
|
video_source.setup(select_season.id)
|
||||||
|
|
||||||
# Collect information about seasons
|
# Collect information about seasons
|
||||||
scrape_serie.collect_info_seasons()
|
scrape_serie.collect_info_title()
|
||||||
seasons_count = scrape_serie.obj_season_manager.get_length()
|
seasons_count = scrape_serie.season_manager.seasons_count
|
||||||
|
|
||||||
# Prompt user for season selection and download episodes
|
# Prompt user for season selection and download episodes
|
||||||
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
console.print(f"\n[green]Seasons found: [red]{seasons_count}")
|
||||||
@ -188,7 +187,7 @@ def display_episodes_list(scrape_serie) -> str:
|
|||||||
table_show_manager.add_column(column_info)
|
table_show_manager.add_column(column_info)
|
||||||
|
|
||||||
# Populate the table with episodes information
|
# Populate the table with episodes information
|
||||||
for i, media in enumerate(scrape_serie.obj_episode_manager.episodes):
|
for i, media in enumerate(scrape_serie.episode_manager.episodes):
|
||||||
table_show_manager.add_tv_show({
|
table_show_manager.add_tv_show({
|
||||||
'Index': str(media.number),
|
'Index': str(media.number),
|
||||||
'Name': media.name,
|
'Name': media.name,
|
||||||
|
@ -10,7 +10,7 @@ import httpx
|
|||||||
# Internal utilities
|
# Internal utilities
|
||||||
from StreamingCommunity.Util.headers import get_headers
|
from StreamingCommunity.Util.headers import get_headers
|
||||||
from StreamingCommunity.Util._jsonConfig import config_manager
|
from StreamingCommunity.Util._jsonConfig import config_manager
|
||||||
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import SeasonManager, EpisodeManager
|
from StreamingCommunity.Api.Player.Helper.Vixcloud.util import Season, EpisodeManager
|
||||||
|
|
||||||
|
|
||||||
# Variable
|
# Variable
|
||||||
@ -46,10 +46,10 @@ class ScrapeSerie:
|
|||||||
if series_name is not None:
|
if series_name is not None:
|
||||||
self.is_series = True
|
self.is_series = True
|
||||||
self.series_name = series_name
|
self.series_name = series_name
|
||||||
self.obj_season_manager: SeasonManager = SeasonManager()
|
self.season_manager = None
|
||||||
self.obj_episode_manager: EpisodeManager = EpisodeManager()
|
self.episode_manager: EpisodeManager = EpisodeManager()
|
||||||
|
|
||||||
def collect_info_seasons(self) -> None:
|
def collect_info_title(self) -> None:
|
||||||
"""
|
"""
|
||||||
Retrieve season information for a TV series from the streaming site.
|
Retrieve season information for a TV series from the streaming site.
|
||||||
|
|
||||||
@ -72,17 +72,22 @@ class ScrapeSerie:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
# Extract seasons from JSON response
|
# Extract seasons from JSON response
|
||||||
json_response = response.json().get('props', {}).get('title', {}).get('seasons', [])
|
json_response = response.json().get('props')
|
||||||
|
|
||||||
# Add each season to the season manager
|
|
||||||
for dict_season in json_response:
|
|
||||||
self.obj_season_manager.add_season(dict_season)
|
|
||||||
|
|
||||||
|
# Collect info about season
|
||||||
|
self.season_manager = Season(json_response.get('title'))
|
||||||
|
self.season_manager.collect_images(self.base_name, self.domain)
|
||||||
|
|
||||||
|
# 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:
|
except Exception as e:
|
||||||
logging.error(f"Error collecting season info: {e}")
|
logging.error(f"Error collecting season info: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def collect_title_season(self, number_season: int) -> None:
|
def collect_info_season(self, number_season: int) -> None:
|
||||||
"""
|
"""
|
||||||
Retrieve episode information for a specific season.
|
Retrieve episode information for a specific season.
|
||||||
|
|
||||||
@ -92,8 +97,13 @@ class ScrapeSerie:
|
|||||||
Raises:
|
Raises:
|
||||||
Exception: If there's an error fetching episode information
|
Exception: If there's an error fetching episode information
|
||||||
"""
|
"""
|
||||||
try:
|
self.headers = {
|
||||||
|
'user-agent': get_headers(),
|
||||||
|
'x-inertia': 'true',
|
||||||
|
'x-inertia-version': self.version,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
response = httpx.get(
|
response = httpx.get(
|
||||||
url=f'https://{self.base_name}.{self.domain}/titles/{self.media_id}-{self.series_name}/stagione-{number_season}',
|
url=f'https://{self.base_name}.{self.domain}/titles/{self.media_id}-{self.series_name}/stagione-{number_season}',
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
@ -102,11 +112,11 @@ class ScrapeSerie:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
# Extract episodes from JSON response
|
# 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 episode manager
|
||||||
for dict_episode in json_response:
|
for dict_episode in json_response:
|
||||||
self.obj_episode_manager.add_episode(dict_episode)
|
self.episode_manager.add(dict_episode)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error collecting title season info: {e}")
|
logging.error(f"Error collecting title season info: {e}")
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
},
|
},
|
||||||
"SITE": {
|
"SITE": {
|
||||||
"streamingcommunity": {
|
"streamingcommunity": {
|
||||||
"domain": "asia"
|
"domain": "family"
|
||||||
},
|
},
|
||||||
"altadefinizione": {
|
"altadefinizione": {
|
||||||
"domain": "now"
|
"domain": "now"
|
||||||
@ -75,7 +75,7 @@
|
|||||||
},
|
},
|
||||||
"ddlstreamitaly": {
|
"ddlstreamitaly": {
|
||||||
"domain": "co",
|
"domain": "co",
|
||||||
"cookie": {
|
"extra": {
|
||||||
"ips4_device_key": "",
|
"ips4_device_key": "",
|
||||||
"ips4_member_id": "",
|
"ips4_member_id": "",
|
||||||
"ips4_login_key": ""
|
"ips4_login_key": ""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user