A-yum1 commited on
Commit
4bc59e8
·
2 Parent(s): 793bb36 d7776bd

merge main and AyumiWork

Browse files
Files changed (2) hide show
  1. app.py +15 -2
  2. templates/index.html +185 -167
app.py CHANGED
@@ -1,6 +1,9 @@
1
  from flask import Flask, request, jsonify, render_template
2
  import base64
3
  import os
 
 
 
4
 
5
  app = Flask(__name__)
6
 
@@ -39,7 +42,7 @@ def upload_audio():
39
  persist_dir = "/tmp/data"
40
  os.makedirs(persist_dir, exist_ok=True)
41
 
42
- filepath = os.path.join(persist_dir, "recorded_audio.wav")
43
  with open(filepath, 'wb') as f:
44
  f.write(audio_binary)
45
 
@@ -49,6 +52,16 @@ def upload_audio():
49
  app.logger.error("エラー: %s", str(e))
50
  return jsonify({"error": "サーバー内部エラー", "details": str(e)}), 500
51
 
 
 
 
 
 
 
 
 
 
 
52
  if __name__ == '__main__':
53
  port = int(os.environ.get("PORT", 7860))
54
- app.run(debug=True, host="0.0.0.0", port=port)
 
1
  from flask import Flask, request, jsonify, render_template
2
  import base64
3
  import os
4
+ import string
5
+ import random
6
+ from datetime import datetime
7
 
8
  app = Flask(__name__)
9
 
 
42
  persist_dir = "/tmp/data"
43
  os.makedirs(persist_dir, exist_ok=True)
44
 
45
+ filepath = os.path.join(persist_dir, generate_filename(6)) #ここだけ変更しましたごめんなさい
46
  with open(filepath, 'wb') as f:
47
  f.write(audio_binary)
48
 
 
52
  app.logger.error("エラー: %s", str(e))
53
  return jsonify({"error": "サーバー内部エラー", "details": str(e)}), 500
54
 
55
+ def generate_random_string(length):
56
+ letters = string.ascii_letters + string.digits
57
+ return ''.join(random.choice(letters) for i in range(length))
58
+
59
+ def generate_filename(random_length):
60
+ random_string = generate_random_string(random_length)
61
+ current_time = datetime.now().strftime("%Y%m%d%H%M%S")
62
+ filename = f"{current_time}_{random_string}.wav"
63
+ return filename
64
+
65
  if __name__ == '__main__':
66
  port = int(os.environ.get("PORT", 7860))
67
+ app.run(debug=True, host="0.0.0.0", port=port)
templates/index.html CHANGED
@@ -1,177 +1,195 @@
1
  <!DOCTYPE html>
2
  <html lang="ja">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Voice Recorder Interface</title>
7
- <style>
8
- body {
9
- display: flex;
10
- flex-direction: column;
11
- justify-content: center;
12
- align-items: center;
13
- height: 100vh;
14
- margin: 0;
15
- background-color: #121212;
16
- color: white;
17
- }
18
- .chart {
19
- width: 300px;
20
- height: 300px;
21
- margin-bottom: 20px;
22
- }
23
- .record-button {
24
- position: fixed;
25
- bottom: 30px;
26
- width: 80px;
27
- height: 80px;
28
- background-color: transparent;
29
- border-radius: 50%;
30
- border: 4px solid white;
31
- display: flex;
32
- justify-content: center;
33
- align-items: center;
34
- cursor: pointer;
35
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
36
- transition: all 0.2s ease;
37
- }
38
- .record-icon {
39
- width: 60px;
40
- height: 60px;
41
- background-color: #d32f2f;
42
- border-radius: 50%;
43
- transition: all 0.2s ease;
44
- }
45
- .recording .record-icon {
46
- width: 40px;
47
- height: 40px;
48
- border-radius: 10%;
49
- }
50
- .result-button {
51
- margin-top: 20px;
52
- padding: 10px 20px;
53
- background-color: #4caf50;
54
- border: none;
55
- border-radius: 5px;
56
- color: white;
57
- cursor: pointer;
58
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
59
- }
60
- .result-button:hover {
61
- background-color: #388e3c;
62
- }
63
- .result {
64
- display: flex;
65
- }
66
- </style>
67
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
68
- </head>
69
- <body>
70
- <div class="chart">
71
- <canvas id="speechChart"></canvas>
72
- </div>
73
- <button class="record-button" id="recordButton" onclick="toggleRecording()">
74
- <div class="record-icon" id="recordIcon"></div>
75
- </button>
76
 
