Spaces:
				
			
			
	
			
			
		Runtime error
		
	
	
	
			
			
	
	
	
	
		
		
		Runtime error
		
	
		buletomato25
		
	commited on
		
		
					Commit 
							
							·
						
						f742eba
	
1
								Parent(s):
							
							bc6ed4d
								
adduser
Browse files- app.py +19 -8
- requirements.txt +3 -0
- static/register_record.js +124 -0
- {templates → static}/style.css +0 -0
- templates/index.html +31 -7
- templates/userRegister.html +5 -145
    	
        app.py
    CHANGED
    
    | @@ -6,13 +6,16 @@ import shutil | |
| 6 | 
             
            from process import AudioProcessor
         | 
| 7 |  | 
| 8 | 
             
            process=AudioProcessor()
         | 
|  | |
| 9 | 
             
            app = Flask(__name__)
         | 
| 10 |  | 
|  | |
|  | |
| 11 | 
             
            # トップページ(テンプレート: index.html)
         | 
| 12 | 
             
            @app.route('/')
         | 
| 13 | 
             
            @app.route('/index', methods=['GET', 'POST'])
         | 
| 14 | 
             
            def index():
         | 
| 15 | 
            -
                return render_template('index.html')
         | 
| 16 |  | 
| 17 | 
             
            # フィードバック画面(テンプレート: feedback.html)
         | 
| 18 | 
             
            @app.route('/feedback', methods=['GET', 'POST'])
         | 
| @@ -35,28 +38,35 @@ def userregister(): | |
| 35 | 
             
            def upload_audio():
         | 
| 36 | 
             
                try:
         | 
| 37 | 
             
                    data = request.get_json()
         | 
| 38 | 
            -
                    if not data or 'audio_data' not in data:
         | 
