diff --git a/Src/Lib/Downloader/HLS/downloader.py b/Src/Lib/Downloader/HLS/downloader.py index 554c878..97b35d4 100644 --- a/Src/Lib/Downloader/HLS/downloader.py +++ b/Src/Lib/Downloader/HLS/downloader.py @@ -31,6 +31,7 @@ from Src.Util.os import ( # Logic class from ...FFmpeg import ( print_duration_table, + get_video_duration_s, join_video, join_audios, join_subtitle @@ -137,7 +138,6 @@ class HLS_Downloader(): # Send a GET request to the provided URL logging.info(f"Test url: {url}") - headers_index = {'user-agent': get_headers()} response = httpx.get(url, headers=headers_index) try: @@ -485,6 +485,11 @@ class HLS_Downloader(): end_output_seconds = dict_to_seconds(end_output_time) missing_ts = not (expected_real_seconds - 3 <= end_output_seconds <= expected_real_seconds + 3) + # Second check + if not missing_ts: + if get_video_duration_s(self.output_filename) < int(expected_real_seconds) - 5: + missing_ts = True + panel_content = ( f"[bold green]Download completed![/bold green]\n" f"[cyan]File size: [bold red]{formatted_size}[/bold red]\n" @@ -499,10 +504,11 @@ class HLS_Downloader(): )) # Delete all files except the output file - if not missing_ts: - delete_files_except_one(self.base_path, os.path.basename(self.output_filename)) - else: - delete_files_except_one(self.base_path, os.path.basename(self.output_filename.replace(".mp4", "_failed.mp4"))) + if missing_ts: + os.rename(self.output_filename, self.output_filename.replace(".mp4", "_failed.mp4")) + + # Delete all other conversion + delete_files_except_one(self.base_path, os.path.basename(self.output_filename.replace(".mp4", "_failed.mp4"))) # Remove the base folder if REMOVE_SEGMENTS_FOLDER: diff --git a/Src/Lib/FFmpeg/__init__.py b/Src/Lib/FFmpeg/__init__.py index 73ac6ba..463766c 100644 --- a/Src/Lib/FFmpeg/__init__.py +++ b/Src/Lib/FFmpeg/__init__.py @@ -1,8 +1,4 @@ # 18.04.24 -from .command import ( - join_video, - join_audios, - join_subtitle, -) -from .util import print_duration_table +from .command import join_video, join_audios, join_subtitle +from .util import print_duration_table, get_video_duration_s diff --git a/Src/Lib/FFmpeg/capture.py b/Src/Lib/FFmpeg/capture.py index 50727c8..074477c 100644 --- a/Src/Lib/FFmpeg/capture.py +++ b/Src/Lib/FFmpeg/capture.py @@ -1,13 +1,9 @@ # 16.04.24 import re -import sys import logging import threading import subprocess -from datetime import datetime - -from typing import Tuple # Internal utilities diff --git a/Src/Lib/FFmpeg/command.py b/Src/Lib/FFmpeg/command.py index b5a8078..221b0ff 100644 --- a/Src/Lib/FFmpeg/command.py +++ b/Src/Lib/FFmpeg/command.py @@ -12,7 +12,7 @@ from typing import List, Dict from Src.Util._jsonConfig import config_manager from Src.Util.os import check_file_existence, suppress_output from Src.Util.console import console -from .util import need_to_force_to_ts, check_ffmpeg_input, check_duration_v_a +from .util import need_to_force_to_ts, check_duration_v_a from .capture import capture_ffmpeg_real_time from ..M3U8 import M3U8_Codec @@ -26,7 +26,6 @@ USE_ACODEC = config_manager.get_bool("M3U8_CONVERSION", "use_acodec") USE_BITRATE = config_manager.get_bool("M3U8_CONVERSION", "use_bitrate") USE_GPU = config_manager.get_bool("M3U8_CONVERSION", "use_gpu") FFMPEG_DEFAULT_PRESET = config_manager.get("M3U8_CONVERSION", "default_preset") -CHECK_OUTPUT_CONVERSION = config_manager.get_bool("M3U8_CONVERSION", "check_output_after_ffmpeg") # Variable @@ -116,12 +115,6 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None): capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join video") print() - # Check file output - if CHECK_OUTPUT_CONVERSION: - console.log("[red]Check output ffmpeg") - time.sleep(0.5) - check_ffmpeg_input(out_path) - time.sleep(0.5) if not check_file_existence(out_path): logging.error("Missing output video for ffmpeg conversion video.") @@ -224,12 +217,6 @@ def join_audios(video_path: str, audio_tracks: List[Dict[str, str]], out_path: s capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join audio") print() - # Check file output - if CHECK_OUTPUT_CONVERSION: - console.log("[red]Check output ffmpeg") - time.sleep(0.5) - check_ffmpeg_input(out_path) - time.sleep(0.5) if not check_file_existence(out_path): logging.error("Missing output video for ffmpeg conversion audio.") @@ -295,13 +282,6 @@ def join_subtitle(video_path: str, subtitles_list: List[Dict[str, str]], out_pat capture_ffmpeg_real_time(ffmpeg_cmd, "[cyan]Join subtitle") print() - - # Check file output - if CHECK_OUTPUT_CONVERSION: - console.log("[red]Check output ffmpeg") - time.sleep(0.5) - check_ffmpeg_input(out_path) - time.sleep(0.5) if not check_file_existence(out_path): logging.error("Missing output video for ffmpeg conversion subtitle.") diff --git a/Src/Lib/FFmpeg/util.py b/Src/Lib/FFmpeg/util.py index b2b3693..8c1c9d8 100644 --- a/Src/Lib/FFmpeg/util.py +++ b/Src/Lib/FFmpeg/util.py @@ -74,6 +74,36 @@ def get_video_duration(file_path: str) -> float: logging.error(f"Error get video duration: {e}") sys.exit(0) +def get_video_duration_s(filename): + """ + Get the duration of a video file using ffprobe. + + Parameters: + - filename (str): Path to the video file (e.g., 'sim.mp4') + + Returns: + - duration (float): Duration of the video in seconds, or None if an error occurs. + """ + ffprobe_cmd = ['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', filename] + + try: + + # Run ffprobe command and capture output + result = subprocess.run(ffprobe_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True) + + # Extract duration from the output + duration_str = result.stdout.strip() + duration = float(duration_str) # Convert duration to float + + return int(duration) + + except subprocess.CalledProcessError as e: + print(f"Error running ffprobe: {e}") + return None + except ValueError as e: + print(f"Error converting duration to float: {e}") + return None + def format_duration(seconds: float) -> Tuple[int, int, int]: """ @@ -104,16 +134,6 @@ def print_duration_table(file_path: str, description: str = "Duration", return_s Returns: - str: The formatted duration string if return_string is True. - dict: A dictionary with keys 'h', 'm', 's' representing hours, minutes, and seconds if return_string is False. - - Example usage: - >>> print_duration_table("path/to/video.mp4") - [cyan]Duration for [white]([green]video.mp4[white]): [yellow]1[red]h [yellow]1[red]m [yellow]1[red]s - - >>> print_duration_table("path/to/video.mp4", description=None) - '[yellow]1[red]h [yellow]1[red]m [yellow]1[red]s' - - >>> print_duration_table("path/to/video.mp4", description=None, return_string=False) - {'h': 1, 'm': 1, 's': 1} """ video_duration = get_video_duration(file_path) @@ -197,45 +217,6 @@ def need_to_force_to_ts(file_path): return False -def check_ffmpeg_input(input_file): - """ - Check if an input file can be processed by FFmpeg. - - Args: - input_file (str): Path to the input file. - - Returns: - bool: True if the input file is valid and can be processed by FFmpeg, False otherwise. - """ - command = [ - 'ffmpeg', '-v', 'error', '-i', input_file, '-f', 'null', '-' - ] - logging.info(f"FFmpeg command check: {command}") - - try: - # Run the FFmpeg command and capture output - result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - # Check the exit status - if result.returncode != 0: - logging.error("FFmpeg encountered an error with the input file:") - logging.error(result.stderr.decode('utf-8')) - return False - - # Optionally, you can analyze the output to check for specific errors - stderr_output = result.stderr.decode('utf-8') - if 'error' in stderr_output.lower(): - logging.error("FFmpeg reported an error in the input file:") - logging.error(stderr_output) - return False - - logging.info(f"Input file is valid: {input_file}") - return True - - except Exception as e: - logging.error(f"An unexpected error occurred: {e}") - return False - def check_duration_v_a(video_path, audio_path): """ Check if the duration of the video and audio matches. diff --git a/Src/Lib/M3U8/estimator.py b/Src/Lib/M3U8/estimator.py index 808c52b..7a306b6 100644 --- a/Src/Lib/M3U8/estimator.py +++ b/Src/Lib/M3U8/estimator.py @@ -56,7 +56,7 @@ class M3U8_Ts_Estimator: self.ts_file_sizes.append(size) self.now_downloaded_size += size_download - def capture_speed(self, interval: float = 0.8): + def capture_speed(self, interval: float = 1): """ Capture the internet speed periodically and store the values in a deque. """ diff --git a/Test/t_down_hls.py b/Test/t_down_hls.py index eedeef5..1e04f5b 100644 --- a/Test/t_down_hls.py +++ b/Test/t_down_hls.py @@ -1,9 +1,20 @@ # 23.06.24 +# Fix import +import time +import sys +import os +src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(src_path) + + + +# Import from Src.Lib.Downloader import HLS_Downloader +# Test HLS_Downloader( - output_filename="EP_1.mp4", + output_filename=r".\EP_1.mp4", m3u8_playlist="" -) \ No newline at end of file +).start() \ No newline at end of file diff --git a/Test/t_down_mp4.py b/Test/t_down_mp4.py index d443dea..5dedaa0 100644 --- a/Test/t_down_mp4.py +++ b/Test/t_down_mp4.py @@ -1,7 +1,19 @@ # 23.06.24 +# Fix import +import time +import sys +import os +src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(src_path) + + + +# Import from Src.Lib.Downloader import MP4_downloader + +# Test MP4_downloader( "", "EP_1.mp4" diff --git a/Test/t_down_tor.py b/Test/t_down_tor.py index a7ef491..8fd26ab 100644 --- a/Test/t_down_tor.py +++ b/Test/t_down_tor.py @@ -1,7 +1,21 @@ # 23.06.24 + +# Fix import +import time +import sys +import os +src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(src_path) + + + +# Import from Src.Lib.Downloader import TOR_downloader + + +# Test manager = TOR_downloader() magnet_link = "magnet:?x" diff --git a/config.json b/config.json index d0fdcfd..8436a2d 100644 --- a/config.json +++ b/config.json @@ -49,8 +49,7 @@ "use_acodec": true, "use_bitrate": true, "use_gpu": false, - "default_preset": "ultrafast", - "check_output_after_ffmpeg": false + "default_preset": "ultrafast" }, "M3U8_PARSER": { "force_resolution": -1