Arghet6 commited on
Commit
27b3bb8
·
verified ·
1 Parent(s): af2b8d4

Upload 18 files

Browse files
.gitattributes CHANGED
@@ -1,35 +1,2 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
1
  *.bin filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  *.pth filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ model_cache/
2
+ models_cache/
3
+ instance/
.idea/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
.idea/emotion_analysis_system.iml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
6
+ </content>
7
+ <orderEntry type="jdk" jdkName="Python 3.11" jdkType="Python SDK" />
8
+ <orderEntry type="sourceFolder" forTests="false" />
9
+ </component>
10
+ </module>
.idea/inspectionProfiles/Project_Default.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <component name="InspectionProjectProfileManager">
2
+ <profile version="1.0">
3
+ <option name="myName" value="Project Default" />
4
+ <inspection_tool class="XmlDuplicatedId" enabled="false" level="ERROR" enabled_by_default="false" />
5
+ </profile>
6
+ </component>
.idea/inspectionProfiles/profiles_settings.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
.idea/misc.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="Python 3.11 (emotion_analysis_system)" />
5
+ </component>
6
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
7
+ <component name="PyCharmProfessionalAdvertiser">
8
+ <option name="shown" value="true" />
9
+ </component>
10
+ </project>
.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/emotion_analysis_system.iml" filepath="$PROJECT_DIR$/.idea/emotion_analysis_system.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
.idea/workspace.xml ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="AutoImportSettings">
4
+ <option name="autoReloadType" value="SELECTIVE" />
5
+ </component>
6
+ <component name="ChangeListManager">
7
+ <list default="true" id="80cad30e-20ea-4a3e-a71a-c380b9fb453b" name="Changes" comment="добавил gunicorn">
8
+ <change beforePath="$PROJECT_DIR$/.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
9
+ </list>
10
+ <option name="SHOW_DIALOG" value="false" />
11
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
12
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
13
+ <option name="LAST_RESOLUTION" value="IGNORE" />
14
+ </component>
15
+ <component name="Git.Settings">
16
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
17
+ </component>
18
+ <component name="ProjectColorInfo">{
19
+ &quot;associatedIndex&quot;: 7
20
+ }</component>
21
+ <component name="ProjectId" id="2wCKFqS6j3fHR0o6mVerabcct5P" />
22
+ <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
23
+ <component name="ProjectViewState">
24
+ <option name="hideEmptyMiddlePackages" value="true" />
25
+ <option name="showLibraryContents" value="true" />
26
+ </component>
27
+ <component name="PropertiesComponent"><![CDATA[{
28
+ "keyToString": {
29
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
30
+ "RunOnceActivity.ShowReadmeOnStart": "true",
31
+ "git-widget-placeholder": "master",
32
+ "last_opened_file_path": "C:/Users/Айрат/Desktop/analyse_diplom_project-master",
33
+ "settings.editor.selected.configurable": "org.jetbrains.plugins.github.ui.GithubSettingsConfigurable"
34
+ }
35
+ }]]></component>
36
+ <component name="SharedIndexes">
37
+ <attachedChunks>
38
+ <set>
39
+ <option value="bundled-python-sdk-09665e90c3a7-d3b881c8e49f-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-233.15026.15" />
40
+ </set>
41
+ </attachedChunks>
42
+ </component>
43
+ <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
44
+ <component name="TaskManager">
45
+ <task active="true" id="Default" summary="Default task">
46
+ <changelist id="80cad30e-20ea-4a3e-a71a-c380b9fb453b" name="Changes" comment="" />
47
+ <created>1745540015058</created>
48
+ <option name="number" value="Default" />
49
+ <option name="presentableId" value="Default" />
50
+ <updated>1745540015058</updated>
51
+ </task>
52
+ <task id="LOCAL-00001" summary="добавил gunicorn">
53
+ <option name="closed" value="true" />
54
+ <created>1746432339386</created>
55
+ <option name="number" value="00001" />
56
+ <option name="presentableId" value="LOCAL-00001" />
57
+ <option name="project" value="LOCAL" />
58
+ <updated>1746432339386</updated>
59
+ </task>
60
+ <task id="LOCAL-00002" summary="добавил gunicorn">
61
+ <option name="closed" value="true" />
62
+ <created>1746433287813</created>
63
+ <option name="number" value="00002" />
64
+ <option name="presentableId" value="LOCAL-00002" />
65
+ <option name="project" value="LOCAL" />
66
+ <updated>1746433287813</updated>
67
+ </task>
68
+ <option name="localTasksCounter" value="3" />
69
+ <servers />
70
+ </component>
71
+ <component name="VcsManagerConfiguration">
72
+ <MESSAGE value="добавил gunicorn" />
73
+ <option name="LAST_COMMIT_MESSAGE" value="добавил gunicorn" />
74
+ </component>
75
+ </project>
READme.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Проблема с NumPy
2
+ Переустановите NumPy и PyTorch:
3
+
4
+ pip uninstall numpy torch -y
5
+ pip install numpy torch --upgrade
6
+
7
+ Конфликт версий PyTorch и NumPy
8
+ Установите совместимые версии (например, для CPU):
9
+
10
+ pip install numpy==1.23.5 torch==2.0.1 --upgrade
11
+
12
+ Проблема с путями или кэшем
13
+ Очистите кэш pip и переустановите зависимости:
14
+
15
+ pip cache purge
16
+ pip install -r requirements.txt --force-reinstall
__pycache__/app.cpython-311.pyc ADDED
Binary file (9.85 kB). View file
 
