give me the complete updated file with these changes: heres the code: # Third party imports
import os
import json
import time
import tomlkit
import hashlib
import numpy as np
from moviepy.editor import VideoFileClip, concatenate_videoclips
from utils.console import print_step, print_substep

class VideoCompiler:
    def __init__(self):
        self.config = self.load_config()
        self.config_hash = self.get_config_hash()
        self.results_folder = self.config['paths']['source_folder']
        self.merge_folder = self.config['paths']['merge_folder'] 
        self.target_duration = self.config['merger']['target_duration'] * 60  # convert to seconds
        self.subreddit_durations = {}
        self.processed_videos_file = "processed_videos.json"
        
        os.makedirs(self.merge_folder, exist_ok=True)
        print_step(f"Looking for videos in: {self.results_folder}")
        print_step(f"Current mode: {self.config['merger']['mode']}")
        print_step(f"Current status: {self.config['merger']['status']}")

        self.processed_videos = self.load_processed_videos()

    def get_config_hash(self):
        """Get hash of current config file to detect changes"""
        try:
            with open("config.toml", "rb") as f:
                return hashlib.md5(f.read()).hexdigest()
        except FileNotFoundError:
            return None

    def check_config_changed(self):
        """Check if config file has been modified"""
        try:
            current_hash = self.get_config_hash()
            if current_hash != self.config_hash:
                print_step("Config file changed! Reloading settings...")
                
                # Load new config
                with open("config.toml", "r", encoding="utf-8") as f:
                    new_config = tomlkit.load(f)
                
                # Update config and hash
                self.config = new_config
                self.config_hash = current_hash
                self.results_folder = new_config['paths']['source_folder']
                self.merge_folder = new_config['paths']['merge_folder']
                self.target_duration = new_config['merger']['target_duration'] * 60
                
                # Print all updated settings
                print_step(f"New settings loaded:")
                print_substep(f"Mode: {new_config['merger']['mode']}")
                print_substep(f"Status: {new_config['merger']['status']}")
                print_substep(f"Target duration: {new_config['merger']['target_duration']} minutes")
                print_substep(f"Check interval: {new_config['merger']['check_interval']} minutes")
                
                return True
            return False
        except Exception as e:
            print_substep(f"Error reloading config: {str(e)}")
            return False

    def check_status(self):
        """Check current processing status"""
        self.check_config_changed()
        status = self.config['merger']['status']

        if status == "paused":
            print_step("Processing is paused. Waiting for resume...")
            while self.config['merger']['status'] == "paused":
                time.sleep(10)  # Check every 10 seconds
                self.check_config_changed()
            print_step("Processing resumed!")
            return True
        elif status == "stopping":
            print_step("Stop signal received. Finishing current task...")
            return False
        return True

    def wait_with_status_check(self, minutes):
        """Wait specified minutes while checking for status changes"""
        print_step(f"Waiting {minutes} minutes before next check...")
        for _ in range(minutes):
            if not self.check_status():  # Stop if requested
                return False
            time.sleep(60)  # Sleep for 1 minute
            if self.check_config_changed():
                print_step("Config changed during wait. Starting new cycle...")
                return True
        return True

    def load_processed_videos(self):
        """Load list of already processed videos"""
        try:
            if os.path.exists(self.processed_videos_file):
                with open(self.processed_videos_file, 'r') as f:
                    return json.load(f)
            return []
        except Exception as e:
            print_substep(f"Error loading processed videos: {str(e)}")
            return []

    def save_processed_videos(self):
        """Save list of processed videos"""
        try:
            with open(self.processed_videos_file, 'w') as f:
                json.dump(self.processed_videos, f)
        except Exception as e:
            print_substep(f"Error saving processed videos: {str(e)}")

    def load_config(self):
        """Load configuration from toml file"""
        try:
            with open("config.toml", "r", encoding="utf-8") as f:
                return tomlkit.load(f)
        except FileNotFoundError:
            print_step("Config file not found. Creating default config...")
            self.create_default_config()
            return self.load_config()

    def create_default_config(self):
        """Create default configuration file"""
        doc = tomlkit.document()
        
        merger = tomlkit.table()
        merger.add("status", "running")
        merger.add("mode", "fastest")
        merger.add("specific_subreddit", "AmItheAsshole+relationship_advice+confession+tifu")
        merger.add("check_interval", 3)
        merger.add("target_duration", 10)
        merger.add("auto_process", True)
        doc.add("merger", merger)
        
        paths = tomlkit.table()
        paths.add("source_folder", r"C:\Users\buggy\Desktop\FullyAutomatedRedditVideoMakerBot-main\results")
        paths.add("merge_folder", "MergeVideo")
        doc.add("paths", paths)
        
        processing = tomlkit.table()
        processing.add("delete_source", False)
        processing.add("output_format", "mp4")
        doc.add("processing", processing)

        splicing = tomlkit.table()
        splicing.add("min_silence_len", 500)
        splicing.add("silence_thresh", -40)
        splicing.add("buffer_time", 0.5)
        doc.add("splicing", splicing)
        
        with open("config.toml", "w", encoding="utf-8") as f:
            f.write(tomlkit.dumps(doc))

    def get_video_duration(self, video_path):
        """Get duration of video in seconds using moviepy"""
        try:
            # Use moviepy instead of ffmpeg for more reliable duration checking
            with VideoFileClip(video_path) as video:
                duration = video.duration
                print_substep(f"Duration of {os.path.basename(video_path)}: {duration:.2f} seconds")
                return duration
        except Exception as e:
            print_substep(f"Error getting duration: {str(e)}")
            return 0

    def detect_title_end(self, video_clip):
        """Detect when title reading ends based on audio analysis"""
        try:
            # Extract audio
            audio = video_clip.audio
            if audio is None:
                print_substep("No audio found in video, using default time")
                return 5.0

            # Get the raw audio data and ensure it's properly formatted
            try:
                audio_array = audio.to_soundarray(fps=22050)  # Specify fps to ensure consistent format
                
                # Handle stereo audio
                if len(audio_array.shape) == 2:
                    audio_array = np.mean(audio_array, axis=1)  # Convert to mono
            except:
                print_substep("Could not process audio, using default time")
                return 5.0

            # Simple volume-based silence detection
            chunk_duration = 0.5  # half second chunks
            chunk_size = int(22050 * chunk_duration)
            
            for i in range(0, len(audio_array), chunk_size):
                chunk = audio_array[i:min(i + chunk_size, len(audio_array))]
                volume = np.abs(chunk).mean()
                
                if volume < 0.01:  # Silence threshold
                    title_end = (i / 22050) + 0.5  # Add buffer
                    if title_end < 2.0:  # Too short, probably false detection
                        continue
                    if title_end > 10.0:  # Too long, use default
                        return 5.0
                    return title_end

            return 5.0  # Default if no clear silence found

        except Exception as e:
            print_substep(f"Error in audio analysis: {str(e)}")
            return 5.0

    def splice_video(self, video_pair):
        """Create spliced version with clean intro and main content"""
        clean_video = None
        subtitled_video = None
        
        try:
            print_substep(f"Processing video: {video_pair['name']}")

            # Load videos
            clean_video = VideoFileClip(video_pair['files']['normal'])
            subtitled_video = VideoFileClip(video_pair['files']['subtitled'])

            # Get the title end point (with default fallback)
            title_end = min(self.detect_title_end(clean_video), clean_video.duration - 1)

            # Create the clips with safety checks
            intro_clip = clean_video.subclip(0, title_end)
            main_clip = subtitled_video.subclip(title_end)

            # Combine the clips
            final_clip = concatenate_videoclips([intro_clip, main_clip], method="compose")
            
            # Close intermediate clips
            intro_clip.close()
            main_clip.close()
            
            return final_clip

        except Exception as e:
            print_substep(f"Error in video splicing: {str(e)}")
            return None
        
        finally:
            # Clean up resources
            if clean_video is not None:
                try:
                    clean_video.close()
                except:
                    pass
            if subtitled_video is not None:
                try:
                    subtitled_video.close()
                except:
                    pass

    def merge_videos(self, selected_videos):
        """Merge selected videos with proper splicing"""
        processed_clips = []
        
        try:
            # Process each video
            for video in selected_videos:
                print_step(f"Processing: {video['name']}")
                processed_clip = self.splice_video(video)
                
                if processed_clip is not None:
                    processed_clips.append(processed_clip)
                else:
                    print_substep(f"Failed to process {video['name']}, skipping...")

            if not processed_clips:
                print_step("No videos were successfully processed")
                return None

            # Merge the successful clips
            print_step(f"Merging {len(processed_clips)} videos...")
            final_clip = concatenate_videoclips(processed_clips, method="compose")

            # Generate output path
            output_path = os.path.join(
                self.merge_folder,
                f"compilation_{time.strftime('%Y%m%d_%H%M%S')}.mp4"
            )

            # Write the final video
            print_step("Writing final video...")
            final_clip.write_videofile(
                output_path,
                codec='libx264',
                audio_codec='aac',
                temp_audiofile='temp-audio.m4a',
                remove_temp=True,
                threads=4
            )

            print_step(f"Successfully created: {output_path}")
            return output_path

        except Exception as e:
            print_step(f"Error in video merging: {str(e)}")
            return None
        
        finally:
            # Clean up all clips
            for clip in processed_clips:
                try:
                    clip.close()
                except:
                    pass

    def find_video_pairs(self, subreddit_folder):
        """Find matching pairs of videos (_out.mp4 and .mp4) in a subreddit folder"""
        try:
            folder_path = os.path.join(self.results_folder, subreddit_folder)
            print_step(f"Scanning folder: {folder_path}")

            if not os.path.exists(folder_path):
                print_substep(f"Folder not found: {folder_path}")
                return {}, 0

            video_pairs = {}
            total_duration = 0

            # First find all subtitled versions (_out.mp4) and get their durations
            for file in os.listdir(folder_path):
                if file.endswith('_out.mp4'):  # Only look for subtitled versions
                    base_name = file.replace('_out.mp4', '')

                    if base_name not in video_pairs:
                        video_pairs[base_name] = {
                            'normal': None,
                            'subtitled': None,
                            'duration': 0
                        }

                    full_path = os.path.join(folder_path, file)
                    video_pairs[base_name]['subtitled'] = full_path
                    duration = self.get_video_duration(full_path)
                    video_pairs[base_name]['duration'] = duration
                    total_duration += duration

            # Now find matching normal versions but don't add their duration
            for file in os.listdir(folder_path):
                if file.endswith('.mp4') and not file.endswith('_out.mp4'):
                    base_name = file.replace('.mp4', '')
                    if base_name in video_pairs:
                        video_pairs[base_name]['normal'] = os.path.join(folder_path, file)

            # Print found pairs and total duration
            print_step(f"Found video pairs in {subreddit_folder}:")
            complete_pairs = 0
            for base_name, pair in video_pairs.items():
                if pair['normal'] and pair['subtitled']:
                    complete_pairs += 1
                    print_substep(f"Pair {complete_pairs}: {base_name}")
                    print_substep(f" Normal: {os.path.basename(pair['normal'])}")
                    print_substep(f" Subtitled: {os.path.basename(pair['subtitled'])}")
                    print_substep(f" Duration: {pair['duration']:.2f} seconds")

            print_substep(f"Total complete pairs: {complete_pairs}")
            print_substep(f"Total duration: {total_duration:.2f} seconds")

            return video_pairs, total_duration

        except Exception as e:
            print_substep(f"Error scanning folder: {str(e)}")
            return {}, 0

    def find_best_combination(self):
        """Find best combination of videos for target duration"""
        available_pairs = []
        total_duration = 0

        # Print target duration clearly at start
        target_minutes = self.target_duration / 60
        print_step(f"\nTarget Duration: {target_minutes:.1f} minutes ({self.target_duration:.0f} seconds) minimum")

        # First, get all unprocessed video pairs
        for subreddit in os.listdir(self.results_folder):
            if os.path.isdir(os.path.join(self.results_folder, subreddit)):
                pairs, folder_duration = self.find_video_pairs(subreddit)
                for name, pair in pairs.items():
                    if pair['normal'] and pair['subtitled']:
                        # Check if video hasn't been processed yet
                        if name not in self.processed_videos:
                            available_pairs.append({
                                'name': name,
                                'subreddit': subreddit,
                                'duration': pair['duration'],
                                'files': pair
                            })

        if not available_pairs:
            print_step("No unprocessed videos found!")
            return None

        # Sort by duration (longest first for efficient packing)
        available_pairs.sort(key=lambda x: x['duration'], reverse=True)

        selected_pairs = []
        current_duration = 0

        # Select videos until we meet or exceed the target duration
        for pair in available_pairs:
            # Add video if we haven't met minimum duration
            if current_duration < self.target_duration:
                selected_pairs.append(pair)
                current_duration += pair['duration']
                print_substep(f"\nAdded: {pair['name']} ({pair['duration']:.2f}s) from {pair['subreddit']}")
                print_substep(f"Current total duration: {current_duration:.2f}s")
                
                if current_duration >= self.target_duration:
                    print_substep(f"Met minimum target duration of {self.target_duration:.2f}s")
                else:
                    print_substep(f"Still need {(self.target_duration - current_duration):.2f}s to reach minimum target")

        if selected_pairs:
            # Final summary
            print_substep(f"\nFinal compilation stats:")
            print_substep(f"Duration: {current_duration:.2f}s ({current_duration/60:.1f} minutes)")
            print_substep(f"Minimum target: {self.target_duration:.0f}s ({target_minutes:.1f} minutes)")
            
            if current_duration >= self.target_duration:
                print_substep(f"Successfully exceeded minimum target by {(current_duration - self.target_duration):.2f}s")
            else:
                print_substep(f"Warning: Could not reach minimum target duration. Short by {(self.target_duration - current_duration):.2f}s")

        return selected_pairs

    def process_specific_folder(self):
        """Process videos from specific folder"""
        subreddit = self.config['merger']['specific_subreddit']
        print_step(f"Processing specific folder: {subreddit}")

        pairs, duration = self.find_video_pairs(subreddit)
        if not pairs:
            print_step("No videos found in specified folder!")
            return None

        selected_pairs = []
        current_duration = 0

        for name, pair in pairs.items():
            if name not in self.processed_videos and pair['normal'] and pair['subtitled']:
                if current_duration + pair['duration'] <= self.target_duration:
                    selected_pairs.append({
                        'name': name,
                        'subreddit': subreddit,
                        'duration': pair['duration'],
                        'files': pair
                    })
                    current_duration += pair['duration']

        if selected_pairs:
            for pair in selected_pairs:
                self.processed_videos.append(pair['name'])
            self.save_processed_videos()

        return selected_pairs

    def process_videos(self):
        """Process videos based on current mode"""
        if self.check_config_changed():
            if self.config['processing'].get('safe_mode', True):
                print_step("Config changed during operation. Restarting process...")
                return None

        mode = self.config['merger']['mode']
        if mode == "fastest":
            return self.find_best_combination()
        elif mode == "specific":
            return self.process_specific_folder()
        else:
            print_step(f"Unknown mode: {mode}")
            return None

