This commit is contained in:
gpatruno
2025-09-06 17:26:38 +02:00
parent baefddd6b3
commit 68cf59ae75
29 changed files with 1387 additions and 1476 deletions
+276 -7
View File
@@ -25,6 +25,10 @@ class BotController:
self.bots = {} # Stockage des instances de bots (pour l'utilisation interne)
self.flux_list = [] # Liste des flux surveillés (pour l'API JSON)
self.config = self.load_config()
self.ia_generator = None
self.control_twitch = None
self.ia_generator_running = False
self.control_twitch_running = False
def load_config(self):
try:
@@ -37,6 +41,21 @@ class BotController:
with open('config/config.json', 'w') as file:
json.dump(self.config, file, indent=4, ensure_ascii=False)
def get_system_status(self):
"""Obtenir le statut de tous les composants"""
return {
'ia_generator': {
'running': self.ia_generator_running,
'status': 'En cours' if self.ia_generator_running else 'Arrêté'
},
'control_twitch': {
'running': self.control_twitch_running,
'status': 'En cours' if self.control_twitch_running else 'Arrêté'
},
'flux_count': len(self.flux_list),
'active_flux': len([f for f in self.flux_list if f['active']])
}
def add_flux(self, channel_name, record_audio=True):
flux_id = len(self.flux_list) + 1
@@ -140,6 +159,112 @@ class BotController:
# Retourner seulement les données JSON (pas les instances de bots)
return self.flux_list
def start_ia_generator(self):
"""Démarrer le générateur IA de manière contrôlée"""
if self.ia_generator_running:
return False, "IA Generator déjà en cours d'exécution"
try:
self.ia_generator = IA_generator("config/config.json")
self.ia_generator_running = True
# Activer l'envoi de messages quand l'IA Generator est démarré
global chat_messages_enabled
chat_messages_enabled = True
# Démarrer dans un thread séparé
threading.Thread(target=self._ia_generator_loop, daemon=True).start()
print(f"[{datetime.now().strftime('%H:%M:%S')}] IA Generator démarré")
return True, "IA Generator démarré avec succès"
except Exception as e:
self.ia_generator_running = False
return False, f"Erreur lors du démarrage de l'IA Generator: {str(e)}"
def stop_ia_generator(self):
"""Arrêter le générateur IA"""
if not self.ia_generator_running:
return False, "IA Generator n'est pas en cours d'exécution"
try:
self.ia_generator_running = False
if self.ia_generator:
self.ia_generator.stop()
# Désactiver l'envoi de messages quand l'IA Generator est arrêté
global chat_messages_enabled
chat_messages_enabled = False
print(f"[{datetime.now().strftime('%H:%M:%S')}] IA Generator arrêté")
return True, "IA Generator arrêté avec succès"
except Exception as e:
return False, f"Erreur lors de l'arrêt de l'IA Generator: {str(e)}"
def _ia_generator_loop(self):
"""Boucle contrôlée pour l'IA Generator"""
while self.ia_generator_running:
try:
if self.ia_generator:
self.ia_generator.main_ask("") # Génération automatique
time.sleep(20) # Attendre 20 secondes entre les générations
except Exception as e:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur dans IA Generator: {e}")
time.sleep(10)
def start_control_twitch(self):
"""Démarrer le contrôleur Twitch de manière contrôlée"""
if self.control_twitch_running:
return False, "Control Twitch déjà en cours d'exécution"
try:
# Utiliser le premier utilisateur par défaut
self.control_twitch = messageTwitch("config/user.json", "default")
self.control_twitch_running = True
# Démarrer dans un thread séparé
threading.Thread(target=self._control_twitch_loop, daemon=True).start()
print(f"[{datetime.now().strftime('%H:%M:%S')}] Control Twitch démarré")
return True, "Control Twitch démarré avec succès"
except Exception as e:
self.control_twitch_running = False
return False, f"Erreur lors du démarrage de Control Twitch: {str(e)}"
def stop_control_twitch(self):
"""Arrêter le contrôleur Twitch"""
if not self.control_twitch_running:
return False, "Control Twitch n'est pas en cours d'exécution"
try:
self.control_twitch_running = False
if self.control_twitch:
self.control_twitch.stop()
print(f"[{datetime.now().strftime('%H:%M:%S')}] Control Twitch arrêté")
return True, "Control Twitch arrêté avec succès"
except Exception as e:
return False, f"Erreur lors de l'arrêt de Control Twitch: {str(e)}"
def _control_twitch_loop(self):
"""Boucle contrôlée pour Control Twitch"""
while self.control_twitch_running:
try:
if self.control_twitch:
# Vérifier s'il y a des générations à envoyer
generation_data = storage.read("IA_generator")
if generation_data:
sorted_keys = sorted(generation_data.keys())
if sorted_keys:
last_generation = generation_data[sorted_keys[-1]]
# Envoyer le message avec le premier utilisateur
self.control_twitch.send_message_user(0, last_generation)
# Supprimer la génération envoyée
storage.delete("IA_generator", sorted_keys[-1])
print(f"[{datetime.now().strftime('%H:%M:%S')}] Message envoyé: {last_generation[:50]}...")
time.sleep(10) # Attendre 10 secondes entre les vérifications
except Exception as e:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur dans Control Twitch: {e}")
time.sleep(10)
bot_controller = BotController()
@app.route('/')
@@ -372,6 +497,33 @@ def get_subtitles():
data = storage.read("subtitle_data")
return jsonify(data)
@app.route('/api/subtitles/clear', methods=['POST'])
def clear_subtitles():
"""Nettoyer l'historique des sous-titres"""
try:
# Récupérer toutes les clés de sous-titres
subtitle_data = storage.read("subtitle_data")
# Supprimer chaque clé une par une
for key in list(subtitle_data.keys()):
storage.delete("subtitle_data", key)
# Supprimer également le fichier JSON s'il existe
storage_dir = "storage"
subtitle_file = os.path.join(storage_dir, "subtitle_data.json")
if os.path.exists(subtitle_file):
os.remove(subtitle_file)
return jsonify({
'success': True,
'message': 'Historique des sous-titres nettoyé avec succès'
})
except Exception as e:
return jsonify({
'success': False,
'error': f'Erreur lors du nettoyage: {str(e)}'
}), 500
@app.route('/api/subtitles/process', methods=['POST'])
def process_subtitles():
"""Lancer manuellement le traitement des sous-titres"""
@@ -643,21 +795,32 @@ auto_message_running = False
current_message_bot = None
# Variable globale pour contrôler l'envoi de messages dans le chat
chat_messages_enabled = True
chat_messages_enabled = False
@app.route('/api/subtitles/auto/start', methods=['POST'])
def start_auto_subtitle():
"""Démarrer la génération automatique de sous-titres"""
global auto_subtitle_running
try:
# Vérifier si déjà en cours
if auto_subtitle_running:
return jsonify({
'success': False,
'error': 'La génération automatique est déjà en cours'
}), 400
auto_subtitle_running = True
print(f"[{datetime.now().strftime('%H:%M:%S')}] Démarrage de la génération automatique de sous-titres")
# Démarrer le thread de génération automatique
threading.Thread(target=auto_subtitle_loop, daemon=True).start()
return jsonify({
'success': True,
'message': 'Génération automatique démarrée'
})
except Exception as e:
auto_subtitle_running = False
return jsonify({
'success': False,
'error': f'Erreur lors du démarrage: {str(e)}'
@@ -668,7 +831,15 @@ def stop_auto_subtitle():
"""Arrêter la génération automatique de sous-titres"""
global auto_subtitle_running
try:
if not auto_subtitle_running:
return jsonify({
'success': False,
'error': 'La génération automatique n\'est pas en cours'
}), 400
print(f"[{datetime.now().strftime('%H:%M:%S')}] Arrêt de la génération automatique de sous-titres")
auto_subtitle_running = False
return jsonify({
'success': True,
'message': 'Génération automatique arrêtée'
@@ -679,6 +850,25 @@ def stop_auto_subtitle():
'error': f'Erreur lors de l\'arrêt: {str(e)}'
}), 500
@app.route('/api/subtitles/auto/force-stop', methods=['POST'])
def force_stop_auto_subtitle():
"""Forcer l'arrêt de la génération automatique de sous-titres"""
global auto_subtitle_running, current_processing_file
try:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Arrêt forcé de la génération automatique de sous-titres")
auto_subtitle_running = False
current_processing_file = None
return jsonify({
'success': True,
'message': 'Arrêt forcé de la génération automatique de sous-titres'
})
except Exception as e:
return jsonify({
'success': False,
'error': f'Erreur lors de l\'arrêt forcé: {str(e)}'
}), 500
@app.route('/api/subtitles/auto/status', methods=['GET'])
def get_auto_subtitle_status():
"""Obtenir le statut de la génération automatique"""
@@ -692,6 +882,8 @@ def auto_subtitle_loop():
"""Boucle de génération automatique de sous-titres"""
global auto_subtitle_running, current_processing_file
print(f"[{datetime.now().strftime('%H:%M:%S')}] Démarrage de la boucle de génération automatique de sous-titres")
while auto_subtitle_running:
try:
# Vérifier s'il y a des fichiers audio à traiter
@@ -709,6 +901,8 @@ def auto_subtitle_loop():
audio_file = audio_files[0]
current_processing_file = audio_file
print(f"[{datetime.now().strftime('%H:%M:%S')}] Traitement de: {audio_file}")
# Émettre l'événement de début de traitement
socketio.emit('subtitle_processing_start', {
'file': audio_file,
@@ -719,7 +913,7 @@ def auto_subtitle_loop():
audio_path = os.path.join(record_dir, audio_file)
try:
# Lancer Whisper
# Lancer Whisper avec vérification périodique de l'arrêt
command = [
'whisper',
'--language', 'fr',
@@ -728,9 +922,30 @@ def auto_subtitle_loop():
'--model', 'large-v3'
]
result = subprocess.run(command, capture_output=True, text=True, timeout=60)
# Utiliser Popen pour pouvoir interrompre le processus
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode == 0:
# Attendre la fin du processus avec vérification périodique
while process.poll() is None:
if not auto_subtitle_running:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Arrêt demandé, interruption du processus Whisper")
process.terminate()
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
process.kill()
break
time.sleep(1)
# Si la boucle a été arrêtée, sortir
if not auto_subtitle_running:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Arrêt de la boucle de génération automatique")
break
result = process.returncode
stdout, stderr = process.communicate()
if result == 0:
# Lire le fichier .txt généré
txt_file = audio_file.replace('.mp3', '.txt')
if os.path.exists(txt_file):
@@ -777,6 +992,8 @@ def auto_subtitle_loop():
'timestamp': current_time
})
print(f"[{datetime.now().strftime('%H:%M:%S')}] Sous-titre généré: {cleaned_content}")
# Nettoyer les fichiers temporaires
os.remove(txt_file)
os.remove(audio_path)
@@ -787,38 +1004,49 @@ def auto_subtitle_loop():
'file': audio_file,
'error': 'Aucun fichier .txt généré'
})
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur: Aucun fichier .txt généré")
else:
# Émettre l'événement d'erreur
socketio.emit('subtitle_processing_error', {
'file': audio_file,
'error': f'Erreur Whisper: {result.stderr}'
'error': f'Erreur Whisper: {stderr}'
})
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur Whisper: {stderr}")
except subprocess.TimeoutExpired:
socketio.emit('subtitle_processing_error', {
'file': audio_file,
'error': 'Timeout - Whisper a pris trop de temps'
})
print(f"[{datetime.now().strftime('%H:%M:%S')}] Timeout - Whisper a pris trop de temps")
except Exception as e:
socketio.emit('subtitle_processing_error', {
'file': audio_file,
'error': f'Erreur: {str(e)}'
})
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur lors du traitement: {str(e)}")
current_processing_file = None
# Vérifier à nouveau si la boucle doit continuer
if not auto_subtitle_running:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Arrêt de la boucle de génération automatique")
break
# Attendre avant de traiter le prochain fichier
time.sleep(2)
except Exception as e:
print(f"Erreur dans la boucle de génération automatique: {e}")
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur dans la boucle de génération automatique: {e}")
time.sleep(5)
print(f"[{datetime.now().strftime('%H:%M:%S')}] Fin de la boucle de génération automatique de sous-titres")
@app.route('/api/messages/auto/start', methods=['POST'])
def start_auto_message():
"""Démarrer l'envoi automatique de messages"""
global auto_message_running, current_message_bot
global auto_message_running
try:
# Vérifier si déjà en cours
if auto_message_running:
@@ -1013,6 +1241,47 @@ def get_chat_messages_status():
'enabled': chat_messages_enabled
})
@app.route('/api/system-status', methods=['GET'])
def get_system_status():
"""Obtenir le statut de tous les composants"""
return jsonify(bot_controller.get_system_status())
@app.route('/api/ia-generator/start', methods=['POST'])
def start_ia_generator():
"""Démarrer le générateur IA"""
success, message = bot_controller.start_ia_generator()
if success:
# Synchroniser l'état de l'interface web
socketio.emit('ia_generator_status_changed', {
'running': True,
'message': message
})
return jsonify({'success': success, 'message': message})
@app.route('/api/ia-generator/stop', methods=['POST'])
def stop_ia_generator():
"""Arrêter le générateur IA"""
success, message = bot_controller.stop_ia_generator()
if success:
# Synchroniser l'état de l'interface web
socketio.emit('ia_generator_status_changed', {
'running': False,
'message': message
})
return jsonify({'success': success, 'message': message})
@app.route('/api/control-twitch/start', methods=['POST'])
def start_control_twitch():
"""Démarrer le contrôleur Twitch"""
success, message = bot_controller.start_control_twitch()
return jsonify({'success': success, 'message': message})
@app.route('/api/control-twitch/stop', methods=['POST'])
def stop_control_twitch():
"""Arrêter le contrôleur Twitch"""
success, message = bot_controller.stop_control_twitch()
return jsonify({'success': success, 'message': message})
if __name__ == '__main__':
# Démarrer les mises à jour en arrière-plan
update_thread = threading.Thread(target=background_updates, daemon=True)