app.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, render_template, session
2
+ from transformers import pipeline
3
+ import torch
4
+ import torchaudio
5
+ from pydub import AudioSegment
6
+ import os
7
+ import io
8
+ import uuid
9
+ from datetime import datetime
10
+ import sqlite3
11
+ from pathlib import Path
12
+ import whisper # Добавлена библиотека для преобразования речи в текст
13
+
14
+ app = Flask(__name__)
15
+ app.secret_key = 'your-very-secret-key-12345'
16
+
17
+
18
+ # Инициализация БД
19
+ def get_db_connection():
20
+ instance_path = Path('instance')
21
+ instance_path.mkdir(exist_ok=True)
22
+ db_path = instance_path / 'chats.db'
23
+ conn = sqlite3.connect(str(db_path))
24
+ conn.row_factory = sqlite3.Row
25
+ return conn
26
+
27
+
28
+ def init_db():
29
+ conn = get_db_connection()
30
+ try:
31
+ conn.execute('''
32
+ CREATE TABLE IF NOT EXISTS chats (
33
+ chat_id TEXT PRIMARY KEY,
34
+ user_id TEXT,
35
+ created_at TEXT,
36
+ title TEXT
37
+ )
38
+ ''')
39
+ conn.execute('''
40
+ CREATE TABLE IF NOT EXISTS messages (
41
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
42
+ chat_id TEXT,
43
+ sender TEXT,
44
+ content TEXT,
45
+ timestamp TEXT,
46
+ FOREIGN KEY(chat_id) REFERENCES chats(chat_id)
47
+ )
48
+ ''')
49
+ conn.commit()
50
+ finally:
51
+ conn.close()
52
+
53
+
54
+ init_db()
55
+
56
+ # Модели для анализа эмоций
57
+ emotion_map = {
58
+ 'joy': '😊 Радость',
59
+ 'neutral': '😐 Нейтрально',
60
+ 'anger': '😠 Злость',
61
+ 'sadness': '😢 Грусть',
62
+ 'surprise': '😲 Удивление'
63
+ }
64
+
65
+ # Инициализация модели для преобразования речи в текст
66
+ try:
67
+ # Модель для преобразования речи в текст
68
+ speech_to_text_model = whisper.load_model("small") # Можно использовать 'base' для меньшего потребления памяти
69
+
70
+ # Модели для анализа эмоций
71
+ text_classifier = pipeline(
72
+ "text-classification",
73
+ model="cointegrated/rubert-tiny2-cedr-emotion-detection",
74
+ top_k=None
75
+ )
76
+ audio_classifier = pipeline(
77
+ "audio-classification",
78
+ model="superb/hubert-large-superb-er",
79
+ device=0 if torch.cuda.is_available() else -1
80
+ )
81
+ except Exception as e:
82
+ print(f"Ошибка загрузки моделей: {e}")
83
+ speech_to_text_model = None
84
+ text_classifier = None
85
+ audio_classifier = None
86
+
87
+
88
+ def transcribe_audio(audio_path):
89
+ """Преобразование аудио в текст с помощью Whisper"""
90
+ if not speech_to_text_model:
91
+ return None
92
+
93
+ try:
94
+ result = speech_to_text_model.transcribe(audio_path, language="ru")
95
+ return result["text"]
96
+ except Exception as e:
97
+ print(f"Ошибка преобразования аудио в текст: {e}")
98
+ return None
99
+
100
+
101
+ @app.route("/")
102
+ def index():
103
+ if 'user_id' not in session:
104
+ session['user_id'] = str(uuid.uuid4())
105
+
106
+ conn = get_db_connection()
107
+ try:
108
+ chats = conn.execute(
109
+ "SELECT chat_id, title FROM chats WHERE user_id = ? ORDER BY created_at DESC",
110
+ (session['user_id'],)
111
+ ).fetchall()
112
+ return render_template("index.html", chats=chats)
113
+ finally:
114
+ conn.close()
115
+
116
+
117
+ @app.route("/get_chats")
118
+ def get_chats():
119
+ if 'user_id' not in session:
120
+ return jsonify([])
121
+
122
+ conn = get_db_connection()
123
+ try:
124
+ chats = conn.execute(
125
+ "SELECT chat_id, title FROM chats WHERE user_id = ? ORDER BY created_at DESC",
126
+ (session['user_id'],)
127
+ ).fetchall()
128
+ return jsonify([dict(chat) for chat in chats])
129
+ finally:
130
+ conn.close()
131
+
132
+
133
+ @app.route("/start_chat", methods=["POST"])
134
+ def start_chat():
135
+ if 'user_id' not in session:
136
+ session['user_id'] = str(uuid.uuid4())
137
+
138
+ chat_id = str(uuid.uuid4())
139
+ title = "Новый чат " + datetime.now().strftime("%d.%m %H:%M")
140
+
141
+ conn = get_db_connection()
142
+ try:
143
+ conn.execute(
144
+ "INSERT INTO chats (chat_id, user_id, created_at, title) VALUES (?, ?, datetime('now'), ?)",
145
+ (chat_id, session['user_id'], title)
146
+ )
147
+ conn.commit()
148
+ return jsonify({"chat_id": chat_id, "title": title})
149
+ finally:
150
+ conn.close()
151
+
152
+
153
+ @app.route("/load_chat/<chat_id>", methods=["GET"])
154
+ def load_chat(chat_id):
155
+ conn = get_db_connection()
156
+ try:
157
+ chat_exists = conn.execute(
158
+ "SELECT 1 FROM chats WHERE chat_id = ?", (chat_id,)
159
+ ).fetchone()
160
+
161
+ if not chat_exists:
162
+ return jsonify({"error": "Chat not found"}), 404
163
+
164
+ messages = conn.execute(
165
+ "SELECT sender, content FROM messages WHERE chat_id = ? ORDER BY timestamp ASC",
166
+ (chat_id,)
167
+ ).fetchall()
168
+
169
+ title_row = conn.execute(
170
+ "SELECT title FROM chats WHERE chat_id = ?", (chat_id,)
171
+ ).fetchone()
172
+
173
+ return jsonify({
174
+ "messages": [dict(msg) for msg in messages],
175
+ "title": title_row['title'] if title_row else "Без названия"
176
+ })
177
+ except Exception as e:
178
+ return jsonify({"error": str(e)}), 500
179
+ finally:
180
+ conn.close()
181
+
182
+
183
+ @app.route("/save_message", methods=["POST"])
184
+ def save_message():
185
+ data = request.get_json()
186
+ if not all([data.get("chat_id"), data.get("sender"), data.get("content")]):
187
+ return jsonify({"error": "Missing parameters"}), 400
188
+
189
+ conn = get_db_connection()
190
+ try:
191
+ conn.execute(
192
+ "INSERT INTO messages (chat_id, sender, content, timestamp) VALUES (?, ?, ?, datetime('now'))",
193
+ (data['chat_id'], data['sender'], data['content'])
194
+ )
195
+ conn.commit()
196
+ return jsonify({"status": "success"})
197
+ except Exception as e:
198
+ return jsonify({"error": str(e)}), 500
199
+ finally:
200
+ conn.close()
201
+
202
+
203
+ @app.route("/analyze", methods=["POST"])
204
+ def analyze_text():
205
+ if not text_classifier:
206
+ return jsonify({"error": "Model not loaded"}), 500
207
+
208
+ text = request.get_json().get("text", "").strip()
209
+ if not text:
210
+ return jsonify({"error": "Empty text"}), 400
211
+
212
+ try:
213
+ predictions = text_classifier(text)[0]
214
+ top_prediction = max(predictions, key=lambda x: x["score"])
215
+ return jsonify({
216
+ "emotion": emotion_map.get(top_prediction["label"], "❓ Неизвестно"),
217
+ "confidence": round(top_prediction["score"], 2)
218
+ })
219
+ except Exception as e:
220
+ return jsonify({"error": str(e)}), 500
221
+
222
+
223
+ @app.route('/analyze_audio', methods=['POST'])
224
+ def analyze_audio():
225
+ if not audio_classifier or not speech_to_text_model:
226
+ return jsonify({"error": "Model not loaded"}), 500
227
+
228
+ if 'audio' not in request.files:
229
+ return jsonify({'error': 'No audio file'}), 400
230
+
231
+ try:
232
+ audio_file = request.files['audio']
233
+ temp_path = "temp_audio.wav"
234
+
235
+ audio = AudioSegment.from_file(io.BytesIO(audio_file.read()))
236
+ audio = audio.set_frame_rate(16000).set_channels(1)
237
+ audio.export(temp_path, format="wav", codec="pcm_s16le")
238
+
239
+ # Преобразование аудио в текст
240
+ transcribed_text = transcribe_audio(temp_path)
241
+
242
+ # Анализ эмоций в аудио
243
+ result = audio_classifier(temp_path)
244
+ os.remove(temp_path)
245
+
246
+ emotion_mapping = {
247
+ 'hap': 'happy',
248
+ 'sad': 'sad',
249
+ 'neu': 'neutral',
250
+ 'ang': 'angry'
251
+ }
252
+ emotions = {emotion_mapping.get(item['label'].lower(), 'neutral'): item['score']
253
+ for item in result if item['label'].lower() in emotion_mapping}
254
+
255
+ dominant_emotion = max(emotions.items(), key=lambda x: x[1])
256
+ response_map = {
257
+ 'happy': '😊 Радость',
258
+ 'sad': '😢 Грусть',
259
+ 'angry': '😠 Злость',
260
+ 'neutral': '😐 Нейтрально'
261
+ }
262
+
263
+ return jsonify({
264
+ 'emotion': response_map.get(dominant_emotion[0], 'неизвестно'),
265
+ 'confidence': round(dominant_emotion[1], 2),
266
+ 'transcribed_text': transcribed_text if transcribed_text else "Не удалось распознать текст"
267
+ })
268
+ except Exception as e:
269
+ return jsonify({'error': str(e)}), 500
270
+
271
+
272
+ if __name__ == "__main__":
273
+ app.run(debug=True)
instance/chats.db ADDED
Binary file (20.5 kB). View file
 
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ flask>=2.0.0
2
+ transformers>=4.15.0
3
+ torch>=1.9.0
4
+ torchaudio>=0.9.0
5
+ pydub>=0.25.1
6
+ librosa>=0.8.0
7
+ numpy>=1.21.0
8
+ gunicorn
9
+ openai-whisper
static/script.js ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener("DOMContentLoaded", () => {
2
+ let mediaRecorder, audioChunks = [], audioStream, currentChatId = null;
3
+ const recordBtn = document.getElementById("record-btn");
4
+ const stopBtn = document.getElementById("stop-btn");
5
+ const sendBtn = document.getElementById("send-btn");
6
+ const uploadBtn = document.getElementById("upload-btn");
7
+ const userInput = document.getElementById("user-input");
8
+ const chatBox = document.getElementById("chat-box");
9
+ const audioFileInput = document.getElementById("audio-file");
10
+ const newChatBtn = document.getElementById("new-chat-btn");
11
+ const chatList = document.getElementById("chat-list");
12
+ const currentChatTitle = document.getElementById("current-chat-title");
13
+
14
+ // Инициализация при загрузке
15
+ initializeChats();
16
+
17
+ function initializeChats() {
18
+ const savedChatId = localStorage.getItem('currentChatId');
19
+
20
+ fetch("/get_chats")
21
+ .then(response => response.json())
22
+ .then(chats => {
23
+ renderChatList(chats);
24
+
25
+ if (savedChatId && chats.some(c => c.chat_id === savedChatId)) {
26
+ loadChat(savedChatId);
27
+ } else if (chats.length > 0) {
28
+ loadChat(chats[0].chat_id);
29
+ } else {
30
+ startNewChat();
31
+ }
32
+ })
33
+ .catch(error => {
34
+ console.error("Ошибка загрузки чатов:", error);
35
+ startNewChat();
36
+ });
37
+ }
38
+
39
+ function renderChatList(chats) {
40
+ chatList.innerHTML = '';
41
+ chats.forEach(chat => {
42
+ const chatItem = document.createElement("div");
43
+ chatItem.className = "chat-item";
44
+ chatItem.dataset.chatId = chat.chat_id;
45
+ chatItem.innerHTML = `
46
+ <i class="fas fa-comment"></i>
47
+ <span class="chat-title">${chat.title}</span>
48
+ `;
49
+ chatItem.addEventListener("click", () => {
50
+ loadChat(chat.chat_id);
51
+ localStorage.setItem('currentChatId', chat.chat_id);
52
+ });
53
+ chatList.appendChild(chatItem);
54
+ });
55
+ }
56
+
57
+ newChatBtn.addEventListener("click", startNewChat);
58
+
59
+ function startNewChat() {
60
+ fetch("/start_chat", {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ })
64
+ .then(response => response.json())
65
+ .then(data => {
66
+ currentChatId = data.chat_id;
67
+ currentChatTitle.textContent = data.title;
68
+ chatBox.innerHTML = '<div class="message bot-message">Привет! Отправьте текст или голосовое сообщение для анализа эмоций.</div>';
69
+ initializeChats(); // Обновляем список чатов
70
+ localStorage.setItem('currentChatId', data.chat_id);
71
+ })
72
+ .catch(console.error);
73
+ }
74
+
75
+ function loadChat(chatId) {
76
+ fetch(`/load_chat/${chatId}`)
77
+ .then(response => response.json())
78
+ .then(data => {
79
+ if (data.error) throw new Error(data.error);
80
+
81
+ currentChatId = chatId;
82
+ currentChatTitle.textContent = data.title;
83
+ updateActiveChat(chatId);
84
+
85
+ chatBox.innerHTML = "";
86
+ data.messages.forEach(msg => {
87
+ appendMessage(msg.sender, msg.content);
88
+ });
89
+
90
+ localStorage.setItem('currentChatId', chatId);
91
+ })
92
+ .catch(error => {
93
+ console.error("Ошибка загрузки чата:", error);
94
+ appendMessage("bot", `❌ Ошибка: ${error.message}`);
95
+ });
96
+ }
97
+
98
+ function updateActiveChat(chatId) {
99
+ document.querySelectorAll(".chat-item").forEach(item => {
100
+ item.classList.toggle("active", item.dataset.chatId === chatId);
101
+ });
102
+ }
103
+
104
+ sendBtn.addEventListener("click", sendMessage);
105
+
106
+ async function sendMessage() {
107
+ const text = userInput.value.trim();
108
+ if (!text || !currentChatId) return;
109
+
110
+ appendAndSaveMessage("user", text);
111
+ userInput.value = "";
112
+
113
+ try {
114
+ const response = await fetch("/analyze", {
115
+ method: "POST",
116
+ headers: { "Content-Type": "application/json" },
117
+ body: JSON.stringify({ text })
118
+ });
119
+ const data = await response.json();
120
+ appendAndSaveMessage("bot", `Эмоция: ${data.emotion} (${(data.confidence * 100).toFixed(1)}%)`);
121
+ } catch (error) {
122
+ console.error("Ошибка:", error);
123
+ appendAndSaveMessage("bot", `❌ Ошибка: ${error.message}`);
124
+ }
125
+ }
126
+
127
+ uploadBtn.addEventListener("click", async () => {
128
+ const file = audioFileInput.files[0];
129
+ if (!file || !currentChatId) return;
130
+
131
+ appendAndSaveMessage("user", "Загружен аудиофайл...");
132
+
133
+ try {
134
+ const formData = new FormData();
135
+ formData.append("audio", file);
136
+
137
+ const response = await fetch("/analyze_audio", {
138
+ method: "POST",
139
+ body: formData
140
+ });
141
+ const data = await response.json();
142
+
143
+ // Добавляем распознанный текст в чат
144
+ if (data.transcribed_text) {
145
+ appendAndSaveMessage("user", `Распознанный текст: ${data.transcribed_text}`);
146
+ }
147
+
148
+ appendAndSaveMessage("bot", `Эмоция: ${data.emotion} (${(data.confidence * 100).toFixed(1)}%)`);
149
+ } catch (error) {
150
+ console.error("Ошибка:", error);
151
+ appendAndSaveMessage("bot", `❌ Ошибка: ${error.message}`);
152
+ }
153
+ });
154
+
155
+ recordBtn.addEventListener("click", startRecording);
156
+ stopBtn.addEventListener("click", stopRecording);
157
+
158
+ async function startRecording() {
159
+ try {
160
+ audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
161
+ mediaRecorder = new MediaRecorder(audioStream);
162
+ audioChunks = [];
163
+
164
+ mediaRecorder.ondataavailable = e => audioChunks.push(e.data);
165
+ mediaRecorder.onstop = async () => {
166
+ try {
167
+ const audioBlob = new Blob(audioChunks, { type: "audio/wav" });
168
+ appendAndSaveMessage("user", "Отправлено голосовое сообщение...");
169
+
170
+ const formData = new FormData();
171
+ formData.append("audio", audioBlob, "recording.wav");
172
+
173
+ const response = await fetch("/analyze_audio", {
174
+ method: "POST",
175
+ body: formData
176
+ });
177
+ const data = await response.json();
178
+
179
+ // Добавляем распознанный текст в чат
180
+ if (data.transcribed_text) {
181
+ appendAndSaveMessage("user", `Распознанный текст: ${data.transcribed_text}`);
182
+ }
183
+
184
+ appendAndSaveMessage("bot", `Эмоция: ${data.emotion} (${(data.confidence * 100).toFixed(1)}%)`);
185
+ } catch (error) {
186
+ console.error("Ошибка:", error);
187
+ appendAndSaveMessage("bot", `❌ Ошибка: ${error.message}`);
188
+ } finally {
189
+ audioStream.getTracks().forEach(track => track.stop());
190
+ }
191
+ };
192
+
193
+ mediaRecorder.start();
194
+ recordBtn.disabled = true;
195
+ stopBtn.disabled = false;
196
+ } catch (error) {
197
+ console.error("Ошибка записи:", error);
198
+ appendMessage("bot", "❌ Не удалось получить доступ к микрофону");
199
+ }
200
+ }
201
+
202
+ function stopRecording() {
203
+ if (mediaRecorder?.state === "recording") {
204
+ mediaRecorder.stop();
205
+ recordBtn.disabled = false;
206
+ stopBtn.disabled = true;
207
+ }
208
+ }
209
+
210
+ function appendMessage(sender, text) {
211
+ const message = document.createElement("div");
212
+ message.className = `message ${sender}-message`;
213
+ message.innerHTML = text;
214
+ chatBox.appendChild(message);
215
+ chatBox.scrollTop = chatBox.scrollHeight;
216
+ }
217
+
218
+ function appendAndSaveMessage(sender, text) {
219
+ appendMessage(sender, text);
220
+
221
+ if (currentChatId) {
222
+ fetch("/save_message", {
223
+ method: "POST",
224
+ headers: { "Content-Type": "application/json" },
225
+ body: JSON.stringify({
226
+ chat_id: currentChatId,
227
+ sender: sender,
228
+ content: text
229
+ })
230
+ }).catch(console.error);
231
+ }
232
+ }
233
+ });
static/styles.css ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Основные стили (как у вас были) */
2
+ /* Основные стили */
3
+ body {
4
+ margin: 0;
5
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
6
+ background: linear-gradient(135deg, #1e1e2f, #2a2a40);
7
+ height: 100vh;
8
+ color: #fff;
9
+ display: flex;
10
+ }
11
+
12
+ /* Сайдбар */
13
+ .sidebar {
14
+ width: 250px;
15
+ background: #1a1a2e;
16
+ height: 100vh;
17
+ display: flex;
18
+ flex-direction: column;
19
+ border-right: 1px solid rgba(255, 255, 255, 0.1);
20
+ }
21
+
22
+ .sidebar-button {
23
+ padding: 12px 16px;
24
+ margin: 10px;
25
+ background: linear-gradient(90deg, #4a4ae8, #3b3b98);
26
+ color: white;
27
+ border: none;
28
+ border-radius: 8px;
29
+ cursor: pointer;
30
+ font-size: 0.9rem;
31
+ text-align: left;
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 8px;
35
+ }
36
+
37
+ .sidebar-button:hover {
38
+ background: linear-gradient(90deg, #5a5aff, #4b4bb8);
39
+ }
40
+
41
+ .chat-list {
42
+ flex: 1;
43
+ overflow-y: auto;
44
+ padding: 0 10px;
45
+ }
46
+
47
+ .chat-item {
48
+ padding: 12px;
49
+ border-radius: 8px;
50
+ margin-bottom: 5px;
51
+ cursor: pointer;
52
+ display: flex;
53
+ align-items: center;
54
+ gap: 10px;
55
+ font-size: 0.9rem;
56
+ }
57
+
58
+ .chat-item:hover {
59
+ background: rgba(255, 255, 255, 0.1);
60
+ }
61
+
62
+ .chat-item.active {
63
+ background: rgba(74, 74, 232, 0.2);
64
+ }
65
+
66
+ .chat-title {
67
+ white-space: nowrap;
68
+ overflow: hidden;
69
+ text-overflow: ellipsis;
70
+ }
71
+
72
+ /* Основное содержимое */
73
+ .main-content {
74
+ flex: 1;
75
+ display: flex;
76
+ justify-content: center;
77
+ align-items: center;
78
+ overflow-y: auto;
79
+ }
80
+
81
+ .chat-container {
82
+ width: 500px;
83
+ background: #2a2a40;
84
+ border-radius: 16px;
85
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
86
+ overflow: hidden;
87
+ display: flex;
88
+ flex-direction: column;
89
+ margin: 20px;
90
+ }
91
+
92
+ .chat-box {
93
+ flex: 1;
94
+ padding: 16px;
95
+ overflow-y: auto;
96
+ background: #1e1e2f;
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: 12px;
100
+ max-height: 400px;
101
+ }
102
+
103
+ .message {
104
+ max-width: 80%;
105
+ padding: 12px 16px;
106
+ border-radius: 16px;
107
+ font-size: 0.9rem;
108
+ line-height: 1.4;
109
+ animation: fadeIn 0.3s ease-in-out;
110
+ }
111
+
112
+ .user-message {
113
+ background: linear-gradient(90deg, #4a4ae8, #3b3b98);
114
+ color: white;
115
+ align-self: flex-end;
116
+ border-bottom-right-radius: 0;
117
+ }
118
+
119
+ .bot-message {
120
+ background: #333;
121
+ color: #fff;
122
+ align-self: flex-start;
123
+ border-bottom-left-radius: 0;
124
+ }
125
+
126
+ @keyframes fadeIn {
127
+ from { opacity: 0; transform: translateY(10px); }
128
+ to { opacity: 1; transform: translateY(0); }
129
+ }
130
+
131
+ .input-container {
132
+ padding: 12px;
133
+ background: #2a2a40;
134
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
135
+ }
136
+
137
+ .input-group {
138
+ display: flex;
139
+ gap: 8px;
140
+ }
141
+
142
+ .chat-input {
143
+ flex: 1;
144
+ padding: 12px;
145
+ border: 2px solid rgba(255, 255, 255, 0.2);
146
+ border-radius: 8px;
147
+ font-size: 0.9rem;
148
+ background: #1e1e2f;
149
+ color: #fff;
150
+ }
151
+
152
+ .chat-input:focus {
153
+ border-color: #4a4ae8;
154
+ outline: none;
155
+ }
156
+
157
+ .send-btn {
158
+ padding: 12px 20px;
159
+ background: linear-gradient(90deg, #4a4ae8, #3b3b98);
160
+ color: white;
161
+ border: none;
162
+ border-radius: 8px;
163
+ cursor: pointer;
164
+ font-size: 0.9rem;
165
+ }
166
+
167
+ /* Стили для аудио элементов */
168
+ .audio-controls {
169
+ display: flex;
170
+ align-items: center;
171
+ gap: 10px;
172
+ padding: 12px;
173
+ background: #2a2a40;
174
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
175
+ }
176
+
177
+ .audio-btn {
178
+ padding: 10px 15px;
179
+ background: linear-gradient(90deg, #4a4ae8, #3b3b98);
180
+ color: white;
181
+ border: none;
182
+ border-radius: 8px;
183
+ cursor: pointer;
184
+ }
185
+
186
+ .audio-btn:disabled {
187
+ background: #555;
188
+ cursor: not-allowed;
189
+ }
190
+
191
+ #waveform {
192
+ flex-grow: 1;
193
+ height: 60px;
194
+ background: #1e1e2f;
195
+ border-radius: 8px;
196
+ }
197
+
198
+ .file-upload {
199
+ display: flex;
200
+ gap: 10px;
201
+ align-items: center;
202
+ margin-top: 10px;
203
+ }
204
+
205
+ .file-upload input[type="file"] {
206
+ flex: 1;
207
+ }
208
+
209
+ /* Красивая кнопка выбора файла */
210
+ .custom-file-upload {
211
+ display: inline-block;
212
+ padding: 10px 16px;
213
+ background: linear-gradient(90deg, #4a4ae8, #3b3b98);
214
+ color: white;
215
+ font-size: 0.9rem;
216
+ border-radius: 8px;
217
+ cursor: pointer;
218
+ transition: background 0.3s ease;
219
+ }
220
+
221
+ .custom-file-upload:hover {
222
+ background: linear-gradient(90deg, #5a5aff, #4b4bb8);
223
+ }
224
+
225
+ /* Скрытие стандартного инпута */
226
+ input[type="file"] {
227
+ display: none;
228
+ }
templates/index.html ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ru">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Emotion Analyzer</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
9
+ </head>
10
+ <body>
11
+ <div class="sidebar">
12
+ <button id="new-chat-btn" class="sidebar-button">
13
+ <i class="fas fa-plus"></i> Новый чат
14
+ </button>
15
+ <div class="chat-list" id="chat-list">
16
+ {% for chat in chats %}
17
+ <div class="chat-item" data-chat-id="{{ chat.chat_id }}">
18
+ <i class="fas fa-comment"></i>
19
+ <span class="chat-title">{{ chat.title }}</span>
20
+ </div>
21
+ {% endfor %}
22
+ </div>
23
+ </div>
24
+
25
+ <div class="main-content">
26
+ <div class="chat-container">
27
+ <h1 class="chat-title" id="current-chat-title">Анализатор эмоций</h1>
28
+
29
+ <div id="chat-box" class="chat-box">
30
+ <div class="message bot-message">
31
+ Привет! Отправьте текст или голосовое сообщение для анализа эмоций.
32
+ </div>
33
+ </div>
34
+
35
+ <div class="audio-controls">
36
+ <button id="record-btn" class="audio-btn">
37
+ <i class="fas fa-microphone"></i> Запись
38
+ </button>
39
+ <button id="stop-btn" class="audio-btn" disabled>
40
+ <i class="fas fa-stop"></i> Стоп
41
+ </button>
42
+ </div>
43
+
44
+ <div class="input-container">
45
+ <div class="input-group">
46
+ <input type="text" id="user-input" class="chat-input" placeholder="Введите сообщение...">
47
+ <button id="send-btn" class="send-btn">Отправить</button>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="upload-container">
52
+ <label for="audio-file" class="upload-label">Или загрузите аудиофайл:</label>
53
+ <div class="file-upload">
54
+ <label for="audio-file" class="custom-file-upload">
55
+ <i class="fas fa-file-audio"></i> Выбрать файл
56
+ </label>
57
+ <input type="file" id="audio-file" accept="audio/*">
58
+ <button id="upload-btn" class="audio-btn">
59
+ <i class="fas fa-upload"></i> Загрузить
60
+ </button>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <script src="{{ url_for('static', filename='script.js') }}"></script>
67
+ </body>
68
+ </html>