LoginSignup
13
11

More than 3 years have passed since last update.

YouTube LiveのチャットをDeepL翻訳してリアルタイム表示する

Last updated at Posted at 2020-10-31

動作イメージ

DeepL翻訳してリアルタイムで表示します。(右側のウィンドウ)
メディア1.gif

きっかけ

  1. YouTube Liveの外国語チャットを翻訳したい
  2. Chrome上で「右クリック→日本語に翻訳」するとGoogle翻訳できるけど精度がイマイチ
  3. DeepL翻訳はGoogle翻訳より精度が高いらしい(下記グラフ参照)
  4. チャットは時々刻々と流れてくるので、いちいちDeepL翻訳サイトにコピペするのは面倒

以上のことから、YouTube LiveのチャットをDeepL翻訳してリアルタイム表示するプログラムを作成しました。
image.png
https://www.deepl.com/quality.html

使い方

  • 準備
  • 実行
    • YouTube APIキーを入力する。
    • DeepL APIキーを入力する。
    • YouTube LiveのURLを入力する。(Liveアーカイブは不可なので注意)
    • どの言語に翻訳するか選択する。(デフォルトは日本語)
    • 「Start」ボタンを押す。

image.png

コード

translate_youtube_live_chat_gui.py
# モジュールを読み込む。
import tkinter  # Tcl/Tk の Python インタフェース
import time

# 関数を読み込む。
from tkinter import messagebox  # メッセージボックス作成
from joblib import Parallel, delayed
from youtube import get_video_id, get_video_data, get_chat_id, get_page_token, get_chat_data  # YouTube
from youtube import get_message_list, get_user_name_list  # YouTube
from translator import deepl_translate  # DeepL翻訳

# ======================================================================================================================
# チャット読み込みの間隔を設定する。(短くし過ぎるとエラーになるので注意)
UPDATE_TIME = 5000  # ミリ秒
# ======================================================================================================================


# ボタンをクリックしたときの処理をする。
def click_button():
    # GUIから情報を取得する。
    youtube_live_url = edit_box1.get()  # YouTubeLiveのURL
    youtube_api_key = edit_box2.get()  # YouTube APIキー

    # YouTubeLiveの情報を取得する。
    video_id = get_video_id(youtube_live_url)  # ビデオID
    video_data = get_video_data(video_id, youtube_api_key)  # ビデオデータ
    chat_id, error_message = get_chat_id(video_data)  # チャットID
    if chat_id is None:  # YouTubeLive以外の場合
        messagebox.showerror('URL Error', error_message)
        return  # 処理を中断する。
    chat_data = get_chat_data(chat_id, None, youtube_api_key)  # チャットデータ(ページトークン初期値=None)
    page_token = get_page_token(chat_data)  # ページトークン

    # テキストボックスにメッセージを追加する。
    root.after(UPDATE_TIME, add_message, chat_id, page_token)


# テキストボックスにメッセージを追加する。
def add_message(chat_id, page_token):
    # GUIから情報を取得する。
    youtube_api_key = edit_box2.get()  # YouTube APIキー
    deepl_api_key = edit_box3.get()  # DeepL APIキー
    target_lang = variable.get()  # 翻訳先の言語

    # YouTubeLiveの情報を取得する。
    chat_data = get_chat_data(chat_id, page_token, youtube_api_key)  # チャットデータ
    page_token = get_page_token(chat_data)  # ページトークン
    message_list = get_message_list(chat_data)  # メッセージリスト
    user_name_list = get_user_name_list(chat_data)  # ユーザー名リスト

    # DeepLで翻訳する。
    start_time = time.time()  # 処理開始時刻
    trans_message_list = Parallel(n_jobs=-1)(
        [delayed(deepl_translate)(message, target_lang, deepl_api_key) for message in message_list]
    )
    end_time = time.time()  # 処理終了時刻
    elapsed_time_s = end_time - start_time  # 処理時間 秒
    elapsed_time_ms = round(elapsed_time_s * 1000)  # 処理時間 ミリ秒

    # テキストボックスに表示するメッセージを設定する。
    display_message_list = []
    for index, trans_message in enumerate(trans_message_list):  # メッセージの数だけ繰り返す。
        text = '[' + user_name_list[index] + '] ' + trans_message  # [ユーザー名]+翻訳メッセージ
        display_message_list.append(text)

    # テキストボックスにメッセージを追加する。
    for display_message in display_message_list:
        txt_box.insert(tkinter.END, display_message)
        txt_box.insert(tkinter.END, '\n')
        txt_box.insert(tkinter.END, '\n')

    # スクロールバーを一番下にする。
    txt_box.see('end')

    # ウィンドウ表示を更新する。
    root.update()

    # 指定した時間待機する。
    wait_time = max({0, UPDATE_TIME - elapsed_time_ms})
    root.after(wait_time, add_message, chat_id, page_token)


