buletomato25 commited on
Commit
093dde1
·
1 Parent(s): 7e8400a
__pycache__/new_record.cpython-310.pyc ADDED
Binary file (1.4 kB). View file
 
app.py CHANGED
@@ -14,6 +14,7 @@ from dotenv import load_dotenv
14
  from google.oauth2 import id_token
15
  from google_auth_oauthlib.flow import Flow
16
  from google.auth.transport import requests as google_requests
 
17
 
18
 
19
  # Hugging Face のトークン取得(環境変数 HF に設定)
@@ -39,7 +40,9 @@ app.config['SECRET_KEY'] = os.urandom(24)
39
  os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
40
  GOOGLE_CLIENT_ID = "228160683186-6u7986qsfhcv3kd9iqtv08iphpl4gdk2.apps.googleusercontent.com"
41
  GOOGLE_CLIENT_SECRET = "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"
 
42
  #REDIRECT_URI = "https://huggingface.co/spaces/Justtalk/JusTalk/callback"
 
43
  REDIRECT_URI = "http://127.0.0.1:7860/callback"
44
 
45
  flow = Flow.from_client_secrets_file(
@@ -105,10 +108,12 @@ def generate_filename(random_length):
105
  filename = f"{current_time}_{random_string}.wav"
106
  return filename
107
 
 
 
108
  # トップページ(テンプレート: index.html)
109
  @app.route('/')
110
  def top():
111
- return redirect('login')
112
 
113
  # ログイン画面(テンプレート: login.html)
114
  @app.route('/login')
@@ -123,7 +128,12 @@ def login():
123
  def callback():
124
  flow.fetch_token(authorization_response=request.url)
125
 
126
- if not session['state'] == request.args['state']:
 
 
 
 
 
127
  return 'State mismatch error', 400
128
 
129
  credentials = flow.credentials
@@ -137,40 +147,48 @@ def callback():
137
  session['email'] = id_info.get("email")
138
  session['name'] = id_info.get("name")
139
 
140
- return redirect(url_for('index'))
 
141
 
142
  # フィードバック画面(テンプレート: feedback.html)
143
  @app.route('/feedback', methods=['GET', 'POST'])
144
  def feedback():
 
 
145
  if 'google_id' not in session:
146
  return redirect(url_for('login'))
147
  user_info = {
148
  'name': session.get('name'),
149
  'email': session.get('email')
150
  }
151
- return render_template('feedback.html', user=user_info)
 
152
 
153
  # 会話詳細画面(テンプレート: talkDetail.html)
154
  @app.route('/talk_detail', methods=['GET', 'POST'])
155
  def talk_detail():
 
156
  if 'google_id' not in session:
157
  return redirect(url_for('login'))
158
  user_info = {
159
  'name': session.get('name'),
160
  'email': session.get('email')
161
  }
162
- return render_template('talkDetail.html', user=user_info)
 
163
 
164
  # インデックス画面(テンプレート: index.html)
165
  @app.route('/index', methods=['GET', 'POST'])
166
  def index():
 
167
  if 'google_id' not in session:
168
  return redirect(url_for('login'))
169
  user_info = {
170
  'name': session.get('name'),
171
  'email': session.get('email')
172
  }
173
- return render_template('index.html', user=user_info)
 
174
 
175
  # 登録画面(テンプレート: new_person.html)
176
  @app.route('/new_person', methods=['GET', 'POST'])
 
14
  from google.oauth2 import id_token
15
  from google_auth_oauthlib.flow import Flow
16
  from google.auth.transport import requests as google_requests
17
+ from new_record import record_bp
18
 
19
 
20
  # Hugging Face のトークン取得(環境変数 HF に設定)
 
40
  os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
41
  GOOGLE_CLIENT_ID = "228160683186-6u7986qsfhcv3kd9iqtv08iphpl4gdk2.apps.googleusercontent.com"
42
  GOOGLE_CLIENT_SECRET = "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"
43
+ #HFにpushするときは下記のコメントアウトを外してください
44
  #REDIRECT_URI = "https://huggingface.co/spaces/Justtalk/JusTalk/callback"
45
+ #ローカルの時はこちら
46
  REDIRECT_URI = "http://127.0.0.1:7860/callback"
47
 
48
  flow = Flow.from_client_secrets_file(
 
108
  filename = f"{current_time}_{random_string}.wav"
109
  return filename
110
 
111
+ app.register_blueprint(record_bp)
112
+
113
  # トップページ(テンプレート: index.html)
114
  @app.route('/')
115
  def top():
116
+ return redirect('index')
117
 
118
  # ログイン画面(テンプレート: login.html)
119
  @app.route('/login')
 
128
  def callback():
129
  flow.fetch_token(authorization_response=request.url)
130
 
131
+ # `session.get('state')` を使用し、エラーを防ぐ
132
+ session_state = session.get('state')
133
+ request_state = request.args.get('state')
134
+
135
+ if session_state is None or session_state != request_state:
136
+ print(f"State mismatch error: session_state={session_state}, request_state={request_state}")
137
  return 'State mismatch error', 400
138
 
139
  credentials = flow.credentials
 
147
  session['email'] = id_info.get("email")
148
  session['name'] = id_info.get("name")
149
 
150
+ return redirect(url_for('new_person'))
151
+
152
 
153
  # フィードバック画面(テンプレート: feedback.html)
154
  @app.route('/feedback', methods=['GET', 'POST'])
155
  def feedback():
156
+ #ログイン問題解決しだい戻す
157
+ """
158
  if 'google_id' not in session:
159
  return redirect(url_for('login'))
160
  user_info = {
161
  'name': session.get('name'),
162
  'email': session.get('email')
163
  }
164
+ """
165
+ return render_template('feedback.html')
166
 
167
  # 会話詳細画面(テンプレート: talkDetail.html)
168
  @app.route('/talk_detail', methods=['GET', 'POST'])
169
  def talk_detail():
170
+ """
171
  if 'google_id' not in session:
172
  return redirect(url_for('login'))
173
  user_info = {
174
  'name': session.get('name'),
175
  'email': session.get('email')
176
  }
177
+ """
178
+ return render_template('talkDetail.html')
179
 
180
  # インデックス画面(テンプレート: index.html)
181
  @app.route('/index', methods=['GET', 'POST'])
182
  def index():
183
+ """
184
  if 'google_id' not in session:
185
  return redirect(url_for('login'))
186
  user_info = {
187
  'name': session.get('name'),
188
  'email': session.get('email')
189
  }
190
+ """
191
+ return render_template('index.html')
192
 
193
  # 登録画面(テンプレート: new_person.html)
194
  @app.route('/new_person', methods=['GET', 'POST'])
new_record.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Blueprint, request, jsonify
2
+ import os
3
+ import base64
4
+ from pydub import AudioSegment
5
+
6
+ record_bp = Blueprint('record', __name__)
7
+
8
+ # 録音データの保存先ディレクトリ
9
+ record_data_dir = "record_data"
10
+ os.makedirs(record_data_dir, exist_ok=True)
11
+
12
+ @record_bp.route('/upload_audio', methods=['POST'])
13
+ def upload_audio():
14
+ try:
15
+ data = request.get_json()
16
+ if not data or 'audio_data' not in data or 'user_name' not in data:
17
+ return jsonify({"error": "音声データまたはユーザー名がありません"}), 400
18
+
19
+ user_name = data['user_name'].replace(" ", "_") # 空白をアンダースコアに変換
20
+ audio_binary = base64.b64decode(data['audio_data'])
21
+
22
+ # 保存先のファイルパス
23
+ audio_path = os.path.join(record_data_dir, f"{user_name}.wav")
24
+
25
+ # 一時ファイルとして保存
26
+ temp_audio_path = os.path.join(record_data_dir, "temp_audio")
27
+ with open(temp_audio_path, 'wb') as f:
28
+ f.write(audio_binary)
29
+
30
+ # pydub を使って WAV に変換
31
+ try:
32
+ audio = AudioSegment.from_file(temp_audio_path, format="webm")
33
+ except Exception:
34
+ audio = AudioSegment.from_file(temp_audio_path)
35
+
36
+ audio.export(audio_path, format="wav")
37
+ os.remove(temp_audio_path)
38
+
39
+ return jsonify({"success": True, "message": f"音声が {audio_path} に保存されました"}), 200
40
+
41
+ except Exception as e:
42
+ print("Error in /upload_audio:", str(e))
43
+ return jsonify({"error": "サーバーエラー", "details": str(e)}), 500
templates/feedback.html CHANGED
@@ -115,7 +115,6 @@
115
  </script>
116
  </head>
117
  <body>
118
- <h1 class="mb-4">ようこそ, {{ user.name }} さん!</h1>
119
  <div class="card">
120
  <div class="level" id="level">話者Lv: 85</div>
121
  <div class="message" id="message">素晴らしい</div>
 
115
  </script>
116
  </head>
117
  <body>
 
118
  <div class="card">
119
  <div class="level" id="level">話者Lv: 85</div>
120
  <div class="message" id="message">素晴らしい</div>
templates/history.html CHANGED
@@ -109,7 +109,7 @@
109
  </head>
110
  <body>
111
  <header>All Recordings</header>
112
- <h1>ようこそ, {{ user.name }} さん!</h1>
113
  <div class="recording-list">
114
  <div class="record-item record-item-template">
115
  <div>
 
109
  </head>
110
  <body>
111
  <header>All Recordings</header>
112
+
113
  <div class="recording-list">
114
  <div class="record-item record-item-template">
115
  <div>
templates/index.html CHANGED
@@ -121,11 +121,22 @@
121
  .result-button:hover {
122
  background-color: #388e3c;
123
  }
 
 
 
124
  </style>
125
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
126
  </head>
127
  <body>
128
- <h1>ようこそ, {{ user.name }} さん!</h1>
 
 
 
 
 
 
 
 
129
  <!-- トグルスイッチ:基準音声保存モード -->
130
  <div class="toggle-container">
131
  <span class="toggle-label">基準音声を保存</span>
@@ -352,6 +363,11 @@
352
  // フィードバック画面へ遷移
353
  window.location.href = "feedback";
354
  }
 
 
 
 
 
355
  </script>
356
  </body>
357
  </html>
 
121
  .result-button:hover {
122
  background-color: #388e3c;
123
  }
124
+ header {
125
+ display: flex;
126
+ }
127
  </style>
128
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
129
  </head>
130
  <body>
131
+ <header>
132
+ <div></div>
133
+ <div>
134
+ <button class="result-button" id="historyButton" onclick="showLogin()">
135
+ ログイン
136
+ </button>
137
+ </div>
138
+ </header>
139
+
140
  <!-- トグルスイッチ:基準音声保存モード -->
141
  <div class="toggle-container">
142
  <span class="toggle-label">基準音声を保存</span>
 
363
  // フィードバック画面へ遷移
364
  window.location.href = "feedback";
365
  }
