0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[英語リスニング学習]ディクテーション用Webアプリを作成しました

Posted at

イントロダクション

概要

英語リスニング練習用のWebアプリを作成しました.

ディクテーションアプリ動作イメージ

  1. トピックを選択
  2. 音声を再生
  3. 聞きとった内容をテキストボックスに入力
  4. 聞き取れない部分はリピートしながら回答

動機

TOEICのリスニング勉強の一環でディクテーションをしていた時,
”聞き取りづらいとこだけ何回も繰り返す方法ないかな~”と思ったのがきっかけ.
ユーザが指定した部分をリピートし続けるプレイヤーを作る目的で作成しました.

要件

機能要件

ディクテーションするにあたって必要と考えた機能は下記の通り.

  • 音声プレイヤーと回答テキストボックス
    • 音声プレイヤー
      • 基本的な機能(再生/停止,シークバー,数秒戻る/進む…など)
      • 開始/終了時間を指定し,対象区間をループする機能
    • 回答テキストボックス
      • テキスト入力機能
      • 解答の表示機能
      • (できれば)回答の正解/不正解表示機能

その他の要件

音声ファイルと原稿となるテキストファイルが必要.
ずっと同じ問題では困るので,原稿&音声の内容は定期的に更新されるようにしたい.
トピックについてはとくにこだわりないので,数種類から選べればOK.

使用技術・API

  • React
    • 機能要件を満たす音声プレイヤー,回答テキストボックスのコンポーネントを作成
  • Google Cloud
    • Cloud Run
      • バックエンドAPIの実行に利用
    • Gemini
      • 原稿テキスト作成に利用
    • Text-to-Speech API(Vertex AI)
      • 原稿テキストの音声化に利用
    • Cloud Storage
      • 音声&原稿テキストの保存に利用
    • Cloud Scheduler
      • 原稿&音声の定期更新に利用

最終的に下記のような構成としました.

  • ページアクセス時のフロー

Webアプリの構成

  • 原稿&音声更新時のフロー
    Webアプリの構成 ファイル更新時

バックエンド

API

下記のAPIを用意しました.

名称 説明
音声&原稿URL取得API 引数:ファイル保存日,トピック
対象トピックの音声&原稿ファイルのURLを取得するAPI
原稿&音声を毎日更新しているため,日付も引数としています
音声&原稿ファイル生成API 引数:トピック
GeminiとText-To-Speech APIにアクセスし,音声と原稿ファイルを生成するAPI
Storageに保存するAPICloud Schedulerによる定期実行用
OIDCトークンによって認証し,Cloud Scheduler以外からの実行を拒否

フロントエンド

ランディングページと問題ページの2つで構成しました.

ランディングページ

トピックを選択して,対応する問題ページに遷移します.

問題ページ

アクセス時にバックエンドの音声&原稿URL取得APIを実行.
取得したURLにアクセスして音声&原稿データを取得し,各コンポーネントに渡します.
Reactで機能要件を満たす音声プレイヤー,回答テキストボックスのコンポーネントを作成しました.

音声プレイヤーコンポーネント

基本的な機能に加え,下のスライダーからループ開始/終了時間を指定できます.

音声プレイヤーコンポーネント

回答テキストボックスコンポーネント

各単語ごとに入力ボックスを分けています.

回答テキストボックスコンポーネント

解答表示はボタンでON/OFF可能.

解答表示状態の回答テキストボックスコンポーネント

回答の正解/不正解表示機能については単語ごとに区切って判定しています.

回答テキストボックスコンポーネントの入力例

音声/原稿の自動更新機能

バックエンドの音声&原稿ファイル生成APIを1日1回実行し,新しい音声&原稿ファイルを作成しています.

Cloud Scheduler側の対応

ターゲットタイプ”HTTP”を選択し,音声&原稿ファイル生成APIのURLを対象URLに設定します.

また,今回は認証を行いたいのでAuthヘッダーに”OIDCトークンを追加”を設定します.

Cloud Schedulerの設定画面

サービスアカウントにはCloud Scheduler Job実行者のロールを設定する必要があります.
必要に応じてIAMと管理からロールを割り当てましょう.

バックエンド(API)側の対応

バックエンド側では認証を行うための対応が必要です.

今回は,OIDCトークンのペイロードに含まれるemailを利用して下記のようなフローでアクセス制御を行います.

OIDCトークンには設定したサービスアカウントのメールアドレスが含まれるので,これを用いて認証を行います.

  1. [Cloud Scheduler側処理] OIDCトークンを含んだリクエストをバックエンドAPIに投げる
  2. [バックエンド側処理] OIDCトークンのペイロードに含まれるemailと,事前に設定しておいたメールアドレスを比較
  3. [バックエンド側処理] メールアドレスが一致していればファイル生成処理継続,一致していなければ拒否する

実装は下記のようになります.

  • 環境変数
ENDPOINT={APIのURL}
SERVICE_ACCOUNT_EMAIL={サービスアカウントのメールアドレス}
  • 認証処理のコード
import { OAuth2Client } from 'google-auth-library';
import dotenv from 'dotenv';

//環境変数にENDPOINT={APIのURL}を設定しておく
const client = new OAuth2Client(process.env.ENDPOINT);

export const authenticate = async (req, res, next) => {
    try {
		//authorizationヘッダーがないなら拒否
        if (!req.headers['authorization']) {
            throw new Error('Forbidden - No token provided');
        }
        
        //OIDCトークンからペイロードを取り出す
        const idToken = req.headers['authorization'].split(' ')[1];
        const ticket = await client.verifyIdToken({
            idToken,
            audience: process.env.ENDPOINT,
        });
        const payload = ticket.getPayload();
        
    if (payload) {
      // サービスアカウントのメールアドレスを検証
      if (payload.email === process.env.SERVICE_ACCOUNT_EMAIL) {
		//一致していれば次の処理
        next();
      } else {
    	//不一致なので拒否
        throw new Error('Forbidden - Invalid service account');
      }
    } else {
      throw new Error('Forbidden');
    }
  } catch (error) {
    console.error(error);
    res.status(403).send('Forbidden');
  }
};
  • サーバのコード
import express from 'express';

const app = express();
const port = 8080;

app.post('/{エンドポイントの名前(任意)}', authenticate, async (_, res) => { //引数に↑で書いたauthenticateを追加
  /*
		ファイル生成処理のコード  
  */
});

デプロイ方法

バックエンド

バックエンドのコードはCloud Runでデプロイしました.
GitHubでリポジトリを作成し,Cloud Runの”リポジトリを接続”からデプロイできます.
フロントエンドを後述のようにVercelにデプロイしているので,”未認証の呼び出しを許可”を選択します.

Cloud Run設定画面

環境変数にURLとサービスアカウントのメールアドレスを設定しておきます.
設定ページ下部の”コンテナ,ボリューム…”の”変数とシークレット”から設定できます.

Cloud Run環境変数設定画面

フロントエンド

ホスティングサービスはVercelを利用しました.
GitHubでリポジトリを作成し,Vercelのページから連携すれば完了です.

Vercelデプロイ画面

Vercelデプロイ画面 リポジトリのインポート
環境変数にバックエンドAPIのURLを設定しておきます.
Vercelデプロイ画面 環境変数設定

まとめ

ディクテーション用アプリの開発内容についてまとめました.

補足

参考リンク

バックエンド

https://cloud.google.com/nodejs/docs/reference/google-auth-library/latest/google-auth-library/oauth2client#google_auth_library_OAuth2Client_verifyIdToken_member_1_

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?