diff --git a/.gitignore b/.gitignore index 9f08978..ce62ea0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,4 @@ pyvenv.cfg # Project specific videos/ -tmp/ -Src/Util/file_list.txt \ No newline at end of file +tmp/ \ No newline at end of file diff --git a/Src/Api/tv.py b/Src/Api/tv.py index 1911b38..0bf7011 100644 --- a/Src/Api/tv.py +++ b/Src/Api/tv.py @@ -121,7 +121,7 @@ def dw_single_ep(tv_id, eps, index_ep_select, domain, token, tv_name, season_sel m3u8_url_audio = get_m3u8_audio(json_win_video, json_win_param, tv_name, season_select, index_ep_select+1, eps[index_ep_select]['name'], token_render) if m3u8_url_audio != None: - console.print("[red]=> Use m3u8 audio") + console.print("[blue]Use m3u8 audio => [red]True") dw_m3u8(m3u8_url, m3u8_url_audio, m3u8_key, mp4_path) diff --git a/Src/Upload/__version__.py b/Src/Upload/__version__.py index d005341..60f5629 100644 --- a/Src/Upload/__version__.py +++ b/Src/Upload/__version__.py @@ -1,5 +1,5 @@ __title__ = 'Streaming_community' -__version__ = 'v0.7.5' +__version__ = 'v0.7.8' __author__ = 'Ghost6446' __description__ = 'A command-line program to download film' __license__ = 'MIT License' diff --git a/Src/Util/Helper/util.py b/Src/Util/Helper/util.py index 2f689df..1ada97e 100644 --- a/Src/Util/Helper/util.py +++ b/Src/Util/Helper/util.py @@ -1,7 +1,7 @@ # 4.01.2023 # Import -import ffmpeg +import ffmpeg, subprocess, re def convert_utf8_name(name): return str(name).encode('utf-8').decode('latin-1') @@ -10,8 +10,15 @@ def there_is_audio(ts_file_path): probe = ffmpeg.probe(ts_file_path) return any(stream['codec_type'] == 'audio' for stream in probe['streams']) - def merge_ts_files(video_path, audio_path, output_path): input_video = ffmpeg.input(video_path) input_audio = ffmpeg.input(audio_path) - ffmpeg.output(input_video, input_audio, output_path, format='mpegts', acodec='copy', vcodec='copy', loglevel='quiet').run() \ No newline at end of file + + ffmpeg_command = ffmpeg.output(input_video, input_audio, output_path, format='mpegts', acodec='copy', vcodec='copy', loglevel='quiet').compile() + + try: + subprocess.run(ffmpeg_command, check=True, stderr=subprocess.PIPE) + return True + except subprocess.CalledProcessError as e: + #print(f"Failed convert: {video_path} \ {audio_path} to {output_path}") + return False \ No newline at end of file diff --git a/Src/Util/m3u8.py b/Src/Util/m3u8.py index de58b20..b39f4ca 100644 --- a/Src/Util/m3u8.py +++ b/Src/Util/m3u8.py @@ -1,12 +1,12 @@ # 5.01.24 -> 7.01.24 # Import -import requests, re, os, ffmpeg, shutil, time, sys +import requests, re, os, ffmpeg, shutil, time, sys, warnings from tqdm.rich import tqdm from concurrent.futures import ThreadPoolExecutor from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend -import moviepy.editor as mp + # Class import from Src.Util.Helper.console import console @@ -14,6 +14,12 @@ from Src.Util.Helper.headers import get_headers from Src.Util.Helper.util import there_is_audio, merge_ts_files +# Disable warning +from tqdm import TqdmExperimentalWarning +warnings.filterwarnings("ignore", category=TqdmExperimentalWarning) +warnings.filterwarnings("ignore", category=UserWarning, module="cryptography") + + # Variable os.makedirs("videos", exist_ok=True) @@ -26,6 +32,9 @@ class M3U8Downloader: self.m3u8_audio = m3u8_audio self.key = key self.output_filename = output_filename + if output_filename == None: + console.log(f"Cant pass None as output file name") + sys.exit(0) self.segments = [] self.segments_audio = [] @@ -35,6 +44,8 @@ class M3U8Downloader: self.temp_folder = "tmp" os.makedirs(self.temp_folder, exist_ok=True) self.download_audio = False + self.max_retry = 3 + self.failed_segments = [] def decode_ext_x_key(self, key_str): key_str = key_str.replace('"', '').lstrip("#EXT-X-KEY:") @@ -119,6 +130,21 @@ class M3U8Downloader: return decrypted_data + def make_req_single_ts_file(self, ts_url, retry=0): + + if retry == self.max_retry: + console.log(f"[red]Failed download: {ts_url}") + self.segments.remove(ts_url) + return None + + req = requests.get(ts_url, headers={'user-agent': get_headers()}, timeout=10, allow_redirects=True) + + if req.status_code == 200: + return req.content + else: + retry += 1 + return self.make_req_single_ts_file(ts_url, retry) + def decrypt_and_save(self, index): video_ts_url = self.segments[index] @@ -126,44 +152,57 @@ class M3U8Downloader: # Download video or audio ts file if not os.path.exists(video_ts_filename): # Only for media that not use audio - ts_response = requests.get(video_ts_url, headers={'user-agent': get_headers()}).content + ts_response = self.make_req_single_ts_file(video_ts_url) - if self.key and self.iv: - decrypted_data = self.decrypt_ts(ts_response) - with open(video_ts_filename, "wb") as ts_file: - ts_file.write(decrypted_data) + if ts_response != None: + if self.key and self.iv: + decrypted_data = self.decrypt_ts(ts_response) + with open(video_ts_filename, "wb") as ts_file: + ts_file.write(decrypted_data) - else: - with open(video_ts_filename, "wb") as ts_file: - ts_file.write(ts_response) + else: + with open(video_ts_filename, "wb") as ts_file: + ts_file.write(ts_response) - # Donwload only audio ts file + # Donwload only audio ts file and merge with video if self.download_audio: audio_ts_url = self.segments_audio[index] audio_ts_filename = os.path.join(self.temp_folder, f"{index}_a.ts") video_audio_ts_filename = os.path.join(self.temp_folder, f"{index}_v_a.ts") if not os.path.exists(video_audio_ts_filename): # Only for media use audio - ts_response = requests.get(audio_ts_url, headers={'user-agent': get_headers()}).content + ts_response = self.make_req_single_ts_file(audio_ts_url) - if self.key and self.iv: - decrypted_data = self.decrypt_ts(ts_response) - with open(audio_ts_filename, "wb") as ts_file: - ts_file.write(decrypted_data) + if ts_response != None: + if self.key and self.iv: + decrypted_data = self.decrypt_ts(ts_response) - else: - with open(audio_ts_filename, "wb") as ts_file: - ts_file.write(ts_response) - - # Join ts video and audio - merge_ts_files(video_ts_filename, audio_ts_filename, video_audio_ts_filename) - os.remove(video_ts_filename) - os.remove(audio_ts_filename) - + with open(audio_ts_filename, "wb") as ts_file: + ts_file.write(decrypted_data) + + else: + with open(audio_ts_filename, "wb") as ts_file: + ts_file.write(ts_response) + + # Join ts video and audio + res_merge = merge_ts_files(video_ts_filename, audio_ts_filename, video_audio_ts_filename) + + if res_merge: + os.remove(video_ts_filename) + os.remove(audio_ts_filename) + + # If merge fail, so we have only video and audio, take only video + else: + self.failed_segments.append(index) + os.remove(audio_ts_filename) + def download_and_save_ts(self): with ThreadPoolExecutor(max_workers=30) as executor: list(tqdm(executor.map(self.decrypt_and_save, range(len(self.segments)) ), total=len(self.segments), unit="bytes", unit_scale=True, unit_divisor=1024, desc="[yellow]Download")) - + + if len(self.failed_segments) > 0: + console.log(f"[red]Segment ts: {self.failed_segments}, cant use audio") + def join_ts_files(self): current_dir = os.path.dirname(os.path.realpath(__file__)) diff --git a/run.py b/run.py index d2fbf56..f3c8642 100644 --- a/run.py +++ b/run.py @@ -18,7 +18,9 @@ site_version = Page.get_version(domain) def main(): msg_start() - main_update() + try: main_update() + except: console.print("[blue]Req github [white]=> [red]Failed") + console.print(f"[blue]Find system [white]=> [red]{sys.platform} \n") film_search = msg.ask("\n[blue]Insert word to search in all site: ").strip()