def main():
    compiler = VideoCompiler()
    print_step("Monitoring for config changes...")
    print_substep("You can modify config.toml at any time to change settings")
    print_substep("Set status to 'paused' to pause, 'running' to resume, or 'stopping' to stop")

    first_run = True

    while True:
        try:
            # Check status first
            if not compiler.check_status():
                print_step("Stopping process...")
                break

            # Check if auto-processing is still enabled
            compiler.check_config_changed()
            if not compiler.config['merger']['auto_process']:
                print_step("Auto-processing disabled in config. Exiting.")
                break

            selected_videos = compiler.process_videos()

            if selected_videos:
                print_step("\nVideos Selected for Compilation:")
                total_duration = 0
                for video in selected_videos:
                    print_substep(f"Video: {video['name']}")
                    print_substep(f"From: {video['subreddit']}")
                    print_substep(f"Duration: {video['duration']:.2f} seconds")
                    print_substep("-" * 50)
                    total_duration += video['duration']

                # Move the remaining time message here
                if total_duration < compiler.target_duration:
                    print_step(f"Still need {(compiler.target_duration - total_duration):.2f}s to reach target")
                
                print_step(f"Total Compilation Duration: {total_duration:.2f} seconds")

                # Process the selected videos
                output_path = compiler.merge_videos(selected_videos)
                if output_path:
                    print_step(f"Successfully created compilation: {output_path}")
                    # Save processed videos only after successful compilation
                    for video in selected_videos:
                        compiler.processed_videos.append(video['name'])
                    compiler.save_processed_videos()

                # Only wait if not first run
                if not first_run:
                    check_interval = compiler.config['merger']['check_interval']
                    if not compiler.wait_with_status_check(check_interval):
                        break
                else:
                    first_run = False
            else:
                print_step("Not enough unprocessed videos for compilation.")
                check_interval = compiler.config['merger']['check_interval']
                if not compiler.wait_with_status_check(check_interval):
                    break

        except Exception as e:
            print_step(f"Error occurred: {str(e)}")
            print_step("Waiting 5 minutes before retrying...")
            if not compiler.wait_with_status_check(5):
                break

