diff --git a/README.md b/README.md
index a9be866..d4422c6 100644
--- a/README.md
+++ b/README.md
@@ -314,6 +314,24 @@ forced-ita hin - Hindi pol - Polish tur - Turkish
+
+# 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.
+
+
+
# Docker
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
-- 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
diff --git a/StreamingCommunity/Api/Player/Helper/Vixcloud/util.py b/StreamingCommunity/Api/Player/Helper/Vixcloud/util.py
index 4c29ee1..cccf4b7 100644
--- a/StreamingCommunity/Api/Player/Helper/Vixcloud/util.py
+++ b/StreamingCommunity/Api/Player/Helper/Vixcloud/util.py
@@ -1,17 +1,22 @@
# 23.11.24
-import re
-import logging
from typing import Dict, Any, List, Union
class Episode:
def __init__(self, data: Dict[str, Any]):
- self.id: int = data.get('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', '')
+ 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 = f"https://cdn.{SITE_NAME}.{domain}/images/{self.data.get('images')[0]['filename']}"
def __str__(self):
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):
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.
@@ -29,8 +34,20 @@ class EpisodeManager:
"""
episode = Episode(episode_data)
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.
@@ -54,61 +71,23 @@ 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.episodes_count: int = season_data.get('episodes_count')
-
- def __str__(self):
- return f"Season(id={self.id}, number={self.number}, name='{self.name}', plot='{self.plot}', episodes_count={self.episodes_count})"
-
-class SeasonManager:
- def __init__(self):
- 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)})"
+ self.type: str = season_data.get('type')
+ self.seasons_count: int = season_data.get('seasons_count')
+ 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:
diff --git a/StreamingCommunity/Api/Player/vixcloud.py b/StreamingCommunity/Api/Player/vixcloud.py
index 6bc4bd8..4351467 100644
--- a/StreamingCommunity/Api/Player/vixcloud.py
+++ b/StreamingCommunity/Api/Player/vixcloud.py
@@ -169,6 +169,56 @@ class VideoSource:
# Construct the new URL with updated query parameters
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):
def __init__(self, site_name: str):
@@ -221,4 +271,4 @@ class VideoSourceAnime(VideoSource):
except Exception as e:
logging.error(f"Error fetching embed URL: {e}")
- return None
+ return None
\ No newline at end of file
diff --git a/StreamingCommunity/Api/Site/ddlstreamitaly/costant.py b/StreamingCommunity/Api/Site/ddlstreamitaly/costant.py
index e2f7a5e..858bfd8 100644
--- a/StreamingCommunity/Api/Site/ddlstreamitaly/costant.py
+++ b/StreamingCommunity/Api/Site/ddlstreamitaly/costant.py
@@ -10,7 +10,7 @@ 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_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')
MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name')
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/series.py b/StreamingCommunity/Api/Site/streamingcommunity/series.py
index 7be3b97..f32c837 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/series.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/series.py
@@ -42,13 +42,13 @@ def download_video(tv_name: str, index_season_selected: int, index_episode_selec
start_message()
# 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}")
print()
# 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_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
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
- scrape_serie.obj_episode_manager.clear()
- season_number = scrape_serie.obj_season_manager.seasons[index_season_selected - 1].number
+ scrape_serie.episode_manager.clear()
# Start message and collect information about episodes
start_message()
- scrape_serie.collect_title_season(season_number)
- episodes_count = scrape_serie.obj_episode_manager.get_length()
+ scrape_serie.collect_info_season(index_season_selected)
+ episodes_count = scrape_serie.episode_manager.length()
if download_all:
@@ -137,8 +136,8 @@ def download_series(select_season: MediaItem, version: str) -> None:
video_source.setup(select_season.id)
# Collect information about seasons
- scrape_serie.collect_info_seasons()
- seasons_count = scrape_serie.obj_season_manager.get_length()
+ scrape_serie.collect_info_title()
+ seasons_count = scrape_serie.season_manager.seasons_count
# Prompt user for season selection and download episodes
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)
# 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({
'Index': str(media.number),
'Name': media.name,
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py b/StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py
index 1f73f3e..3dc2fd5 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/util/ScrapeSerie.py
@@ -10,7 +10,7 @@ import httpx
# Internal utilities
from StreamingCommunity.Util.headers import get_headers
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
@@ -46,10 +46,10 @@ class ScrapeSerie:
if series_name is not None:
self.is_series = True
self.series_name = series_name
- self.obj_season_manager: SeasonManager = SeasonManager()
- self.obj_episode_manager: EpisodeManager = EpisodeManager()
+ self.season_manager = None
+ 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.
@@ -72,17 +72,22 @@ class ScrapeSerie:
response.raise_for_status()
# Extract seasons from JSON response
- json_response = response.json().get('props', {}).get('title', {}).get('seasons', [])
-
- # Add each season to the season manager
- for dict_season in json_response:
- self.obj_season_manager.add_season(dict_season)
+ json_response = response.json().get('props')
+ # 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:
logging.error(f"Error collecting season info: {e}")
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.
@@ -92,8 +97,13 @@ class ScrapeSerie:
Raises:
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(
url=f'https://{self.base_name}.{self.domain}/titles/{self.media_id}-{self.series_name}/stagione-{number_season}',
headers=self.headers,
@@ -102,11 +112,11 @@ class ScrapeSerie:
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
for dict_episode in json_response:
- self.obj_episode_manager.add_episode(dict_episode)
+ self.episode_manager.add(dict_episode)
except Exception as e:
logging.error(f"Error collecting title season info: {e}")
diff --git a/config.json b/config.json
index 287471a..75103e9 100644
--- a/config.json
+++ b/config.json
@@ -62,7 +62,7 @@
},
"SITE": {
"streamingcommunity": {
- "domain": "asia"
+ "domain": "family"
},
"altadefinizione": {
"domain": "now"
@@ -75,7 +75,7 @@
},
"ddlstreamitaly": {
"domain": "co",
- "cookie": {
+ "extra": {
"ips4_device_key": "",
"ips4_member_id": "",
"ips4_login_key": ""