From 9ee9c91a2d7e44a615a5ff1313cefe444302b5cc Mon Sep 17 00:00:00 2001
From: Cohvir <63691557+Cohvir@users.noreply.github.com>
Date: Mon, 4 Mar 2024 22:34:36 +0100
Subject: [PATCH] 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)
---
.idea/.gitignore | 8 ++
.idea/StreamingCommunity_api.iml | 14 +++
.../inspectionProfiles/profiles_settings.xml | 6 +
.idea/modules.xml | 8 ++
.idea/vcs.xml | 6 +
README.md | 28 ++++-
Src/Api/film.py | 23 ++--
Src/Api/page.py | 2 +-
Src/Api/tv.py | 45 +++++---
Src/Lib/FFmpeg/installer.py | 2 +-
Src/Lib/FFmpeg/my_m3u8.py | 107 +++++++++---------
Src/Upload/update.py | 8 +-
Src/Util/config.py | 10 ++
config.json | 7 ++
run.py | 28 ++---
update.py | 2 +-
16 files changed, 203 insertions(+), 101 deletions(-)
create mode 100644 .idea/.gitignore
create mode 100644 .idea/StreamingCommunity_api.iml
create mode 100644 .idea/inspectionProfiles/profiles_settings.xml
create mode 100644 .idea/modules.xml
create mode 100644 .idea/vcs.xml
create mode 100644 Src/Util/config.py
create mode 100644 config.json
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -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
diff --git a/.idea/StreamingCommunity_api.iml b/.idea/StreamingCommunity_api.iml
new file mode 100644
index 0000000..8e5446a
--- /dev/null
+++ b/.idea/StreamingCommunity_api.iml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..b0ca488
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index a34712f..ad18e5f 100644
--- a/README.md
+++ b/README.md
@@ -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)
diff --git a/Src/Api/film.py b/Src/Api/film.py
index 7820d89..04b54c5 100644
--- a/Src/Api/film.py
+++ b/Src/Api/film.py
@@ -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)
diff --git a/Src/Api/page.py b/Src/Api/page.py
index c060beb..700b5fa 100644
--- a/Src/Api/page.py
+++ b/Src/Api/page.py
@@ -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)
diff --git a/Src/Api/tv.py b/Src/Api/tv.py
index 3848662..9592bcb 100644
--- a/Src/Api/tv.py
+++ b/Src/Api/tv.py
@@ -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 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)
- 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_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)
diff --git a/Src/Lib/FFmpeg/installer.py b/Src/Lib/FFmpeg/installer.py
index 725d5d8..9e2820c 100644
--- a/Src/Lib/FFmpeg/installer.py
+++ b/Src/Lib/FFmpeg/installer.py
@@ -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()
diff --git a/Src/Lib/FFmpeg/my_m3u8.py b/Src/Lib/FFmpeg/my_m3u8.py
index d493b98..e4544dd 100644
--- a/Src/Lib/FFmpeg/my_m3u8.py
+++ b/Src/Lib/FFmpeg/my_m3u8.py
@@ -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}")
- M3U8_Downloader(m3u8_index, m3u8_audio, key=key, output_filename=output_filename).start()
\ No newline at end of file
+ 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()
diff --git a/Src/Upload/update.py b/Src/Upload/update.py
index d256b69..42ddf69 100644
--- a/Src/Upload/update.py
+++ b/Src/Upload/update.py
@@ -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")
diff --git a/Src/Util/config.py b/Src/Util/config.py
new file mode 100644
index 0000000..1280b8c
--- /dev/null
+++ b/Src/Util/config.py
@@ -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)
\ No newline at end of file
diff --git a/config.json b/config.json
new file mode 100644
index 0000000..e63a053
--- /dev/null
+++ b/config.json
@@ -0,0 +1,7 @@
+{
+ "root_path": "videos",
+ "movies_folder_name": "Film",
+ "series_folder_name": "Serie",
+ "download_subtitles": true,
+ "download_default_language": false
+}
\ No newline at end of file
diff --git a/run.py b/run.py
index b4549eb..d398b21 100644
--- a/run.py
+++ b/run.py
@@ -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()
diff --git a/update.py b/update.py
index 0fed552..455ef57 100644
--- a/update.py
+++ b/update.py
@@ -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