chatGPTを使用して, Youtubeの字幕を要約する (3)

# 更新履歴
2023/09/19 : GitHub Actions の設定ファイルを追加

# 振り返り
前回chatGPTを使って字幕要約ツールを作り, REST APIを作って見たいという形で終わっていた.
2023年5月に公開して, 2023年8月頃に着手した形になる.

https://qiita.com/nagato0614/items/ab163d32ff08086e9be7

# はじめに
APIではないがDjangoを使用してYouTube要約Web appを作成して公開するところまでできた.
大体1ヶ月ほどでURLを渡して要約を表示するところまでできたので,
どのようにして実装して, どこで詰まったのかなどをまとめる.
開発の際には, chatGPT, GitHub Copilotなどをフル活用して開発を行った.

https://www.nagato-app.com/

# 開発環境

- OS : Windows 10
- IDE, エディタ : PyCharm
- バージョン管理 : GitHub
- CIツール : GitHub Actions
- Web Framework : Django
- Webサーバ : Heroku
- DB : PostgreSQL

# 機能

## Home 画面
Home画面には YouTube 動画要約と PDF 要約 (未完成) 画面に遷移できるようにしています.
また, なにか更新したとき更新内容を表示するお知らせもあります.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/1ea52a5a-3e2e-3196-d4b7-195bec4d2087.png)


## YouTube 要約機能
機能としては, YouTube の動画 URL をフォームに入れると, 要約分が生成されたページに遷移するというものです.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/7a0bf75f-3c41-38e9-a2d7-7ae738d31efc.png)

動画をForm に入力して"動画要約"ボタンを押すと,
下記のような動画要約画面に遷移します.
要約画面には, 動画名, 投稿者, 動画投稿日, 入力データ(字幕)が表示されます.

```
https://www.youtube.com/watch?v=YyhfK-aBo-Y&ab_channel=GOTOConferences
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/1032599f-e748-ec6d-7b8e-ea7e97ccef32.png)

要約するには時間がかかるので 5秒ごとにリロードしてDBに要約データがあるか確認します.
動画要約がDBに登録されると, 晴れて要約が生成されます.
動画要約に時間がかかってしまうため静的サイトで表現するのが難しかったですが,
リダイレクトを駆使してそれっぽく見せています.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/6ae53fd4-4464-5e30-2491-bf2730abe869.png)

## アカウント
アカウントを登録すると更に便利な機能が使用できるように SignUpページも用意しています.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/6f54d0f7-b734-1672-5dcd-87fe42676295.png)


## 動画リスト

要約された動画は誰でも見られるようにしています.
この画面には 10個の動画情報を一度に表示し要約ページへのリンク, 要約が作成された日時, 元の動画へのリンクがあります.

ログイン時にはお気に入り登録できるようにして, 後で確認できます.

ログイン時のリスト. お気に入りボタンが表示される. 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/ac46aa19-176c-5818-d1e4-b4f1e353d1eb.png)

未お気に入り時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/c86c747d-eeda-c2ad-951f-4a5cd4019bd8.png)

お気に入り登録時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/f8c77a41-051a-2001-e957-b56dd8de5b41.png)


# デザイン

デザインはBootstrapを使用することできれいに見せている.
他の CSS フレームワークもあったが一番キレイだったため採用した.

# パフォーマンス

要約部分の表示が苦労した点で, 
要約を登録後背後で, RQ (Redis Queue) を使用して取得処理を行っている.
流れとしては,

1. 要約開始ボタン押下
2. urlから video_id を取得してまだ検索していないか確認
    - 検索済みのときはDBからデータを取得して表示するだけ
3. video_id をバックエンドで実行している workerのqueueに入れる
4. worker はqueue から video_id を取得して動画要約を作成DBに登録
5. 要約画面では, DBにデータが無いときは要約はロード画面を表示しつつ, 5秒ごとにリロード
6. DBにデータが登録されると要約を押して処理が完了する.

# テスト

テストは単体テストだけで, 正常系テストを書く.
以下のサイトを参考にした.

https://zenn.dev/tmasuyama1114/articles/django-how-to-unit-test

## Url テスト
書く URL とview の紐づけがあっているかを確認する.

例 : 
```python
    def test_home_url(self):
        """
        ホーム画面のURLが正しいかテスト
        :return:
        """
        url = reverse('gpt_tools:home')
        self.assertEqual(resolve(url).func.view_class, Home)
```

## Model テスト

確認することは
1.初期状態では何も登録されていないこと
2.1つレコードを適当に作成すると、レコードが1つだけカウントされること
3.内容を指定してデータを保存し、すぐに取り出した時に保存した時と同じ値が返されること
(上記は[ここ](https://zenn.dev/tmasuyama1114/articles/django-how-to-unit-test)から引用)

## View テスト

確認することは以下のようなことである.
- ステータスコードは200か
- Headerにあるべきボタンが全てあるか
- Login時, 非ログイン時それぞれで出力するべきボタンがあるか
- ホーム画面のテンプレートはあっているか
- Form がある場合, 正しいデータを渡したとき期待する結果になるか, DBに登録されているか
- DBに適当なデータを入れて期待したHTML文になるか. 


# 運用

サーバーは Heroku を利用した.
AWSも考慮に入れたが自分のモチベーションのためにとにかく早く公開したかったので
煩雑な処理をやってくれる Heroku を採用した.

各機能は GitHub 上で issue を立ち上げた上で実装し,
PR発行, main にマージされるとHerokuがデプロイしてくれる様になっている.
PRでマージを認証するとき, GitHub Actionsを使用してテストを実行するようにした.
これによって, それまでの機能に影響が出たとき勝手に merge されなくなるので結構役に立っている.

参考にテスト時に実行するdocker-composeファイルは以下のようなもの.

```yaml:docker-compose-test.yaml
version: "3.3"

services:
  test:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - REDIS_URL=redis://redis:6379
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - DATABASE_URL=postgres://user:password@db:5432/db
    command: python3 manage.py test
    volumes:
      - .:/code
    depends_on:
      - db
      - redis
  worker:
    build:
      context: .
      dockerfile: Dockerfile
    command: python3 worker.py
    environment:
      - REDIS_URL=redis://redis:6379
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    volumes:
      - .:/code
    depends_on:
      - test
      - redis
  redis:
    image: "redis:latest"
    ports:
      - "6379:6379"
  db:
    image: postgres:latest
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: db
    ports:
      - "5432:5432"
```


GitHub Actionsは以下のように作成した yaml ファイルを実行するようにした.
(docker-compost-test.yaml が上記で定義したファイルを指しています)

```yaml:django.yml
name: Django CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
env:
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Cache Docker layers
        uses: actions/cache@v2
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-buildx-

      - name: Build and run tests
        run: |
          docker-compose -f docker-compose-test.yaml up --build --exit-code-from test
```

# まとめ?

最初, 要約ページしかなく, アカウントやホーム画面もないときにとりあえず公開したがモチベーションが上がるので良かった.
自分自身が必要だっため外出時自分のサイトを開いて使えるし, 見ているだけであれをしたいこれをしたいと考えることができてよかった.
また開発環境を整えてデプロイやテストを自動化したことで, ちょっとした修正のとき簡単にデプロイできるようになり, 簡単なバグを見つけられるという点で効率が上がった.

要約以外にもDjangoの使い方を学ぶ必要があったがそれも楽しかった.



# 今後

- PDFの機能があるとしているがこれも追加で実行する.
- 各動画ごとの見どころなどがわかったり, ありきたりだが動画について質問などもしたい.
- vue.js, react.js などのフロントも強化したい.