# ======================================================================================================================


# ウィンドウを作成する。
root = tkinter.Tk()
root.title('YouTube Live Chat DeepL Translator')
root.geometry('600x650')
root.resizable(False, False)

# フレームを作成する。
frame = tkinter.Frame(root)
frame.pack()
frame.place(x=0, y=70)

# テキストボックスを作成する。
txt_box = tkinter.Text(frame, font=('Arial', 10), width=82, height=29)
y_scroll = tkinter.Scrollbar(frame, orient=tkinter.VERTICAL, command=txt_box.yview)  # 縦方向スクロールバー
y_scroll.pack(side=tkinter.RIGHT, fill='y')  # 縦方向スクロールバー
txt_box['yscrollcommand'] = y_scroll.set  # 縦方向スクロールバー
txt_box.pack()

# ボタンを作成する。
button = tkinter.Button(root, text='Start', font=('Arial', 15), command=click_button)
button.pack()
button.place(x=500, y=20)

# ラベルを作成する。
label1 = tkinter.Label(root, text='YouTube Live URL', font=('Arial', 12))
label1.pack()
label1.place(x=20, y=5)

# ラベルを作成する。
label2 = tkinter.Label(root, text='YouTube API Key', font=('Arial', 10))
label2.pack()
label2.place(x=140, y=590)

# ラベルを作成する。
label3 = tkinter.Label(root, text='DeepL API Key', font=('Arial', 10))
label3.pack()
label3.place(x=370, y=590)

# ラベルを作成する。
label4 = tkinter.Label(root, text='Target Language', font=('Arial', 10))
label4.pack()
label4.place(x=10, y=590)

# YouTubeLiveのURLを入力するエディットボックスを作成する。
edit_box1 = tkinter.Entry(root, font=('Arial', 12), width=50)
edit_box1.pack()
edit_box1.place(x=20, y=30)

# YouTube APIキーを入力するエディットボックスを作成する。
edit_box2 = tkinter.Entry(root, font=('Arial', 10), width=30, show='*')
edit_box2.pack()
edit_box2.place(x=140, y=610)

# DeepL APIキーを入力するエディットボックスを作成する。
edit_box3 = tkinter.Entry(root, font=('Arial', 10), width=30, show='*')
edit_box3.pack()
edit_box3.place(x=370, y=610)

# 翻訳言語を選択するプルダウンメニューを作成する。
option_list = [
    'German',      # DE
    'English',     # EN
    'French',      # FR
    'Italian',     # IT
    'Japanese',    # JA
    'Spanish',     # ES
    'Dutch',       # NL
    'Polish',      # PL
    'Portuguese',  # PT
    'Russian',     # RU
    'Chinese'      # ZH
]
variable = tkinter.StringVar(root)
variable.set(option_list[4])  # JA
pull_down_menu = tkinter.OptionMenu(root, variable, *option_list)
pull_down_menu.config(width=10, font=('Arial', 10))
pull_down_menu.pack()
pull_down_menu.place(x=10, y=610)

# ウインドウを描画する。
root.mainloop()

youtube.py
# モジュールを読み込む。
import requests  # HTTP通信ライブラリ