77
- <form method="POST" action="/feedback">
78
- <div class="result">
79
- <button class="result-button" id="resultButton" onclick="showHistory()">
80
- 会話履歴を表示
81
- </button>
82
- <button class="result-button" id="resultButton" onclick="showResults()">
83
- フィードバック画面を表示
84
- </button>
85
- </div>
86
- </form>
 
 
 
 
 
 
 
87
 
88
- <script>
89
- let isRecording = false;
90
- let mediaRecorder;
91
- let audioChunks = [];
92
- async function toggleRecording() {
93
- const recordButton = document.getElementById("recordButton");
94
- const recordIcon = document.getElementById("recordIcon");
95
- if (!isRecording) {
96
- // 録音開始
97
- isRecording = true;
98
- recordButton.classList.add("recording");
99
- try {
100
- const stream = await navigator.mediaDevices.getUserMedia({
101
- audio: true,
102
- });
103
- mediaRecorder = new MediaRecorder(stream);
 
 
 
104
  audioChunks = [];
105
- mediaRecorder.ondataavailable = (event) => {
106
- if (event.data.size > 0) {
107
- audioChunks.push(event.data);
108
- }
109
- };
110
- mediaRecorder.onstop = () => {
111
- const audioBlob = new Blob(audioChunks, { type: "audio/wav" });
112
- const reader = new FileReader();
113
- reader.onloadend = () => {
114
- const base64String = reader.result.split(",")[1]; // Base64 エンコードされた音声データ
115
- // サーバーへ音声データを送信
116
- fetch("/upload_audio", {
117
- method: "POST",
118
- headers: {
119
- "Content-Type": "application/json",
120
- },
121
- body: JSON.stringify({ audio_data: base64String }),
122
- })
123
- .then((response) => response.json())
124
- .then((data) => {
125
- alert("音声がバックエンドに送信されました。");
126
- })
127
- .catch((error) => {
128
- console.error("エラー:", error);
129
- });
130
- };
131
- reader.readAsDataURL(audioBlob);
132
- };
133
- mediaRecorder.start();
134
- } catch (error) {
135
- console.error("マイクへのアクセスに失敗しました:", error);
136
- isRecording = false;
137
- recordButton.classList.remove("recording");
138
- }
139
- } else {
140
- // 録音停止
141
  isRecording = false;
142
- recordButton.classList.remove("recording");
143
- if (mediaRecorder && mediaRecorder.state !== "inactive") {
144
- mediaRecorder.stop();
145
- }
 
 
 
 
 
146
  }
147
  }
 
148
 
149
- // Chart.js の初期化
150
- const ctx = document.getElementById("speechChart").getContext("2d");
151
- const speechChart = new Chart(ctx, {
152
- type: "doughnut",
153
- data: {
154
- labels: ["自分", "他の人"],
155
- datasets: [
156
- {
157
- data: [30, 70],
158
- backgroundColor: ["#4caf50", "#757575"],
159
- },
160
- ],
161
- },
162
- options: {
163
- responsive: true,
164
- plugins: {
165
- legend: {
166
- display: true,
167
- position: "bottom",
168
- labels: {
169
- color: "white",
170
- },
171
- },
172
  },
173
- },
174
- });
175
- </script>
176
- </body>
177
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <!DOCTYPE html>
2
  <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Voice Recorder Interface</title>
7
+ <style>
8
+ body {
9
+ display: flex;
10
+ flex-direction: column;
11
+ justify-content: center;
12
+ align-items: center;
13
+ height: 100vh;
14
+ margin: 0;
15
+ background-color: #121212;
16
+ color: white;
17
+ }
18
+ .chart {
19
+ width: 300px;
20
+ height: 300px;
21
+ margin-bottom: 20px;
22
+ }
23
+ .record-button {
24
+ position: fixed;
25
+ bottom: 30px;
26
+ width: 80px;
27
+ height: 80px;
28
+ background-color: transparent;
29
+ border-radius: 50%;
30
+ border: 4px solid white;
31
+ display: flex;
32
+ justify-content: center;
33
+ align-items: center;
34
+ cursor: pointer;
35
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
36
+ transition: all 0.2s ease;
37
+ }
38
+ .record-icon {
39
+ width: 60px;
40
+ height: 60px;
41
+ background-color: #d32f2f;
42
+ border-radius: 50%;
43
+ transition: all 0.2s ease;
44
+ }
45
+ .recording .record-icon {
46
+ width: 40px;
47
+ height: 40px;
48
+ border-radius: 10%;
49
+ }
50
+ .result-button {
51
+ margin-top: 20px;
52
+ padding: 10px 20px;
53
+ background-color: #4caf50;
54
+ border: none;
55
+ border-radius: 5px;
56
+ color: white;
57
+ cursor: pointer;
58
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
59
+ }
60
+ .result-button:hover {
61
+ background-color: #388e3c;
62
+ }
63
+ </style>
64
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
65
+ </head>
66
+ <body>
67
+ <div class="chart">
68
+ <canvas id="speechChart"></canvas>
69
+ </div>
70
+ <button class="record-button" id="recordButton" onclick="toggleRecording()">
71
+ <div class="record-icon" id="recordIcon"></div>
72
+ </button>
 
 
 