if __name__ == "__main__":
    main(), heres the files: def splice_video(self, video_pair):
    """Create spliced version with clean intro and main content"""
    clean_video = None
    subtitled_video = None
    intro_clip = None
    main_clip = None
    
    try:
        print_substep(f"Processing video: {video_pair['name']}")

        # Load videos and verify they're valid
        clean_video = VideoFileClip(video_pair['files']['normal'])
        if clean_video is None or clean_video.duration == 0:
            print_substep("Invalid clean video")
            return None

        subtitled_video = VideoFileClip(video_pair['files']['subtitled'])
        if subtitled_video is None or subtitled_video.duration == 0:
            print_substep("Invalid subtitled video")
            return None

        # Use the shorter video duration to be safe
        max_duration = min(clean_video.duration, subtitled_video.duration)

        # Default to 5 seconds for title if detection fails
        title_end = 5.0
        try:
            title_end = self.detect_title_end(clean_video)
        except:
            print_substep("Using default title duration")

        # Ensure title_end is within valid range
        title_end = min(title_end, max_duration - 1)  # Leave at least 1 second for main content

        print_substep(f"Creating clip with title duration: {title_end:.2f}s")

        # Create and verify intro clip
        intro_clip = clean_video.subclip(0, title_end)
        if intro_clip is None or intro_clip.duration == 0:
            print_substep("Failed to create valid intro clip")
            return None

        # Create and verify main clip
        main_clip = subtitled_video.subclip(title_end, max_duration)
        if main_clip is None or main_clip.duration == 0:
            print_substep("Failed to create valid main clip")
            return None

        print_substep(f"Intro duration: {intro_clip.duration:.2f}s")
        print_substep(f"Main duration: {main_clip.duration:.2f}s")

        # Combine the clips
        final_clip = concatenate_videoclips([intro_clip, main_clip], method="compose")
        if final_clip is None or final_clip.duration == 0:
            print_substep("Failed to create valid final clip")
            return None

        print_substep(f"Successfully created clip of duration: {final_clip.duration:.2f}s")
        return final_clip

    except Exception as e:
        print_substep(f"Error in video splicing: {str(e)}")
        return None
    
    finally:
        # Clean up all clips
        for clip in [clean_video, subtitled_video, intro_clip, main_clip]:
            if clip is not None:
                try:
                    clip.close()
                except:
                    pass, def merge_videos(self, selected_videos):
    """Merge selected videos with proper splicing"""
    processed_clips = []
    
    try:
        # Process each video
        for video in selected_videos:
            print_step(f"Processing: {video['name']}")
            processed_clip = self.splice_video(video)
            
            # Verify clip is valid before adding
            if processed_clip is not None and processed_clip.duration > 0:
                print_substep(f"Verified clip duration: {processed_clip.duration:.2f}s")
                processed_clips.append(processed_clip)
            else:
                print_substep(f"Failed to process {video['name']}, skipping...")

        if not processed_clips:
            print_step("No videos were successfully processed")
            return None

        # Verify all clips again before merging
        valid_clips = []
        for clip in processed_clips:
            try:
                if clip is not None and clip.duration > 0:
                    # Try to access a frame to verify clip is valid
                    _ = clip.get_frame(0)
                    valid_clips.append(clip)
                else:
                    print_substep("Found invalid clip, skipping...")
            except:
                print_substep("Found corrupted clip, skipping...")
                continue

        if not valid_clips:
            print_step("No valid clips to merge")
            return None

        # Merge the verified clips
        print_step(f"Merging {len(valid_clips)} verified clips...")
        final_clip = concatenate_videoclips(valid_clips, method="compose")

        # Generate output path
        output_path = os.path.join(
            self.merge_folder,
            f"compilation_{time.strftime('%Y%m%d_%H%M%S')}.mp4"
        )

        # Write the final video
        print_step("Writing final video...")
        final_clip.write_videofile(
            output_path,
            codec='libx264',
            audio_codec='aac',
            temp_audiofile='temp-audio.m4a',
            remove_temp=True,
            threads=4
        )

        print_step(f"Successfully created: {output_path}")
        return output_path

    except Exception as e:
        print_step(f"Error in video merging: {str(e)}")
        return None
    
    finally:
        # Clean up all clips
        for clip in processed_clips:
            try:
                clip.close()
            except:
                pass