100MBまでの動画をdiscordに送信できるBOTを作ろう！

# はじめに
discordってとても便利ですよね。気軽に通話できるし、メッセージも簡単にやり取りできます。筆者自身も毎日使用しており、今では必須ツールの１つになっています。  
そんなdiscordですが、**8MB以上のファイルが送信できない(Freeプラン)** というでかすぎる制約があります。  
動画をよく送信する私にしてみれば、動画を送る際にいちいち圧縮してから送信するのはめちゃくちゃ面倒くさいです。そこで、discord BOTを使って簡単に圧縮できればいいな、と思い今回の開発に取り組みました。 
  
# 概要
指定のwebサイトに動画をアップロードすると、8MB制限を回避してdiscordに投稿してくれるBOTを作成します。サーバの人たちがいつでも使えるように、デプロイもします。

:::note warn
本記事は、discord BOTを立ちあげるところは説明しません。discordのBOTの作り方は以下を参考にしてください。
https://qiita.com/ukwhatn/items/c3abfbbd447f5802afa3
:::

# コード
ファイル構成を最初に示しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654841/d5122327-9e07-4c38-8193-0b5f56708f7f.png)
ーーーー
## フロントエンド
フロントエンドも少しこだわっているので、「そこは俺にとっては重要じゃないんだよ！！」という方は、簡易版の以下のコードを使用してください。
```HTML
<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>動画アップロード</title>

</head>
<body>
    <h1>タイトルを入力してください</h1>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="text" name="title" placeholder="タイトルを入力" required>
        <h1>動画をアップロードしてください</h1>
        <input type="file" name="file" accept="video/*" required>
        <input type="submit" value="アップロード">
    </form>
</body>
</html>
```
私のこだわりに付き合ってくれる人は、以下に完成版の説明を書きましたのでこちらもご覧になっていただけると嬉しいです。
（執筆中URL）

## バックエンド
次に、バックエンド側の処理を説明します。  
処理としては、 

① フロントから動画を受け取る
② 受け取った動画をCloudinaryのクラウドストレージに保存
③ 保存した動画のURLを取得
④ URLをdiscordに送信する

これだけです。discordでは、動画リンクを張るとその場で再生できるため、8MBの制限を回避することが出来ます。
処理の内容を順に説明します。

### ① フロントから動画を受け取る
HTMLにて、ファイルをまずは受け取ります。
```html
<input type="file" name="file" accept="video/*" required>
```
上のように書くことで、ファイルを受け取り、それに対して "file" という識別子を付けることが出来ます。(name="file" の部分)

以下はサーバ側のコードです。
```python
@app.route('/upload', methods=['POST'])
def upload_file():
    """動画を受け取り、Cloudinary にアップロードし、Discord に送信"""
    if 'file' not in request.files:
        return 'ファイルが見つかりませんでした。', 400
    file = request.files['file']
    if file.filename == '':
        return 'ファイル名が空です。', 400

    title = request.form.get('title', 'Video')
    file_path = f"uploads/{file.filename}"
    file.save(file_path)

    threading.Thread(target=process_and_upload, args=(file_path, title)).start()

    # アップロード後に同じページにリダイレクト
    return redirect(url_for('index'))
```

先ほど、受け取ったファイルに "file" と名前を付けたので、サーバ側で
```python
file = request.files['file']
```
 として、fileに代入します。そして、uploadsフォルダに保存し、そのパスを `process_and_upload` 関数に渡します。

 ## ② 受け取った動画をCloudinaryのクラウドストレージに保存
 ```python
 def process_and_upload(file_path, title):
    """動画をCloudinaryにアップロードし、そのURLをDiscordに送信する"""
    logger.info(f"Starting upload for file: {file_path} with title: {title}")

    try:
        logger.info("Uploading video to Cloudinary...")
        upload_result = cloudinary.uploader.upload(
            file_path, resource_type='video', eager=[{'width': 800, 'height': 600, 'crop': 'limit'}]
        )
        video_url = upload_result['secure_url']
        logger.info(f"Upload successful. Video URL: {video_url}")

        async def send_to_discord():
            logger.info(f"Sending video URL to Discord channel: {CHANNEL_ID}")
            channel = bot.get_channel(CHANNEL_ID)
            if isinstance(channel, discord.TextChannel):
                await channel.send(f"[{title}]({video_url})")
            else:
                logger.error(f"Invalid channel: {CHANNEL_ID}")

        loop = bot.loop
        loop.create_task(send_to_discord())

        logger.info("Video URL sent to Discord successfully.")
    except Exception as e:
        logger.error(f"Error during video upload and Discord send: {e}")
 ```
 保存したファイルパスを受け取ったら、Cloudinaryのクラウドストレージに保存します。保存方法はとても簡単で、CloudinaryのAPI取得後、
```python
upload_result = cloudinary.uploader.upload(
            file_path, resource_type='video', eager=[{'width': 800, 'height': 600, 'crop': 'limit'}]
        )
```
と書くだけです。`cloudinary.uploader.upload` の引数に、ファイルパス、タイプ、サイズを指定し、戻り値をvideo_result に保存しています。

## ③ 保存した動画のURLを取得
クラウド上の動画URLは `video_result[`secure_url`]` で取得することが出来ます。後は、discordに送信するだけで8MB制限を気にせずに動画を貼ることが出来ます。

## ④ URLをdiscordに送信する
discordに送信する際には、BOTに「ここのチャンネルに送信してくださいね～」と指定してあげる必要があります。そこで登場するのが、チャンネルIDです。  
チャンネルIDの取得方法は以下サイトをご覧ください。
https://qiita.com/ryo_001339/items/10379e7e108a9f797e5e
  
チャンネルIDを取得したら、以下のように書くことでdiscordの指定Ch.に送信することが出来ます。取得したチャンネルIDは、.env に書いているため、コード上ではCHANNEL_IDで呼び出しています。（良く分からない人は .env で検索）
```python
async def send_to_discord():
    channel = bot.get_channel(CHANNEL_ID)
    if isinstance(channel, discord.TextChannel):
        await channel.send(f"[{title}]({video_url})")
    else:
        logger.error(f"Invalid channel: {CHANNEL_ID}")
```
これは少し工夫なのですが、URLをそのまま送ると見た目が悪いため、サイトで入力してもらったタイトルを用いて`[{title}]({video_url})`として送信することで、タイトルにURLを埋め込む？ことが出来ます。
  
これで完了です。  
しかし、現状ではローカルでしか動きません。ファイルを圧縮するたびにBOTを起動しているようでは手間がかかってしまうので、デプロイしていつでも簡単に圧縮できるようにしていきましょう。

https://qiita.com/yonuma28/items/d0dd9401cda316c3c6da

## 最後に
今回開発したdiscord BOT のコードを載せておきます
https://github.com/yonuma28/compress_movie