366
+
367
+ function showLogin() {
368
+ // フィードバック画面へ遷移
369
+ window.location.href = "login";
370
+ }
371
  </script>
372
  </body>
373
  </html>
templates/new_person.html CHANGED
@@ -188,16 +188,14 @@
188
  let audioChunks = [];
189
  let countdownTimer;
190
  let isAudioRecorded = false;
191
-
192
  async function toggleRecording() {
193
  const recordButton = document.getElementById("recordButton");
194
-
195
  const countdownDisplay = document.getElementById("countdownDisplay");
196
 
197
  recordButton.disabled = true; // ボタンを無効化(連打防止)
198
  countdownDisplay.textContent = "録音中…"; // 初期表示
199
  isAudioRecorded = false;
200
- recordIcon.classList.add("recording"); // アイコンを正方形に変更
201
 
202
  try {
203
  const stream = await navigator.mediaDevices.getUserMedia({
@@ -215,17 +213,15 @@
215
  mediaRecorder.onstop = () => {
216
  sendAudioChunks([...audioChunks]); // 録音データをサーバーに送信
217
  audioChunks = [];
218
- recordButton.disabled = false; // ボタンを再度有効化
219
- countdownDisplay.textContent = ""; // カウントダウンを消す
220
- isAudioRecorded = true; // 録音完了
221
- recordIcon.classList.remove("recording"); // アイコンを元の丸に戻す
222
- validateForm(); // フォームの状態を更新
223
  };
224
 
225
  mediaRecorder.start();
226
- startCountdown(10, countdownDisplay); // 10秒のカウントダウン開始
227
 
228
- // 10秒後に録音を自動停止
229
  setTimeout(() => {
230
  if (mediaRecorder.state === "recording") {
231
  mediaRecorder.stop();
@@ -233,9 +229,8 @@
233
  }, 10000);
234
  } catch (error) {
235
  console.error("マイクのアクセスに失敗しました:", error);
236
- recordButton.disabled = false; // ボタンを再度有効化
237
- countdownDisplay.textContent = ""; // カウントダウンを消す
238
- recordIcon.classList.remove("recording"); // アイコンを元の丸に戻す
239
  }
240
  }
241
 
@@ -248,29 +243,36 @@
248
  displayElement.textContent = `録音終了まで: ${remaining}秒`;
249
 
250
  if (remaining <= 0) {
251
- clearInterval(countdownTimer); // カウントダウン終了
252
  }
253
  }, 1000);
254
  }
