import os
import subprocess
import numpy as np
import uuid  # Import uuid to generate unique IDs
from moviepy.editor import VideoFileClip
from scipy.io import wavfile

temporary_audio_files = []

# Define video file extensions and the output folder
video_extensions = ['.mp4', '.mkv', '.wmv', '.avi']
output_folder = 'Output'

def calculate_loudness(audio_data):
    if audio_data.ndim == 1:
        volume = audio_data ** 2
    else:
        volume = np.mean(audio_data ** 2, axis=1)
    return np.sqrt(volume)

def find_loudest_moments(audio_filename, num_moments, segment_duration, video_duration, starting_offset, ending_offset):
    rate, audio_data = wavfile.read(audio_filename)
    if audio_data.ndim == 2:
        audio_data = np.mean(audio_data, axis=1)
    volume = calculate_loudness(audio_data.astype('float32'))
    
    segment_half_duration = segment_duration / 2.0
    start_index = int(starting_offset * rate)
    end_index = int((video_duration - ending_offset) * rate)
    moments = []
    
    while len(moments) < num_moments:
        index = np.argmax(volume[start_index:end_index])
        moment = (start_index + index) / rate
        
        # Skip the moment if it is too close to the start or the end of the video
        if moment - segment_half_duration < starting_offset or moment + segment_half_duration > video_duration - ending_offset:
            volume[start_index + index] = 0
            continue
        
        moments.append(moment)
        clear_start = max(start_index, start_index + index - int(rate * segment_half_duration))
        clear_end = min(end_index, start_index + index + int(rate * segment_half_duration))
        volume[clear_start:clear_end] = 0

    return moments

def extract_segments(video_path, moments, segment_duration, video_duration):
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
    base_name = os.path.splitext(os.path.basename(video_path))[0]
    half_segment_duration = segment_duration / 2

    for i, moment in enumerate(moments):
        start_time = max(moment - half_segment_duration, 0)
        end_time = min(start_time + segment_duration, video_duration)

        output_filename = f"{base_name}_moment{i + 1}.mp4"
        output_path = os.path.join(output_folder, output_filename)

        command = [
            "ffmpeg",
            "-y",                     # Overwrite output files without asking
            "-ss", str(start_time),   # Start time
            "-i", video_path,         # Input file
            "-t", str(min(segment_duration, video_duration - start_time)),  # Duration or remaining video
            "-c:v", "libx264",        # Specify video codec for output
            "-preset", "medium",      # Specify the encoding preset (trade-off between encoding speed and quality)
            "-crf", "23",             # Specify the Constant Rate Factor for quality (lower means better quality)
            "-c:a", "aac",            # Specify audio codec for output
            "-strict", "-2",          # Necessary for some versions of ffmpeg to use experimental aac encoder
            "-b:a", "192k",           # Specify the audio bitrate
            output_path               # Output path
        ]

        subprocess.run(command, check=True, stderr=subprocess.PIPE)
        print(f"Extracted and re-encoded {output_filename}")

def process_video_files(starting_offset_seconds, ending_offset_seconds, num_moments, segment_duration):
    for root, _, files in os.walk('.'):
        for file in files:
            if file.lower().endswith(tuple(video_extensions)):
                video_path = os.path.join(root, file)
                unique_id = str(uuid.uuid4())  # Generate a unique ID for this video's audio file
                audio_path = f'temp_audio_{unique_id}.wav'  # Name the temporary file using the unique ID
                temporary_audio_files.append(audio_path)  # Add the temporary file path to the list
                
                try:
                    video_clip = VideoFileClip(video_path)
                    video_duration = video_clip.duration
                    video_clip.audio.write_audiofile(audio_path, codec='pcm_s16le', fps=44100, nbytes=2, buffersize=2000)
                    video_clip.close()  # Ensure to close the video clip
                    
                    moments = find_loudest_moments(audio_path, num_moments, segment_duration, video_duration, starting_offset_seconds, ending_offset_seconds)
                    extract_segments(video_path, moments, segment_duration, video_duration)  # Now pass video_duration
                
                finally:
                    if os.path.exists(audio_path):
                        os.remove(audio_path)
                    
                print(f"Finished processing video {video_path}")

if __name__ == "__main__":
    starting_offset_seconds = float(input("Combien de secondes à ignorer au début pour l'analyse ? "))
    ending_offset_seconds = float(input("Combien de secondes à ignorer à la fin pour l'analyse ? "))
    num_moments = int(input("Combien de moments forts souhaiteriez-vous extraire de chaque vidéo ? "))
    segment_duration = float(input("Quelle est la durée (en secondes) de chaque segment vidéo à extraire ? "))
    
    process_video_files(starting_offset_seconds, ending_offset_seconds, num_moments, segment_duration)

    # Après le traitement de tous les fichiers vidéo, nettoyez les fichiers WAV temporaires
    for audio_file in temporary_audio_files:
        if os.path.exists(audio_file):
            os.remove(audio_file)

    print("All temporary audio files have been cleaned up.")
    print("All videos have been processed.")