73
 
74
+ <form method="POST" action="/feedback">
75
+ <div class="result">
76
+ <button class="result-button" id="resultButton" onclick="showHistory()">
77
+ 会話履歴を表示
78
+ </button>
79
+ <button class="result-button" id="resultButton" onclick="showResults()">
80
+ フィードバック画面を表示
81
+ </button>
82
+ </div>
83
+ </form>
84
+
85
+ <script>
86
+ let isRecording = false;
87
+ let mediaRecorder;
88
+ let audioChunks = [];
89
+ let isRecordingStopped = false; //自動停止を管理する
90
+ let recordingInterval;
91
 
92
+ async function toggleRecording() {
93
+ const recordButton = document.getElementById('recordButton');
94
+ const recordIcon = document.getElementById('recordIcon');
95
+ if (!isRecording) {
96
+ // 録音開始
97
+ isRecording = true;
98
+ recordButton.classList.add('recording');
99
+ try {
100
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
101
+ mediaRecorder = new MediaRecorder(stream);
102
+ audioChunks = [];
103
+ previousAudioChunks = [];
104
+ mediaRecorder.ondataavailable = event => {
105
+ if (event.data.size > 0) {
106
+ audioChunks.push(event.data);
107
+ }
108
+ };
109
+ mediaRecorder.onstop = () => {
110
+ sendAudioChunks([...audioChunks]);
111
  audioChunks = [];
112
+ };
113
+
114
+ mediaRecorder.start();
115
+
116
+ recordingInterval = setInterval(() => {
117
+ if(isRecording && !isRecordingStopped){
118
+ isRecordingStopped = true;
119
+ mediaRecorder.stop();
120
+ }
121
+ },10000);
122
+ } catch (error) {
123
+ console.error('マイクへのアクセスに失敗しました:', error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  isRecording = false;
125
+ recordButton.classList.remove('recording');
126
+ }
127
+ } else {
128
+ // 録音停止
129
+ isRecording = false;
130
+ recordButton.classList.remove('recording');
131
+ clearInterval(recordingInterval);
132
+ if (mediaRecorder && mediaRecorder.state !== 'inactive') {
133
+ mediaRecorder.stop();
134
  }
135
  }
136
+ }
137
 
138
+ function sendAudioChunks(chunks){
139
+ const audioBlob = new Blob(chunks, { type: 'audio/wav' });
140
+ const reader = new FileReader();
141
+ reader.onloadend = () => {
142
+ const base64String = reader.result.split(',')[1]; // Base64 エンコードされた音声データ
143
+ // サーバーへ音声データを送信
144
+ fetch('/upload_audio', {
145
+ method: 'POST',
146
+ headers: {
147
+ 'Content-Type': 'application/json',
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  },
149
+ body: JSON.stringify({ audio_data: base64String }),
150
+ })
151
+ .then(response => response.json())
152
+ .then(data => {
153
+ alert('音声がバックエンドに送信されました。');
154
+ })
155
+ .catch(error => {
156
+ console.error('エラー:', error);
157
+ });
158
+ };
159
+ reader.readAsDataURL(audioBlob);
160
+
161
+ isRecordingStopped = false;
162
+ mediaRecorder.start();
163
+ }
164
+
165
+ function showResults() {
166
+ window.location.href = 'feedback.html';
167
+ }
168
+
169
+ // Chart.js の初期化
170
+ const ctx = document.getElementById('speechChart').getContext('2d');
171
+ const speechChart = new Chart(ctx, {
172
+ type: 'doughnut',
173
+ data: {
174
+ labels: ['自分', '他の人'],
175
+ datasets: [{
176
+ data: [30, 70],
177
+ backgroundColor: ['#4caf50', '#757575'],
178
+ }],
179
+ },
180
+ options: {
181
+ responsive: true,
182
+ plugins: {
183
+ legend: {
184
+ display: true,
185
+ position: 'bottom',
186
+ labels: {
187
+ color: 'white'
188
+ }
189
+ }
190
+ }
191
+ }
192
+ });
193
+ </script>
194
+ </body>
195
+ </html>