255
 
256
  function sendAudioChunks(chunks) {
 
 
 
 
 
 
257
  const audioBlob = new Blob(chunks, { type: "audio/wav" });
258
  const reader = new FileReader();
259
  reader.onloadend = () => {
260
  const base64String = reader.result.split(",")[1]; // Base64 に変換
261
  fetch("/upload_audio", {
262
  method: "POST",
263
- headers: {
264
- "Content-Type": "application/json",
265
- },
266
- body: JSON.stringify({ audio_data: base64String }),
 
267
  })
268
  .then((response) => response.json())
269
  .then((data) => {
270
  if (data.success) {
271
  alert("音声がサーバーに送信されました。");
272
- isAudioRecorded = true; // サーバーからの成功レスポンスを受け取った後に設定
273
- validateForm(); // フォームの状態を更新
274
  } else {
275
  alert("音声の送信に失敗しました。");
276
  }
@@ -283,17 +285,9 @@
283
  reader.readAsDataURL(audioBlob);
284
  }
285
 
286
- // フォームの入力状態をチェック
287
- function validateForm() {
288
- const submitButton = document.getElementById("submit");
289
-
290
- if (isAudioRecorded) {
291
- submitButton.classList.remove("disabled");
292
- submitButton.disabled = false;
293
- } else {
294
- submitButton.classList.add("disabled");
295
- submitButton.disabled = true;
296
- }
297
  }
298
 
299
  // 初期化処理
 
188
  let audioChunks = [];
189
  let countdownTimer;
190
  let isAudioRecorded = false;
191
+ sessionStorage.setItem("userName", "{{ user.name }}");
192
  async function toggleRecording() {
193
  const recordButton = document.getElementById("recordButton");
 
194
  const countdownDisplay = document.getElementById("countdownDisplay");
195
 
196
  recordButton.disabled = true; // ボタンを無効化(連打防止)
197
  countdownDisplay.textContent = "録音中…"; // 初期表示
198
  isAudioRecorded = false;
 
199
 
200
  try {
201
  const stream = await navigator.mediaDevices.getUserMedia({
 
213
  mediaRecorder.onstop = () => {
214
  sendAudioChunks([...audioChunks]); // 録音データをサーバーに送信
215
  audioChunks = [];
216
+ recordButton.disabled = false;
217
+ countdownDisplay.textContent = "";
218
+ isAudioRecorded = true;
219
+ validateForm();
 
220
  };
221
 
222
  mediaRecorder.start();
223
+ startCountdown(10, countdownDisplay);
224
 
 
225
  setTimeout(() => {
226
  if (mediaRecorder.state === "recording") {
227
  mediaRecorder.stop();
 
229
  }, 10000);
230
  } catch (error) {
231
  console.error("マイクのアクセスに失敗しました:", error);
232
+ recordButton.disabled = false;
233
+ countdownDisplay.textContent = "";
 
234
  }
235
  }
236
 
 
243
  displayElement.textContent = `録音終了まで: ${remaining}秒`;
244
 
245
  if (remaining <= 0) {
246
+ clearInterval(countdownTimer);
247
  }
248
  }, 1000);
249
  }
250
 
251
  function sendAudioChunks(chunks) {
252
+ const userName = sessionStorage.getItem("userName"); // ユーザー名を取得
253
+ if (!userName) {
254
+ alert("ユーザー名が取得できません。ログインしてください。");
255
+ return;
256
+ }
257
+
258
  const audioBlob = new Blob(chunks, { type: "audio/wav" });
259
  const reader = new FileReader();
260
  reader.onloadend = () => {
261
  const base64String = reader.result.split(",")[1]; // Base64 に変換
262
  fetch("/upload_audio", {
263
  method: "POST",
264
+ headers: { "Content-Type": "application/json" },
265
+ body: JSON.stringify({
266
+ audio_data: base64String,
267
+ user_name: userName,
268
+ }),
269
  })
270
  .then((response) => response.json())
271
  .then((data) => {
272
  if (data.success) {
273
  alert("音声がサーバーに送信されました。");
274
+ isAudioRecorded = true;
275
+ validateForm();
276
  } else {
277
  alert("音声の送信に失敗しました。");
278
  }
 
285
  reader.readAsDataURL(audioBlob);
286
  }
287
 
288
+ // ユーザー名をセッションに保存(ログイン後に実行)
289
+ function setUserName(userName) {
290
+ sessionStorage.setItem("userName", userName);
 
 
 
 
 
 
 
 
291
  }
292
 
293
  // 初期化処理
templates/talkDetail.html CHANGED
@@ -70,7 +70,6 @@
70
  </style>
71
  </head>
72
  <body>
73
- <h1>ようこそ, {{ user.name }} さん!</h1>
74
  <div class="container">
75
  <h2>会話の文字起こし表示</h2>
76
  <div id="transcription">ここに会話内容が表示されます。</div>
 
70
  </style>
71
  </head>
72
  <body>
 
73
  <div class="container">
74
  <h2>会話の文字起こし表示</h2>
75
  <div id="transcription">ここに会話内容が表示されます。</div>