Fix #38, #57, Implement Config file (#58)

* style: correct some user messages and typos

* feat(config file): implement configuration settings

* style(config): improve config variables readability

* style(config params): config conformity fix

* feat(config): add config feature to variables

* feat(config): add config feature examples to README.md

* style: uniformity

* fix(subtitles): add subtitles to corresponding folder

* fix(tv-series subtitles folder name)
This commit is contained in:
Cohvir 2024-03-04 22:34:36 +01:00 committed by GitHub
parent 0094458c3d
commit 9ee9c91a2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 203 additions and 101 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

14
.idea/StreamingCommunity_api.iml generated Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/StreamingCommunity_api.iml" filepath="$PROJECT_DIR$/.idea/StreamingCommunity_api.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -24,13 +24,13 @@ pip install -r requirements.txt
## Usage
Run the script with the following command:
```python
```powershell
python run.py
```
## Auto Update
Keep your script up to date with the latest features by running:
```python
```powershell
python update.py
```
@ -39,10 +39,32 @@ python update.py
- Download Specific Episodes or Entire Series: Seamlessly retrieve specific episodes or entire series using intuitive commands. Specify a range of episodes with square brackets notation, e.g., [5-7], or download all episodes with an asterisk (*).
- Download Subtitles: Automatically fetch subtitles if available for downloaded content. (Note: To disable this feature, navigate to ".\Src\Lib\FFmpeg\my_m3u8" and change 'DOWNLOAD_SUB' to False in the configuration file.)
- Download Subtitles: Automatically fetch subtitles if available for downloaded content. (Note: To disable this feature, see [Configuration](#configuration))
- Sync Audio and Video: Ensure perfect synchronization between audio and video during the download process for an enhanced viewing experience.
## Configuration
You can change some behaviors by tweaking the configuration file.
```json
{
"root_path": "videos",
"movies_folder_name": "Film",
"series_folder_name": "Serie",
"download_subtitles": true,
"download_default_language": false
}
```
#### Options
| Key | Default Value | Description | Value Example |
|---------------------------|---------------|------------------------------------------------------------------------------------|--------------------------|
| root_path | videos | Path where the script will add movies and tv series. Do not put trailing slash. | media/streamingcommunity |
| movies_folder_name | Film | The folder name where all the movies will be placed. Do not put trailing slash. | downloaded-movies |
| series_folder_name | Serie | The folder name where all the TV Series will be placed. Do not put trailing slash. | mytvseries |
| download_subtitles | true | Whether or not you want all the found subtitles to be downloaded. | false |
| download_default_language | false | Whether or not you want to download only the default Italian audio language. | true |
## Tutorial
For a detailed walkthrough, refer to the [video tutorial](https://www.youtube.com/watch?v=Ok7hQCgxqLg&ab_channel=Nothing)

View File

@ -3,12 +3,14 @@
# Class import
from Src.Util.headers import get_headers
from Src.Util.console import console
from Src.Util.config import config
from Src.Lib.FFmpeg.my_m3u8 import download_m3u8
# General import
import requests, os, re, json, sys, binascii
from bs4 import BeautifulSoup
# [func]
def get_iframe(id_title, domain):
req = requests.get(url = f"https://streamingcommunity.{domain}/iframe/{id_title}", headers = {
@ -22,13 +24,14 @@ def get_iframe(id_title, domain):
try:
return BeautifulSoup(req_embed, "lxml").find("body").find("script").text
except:
console.log("[red]Cant play this video, (video not available)")
console.log("[red]Couldn't play this video file (video not available)")
sys.exit(0)
else:
console.log(f"[red]Error: {req.status_code}")
sys.exit(0)
def select_quality(json_win_param):
if json_win_param['token1080p']:
@ -40,6 +43,7 @@ def select_quality(json_win_param):
else:
return "360p"
def parse_content(embed_content):
# Parse parameter from req embed content
@ -52,10 +56,12 @@ def parse_content(embed_content):
json_win_param = json_win_param.replace(",}", "}").replace("'", '"')
return json.loads(json_win_video), json.loads(json_win_param), select_quality(json.loads(json_win_param))
def get_m3u8_url(json_win_video, json_win_param, render_quality):
token_render = f"token{render_quality}"
return f"https://vixcloud.co/playlist/{json_win_video['id']}?type=video&rendition={render_quality}&token={json_win_param[token_render]}&expires={json_win_param['expires']}"
def get_m3u8_key(json_win_video, json_win_param, title_name, token_render):
response = requests.get('https://vixcloud.co/storage/enc.key', headers={
'referer': f'https://vixcloud.co/embed/{json_win_video["id"]}?token={json_win_param[token_render]}&title={title_name}&referer=1&expires={json_win_param["expires"]}',
@ -67,6 +73,7 @@ def get_m3u8_key(json_win_video, json_win_param, title_name, token_render):
console.log(f"[red]Error: {response.status_code}")
sys.exit(0)
def get_m3u8_audio(json_win_video, json_win_param, title_name, token_render):
req = requests.get(f'https://vixcloud.co/playlist/{json_win_video["id"]}', params={'token': json_win_param['token'], 'expires': json_win_param["expires"] }, headers={
'referer': f'https://vixcloud.co/embed/{json_win_video["id"]}?token={json_win_param[token_render]}&title={title_name}&referer=1&expires={json_win_param["expires"]}'
@ -89,18 +96,18 @@ def main_dw_film(id_film, title_name, domain):
json_win_video, json_win_param, render_quality = parse_content(embed_content)
token_render = f"token{render_quality}"
console.print(f"[blue]Quality select => [red]{render_quality}")
console.print(f"[blue]Selected quality => [red]{render_quality}")
m3u8_url = get_m3u8_url(json_win_video, json_win_param, render_quality)
m3u8_key = get_m3u8_key(json_win_video, json_win_param, title_name, token_render)
mp4_name = title_name.replace("+", " ").replace(",", "").replace("-", "_")
mp4_format = mp4_name + ".mp4"
mp4_path = os.path.join("videos", mp4_format)
mp4_path = os.path.join(config['root_path'], config['movies_folder_name'], mp4_name, mp4_format)
m3u8_url_audio = get_m3u8_audio(json_win_video, json_win_param, title_name, token_render)
if m3u8_url_audio != None:
console.print("[blue]Use m3u8 audio => [red]True")
download_m3u8(m3u8_index=m3u8_url, m3u8_audio=m3u8_url_audio, m3u8_subtitle=m3u8_url, key=m3u8_key, output_filename=mp4_path)
if m3u8_url_audio is not None:
console.print("[blue]Using m3u8 audio => [red]True")
subtitle_path = os.path.join(config['root_path'], config['series_folder_name'], mp4_name)
download_m3u8(m3u8_index=m3u8_url, m3u8_audio=m3u8_url_audio, m3u8_subtitle=m3u8_url, key=m3u8_key, output_filename=mp4_path, subtitle_folder=subtitle_path, content_name=mp4_name)

View File

@ -26,7 +26,7 @@ def domain_version():
return domain, version
except Exception as e:
console.log("[red]Cant get version, problem with domain. Try again.")
console.log("[red]Couldn't get the version, there's a problem with the domain. Try again.")
sys.exit(0)

View File

@ -3,6 +3,7 @@
# Class import
from Src.Util.headers import get_headers
from Src.Util.console import console, msg
from Src.Util.config import config
from Src.Lib.FFmpeg.my_m3u8 import download_m3u8
# General import
@ -16,6 +17,7 @@ def get_token(id_tv, domain):
session.get(f"https://streamingcommunity.{domain}/watch/{id_tv}")
return session.cookies['XSRF-TOKEN']
def get_info_tv(id_film, title_name, site_version, domain):
req = requests.get(f"https://streamingcommunity.{domain}/titles/{id_film}-{title_name}", headers={
@ -30,6 +32,7 @@ def get_info_tv(id_film, title_name, site_version, domain):
console.log(f"[red]Error: {req.status_code}")
sys.exit(0)
def get_info_season(tv_id, tv_name, domain, version, token, n_stagione):
req = requests.get(f'https://streamingcommunity.{domain}/titles/{tv_id}-{tv_name}/stagione-{n_stagione}', headers={
'authority': f'streamingcommunity.{domain}', 'referer': f'https://streamingcommunity.broker/titles/{tv_id}-{tv_name}',
@ -42,6 +45,7 @@ def get_info_season(tv_id, tv_name, domain, version, token, n_stagione):
console.log(f"[red]Error: {req.status_code}")
sys.exit(0)
def get_iframe(tv_id, ep_id, domain, token):
req = requests.get(f'https://streamingcommunity.{domain}/iframe/{tv_id}', params={'episode_id': ep_id, 'next_episode': '1'}, cookies={'XSRF-TOKEN': token}, headers={
'referer': f'https://streamingcommunity.{domain}/watch/{tv_id}?e={ep_id}',
@ -85,14 +89,17 @@ def parse_content(embed_content):
json_win_param = json_win_param.replace(",}", "}").replace("'", '"')
return json.loads(json_win_video), json.loads(json_win_param), select_quality(json.loads(json_win_param))
def get_playlist(json_win_video, json_win_param, render_quality):
token_render = f"token{render_quality}"
return f"https://vixcloud.co/playlist/{json_win_video['id']}?token={json_win_param['token']}&{token_render}={json_win_param[token_render]}&expires={json_win_param['expires']}"
def get_m3u8_url(json_win_video, json_win_param, render_quality):
token_render = f"token{render_quality}"
return f"https://vixcloud.co/playlist/{json_win_video['id']}?type=video&rendition={render_quality}&token={json_win_param[token_render]}&expires={json_win_param['expires']}"
def get_m3u8_key_ep(json_win_video, json_win_param, tv_name, n_stagione, n_ep, ep_title, token_render):
response = requests.get('https://vixcloud.co/storage/enc.key', headers={
'referer': f'https://vixcloud.co/embed/{json_win_video["id"]}?token={json_win_param[token_render]}&title={tv_name}&referer=1&expires={json_win_param["expires"]}&description=S{n_stagione}%3AE{n_ep}+{ep_title}&nextEpisode=1',
@ -123,38 +130,42 @@ def get_m3u8_playlist(json_win_video, json_win_param, tv_name, n_stagione, n_ep,
# [func \ main]
def dw_single_ep(tv_id, eps, index_ep_select, domain, token, tv_name, season_select):
enccoded_name = urllib.parse.quote(eps[index_ep_select]['name'])
encoded_name = urllib.parse.quote(eps[index_ep_select]['name'])
console.print(f"[green]Download ep: [blue]{eps[index_ep_select]['n']} [green]=> [purple]{eps[index_ep_select]['name']}")
console.print(f"[green]Downloading episode: [blue]{eps[index_ep_select]['n']} [green]=> [purple]{eps[index_ep_select]['name']}")
embed_content = get_iframe(tv_id, eps[index_ep_select]['id'], domain, token)
json_win_video, json_win_param, render_quality = parse_content(embed_content)
token_render = f"token{render_quality}"
console.print(f"[blue]Quality select => [red]{render_quality}")
console.print(f"[blue]Selected quality => [red]{render_quality}")
m3u8_playlist = get_playlist(json_win_video, json_win_param, render_quality)
m3u8_url = get_m3u8_url(json_win_video, json_win_param, render_quality)
m3u8_key = get_m3u8_key_ep(json_win_video, json_win_param, tv_name, season_select, index_ep_select+1, enccoded_name, token_render)
m3u8_key = get_m3u8_key_ep(json_win_video, json_win_param, tv_name, season_select, index_ep_select+1, encoded_name, token_render)
mp4_name = f"{tv_name.replace('+', '_')}_S{str(season_select).zfill(2)}E{str(index_ep_select+1).zfill(2)}"
mp4_format = f"{mp4_name}.mp4"
mp4_path = os.path.join("videos",tv_name, mp4_format)
season = mp4_name.rsplit("E", 1)[0]
mp4_path = os.path.join(config['root_path'], config['series_folder_name'], tv_name, season, mp4_format)
m3u8_url_audio = get_m3u8_playlist(json_win_video, json_win_param, tv_name, season_select, index_ep_select+1, enccoded_name, token_render)
m3u8_url_audio = get_m3u8_playlist(json_win_video, json_win_param, tv_name, season_select, index_ep_select+1, encoded_name, token_render)
if m3u8_url_audio != None:
console.print("[blue]Use m3u8 audio => [red]True")
if m3u8_url_audio is not None:
console.print("[blue]Using m3u8 audio => [red]True")
# Movie_Name.[Language_Code].vtt
# Movie_Name.[Language_Code].forced.vtt
subtitle_path = os.path.join(config['root_path'], config['series_folder_name'], tv_name, season)
download_m3u8(m3u8_index=m3u8_url, m3u8_audio=m3u8_url_audio, m3u8_subtitle=m3u8_playlist, key=m3u8_key, output_filename=mp4_path, subtitle_folder=subtitle_path, content_name=mp4_name)
download_m3u8(m3u8_index=m3u8_url, m3u8_audio=m3u8_url_audio, m3u8_subtitle=m3u8_playlist, key=m3u8_key, output_filename=mp4_path)
def main_dw_tv(tv_id, tv_name, version, domain):
token = get_token(tv_id, domain)
num_season_find = get_info_tv(tv_id, tv_name, version, domain)
console.print("\n[green]Insert season [red]number [yellow]or [red](*) [green]to download all seasons [yellow]or [red][1-2] [green]for a range of season")
console.print(f"\n[blue]Season find: [red]{num_season_find}")
season_select = str(msg.ask("\n[green]Insert season number: "))
console.print("\n[green]Insert season [red]number[green], or [red](*) [green]to download all seasons, or [red][1-2] [green]for a range of seasons")
console.print(f"\n[blue]Season(s) found: [red]{num_season_find}")
season_select = str(msg.ask("\n[green]Insert which season(s) number you'd like to download"))
if "[" in season_select:
start, end = map(int, season_select[1:-1].split('-'))
result = list(range(start, end + 1))
@ -169,8 +180,8 @@ def main_dw_tv(tv_id, tv_name, version, domain):
eps = get_info_season(tv_id, tv_name, domain, version, token, season_select)
for ep in eps:
console.print(f"[green]Ep: [blue]{ep['n']} [green]=> [purple]{ep['name']}")
index_ep_select = str(msg.ask("\n[green]Insert ep [red]number [yellow]or [red](*) [green]to download all ep [yellow]or [red][1-2] [green]for a range of ep: "))
console.print(f"[green]Episode: [blue]{ep['n']} [green]=> [purple]{ep['name']}")
index_ep_select = str(msg.ask("\n[green]Insert episode [blue]number[green], or [red](*) [green]to download all episodes, or [red][1-2] [green]for a range of episodes"))
# Download range []
if "[" in index_ep_select:
@ -187,7 +198,7 @@ def main_dw_tv(tv_id, tv_name, version, domain):
index_ep_select = int(index_ep_select) - 1
dw_single_ep(tv_id, eps, index_ep_select, domain, token, tv_name, season_select)
else:
console.print("[red]Wrong index for ep")
console.print("[red]Wrong [yellow]INDEX [red]for the selected Episode")
# Download all
else:
@ -196,7 +207,7 @@ def main_dw_tv(tv_id, tv_name, version, domain):
print("\n")
else:
console.print("[red]Wrong index for season")
console.print("[red]Wrong [yellow]INDEX for the selected Season")
else:
for n_season in range(1, num_season_find+1):
eps = get_info_season(tv_id, tv_name, domain, version, token, n_season)

View File

@ -55,7 +55,7 @@ def check_ffmpeg():
console.print("[cyan]FFmpeg is not in the PATH. Downloading and adding to the PATH...[/cyan]")
if not isAdmin():
console.log("[red]You need to be admin to proceed!")
console.log("[red]You need admin privileges to proceed!")
sys.exit(0)
download_ffmpeg()

View File

@ -3,6 +3,7 @@
# Class import
from Src.Util.console import console
from Src.Util.headers import get_headers
from Src.Util.config import config
from Src.Lib.FFmpeg.util import print_duration_table
# Import
@ -20,8 +21,8 @@ warnings.filterwarnings("ignore", category=UserWarning, module="cryptography")
# Variable
MAX_WORKER = 20
DONWLOAD_SUB = True
DOWNLOAD_DEFAULT_LANGUAGE = False
DOWNLOAD_SUB = config['download_subtitles']
DOWNLOAD_DEFAULT_LANGUAGE = config['download_default_language']
failed_segments = []
@ -65,7 +66,7 @@ class M3U8_Parser:
})
for key in m3u8_obj.keys:
if key != None:
if key is not None:
self.keys = ({
"method": key.method,
"uri": key.uri,
@ -90,7 +91,6 @@ class M3U8_Parser:
"uri": media.uri
})
for segment in m3u8_obj.segments:
if "vtt" not in segment.uri:
self.segments.append(segment.uri)
@ -107,13 +107,13 @@ class M3U8_Parser:
if self.video_playlist:
return self.video_playlist[0].get('uri')
else:
print("No video playlist find")
print("No video playlist found")
return None
def download_subtitle(self):
def download_subtitle(self, subtitle_path, content_name):
"""Download all subtitle if present"""
path = os.path.join("videos", "subtitle")
path = subtitle_path
if self.subtitle_playlist:
for sub_info in self.subtitle_playlist:
@ -123,25 +123,29 @@ class M3U8_Parser:
continue
os.makedirs(path, exist_ok=True)
console.log(f"[green]Download subtitle: [red]{name_language}")
console.log(f"[green]Downloading subtitle: [red]{name_language}")
req_sub_content = requests.get(sub_info.get("uri"))
sub_parse = M3U8_Parser()
sub_parse.parse_data(req_sub_content.text)
url_subititle = sub_parse.subtitle[0]
url_subtitle = sub_parse.subtitle[0]
open(os.path.join(path, name_language + ".vtt"), "wb").write(requests.get(url_subititle).content)
# Subtitles convention:
# Movie_Name.[Language_Code].vtt
# Movie_Name.[Language_Code].forced.vtt # If forced
# Implementare "forced"
open(os.path.join(path, content_name + f".{name_language}" + ".vtt"), "wb").write(requests.get(url_subtitle).content)
else:
console.log("[red]No subtitle find")
console.log("[red]No subtitle found")
def get_track_audio(self, language_name): # Ex. English
"""Return url of audio eng audio playlist if present"""
if self.audio_ts:
console.log(f"[cyan]Find {len(self.audio_ts)}, playlist with audio")
console.log(f"[cyan]Found {len(self.audio_ts)}, playlist with audio")
if language_name != None:
if language_name is not None:
for obj_audio in self.audio_ts:
if obj_audio.get("name") == language_name:
return obj_audio.get("uri")
@ -149,13 +153,14 @@ class M3U8_Parser:
return None
else:
console.log("[red]Cant find playlist with audio")
console.log("[red]Couldn't find any playlist with audio")
class M3U8_Segments:
def __init__(self, url, key=None):
self.url = url
self.key = key
if key != None:
if key is not None:
self.decription = Decryption(key)
self.temp_folder = os.path.join("tmp", "segments")
@ -171,7 +176,7 @@ class M3U8_Segments:
m3u8_parser.parse_data(m3u8_content)
# Add decryption iv if present
if self.key != None and m3u8_parser.keys:
if self.key is not None and m3u8_parser.keys:
self.decription.parse_key(m3u8_parser.keys.get("iv"))
# Add all segments
@ -184,10 +189,10 @@ class M3U8_Segments:
if response.ok:
self.parse_data(response.text)
#console.log(f"[red]Ts segments find [white]=> [yellow]{len(self.segments)}")
# console.log(f"[red]Ts segments find [white]=> [yellow]{len(self.segments)}")
if len(self.segments) == 0:
console.log("[red]Cant find segments to donwload, retry")
console.log("[red]Couldn't find any segments to download, retry")
sys.exit(0)
else:
@ -213,20 +218,19 @@ class M3U8_Segments:
if response.status_code == 200:
return response.content
else:
#print(f"Failed to fetch {ts_url}: {response.status_code}")
# print(f"Failed to fetch {ts_url}: {response.status_code}")
failed_segments.append(str(url_number))
return None
except Exception as e:
#print(f"Failed to fetch {ts_url}: {str(e)}")
# print(f"Failed to fetch {ts_url}: {str(e)}")
failed_segments.append(str(url_number))
return None
else:
#print("Skip ", ts_url, " arr ", failed_segments)
# print("Skip ", ts_url, " arr ", failed_segments)
return None
def save_ts(self, index, progress_counter, quit_event):
"""Save ts file and decrypt if there is iv present in decryption class"""
@ -247,7 +251,7 @@ class M3U8_Segments:
progress_counter.update(1)
def download_ts(self):
"""Loop to donwload all segment of playlist m3u8 and break it if there is no progress"""
"""Loop to download all segment of playlist m3u8 and break it if there is no progress"""
progress_counter = tqdm(total=len(self.segments), unit="bytes", desc="[yellow]Download")
@ -311,21 +315,21 @@ class M3U8_Segments:
file_list_path = os.path.join(current_dir, 'file_list.txt')
ts_files = [f for f in os.listdir(self.temp_folder) if f.endswith(".ts")]
def extract_number(file_name):
return int(''.join(filter(str.isdigit, file_name)))
ts_files.sort(key=extract_number)
if len(ts_files) == 0:
console.log("[red]Cant find segments to join, retry")
console.log("[red]Couldn't find any segments to join, retry")
sys.exit(0)
with open(file_list_path, 'w') as f:
for ts_file in ts_files:
relative_path = os.path.relpath(os.path.join(self.temp_folder, ts_file), current_dir)
f.write(f"file '{relative_path}'\n")
console.log("[cyan]Start join all file")
console.log("[cyan]Joining all files...")
try:
ffmpeg.input(file_list_path, format='concat', safe=0).output(output_filename, c='copy', loglevel='error').run()
except ffmpeg.Error as e:
@ -336,6 +340,7 @@ class M3U8_Segments:
os.remove(file_list_path)
shutil.rmtree("tmp", ignore_errors=True)
class M3U8_Downloader:
def __init__(self, m3u8_url, m3u8_audio = None, key=None, output_filename="output.mp4"):
self.m3u8_url = m3u8_url
@ -346,16 +351,16 @@ class M3U8_Downloader:
def start(self):
video_m3u8 = M3U8_Segments(self.m3u8_url, self.key)
console.log("[green]Download video ts")
console.log("[green]Downloading video ts")
video_m3u8.get_info()
video_m3u8.download_ts()
video_m3u8.join(self.video_path)
print_duration_table(self.video_path)
print("\n")
if self.m3u8_audio != None:
if self.m3u8_audio is not None:
audio_m3u8 = M3U8_Segments(self.m3u8_audio, self.key)
console.log("[green]Download audio ts")
console.log("[green]Downloading audio ts")
audio_m3u8.get_info()
audio_m3u8.download_ts()
audio_m3u8.join(self.audio_path)
@ -405,22 +410,24 @@ def df_make_req(url):
console.log(f"[red]Wrong url, error: {response.status_code}")
sys.exit(0)
def download_subtitle(url, name_language):
"""Make req to vtt url and save to video subtitle folder"""
path = os.path.join("videos", "subtitle")
os.makedirs(path, exist_ok=True)
console.log(f"[green]Download subtitle: [red]{name_language}")
console.log(f"[green]Downloading subtitle: [red]{name_language}")
open(os.path.join(path, name_language + ".vtt"), "wb").write(requests.get(url).content)
def download_m3u8(m3u8_playlist=None, m3u8_index = None, m3u8_audio=None, m3u8_subtitle=None, key=None, output_filename=os.path.join("videos", "output.mp4"), log=False):
def download_m3u8(m3u8_playlist=None, m3u8_index = None, m3u8_audio=None, m3u8_subtitle=None, key=None, output_filename=os.path.join("videos", "output.mp4"), log=False, subtitle_folder="subtitles", content_name=""):
# Get byte of key
key = bytes.fromhex(key) if key is not None else key
if m3u8_playlist != None:
console.log(f"[green]Dowload m3u8 from playlist")
if m3u8_playlist is not None:
console.log(f"[green]Downloading m3u8 from playlist")
# Parse m3u8 playlist
parse_class_m3u8 = M3U8_Parser()
@ -431,28 +438,25 @@ def download_m3u8(m3u8_playlist=None, m3u8_index = None, m3u8_audio=None, m3u8_s
else:
parse_class_m3u8.parse_data(m3u8_playlist)
# Get italian language in present as defualt
# Get italian language if present as default
if DOWNLOAD_DEFAULT_LANGUAGE:
m3u8_audio = parse_class_m3u8.get_track_audio("Italian")
console.log(f"[green]Select language => [purple]{m3u8_audio}")
console.log(f"[green]Selected language => [purple]{m3u8_audio}")
# Get best quality
if m3u8_index == None:
if m3u8_index is None:
m3u8_index = parse_class_m3u8.get_best_quality()
if "https" in m3u8_index:
if log: console.log(f"[green]Select m3u8 index => [purple]{m3u8_index}")
if log: console.log(f"[green]Selected m3u8 index => [purple]{m3u8_index}")
else:
console.log("[red]Cant find a valid m3u8 index")
console.log("[red]Couldn't find a valid m3u8 index")
sys.exit(0)
# Download subtitle if present ( normally in m3u8 playlist )
if DOWNLOAD_SUB:
parse_class_m3u8.download_subtitle(subtitle_path=subtitle_folder, content_name=content_name)
# Download subtitle if present ( normaly in m3u8 playlist )
if DONWLOAD_SUB:
parse_class_m3u8.download_subtitle()
if m3u8_subtitle != None:
if m3u8_subtitle is not None:
parse_class_m3u8_sub = M3U8_Parser()
@ -462,10 +466,9 @@ def download_m3u8(m3u8_playlist=None, m3u8_index = None, m3u8_audio=None, m3u8_s
else:
parse_class_m3u8_sub.parse_data(m3u8_subtitle)
# Download subtitle if present ( normaly in m3u8 playlist )
if DONWLOAD_SUB:
parse_class_m3u8_sub.download_subtitle()
# Download subtitle if present ( normally in m3u8 playlist )
if DOWNLOAD_SUB:
parse_class_m3u8_sub.download_subtitle(subtitle_path=subtitle_folder, content_name=content_name)
# Download m3u8 index, with segments
# os.makedirs("videos", exist_ok=True)
@ -473,5 +476,5 @@ def download_m3u8(m3u8_playlist=None, m3u8_index = None, m3u8_audio=None, m3u8_s
os.makedirs(path, exist_ok=True)
if log:
console.log(f"[green]Dowload m3u8 from index [white]=> [purple]{m3u8_index}")
console.log(f"[green]Download m3u8 from index [white]=> [purple]{m3u8_index}")
M3U8_Downloader(m3u8_index, m3u8_audio, key=key, output_filename=output_filename).start()

View File

@ -19,7 +19,7 @@ def get_install_version():
return about['__version__']
def main_update():
console.print("[green]Checking github version ...")
console.print("[green]Checking GitHub version ...")
json = requests.get(f"https://api.github.com/repos/{repo_user}/{repo_name}/releases").json()[0]
stargazers_count = requests.get(f"https://api.github.com/repos/{repo_user}/{repo_name}").json()['stargazers_count']
@ -34,11 +34,11 @@ def main_update():
if get_install_version() != last_version:
console.print(f"[red]=> A new version is available: [green]{json['zipball_url']}")
console.print(f"[red]=> Versione: [yellow]{json['name']}")
console.print(f"[red]=> New Version: [yellow]{json['name']}")
else:
console.print(f"[red]=> Everything up to date")
console.print(f"[red]=> Last version: [yellow]{json['name']}")
console.print(f"[red]=> Everything is up to date")
console.print(f"[red]=> You're on Version: [yellow]{json['name']}")
print("\n")

10
Src/Util/config.py Normal file
View File

@ -0,0 +1,10 @@
import json
from pathlib import Path
def load_config(file_path):
with open(file_path, 'r') as file:
config_file = json.load(file)
return config_file
config_path = Path(__file__).parent.parent.parent / 'config.json' # path for config.json (in root directory)
config = load_config(config_path)

7
config.json Normal file
View File

@ -0,0 +1,7 @@
{
"root_path": "videos",
"movies_folder_name": "Film",
"series_folder_name": "Serie",
"download_subtitles": true,
"download_default_language": false
}

28
run.py
View File

@ -36,30 +36,30 @@ def main():
initialize()
domain, site_version = Page.domain_version()
film_search = msg.ask("\n[blue]Insert word to search in all site: ").strip()
film_search = msg.ask("\n[blue]Search for any movie or tv series title").strip()
db_title = Page.search(film_search, domain)
Page.display_search_results(db_title)
if len(db_title) != 0:
console.print(f"\n[blue]Total result: {len(db_title)}")
console.print(
"\n[green]Insert index [red]number [yellow]or [red][1-2] [green]for a range of movies/tv [yellow]or [red][1,3,5] [green]to select discontinued movie/tv"
"\n[green]Insert [yellow]INDEX [red]number[green], or [red][1-2] [green]for a range of movies/tv series, or [red][1,3,5] [green]to select discontinued movie/tv series"
)
console.print("\n[red]In case of tv show you will have to choose season and episode to download")
index_select = str(msg.ask("\n[blue]Index to download: "))
console.print("\n[red]In case of a TV Series you will also choose seasons and episodes to download")
index_select = str(msg.ask("\n[blue]Select [yellow]INDEX [blue]to download"))
if index_select.isnumeric():
index_select = int(index_select)
if 0 <= index_select <= len(db_title) - 1:
selected_title = db_title[index_select]
if selected_title['type'] == "movie":
console.print(f"[green]\nMovie select: {selected_title['name']}")
console.print(f"[green]\nSelected Movie: {selected_title['name']}")
download_film(selected_title['id'], selected_title['slug'], domain)
else:
console.print(f"[green]\nTv select: {selected_title['name']}")
console.print(f"[green]\nSelected TV Series: {selected_title['name']}")
download_tv(selected_title['id'], selected_title['slug'], site_version, domain)
else:
console.print("[red]Wrong index for selection")
console.print("[red]Wrong INDEX for selection")
elif "[" in index_select:
if "-" in index_select:
start, end = map(int, index_select[1:-1].split('-'))
@ -67,27 +67,27 @@ def main():
for n in result:
selected_title = db_title[n]
if selected_title['type'] == "movie":
console.print(f"[green]\nMovie select: {selected_title['name']}")
console.print(f"[green]\nSelected Movie: {selected_title['name']}")
download_film(selected_title['id'], selected_title['slug'], domain)
else:
console.print(f"[green]\nTv select: {selected_title['name']}")
console.print(f"[green]\nSelected TV Series: {selected_title['name']}")
download_tv(selected_title['id'], selected_title['slug'], site_version, domain)
elif "," in index_select:
result = list(map(int, index_select[1:-1].split(',')))
for n in result:
selected_title = db_title[n]
if selected_title['type'] == "movie":
console.print(f"[green]\nMovie select: {selected_title['name']}")
console.print(f"[green]\nSelected Movie: {selected_title['name']}")
download_film(selected_title['id'], selected_title['slug'], domain)
else:
console.print(f"[green]\nTv select: {selected_title['name']}")
console.print(f"[green]\nSelected TV Series: {selected_title['name']}")
download_tv(selected_title['id'], selected_title['slug'], site_version, domain)
else:
console.print("[red]Wrong index for selection")
console.print("[red]Wrong INDEX for selection")
else:
console.print("[red]Cant find a single element")
console.print("[red]Couldn't find any entries for the selected title")
console.print("[red]Done")
console.print("[red]Done!")
if __name__ == '__main__':
main()

View File

@ -66,7 +66,7 @@ def download_and_extract_latest_commit(author, repo_name):
temp_path = os.path.join(os.path.dirname(os.getcwd()), 'temp_extracted')
with ZipFile(BytesIO(response.content)) as zip_ref:
zip_ref.extractall(temp_path)
console.log("[green]Extract file ...")
console.log("[green]Extracting file ...")
# Move files from the temporary folder to the current folder