YouTubuからの文字起こし

## 1. はじめに

　YouTubuの動画を見る時にまだ字幕が付けられていないものをいち早く見たいという時があり、自分で文字起こしできたらと考えていました。

　ChatGPTを使い試みていたのですがPythonライブラリのインストールがなかなか上手くいかず、GPT-4oの公開後に再度チャレンジしてみたら上手くいったので、記録に残しておきたいと思います。


## 2.準備するもの

- ffmpegをHP(https://ffmpeg.org/download.html) からダウンロードする。
これは音声を抽出するyt-dlpというライブラリを使用するために、必要なソフトになります。

- ffmpegをダウンロード後、Windowsを使用している場合はシステム環境変数の設定を行ない、再起動する。
　- システム環境変数の設定方法の説明は省略します。

- Youtubu音声を保存と、テキストファイルを保存するための、フォルダ。

- YouTubeの動画URL

## 3.コード

インストール

```python
pip install openai
pip install git+https://github.com/openai/whisper.git yt-dlp
pip install soundfile
pip install soundfile librosa
```

- 途中までの文字起こし結果を保存するため、各チャンク(60秒ごとに一旦区切る)ごとに、途中までのテキストファイルを保存します。これにより途中でスクリプトが中断しても、進行状況が保存されます。
- 途中でエラーになった場合、どこでエラーになったかを特定したかったので、ファイル場所の存在確認、アクセス権限の確認、オーディオファイル読み込みの所にエラーハンドリングを入れています。

```python
import yt_dlp
import whisper
import os
import numpy as np
import soundfile as sf
import librosa

# YouTubeの動画URL
video_url = "YouTubeの動画URL"

# ダウンロードディレクトリ
download_dir = r"C:\ディレクトリ"

# MP3ファイルの保存パス
audio_file = os.path.join(download_dir, "audio")

# yt-dlp設定
ydl_opts = {
    'format': 'bestaudio/best',
    'outtmpl': audio_file + '.%(ext)s',  # 拡張子は自動で追加されます
    'postprocessors': [{
        'key': 'FFmpegExtractAudio',
        'preferredcodec': 'mp3',
        'preferredquality': '192',
    }],
    'ffmpeg_location': 'C:\\ffmpeg-7.0-essentials_build\\bin'  # ここでffmpegの場所を指定
}

# YouTube動画をMP3に変換してダウンロード
print("YouTube動画をダウンロードしてMP3に変換しています...")
try:
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        ydl.download([video_url])
    print(f"ダウンロードが完了しました: {audio_file}")
except Exception as e:
    print(f"ダウンロード中にエラーが発生しました: {e}")

# MP3ファイルの存在確認
if not os.path.isfile(audio_file):
    raise RuntimeError(f"ファイルが見つかりません: {audio_file}")

# ファイルアクセス権限の確認
try:
    with open(audio_file, 'rb') as f:
        print("ファイルにアクセスできます。")
except Exception as e:
    raise RuntimeError(f"ファイルにアクセスできません: {e}")

# Whisperモデルをロード
print("Whisperモデルをロードしています...")
try:
    model = whisper.load_model("large")
    print("Whisperモデルが正しくロードされました。")
except Exception as e:
    print(f"Whisperモデルのロード中にエラーが発生しました: {e}")

# オーディオファイルを読み込む
print("オーディオファイルを読み込みます...")
try:
    data, samplerate = sf.read(audio_file)
    print(f"オーディオファイルが正常に読み取られました。サンプルレート: {samplerate}, サイズ: {data.shape} バイト")
except Exception as e:
    raise RuntimeError(f"オーディオファイルを開く際にエラーが発生しました: {e}")

# サンプルレートを16kHzに変更し、モノラルに変換
print("オーディオを16kHzモノラルに変換します...")
try:
    data_mono = librosa.to_mono(data.T)
    data_resampled = librosa.resample(data_mono, orig_sr=samplerate, target_sr=16000)
    data_resampled = data_resampled.astype(np.float32)
    print(f"変換が完了しました。新しいサイズ: {data_resampled.shape}")
except Exception as e:
    raise RuntimeError(f"オーディオ変換中にエラーが発生しました: {e}")

# チャンクサイズを設定（例: 60秒ごとに分割）
chunk_size = 60 * 16000  # 60秒 * 16kHz

# テキストファイルの保存先ディレクトリ
os.makedirs(download_dir, exist_ok=True)

# テキストファイルのパスを指定
output_path = os.path.join(download_dir, "transcription.txt")

# 既存のファイルがあれば削除
if os.path.exists(output_path):
    os.remove(output_path)

# 音声ファイルをチャンクに分割して処理
# 英語で文字起こししたいときはlanguage='en'にする。
print("音声ファイルをチャンクに分割して文字起こしを開始します...")
for i in range(0, len(data_resampled), chunk_size):
    chunk = data_resampled[i:i + chunk_size]
    try:
        result = model.transcribe(chunk, verbose=True, language='ja')
        chunk_transcription = result['text']
        print(f"チャンク {i//chunk_size + 1} の文字起こしが完了しました。")

        # 部分的な文字起こし結果をファイルに追記
        with open(output_path, "a", encoding="utf-8") as file:
            file.write(chunk_transcription + "\n")
    except Exception as e:
        print(f"チャンク {i//chunk_size + 1} の文字起こし中にエラーが発生しました: {e}")

print(f"文字起こし結果が {output_path} に保存されました。")
```

## 4.おわりに
このコードを作成するにあたり、こちらの記事を参考にさせて頂きました。
https://note.com/currypurin/n/n25a0f23d760e

自分では６分ほどの動画の文字起こしがうまくいきました。PCのスペックが良いものであれば、チャンクの時間を長くしても良いかもしれません。

お役に立てれば幸いです。

☆このコードを使い、英語動画の翻訳を比較をした記事です。

https://qiita.com/Noble_Laurel031212/items/cc83e00e809194b365f4

(更新)　mp3の拡張子が二重に作られるエラーを修正(2024/5/19)