# ======================================================================================================================
# APIのURLを設定する。
YOUTUBE_API_VIDEO_URL = 'https://www.googleapis.com/youtube/v3/videos'  # YouTubeビデオ
YOUTUBE_API_CHAT_URL = 'https://www.googleapis.com/youtube/v3/liveChat/messages'  # YouTubeチャット
# ======================================================================================================================


# YouTubeLiveのURLからビデオIDを取得する。
def get_video_id(youtube_live_url):
    video_id = youtube_live_url.replace('https://www.youtube.com/watch?v=', '')
    return video_id


# 指定したビデオIDのビデオデータを取得する。
def get_video_data(video_id, youtube_api_key):
    video_data = requests.get(
        YOUTUBE_API_VIDEO_URL,
        params={
            'key': youtube_api_key,
            'id': video_id,
            'part': 'liveStreamingDetails'}
    ).json()
    return video_data


# チャットIDを取得する。
def get_chat_id(video_data):
    # YouTubeLive以外の場合処理を中断する。
    if len(video_data['items']) == 0:
        chat_id = None
        error_message = 'Not Live'
    elif 'liveStreamingDetails' not in video_data['items'][0].keys():
        chat_id = None
        error_message = 'Not Live'
    elif 'activeLiveChatId' not in video_data['items'][0]['liveStreamingDetails'].keys():
        chat_id = None
        error_message = 'No Chat'
    else:
        chat_id = video_data['items'][0]['liveStreamingDetails']['activeLiveChatId']
        error_message = None
    return chat_id, error_message


# ページトークンを取得する。
def get_page_token(chat_data):
    page_token = chat_data['nextPageToken']
    return page_token


# 指定したチャットIDのチャットデータを取得する。
def get_chat_data(chat_id, page_token, youtube_api_key):
    chat_data = requests.get(
        YOUTUBE_API_CHAT_URL,
        params={
            'key': youtube_api_key,
            'liveChatId': chat_id,
            'part': 'id,snippet,authorDetails',
            'pageToken': page_token}
    ).json()
    return chat_data


# メッセージリストを作成する。
def get_message_list(chat_data):
    message_list = []
    for chat_item in chat_data['items']:  # チャットアイテムの数だけ繰り返す。
        message_list.append(chat_item['snippet']['displayMessage'])
    return message_list


# ユーザー名リストを作成する。
def get_user_name_list(chat_data):
    user_name_list = []
    for chat_item in chat_data['items']:  # チャットアイテムの数だけ繰り返す。
        user_name_list.append(chat_item['authorDetails']['displayName'])
    return user_name_list

translator.py
# モジュールを読み込む。
import requests  # HTTP通信ライブラリ

# ======================================================================================================================
# DeepL APIのURLを設定する。
DEEPL_API_URL = 'https://api.deepl.com/v2/translate'
# ======================================================================================================================


# DeepLで翻訳する。
def deepl_translate(src_text, target_lang, deepl_api_key):
    # 翻訳言語のコードを設定する。
    if target_lang == 'German':
        target_lang_code = 'DE'
    elif target_lang == 'English':
        target_lang_code = 'EN'
    elif target_lang == 'French':
        target_lang_code = 'FR'
    elif target_lang == 'Italian':
        target_lang_code = 'IT'
    elif target_lang == 'Japanese':
        target_lang_code = 'JA'
    elif target_lang == 'Spanish':
        target_lang_code = 'ES'
    elif target_lang == 'Dutch':
        target_lang_code = 'NL'
    elif target_lang == 'Polish':
        target_lang_code = 'PL'
    elif target_lang == 'Portuguese':
        target_lang_code = 'PT'
    elif target_lang == 'Russian':
        target_lang_code = 'RU'
    elif target_lang == 'Chinese':
        target_lang_code = 'ZH'
    else:
        target_lang_code = None

    deepl_trans_result = requests.post(
        DEEPL_API_URL,
        data={
            "auth_key": deepl_api_key,
            "text": src_text,
            "target_lang": target_lang_code}
    ).json()
    trans_text = deepl_trans_result["translations"][0]["text"]

    return trans_text

13
11
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
13
11