USER
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
import random
from termcolor import colored
temporary_audio_files = []
# Define video file extensions and the output folder
video_extensions = ['.mp4', '.mkv', '.wmv', '.avi']
output_folder = 'Output'
def print_menu():
global include_subfolders, starting_offset_seconds, ending_offset_seconds
global segment_duration, num_moments, sorting_preference, peak_position
print("\nMenu des options :")
print(f"1. Traiter les sous-dossiers ou non (actuel: {colored(str(include_subfolders), 'yellow')})")
print(f"2. Effectuer un retrait temporel (début: {colored(str(starting_offset_seconds), 'yellow')}s, fin: {colored(str(ending_offset_seconds), 'yellow')}s)")
print(f"3. Changer la durée des segments (actuel: {colored(str(segment_duration), 'yellow')}s)")
print(f"4. Changer le nombre de segments à extraire (actuel: {colored(str(num_moments), 'yellow')})")
print(f"5. Changer l'ordre de tri (actuel: {colored(str(sorting_preference_to_string(sorting_preference)), 'yellow')})")
print(f"6. Changer l'emplacement du pic sonore (actuel: {colored(peak_position_to_string(peak_position), 'yellow')})")
print(f"7. Relancer l'extraction")
print(f"8. Quitter")
def ask_for_number_of_moments(max_segments):
while True:
num_input = input(f"Veuillez entrer le nombre de moments forts à extraire (maximum {max_segments}): ")
try:
num = int(num_input)
if num > 0 and num <= max_segments:
return num
else:
print(f"Le nombre doit être supérieur à 0 et inférieur ou égal à {max_segments}. Veuillez réessayer.")
except ValueError:
print("Entrée non valide, veuillez réessayer avec un nombre entier.")
def ask_yes_no_question(question):
answer = None
while answer not in ('1', '2'):
print(question)
print("1- Oui")
print("2- Non")
answer = input("Veuillez entrer le numéro de votre choix (1 ou 2) : ").strip()
if answer not in ('1', '2'):
print("Entrée non valide, veuillez réessayer.")
return answer == '1'
def ask_offset_type():
print("Souhaitez-vous un décalage temporel relatif ou absolu ?")
print("1- Relatif (pourcentage)")
print("2- Absolu (secondes)")
while True:
choice = input("Veuillez entrer le numéro de votre choix (1 ou 2) : ").strip()
if choice in ('1', '2'):
return choice
else:
print("Entrée non valide, veuillez réessayer.")
def get_offset_value(video_duration, offset_type):
if offset_type == '1': # Relative offset
while True:
percent = input("Entrez le pourcentage du temps vidéo à ignorer : ")
try:
percent_value = float(percent)
return percent_value * video_duration / 100
except ValueError:
print("Veuillez entrer un nombre valide.")
else: # Absolute offset
while True:
seconds = input("Entrez le nombre de secondes à ignorer : ")
try:
return float(seconds)
except ValueError:
print("Veuillez entrer un nombre valide.")
def ask_for_segment_duration(allowable_duration, video_duration, starting_offset_seconds, ending_offset_seconds):
while True:
duration = input("Quelle est la durée (en secondes) de chaque segment vidéo à extraire ? ")
try:
segment_duration = float(duration)
if segment_duration > 0 and segment_duration <= allowable_duration:
# Calculez le nombre maximal de segments pour une vidéo
available_duration = video_duration - (starting_offset_seconds + ending_offset_seconds)
max_segments = int(available_duration // segment_duration)
return segment_duration, max_segments
else:
print(f"La durée doit être un nombre positif et inférieure ou égale à {allowable_duration} secondes.")
except ValueError:
print("Veuillez entrer un nombre valide.")
def ask_directory_preference():
print("Souhaitez-vous inclure les sous-dossiers dans la recherche des vidéos ?")
print("1- Oui")
print("2- Non")
choice = input("Veuillez entrer le numéro de votre choix (1 ou 2) : ")
return choice.strip() == '1' # Retourne True si l'utilisateur choisit '1' (Oui), False sinon
def calculate_loudness(audio_data):
if audio_data.ndim == 1:
volume = audio_data.astype('float32') ** 2
else:
volume = np.mean(audio_data.astype('float32') ** 2, axis=1)
volume_dB = 10 * np.log10(volume + 1e-9) # +1e-9 to avoid log(0) and convert to dB
return volume_dB
def find_loudest_moments(audio_filename, num_moments, segment_duration, video_duration, starting_offset, ending_offset):
rate, audio_data = wavfile.read(audio_filename)
# Convert stereo to mono if necessary
if audio_data.ndim == 2:
audio_data = np.mean(audio_data, axis=1)
volume_dB = calculate_loudness(audio_data)
segment_half_duration = segment_duration / 2.0
start_index = int(starting_offset * rate)
end_index = int((video_duration - ending_offset) * rate)
moments = []
volumes = []
while len(moments) < num_moments and (end_index - start_index) > 0:
index = np.argmax(volume_dB[start_index:end_index])
print(f"Current index: {index}, start_index: {start_index}, end_index: {end_index}") # Ajouté pour le débogage
moment = (start_index + index) / rate
print(f"Added moment at {moment} seconds") # Ajouté pour le débogage
moment_volume = volume_dB[start_index + index]
if moment - segment_half_duration < starting_offset or moment + segment_half_duration > video_duration - ending_offset:
volume_dB[start_index + index] = -np.inf
continue
moments.append(moment)
print(f"Added moment at {moment} seconds") # Ajouté pour le débogage
volumes.append(moment_volume)
# Clear the volume around the found moment to prevent picking up nearby moments
# Increment the global index by adding the start_index
global_index = start_index + index
# Clear the volume around the found moment to prevent picking up nearby moments
clear_range_start = max(0, global_index - int(rate * segment_half_duration))
clear_range_end = min(len(volume_dB), global_index + int(rate * segment_half_duration))
# Debug print statements to check if the index and the volume are cleared
print(f"Clearing from {clear_range_start} to {clear_range_end}")
print(f"Volume before clearing at index {global_index}: {volume_dB[global_index]}")
volume_dB[clear_range_start:clear_range_end] = -np.inf
print(f"Volume after clearing at index {global_index}: {volume_dB[global_index]}")
return moments, volumes
def extract_segments(video_path, moments, segment_duration, video_duration, peak_position):
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):
if peak_position == '1': # 1/4
start_time = max(moment - segment_duration * 0.25, 0)
elif peak_position == '2': # 1/2
start_time = max(moment - segment_duration * 0.5, 0)
elif peak_position == '3': # 3/4
start_time = max(moment - segment_duration * 0.75, 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
]
try:
subprocess.run(command, check=True, stderr=subprocess.PIPE)
print(f"Extracted and re-encoded {output_filename}")
except subprocess.CalledProcessError as e:
# Catch the exception for this process, print an error message,
# but don't break from the for loop.
err_msg = e.stderr.decode('utf-8') if e.stderr else 'Unknown error'
print(f"Failed to extract segment from {video_path}: {err_msg}")
def store_segment_info(video_path, moment, volume, order):
base_name = os.path.splitext(os.path.basename(video_path))[0]
output_filename = f"{base_name}_moment{order}.mp4"
output_path = os.path.join(output_folder, output_filename)
extracted_segments.append({
'path': output_path,
'timestamp': moment,
'volume': volume
})
def ask_sorting_preference():
print("Comment souhaitez-vous trier les vidéos extraites ?")
print("1- Par ordre de lecture de la vidéo")
print("2- Par ordre inverse de lecture de la vidéo")
print("3- Par volume croissant")
print("4- Par volume décroissant")
print("5- Aléatoire")
choice = int(input("Veuillez entrer le numéro de votre choix : "))
return choice
def sort_moments(moments, volumes, choice):
if choice == 1: # Par ordre de lecture de la vidéo
zipped = sorted(zip(moments, volumes), key=lambda x: x[0])
elif choice == 2: # Par ordre inverse de lecture de la vidéo
zipped = sorted(zip(moments, volumes), key=lambda x: x[0], reverse=True)
elif choice == 3: # Par volume croissant
zipped = sorted(zip(moments, volumes), key=lambda x: x[1])
elif choice == 4: # Par volume décroissant
zipped = sorted(zip(moments, volumes), key=lambda x: x[1], reverse=True)
elif choice == 5: # Pas de tri, sélection aléatoire
zipped = list(zip(moments, volumes))
random.shuffle(zipped)
else:
zipped = zip(moments, volumes)
# Unzip the list of tuples to two lists
sorted_moments, sorted_volumes = zip(*zipped) if zipped else ([], [])
return list(sorted_moments), list(sorted_volumes)
def get_video_durations(include_subfolders):
video_durations = {}
for root, dirs, files in os.walk('.', topdown=True):
# Si l'utilisateur ne souhaite pas inclure les sous-dossiers, nous modifions dirs sur place
if not include_subfolders:
dirs[:] = [] # Cela empêchera os.walk de descendre dans tous les sous-dossiers
# La suite du traitement des fichiers reste inchangée
for file in files:
if file.lower().endswith(tuple(video_extensions)):
video_path = os.path.join(root, file)
try:
# Essayez d'ouvrir et d'extraire les métadonnées du fichier vidéo.
video_clip = VideoFileClip(video_path)
video_duration = video_clip.duration
video_durations[video_path] = video_duration
except Exception as e:
# Si une erreur survient, affichez un message et ignorez le fichier vidéo.
print(f"Error processing video {video_path}: {e}")
finally:
# Assurez-vous de fermer le clip pour libérer les ressources.
video_clip.close()
return video_durations
def confirm_segment_number_or_ask_again(video_duration, starting_offset_seconds, ending_offset_seconds):
allowable_duration = video_duration - (starting_offset_seconds + ending_offset_seconds)
while True:
segment_duration, max_segments = ask_for_segment_duration(allowable_duration, video_duration, starting_offset_seconds, ending_offset_seconds)
print(f"Avec cette durée, vous pouvez extraire jusqu'à {max_segments} segments.")
confirmation = ask_yes_no_question("Voulez-vous continuer avec ce nombre de segments ?")
if confirmation:
return segment_duration, max_segments
def ask_peak_position():
print("Où doit être situé le pic sonore dans la vidéo extraite ?")
print("1- A 1/4 du temps de lecture de la vidéo")
print("2- A 1/2 du temps de lecture de la vidéo")
print("3- A 3/4 du temps de lecture de la vidéo")
while True:
choice = input("Veuillez entrer le numéro de votre choix (1, 2, ou 3) : ").strip()
if choice in ('1', '2', '3'):
return choice
else:
print("Entrée non valide, veuillez réessayer.")
def main():
# Définissez les variables au niveau de la fonction main comme variables extensibles,
# afin qu'elles puissent être modifiées à partir des sous-fonctions.
global include_subfolders, starting_offset_seconds, ending_offset_seconds
global segment_duration, max_segments, num_moments, sorting_preference, peak_position
# Initialisez vos variables avec des valeurs par défaut ou des appels de fonction.
include_subfolders = ask_directory_preference()
video_durations = get_video_durations(include_subfolders)
if not video_durations:
print("Aucune vidéo trouvée pour l'analyse.")
exit()
starting_offset_seconds = 0
ending_offset_seconds = 0
perform_temporal_offset = ask_yes_no_question("Souhaitez-vous effectuer un retrait temporel (début et fin) pour l'analyse des vidéos ?")
if perform_temporal_offset:
offset_type = ask_offset_type()
print("Configuration du retrait temporel pour le début de la vidéo:")
starting_offset_seconds = get_offset_value(min(video_durations.values()), offset_type)
print("Configuration du retrait temporel pour la fin de la vidéo:")
ending_offset_seconds = get_offset_value(min(video_durations.values()), offset_type)
# segment_duration, max_segments = confirm_segment_number_or_ask_again(min(video_durations.values()), starting_offset_seconds, ending_offset_seconds)
# num_moments = ask_for_number_of_moments(max_segments)
# sorting_preference = ask_sorting_preference()
# peak_position = ask_peak_position()
while True:
print_menu()
choice = input("Veuillez entrer le numéro de votre choix : ").strip()
if choice == '1':
include_subfolders = ask_directory_preference()
video_durations = get_video_durations(include_subfolders)
elif choice == '2':
# Récupérez les nouvelles valeurs.
offset_type = ask_offset_type()
# Utilisez une valeur minimale mise à jour si nécessaire.
min_duration = min(video_durations.values())
print("Configuration du retrait temporel pour le début de la vidéo:")
starting_offset_seconds = get_offset_value(min_duration, offset_type)
print("Configuration du retrait temporel pour la fin de la vidéo:")
ending_offset_seconds = get_offset_value(min_duration, offset_type)
elif choice == '3':
segment_duration, max_segments = confirm_segment_number_or_ask_again(min(video_durations.values()), starting_offset_seconds, ending_offset_seconds)
elif choice == '4':
num_moments = ask_for_number_of_moments(max_segments)
elif choice == '5':
sorting_preference = ask_sorting_preference()
elif choice == '6':
peak_position = ask_peak_position()
elif choice == '7':
# Exécutez vos sous-fonctions pour effectuer l'extraction des segments.
# Vous souhaiterez probablement encapsuler cela dans une autre fonction.
extract_segments(video_path, selected_moments, segment_duration, duration, peak_position)
elif choice == '8':
print("Fin du programme.")
break
else:
print("Choix non valide. Veuillez réessayer.")
print(f"Le traitement de toutes les vidéos est terminé. {processed_videos} vidéos ont été traitées.")
if __name__ == "__main__":
main()
Je veux que le script affiche directement le menu.ASSISTANT
Pour que le script affiche directement le menu principal dès son lancement et pour que les utilisateurs puissent modifier les paramètres ou commencer l'extraction sans devoir tout paramétrer à nouveau, vous devrez :
1. Initialiser les variables avec des valeurs par défaut.
2. Afficher le menu immédiatement dans la boucle principale.
3. Réaliser les actions en fonction du choix de l'utilisateur.
4. S'assurer que le menu redemande les entrées si l'utilisateur souhaite modifier le paramétrage.
5. Faire en sorte que l'entrée '7' déclenche un processus qui utilise tous les paramètres actuellement définis.
Voici comment vous pouvez modifier le code pour satisfaire ces conditions :
```python
# ...autres fonctions...
def perform_extraction(video_durations):
global starting_offset_seconds, ending_offset_seconds
global segment_duration, num_moments, sorting_preference, peak_position, processed_videos
processed_videos = 0
for video_path, duration in video_durations.items():
available_duration = duration - (starting_offset_seconds + ending_offset_seconds)
# Votre logique d'extraction...
# ...
print(f"Le traitement de toutes les vidéos est terminé. {processed_videos} vidéos ont été traitées.")
def main():
global include_subfolders, starting_offset_seconds, ending_offset_seconds
global segment_duration, num_moments, sorting_preference, peak_position, processed_videos
# Initialisation des variables avec des valeurs par défaut
include_subfolders = True
starting_offset_seconds = 0
ending_offset_seconds = 0
segment_duration = 5.0 # Valeur par défaut, par exemple 5 secondes
max_segments = 10 # Valeur par défaut, par exemple 10
num_moments = 5 # Valeur par défaut, par exemple 5 moments
sorting_preference = 1 # Valeur par défaut, par exemple tri par ordre de lecture de la vidéo
peak_position = '2' # Valeur par défaut, par exemple à 1/2 du temps de lecture de la vidéo
processed_videos = 0
# Chargement des durées des vidéos au démarrage du script
video_durations = get_video_durations(include_subfolders)
if not video_durations:
print("Aucune vidéo trouvée pour l'analyse.")
exit()
while True:
print_menu()
choice = input("Veuillez entrer le numéro de votre choix : ").strip()
if choice == '1':
# Votre logique de menu...
# ...
elif choice == '7':
perform_extraction(video_durations)
# ...autres choix...
# Affichage du menu à nouveau à la fin de la boucle while
# ...autres fonctions...
if __name__ == "__main__":
main()
```
N'oubliez pas d'ajouter la logique de votre extraction au sein de la fonction `perform_extraction` en reprenant le code responsable de ce processus depuis votre script original.
Le script complet avec toutes les fonctions reste inchangé, excepté pour la fonction `main` qui vient maintenant initialiser quelques valeurs par défaut avant de rentrer dans la boucle qui affiche le menu. Vous pouvez ajuster les valeurs par défaut selon ce qui convient le mieux à votre usage typique.
Chaque fois que l'utilisateur choisit une option autre que '7', cela doit affecter ou mettre à jour les variables globales (si cela est nécessaire). Lorsque l'utilisateur choisit l'option '7', le script exécute la fonction `perform_extraction` avec les paramètres actuels. Après le traitement, ou si l'utilisateur choisit une autre option, le script affiche à nouveau le menu, permettant ainsi à l'utilisateur de faire d'autres choix ou de quitter le programme.