diff --git a/editor.py b/editor.py index 8a726dc..ed056cf 100644 --- a/editor.py +++ b/editor.py @@ -340,7 +340,7 @@ def apply_edits(self, input_path, output_path, filter_data): 'ffmpeg', '-y', '-i', input_path, '-vf', filter_string, - '-c:v', 'libx264', '-preset', 'fast', '-crf', '22', + '-c:v', 'libx264', '-preset', 'medium', '-crf', '18', '-c:a', 'copy', output_path ] diff --git a/hooks.py b/hooks.py index 3649008..edee0b1 100644 --- a/hooks.py +++ b/hooks.py @@ -221,7 +221,7 @@ def add_hook_to_video(video_path, text, output_path, position="top", font_scale= '-i', img_path, '-filter_complex', f"[0:v][1:v]overlay={overlay_x}:{overlay_y}", '-c:a', 'copy', - '-c:v', 'libx264', '-preset', 'fast', '-crf', '22', + '-c:v', 'libx264', '-preset', 'medium', '-crf', '18', output_path ] diff --git a/main.py b/main.py index 2d30001..2021d24 100644 --- a/main.py +++ b/main.py @@ -346,14 +346,14 @@ def create_general_frame(frame, output_width, output_height): # Crop center to aspect ratio bg_scale = output_height / orig_h bg_w = int(orig_w * bg_scale) - bg_resized = cv2.resize(frame, (bg_w, output_height)) + bg_resized = cv2.resize(frame, (bg_w, output_height), interpolation=cv2.INTER_LANCZOS4) # Crop center of background start_x = (bg_w - output_width) // 2 if start_x < 0: start_x = 0 background = bg_resized[:, start_x:start_x+output_width] if background.shape[1] != output_width: - background = cv2.resize(background, (output_width, output_height)) + background = cv2.resize(background, (output_width, output_height), interpolation=cv2.INTER_LANCZOS4) # Blur background background = cv2.GaussianBlur(background, (51, 51), 0) @@ -361,7 +361,7 @@ def create_general_frame(frame, output_width, output_height): # 2. Foreground (Fit Width) scale = output_width / orig_w fg_h = int(orig_h * scale) - foreground = cv2.resize(frame, (output_width, fg_h)) + foreground = cv2.resize(frame, (output_width, fg_h), interpolation=cv2.INTER_LANCZOS4) # 3. Overlay y_offset = (output_height - fg_h) // 2 @@ -492,7 +492,7 @@ def download_youtube_video(url, output_dir="."): 'cachedir': False, 'extractor_args': { 'youtube': { - 'player_client': ['tv_embed', 'android', 'mweb', 'web'], + 'player_client': ['ios', 'android', 'mweb', 'web'], 'player_skip': ['webpage', 'configs'], } }, @@ -556,7 +556,7 @@ def download_youtube_video(url, output_dir="."): ydl_opts = { **_COMMON_YDL_OPTS, - 'format': 'bestvideo[vcodec^=avc1][ext=mp4]+bestaudio[ext=m4a]/bestvideo[vcodec^=avc1]+bestaudio/best[ext=mp4]/best', + 'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio/best', 'outtmpl': output_template, 'merge_output_format': 'mp4', 'overwrites': True, @@ -612,10 +612,9 @@ def process_video_to_vertical(input_video, final_output_video): print("\n 🧠 Step 2: Preparing Active Tracking...") original_width, original_height = get_video_resolution(input_video) - OUTPUT_HEIGHT = original_height - OUTPUT_WIDTH = int(OUTPUT_HEIGHT * ASPECT_RATIO) - if OUTPUT_WIDTH % 2 != 0: - OUTPUT_WIDTH += 1 + # Enforce standard vertical 1080p output (1080x1920) for high quality + OUTPUT_WIDTH = 1080 + OUTPUT_HEIGHT = 1920 # Initialize Cameraman cameraman = SmoothedCameraman(OUTPUT_WIDTH, OUTPUT_HEIGHT, original_width, original_height) @@ -630,8 +629,9 @@ def process_video_to_vertical(input_video, final_output_video): command = [ 'ffmpeg', '-y', '-f', 'rawvideo', '-vcodec', 'rawvideo', '-s', f'{OUTPUT_WIDTH}x{OUTPUT_HEIGHT}', '-pix_fmt', 'bgr24', - '-r', str(fps), '-i', '-', '-c:v', 'libx264', - '-preset', 'fast', '-crf', '23', '-an', temp_video_output + '-r', str(fps), '-i', '-', + '-vf', 'unsharp=5:5:1.5,eq=brightness=0.06:contrast=1.1:saturation=1.15', + '-c:v', 'libx264', '-preset', 'medium', '-crf', '18', '-an', temp_video_output ] ffmpeg_process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE) @@ -696,9 +696,9 @@ def process_video_to_vertical(input_video, final_output_video): # Crop if y2 > y1 and x2 > x1: cropped = frame[y1:y2, x1:x2] - output_frame = cv2.resize(cropped, (OUTPUT_WIDTH, OUTPUT_HEIGHT)) + output_frame = cv2.resize(cropped, (OUTPUT_WIDTH, OUTPUT_HEIGHT), interpolation=cv2.INTER_LANCZOS4) else: - output_frame = cv2.resize(frame, (OUTPUT_WIDTH, OUTPUT_HEIGHT)) + output_frame = cv2.resize(frame, (OUTPUT_WIDTH, OUTPUT_HEIGHT), interpolation=cv2.INTER_LANCZOS4) ffmpeg_process.stdin.write(output_frame.tobytes()) frame_number += 1 @@ -995,7 +995,7 @@ def _ensure_dir(path: str) -> str: '-ss', str(start), '-to', str(end), '-i', input_video, - '-c:v', 'libx264', '-crf', '18', '-preset', 'fast', + '-c:v', 'libx264', '-crf', '12', '-preset', 'ultrafast', '-c:a', 'aac', clip_temp_path ] diff --git a/requirements.txt b/requirements.txt index d132879..6c69c4e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ scenedetect ultralytics +--extra-index-url https://download.pytorch.org/whl/cpu torch torchvision tqdm diff --git a/subtitles.py b/subtitles.py index f961a0a..3cb9fc0 100644 --- a/subtitles.py +++ b/subtitles.py @@ -117,7 +117,15 @@ def generate_srt(transcript, clip_start, clip_end, output_path, max_chars=20, ma block_end = current_block[-1]['end'] - clip_start text = " ".join([w['word'] for w in current_block]).strip() srt_content += format_srt_block(index, block_start, block_end, text) - + index += 1 + + # CTA block: "follow for more" in the last 2 seconds of the clip + clip_duration = clip_end - clip_start + cta_start = max(0, clip_duration - 2.0) + cta_end = clip_duration + if cta_end > cta_start: + srt_content += format_srt_block(index, cta_start, cta_end, "follow for more such content 🔥") + with open(output_path, 'w', encoding='utf-8') as f: f.write(srt_content) @@ -209,7 +217,7 @@ def burn_subtitles(video_path, srt_path, output_path, alignment=2, fontsize=16, '-i', video_path, '-vf', f"subtitles='{safe_srt_path}':force_style='{style_string}'", '-c:a', 'copy', - '-c:v', 'libx264', '-preset', 'fast', '-crf', '23', + '-c:v', 'libx264', '-preset', 'medium', '-crf', '18', output_path ]