| 39 | 
            -
                        return jsonify({"error": " | 
| 40 |  | 
| 41 | 
             
                    # Base64デコードして音声バイナリを取得
         | 
| 42 | 
             
                    audio_binary = base64.b64decode(data['audio_data'])
         | 
|  | |
| 43 | 
             
                    audio_dir = "/tmp/data"
         | 
| 44 | 
             
                    os.makedirs(audio_dir, exist_ok=True)
         | 
| 45 | 
             
                    # 固定ファイル名(必要に応じて generate_filename() で一意のファイル名に変更可能)
         | 
| 46 | 
            -
                    audio_path = os.path.join(audio_dir, " | 
|  | |
| 47 | 
             
                    with open(audio_path, 'wb') as f:
         | 
| 48 | 
             
                        f.write(audio_binary)
         | 
| 49 |  | 
| 50 | 
             
                    # 参照音声ファイルのパスを指定(sample.wav を正しい場所に配置すること)
         | 
| 51 | 
            -
                    reference_audio = os.path.abspath( | 
|  | |
| 52 | 
             
                    if not os.path.exists(reference_audio):
         | 
| 53 | 
             
                        return jsonify({"error": "参照音声ファイルが見つかりません", "details": reference_audio}), 500
         | 
| 54 |  | 
| 55 | 
             
                    # 音声解析:参照音声とアップロードされた音声との類似度をセグメント毎に計算
         | 
| 56 | 
             
                    # threshold の値は調整可能です(例: 0.1)
         | 
| 57 | 
            -
                     | 
| 58 | 
            -
             | 
| 59 | 
            -
                     | 
|  | |
|  | |
|  | |
|  | |
| 60 |  | 
| 61 | 
             
                    return jsonify({"rate": rate}), 200
         | 
| 62 | 
             
                except Exception as e:
         | 
| @@ -72,6 +82,7 @@ def upload_base_audio(): | |
| 72 | 
             
                    # Base64デコードして音声バイナリを取得
         | 
| 73 | 
             
                    audio_binary = base64.b64decode(data['audio_data'])
         | 
| 74 | 
             
                    name = data['name']  # 名前を取得
         | 
|  | |
| 75 |  | 
| 76 | 
             
                    # 保存先ディレクトリの作成
         | 
| 77 | 
             
                    audio_dir = "/tmp/data/base_audio"
         | 
|  | |
| 6 | 
             
            from process import AudioProcessor
         | 
| 7 |  | 
| 8 | 
             
            process=AudioProcessor()
         | 
| 9 | 
            +
            multi_process=process_multi_audio()
         | 
| 10 | 
             
            app = Flask(__name__)
         | 
| 11 |  | 
| 12 | 
            +
            users = ["ccc"]
         | 
| 13 | 
            +
             | 
| 14 | 
             
            # トップページ(テンプレート: index.html)
         | 
| 15 | 
             
            @app.route('/')
         | 
| 16 | 
             
            @app.route('/index', methods=['GET', 'POST'])
         | 
| 17 | 
             
            def index():
         | 
| 18 | 
            +
                return render_template('index.html', users = users)
         | 
| 19 |  | 
| 20 | 
             
            # フィードバック画面(テンプレート: feedback.html)
         | 
| 21 | 
             
            @app.route('/feedback', methods=['GET', 'POST'])
         | 
|  | |
| 38 | 
             
            def upload_audio():
         | 
| 39 | 
             
                try:
         | 
| 40 | 
             
                    data = request.get_json()
         | 
| 41 | 
            +
                    if not data or 'audio_data' not in data or 'name' not in data:
         | 
| 42 | 
            +
                        return jsonify({"error": "音声データまたは名前がありません"}), 400
         | 
| 43 |  | 
| 44 | 
             
                    # Base64デコードして音声バイナリを取得
         | 
| 45 | 
             
                    audio_binary = base64.b64decode(data['audio_data'])
         | 
| 46 | 
            +
                    name = data['name']  # 名前を取得
         | 
| 47 | 
             
                    audio_dir = "/tmp/data"
         | 
| 48 | 
             
                    os.makedirs(audio_dir, exist_ok=True)
         | 
| 49 | 
             
                    # 固定ファイル名(必要に応じて generate_filename() で一意のファイル名に変更可能)
         | 
| 50 | 
            +
                    audio_path = os.path.join(audio_dir, f"{name}.wav")
         | 
| 51 | 
            +
                   
         | 
| 52 | 
             
                    with open(audio_path, 'wb') as f:
         | 
| 53 | 
             
                        f.write(audio_binary)
         | 
| 54 |  | 
| 55 | 
             
                    # 参照音声ファイルのパスを指定(sample.wav を正しい場所に配置すること)
         | 
| 56 | 
            +
                    reference_audio = os.path.abspath("/tmp/data/base_audio/",  f"{name}.wav")
         | 
| 57 | 
            +
                    
         | 
| 58 | 
             
                    if not os.path.exists(reference_audio):
         | 
| 59 | 
             
                        return jsonify({"error": "参照音声ファイルが見つかりません", "details": reference_audio}), 500
         | 
| 60 |  | 
| 61 | 
             
                    # 音声解析:参照音声とアップロードされた音声との類似度をセグメント毎に計算
         | 
| 62 | 
             
                    # threshold の値は調整可能です(例: 0.1)
         | 
| 63 | 
            +
                    if(users.length > 2):
         | 
| 64 | 
            +
                        print("複数人の場合の処理")
         | 
| 65 | 
            +
                    else:
         | 
| 66 | 
            +
                        matched_time, unmatched_time = process.process_audio(reference_audio, audio_path, threshold=0.05)
         | 
| 67 | 
            +
                        total_time = matched_time + unmatched_time
         | 
| 68 | 
            +
                        rate = (matched_time / total_time) * 100 if total_time > 0 else 0
         | 
| 69 | 
            +
                    
         | 
| 70 |  | 
| 71 | 
             
                    return jsonify({"rate": rate}), 200
         | 
| 72 | 
             
                except Exception as e:
         | 
|  | |
| 82 | 
             
                    # Base64デコードして音声バイナリを取得
         | 
| 83 | 
             
                    audio_binary = base64.b64decode(data['audio_data'])
         | 
| 84 | 
             
                    name = data['name']  # 名前を取得
         | 
| 85 | 
            +
                    users.append(name)
         | 
| 86 |  | 
| 87 | 
             
                    # 保存先ディレクトリの作成
         | 
| 88 | 
             
                    audio_dir = "/tmp/data/base_audio"
         | 
    	
        requirements.txt
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 | 
             
            Flask==2.2.5
         | 
|  | |
| 2 | 
             
            pyannote.audio==2.1.1
         | 
| 3 | 
             
            numpy==1.23.5
         | 
| 4 | 
             
            pydub==0.25.1
         | 
| @@ -13,4 +14,6 @@ google-auth==2.38.0 | |
| 13 | 
             
            google-auth-oauthlib==1.2.1
         | 
| 14 | 
             
            google-auth-httplib2==0.2.0
         | 
| 15 | 
             
            faster-whisper
         | 
|  | |
|  | |
| 16 |  | 
|  | |
| 1 | 
             
            Flask==2.2.5
         | 
| 2 | 
            +
            Flask-WTF
         | 
| 3 | 
             
            pyannote.audio==2.1.1
         | 
| 4 | 
             
            numpy==1.23.5
         | 
| 5 | 
             
            pydub==0.25.1
         | 
|  | |
| 14 | 
             
            google-auth-oauthlib==1.2.1
         | 
| 15 | 
             
            google-auth-httplib2==0.2.0
         | 
| 16 | 
             
            faster-whisper
         | 
| 17 | 
            +
            Flask-Migrate
         | 
| 18 | 
            +
            requests
         | 
| 19 |  | 
    	
        static/register_record.js
    ADDED
    
    | @@ -0,0 +1,124 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            let mediaRecorder;
         | 
| 2 | 
            +
            let audioChunks = [];
         | 
| 3 | 
            +
            let userCount = 0; // 追加されたメンバー数を保持
         | 
| 4 | 
            +
            let isRecording = false; // 録音中かどうかを判定するフラグ
         | 
| 5 | 
            +
            let currentRecordingButton = null; // 現在録音中のボタンを保持
         | 
| 6 | 
            +
            let userNames = [];
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            function toggleRecording(button) {
         | 
| 9 | 
            +
              button.classList.toggle("recording");
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            async function startRecording(button) {
         | 
| 13 | 
            +
              if (isRecording && currentRecordingButton !== button) return; // 他の人が録音中なら何もしない
         | 
| 14 | 
            +
              isRecording = true; // 録音中に設定
         | 
| 15 | 
            +
              currentRecordingButton = button; // 録音中のボタンを記録
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              try {
         | 
| 18 | 
            +
                const stream = await navigator.mediaDevices.getUserMedia({
         | 
| 19 | 
            +
                  audio: true,
         | 
| 20 | 
            +
                });
         | 
| 21 | 
            +
                mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
         | 
| 22 | 
            +
                audioChunks = [];
         | 
| 23 | 
            +
                mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);
         | 
| 24 | 
            +
                mediaRecorder.onstop = () => {
         | 
| 25 | 
            +
                  sendAudioChunks(audioChunks, button); // ボタン情報を渡す
         | 
| 26 | 
            +
                  audioChunks = [];
         | 
| 27 | 
            +
                  isRecording = false; // 録音停止後はフラグを戻す
         | 
| 28 | 
            +
                  currentRecordingButton = null; // 録音ボタンを解除
         | 
| 29 | 
            +
                };
         | 
| 30 | 
            +
                mediaRecorder.start();
         | 
| 31 | 
            +
                toggleRecording(button);
         | 
| 32 | 
            +
              } catch (err) {
         | 
| 33 | 
            +
                console.error("マイクアクセスに失敗しました:", err);
         | 
| 34 | 
            +
                isRecording = false; // エラー発生時もフラグを戻す
         | 
| 35 | 
            +
                currentRecordingButton = null;
         | 
| 36 | 
            +
              }
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            function stopRecording(button) {
         | 
| 40 | 
            +
              if (!isRecording) return; // 録音中でない場合は停止しない
         | 
| 41 | 
            +
              mediaRecorder.stop();
         | 
| 42 | 
            +
              toggleRecording(button);
         | 
| 43 | 
            +
            }
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            function handleRecording(e) {
         | 
| 46 | 
            +
              const button = e.target.closest(".record-button");
         | 
| 47 | 
            +
              if (button) {
         | 
| 48 | 
            +
                if (isRecording && currentRecordingButton !== button) {
         | 
| 49 | 
            +
                  // 他の人が録音中なら反応しない
         | 
| 50 | 
            +
                  return;
         | 
| 51 | 
            +
                }
         | 
| 52 | 
            +
                if (mediaRecorder && mediaRecorder.state === "recording") {
         | 
| 53 | 
            +
                  stopRecording(button);
         | 
| 54 | 
            +
                } else {
         | 
| 55 | 
            +
                  startRecording(button);
         | 
| 56 | 
            +
                }
         | 
| 57 | 
            +
              }
         | 
| 58 | 
            +
            }
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            function sendAudioChunks(chunks, button) {
         | 
| 61 | 
            +
              // 引数に button を追加
         | 
| 62 | 
            +
              const audioBlob = new Blob(chunks, { type: "audio/wav" });
         | 
| 63 | 
            +
              const reader = new FileReader();
         | 
| 64 | 
            +
              reader.onloadend = () => {
         | 
| 65 | 
            +
                const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
         | 
| 66 | 
            +
                const form = button.closest("form");
         | 
| 67 | 
            +
                const nameInput = form.querySelector('input[name="name"]');
         | 
| 68 | 
            +
                const name = nameInput ? nameInput.value : "unknown"; // 名前がない
         | 
| 69 | 
            +
                fetch("/upload_base_audio", {
         | 
| 70 | 
            +
                  method: "POST",
         | 
| 71 | 
            +
                  headers: {
         | 
| 72 | 
            +
                    "Content-Type": "application/json",
         | 
| 73 | 
            +
                  },
         | 
| 74 | 
            +
                  body: JSON.stringify({ audio_data: base64String, name: name }),
         | 
| 75 | 
            +
                })
         | 
| 76 | 
            +
                  .then((response) => response.json())
         | 
| 77 | 
            +
                  .then((data) => {
         | 
| 78 | 
            +
                    // エラー処理のみ残す
         | 
| 79 | 
            +
                    if (data.error) {
         | 
| 80 | 
            +
                      alert("エラー: " + data.error);
         | 
| 81 | 
            +
                      console.error(data.details);
         | 
| 82 | 
            +
                    }
         | 
| 83 | 
            +
                    // 成功時の処理(ボタンの有効化など)
         | 
| 84 | 
            +
                    else {
         | 
| 85 | 
            +
                      console.log("音声データ送信成功:", data);
         | 
| 86 | 
            +
                      userNames.push(name);
         | 
| 87 | 
            +
                      // 必要に応じて、ここでUIの変更(ボタンの有効化など)を行う
         | 
| 88 | 
            +
                      // 例: button.disabled = true; // 送信ボタンを無効化
         | 
| 89 | 
            +
                      // 例: button.classList.remove("recording"); //録音中のスタイルを解除
         | 
| 90 | 
            +
                    }
         | 
| 91 | 
            +
                  })
         | 
| 92 | 
            +
                  .catch((error) => {
         | 
| 93 | 
            +
                    console.error("エラー:", error);
         | 
| 94 | 
            +
                  });
         | 
| 95 | 
            +
              };
         | 
| 96 | 
            +
              reader.readAsDataURL(audioBlob);
         | 
| 97 | 
            +
            }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            document.getElementById("add-btn").addEventListener("click", () => {
         | 
| 100 | 
            +
              const newItem = document.createElement("div");
         | 
| 101 | 
            +
              newItem.className = "flex items-center gap-3 flex-wrap";
         | 
| 102 | 
            +
              newItem.innerHTML = `
         | 
| 103 | 
            +
                          <form
         | 
| 104 | 
            +
                              action="/submit"
         | 
| 105 | 
            +
                              method="POST"
         | 
| 106 | 
            +
                              class="flex items-center space-x-2 w-full sm:w-auto"
         | 
| 107 | 
            +
                              onsubmit="event.preventDefault();"
         | 
| 108 | 
            +
                          >
         | 
| 109 | 
            +
                              <input
         | 
| 110 | 
            +
                                  type="text"
         | 
| 111 | 
            +
                                  name="name"
         | 
| 112 | 
            +
                                  placeholder="名前を入力"
         | 
| 113 | 
            +
                                  class="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-gray-700 text-white"
         | 
| 114 | 
            +
                              />
         | 
| 115 | 
            +
              
         | 
| 116 | 
            +
                              <button type="button" class="record-button" aria-label="音声録音開始">
         | 
| 117 | 
            +
                                  <div class="record-icon"></div>
         | 
| 118 | 
            +
                              </button>
         | 
| 119 | 
            +
                          </form>
         | 
| 120 | 
            +
                      `;
         | 
| 121 | 
            +
              newItem.addEventListener("click", handleRecording);
         | 
| 122 | 
            +
              document.getElementById("people-list").appendChild(newItem);
         | 
| 123 | 
            +
              userCount++; // 新しいメンバーを追加するたびにカウントを増やす
         | 
| 124 | 
            +
            });
         | 
    	
        {templates → static}/style.css
    RENAMED
    
    | 
            File without changes
         | 
    	
        templates/index.html
    CHANGED
    
    | @@ -25,11 +25,30 @@ | |
| 25 | 
             
                <div class="chart w-72 h-72 mb-5">
         | 
| 26 | 
             
                  <canvas id="speechChart"></canvas>
         | 
| 27 | 
             
                </div>
         | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
                   | 
| 32 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 33 |  | 
| 34 | 
             
                <!-- 結果ボタン -->
         | 
| 35 | 
             
                <div class="flex mt-5">
         | 
| @@ -88,7 +107,8 @@ | |
| 88 | 
             
                  let before_rate = 0;
         | 
| 89 |  | 
| 90 | 
             
                  // 初期設定:人数と名前を受け取って円グラフを作成
         | 
| 91 | 
            -
                  let members = [" | 
|  | |
| 92 | 
             
                  let voiceData = [50, 20, 20, 10]; // 自分と不明の割合を仮設定
         | 
| 93 |  | 
| 94 | 
             
                  // Chart.js の初期化
         | 
| @@ -180,12 +200,16 @@ | |
| 180 | 
             
                    const reader = new FileReader();
         | 
| 181 | 
             
                    reader.onloadend = () => {
         | 
| 182 | 
             
                      const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
         | 
|  | |
|  | |
|  | |
|  | |
| 183 | 
             
                      fetch("/upload_audio", {
         | 
| 184 | 
             
                        method: "POST",
         | 
| 185 | 
             
                        headers: {
         | 
| 186 | 
             
                          "Content-Type": "application/json",
         | 
| 187 | 
             
                        },
         | 
| 188 | 
            -
                        body: JSON.stringify({ audio_data: base64String }),
         | 
| 189 | 
             
                      })
         | 
| 190 | 
             
                        .then((response) => response.json())
         | 
| 191 | 
             
                        .then((data) => {
         | 
|  | |
| 25 | 
             
                <div class="chart w-72 h-72 mb-5">
         | 
| 26 | 
             
                  <canvas id="speechChart"></canvas>
         | 
| 27 | 
             
                </div>
         | 
| 28 | 
            +
                <form
         | 
| 29 | 
            +
                  id="recordForm"
         | 
| 30 | 
            +
                  action="/submit"
         | 
| 31 | 
            +
                  method="POST"
         | 
| 32 | 
            +
                  class="flex items-center space-x-2 w-full sm:w-auto"
         | 
| 33 | 
            +
                  onsubmit="event.preventDefault();"
         | 
| 34 | 
            +
                >
         | 
| 35 | 
            +
                  <input
         | 
| 36 | 
            +
                    type="text"
         | 
| 37 | 
            +
                    name="name"
         | 
| 38 | 
            +
                    placeholder="名前を入力"
         | 
| 39 | 
            +
                    class="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-gray-700 text-white"
         | 
| 40 | 
            +
                  />
         | 
| 41 | 
            +
                  <!-- 録音ボタン -->
         | 
| 42 | 
            +
                  <button
         | 
| 43 | 
            +
                    type="button"
         | 
| 44 | 
            +
                    class="record-button"
         | 
| 45 | 
            +
                    id="recordButton"
         | 
| 46 | 
            +
                    onclick="toggleRecording()"
         | 
| 47 | 
            +
                  >
         | 
| 48 | 
            +
                    <div class="record-icon" id="recordIcon"></div>
         | 
| 49 | 
            +
                  </button>
         | 
| 50 | 
            +
                </form>
         | 
| 51 | 
            +
                <!-- 名前入力 -->
         | 
| 52 |  | 
| 53 | 
             
                <!-- 結果ボタン -->
         | 
| 54 | 
             
                <div class="flex mt-5">
         | 
|  | |
| 107 | 
             
                  let before_rate = 0;
         | 
| 108 |  | 
| 109 | 
             
                  // 初期設定:人数と名前を受け取って円グラフを作成
         | 
| 110 | 
            +
                  let members = ["aaa", "bbb", "ccc", "その他"];
         | 
| 111 | 
            +
             | 
| 112 | 
             
                  let voiceData = [50, 20, 20, 10]; // 自分と不明の割合を仮設定
         | 
| 113 |  | 
| 114 | 
             
                  // Chart.js の初期化
         | 
|  | |
| 200 | 
             
                    const reader = new FileReader();
         | 
| 201 | 
             
                    reader.onloadend = () => {
         | 
| 202 | 
             
                      const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
         | 
| 203 | 
            +
                      // フォーム要素を取得
         | 
| 204 | 
            +
                      const form = document.getElementById("recordForm");
         | 
| 205 | 
            +
                      const nameInput = form.querySelector('input[name="name"]');
         | 
| 206 | 
            +
                      const name = nameInput ? nameInput.value : "unknown"; // 名前がない
         | 
| 207 | 
             
                      fetch("/upload_audio", {
         | 
| 208 | 
             
                        method: "POST",
         | 
| 209 | 
             
                        headers: {
         | 
| 210 | 
             
                          "Content-Type": "application/json",
         | 
| 211 | 
             
                        },
         | 
| 212 | 
            +
                        body: JSON.stringify({ audio_data: base64String, name: name }),
         | 
| 213 | 
             
                      })
         | 
| 214 | 
             
                        .then((response) => response.json())
         | 
| 215 | 
             
                        .then((data) => {
         | 
    	
        templates/userRegister.html
    CHANGED
    
    | @@ -67,158 +67,18 @@ | |
| 67 | 
             
                  <!-- 録音画面に戻るボタン -->
         | 
| 68 | 
             
                  <button
         | 
| 69 | 
             
                    id="backButton"
         | 
|  | |
| 70 | 
             
                    class="mt-6 px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
         | 
| 71 | 
             
                  >
         | 
| 72 | 
             
                    録音画面に戻る
         | 
| 73 | 
             
                  </button>
         | 
| 74 | 
             
                </div>
         | 
| 75 |  | 
|  | |
| 76 | 
             
                <script>
         | 
| 77 | 
            -
                   | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
                  let isRecording = false; // 録音中かどうかを判定するフラグ
         | 
| 81 | 
            -
                  let currentRecordingButton = null; // 現在録音中のボタンを保持
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                  function toggleRecording(button) {
         | 
| 84 | 
            -
                    button.classList.toggle("recording");
         | 
| 85 | 
            -
                  }
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                  async function startRecording(button) {
         | 
| 88 | 
            -
                    if (isRecording && currentRecordingButton !== button) return; // 他の人が録音中なら何もしない
         | 
| 89 | 
            -
                    isRecording = true; // 録音中に設定
         | 
| 90 | 
            -
                    currentRecordingButton = button; // 録音中のボタンを記録
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                    try {
         | 
| 93 | 
            -
                      const stream = await navigator.mediaDevices.getUserMedia({
         | 
| 94 | 
            -
                        audio: true,
         | 
| 95 | 
            -
                      });
         | 
| 96 | 
            -
                      mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
         | 
| 97 | 
            -
                      audioChunks = [];
         | 
| 98 | 
            -
                      mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);
         | 
| 99 | 
            -
                      mediaRecorder.onstop = () => {
         | 
| 100 | 
            -
                        sendAudioChunks(audioChunks, button); // ボタン情報を渡す
         | 
| 101 | 
            -
                        audioChunks = [];
         | 
| 102 | 
            -
                        isRecording = false; // 録音停止後はフラグを戻す
         | 
| 103 | 
            -
                        currentRecordingButton = null; // 録音ボタンを解除
         | 
| 104 | 
            -
                      };
         | 
| 105 | 
            -
                      mediaRecorder.start();
         | 
| 106 | 
            -
                      toggleRecording(button);
         | 
| 107 | 
            -
                    } catch (err) {
         | 
| 108 | 
            -
                      console.error("マイクアクセスに失敗しました:", err);
         | 
| 109 | 
            -
                      isRecording = false; // エラー発生時もフラグを戻す
         | 
| 110 | 
            -
                      currentRecordingButton = null;
         | 
| 111 | 
            -
                    }
         | 
| 112 | 
            -
                  }
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                  function stopRecording(button) {
         | 
| 115 | 
            -
                    if (!isRecording) return; // 録音中でない場合は停止しない
         | 
| 116 | 
            -
                    mediaRecorder.stop();
         | 
| 117 | 
            -
                    toggleRecording(button);
         | 
| 118 | 
            -
                  }
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                  function handleRecording(e) {
         | 
| 121 | 
            -
                    const button = e.target.closest(".record-button");
         | 
| 122 | 
            -
                    if (button) {
         | 
| 123 | 
            -
                      if (isRecording && currentRecordingButton !== button) {
         | 
| 124 | 
            -
                        // 他の人が録音中なら反応しない
         | 
| 125 | 
            -
                        return;
         | 
| 126 | 
            -
                      }
         | 
| 127 | 
            -
                      if (mediaRecorder && mediaRecorder.state === "recording") {
         | 
| 128 | 
            -
                        stopRecording(button);
         | 
| 129 | 
            -
                      } else {
         | 
| 130 | 
            -
                        startRecording(button);
         | 
| 131 | 
            -
                      }
         | 
| 132 | 
            -
                    }
         | 
| 133 | 
            -
                  }
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                  function sendAudioChunks(chunks, button) {
         | 
| 136 | 
            -
                    // 引数に button を追加
         | 
| 137 | 
            -
                    const audioBlob = new Blob(chunks, { type: "audio/wav" });
         | 
| 138 | 
            -
                    const reader = new FileReader();
         | 
| 139 | 
            -
                    reader.onloadend = () => {
         | 
| 140 | 
            -
                      const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
         | 
| 141 | 
            -
                      const form = button.closest("form");
         | 
| 142 | 
            -
                      const nameInput = form.querySelector('input[name="name"]');
         | 
| 143 | 
            -
                      const name = nameInput ? nameInput.value : "unknown"; // 名前がない
         | 
| 144 | 
            -
                      fetch("/upload_base_audio", {
         | 
| 145 | 
            -
                        method: "POST",
         | 
| 146 | 
            -
                        headers: {
         | 
| 147 | 
            -
                          "Content-Type": "application/json",
         | 
| 148 | 
            -
                        },
         | 
| 149 | 
            -
                        body: JSON.stringify({ audio_data: base64String, name: name }),
         | 
| 150 | 
            -
                      })
         | 
| 151 | 
            -
                        .then((response) => response.json())
         | 
| 152 | 
            -
                        .then((data) => {
         | 
| 153 | 
            -
                          // エラー処理のみ残す
         | 
| 154 | 
            -
                          if (data.error) {
         | 
| 155 | 
            -
                            alert("エラー: " + data.error);
         | 
| 156 | 
            -
                            console.error(data.details);
         | 
| 157 | 
            -
                          }
         | 
| 158 | 
            -
                          // 成功時の処理(ボタンの有効化など)
         | 
| 159 | 
            -
                          else {
         | 
| 160 | 
            -
                            console.log("音声データ送信成功:", data);
         | 
| 161 | 
            -
                            // 必要に応じて、ここでUIの変更(ボタンの有効化など)を行う
         | 
| 162 | 
            -
                            // 例: button.disabled = true; // 送信ボタンを無効化
         | 
| 163 | 
            -
                            // 例: button.classList.remove("recording"); //録音中のスタイルを解除
         | 
| 164 | 
            -
                          }
         | 
| 165 | 
            -
                        })
         | 
| 166 | 
            -
                        .catch((error) => {
         | 
| 167 | 
            -
                          console.error("エラー:", error);
         | 
| 168 | 
            -
                        });
         | 
| 169 | 
            -
                    };
         | 
| 170 | 
            -
                    reader.readAsDataURL(audioBlob);
         | 
| 171 | 
            -
                  }
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                  document.getElementById("add-btn").addEventListener("click", () => {
         | 
| 174 | 
            -
                    const newItem = document.createElement("div");
         | 
| 175 | 
            -
                    newItem.className = "flex items-center gap-3 flex-wrap";
         | 
| 176 | 
            -
                    newItem.innerHTML = `
         | 
| 177 | 
            -
                          <form
         | 
| 178 | 
            -
                              action="/submit"
         | 
| 179 | 
            -
                              method="POST"
         | 
| 180 | 
            -
                              class="flex items-center space-x-2 w-full sm:w-auto"
         | 
| 181 | 
            -
                              onsubmit="event.preventDefault();"
         | 
| 182 | 
            -
                          >
         | 
| 183 | 
            -
                              <input
         | 
| 184 | 
            -
                                  type="text"
         | 
| 185 | 
            -
                                  name="name"
         | 
| 186 | 
            -
                                  placeholder="名前を入力"
         | 
| 187 | 
            -
                                  class="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-gray-700 text-white"
         | 
| 188 | 
            -
                              />
         | 
| 189 | 
            -
              
         | 
| 190 | 
            -
                              <button type="button" class="record-button" aria-label="音声録音開始">
         | 
| 191 | 
            -
                                  <div class="record-icon"></div>
         | 
| 192 | 
            -
                              </button>
         | 
| 193 | 
            -
              
         | 
| 194 | 
            -
                              <button
         | 
| 195 | 
            -
                                  type="submit"
         | 
| 196 | 
            -
                                  class="submit-button px-4 py-2 border rounded-lg bg-blue-500 text-white hover:bg-blue-600"
         | 
| 197 | 
            -
                              >
         | 
| 198 | 
            -
                                  送信
         | 
| 199 | 
            -
                              </button>
         | 
| 200 | 
            -
                          </form>
         | 
| 201 | 
            -
                      `;
         | 
| 202 | 
            -
                    newItem.addEventListener("click", handleRecording);
         | 
| 203 | 
            -
                    document.getElementById("people-list").appendChild(newItem);
         | 
| 204 | 
            -
                    userCount++; // 新しいメンバーを追加するたびにカウントを増やす
         | 
| 205 | 
            -
                  });
         | 
| 206 | 
            -
             | 
| 207 | 
            -
                  // 「録音画面に戻る」ボタンの処理
         | 
| 208 | 
            -
                  document
         | 
| 209 | 
            -
                    .getElementById("backButton")
         | 
| 210 | 
            -
                    .addEventListener("click", function () {
         | 
| 211 | 
            -
                      // メンバーの人数を送信する
         | 
| 212 | 
            -
                      sendUserCount();
         | 
| 213 | 
            -
             | 
| 214 | 
            -
                      // index.htmlに戻る
         | 
| 215 | 
            -
                      window.location.href = "index.html";
         | 
| 216 | 
            -
                    });
         | 
| 217 | 
            -
             | 
| 218 | 
            -
                  // メンバーの人数を送信する関数
         | 
| 219 | 
            -
                  function sendUserCount() {
         | 
| 220 | 
            -
                    console.log(`追加された人数: ${userCount}`);
         | 
| 221 | 
            -
                    // ここで人数を送信する処理を実行(例: fetchを使ってサーバーに送信)
         | 
| 222 | 
             
                  }
         | 
| 223 | 
             
                </script>
         | 
| 224 | 
             
              </body>
         | 
|  | |
| 67 | 
             
                  <!-- 録音画面に戻るボタン -->
         | 
| 68 | 
             
                  <button
         | 
| 69 | 
             
                    id="backButton"
         | 
| 70 | 
            +
                    onclick="showRecorder()"
         | 
| 71 | 
             
                    class="mt-6 px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
         | 
| 72 | 
             
                  >
         | 
| 73 | 
             
                    録音画面に戻る
         | 
| 74 | 
             
                  </button>
         | 
| 75 | 
             
                </div>
         | 
| 76 |  | 
| 77 | 
            +
                <script src="{{ url_for('static', filename='register_record.js') }}"></script>
         | 
| 78 | 
             
                <script>
         | 
| 79 | 
            +
                  function showRecorder() {
         | 
| 80 | 
            +
                    // 録音画面へ遷移
         | 
| 81 | 
            +
                    window.location.href = "index";
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 82 | 
             
                  }
         | 
| 83 | 
             
                </script>
         | 
| 84 | 
             
              </body>
         |