commit d99da42a2edda1552f969463921083a40b5f24d6 Author: tigeren Date: Sat Apr 11 15:23:52 2026 +0800 init project diff --git a/archive/cut - Copy.py b/archive/cut - Copy.py new file mode 100644 index 0000000..c1abbc1 --- /dev/null +++ b/archive/cut - Copy.py @@ -0,0 +1,128 @@ +import subprocess +import json +import os +import glob + +def get_video_duration(video_path): + """Uses ffprobe to extract the exact duration of the baseline video.""" + print(f"Analyzing baseline duration: {video_path}...") + cmd = [ + 'ffprobe', '-v', 'error', '-show_entries', 'format=duration', + '-of', 'default=noprint_wrappers=1:nokey=1', video_path + ] + result = subprocess.run(cmd, capture_output=True, text=True) + try: + duration = float(result.stdout.strip()) + print(f"Baseline duration found: {duration} seconds.") + return duration + except ValueError: + print("Error: Could not determine the duration of the baseline video.") + return None + +def get_next_keyframe(input_video, cut_timestamp): + """Finds the next keyframe immediately following the cut timestamp.""" + cmd = [ + 'ffprobe', '-v', 'quiet', '-select_streams', 'v', + '-skip_frame', 'nokey', '-show_frames', + '-show_entries', 'frame=pkt_pts_time,pkt_dts_time,best_effort_timestamp_time', + '-of', 'json', '-read_intervals', f'{cut_timestamp}%+10', input_video + ] + result = subprocess.run(cmd, capture_output=True, text=True) + frames = json.loads(result.stdout).get('frames', []) + + for frame in frames: + # Safely try multiple timestamp keys since different codecs/containers vary + time_str = (frame.get('best_effort_timestamp_time') or + frame.get('pkt_pts_time') or + frame.get('pkt_dts_time')) + + if time_str is not None: + keyframe_time = float(time_str) + if keyframe_time > cut_timestamp: + return keyframe_time + return None + +def smart_cut(input_video, output_video, cut_timestamp): + """Executes the Smart Cut process: Re-encode a tiny segment, copy the rest, and stitch.""" + print(f"Processing: {input_video}...") + next_keyframe = get_next_keyframe(input_video, cut_timestamp) + + if not next_keyframe: + print(f"Skipping {input_video}: Could not find a keyframe after {cut_timestamp}s.") + return + + part1 = f"temp_part1_{input_video}" + part2 = f"temp_part2_{input_video}" + concat_list = f"concat_{input_video}.txt" + + try: + # 1. Re-encode the tiny segment (from exact cut to next keyframe) + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-i', input_video, + '-ss', str(cut_timestamp), '-to', str(next_keyframe), + '-c:v', 'libx264', '-crf', '18', '-c:a', 'copy', part1 + ], check=True) + + # 2. Copy the rest of the video (from next keyframe to the end) + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-ss', str(next_keyframe), '-i', input_video, + '-c:v', 'copy', '-c:a', 'copy', part2 + ], check=True) + + # 3. Concatenate them using a text list + with open(concat_list, 'w', encoding='utf-8') as f: + f.write(f"file '{part1}'\nfile '{part2}'\n") + + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-f', 'concat', '-safe', '0', + '-i', concat_list, '-c', 'copy', output_video + ], check=True) + print(f"Success! Saved to {output_video}") + + except subprocess.CalledProcessError as e: + print(f"Error processing {input_video}. FFmpeg failed.") + finally: + # 4. Clean up temporary files safely + for temp_file in [part1, part2, concat_list]: + if os.path.exists(temp_file): + os.remove(temp_file) + +def main(): + baseline_file = "baseline.mp4" + output_dir = "processed_videos" + + # Check if baseline exists + if not os.path.exists(baseline_file): + print(f"Error: '{baseline_file}' not found in the current directory.") + return + + # Get exact cut timestamp from the baseline + cut_timestamp = get_video_duration(baseline_file) + if not cut_timestamp: + return + + # Create output directory if it doesn't exist + os.makedirs(output_dir, exist_ok=True) + + # Find all mp4 files in the folder + video_files = glob.glob("*.mp4") + + # Remove the baseline file from the processing list + if baseline_file in video_files: + video_files.remove(baseline_file) + + if not video_files: + print("No other .mp4 files found to process.") + return + + print(f"Found {len(video_files)} videos to process.\n" + "-"*30) + + # Process each video + for video in video_files: + output_path = os.path.join(output_dir, video) + smart_cut(video, output_path, cut_timestamp) + + print("-" * 30 + "\nBatch processing complete!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/archive/cut copy.py b/archive/cut copy.py new file mode 100644 index 0000000..c1abbc1 --- /dev/null +++ b/archive/cut copy.py @@ -0,0 +1,128 @@ +import subprocess +import json +import os +import glob + +def get_video_duration(video_path): + """Uses ffprobe to extract the exact duration of the baseline video.""" + print(f"Analyzing baseline duration: {video_path}...") + cmd = [ + 'ffprobe', '-v', 'error', '-show_entries', 'format=duration', + '-of', 'default=noprint_wrappers=1:nokey=1', video_path + ] + result = subprocess.run(cmd, capture_output=True, text=True) + try: + duration = float(result.stdout.strip()) + print(f"Baseline duration found: {duration} seconds.") + return duration + except ValueError: + print("Error: Could not determine the duration of the baseline video.") + return None + +def get_next_keyframe(input_video, cut_timestamp): + """Finds the next keyframe immediately following the cut timestamp.""" + cmd = [ + 'ffprobe', '-v', 'quiet', '-select_streams', 'v', + '-skip_frame', 'nokey', '-show_frames', + '-show_entries', 'frame=pkt_pts_time,pkt_dts_time,best_effort_timestamp_time', + '-of', 'json', '-read_intervals', f'{cut_timestamp}%+10', input_video + ] + result = subprocess.run(cmd, capture_output=True, text=True) + frames = json.loads(result.stdout).get('frames', []) + + for frame in frames: + # Safely try multiple timestamp keys since different codecs/containers vary + time_str = (frame.get('best_effort_timestamp_time') or + frame.get('pkt_pts_time') or + frame.get('pkt_dts_time')) + + if time_str is not None: + keyframe_time = float(time_str) + if keyframe_time > cut_timestamp: + return keyframe_time + return None + +def smart_cut(input_video, output_video, cut_timestamp): + """Executes the Smart Cut process: Re-encode a tiny segment, copy the rest, and stitch.""" + print(f"Processing: {input_video}...") + next_keyframe = get_next_keyframe(input_video, cut_timestamp) + + if not next_keyframe: + print(f"Skipping {input_video}: Could not find a keyframe after {cut_timestamp}s.") + return + + part1 = f"temp_part1_{input_video}" + part2 = f"temp_part2_{input_video}" + concat_list = f"concat_{input_video}.txt" + + try: + # 1. Re-encode the tiny segment (from exact cut to next keyframe) + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-i', input_video, + '-ss', str(cut_timestamp), '-to', str(next_keyframe), + '-c:v', 'libx264', '-crf', '18', '-c:a', 'copy', part1 + ], check=True) + + # 2. Copy the rest of the video (from next keyframe to the end) + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-ss', str(next_keyframe), '-i', input_video, + '-c:v', 'copy', '-c:a', 'copy', part2 + ], check=True) + + # 3. Concatenate them using a text list + with open(concat_list, 'w', encoding='utf-8') as f: + f.write(f"file '{part1}'\nfile '{part2}'\n") + + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-f', 'concat', '-safe', '0', + '-i', concat_list, '-c', 'copy', output_video + ], check=True) + print(f"Success! Saved to {output_video}") + + except subprocess.CalledProcessError as e: + print(f"Error processing {input_video}. FFmpeg failed.") + finally: + # 4. Clean up temporary files safely + for temp_file in [part1, part2, concat_list]: + if os.path.exists(temp_file): + os.remove(temp_file) + +def main(): + baseline_file = "baseline.mp4" + output_dir = "processed_videos" + + # Check if baseline exists + if not os.path.exists(baseline_file): + print(f"Error: '{baseline_file}' not found in the current directory.") + return + + # Get exact cut timestamp from the baseline + cut_timestamp = get_video_duration(baseline_file) + if not cut_timestamp: + return + + # Create output directory if it doesn't exist + os.makedirs(output_dir, exist_ok=True) + + # Find all mp4 files in the folder + video_files = glob.glob("*.mp4") + + # Remove the baseline file from the processing list + if baseline_file in video_files: + video_files.remove(baseline_file) + + if not video_files: + print("No other .mp4 files found to process.") + return + + print(f"Found {len(video_files)} videos to process.\n" + "-"*30) + + # Process each video + for video in video_files: + output_path = os.path.join(output_dir, video) + smart_cut(video, output_path, cut_timestamp) + + print("-" * 30 + "\nBatch processing complete!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/archive/run_python_code3.bat b/archive/run_python_code3.bat new file mode 100644 index 0000000..a252a88 --- /dev/null +++ b/archive/run_python_code3.bat @@ -0,0 +1,9 @@ +@echo off +REM 1. Activate the miniconda environment +call "C:\Users\tigeren\miniconda3\Scripts\activate.bat" "C:\Users\tigeren\miniconda3" + +REM 2. Run the python script and pass all arguments (%*) +python "C:\Dev\videocut\cut.py" %* + +:: 3. Keep the window open so you can see any print statements or errors +pause \ No newline at end of file diff --git a/cut.py b/cut.py new file mode 100644 index 0000000..c4979e4 --- /dev/null +++ b/cut.py @@ -0,0 +1,162 @@ +import subprocess +import json +import os +import glob +import sys + +def get_video_duration(video_path): + """Uses ffprobe to extract the exact duration of the baseline video.""" + print(f"Analyzing baseline duration: {video_path}...") + cmd = [ + 'ffprobe', '-v', 'error', '-show_entries', 'format=duration', + '-of', 'default=noprint_wrappers=1:nokey=1', video_path + ] + result = subprocess.run(cmd, capture_output=True, text=True) + try: + duration = float(result.stdout.strip()) + print(f"Baseline duration found: {duration} seconds.") + return duration + except ValueError: + print("Error: Could not determine the duration of the baseline video.") + return None + +def get_next_keyframe(input_video, cut_timestamp): + """Finds the next keyframe immediately following the cut timestamp.""" + cmd = [ + 'ffprobe', '-v', 'quiet', '-select_streams', 'v', + '-skip_frame', 'nokey', '-show_frames', + '-show_entries', 'frame=pkt_pts_time,pkt_dts_time,best_effort_timestamp_time', + '-of', 'json', '-read_intervals', f'{cut_timestamp}%+180', input_video + ] + result = subprocess.run(cmd, capture_output=True, text=True) + frames = json.loads(result.stdout).get('frames', []) + + for frame in frames: + # Safely try multiple timestamp keys + time_str = (frame.get('best_effort_timestamp_time') or + frame.get('pkt_pts_time') or + frame.get('pkt_dts_time')) + + if time_str is not None: + keyframe_time = float(time_str) + if keyframe_time > cut_timestamp: + return keyframe_time + return None + +def smart_cut(input_video, output_video, cut_timestamp): + """Executes the Smart Cut process: Re-encode a tiny segment, copy the rest, and stitch.""" + print(f"Processing: {input_video}...") + next_keyframe = get_next_keyframe(input_video, cut_timestamp) + + if not next_keyframe: + print(f"Skipping {input_video}: Could not find a keyframe after {cut_timestamp}s.") + return + + part1 = f"temp_part1_{input_video}" + part2 = f"temp_part2_{input_video}" + concat_list = f"concat_{input_video}.txt" + + try: + # 1. Re-encode the tiny segment (from exact cut to next keyframe) + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-i', input_video, + '-ss', str(cut_timestamp), '-to', str(next_keyframe), + '-c:v', 'libx264', '-crf', '18', '-c:a', 'copy', part1 + ], check=True) + + # 2. Copy the rest of the video (from next keyframe to the end) + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-ss', str(next_keyframe), '-i', input_video, + '-c:v', 'copy', '-c:a', 'copy', part2 + ], check=True) + + # 3. Concatenate them using a text list (Forced UTF-8 encoding for emojis/special chars) + with open(concat_list, 'w', encoding='utf-8') as f: + f.write(f"file '{part1}'\nfile '{part2}'\n") + + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-f', 'concat', '-safe', '0', + '-i', concat_list, '-c', 'copy', output_video + ], check=True) + print(f"Success! Saved to {output_video}") + + except subprocess.CalledProcessError as e: + print(f"Error processing {input_video}. FFmpeg failed.") + finally: + # 4. Clean up temporary files safely + for temp_file in [part1, part2, concat_list]: + if os.path.exists(temp_file): + os.remove(temp_file) + +def main(): + # 1. Check if the user provided an argument + if len(sys.argv) < 2: + print("Usage: python cut.py ") + return + + # 2. Get the absolute path of the provided argument + # 2. Get the absolute path and strip any accidental quotes + raw_input = sys.argv[1].strip(' "''') + input_arg = os.path.abspath(raw_input) + # input_arg = os.path.abspath(sys.argv[1]) + print(input_arg) + + # 3. Determine if it's a file or a folder + if os.path.isfile(input_arg): + if not input_arg.lower().endswith('.mp4'): + print("Error: The specified file is not an .mp4 video.") + return + target_dir = os.path.dirname(input_arg) + target_files = [os.path.basename(input_arg)] # Process only this single file + + elif os.path.isdir(input_arg): + target_dir = input_arg + target_files = None # We will find all files later + + else: + print(f"Error: Path '{input_arg}' does not exist.") + return + + # 4. Change the working directory to the target folder + # This prevents FFmpeg from crashing due to complex Windows path names during concatenation + os.chdir(target_dir) + + # 5. If it was a folder, grab all .mp4 files + if target_files is None: + target_files = glob.glob("*.mp4") + + baseline_file = "baseline.mp4" + + # Check if baseline exists in the target directory + if not os.path.exists(baseline_file): + print(f"Error: '{baseline_file}' not found in the target folder: {target_dir}") + return + + # Remove the baseline file from the processing list + if baseline_file in target_files: + target_files.remove(baseline_file) + + if not target_files: + print("No other .mp4 files found to process.") + return + + # Get exact cut timestamp from the baseline + cut_timestamp = get_video_duration(baseline_file) + if not cut_timestamp: + return + + # Create output directory + output_dir = "processed_videos" + os.makedirs(output_dir, exist_ok=True) + + print(f"Found {len(target_files)} video(s) to process in '{target_dir}'.\n" + "-"*30) + + # Process each video + for video in target_files: + output_path = os.path.join(output_dir, video) + smart_cut(video, output_path, cut_timestamp) + + print("-" * 30 + "\nBatch processing complete!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/cut_head.py b/cut_head.py new file mode 100644 index 0000000..434c38f --- /dev/null +++ b/cut_head.py @@ -0,0 +1,162 @@ +import subprocess +import json +import os +import glob +import sys + +def get_video_duration(video_path): + """Uses ffprobe to extract the exact duration of the baseline video.""" + print(f"Analyzing baseline duration: {video_path}...") + cmd = [ + 'ffprobe', '-v', 'error', '-show_entries', 'format=duration', + '-of', 'default=noprint_wrappers=1:nokey=1', video_path + ] + result = subprocess.run(cmd, capture_output=True, text=True) + try: + duration = float(result.stdout.strip()) + print(f"Baseline duration found: {duration} seconds.") + return duration + except ValueError: + print("Error: Could not determine the duration of the baseline video.") + return None + +def get_next_keyframe(input_video, cut_timestamp): + """Finds the next keyframe immediately following the cut timestamp.""" + cmd = [ + 'ffprobe', '-v', 'quiet', '-select_streams', 'v', + '-skip_frame', 'nokey', '-show_frames', + '-show_entries', 'frame=pkt_pts_time,pkt_dts_time,best_effort_timestamp_time', + '-of', 'json', '-read_intervals', f'{cut_timestamp}%+180', input_video + ] + result = subprocess.run(cmd, capture_output=True, text=True) + frames = json.loads(result.stdout).get('frames', []) + + for frame in frames: + # Safely try multiple timestamp keys + time_str = (frame.get('best_effort_timestamp_time') or + frame.get('pkt_pts_time') or + frame.get('pkt_dts_time')) + + if time_str is not None: + keyframe_time = float(time_str) + if keyframe_time > cut_timestamp: + return keyframe_time + return None + +def smart_cut(input_video, output_video, cut_timestamp): + """Executes the Smart Cut process: Re-encode a tiny segment, copy the rest, and stitch.""" + print(f"Processing: {input_video}...") + next_keyframe = get_next_keyframe(input_video, cut_timestamp) + + if not next_keyframe: + print(f"Skipping {input_video}: Could not find a keyframe after {cut_timestamp}s.") + return + + part1 = f"temp_part1_{input_video}" + part2 = f"temp_part2_{input_video}" + concat_list = f"concat_{input_video}.txt" + + try: + # 1. Re-encode the tiny segment (from exact cut to next keyframe) + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-i', input_video, + '-ss', str(cut_timestamp), '-to', str(next_keyframe), + '-c:v', 'libx264', '-crf', '18', '-c:a', 'copy', part1 + ], check=True) + + # 2. Copy the rest of the video (from next keyframe to the end) + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-ss', str(next_keyframe), '-i', input_video, + '-c:v', 'copy', '-c:a', 'copy', part2 + ], check=True) + + # 3. Concatenate them using a text list (Forced UTF-8 encoding for emojis/special chars) + with open(concat_list, 'w', encoding='utf-8') as f: + f.write(f"file '{part1}'\nfile '{part2}'\n") + + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-f', 'concat', '-safe', '0', + '-i', concat_list, '-c', 'copy', output_video + ], check=True) + print(f"Success! Saved to {output_video}") + + except subprocess.CalledProcessError as e: + print(f"Error processing {input_video}. FFmpeg failed.") + finally: + # 4. Clean up temporary files safely + for temp_file in [part1, part2, concat_list]: + if os.path.exists(temp_file): + os.remove(temp_file) + +def main(): + # 1. Check if the user provided an argument + if len(sys.argv) < 2: + print("Usage: python cut.py ") + return + + # 2. Get the absolute path of the provided argument + # 2. Get the absolute path and strip any accidental quotes + raw_input = sys.argv[1].strip(' "''') + input_arg = os.path.abspath(raw_input) + # input_arg = os.path.abspath(sys.argv[1]) + print(input_arg) + + # 3. Determine if it's a file or a folder + if os.path.isfile(input_arg): + if not input_arg.lower().endswith('.mp4'): + print("Error: The specified file is not an .mp4 video.") + return + target_dir = os.path.dirname(input_arg) + target_files = [os.path.basename(input_arg)] # Process only this single file + + elif os.path.isdir(input_arg): + target_dir = input_arg + target_files = None # We will find all files later + + else: + print(f"Error: Path '{input_arg}' does not exist.") + return + + # 4. Change the working directory to the target folder + # This prevents FFmpeg from crashing due to complex Windows path names during concatenation + os.chdir(target_dir) + + # 5. If it was a folder, grab all .mp4 files + if target_files is None: + target_files = glob.glob("*.mp4") + + baseline_file = "i.mp4" + + # Check if baseline exists in the target directory + if not os.path.exists(baseline_file): + print(f"Error: '{baseline_file}' not found in the target folder: {target_dir}") + return + + # Remove the baseline file from the processing list + if baseline_file in target_files: + target_files.remove(baseline_file) + + if not target_files: + print("No other .mp4 files found to process.") + return + + # Get exact cut timestamp from the baseline + cut_timestamp = get_video_duration(baseline_file) + if not cut_timestamp: + return + + # Create output directory + output_dir = "processed_videos" + os.makedirs(output_dir, exist_ok=True) + + print(f"Found {len(target_files)} video(s) to process in '{target_dir}'.\n" + "-"*30) + + # Process each video + for video in target_files: + output_path = os.path.join(output_dir, video) + smart_cut(video, output_path, cut_timestamp) + + print("-" * 30 + "\nBatch processing complete!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/cut_tail.py b/cut_tail.py new file mode 100644 index 0000000..55b28f9 --- /dev/null +++ b/cut_tail.py @@ -0,0 +1,182 @@ +import subprocess +import json +import os +import glob +import sys + +def get_video_duration(video_path): + """Uses ffprobe to extract the exact duration of a video.""" + cmd = [ + 'ffprobe', '-v', 'error', '-show_entries', 'format=duration', + '-of', 'default=noprint_wrappers=1:nokey=1', video_path + ] + result = subprocess.run(cmd, capture_output=True, text=True) + try: + return float(result.stdout.strip()) + except ValueError: + return None + +def get_previous_keyframe(input_video, cut_timestamp): + """Scans backward to find the closest keyframe immediately PRECEDING the cut timestamp.""" + # Look up to 5 minutes backward for a keyframe to be safe with heavy compression + start_search = max(0, cut_timestamp - 300) + cmd = [ + 'ffprobe', '-v', 'quiet', '-select_streams', 'v', + '-skip_frame', 'nokey', '-show_frames', + '-show_entries', 'frame=pkt_pts_time,pkt_dts_time,best_effort_timestamp_time', + '-of', 'json', '-read_intervals', f'{start_search}%{cut_timestamp}', input_video + ] + result = subprocess.run(cmd, capture_output=True, text=True) + frames = json.loads(result.stdout).get('frames', []) + + best_keyframe = 0.0 # Default to the start of the video if no keyframe is found + + for frame in frames: + # Safely try multiple timestamp keys + time_str = (frame.get('best_effort_timestamp_time') or + frame.get('pkt_pts_time') or + frame.get('pkt_dts_time')) + + if time_str is not None: + keyframe_time = float(time_str) + # Find the highest keyframe timestamp that is strictly less than or equal to our cut point + if keyframe_time <= cut_timestamp and keyframe_time > best_keyframe: + best_keyframe = keyframe_time + + return best_keyframe + +def smart_cut_tail(input_video, output_video, tail_duration): + """Executes the Reverse Smart Cut process: Copy the bulk, re-encode the tiny tail tip, and stitch.""" + + # 1. Determine exactly where to cut + target_duration = get_video_duration(input_video) + if not target_duration: + print(f"Skipping {input_video}: Could not determine its total duration.") + return + + cut_timestamp = target_duration - tail_duration + + if cut_timestamp <= 0: + print(f"Skipping {input_video}: Video is shorter than the tail reference clip.") + return + + print(f"Processing: {input_video} (Cutting tail at {cut_timestamp:.2f}s)...") + + # 2. Find the preceding keyframe + prev_keyframe = get_previous_keyframe(input_video, cut_timestamp) + + part1 = f"temp_part1_{input_video}" + part2 = f"temp_part2_{input_video}" + concat_list = f"concat_{input_video}.txt" + + try: + # 3. Copy the bulk of the video (from 0 to the preceding keyframe) + if prev_keyframe > 0.0: + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-i', input_video, + '-t', str(prev_keyframe), + '-c:v', 'copy', '-c:a', 'copy', part1 + ], check=True) + + # 4. Re-encode the tiny tip (from the preceding keyframe to the exact cut timestamp) + tiny_duration = cut_timestamp - prev_keyframe + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', + '-ss', str(prev_keyframe), '-i', input_video, + '-t', str(tiny_duration), + '-c:v', 'libx264', '-crf', '18', '-c:a', 'copy', part2 + ], check=True) + + # 5. Concatenate them using a text list (Forced UTF-8 encoding for emojis/special chars) + with open(concat_list, 'w', encoding='utf-8') as f: + if prev_keyframe > 0.0: + f.write(f"file '{part1}'\n") + f.write(f"file '{part2}'\n") + + subprocess.run([ + 'ffmpeg', '-y', '-v', 'error', '-f', 'concat', '-safe', '0', + '-i', concat_list, '-c', 'copy', output_video + ], check=True) + print(f"Success! Saved to {output_video}") + + except subprocess.CalledProcessError: + print(f"Error processing {input_video}. FFmpeg failed.") + finally: + # 6. Clean up temporary files safely + for temp_file in [part1, part2, concat_list]: + if os.path.exists(temp_file): + os.remove(temp_file) + +def main(): + if len(sys.argv) < 2: + print("Usage: python cut_tail.py ") + return + + # Get the absolute path and strip any accidental quotes + raw_input = sys.argv[1].strip(' "''') + input_arg = os.path.abspath(raw_input) + + # Determine if it's a file or a folder + if os.path.isfile(input_arg): + if not input_arg.lower().endswith('.mp4'): + print("Error: The specified file is not an .mp4 video.") + return + target_dir = os.path.dirname(input_arg) + target_files = [os.path.basename(input_arg)] + + elif os.path.isdir(input_arg): + target_dir = input_arg + target_files = None + + else: + print(f"Error: Path '{input_arg}' does not exist.") + return + + # Change the working directory to the target folder + os.chdir(target_dir) + + # If it was a folder, grab all .mp4 files + if target_files is None: + target_files = glob.glob("*.mp4") + + # WE NOW LOOK FOR 'tail_ref.mp4' INSTEAD OF 'baseline.mp4' + tail_ref_file = "o.mp4" + + # Check if tail reference exists in the target directory + if not os.path.exists(tail_ref_file): + print(f"Error: '{tail_ref_file}' not found in the target folder: {target_dir}") + return + + # Remove the reference file from the processing list so we don't try to cut it + if tail_ref_file in target_files: + target_files.remove(tail_ref_file) + + if not target_files: + print("No other .mp4 files found to process.") + return + + # Get exact duration of the tail reference + print(f"Analyzing tail reference duration: {tail_ref_file}...") + tail_duration = get_video_duration(tail_ref_file) + + if not tail_duration: + print("Error: Could not determine the duration of the tail reference video.") + return + + print(f"Tail reference duration found: {tail_duration} seconds.") + + # Create output directory + output_dir = "processed_videos" + os.makedirs(output_dir, exist_ok=True) + + print(f"\nFound {len(target_files)} video(s) to process in '{target_dir}'.\n" + "-"*30) + + # Process each video + for video in target_files: + output_path = os.path.join(output_dir, video) + smart_cut_tail(video, output_path, tail_duration) + + print("-" * 30 + "\nBatch processing complete!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/run_python_code_head.bat b/run_python_code_head.bat new file mode 100644 index 0000000..c1ad338 --- /dev/null +++ b/run_python_code_head.bat @@ -0,0 +1,8 @@ +@echo off +:: 1. Activate the Conda base script +call C:\Users\tigeren\miniconda3\Scripts\activate.bat +:: 2. Switch to your specific environment +call conda activate videocut +:: 3. Run your Python script +python C:\Dev\videocut\cut_head.py %* +pause \ No newline at end of file diff --git a/run_python_code_tail.bat b/run_python_code_tail.bat new file mode 100644 index 0000000..2718377 --- /dev/null +++ b/run_python_code_tail.bat @@ -0,0 +1,8 @@ +@echo off +:: 1. Activate the Conda base script +call C:\Users\tigeren\miniconda3\Scripts\activate.bat +:: 2. Switch to your specific environment +call conda activate videocut +:: 3. Run your Python script +python C:\Dev\videocut\cut_tail.py %* +pause \ No newline at end of file diff --git a/run_python_code_zhconv.bat b/run_python_code_zhconv.bat new file mode 100644 index 0000000..83660db --- /dev/null +++ b/run_python_code_zhconv.bat @@ -0,0 +1,8 @@ +@echo off +:: 1. Activate the Conda base script +call C:\Users\tigeren\miniconda3\Scripts\activate.bat +:: 2. Switch to your specific environment +call conda activate videocut +:: 3. Run your Python script +python C:\Dev\videocut\zhconv.py %* +pause diff --git a/zhconv.py b/zhconv.py new file mode 100644 index 0000000..ab58da0 --- /dev/null +++ b/zhconv.py @@ -0,0 +1,62 @@ +import os +import sys +import glob + +try: + from opencc import OpenCC +except ImportError: + print("Error: 'opencc-python' package not found. Installing...") + import subprocess + subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'opencc-python']) + from opencc import OpenCC + +def convert_filename(filename, cc): + name, ext = os.path.splitext(filename) + simplified_name = cc.convert(name) + if simplified_name != name: + return simplified_name + ext + return None + +def main(): + if len(sys.argv) < 2: + print("Usage: python zhconv.py ") + return + + raw_input = sys.argv[1].strip(' "\'') + input_arg = os.path.abspath(raw_input) + print(f"Target: {input_arg}") + + cc = OpenCC('t2s') + + if os.path.isfile(input_arg): + target_dir = os.path.dirname(input_arg) + target_files = [os.path.basename(input_arg)] + elif os.path.isdir(input_arg): + target_dir = input_arg + target_files = None + else: + print(f"Error: Path '{input_arg}' does not exist.") + return + + if target_files is None: + target_files = glob.glob(os.path.join(target_dir, '*')) + + converted_count = 0 + for file_path in target_files: + if not os.path.isfile(file_path): + continue + filename = os.path.basename(file_path) + new_filename = convert_filename(filename, cc) + if new_filename: + new_path = os.path.join(target_dir, new_filename) + if os.path.exists(new_path): + print(f"Skipped (already exists): {new_filename}") + else: + os.rename(file_path, new_path) + print(f"Converted: {filename} -> {new_filename}") + converted_count += 1 + + print(f"\nConverted {converted_count} file(s).") + +if __name__ == "__main__": + main()