update
This commit is contained in:
@@ -0,0 +1,191 @@
|
|||||||
|
# 🤖 TwitchBot Controller - Architecture Centralisée
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
|
||||||
|
Le TwitchBot Controller a été refactorisé pour utiliser une architecture centralisée qui permet un contrôle complet depuis l'interface web. Cette nouvelle architecture résout les problèmes de synchronisation et de duplication observés dans les logs.
|
||||||
|
|
||||||
|
## 🏗️ Architecture
|
||||||
|
|
||||||
|
### Composants principaux
|
||||||
|
|
||||||
|
1. **Interface Web** (`web_interface.py`)
|
||||||
|
- Contrôle central de tous les composants
|
||||||
|
- API REST pour la gestion des flux et composants
|
||||||
|
- Interface utilisateur moderne avec Bootstrap
|
||||||
|
|
||||||
|
2. **BotController** (classe dans `web_interface.py`)
|
||||||
|
- Gestion centralisée de tous les bots
|
||||||
|
- Contrôle des états et synchronisation
|
||||||
|
- Gestion des erreurs et récupération
|
||||||
|
|
||||||
|
3. **Composants modulaires**
|
||||||
|
- **IA_generator** : Génération automatique de réponses
|
||||||
|
- **Control_Twitch** : Envoi de messages sur Twitch
|
||||||
|
- **RecordTwitch** : Enregistrement audio des streams
|
||||||
|
- **Subtitle_translation** : Traitement des sous-titres
|
||||||
|
- **TwitchChatBot** : Connexion aux chats Twitch
|
||||||
|
|
||||||
|
## 🔧 Fonctionnalités
|
||||||
|
|
||||||
|
### Contrôle depuis l'interface web
|
||||||
|
|
||||||
|
#### Gestion des flux
|
||||||
|
- ✅ Ajouter/supprimer des flux Twitch
|
||||||
|
- ✅ Activer/désactiver les flux
|
||||||
|
- ✅ Surveillance en temps réel
|
||||||
|
- ✅ Gestion des erreurs
|
||||||
|
|
||||||
|
#### Contrôle des composants
|
||||||
|
- ✅ **IA Generator** : Démarrage/arrêt depuis l'interface
|
||||||
|
- ✅ **Control Twitch** : Contrôle de l'envoi de messages
|
||||||
|
- ✅ **Auto Subtitle** : Génération automatique de sous-titres
|
||||||
|
- ✅ **Auto Messages** : Envoi automatique de messages
|
||||||
|
|
||||||
|
#### Gestion des utilisateurs
|
||||||
|
- ✅ Ajouter/modifier/supprimer des utilisateurs Twitch
|
||||||
|
- ✅ Gestion des tokens OAuth
|
||||||
|
- ✅ Sélection d'utilisateur pour l'envoi de messages
|
||||||
|
|
||||||
|
#### Configuration
|
||||||
|
- ✅ Gestion des prompts IA
|
||||||
|
- ✅ Configuration des paramètres
|
||||||
|
- ✅ Sauvegarde automatique
|
||||||
|
|
||||||
|
## 🚀 Démarrage
|
||||||
|
|
||||||
|
### Prérequis
|
||||||
|
```bash
|
||||||
|
pip install flask flask-socketio requests pytmi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
1. Créez les fichiers de configuration :
|
||||||
|
- `config/config.json` : Configuration générale
|
||||||
|
- `config/user.json` : Utilisateurs Twitch
|
||||||
|
|
||||||
|
2. Configurez vos paramètres :
|
||||||
|
- Tokens OAuth Twitch
|
||||||
|
- Noms de canaux
|
||||||
|
- Prompts IA
|
||||||
|
|
||||||
|
### Lancement
|
||||||
|
```bash
|
||||||
|
python start_web_interface.py
|
||||||
|
```
|
||||||
|
|
||||||
|
L'interface sera disponible sur : http://localhost:5000
|
||||||
|
|
||||||
|
## 📊 Interface utilisateur
|
||||||
|
|
||||||
|
### Tableau de bord
|
||||||
|
- **Statut des flux** : Nombre de flux actifs
|
||||||
|
- **Enregistrements** : Flux avec enregistrement audio
|
||||||
|
- **Connexions chat** : Flux connectés aux chats
|
||||||
|
|
||||||
|
### Contrôles principaux
|
||||||
|
- **Générateur IA** : Toggle pour démarrer/arrêter
|
||||||
|
- **Contrôleur Twitch** : Toggle pour l'envoi de messages
|
||||||
|
- **Auto Subtitle** : Génération automatique de sous-titres
|
||||||
|
- **Auto Messages** : Envoi automatique de messages
|
||||||
|
|
||||||
|
### Gestion des flux
|
||||||
|
- Ajouter de nouveaux flux Twitch
|
||||||
|
- Activer/désactiver les flux
|
||||||
|
- Surveillance du statut en temps réel
|
||||||
|
- Gestion des erreurs
|
||||||
|
|
||||||
|
## 🔄 Flux de données
|
||||||
|
|
||||||
|
### 1. Enregistrement audio
|
||||||
|
```
|
||||||
|
RecordTwitch → Fichiers MP3 → Whisper → Sous-titres
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Génération IA
|
||||||
|
```
|
||||||
|
Sous-titres → IA_generator → Réponses IA → Stockage
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Envoi de messages
|
||||||
|
```
|
||||||
|
Stockage → Control_Twitch → Chat Twitch
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ API Endpoints
|
||||||
|
|
||||||
|
### Flux
|
||||||
|
- `GET /api/flux` : Liste des flux
|
||||||
|
- `POST /api/flux` : Ajouter un flux
|
||||||
|
- `DELETE /api/flux/<id>` : Supprimer un flux
|
||||||
|
- `POST /api/flux/<id>/toggle` : Activer/désactiver
|
||||||
|
|
||||||
|
### Composants
|
||||||
|
- `POST /api/ia-generator/start` : Démarrer IA Generator
|
||||||
|
- `POST /api/ia-generator/stop` : Arrêter IA Generator
|
||||||
|
- `POST /api/control-twitch/start` : Démarrer Control Twitch
|
||||||
|
- `POST /api/control-twitch/stop` : Arrêter Control Twitch
|
||||||
|
|
||||||
|
### Statut
|
||||||
|
- `GET /api/system-status` : Statut de tous les composants
|
||||||
|
- `GET /api/status` : Statut général
|
||||||
|
|
||||||
|
## 🔍 Résolution des problèmes
|
||||||
|
|
||||||
|
### Problèmes résolus
|
||||||
|
|
||||||
|
1. **Générations dupliquées** ✅
|
||||||
|
- Contrôle centralisé de l'IA Generator
|
||||||
|
- Vérification des états avant génération
|
||||||
|
|
||||||
|
2. **Messages dupliqués** ✅
|
||||||
|
- Contrôle centralisé de Control Twitch
|
||||||
|
- Suppression automatique des messages envoyés
|
||||||
|
|
||||||
|
3. **Synchronisation** ✅
|
||||||
|
- États centralisés dans BotController
|
||||||
|
- Communication via API REST
|
||||||
|
|
||||||
|
4. **Gestion des erreurs** ✅
|
||||||
|
- Try/catch dans tous les composants
|
||||||
|
- Récupération automatique
|
||||||
|
|
||||||
|
### Logs améliorés
|
||||||
|
|
||||||
|
Les logs sont maintenant plus clairs et informatifs :
|
||||||
|
```
|
||||||
|
[12:04:15] IA Generator démarré
|
||||||
|
[12:04:20] Control Twitch démarré
|
||||||
|
[12:04:25] Message envoyé: Réponse IA...
|
||||||
|
[12:04:30] Génération créée: Nouvelle réponse...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Maintenance
|
||||||
|
|
||||||
|
### Redémarrage propre
|
||||||
|
```bash
|
||||||
|
# Arrêter tous les composants depuis l'interface
|
||||||
|
# Puis redémarrer l'application
|
||||||
|
python start_web_interface.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug
|
||||||
|
- Logs détaillés dans la console
|
||||||
|
- Statut en temps réel dans l'interface
|
||||||
|
- Gestion des erreurs avec messages explicites
|
||||||
|
|
||||||
|
## 📈 Avantages de la nouvelle architecture
|
||||||
|
|
||||||
|
1. **Contrôle centralisé** : Tous les composants sont gérés depuis l'interface web
|
||||||
|
2. **Synchronisation** : Élimination des conflits et duplications
|
||||||
|
3. **Modularité** : Chaque composant peut être démarré/arrêté indépendamment
|
||||||
|
4. **Observabilité** : Statut en temps réel de tous les composants
|
||||||
|
5. **Maintenabilité** : Code plus propre et mieux organisé
|
||||||
|
6. **Extensibilité** : Facile d'ajouter de nouveaux composants
|
||||||
|
|
||||||
|
## 🎯 Utilisation recommandée
|
||||||
|
|
||||||
|
1. **Démarrage** : Lancez l'interface web
|
||||||
|
2. **Configuration** : Ajoutez vos flux et utilisateurs
|
||||||
|
3. **Activation** : Démarrez les composants depuis l'interface
|
||||||
|
4. **Surveillance** : Surveillez les statuts en temps réel
|
||||||
|
5. **Ajustement** : Modifiez les paramètres selon vos besoins
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
# 🔧 Correction du Bouton Slide "Génération Auto Sous-titres"
|
||||||
|
|
||||||
|
## 📋 Problème Identifié
|
||||||
|
|
||||||
|
### ❌ **Problème Initial**
|
||||||
|
- Le bouton slide "Génération Auto Sous-titres" ne pouvait pas arrêter la boucle de traitement
|
||||||
|
- La boucle `auto_subtitle_loop()` utilisait `subprocess.run()` qui bloquait jusqu'à la fin du processus Whisper
|
||||||
|
- Impossible d'interrompre le traitement en cours
|
||||||
|
- Aucune vérification de la variable `auto_subtitle_running` pendant le traitement
|
||||||
|
|
||||||
|
### 🔍 **Cause Racine**
|
||||||
|
```python
|
||||||
|
# Ancien code problématique
|
||||||
|
result = subprocess.run(command, capture_output=True, text=True, timeout=60)
|
||||||
|
```
|
||||||
|
- `subprocess.run()` bloque l'exécution jusqu'à la fin du processus
|
||||||
|
- Aucune possibilité d'interruption pendant le traitement
|
||||||
|
- La boucle ne peut pas vérifier `auto_subtitle_running` pendant l'exécution de Whisper
|
||||||
|
|
||||||
|
## ✅ **Solution Implémentée**
|
||||||
|
|
||||||
|
### 🔧 **Améliorations Principales**
|
||||||
|
|
||||||
|
#### 1. **Utilisation de `subprocess.Popen()`**
|
||||||
|
```python
|
||||||
|
# Nouveau code avec contrôle d'interruption
|
||||||
|
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
|
# Attendre la fin du processus avec vérification périodique
|
||||||
|
while process.poll() is None:
|
||||||
|
if not auto_subtitle_running:
|
||||||
|
print("Arrêt demandé, interruption du processus Whisper")
|
||||||
|
process.terminate()
|
||||||
|
try:
|
||||||
|
process.wait(timeout=5)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
process.kill()
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. **Vérification Périodique de l'Arrêt**
|
||||||
|
- Vérification de `auto_subtitle_running` toutes les secondes
|
||||||
|
- Interruption immédiate du processus Whisper si arrêt demandé
|
||||||
|
- Nettoyage propre des processus en cours
|
||||||
|
|
||||||
|
#### 3. **Logs de Débogage Améliorés**
|
||||||
|
```python
|
||||||
|
print(f"[{datetime.now().strftime('%H:%M:%S')}] Démarrage de la boucle de génération automatique de sous-titres")
|
||||||
|
print(f"[{datetime.now().strftime('%H:%M:%S')}] Traitement de: {audio_file}")
|
||||||
|
print(f"[{datetime.now().strftime('%H:%M:%S')}] Arrêt demandé, interruption du processus Whisper")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. **Fonction d'Arrêt Forcé**
|
||||||
|
```python
|
||||||
|
@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
|
||||||
|
auto_subtitle_running = False
|
||||||
|
current_processing_file = None
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. **Bouton d'Arrêt Forcé dans l'Interface**
|
||||||
|
```html
|
||||||
|
<button class="btn btn-warning btn-sm w-100" onclick="forceStopAutoSubtitle()" title="Forcer l'arrêt en cas de problème">
|
||||||
|
<i class="fas fa-stop-circle me-1"></i>Force Stop
|
||||||
|
</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 **Fonctionnalités Ajoutées**
|
||||||
|
|
||||||
|
### **Contrôle Interruptible**
|
||||||
|
- ✅ Arrêt immédiat même pendant le traitement Whisper
|
||||||
|
- ✅ Interruption propre des processus en cours
|
||||||
|
- ✅ Nettoyage des ressources
|
||||||
|
|
||||||
|
### **Interface Utilisateur Améliorée**
|
||||||
|
- ✅ Bouton "Force Stop" pour les cas d'urgence
|
||||||
|
- ✅ Feedback visuel en temps réel
|
||||||
|
- ✅ Logs de débogage détaillés
|
||||||
|
|
||||||
|
### **Gestion d'Erreurs Robuste**
|
||||||
|
- ✅ Vérification de l'état avant démarrage
|
||||||
|
- ✅ Gestion des timeouts
|
||||||
|
- ✅ Nettoyage automatique des fichiers temporaires
|
||||||
|
|
||||||
|
## 🔍 **Tests et Validation**
|
||||||
|
|
||||||
|
### **Script de Test**
|
||||||
|
```bash
|
||||||
|
python test_subtitle_stop.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Tests Inclus**
|
||||||
|
1. ✅ Vérification du statut initial
|
||||||
|
2. ✅ Démarrage de la génération automatique
|
||||||
|
3. ✅ Vérification du démarrage
|
||||||
|
4. ✅ Arrêt pendant le traitement
|
||||||
|
5. ✅ Vérification de l'arrêt
|
||||||
|
6. ✅ Test de l'arrêt forcé
|
||||||
|
7. ✅ Test du traitement avec interruption
|
||||||
|
|
||||||
|
## 📊 **Améliorations de Performance**
|
||||||
|
|
||||||
|
### **Optimisations**
|
||||||
|
- Interruption immédiate des processus
|
||||||
|
- Vérification périodique de l'état
|
||||||
|
- Nettoyage automatique des ressources
|
||||||
|
- Logs de débogage détaillés
|
||||||
|
|
||||||
|
### **Sécurité**
|
||||||
|
- Gestion des timeouts
|
||||||
|
- Interruption propre des processus
|
||||||
|
- Protection contre les fuites de ressources
|
||||||
|
|
||||||
|
## 🎯 **Utilisation**
|
||||||
|
|
||||||
|
### **Arrêt Normal**
|
||||||
|
1. Cliquer sur le toggle "Génération Auto Sous-titres"
|
||||||
|
2. Attendre la confirmation d'arrêt
|
||||||
|
3. Vérifier le statut affiché
|
||||||
|
|
||||||
|
### **Arrêt Forcé**
|
||||||
|
1. Cliquer sur "Force Stop"
|
||||||
|
2. Confirmer l'action
|
||||||
|
3. Vérifier que le traitement s'arrête immédiatement
|
||||||
|
|
||||||
|
### **Débogage**
|
||||||
|
1. Ouvrir la console du navigateur
|
||||||
|
2. Vérifier les logs de débogage
|
||||||
|
3. Utiliser le script de test
|
||||||
|
|
||||||
|
## 📝 **Code Modifié**
|
||||||
|
|
||||||
|
### **Fonction `auto_subtitle_loop()`**
|
||||||
|
```python
|
||||||
|
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érification des fichiers audio
|
||||||
|
audio_files = [f for f in os.listdir(record_dir) if f.endswith('.mp3')]
|
||||||
|
if not audio_files:
|
||||||
|
time.sleep(5)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Traitement avec contrôle d'interruption
|
||||||
|
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
|
while process.poll() is None:
|
||||||
|
if not auto_subtitle_running:
|
||||||
|
process.terminate()
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Vérification finale de l'arrêt
|
||||||
|
if not auto_subtitle_running:
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erreur dans la boucle: {e}")
|
||||||
|
time.sleep(5)
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Fonction `force_stop_auto_subtitle()`**
|
||||||
|
```python
|
||||||
|
@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
|
||||||
|
auto_subtitle_running = False
|
||||||
|
current_processing_file = None
|
||||||
|
return jsonify({'success': True, 'message': 'Arrêt forcé'})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎉 **Résultat Final**
|
||||||
|
|
||||||
|
✅ **Bouton slide fonctionnel**
|
||||||
|
✅ **Arrêt immédiat même pendant le traitement**
|
||||||
|
✅ **Interruption propre des processus Whisper**
|
||||||
|
✅ **Interface utilisateur améliorée**
|
||||||
|
✅ **Logs de débogage détaillés**
|
||||||
|
✅ **Gestion d'erreurs robuste**
|
||||||
|
✅ **Tests de validation complets**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Le bouton slide "Génération Auto Sous-titres" fonctionne maintenant parfaitement et peut arrêter la boucle même pendant le traitement d'un fichier ! 🚀*
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
# 🔧 Améliorations des Toggles - TwitchBot Controller
|
||||||
|
|
||||||
|
## 📋 Problème Résolu
|
||||||
|
|
||||||
|
### ❌ **Problème Initial**
|
||||||
|
- Les boutons slide étaient désactivés visuellement mais les fonctions restaient actives
|
||||||
|
- Manque de synchronisation entre l'état visuel et l'état réel du serveur
|
||||||
|
- Contrôle insuffisant des fonctions automatiques
|
||||||
|
|
||||||
|
### ✅ **Solution Implémentée**
|
||||||
|
- Synchronisation complète entre l'interface et le serveur
|
||||||
|
- Variables globales pour suivre l'état réel
|
||||||
|
- Contrôle robuste des fonctions automatiques
|
||||||
|
- Gestion d'erreurs améliorée
|
||||||
|
|
||||||
|
## 🚀 **Améliorations Apportées**
|
||||||
|
|
||||||
|
### 1. **Variables Globales de Contrôle**
|
||||||
|
```javascript
|
||||||
|
let autoSubtitleRunning = false;
|
||||||
|
let autoMessageRunning = false;
|
||||||
|
let chatMessagesEnabled = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **Fonctions de Toggle Améliorées**
|
||||||
|
|
||||||
|
#### **Génération Auto Sous-titres**
|
||||||
|
- ✅ Vérification de l'état avant action
|
||||||
|
- ✅ Désactivation du toggle pendant l'opération
|
||||||
|
- ✅ Gestion d'erreurs robuste
|
||||||
|
- ✅ Restauration de l'état en cas d'échec
|
||||||
|
- ✅ Logs de débogage détaillés
|
||||||
|
|
||||||
|
#### **Envoi Auto Messages**
|
||||||
|
- ✅ Synchronisation avec l'état serveur
|
||||||
|
- ✅ Contrôle des conditions d'activation/désactivation
|
||||||
|
- ✅ Gestion des erreurs réseau
|
||||||
|
- ✅ Mise à jour de l'interface en temps réel
|
||||||
|
|
||||||
|
#### **Envoi Messages Chat**
|
||||||
|
- ✅ Contrôle précis de l'état
|
||||||
|
- ✅ Vérification des permissions
|
||||||
|
- ✅ Feedback utilisateur amélioré
|
||||||
|
|
||||||
|
### 3. **Fonctions de Vérification de Statut**
|
||||||
|
|
||||||
|
#### **checkAutoSubtitleStatus()**
|
||||||
|
- Vérifie l'état réel du serveur
|
||||||
|
- Synchronise l'interface
|
||||||
|
- Gère les erreurs de connexion
|
||||||
|
|
||||||
|
#### **checkAutoMessageStatus()**
|
||||||
|
- Contrôle l'état des messages automatiques
|
||||||
|
- Met à jour l'interface
|
||||||
|
- Logs de débogage
|
||||||
|
|
||||||
|
#### **checkChatMessageStatus()**
|
||||||
|
- Vérifie l'état des messages chat
|
||||||
|
- Synchronise le toggle
|
||||||
|
- Gestion des erreurs
|
||||||
|
|
||||||
|
### 4. **Fonctions d'Arrêt Forcé**
|
||||||
|
|
||||||
|
#### **forceStopAutoMessage()**
|
||||||
|
- Arrêt forcé des messages automatiques
|
||||||
|
- Mise à jour de l'interface
|
||||||
|
- Gestion d'erreurs
|
||||||
|
|
||||||
|
#### **forceStopAutoSubtitle()**
|
||||||
|
- Arrêt forcé de la génération de sous-titres
|
||||||
|
- Nettoyage de l'interface
|
||||||
|
- Feedback utilisateur
|
||||||
|
|
||||||
|
### 5. **Écouteurs d'Événements**
|
||||||
|
```javascript
|
||||||
|
// Remplacement des attributs onchange par des écouteurs
|
||||||
|
const autoSubtitleToggle = document.getElementById('autoSubtitleToggle');
|
||||||
|
const autoMessageToggle = document.getElementById('autoMessageToggle');
|
||||||
|
const chatMessageToggle = document.getElementById('chatMessageToggle');
|
||||||
|
|
||||||
|
if (autoSubtitleToggle) {
|
||||||
|
autoSubtitleToggle.addEventListener('change', toggleAutoSubtitle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 **Fonctionnalités Ajoutées**
|
||||||
|
|
||||||
|
### **Boutons d'Arrêt Forcé**
|
||||||
|
- Bouton "Arrêt Forcé" pour les sous-titres automatiques
|
||||||
|
- Bouton "Arrêt Forcé" pour les messages automatiques
|
||||||
|
- Gestion des cas de blocage
|
||||||
|
|
||||||
|
### **Logs de Débogage**
|
||||||
|
- Console logs détaillés
|
||||||
|
- Suivi de l'état des toggles
|
||||||
|
- Messages d'erreur explicites
|
||||||
|
|
||||||
|
### **Gestion d'Erreurs Améliorée**
|
||||||
|
- Restauration automatique de l'état
|
||||||
|
- Messages d'erreur utilisateur
|
||||||
|
- Désactivation temporaire des toggles
|
||||||
|
|
||||||
|
## 🔍 **Tests et Validation**
|
||||||
|
|
||||||
|
### **Script de Test**
|
||||||
|
```bash
|
||||||
|
python test_toggles.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Tests Inclus**
|
||||||
|
1. ✅ Vérification du statut initial
|
||||||
|
2. ✅ Activation des messages automatiques
|
||||||
|
3. ✅ Vérification après activation
|
||||||
|
4. ✅ Désactivation des messages
|
||||||
|
5. ✅ Activation des sous-titres
|
||||||
|
6. ✅ Désactivation des sous-titres
|
||||||
|
7. ✅ Test du toggle chat
|
||||||
|
|
||||||
|
## 📊 **Améliorations de Performance**
|
||||||
|
|
||||||
|
### **Optimisations**
|
||||||
|
- Désactivation temporaire des toggles pendant les opérations
|
||||||
|
- Vérification de l'état avant action
|
||||||
|
- Réduction des appels API inutiles
|
||||||
|
- Gestion des timeouts
|
||||||
|
|
||||||
|
### **Sécurité**
|
||||||
|
- Validation des réponses serveur
|
||||||
|
- Gestion des erreurs réseau
|
||||||
|
- Protection contre les actions multiples
|
||||||
|
|
||||||
|
## 🎨 **Interface Utilisateur**
|
||||||
|
|
||||||
|
### **Améliorations Visuelles**
|
||||||
|
- Feedback visuel immédiat
|
||||||
|
- États clairs (Activé/Arrêté/En cours)
|
||||||
|
- Messages d'erreur explicites
|
||||||
|
- Boutons d'arrêt forcé
|
||||||
|
|
||||||
|
### **Expérience Utilisateur**
|
||||||
|
- Synchronisation en temps réel
|
||||||
|
- Feedback sonore et visuel
|
||||||
|
- Gestion des cas d'erreur
|
||||||
|
- Interface responsive
|
||||||
|
|
||||||
|
## 🔧 **Utilisation**
|
||||||
|
|
||||||
|
### **Activation/Désactivation**
|
||||||
|
1. Cliquer sur le toggle souhaité
|
||||||
|
2. Attendre la confirmation
|
||||||
|
3. Vérifier le statut affiché
|
||||||
|
|
||||||
|
### **Arrêt Forcé**
|
||||||
|
1. Cliquer sur "Arrêt Forcé"
|
||||||
|
2. Confirmer l'action
|
||||||
|
3. Vérifier le statut
|
||||||
|
|
||||||
|
### **Débogage**
|
||||||
|
1. Ouvrir la console du navigateur
|
||||||
|
2. Vérifier les logs
|
||||||
|
3. Utiliser le script de test
|
||||||
|
|
||||||
|
## 📝 **Notes Techniques**
|
||||||
|
|
||||||
|
### **Variables Globales**
|
||||||
|
- `autoSubtitleRunning`: État de la génération de sous-titres
|
||||||
|
- `autoMessageRunning`: État de l'envoi de messages
|
||||||
|
- `chatMessagesEnabled`: État des messages chat
|
||||||
|
|
||||||
|
### **Endpoints API**
|
||||||
|
- `GET /api/messages/auto/status`: Statut messages
|
||||||
|
- `POST /api/messages/auto/start`: Démarrer messages
|
||||||
|
- `POST /api/messages/auto/stop`: Arrêter messages
|
||||||
|
- `GET /api/subtitles/auto/status`: Statut sous-titres
|
||||||
|
- `POST /api/subtitles/auto/start`: Démarrer sous-titres
|
||||||
|
- `POST /api/subtitles/auto/stop`: Arrêter sous-titres
|
||||||
|
- `GET /api/chat/messages/status`: Statut chat
|
||||||
|
- `POST /api/chat/messages/enable`: Activer chat
|
||||||
|
- `POST /api/chat/messages/disable`: Désactiver chat
|
||||||
|
|
||||||
|
## 🎉 **Résultat Final**
|
||||||
|
|
||||||
|
✅ **Toggles parfaitement synchronisés**
|
||||||
|
✅ **Contrôle précis des fonctions**
|
||||||
|
✅ **Gestion d'erreurs robuste**
|
||||||
|
✅ **Interface utilisateur améliorée**
|
||||||
|
✅ **Débogage facilité**
|
||||||
|
✅ **Performance optimisée**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Les toggles sont maintenant parfaitement contrôlés et synchronisés avec l'état réel du serveur ! 🚀*
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de démonstration des systèmes automatiques
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def demo_auto_systems():
|
|
||||||
"""Démonstration des systèmes automatiques"""
|
|
||||||
print("🎬 Démonstration des systèmes automatiques")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# 1. Vérifier les fichiers audio
|
|
||||||
record_dir = "record"
|
|
||||||
if os.path.exists(record_dir):
|
|
||||||
audio_files = [f for f in os.listdir(record_dir) if f.endswith('.mp3')]
|
|
||||||
print(f"✅ {len(audio_files)} fichiers audio disponibles pour les sous-titres")
|
|
||||||
else:
|
|
||||||
print("❌ Dossier record non trouvé")
|
|
||||||
|
|
||||||
# 2. Vérifier les générations
|
|
||||||
storage_dir = "storage"
|
|
||||||
generation_file = os.path.join(storage_dir, "IA_generator.json")
|
|
||||||
if os.path.exists(generation_file):
|
|
||||||
try:
|
|
||||||
with open(generation_file, 'r', encoding='utf-8') as f:
|
|
||||||
generation_data = json.load(f)
|
|
||||||
print(f"✅ {len(generation_data)} générations disponibles pour l'envoi automatique")
|
|
||||||
|
|
||||||
if generation_data:
|
|
||||||
sorted_keys = sorted(generation_data.keys())
|
|
||||||
last_generation = generation_data[sorted_keys[-1]]
|
|
||||||
print(f"📝 Dernière génération: {last_generation}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ Erreur lecture générations: {e}")
|
|
||||||
else:
|
|
||||||
print("📝 Aucune génération disponible")
|
|
||||||
|
|
||||||
# 3. Vérifier les utilisateurs
|
|
||||||
user_config = "config/user.json"
|
|
||||||
if os.path.exists(user_config):
|
|
||||||
try:
|
|
||||||
with open(user_config, 'r') as f:
|
|
||||||
users = json.load(f)
|
|
||||||
print(f"✅ {len(users)} utilisateur(s) configuré(s) pour l'envoi")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ Erreur lecture utilisateurs: {e}")
|
|
||||||
else:
|
|
||||||
print("❌ Fichier de configuration utilisateur non trouvé")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def show_interface_instructions():
|
|
||||||
"""Afficher les instructions pour l'interface"""
|
|
||||||
print("\n🌐 Instructions pour l'interface web:")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
print("\n📺 Génération automatique de sous-titres:")
|
|
||||||
print(" 1. Ouvrir http://localhost:5000")
|
|
||||||
print(" 2. Dans le tableau de bord (sidebar gauche)")
|
|
||||||
print(" 3. Trouver le switch 'Génération Auto Sous-titres'")
|
|
||||||
print(" 4. Activer le switch pour démarrer")
|
|
||||||
print(" 5. Aller dans l'onglet 'Sous-titres' pour voir le fichier en cours")
|
|
||||||
print(" 6. Les sous-titres seront créés automatiquement fichier par fichier")
|
|
||||||
|
|
||||||
print("\n💬 Envoi automatique de messages:")
|
|
||||||
print(" 1. Dans le tableau de bord (sidebar gauche)")
|
|
||||||
print(" 2. Trouver le switch 'Envoi Auto Messages'")
|
|
||||||
print(" 3. Activer le switch pour démarrer")
|
|
||||||
print(" 4. Les messages seront envoyés automatiquement quand des générations sont disponibles")
|
|
||||||
print(" 5. Délai de 10 secondes entre chaque envoi")
|
|
||||||
|
|
||||||
print("\n🔄 Fonctionnement combiné:")
|
|
||||||
print(" - Les sous-titres sont générés automatiquement")
|
|
||||||
print(" - Les générations IA sont créées à partir des sous-titres")
|
|
||||||
print(" - Les messages sont envoyés automatiquement")
|
|
||||||
print(" - Tout fonctionne en boucle automatique")
|
|
||||||
|
|
||||||
def show_features():
|
|
||||||
"""Afficher les fonctionnalités ajoutées"""
|
|
||||||
print("\n📋 Fonctionnalités ajoutées:")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
print("\n🎛️ Contrôles automatiques:")
|
|
||||||
print(" ✅ Switch 'Génération Auto Sous-titres'")
|
|
||||||
print(" ✅ Switch 'Envoi Auto Messages'")
|
|
||||||
print(" ✅ Indicateurs de statut en temps réel")
|
|
||||||
print(" ✅ Gestion des erreurs avec alertes")
|
|
||||||
|
|
||||||
print("\n🔄 Traitement automatique:")
|
|
||||||
print(" ✅ Surveillance des fichiers audio")
|
|
||||||
print(" ✅ Transcription avec Whisper")
|
|
||||||
print(" ✅ Nettoyage et formatage des sous-titres")
|
|
||||||
print(" ✅ Sauvegarde automatique")
|
|
||||||
print(" ✅ Surveillance des générations IA")
|
|
||||||
print(" ✅ Envoi automatique de messages")
|
|
||||||
print(" ✅ Suppression des données traitées")
|
|
||||||
|
|
||||||
print("\n📡 Communication temps réel:")
|
|
||||||
print(" ✅ Événements Socket.IO pour le traitement")
|
|
||||||
print(" ✅ Événements Socket.IO pour l'envoi")
|
|
||||||
print(" ✅ Alertes en temps réel")
|
|
||||||
print(" ✅ Actualisation automatique de l'interface")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("🚀 Démonstration des systèmes automatiques")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# Test des systèmes
|
|
||||||
success = demo_auto_systems()
|
|
||||||
|
|
||||||
# Instructions d'interface
|
|
||||||
show_interface_instructions()
|
|
||||||
|
|
||||||
# Fonctionnalités
|
|
||||||
show_features()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
if success:
|
|
||||||
print("✅ Systèmes prêts !")
|
|
||||||
print("🎉 Vous pouvez maintenant utiliser les switches dans l'interface web")
|
|
||||||
print("💡 Les deux systèmes fonctionnent indépendamment ou ensemble")
|
|
||||||
else:
|
|
||||||
print("❌ Problèmes détectés")
|
|
||||||
print("🔧 Vérifiez la configuration avant d'utiliser les systèmes automatiques")
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de démonstration du système de sous-titres
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def demo_subtitle_system():
|
|
||||||
"""Démonstration du système de sous-titres"""
|
|
||||||
print("🎬 Démonstration du système de sous-titres")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# 1. Vérifier les fichiers audio
|
|
||||||
record_dir = "record"
|
|
||||||
if not os.path.exists(record_dir):
|
|
||||||
print(f"❌ Dossier record non trouvé: {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
audio_files = [f for f in os.listdir(record_dir) if f.endswith('.mp3')]
|
|
||||||
if not audio_files:
|
|
||||||
print(f"❌ Aucun fichier audio trouvé dans {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f"✅ {len(audio_files)} fichiers audio trouvés")
|
|
||||||
print(f"📁 Premier fichier: {audio_files[0]}")
|
|
||||||
print(f"📁 Dernier fichier: {audio_files[-1]}")
|
|
||||||
|
|
||||||
# 2. Vérifier le stockage
|
|
||||||
storage_dir = "storage"
|
|
||||||
subtitle_file = os.path.join(storage_dir, "subtitle_data.json")
|
|
||||||
|
|
||||||
if os.path.exists(subtitle_file):
|
|
||||||
try:
|
|
||||||
with open(subtitle_file, 'r', encoding='utf-8') as f:
|
|
||||||
subtitle_data = json.load(f)
|
|
||||||
print(f"✅ Stockage existant avec {len(subtitle_data)} sous-titre(s)")
|
|
||||||
|
|
||||||
if subtitle_data:
|
|
||||||
print("📝 Derniers sous-titres:")
|
|
||||||
sorted_keys = sorted(subtitle_data.keys())
|
|
||||||
for key in sorted_keys[-3:]: # Afficher les 3 derniers
|
|
||||||
print(f" {key}: {subtitle_data[key]}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ Erreur lecture stockage: {e}")
|
|
||||||
else:
|
|
||||||
print("📝 Aucun stockage existant")
|
|
||||||
|
|
||||||
# 3. Simuler le traitement manuel
|
|
||||||
print("\n🔧 Simulation du traitement manuel:")
|
|
||||||
print(" 1. Détection des fichiers audio ✅")
|
|
||||||
print(" 2. Lancement de Whisper sur chaque fichier")
|
|
||||||
print(" 3. Nettoyage et suppression des répétitions")
|
|
||||||
print(" 4. Sauvegarde dans le stockage")
|
|
||||||
print(" 5. Nettoyage des fichiers temporaires")
|
|
||||||
|
|
||||||
# 4. Créer un exemple de sous-titre
|
|
||||||
example_subtitle = {
|
|
||||||
"03:52:30": "Voici un exemple de sous-titre généré",
|
|
||||||
"03:52:35": "Le système fonctionne correctement",
|
|
||||||
"03:52:40": "Whisper traite les fichiers audio"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Sauvegarder l'exemple
|
|
||||||
if not os.path.exists(storage_dir):
|
|
||||||
os.makedirs(storage_dir)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(subtitle_file, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(example_subtitle, f, indent=4, ensure_ascii=False)
|
|
||||||
print("✅ Exemple de sous-titres sauvegardé")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur sauvegarde: {e}")
|
|
||||||
|
|
||||||
# 5. Afficher les instructions pour l'interface web
|
|
||||||
print("\n🌐 Instructions pour l'interface web:")
|
|
||||||
print(" 1. Ouvrir http://localhost:5000")
|
|
||||||
print(" 2. Aller dans l'onglet 'Sous-titres'")
|
|
||||||
print(" 3. Cliquer sur 'Traitement Manuel'")
|
|
||||||
print(" 4. Attendre le traitement des fichiers audio")
|
|
||||||
print(" 5. Voir les sous-titres générés")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def test_whisper_availability():
|
|
||||||
"""Tester la disponibilité de Whisper"""
|
|
||||||
print("\n🔍 Test de Whisper:")
|
|
||||||
|
|
||||||
try:
|
|
||||||
import subprocess
|
|
||||||
result = subprocess.run(['whisper', '--help'], capture_output=True, text=True, timeout=10)
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✅ Whisper est disponible et fonctionnel")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ Whisper ne répond pas correctement")
|
|
||||||
return False
|
|
||||||
except FileNotFoundError:
|
|
||||||
print("❌ Whisper n'est pas installé")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur test Whisper: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("🚀 Démonstration du système de sous-titres")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# Test 1: Système de base
|
|
||||||
system_ok = demo_subtitle_system()
|
|
||||||
|
|
||||||
# Test 2: Whisper
|
|
||||||
whisper_ok = test_whisper_availability()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
if system_ok and whisper_ok:
|
|
||||||
print("✅ Système prêt !")
|
|
||||||
print("🎉 Vous pouvez maintenant utiliser le bouton 'Traitement Manuel'")
|
|
||||||
print(" dans l'interface web pour créer des sous-titres.")
|
|
||||||
else:
|
|
||||||
print("❌ Problèmes détectés")
|
|
||||||
if not system_ok:
|
|
||||||
print(" - Problème avec le système de base")
|
|
||||||
if not whisper_ok:
|
|
||||||
print(" - Problème avec Whisper")
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de démonstration pour tester l'interface web du TwitchBot Controller
|
|
||||||
Génère des données de test pour montrer les fonctionnalités
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
from datetime import datetime
|
|
||||||
import random
|
|
||||||
|
|
||||||
def create_demo_data():
|
|
||||||
"""Créer des données de démonstration"""
|
|
||||||
|
|
||||||
# Créer les dossiers nécessaires
|
|
||||||
os.makedirs('working_bot/storage', exist_ok=True)
|
|
||||||
os.makedirs('config', exist_ok=True)
|
|
||||||
|
|
||||||
# Données de démonstration pour les sous-titres
|
|
||||||
demo_subtitles = {
|
|
||||||
"14h30m15s": "Salut les viewers ! Comment ça va aujourd'hui ?",
|
|
||||||
"14h31m22s": "On va jouer à ce nouveau jeu, j'ai hâte de voir ce que ça donne",
|
|
||||||
"14h32m45s": "Oh non, je suis mort déjà ! C'est plus dur que je pensais",
|
|
||||||
"14h33m12s": "Merci pour le follow @nouveau_viewer !",
|
|
||||||
"14h34m30s": "Cette partie est vraiment intense, regardez ça !",
|
|
||||||
"14h35m18s": "Je pense qu'on devrait essayer une autre stratégie",
|
|
||||||
"14h36m05s": "Excellent, on progresse enfin dans ce niveau",
|
|
||||||
"14h37m22s": "N'hésitez pas à poser vos questions dans le chat"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Données de démonstration pour les générations IA
|
|
||||||
demo_generations = {
|
|
||||||
"14h30m20s": "Salut ! Ça va super bien merci ! 😊",
|
|
||||||
"14h31m28s": "Ce jeu a l'air génial, bonne chance !",
|
|
||||||
"14h32m50s": "Ça arrive aux meilleurs, tu vas y arriver !",
|
|
||||||
"14h33m15s": "Bienvenue dans la communauté ! 🎉",
|
|
||||||
"14h34m35s": "Waouh cette action était épique ! 🔥",
|
|
||||||
"14h35m25s": "Bonne idée, change de tactique !",
|
|
||||||
"14h36m10s": "Bravo ! Tu maîtrises de mieux en mieux",
|
|
||||||
"14h37m28s": "Toujours là pour aider ! 💪"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Sauvegarder les données de démonstration
|
|
||||||
with open('working_bot/storage/subtitle_data.json', 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(demo_subtitles, f, indent=4, ensure_ascii=False)
|
|
||||||
|
|
||||||
with open('working_bot/storage/IA_generator.json', 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(demo_generations, f, indent=4, ensure_ascii=False)
|
|
||||||
|
|
||||||
print("✅ Données de démonstration créées")
|
|
||||||
|
|
||||||
def create_demo_config():
|
|
||||||
"""Créer une configuration de démonstration"""
|
|
||||||
|
|
||||||
demo_config = {
|
|
||||||
"twitchname": "demo_streamer",
|
|
||||||
"recordtime": 60,
|
|
||||||
"threads": 1,
|
|
||||||
"language": "fr",
|
|
||||||
"max_flux_lifetime": 3600,
|
|
||||||
"active_flux": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "demo_streamer",
|
|
||||||
"twitchname": "demo_streamer",
|
|
||||||
"quantity": 1,
|
|
||||||
"lifetime": 3600,
|
|
||||||
"created_at": datetime.now().isoformat(),
|
|
||||||
"active": True
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"list_prompt": [
|
|
||||||
"Réponds en 8 mots max avec humour : ",
|
|
||||||
"Réagis comme un viewer twitch en 6 mots : ",
|
|
||||||
"Commentaire de live en 7 mots drôle : ",
|
|
||||||
"Réponse sarcastique en 5 mots : ",
|
|
||||||
"Blague rapide en 6 mots : ",
|
|
||||||
"Commentaire décalé en 7 mots : ",
|
|
||||||
"Réponse ironique en 6 mots : ",
|
|
||||||
"Réponse punchy en 7 mots : ",
|
|
||||||
"Réaction wtf en 5 mots : ",
|
|
||||||
"Commentaire troll en 7 mots : "
|
|
||||||
],
|
|
||||||
"bad_answer": [
|
|
||||||
"suis un assistant",
|
|
||||||
"Je ne comprends pas.",
|
|
||||||
"pas un humain",
|
|
||||||
"je suis désolé"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
demo_user = [
|
|
||||||
{
|
|
||||||
"tw_acc_pseudo": "demo_bot_1",
|
|
||||||
"tw_acc_token": "oauth:demo_token_1",
|
|
||||||
"charactere": "😊"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tw_acc_pseudo": "demo_bot_2",
|
|
||||||
"tw_acc_token": "oauth:demo_token_2",
|
|
||||||
"charactere": "🤖"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
# Sauvegarder les configurations
|
|
||||||
with open('config/config.json', 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(demo_config, f, indent=4, ensure_ascii=False)
|
|
||||||
|
|
||||||
with open('config/user.json', 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(demo_user, f, indent=4, ensure_ascii=False)
|
|
||||||
|
|
||||||
print("✅ Configuration de démonstration créée")
|
|
||||||
|
|
||||||
def simulate_live_updates():
|
|
||||||
"""Simuler des mises à jour en temps réel"""
|
|
||||||
|
|
||||||
def update_data():
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
# Simuler un nouveau sous-titre
|
|
||||||
current_time = datetime.now().strftime("%Hh%Mm%Ss")
|
|
||||||
|
|
||||||
demo_messages = [
|
|
||||||
"Cette partie devient vraiment intéressante !",
|
|
||||||
"Merci pour tous vos messages dans le chat !",
|
|
||||||
"Je vais essayer cette nouvelle technique",
|
|
||||||
"Waouh, je ne m'attendais pas à ça !",
|
|
||||||
"Vous avez vu cette action incroyable ?",
|
|
||||||
"On approche du boss final !",
|
|
||||||
"N'oubliez pas de follow si vous aimez le contenu",
|
|
||||||
"Cette musique de fond est parfaite"
|
|
||||||
]
|
|
||||||
|
|
||||||
demo_responses = [
|
|
||||||
"C'est parti pour l'action ! 🎮",
|
|
||||||
"Toujours un plaisir de vous lire ! 💬",
|
|
||||||
"Bonne stratégie, ça va marcher ! 👍",
|
|
||||||
"Plot twist inattendu ! 😮",
|
|
||||||
"Action de malade ! 🔥",
|
|
||||||
"Le boss va trembler ! 💪",
|
|
||||||
"Contenu de qualité garanti ! ⭐",
|
|
||||||
"Parfait pour l'ambiance ! 🎵"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Charger les données existantes
|
|
||||||
try:
|
|
||||||
with open('working_bot/storage/subtitle_data.json', 'r', encoding='utf-8') as f:
|
|
||||||
subtitles = json.load(f)
|
|
||||||
with open('working_bot/storage/IA_generator.json', 'r', encoding='utf-8') as f:
|
|
||||||
generations = json.load(f)
|
|
||||||
except:
|
|
||||||
subtitles = {}
|
|
||||||
generations = {}
|
|
||||||
|
|
||||||
# Ajouter de nouvelles données
|
|
||||||
new_message = random.choice(demo_messages)
|
|
||||||
new_response = random.choice(demo_responses)
|
|
||||||
|
|
||||||
subtitles[current_time] = new_message
|
|
||||||
generations[current_time] = new_response
|
|
||||||
|
|
||||||
# Limiter à 20 entrées maximum
|
|
||||||
if len(subtitles) > 20:
|
|
||||||
oldest_key = min(subtitles.keys())
|
|
||||||
del subtitles[oldest_key]
|
|
||||||
del generations[oldest_key]
|
|
||||||
|
|
||||||
# Sauvegarder
|
|
||||||
with open('working_bot/storage/subtitle_data.json', 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(subtitles, f, indent=4, ensure_ascii=False)
|
|
||||||
|
|
||||||
with open('working_bot/storage/IA_generator.json', 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(generations, f, indent=4, ensure_ascii=False)
|
|
||||||
|
|
||||||
print(f"🔄 Mise à jour simulée: {current_time}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur lors de la simulation: {e}")
|
|
||||||
|
|
||||||
# Attendre entre 30 secondes et 2 minutes
|
|
||||||
time.sleep(random.randint(30, 120))
|
|
||||||
|
|
||||||
# Démarrer la simulation dans un thread
|
|
||||||
update_thread = threading.Thread(target=update_data, daemon=True)
|
|
||||||
update_thread.start()
|
|
||||||
|
|
||||||
print("🎭 Simulation des mises à jour en temps réel démarrée")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
print("🎬 TwitchBot Controller - Mode Démonstration")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Créer les données et configuration de démonstration
|
|
||||||
create_demo_data()
|
|
||||||
create_demo_config()
|
|
||||||
|
|
||||||
# Démarrer la simulation des mises à jour
|
|
||||||
simulate_live_updates()
|
|
||||||
|
|
||||||
print("\n🚀 Démarrage de l'interface web en mode démonstration...")
|
|
||||||
print("📍 Les données de test sont générées automatiquement")
|
|
||||||
print("🔄 De nouvelles données apparaîtront toutes les 30s-2min")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Importer et démarrer l'interface web
|
|
||||||
try:
|
|
||||||
from web_interface import app, socketio
|
|
||||||
|
|
||||||
socketio.run(
|
|
||||||
app,
|
|
||||||
host='0.0.0.0',
|
|
||||||
port=5000,
|
|
||||||
debug=False
|
|
||||||
)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("\n🛑 Arrêt de la démonstration...")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur: {e}")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@@ -808,6 +808,25 @@ class IA_generator:
|
|||||||
# sprint(self.script_name,"green",f"start imagine_response\n")
|
# sprint(self.script_name,"green",f"start imagine_response\n")
|
||||||
debug_print("v", "start imagine_response", self.type_debug,self.script_name)
|
debug_print("v", "start imagine_response", self.type_debug,self.script_name)
|
||||||
|
|
||||||
|
# Vérifier si l'envoi de messages est activé
|
||||||
|
try:
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
||||||
|
|
||||||
|
# Essayer d'importer la variable depuis web_interface
|
||||||
|
try:
|
||||||
|
from web_interface import chat_messages_enabled
|
||||||
|
if not chat_messages_enabled:
|
||||||
|
debug_print("w", "Génération IA désactivée, réponse ignorée", self.type_debug, self.script_name)
|
||||||
|
return
|
||||||
|
except ImportError:
|
||||||
|
# Si web_interface n'est pas disponible, on continue normalement
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
# En cas d'erreur, on continue normalement
|
||||||
|
pass
|
||||||
|
|
||||||
# sprint(self.script_name,"magenta",f"streamer_word : \n"+str(self.streamer_word))
|
# sprint(self.script_name,"magenta",f"streamer_word : \n"+str(self.streamer_word))
|
||||||
# sprint(self.script_name,"magenta",f"streamer_word : \n"+str(len(self.streamer_word[0])))
|
# sprint(self.script_name,"magenta",f"streamer_word : \n"+str(len(self.streamer_word[0])))
|
||||||
if str(self.streamer_word) == "":
|
if str(self.streamer_word) == "":
|
||||||
@@ -1098,6 +1117,26 @@ class messageTwitch:
|
|||||||
sprint(self.script_name,"blue", "main_loop_respond start")
|
sprint(self.script_name,"blue", "main_loop_respond start")
|
||||||
try:
|
try:
|
||||||
while self.message_running:
|
while self.message_running:
|
||||||
|
# Vérifier si l'envoi de messages est activé
|
||||||
|
try:
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
||||||
|
|
||||||
|
# Essayer d'importer la variable depuis web_interface
|
||||||
|
try:
|
||||||
|
from web_interface import chat_messages_enabled
|
||||||
|
if not chat_messages_enabled:
|
||||||
|
debug_print("w", "Envoi de messages désactivé, boucle en attente", self.type_debug, self.script_name)
|
||||||
|
sleep_control(20, self.message_running)
|
||||||
|
continue
|
||||||
|
except ImportError:
|
||||||
|
# Si web_interface n'est pas disponible, on continue normalement
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
# En cas d'erreur, on continue normalement
|
||||||
|
pass
|
||||||
|
|
||||||
self.get_last_generation()
|
self.get_last_generation()
|
||||||
if (self.generation_text == ""):
|
if (self.generation_text == ""):
|
||||||
sprint(self.script_name,"yellow",f"pas encore de génération")
|
sprint(self.script_name,"yellow",f"pas encore de génération")
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+105
-108
@@ -1,140 +1,137 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Script de démarrage pour l'interface web du TwitchBot Controller
|
Script de démarrage amélioré pour TwitchBot Controller
|
||||||
|
Utilise l'architecture centralisée avec contrôle depuis l'interface web
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import argparse
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
# Configurer l'environnement Python pour l'interface web
|
def print_banner():
|
||||||
def setup_environment():
|
"""Afficher la bannière de démarrage"""
|
||||||
"""Configurer l'environnement Python"""
|
print("🤖 TwitchBot Controller - Interface Web")
|
||||||
# Ajouter le chemin de l'environnement virtuel au PYTHONPATH
|
print("=" * 50)
|
||||||
venv_path = os.path.join(os.path.dirname(__file__), 'env', 'lib', 'python3.10', 'site-packages')
|
print("✓ Architecture centralisée")
|
||||||
if venv_path not in sys.path:
|
print("✓ Contrôle depuis l'interface web")
|
||||||
sys.path.insert(0, venv_path)
|
print("✓ Gestion des composants modulaire")
|
||||||
print(f"✓ Environnement Python configuré: {venv_path}")
|
print("=" * 50)
|
||||||
|
|
||||||
# Ajouter le répertoire courant au PYTHONPATH
|
|
||||||
current_dir = os.path.dirname(__file__)
|
|
||||||
if current_dir not in sys.path:
|
|
||||||
sys.path.insert(0, current_dir)
|
|
||||||
|
|
||||||
def check_dependencies():
|
def check_dependencies():
|
||||||
"""Vérifier que les dépendances sont installées"""
|
"""Vérifier les dépendances requises"""
|
||||||
try:
|
print("🔧 Vérification des dépendances...")
|
||||||
import flask
|
|
||||||
import flask_socketio
|
required_packages = [
|
||||||
print("✓ Dépendances Flask installées")
|
'flask',
|
||||||
return True
|
'flask-socketio',
|
||||||
except ImportError as e:
|
'requests',
|
||||||
print(f"✗ Dépendances manquantes: {e}")
|
'pytmi'
|
||||||
print("Installez les dépendances avec: pip install -r requirements_web.txt")
|
]
|
||||||
|
|
||||||
|
missing_packages = []
|
||||||
|
|
||||||
|
for package in required_packages:
|
||||||
|
try:
|
||||||
|
__import__(package.replace('-', '_'))
|
||||||
|
print(f"✓ {package}")
|
||||||
|
except ImportError:
|
||||||
|
missing_packages.append(package)
|
||||||
|
print(f"✗ {package} - MANQUANT")
|
||||||
|
|
||||||
|
if missing_packages:
|
||||||
|
print(f"\n❌ Dépendances manquantes: {', '.join(missing_packages)}")
|
||||||
|
print("💡 Installez-les avec: pip install " + " ".join(missing_packages))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
print("✅ Toutes les dépendances sont installées")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_config_files():
|
||||||
|
"""Vérifier les fichiers de configuration"""
|
||||||
|
print("\n📁 Vérification des fichiers de configuration...")
|
||||||
|
|
||||||
|
required_files = [
|
||||||
|
'config/config.json',
|
||||||
|
'config/user.json'
|
||||||
|
]
|
||||||
|
|
||||||
|
missing_files = []
|
||||||
|
|
||||||
|
for file_path in required_files:
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
print(f"✓ {file_path}")
|
||||||
|
else:
|
||||||
|
missing_files.append(file_path)
|
||||||
|
print(f"✗ {file_path} - MANQUANT")
|
||||||
|
|
||||||
|
if missing_files:
|
||||||
|
print(f"\n⚠️ Fichiers de configuration manquants: {', '.join(missing_files)}")
|
||||||
|
print("💡 Créez ces fichiers avant de continuer")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("✅ Tous les fichiers de configuration sont présents")
|
||||||
|
return True
|
||||||
|
|
||||||
def create_directories():
|
def create_directories():
|
||||||
"""Créer les dossiers nécessaires s'ils n'existent pas"""
|
"""Créer les répertoires nécessaires"""
|
||||||
|
print("\n📂 Création des répertoires...")
|
||||||
|
|
||||||
directories = [
|
directories = [
|
||||||
'working_bot/storage',
|
'storage',
|
||||||
'config',
|
'record',
|
||||||
'templates',
|
'in_record'
|
||||||
'static/css',
|
|
||||||
'static/js'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for directory in directories:
|
for directory in directories:
|
||||||
if not os.path.exists(directory):
|
if not os.path.exists(directory):
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
print(f"✓ Dossier créé: {directory}")
|
print(f"✓ Créé: {directory}")
|
||||||
|
else:
|
||||||
|
print(f"✓ Existe: {directory}")
|
||||||
|
|
||||||
def check_config_files():
|
def start_web_interface():
|
||||||
"""Vérifier que les fichiers de configuration existent"""
|
"""Démarrer l'interface web"""
|
||||||
config_files = {
|
|
||||||
'config/config.json': {
|
|
||||||
"twitchname": "votre_channel",
|
|
||||||
"recordtime": 60,
|
|
||||||
"threads": 1,
|
|
||||||
"language": "fr",
|
|
||||||
"list_prompt": [
|
|
||||||
"Réponds en 8 mots max avec humour : ",
|
|
||||||
"Réagis comme un viewer twitch en 6 mots : ",
|
|
||||||
"Commentaire de live en 7 mots drôle : "
|
|
||||||
],
|
|
||||||
"bad_answer": [
|
|
||||||
"suis un assistant",
|
|
||||||
"Je ne comprends pas.",
|
|
||||||
"pas un humain"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'config/user.json': [
|
|
||||||
{
|
|
||||||
"tw_acc_pseudo": "votre_pseudo",
|
|
||||||
"tw_acc_token": "oauth:votre_token",
|
|
||||||
"charactere": "😊"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
import json
|
|
||||||
for file_path, default_content in config_files.items():
|
|
||||||
if not os.path.exists(file_path):
|
|
||||||
with open(file_path, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(default_content, f, indent=4, ensure_ascii=False)
|
|
||||||
print(f"✓ Fichier de configuration créé: {file_path}")
|
|
||||||
print(f"⚠️ Pensez à modifier {file_path} avec vos propres paramètres")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(description='Démarrer l\'interface web du TwitchBot Controller')
|
|
||||||
parser.add_argument('--host', default='0.0.0.0', help='Adresse IP d\'écoute (défaut: 0.0.0.0)')
|
|
||||||
parser.add_argument('--port', type=int, default=5000, help='Port d\'écoute (défaut: 5000)')
|
|
||||||
parser.add_argument('--debug', action='store_true', help='Mode debug')
|
|
||||||
parser.add_argument('--no-check', action='store_true', help='Ignorer la vérification des dépendances')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
print("🤖 TwitchBot Controller - Interface Web")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Vérifications préliminaires
|
|
||||||
if not args.no_check:
|
|
||||||
if not check_dependencies():
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
create_directories()
|
|
||||||
check_config_files()
|
|
||||||
|
|
||||||
# Changer vers le répertoire de travail si nécessaire
|
|
||||||
if not os.path.exists('working_bot'):
|
|
||||||
os.makedirs('working_bot')
|
|
||||||
|
|
||||||
print("\n🚀 Démarrage de l'interface web...")
|
print("\n🚀 Démarrage de l'interface web...")
|
||||||
print(f"📍 Adresse: http://{args.host}:{args.port}")
|
print("📍 Adresse: http://0.0.0.0:5000")
|
||||||
print("🔧 Utilisez Ctrl+C pour arrêter le serveur")
|
print("🔧 Utilisez Ctrl+C pour arrêter le serveur")
|
||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
|
|
||||||
# Importer et démarrer l'application Flask
|
|
||||||
try:
|
try:
|
||||||
|
# Importer et démarrer l'interface web
|
||||||
from web_interface import app, socketio
|
from web_interface import app, socketio
|
||||||
|
|
||||||
# Configurer l'application
|
|
||||||
app.config['DEBUG'] = args.debug
|
|
||||||
|
|
||||||
# Démarrer le serveur
|
# Démarrer le serveur
|
||||||
socketio.run(
|
socketio.run(app, host='0.0.0.0', port=5000, debug=False)
|
||||||
app,
|
|
||||||
host=args.host,
|
|
||||||
port=args.port,
|
|
||||||
debug=args.debug,
|
|
||||||
use_reloader=False # Éviter les problèmes avec les threads
|
|
||||||
)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\n🛑 Arrêt de l'interface web...")
|
print("\n🛑 Arrêt demandé par l'utilisateur")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Erreur lors du démarrage: {e}")
|
print(f"\n❌ Erreur lors du démarrage: {e}")
|
||||||
sys.exit(1)
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def main():
|
||||||
main()
|
"""Fonction principale"""
|
||||||
|
print_banner()
|
||||||
|
|
||||||
|
# Vérifier les dépendances
|
||||||
|
if not check_dependencies():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Vérifier les fichiers de configuration
|
||||||
|
if not check_config_files():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Créer les répertoires nécessaires
|
||||||
|
create_directories()
|
||||||
|
|
||||||
|
# Démarrer l'interface web
|
||||||
|
return start_web_interface()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
success = main()
|
||||||
|
if not success:
|
||||||
|
sys.exit(1)
|
||||||
@@ -608,7 +608,8 @@ textarea, input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chat-messages {
|
.chat-messages {
|
||||||
height: 400px;
|
min-height: 400px;
|
||||||
|
max-height: 800px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: var(--primary-bg);
|
background-color: var(--primary-bg);
|
||||||
|
|||||||
+251
-10
@@ -38,6 +38,29 @@ function initializeSocketIO() {
|
|||||||
socket.on('new_generation', function(data) {
|
socket.on('new_generation', function(data) {
|
||||||
updateNextMessage(data);
|
updateNextMessage(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('ia_generator_status_changed', function(data) {
|
||||||
|
// Synchroniser l'état de l'interface web
|
||||||
|
const iaGeneratorToggle = document.getElementById('iaGeneratorToggle');
|
||||||
|
const iaGeneratorStatus = document.getElementById('iaGeneratorStatus');
|
||||||
|
const chatMessageToggle = document.getElementById('chatMessageToggle');
|
||||||
|
const chatMessageStatus = document.getElementById('chatMessageStatus');
|
||||||
|
|
||||||
|
if (iaGeneratorToggle && iaGeneratorStatus) {
|
||||||
|
iaGeneratorToggle.checked = data.running;
|
||||||
|
iaGeneratorStatus.textContent = data.running ? 'En cours...' : 'Arrêté';
|
||||||
|
iaGeneratorStatus.className = data.running ? 'text-success' : 'text-muted';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchroniser aussi l'état de l'envoi de messages
|
||||||
|
if (chatMessageToggle && chatMessageStatus) {
|
||||||
|
chatMessageToggle.checked = data.running;
|
||||||
|
chatMessageStatus.textContent = data.running ? 'Activé' : 'Désactivé';
|
||||||
|
chatMessageStatus.className = data.running ? 'text-success' : 'text-muted';
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('IA Generator status changed:', data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mise à jour du statut de connexion
|
// Mise à jour du statut de connexion
|
||||||
@@ -66,7 +89,8 @@ async function loadInitialData() {
|
|||||||
loadGenerations(),
|
loadGenerations(),
|
||||||
checkAutoSubtitleStatus(), // Ajouter la vérification du statut auto
|
checkAutoSubtitleStatus(), // Ajouter la vérification du statut auto
|
||||||
checkAutoMessageStatus(), // Ajouter la vérification du statut auto messages
|
checkAutoMessageStatus(), // Ajouter la vérification du statut auto messages
|
||||||
checkChatMessageStatus() // Ajouter la vérification du statut chat messages
|
checkChatMessageStatus(), // Ajouter la vérification du statut chat messages
|
||||||
|
checkSystemStatus() // Ajouter la vérification du statut système
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur lors du chargement des données:', error);
|
console.error('Erreur lors du chargement des données:', error);
|
||||||
@@ -577,16 +601,16 @@ function processSubtitles() {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
showAlert(data.message, 'success');
|
showToast(data.message, 'success');
|
||||||
// Actualiser les sous-titres
|
// Actualiser les sous-titres
|
||||||
loadSubtitles();
|
loadSubtitles();
|
||||||
} else {
|
} else {
|
||||||
showAlert('Erreur: ' + data.error, 'danger');
|
showToast('Erreur: ' + data.error, 'danger');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Erreur:', error);
|
console.error('Erreur:', error);
|
||||||
showAlert('Erreur lors du traitement des sous-titres', 'danger');
|
showToast('Erreur lors du traitement des sous-titres', 'danger');
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
// Restaurer le bouton
|
// Restaurer le bouton
|
||||||
@@ -595,6 +619,38 @@ function processSubtitles() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function clearSubtitlesHistory() {
|
||||||
|
// Demander confirmation avant de nettoyer
|
||||||
|
if (!confirm('Êtes-vous sûr de vouloir nettoyer l\'historique des sous-titres ? Cette action est irréversible.')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/subtitles/clear', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
showToast(data.message, 'success');
|
||||||
|
// Vider l'affichage de l'historique
|
||||||
|
document.getElementById('subtitles-history').innerHTML = '<div class="text-muted text-center">Aucun sous-titre dans l\'historique</div>';
|
||||||
|
// Mettre à jour le dernier sous-titre
|
||||||
|
document.getElementById('last-subtitle').innerHTML = 'Aucun texte détecté pour le moment';
|
||||||
|
document.getElementById('last-subtitle').className = 'alert alert-warning';
|
||||||
|
} else {
|
||||||
|
showToast(data.error || 'Erreur lors du nettoyage', 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur:', error);
|
||||||
|
showToast('Erreur lors du nettoyage de l\'historique', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === GESTION DES GÉNÉRATIONS ===
|
// === GESTION DES GÉNÉRATIONS ===
|
||||||
|
|
||||||
// Chargement des générations
|
// Chargement des générations
|
||||||
@@ -1532,10 +1588,10 @@ async function forceStopAutoMessage() {
|
|||||||
const statusElement = document.getElementById('autoMessageStatus');
|
const statusElement = document.getElementById('autoMessageStatus');
|
||||||
|
|
||||||
if (toggle && statusElement) {
|
if (toggle && statusElement) {
|
||||||
|
autoMessageRunning = false;
|
||||||
toggle.checked = false;
|
toggle.checked = false;
|
||||||
statusElement.textContent = 'Arrêté';
|
statusElement.textContent = 'Arrêté';
|
||||||
statusElement.className = 'text-muted';
|
statusElement.className = 'text-muted';
|
||||||
autoMessageRunning = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showAlert('Arrêt forcé de l\'envoi automatique de messages', 'warning');
|
showAlert('Arrêt forcé de l\'envoi automatique de messages', 'warning');
|
||||||
@@ -1546,7 +1602,42 @@ async function forceStopAutoMessage() {
|
|||||||
console.error('Erreur lors de l\'arrêt forcé:', error);
|
console.error('Erreur lors de l\'arrêt forcé:', error);
|
||||||
showAlert('Erreur lors de l\'arrêt forcé', 'danger');
|
showAlert('Erreur lors de l\'arrêt forcé', 'danger');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fonction pour forcer l'arrêt de la génération automatique de sous-titres
|
||||||
|
async function forceStopAutoSubtitle() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/subtitles/auto/force-stop', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// Mettre à jour l'interface
|
||||||
|
const toggle = document.getElementById('autoSubtitleToggle');
|
||||||
|
const statusElement = document.getElementById('autoSubtitleStatus');
|
||||||
|
|
||||||
|
if (toggle && statusElement) {
|
||||||
|
autoSubtitleRunning = false;
|
||||||
|
toggle.checked = false;
|
||||||
|
statusElement.textContent = 'Arrêté';
|
||||||
|
statusElement.className = 'text-muted';
|
||||||
|
hideProcessingFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
showAlert('Arrêt forcé de la génération automatique de sous-titres', 'warning');
|
||||||
|
} else {
|
||||||
|
showAlert('Erreur: ' + result.error, 'danger');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur lors de l\'arrêt forcé:', error);
|
||||||
|
showAlert('Erreur lors de l\'arrêt forcé', 'danger');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fonction pour activer/désactiver l'envoi de messages dans le chat
|
// Fonction pour activer/désactiver l'envoi de messages dans le chat
|
||||||
async function toggleChatMessage() {
|
async function toggleChatMessage() {
|
||||||
@@ -1628,13 +1719,163 @@ async function checkChatMessageStatus() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur lors de la vérification du statut chat:', error);
|
console.error('Erreur lors de la vérification du statut chat:', error);
|
||||||
// En cas d'erreur, forcer l'activation par défaut
|
// En cas d'erreur, forcer la désactivation par défaut
|
||||||
const toggle = document.getElementById('chatMessageToggle');
|
const toggle = document.getElementById('chatMessageToggle');
|
||||||
const statusElement = document.getElementById('chatMessageStatus');
|
const statusElement = document.getElementById('chatMessageStatus');
|
||||||
if (toggle && statusElement) {
|
if (toggle && statusElement) {
|
||||||
toggle.checked = true;
|
toggle.checked = false;
|
||||||
statusElement.textContent = 'Activé';
|
statusElement.textContent = 'Désactivé';
|
||||||
statusElement.className = 'text-success';
|
statusElement.className = 'text-muted';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === GESTION DES COMPOSANTS SYSTÈME ===
|
||||||
|
|
||||||
|
// Fonction pour activer/désactiver le générateur IA
|
||||||
|
async function toggleIAGenerator() {
|
||||||
|
const toggle = document.getElementById('iaGeneratorToggle');
|
||||||
|
const statusElement = document.getElementById('iaGeneratorStatus');
|
||||||
|
|
||||||
|
if (toggle.checked) {
|
||||||
|
// Démarrer le générateur IA
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/ia-generator/start', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
statusElement.textContent = 'En cours...';
|
||||||
|
statusElement.className = 'text-success';
|
||||||
|
showToast('Générateur IA démarré', 'success');
|
||||||
|
} else {
|
||||||
|
toggle.checked = false;
|
||||||
|
showToast('Erreur: ' + result.message, 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur:', error);
|
||||||
|
toggle.checked = false;
|
||||||
|
showToast('Erreur lors du démarrage du générateur IA', 'error');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Arrêter le générateur IA
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/ia-generator/stop', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
statusElement.textContent = 'Arrêté';
|
||||||
|
statusElement.className = 'text-muted';
|
||||||
|
showToast('Générateur IA arrêté', 'info');
|
||||||
|
} else {
|
||||||
|
toggle.checked = true;
|
||||||
|
showToast('Erreur: ' + result.message, 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur:', error);
|
||||||
|
toggle.checked = true;
|
||||||
|
showToast('Erreur lors de l\'arrêt du générateur IA', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour activer/désactiver le contrôleur Twitch
|
||||||
|
async function toggleControlTwitch() {
|
||||||
|
const toggle = document.getElementById('controlTwitchToggle');
|
||||||
|
const statusElement = document.getElementById('controlTwitchStatus');
|
||||||
|
|
||||||
|
if (toggle.checked) {
|
||||||
|
// Démarrer le contrôleur Twitch
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/control-twitch/start', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
statusElement.textContent = 'En cours...';
|
||||||
|
statusElement.className = 'text-success';
|
||||||
|
showToast('Contrôleur Twitch démarré', 'success');
|
||||||
|
} else {
|
||||||
|
toggle.checked = false;
|
||||||
|
showToast('Erreur: ' + result.message, 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur:', error);
|
||||||
|
toggle.checked = false;
|
||||||
|
showToast('Erreur lors du démarrage du contrôleur Twitch', 'error');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Arrêter le contrôleur Twitch
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/control-twitch/stop', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
statusElement.textContent = 'Arrêté';
|
||||||
|
statusElement.className = 'text-muted';
|
||||||
|
showToast('Contrôleur Twitch arrêté', 'info');
|
||||||
|
} else {
|
||||||
|
toggle.checked = true;
|
||||||
|
showToast('Erreur: ' + result.message, 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur:', error);
|
||||||
|
toggle.checked = true;
|
||||||
|
showToast('Erreur lors de l\'arrêt du contrôleur Twitch', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour vérifier le statut système au chargement
|
||||||
|
async function checkSystemStatus() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/system-status');
|
||||||
|
const status = await response.json();
|
||||||
|
|
||||||
|
// Mettre à jour le statut du générateur IA
|
||||||
|
const iaGeneratorToggle = document.getElementById('iaGeneratorToggle');
|
||||||
|
const iaGeneratorStatus = document.getElementById('iaGeneratorStatus');
|
||||||
|
|
||||||
|
if (iaGeneratorToggle && iaGeneratorStatus) {
|
||||||
|
iaGeneratorToggle.checked = status.ia_generator.running;
|
||||||
|
iaGeneratorStatus.textContent = status.ia_generator.status;
|
||||||
|
iaGeneratorStatus.className = status.ia_generator.running ? 'text-success' : 'text-muted';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le statut du contrôleur Twitch
|
||||||
|
const controlTwitchToggle = document.getElementById('controlTwitchToggle');
|
||||||
|
const controlTwitchStatus = document.getElementById('controlTwitchStatus');
|
||||||
|
|
||||||
|
if (controlTwitchToggle && controlTwitchStatus) {
|
||||||
|
controlTwitchToggle.checked = status.control_twitch.running;
|
||||||
|
controlTwitchStatus.textContent = status.control_twitch.status;
|
||||||
|
controlTwitchStatus.className = status.control_twitch.running ? 'text-success' : 'text-muted';
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Statut système chargé:', status);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur lors de la vérification du statut système:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
+23
-13
@@ -1,15 +1,25 @@
|
|||||||
{
|
{
|
||||||
"03:52:30": "Voici un exemple de sous-titre généré",
|
"00:33:44": "Là tu ne trouves pas assez de poissons cool avant ? Mais oui il faut monter. Tes sans jeu les montées. Parce que je me suis dit peut-être quon va avoir des trucs rares. Oh ! De toute façon elle a maillé. Cest mine ça. Elle te fait la plage. rare. Ah dommage prend elle. Tu contrôles tes bien. Jai contrôlé mais sûr. Ouais ben non. fait.",
|
||||||
"03:52:35": "Le système fonctionne correctement",
|
"00:34:24": "Hal tes pas contrôle en plus Jai de QN à la putaire Là il y a un coup qui joué Tu essaies te barrer là ? Ouais Cest juste au dessus droite Mais tétais passé par Non non Ah oui ! Dans fente La nez",
|
||||||
"03:52:40": "Whisper traite les fichiers audio",
|
"00:35:06": "Moi jpourrais que cétait... Ah ouais rien à voir ! Et toi tes là ? Non mais je sais pas regarde cest encore fermé non Je jai joué la map connais moi qui lai exploré En vrai il savait depuis le début oui Tu vas devoir utiliser ta lampe Comme en fait savais quil tu passes par trou bien vu tas raison Ouais connait tout Ça doit être panneau menant... quon peut y aller avec ce truc",
|
||||||
"03:58:02": "elles sont marquées dune encore un droite et vas-y je nai même pas vu une tatouée jungle lendroit où est la fenêtre non il",
|
"00:35:28": "Cest long hein ? Tu tournes en rond quoi. Ah oui. Non mais tu Mais je fais comment là Là elle va pas se barrer cest bon non Elle est dans un truc bout. Oui maides pas. Moi croyais que cétait par la sortie. oui ok retournes effectivement. On tourne de fou. Sous-titrage ST 501",
|
||||||
"04:04:51": "Oh il a pas de kit Ah putain ouais bah Jai besoin des tatouages exotiques mais joublie où est la fenêtre Hein ? Ça veut dire quoi",
|
"00:36:08": "Oh le beau poisson ! Vas-y fume-le Comment je fais ? Et tu vas pas tuer tas que 2 balles mais cest grave Tu finis au... harponne-le go Dash Tes dans lui là quest-ce Mais il maspire",
|
||||||
"04:10:30": "Ah zut où est-ce que jai mis ma fenêtre à jouer des tatouages ? Ça ne maide rien. Vraiment cest tatouage hein lui Cest exotique.",
|
"00:36:32": "Cest bon ça ? Ça va tas un coquet juste en dessous. Oh R cest bien elle fait... Non non le fais pas maintenant déjà commencé à manger maintenant. si prend... on a quun seul continue continue. Mais je comprends là. Continue quoi qui sest passé prend plus pareil. Si plus. sur elle. truc expliquez ! te ramène poisson. entier direct la base.",
|
||||||
"04:11:07": "Merci à tous.",
|
"00:37:16": "Lété par Alada sorti oui ok retourne effectivement du son sous-titrage dST500 merde. Ten fous de le tuer. Vas-y va faire péter ça. Voilà. Faut la pioche. Ouais. Ouais laisse tomber. pêcher pêcher. Mais ça là peut-être que demain ya pas Non mais cest soja là. vas-y prends-le fait un truc pour les recettes. Cest mal. Curcuma. On connait. Ah ouais.",
|
||||||
"04:11:44": "comment ça se passe avec son bot on y est là",
|
"00:37:37": "ça prend pas plus juste le poisson direct te ramène hein cest quand tu as les crabes bah comme peux prendre munitions à côté faut que mettes assez haut en fait bien un truc quon peut arrêter long tournes rond quoi ah oui non je sais comment là elle va se barrer bon heeeeeee",
|
||||||
"04:16:54": "Sous-titrage ST 501",
|
"00:38:25": "On reconnaît quand même est des croustos on pas Gingembre Ouais gingembre surtout Vas-y motte oh petite caisse Là ya une pioche Non prends la cest sûr quil y a 100% Oh vas-y le 10 de dégâts mieux que batte Et top",
|
||||||
"04:17:32": "un peu con première la pertinence des réponses en effort supplémentaire de lutilisateur ouais ok mais sortie doù le truc exotique",
|
"00:38:48": "Amélioration de Warpant ! 13 dégâts en plus je suis pas tabassé Vas-y monte On va chasser du... Oh ça Cest cest cest... Au nom Méduse Ça suivons-le Casse les couilles Retourne retourne on le re-suivre. Ils se préparent à avoir des tortues. Des tortues ? Ben oui ce quils disaient.",
|
||||||
"04:18:11": "cest un machin exotique ton bot il parle de tatouage sur Sadu on a pas parlé",
|
"00:39:27": "Si on a de la tortue... On repasse au point... pète les couilles ! Oh 16 dégâts vas-y prends 7 si je peux plus...",
|
||||||
"04:18:47": "Sous-titrage ST 501",
|
"00:39:46": "Oh oh mais non prends le comme ça on a plan En vrai tu peux reprendre attends essaye de tirer une fois avec Non cest pas Ok vas-y re-change remets toi en contre",
|
||||||
"04:33:10": "Un mois vraiment je lui envoyais des messages il répondait pas quoi. Il est mort. Cest moi. qui le patron ? Elle légendaire cette ref. pour ça que cétait quand du coup. Mais pourquoi parle anglais maintenant encore en vie. Ouais suis"
|
"00:40:24": "Oh une piège Je peux aller pêcher le gros caillou si tu en veux Ah habitation ça il faut les prendre trucs comme",
|
||||||
|
"00:40:44": "On na jamais pris ça Si moi je les prends à chaque fois Mais cest pour lémission quon sen bat couilles là Floé dit Ouais ouais vrai lécologie quest-ce fouille Oh la tortue Tu vas tuer toi",
|
||||||
|
"00:41:24": "Mange-la mange-la ! Calmez la tortue calmez ça va mettre des patates en fait Je peux rien faire ? Vas-y cest de lapprocher. Ah faut que tu tattaches à pour ralentir. comment fais Bah capter une minute. Mais il est ouais faites pas chez vous",
|
||||||
|
"17:22:02": "Sous-titres par Jérémy Diaz",
|
||||||
|
"17:22:53": "La carte ciblée se retrouve à 100% de ses HP max Ça cest exceptionnel les gars Genre là tous 7 tours tu reviens full vie Et NaoA qui tape 20 Cest Mais dégâts augmentent base vraiment fort jaime beaucoup cette en vrai va partir Oula Ok alors comment on cook ça On prendre le roi Le problème que un peu bizarre 40 et après entre 1 lumière Donc ombre plus bon",
|
||||||
|
"17:23:41": "Après il faut que je teste la mécanique de fumée donc en vrai vais aller découvrir nouvelle Et ça cest fort non vas-y faire un deck full on comme On va voir ce donne toute façon maintenant le in-game avoir pas mal dimpact Alors vous présenter les nouveautés Directement adversaire bien sûr Regardez regardez stylé par contre jai oublié mettre mon visuel mis à moi Ok est",
|
||||||
|
"17:24:08": "Oh là il y a une légendaire donc la rareté maintenant cest le truc qui est en dessous Donc rare et deux communes Moi jai rares épique trois moi les gars Jai Euh... Salut Croco eu chance davoir drop clé je voulais te faire un retour car javais joué à bêta publique moment de ça Jétais déjà assez content du jeu mais dois dire encore mieux Le système roguelike surprise que nattendais pas voir rend vraiment fun amusant Merci toi ton équipe pour ce souhaite beaucoup succès Norma",
|
||||||
|
"17:24:53": "Non cest un amour on a encore beaucoup de travail mais va tout donner. Alors mon deck ah bah simple jai rien up. Voilà super Croco merci pour ce tour. Allez magnifique très bon premier tour se régale en cas. ladversaire il joue la souris 4 disparitions avant ça faisait disparaître les attaques maintenant fait passifs. Donc gros là certainement faire ma fumée qui que je naurai plus fumée. Mais ne peut attaques. éternité augmente dun vous voyez mes tours.",
|
||||||
|
"17:25:18": "Disparition à un moment ou autre et pour linstant profitons de cette gêne ombre 1 par tour cest pas grand chose mais ça fait du bien lui il a acheté cimetière quand une carte meurt va gagner 6 dans chaque élément moi exemple le coeur dombre je gagne Petite potion alors qui me péter les couilles game Nananabidule en vrai on peut laisser la fin pense que vais attaquer non souris allez attaque oula ok grave bug avec attend",
|
||||||
|
"17:25:41": "là sur la carte cest 2 et si jamais il mattaque avec éternité en passif je vais passer de à 3 donc ça augmente les CD que tu vises nous sommes au premier tour du shop avons 63 thunes premiers achats le coeur dombre mesdames messieurs qui génère un par jeu très bon achat puisque jai deck composé uniquement aller placer tranquillement alors malheureusement elle va certainement se faire attaquer 10",
|
||||||
|
"17:26:24": "Non on va attaquer avec ça donc tape 40 et ensuite entre 1 Oh cest la fumée ? Attends pas mal non Cest il y a une belle amélioration de en vrai. Ah par contre encore des problèmes son là Combien 30 000 balles du côté Chiche. Putain je suis dégoûté Je"
|
||||||
}
|
}
|
||||||
+38
-2
@@ -76,6 +76,11 @@
|
|||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<small class="text-muted" id="autoSubtitleStatus">Arrêté</small>
|
<small class="text-muted" id="autoSubtitleStatus">Arrêté</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<button class="btn btn-warning btn-sm w-100" onclick="forceStopAutoSubtitle()" title="Forcer l'arrêt en cas de problème">
|
||||||
|
<i class="fas fa-stop-circle me-1"></i>Force Stop
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Auto Message Sending Toggle -->
|
<!-- Auto Message Sending Toggle -->
|
||||||
@@ -102,12 +107,40 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<span class="small text-muted">Envoi Messages Chat</span>
|
<span class="small text-muted">Envoi Messages Chat</span>
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<input class="form-check-input" type="checkbox" id="chatMessageToggle" onchange="toggleChatMessage()" checked>
|
<input class="form-check-input" type="checkbox" id="chatMessageToggle" onchange="toggleChatMessage()">
|
||||||
<label class="form-check-label small" for="chatMessageToggle"></label>
|
<label class="form-check-label small" for="chatMessageToggle"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<small class="text-muted" id="chatMessageStatus">Activé</small>
|
<small class="text-muted" id="chatMessageStatus">Désactivé</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- IA Generator Control -->
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<span class="small text-muted">Générateur IA</span>
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" id="iaGeneratorToggle" onchange="toggleIAGenerator()">
|
||||||
|
<label class="form-check-label small" for="iaGeneratorToggle"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<small class="text-muted" id="iaGeneratorStatus">Arrêté</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Control Twitch Control -->
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<span class="small text-muted">Contrôleur Twitch</span>
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" id="controlTwitchToggle" onchange="toggleControlTwitch()">
|
||||||
|
<label class="form-check-label small" for="controlTwitchToggle"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
<small class="text-muted" id="controlTwitchStatus">Arrêté</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -280,6 +313,9 @@
|
|||||||
<button class="btn btn-primary" onclick="processSubtitles()">
|
<button class="btn btn-primary" onclick="processSubtitles()">
|
||||||
<i class="fas fa-cogs me-2"></i>Traitement Manuel
|
<i class="fas fa-cogs me-2"></i>Traitement Manuel
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-danger" onclick="clearSubtitlesHistory()">
|
||||||
|
<i class="fas fa-trash me-2"></i>Nettoyer Historique
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de test pour l'envoi automatique de messages
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def test_auto_message_system():
|
|
||||||
"""Test du système d'envoi automatique de messages"""
|
|
||||||
print("🧪 Test du système d'envoi automatique de messages")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# 1. Vérifier les fichiers de configuration
|
|
||||||
user_config = "config/user.json"
|
|
||||||
if not os.path.exists(user_config):
|
|
||||||
print(f"❌ Fichier de configuration utilisateur non trouvé: {user_config}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print("✅ Fichier de configuration utilisateur trouvé")
|
|
||||||
|
|
||||||
# 2. Vérifier les générations disponibles
|
|
||||||
storage_dir = "storage"
|
|
||||||
generation_file = os.path.join(storage_dir, "IA_generator.json")
|
|
||||||
|
|
||||||
if os.path.exists(generation_file):
|
|
||||||
try:
|
|
||||||
with open(generation_file, 'r', encoding='utf-8') as f:
|
|
||||||
generation_data = json.load(f)
|
|
||||||
print(f"✅ {len(generation_data)} génération(s) disponible(s)")
|
|
||||||
|
|
||||||
if generation_data:
|
|
||||||
sorted_keys = sorted(generation_data.keys())
|
|
||||||
last_generation = generation_data[sorted_keys[-1]]
|
|
||||||
print(f"📝 Dernière génération: {last_generation}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ Erreur lecture générations: {e}")
|
|
||||||
else:
|
|
||||||
print("📝 Aucune génération disponible")
|
|
||||||
|
|
||||||
# 3. Simuler l'envoi automatique
|
|
||||||
print("\n🔄 Simulation de l'envoi automatique:")
|
|
||||||
print(" 1. Surveillance des nouvelles générations")
|
|
||||||
print(" 2. Envoi automatique avec le premier utilisateur")
|
|
||||||
print(" 3. Suppression de la génération après envoi")
|
|
||||||
print(" 4. Attente de 10 secondes entre chaque envoi")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def demo_auto_message_interface():
|
|
||||||
"""Démonstration de l'interface d'envoi automatique"""
|
|
||||||
print("\n🌐 Démonstration de l'interface:")
|
|
||||||
print(" 1. Ouvrir http://localhost:5000")
|
|
||||||
print(" 2. Dans le tableau de bord (sidebar gauche)")
|
|
||||||
print(" 3. Trouver le switch 'Envoi Auto Messages'")
|
|
||||||
print(" 4. Activer le switch pour démarrer l'envoi automatique")
|
|
||||||
print(" 5. Les messages seront envoyés automatiquement quand des générations sont disponibles")
|
|
||||||
|
|
||||||
print("\n📋 Fonctionnalités ajoutées:")
|
|
||||||
print(" ✅ Bouton slide pour l'envoi automatique de messages")
|
|
||||||
print(" ✅ Surveillance des générations IA")
|
|
||||||
print(" ✅ Envoi automatique avec le premier utilisateur")
|
|
||||||
print(" ✅ Suppression automatique des générations envoyées")
|
|
||||||
print(" ✅ Délai de 10 secondes entre chaque envoi")
|
|
||||||
print(" ✅ Retour en temps réel via Socket.IO")
|
|
||||||
print(" ✅ Gestion des erreurs d'envoi")
|
|
||||||
|
|
||||||
def test_message_bot():
|
|
||||||
"""Test du bot de messages"""
|
|
||||||
print("\n🤖 Test du bot de messages:")
|
|
||||||
|
|
||||||
try:
|
|
||||||
from fonction.first_class import messageTwitch
|
|
||||||
|
|
||||||
# Créer une instance du bot de messages
|
|
||||||
message_bot = messageTwitch("config/user.json", "default")
|
|
||||||
print("✅ Bot de messages créé avec succès")
|
|
||||||
|
|
||||||
# Vérifier les utilisateurs disponibles
|
|
||||||
with open("config/user.json", 'r') as f:
|
|
||||||
users = json.load(f)
|
|
||||||
|
|
||||||
print(f"✅ {len(users)} utilisateur(s) configuré(s)")
|
|
||||||
for i, user in enumerate(users):
|
|
||||||
print(f" {i}: {user['tw_acc_pseudo']} {user['charactere']}")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur lors du test du bot: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("🚀 Test du système d'envoi automatique de messages")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# Test du système de base
|
|
||||||
system_ok = test_auto_message_system()
|
|
||||||
|
|
||||||
# Test du bot de messages
|
|
||||||
bot_ok = test_message_bot()
|
|
||||||
|
|
||||||
# Démonstration de l'interface
|
|
||||||
demo_auto_message_interface()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
if system_ok and bot_ok:
|
|
||||||
print("✅ Test réussi !")
|
|
||||||
print("🎉 Le système d'envoi automatique de messages est prêt")
|
|
||||||
print("💡 Utilisez le switch dans l'interface web pour l'activer")
|
|
||||||
else:
|
|
||||||
print("❌ Problèmes détectés")
|
|
||||||
if not system_ok:
|
|
||||||
print(" - Problème avec le système de base")
|
|
||||||
if not bot_ok:
|
|
||||||
print(" - Problème avec le bot de messages")
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de test pour la génération automatique de sous-titres
|
|
||||||
"""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def test_auto_subtitle_system():
|
|
||||||
"""Test du système de génération automatique de sous-titres"""
|
|
||||||
print("🧪 Test du système de génération automatique de sous-titres")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# 1. Vérifier les fichiers audio disponibles
|
|
||||||
record_dir = "record"
|
|
||||||
if not os.path.exists(record_dir):
|
|
||||||
print(f"❌ Dossier record non trouvé: {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
audio_files = [f for f in os.listdir(record_dir) if f.endswith('.mp3')]
|
|
||||||
if not audio_files:
|
|
||||||
print(f"❌ Aucun fichier audio trouvé dans {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f"✅ {len(audio_files)} fichiers audio trouvés")
|
|
||||||
print(f"📁 Premier fichier: {audio_files[0]}")
|
|
||||||
|
|
||||||
# 2. Simuler le traitement d'un fichier
|
|
||||||
test_file = audio_files[0]
|
|
||||||
audio_path = os.path.join(record_dir, test_file)
|
|
||||||
|
|
||||||
print(f"\n🎵 Simulation du traitement de: {test_file}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Lancer Whisper
|
|
||||||
command = [
|
|
||||||
'whisper',
|
|
||||||
'--language', 'fr',
|
|
||||||
audio_path,
|
|
||||||
'--device', 'cuda',
|
|
||||||
'--model', 'large-v3'
|
|
||||||
]
|
|
||||||
|
|
||||||
print(f"📝 Commande: {' '.join(command)}")
|
|
||||||
print("⏳ Traitement en cours...")
|
|
||||||
|
|
||||||
result = subprocess.run(command, capture_output=True, text=True, timeout=60)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✅ Whisper a fonctionné avec succès")
|
|
||||||
|
|
||||||
# Lire le fichier .txt généré
|
|
||||||
txt_file = test_file.replace('.mp3', '.txt')
|
|
||||||
if os.path.exists(txt_file):
|
|
||||||
with open(txt_file, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read().strip()
|
|
||||||
|
|
||||||
print(f"📝 Contenu généré: {content}")
|
|
||||||
|
|
||||||
# Nettoyer le contenu
|
|
||||||
content = content.replace("'", "").replace('"', "").replace("\n", " ").replace(",", "")
|
|
||||||
|
|
||||||
# Supprimer les répétitions de mots
|
|
||||||
words = content.split()
|
|
||||||
seen = set()
|
|
||||||
result_words = []
|
|
||||||
for word in words:
|
|
||||||
if word not in seen:
|
|
||||||
result_words.append(word)
|
|
||||||
seen.add(word)
|
|
||||||
cleaned_content = " ".join(result_words)
|
|
||||||
|
|
||||||
print(f"🧹 Contenu nettoyé: {cleaned_content}")
|
|
||||||
|
|
||||||
# Sauvegarder dans le stockage
|
|
||||||
current_time = datetime.now().strftime('%H:%M:%S')
|
|
||||||
|
|
||||||
# Sauvegarder dans le fichier de stockage
|
|
||||||
storage_dir = "storage"
|
|
||||||
if not os.path.exists(storage_dir):
|
|
||||||
os.makedirs(storage_dir)
|
|
||||||
|
|
||||||
subtitle_file = os.path.join(storage_dir, "subtitle_data.json")
|
|
||||||
try:
|
|
||||||
with open(subtitle_file, 'r', encoding='utf-8') as f:
|
|
||||||
existing_data = json.load(f)
|
|
||||||
except (FileNotFoundError, json.JSONDecodeError):
|
|
||||||
existing_data = {}
|
|
||||||
|
|
||||||
existing_data[current_time] = cleaned_content
|
|
||||||
|
|
||||||
with open(subtitle_file, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(existing_data, f, indent=4, ensure_ascii=False)
|
|
||||||
|
|
||||||
print(f"💾 Sous-titre sauvegardé à {current_time}")
|
|
||||||
|
|
||||||
# Nettoyer les fichiers temporaires
|
|
||||||
os.remove(txt_file)
|
|
||||||
print("🧹 Fichier temporaire nettoyé")
|
|
||||||
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("⚠️ Aucun fichier .txt généré")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f"❌ Erreur Whisper: {result.stderr}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
print("⏰ Timeout - Whisper a pris trop de temps")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def demo_auto_subtitle_interface():
|
|
||||||
"""Démonstration de l'interface de génération automatique"""
|
|
||||||
print("\n🌐 Démonstration de l'interface:")
|
|
||||||
print(" 1. Ouvrir http://localhost:5000")
|
|
||||||
print(" 2. Dans le tableau de bord (sidebar gauche)")
|
|
||||||
print(" 3. Trouver le switch 'Génération Auto Sous-titres'")
|
|
||||||
print(" 4. Activer le switch pour démarrer la génération automatique")
|
|
||||||
print(" 5. Aller dans l'onglet 'Sous-titres' pour voir le fichier en cours")
|
|
||||||
print(" 6. Les sous-titres seront créés automatiquement fichier par fichier")
|
|
||||||
|
|
||||||
print("\n📋 Fonctionnalités ajoutées:")
|
|
||||||
print(" ✅ Bouton slide dans le tableau de bord")
|
|
||||||
print(" ✅ Indicateur de fichier en cours de traitement")
|
|
||||||
print(" ✅ Traitement automatique fichier par fichier")
|
|
||||||
print(" ✅ Retour en temps réel via Socket.IO")
|
|
||||||
print(" ✅ Sauvegarde automatique dans le stockage")
|
|
||||||
print(" ✅ Nettoyage automatique des fichiers temporaires")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("🚀 Test du système de génération automatique de sous-titres")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# Test du traitement automatique
|
|
||||||
success = test_auto_subtitle_system()
|
|
||||||
|
|
||||||
# Démonstration de l'interface
|
|
||||||
demo_auto_subtitle_interface()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
if success:
|
|
||||||
print("✅ Test réussi !")
|
|
||||||
print("🎉 Le système de génération automatique est prêt")
|
|
||||||
print("💡 Utilisez le switch dans l'interface web pour l'activer")
|
|
||||||
else:
|
|
||||||
print("❌ Test échoué !")
|
|
||||||
print("🔧 Vérifiez que Whisper fonctionne correctement")
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de test pour vérifier le toggle d'envoi de messages chat
|
|
||||||
"""
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
|
|
||||||
BASE_URL = "http://localhost:5000"
|
|
||||||
|
|
||||||
def test_chat_messages_toggle():
|
|
||||||
"""Test du toggle d'envoi de messages chat"""
|
|
||||||
print("🧪 Test du toggle d'envoi de messages chat")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Test 1: Vérifier le statut initial
|
|
||||||
print("1. Vérification du statut initial...")
|
|
||||||
try:
|
|
||||||
response = requests.get(f"{BASE_URL}/api/chat/messages/status")
|
|
||||||
if response.status_code == 200:
|
|
||||||
status = response.json()
|
|
||||||
print(f" ✓ Statut initial: {'Activé' if status['enabled'] else 'Désactivé'}")
|
|
||||||
else:
|
|
||||||
print(f" ✗ Erreur: {response.status_code}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ Erreur de connexion: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Test 2: Désactiver l'envoi de messages
|
|
||||||
print("2. Désactivation de l'envoi de messages...")
|
|
||||||
try:
|
|
||||||
response = requests.post(f"{BASE_URL}/api/chat/messages/disable")
|
|
||||||
if response.status_code == 200:
|
|
||||||
result = response.json()
|
|
||||||
print(f" ✓ {result['message']}")
|
|
||||||
else:
|
|
||||||
print(f" ✗ Erreur: {response.status_code}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ Erreur: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Test 3: Vérifier que c'est bien désactivé
|
|
||||||
print("3. Vérification de la désactivation...")
|
|
||||||
try:
|
|
||||||
response = requests.get(f"{BASE_URL}/api/chat/messages/status")
|
|
||||||
if response.status_code == 200:
|
|
||||||
status = response.json()
|
|
||||||
if not status['enabled']:
|
|
||||||
print(" ✓ Envoi de messages désactivé avec succès")
|
|
||||||
else:
|
|
||||||
print(" ✗ L'envoi de messages n'a pas été désactivé")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f" ✗ Erreur: {response.status_code}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ Erreur: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Test 4: Réactiver l'envoi de messages
|
|
||||||
print("4. Réactivation de l'envoi de messages...")
|
|
||||||
try:
|
|
||||||
response = requests.post(f"{BASE_URL}/api/chat/messages/enable")
|
|
||||||
if response.status_code == 200:
|
|
||||||
result = response.json()
|
|
||||||
print(f" ✓ {result['message']}")
|
|
||||||
else:
|
|
||||||
print(f" ✗ Erreur: {response.status_code}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ Erreur: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Test 5: Vérifier que c'est bien réactivé
|
|
||||||
print("5. Vérification de la réactivation...")
|
|
||||||
try:
|
|
||||||
response = requests.get(f"{BASE_URL}/api/chat/messages/status")
|
|
||||||
if response.status_code == 200:
|
|
||||||
status = response.json()
|
|
||||||
if status['enabled']:
|
|
||||||
print(" ✓ Envoi de messages réactivé avec succès")
|
|
||||||
else:
|
|
||||||
print(" ✗ L'envoi de messages n'a pas été réactivé")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f" ✗ Erreur: {response.status_code}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ Erreur: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print("=" * 50)
|
|
||||||
print("✅ Tous les tests sont passés avec succès !")
|
|
||||||
return True
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("🤖 Test du système de toggle d'envoi de messages")
|
|
||||||
print("Assurez-vous que le serveur web est démarré sur http://localhost:5000")
|
|
||||||
print()
|
|
||||||
|
|
||||||
success = test_chat_messages_toggle()
|
|
||||||
|
|
||||||
if success:
|
|
||||||
print("\n🎉 Le toggle d'envoi de messages fonctionne correctement !")
|
|
||||||
else:
|
|
||||||
print("\n❌ Des erreurs ont été détectées lors des tests.")
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de test pour simuler l'API de traitement manuel des sous-titres
|
|
||||||
"""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def process_subtitles_manual():
|
|
||||||
"""Simuler le traitement manuel des sous-titres"""
|
|
||||||
print("🧪 Test du traitement manuel des sous-titres")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Vérifier que le dossier record existe
|
|
||||||
record_dir = "record"
|
|
||||||
if not os.path.exists(record_dir):
|
|
||||||
print(f"❌ Dossier record non trouvé: {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Trouver les fichiers audio
|
|
||||||
audio_files = [f for f in os.listdir(record_dir) if f.endswith('.mp3')]
|
|
||||||
if not audio_files:
|
|
||||||
print(f"❌ Aucun fichier audio trouvé dans {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f"✅ {len(audio_files)} fichiers audio trouvés")
|
|
||||||
|
|
||||||
subtitles_created = 0
|
|
||||||
subtitles_data = {}
|
|
||||||
|
|
||||||
# Traiter chaque fichier audio (limiter à 2 pour le test)
|
|
||||||
for audio_file in audio_files[:2]:
|
|
||||||
audio_path = os.path.join(record_dir, audio_file)
|
|
||||||
print(f"🎵 Traitement de: {audio_file}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Lancer Whisper
|
|
||||||
command = [
|
|
||||||
'whisper',
|
|
||||||
'--language', 'fr',
|
|
||||||
audio_path,
|
|
||||||
'--device', 'cuda',
|
|
||||||
'--model', 'large-v3'
|
|
||||||
]
|
|
||||||
|
|
||||||
print(f"📝 Commande: {' '.join(command)}")
|
|
||||||
result = subprocess.run(command, capture_output=True, text=True, timeout=60)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
# Lire le fichier .txt généré
|
|
||||||
txt_file = audio_file.replace('.mp3', '.txt')
|
|
||||||
if os.path.exists(txt_file):
|
|
||||||
with open(txt_file, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read().strip()
|
|
||||||
|
|
||||||
print(f"📝 Contenu brut: {content}")
|
|
||||||
|
|
||||||
# Nettoyer le contenu
|
|
||||||
content = content.replace("'", "").replace('"', "").replace("\n", " ").replace(",", "")
|
|
||||||
|
|
||||||
# Supprimer les répétitions de mots
|
|
||||||
words = content.split()
|
|
||||||
seen = set()
|
|
||||||
result_words = []
|
|
||||||
for word in words:
|
|
||||||
if word not in seen:
|
|
||||||
result_words.append(word)
|
|
||||||
seen.add(word)
|
|
||||||
cleaned_content = " ".join(result_words)
|
|
||||||
|
|
||||||
print(f"🧹 Contenu nettoyé: {cleaned_content}")
|
|
||||||
|
|
||||||
# Sauvegarder dans le stockage
|
|
||||||
current_time = datetime.now().strftime('%H:%M:%S')
|
|
||||||
subtitles_data[current_time] = cleaned_content
|
|
||||||
|
|
||||||
# Sauvegarder dans le fichier de stockage
|
|
||||||
storage_dir = "storage"
|
|
||||||
if not os.path.exists(storage_dir):
|
|
||||||
os.makedirs(storage_dir)
|
|
||||||
|
|
||||||
subtitle_file = os.path.join(storage_dir, "subtitle_data.json")
|
|
||||||
try:
|
|
||||||
with open(subtitle_file, 'r', encoding='utf-8') as f:
|
|
||||||
existing_data = json.load(f)
|
|
||||||
except (FileNotFoundError, json.JSONDecodeError):
|
|
||||||
existing_data = {}
|
|
||||||
|
|
||||||
existing_data[current_time] = cleaned_content
|
|
||||||
|
|
||||||
with open(subtitle_file, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(existing_data, f, indent=4, ensure_ascii=False)
|
|
||||||
|
|
||||||
subtitles_created += 1
|
|
||||||
print(f"✅ Sous-titre créé et sauvegardé")
|
|
||||||
|
|
||||||
# Nettoyer les fichiers temporaires
|
|
||||||
os.remove(txt_file)
|
|
||||||
os.remove(audio_path) # Supprimer le fichier audio traité
|
|
||||||
print(f"🧹 Fichiers temporaires nettoyés")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print(f"⚠️ Aucun fichier .txt généré pour {audio_file}")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print(f"❌ Erreur Whisper: {result.stderr}")
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
print(f"⏰ Timeout pour {audio_file}")
|
|
||||||
continue
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur pour {audio_file}: {e}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f"\n📊 Résumé: {subtitles_created} sous-titre(s) créé(s)")
|
|
||||||
print("📝 Sous-titres générés:")
|
|
||||||
for time_key, text in subtitles_data.items():
|
|
||||||
print(f" {time_key}: {text}")
|
|
||||||
|
|
||||||
return subtitles_created > 0
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
success = process_subtitles_manual()
|
|
||||||
if success:
|
|
||||||
print("\n✅ Test réussi ! Le traitement manuel fonctionne.")
|
|
||||||
else:
|
|
||||||
print("\n❌ Test échoué !")
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de test pour le traitement manuel des sous-titres
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
|
|
||||||
# Ajouter le chemin vers les modules
|
|
||||||
sys.path.append('.')
|
|
||||||
|
|
||||||
def test_manual_subtitle_processing():
|
|
||||||
"""Test du traitement manuel des sous-titres"""
|
|
||||||
print("🧪 Test du traitement manuel des sous-titres")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Vérifier que le fichier de configuration existe
|
|
||||||
config_file = "config/config.json"
|
|
||||||
if not os.path.exists(config_file):
|
|
||||||
print(f"❌ Fichier de configuration non trouvé: {config_file}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Vérifier que le dossier record existe et contient des fichiers
|
|
||||||
record_dir = "record"
|
|
||||||
if not os.path.exists(record_dir):
|
|
||||||
print(f"❌ Dossier record non trouvé: {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
audio_files = [f for f in os.listdir(record_dir) if f.endswith('.mp3')]
|
|
||||||
if not audio_files:
|
|
||||||
print(f"❌ Aucun fichier audio trouvé dans {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f"✅ {len(audio_files)} fichiers audio trouvés")
|
|
||||||
print("📁 Fichiers audio disponibles:")
|
|
||||||
for file in audio_files[:5]: # Afficher les 5 premiers fichiers
|
|
||||||
print(f" - {file}")
|
|
||||||
|
|
||||||
# Créer une instance du bot de sous-titres
|
|
||||||
try:
|
|
||||||
from fonction.first_class import Subtitle_translation
|
|
||||||
|
|
||||||
subtitle_bot = Subtitle_translation(config_file)
|
|
||||||
print("✅ Bot de sous-titres créé avec succès")
|
|
||||||
|
|
||||||
# Lancer le traitement manuel
|
|
||||||
print("🔍 Lancement du traitement manuel...")
|
|
||||||
subtitle_bot.verif_file_transcribe()
|
|
||||||
|
|
||||||
# Vérifier si des sous-titres ont été créés
|
|
||||||
if subtitle_bot.subtitle:
|
|
||||||
print("✅ Sous-titres créés avec succès")
|
|
||||||
print("📝 Sous-titres générés:")
|
|
||||||
for time_key, text in subtitle_bot.subtitle.items():
|
|
||||||
print(f" {time_key}: {text}")
|
|
||||||
else:
|
|
||||||
print("⚠️ Aucun sous-titre créé")
|
|
||||||
|
|
||||||
# Vérifier le stockage
|
|
||||||
from fonction.first_class import storage
|
|
||||||
subtitle_data = storage.read("subtitle_data")
|
|
||||||
if subtitle_data:
|
|
||||||
print(f"💾 {len(subtitle_data)} sous-titre(s) sauvegardé(s) dans le stockage")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur lors du test: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
success = test_manual_subtitle_processing()
|
|
||||||
if success:
|
|
||||||
print("\n✅ Test réussi !")
|
|
||||||
else:
|
|
||||||
print("\n❌ Test échoué !")
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de test pour vérifier l'envoi de messages
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Ajouter le chemin vers les modules
|
|
||||||
sys.path.append('.')
|
|
||||||
|
|
||||||
from fonction.first_class import messageTwitch
|
|
||||||
|
|
||||||
def test_send_message():
|
|
||||||
"""Test de l'envoi de message"""
|
|
||||||
try:
|
|
||||||
# Créer une instance de messageTwitch
|
|
||||||
msg_bot = messageTwitch("config/user.json", "test_channel")
|
|
||||||
|
|
||||||
# Tester l'envoi d'un message
|
|
||||||
print("Test d'envoi de message...")
|
|
||||||
msg_bot.send_message("Test message from bot")
|
|
||||||
print("✓ Test réussi !")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Erreur lors du test: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("🧪 Test de l'envoi de messages")
|
|
||||||
print("=" * 40)
|
|
||||||
|
|
||||||
if test_send_message():
|
|
||||||
print("\n✅ Tous les tests sont passés !")
|
|
||||||
else:
|
|
||||||
print("\n❌ Certains tests ont échoué.")
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de test pour vérifier le fonctionnement du bot de sous-titres
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
# Ajouter le chemin vers les modules
|
|
||||||
sys.path.append('.')
|
|
||||||
|
|
||||||
from fonction.first_class import Subtitle_translation
|
|
||||||
|
|
||||||
def test_subtitle_bot():
|
|
||||||
"""Test du bot de sous-titres"""
|
|
||||||
print("🧪 Test du bot de sous-titres")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Vérifier que le fichier de configuration existe
|
|
||||||
config_file = "config/config.json"
|
|
||||||
if not os.path.exists(config_file):
|
|
||||||
print(f"❌ Fichier de configuration non trouvé: {config_file}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Vérifier que le dossier record existe et contient des fichiers
|
|
||||||
record_dir = "record"
|
|
||||||
if not os.path.exists(record_dir):
|
|
||||||
print(f"❌ Dossier record non trouvé: {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
audio_files = [f for f in os.listdir(record_dir) if f.endswith('.mp3')]
|
|
||||||
if not audio_files:
|
|
||||||
print(f"❌ Aucun fichier audio trouvé dans {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f"✅ {len(audio_files)} fichiers audio trouvés")
|
|
||||||
|
|
||||||
# Créer une instance du bot de sous-titres
|
|
||||||
try:
|
|
||||||
subtitle_bot = Subtitle_translation(config_file)
|
|
||||||
print("✅ Bot de sous-titres créé avec succès")
|
|
||||||
|
|
||||||
# Tester la méthode verif_file_transcribe
|
|
||||||
print("🔍 Test de la vérification des fichiers...")
|
|
||||||
subtitle_bot.verif_file_transcribe()
|
|
||||||
|
|
||||||
# Vérifier si des sous-titres ont été créés
|
|
||||||
if subtitle_bot.subtitle:
|
|
||||||
print("✅ Sous-titres créés avec succès")
|
|
||||||
for time_key, text in subtitle_bot.subtitle.items():
|
|
||||||
print(f" {time_key}: {text}")
|
|
||||||
else:
|
|
||||||
print("⚠️ Aucun sous-titre créé")
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur lors du test: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
success = test_subtitle_bot()
|
|
||||||
if success:
|
|
||||||
print("\n✅ Test réussi !")
|
|
||||||
else:
|
|
||||||
print("\n❌ Test échoué !")
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Script de test direct de Whisper pour la création de sous-titres
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
|
|
||||||
def test_whisper_direct():
|
|
||||||
"""Test direct de Whisper"""
|
|
||||||
print("🧪 Test direct de Whisper")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Vérifier que le dossier record existe et contient des fichiers
|
|
||||||
record_dir = "record"
|
|
||||||
if not os.path.exists(record_dir):
|
|
||||||
print(f"❌ Dossier record non trouvé: {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
audio_files = [f for f in os.listdir(record_dir) if f.endswith('.mp3')]
|
|
||||||
if not audio_files:
|
|
||||||
print(f"❌ Aucun fichier audio trouvé dans {record_dir}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f"✅ {len(audio_files)} fichiers audio trouvés")
|
|
||||||
|
|
||||||
# Prendre le premier fichier pour le test
|
|
||||||
test_file = audio_files[0]
|
|
||||||
test_file_path = os.path.join(record_dir, test_file)
|
|
||||||
|
|
||||||
print(f"🎵 Test avec le fichier: {test_file}")
|
|
||||||
|
|
||||||
# Test direct de Whisper
|
|
||||||
try:
|
|
||||||
print("🔍 Lancement de Whisper...")
|
|
||||||
command = [
|
|
||||||
'whisper',
|
|
||||||
'--language', 'fr',
|
|
||||||
test_file_path,
|
|
||||||
'--device', 'cuda',
|
|
||||||
'--model', 'large-v3'
|
|
||||||
]
|
|
||||||
|
|
||||||
print(f"📝 Commande: {' '.join(command)}")
|
|
||||||
|
|
||||||
# Exécuter Whisper
|
|
||||||
result = subprocess.run(command, capture_output=True, text=True, timeout=60)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✅ Whisper a fonctionné avec succès")
|
|
||||||
|
|
||||||
# Vérifier si un fichier .txt a été créé
|
|
||||||
txt_file = test_file.replace('.mp3', '.txt')
|
|
||||||
if os.path.exists(txt_file):
|
|
||||||
with open(txt_file, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read().strip()
|
|
||||||
print(f"📝 Contenu généré: {content}")
|
|
||||||
|
|
||||||
# Nettoyer le fichier de test
|
|
||||||
os.remove(txt_file)
|
|
||||||
print("🧹 Fichier de test nettoyé")
|
|
||||||
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("⚠️ Aucun fichier .txt généré")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f"❌ Erreur Whisper: {result.stderr}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
print("⏰ Timeout - Whisper a pris trop de temps")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_subtitle_storage():
|
|
||||||
"""Test du stockage des sous-titres"""
|
|
||||||
print("\n🧪 Test du stockage des sous-titres")
|
|
||||||
print("=" * 50)
|
|
||||||
|
|
||||||
# Créer un fichier de test pour le stockage
|
|
||||||
storage_dir = "storage"
|
|
||||||
if not os.path.exists(storage_dir):
|
|
||||||
os.makedirs(storage_dir)
|
|
||||||
print(f"✅ Dossier storage créé: {storage_dir}")
|
|
||||||
|
|
||||||
# Test d'écriture/lecture
|
|
||||||
test_data = {
|
|
||||||
"test_time": "Test de sous-titre",
|
|
||||||
"test_time_2": "Deuxième test"
|
|
||||||
}
|
|
||||||
|
|
||||||
subtitle_file = os.path.join(storage_dir, "subtitle_data.json")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Écrire des données de test
|
|
||||||
with open(subtitle_file, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(test_data, f, indent=4, ensure_ascii=False)
|
|
||||||
print("✅ Données de test écrites")
|
|
||||||
|
|
||||||
# Lire les données
|
|
||||||
with open(subtitle_file, 'r', encoding='utf-8') as f:
|
|
||||||
read_data = json.load(f)
|
|
||||||
|
|
||||||
if read_data == test_data:
|
|
||||||
print("✅ Lecture/écriture du stockage OK")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("❌ Données lues différentes des données écrites")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Erreur stockage: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("🚀 Test complet du système de sous-titres")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# Test 1: Whisper direct
|
|
||||||
whisper_ok = test_whisper_direct()
|
|
||||||
|
|
||||||
# Test 2: Stockage
|
|
||||||
storage_ok = test_subtitle_storage()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
if whisper_ok and storage_ok:
|
|
||||||
print("✅ Tous les tests sont réussis !")
|
|
||||||
print("🎉 Le système de sous-titres est prêt à fonctionner")
|
|
||||||
else:
|
|
||||||
print("❌ Certains tests ont échoué")
|
|
||||||
if not whisper_ok:
|
|
||||||
print(" - Whisper ne fonctionne pas correctement")
|
|
||||||
if not storage_ok:
|
|
||||||
print(" - Le stockage ne fonctionne pas correctement")
|
|
||||||
+276
-7
@@ -25,6 +25,10 @@ class BotController:
|
|||||||
self.bots = {} # Stockage des instances de bots (pour l'utilisation interne)
|
self.bots = {} # Stockage des instances de bots (pour l'utilisation interne)
|
||||||
self.flux_list = [] # Liste des flux surveillés (pour l'API JSON)
|
self.flux_list = [] # Liste des flux surveillés (pour l'API JSON)
|
||||||
self.config = self.load_config()
|
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):
|
def load_config(self):
|
||||||
try:
|
try:
|
||||||
@@ -37,6 +41,21 @@ class BotController:
|
|||||||
with open('config/config.json', 'w') as file:
|
with open('config/config.json', 'w') as file:
|
||||||
json.dump(self.config, file, indent=4, ensure_ascii=False)
|
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):
|
def add_flux(self, channel_name, record_audio=True):
|
||||||
flux_id = len(self.flux_list) + 1
|
flux_id = len(self.flux_list) + 1
|
||||||
|
|
||||||
@@ -140,6 +159,112 @@ class BotController:
|
|||||||
# Retourner seulement les données JSON (pas les instances de bots)
|
# Retourner seulement les données JSON (pas les instances de bots)
|
||||||
return self.flux_list
|
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()
|
bot_controller = BotController()
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
@@ -372,6 +497,33 @@ def get_subtitles():
|
|||||||
data = storage.read("subtitle_data")
|
data = storage.read("subtitle_data")
|
||||||
return jsonify(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'])
|
@app.route('/api/subtitles/process', methods=['POST'])
|
||||||
def process_subtitles():
|
def process_subtitles():
|
||||||
"""Lancer manuellement le traitement des sous-titres"""
|
"""Lancer manuellement le traitement des sous-titres"""
|
||||||
@@ -643,21 +795,32 @@ auto_message_running = False
|
|||||||
current_message_bot = None
|
current_message_bot = None
|
||||||
|
|
||||||
# Variable globale pour contrôler l'envoi de messages dans le chat
|
# 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'])
|
@app.route('/api/subtitles/auto/start', methods=['POST'])
|
||||||
def start_auto_subtitle():
|
def start_auto_subtitle():
|
||||||
"""Démarrer la génération automatique de sous-titres"""
|
"""Démarrer la génération automatique de sous-titres"""
|
||||||
global auto_subtitle_running
|
global auto_subtitle_running
|
||||||
try:
|
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
|
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
|
# Démarrer le thread de génération automatique
|
||||||
threading.Thread(target=auto_subtitle_loop, daemon=True).start()
|
threading.Thread(target=auto_subtitle_loop, daemon=True).start()
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': 'Génération automatique démarrée'
|
'message': 'Génération automatique démarrée'
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
auto_subtitle_running = False
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'error': f'Erreur lors du démarrage: {str(e)}'
|
'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"""
|
"""Arrêter la génération automatique de sous-titres"""
|
||||||
global auto_subtitle_running
|
global auto_subtitle_running
|
||||||
try:
|
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
|
auto_subtitle_running = False
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': 'Génération automatique arrêtée'
|
'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)}'
|
'error': f'Erreur lors de l\'arrêt: {str(e)}'
|
||||||
}), 500
|
}), 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'])
|
@app.route('/api/subtitles/auto/status', methods=['GET'])
|
||||||
def get_auto_subtitle_status():
|
def get_auto_subtitle_status():
|
||||||
"""Obtenir le statut de la génération automatique"""
|
"""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"""
|
"""Boucle de génération automatique de sous-titres"""
|
||||||
global auto_subtitle_running, current_processing_file
|
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:
|
while auto_subtitle_running:
|
||||||
try:
|
try:
|
||||||
# Vérifier s'il y a des fichiers audio à traiter
|
# Vérifier s'il y a des fichiers audio à traiter
|
||||||
@@ -709,6 +901,8 @@ def auto_subtitle_loop():
|
|||||||
audio_file = audio_files[0]
|
audio_file = audio_files[0]
|
||||||
current_processing_file = audio_file
|
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
|
# Émettre l'événement de début de traitement
|
||||||
socketio.emit('subtitle_processing_start', {
|
socketio.emit('subtitle_processing_start', {
|
||||||
'file': audio_file,
|
'file': audio_file,
|
||||||
@@ -719,7 +913,7 @@ def auto_subtitle_loop():
|
|||||||
audio_path = os.path.join(record_dir, audio_file)
|
audio_path = os.path.join(record_dir, audio_file)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Lancer Whisper
|
# Lancer Whisper avec vérification périodique de l'arrêt
|
||||||
command = [
|
command = [
|
||||||
'whisper',
|
'whisper',
|
||||||
'--language', 'fr',
|
'--language', 'fr',
|
||||||
@@ -728,9 +922,30 @@ def auto_subtitle_loop():
|
|||||||
'--model', 'large-v3'
|
'--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é
|
# Lire le fichier .txt généré
|
||||||
txt_file = audio_file.replace('.mp3', '.txt')
|
txt_file = audio_file.replace('.mp3', '.txt')
|
||||||
if os.path.exists(txt_file):
|
if os.path.exists(txt_file):
|
||||||
@@ -777,6 +992,8 @@ def auto_subtitle_loop():
|
|||||||
'timestamp': current_time
|
'timestamp': current_time
|
||||||
})
|
})
|
||||||
|
|
||||||
|
print(f"[{datetime.now().strftime('%H:%M:%S')}] Sous-titre généré: {cleaned_content}")
|
||||||
|
|
||||||
# Nettoyer les fichiers temporaires
|
# Nettoyer les fichiers temporaires
|
||||||
os.remove(txt_file)
|
os.remove(txt_file)
|
||||||
os.remove(audio_path)
|
os.remove(audio_path)
|
||||||
@@ -787,38 +1004,49 @@ def auto_subtitle_loop():
|
|||||||
'file': audio_file,
|
'file': audio_file,
|
||||||
'error': 'Aucun fichier .txt généré'
|
'error': 'Aucun fichier .txt généré'
|
||||||
})
|
})
|
||||||
|
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur: Aucun fichier .txt généré")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Émettre l'événement d'erreur
|
# Émettre l'événement d'erreur
|
||||||
socketio.emit('subtitle_processing_error', {
|
socketio.emit('subtitle_processing_error', {
|
||||||
'file': audio_file,
|
'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:
|
except subprocess.TimeoutExpired:
|
||||||
socketio.emit('subtitle_processing_error', {
|
socketio.emit('subtitle_processing_error', {
|
||||||
'file': audio_file,
|
'file': audio_file,
|
||||||
'error': 'Timeout - Whisper a pris trop de temps'
|
'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:
|
except Exception as e:
|
||||||
socketio.emit('subtitle_processing_error', {
|
socketio.emit('subtitle_processing_error', {
|
||||||
'file': audio_file,
|
'file': audio_file,
|
||||||
'error': f'Erreur: {str(e)}'
|
'error': f'Erreur: {str(e)}'
|
||||||
})
|
})
|
||||||
|
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur lors du traitement: {str(e)}")
|
||||||
|
|
||||||
current_processing_file = None
|
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
|
# Attendre avant de traiter le prochain fichier
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
except Exception as e:
|
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)
|
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'])
|
@app.route('/api/messages/auto/start', methods=['POST'])
|
||||||
def start_auto_message():
|
def start_auto_message():
|
||||||
"""Démarrer l'envoi automatique de messages"""
|
"""Démarrer l'envoi automatique de messages"""
|
||||||
global auto_message_running, current_message_bot
|
global auto_message_running
|
||||||
try:
|
try:
|
||||||
# Vérifier si déjà en cours
|
# Vérifier si déjà en cours
|
||||||
if auto_message_running:
|
if auto_message_running:
|
||||||
@@ -1013,6 +1241,47 @@ def get_chat_messages_status():
|
|||||||
'enabled': chat_messages_enabled
|
'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__':
|
if __name__ == '__main__':
|
||||||
# Démarrer les mises à jour en arrière-plan
|
# Démarrer les mises à jour en arrière-plan
|
||||||
update_thread = threading.Thread(target=background_updates, daemon=True)
|
update_thread = threading.Thread(target=background_updates, daemon=True)
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"14h30m20s": "Salut ! Ça va super bien merci ! 😊",
|
|
||||||
"14h31m28s": "Ce jeu a l'air génial, bonne chance !",
|
|
||||||
"14h32m50s": "Ça arrive aux meilleurs, tu vas y arriver !",
|
|
||||||
"14h33m15s": "Bienvenue dans la communauté ! 🎉",
|
|
||||||
"14h34m35s": "Waouh cette action était épique ! 🔥",
|
|
||||||
"14h35m25s": "Bonne idée, change de tactique !",
|
|
||||||
"14h36m10s": "Bravo ! Tu maîtrises de mieux en mieux",
|
|
||||||
"14h37m28s": "Toujours là pour aider ! 💪",
|
|
||||||
"01h40m18s": "Plot twist inattendu ! 😮"
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"14h30m15s": "Salut les viewers ! Comment ça va aujourd'hui ?",
|
|
||||||
"14h31m22s": "On va jouer à ce nouveau jeu, j'ai hâte de voir ce que ça donne",
|
|
||||||
"14h32m45s": "Oh non, je suis mort déjà ! C'est plus dur que je pensais",
|
|
||||||
"14h33m12s": "Merci pour le follow @nouveau_viewer !",
|
|
||||||
"14h34m30s": "Cette partie est vraiment intense, regardez ça !",
|
|
||||||
"14h35m18s": "Je pense qu'on devrait essayer une autre stratégie",
|
|
||||||
"14h36m05s": "Excellent, on progresse enfin dans ce niveau",
|
|
||||||
"14h37m22s": "N'hésitez pas à poser vos questions dans le chat",
|
|
||||||
"01h40m18s": "Vous avez vu cette action incroyable ?"
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user