YouTube Data Apiで動画情報を更新する(タイトル,説明,タグ,カテゴリ,公開予定日,サムネイル,プレイリスト)Python3

もくじ

書くこと

YouTubeに動画をアップロードしたあと、普通ならYouTube Studioで動画のタイトルや説明、タグなどを追加していくと思います。

僕はこれがめんどくさくて、プログラムで動画の情報を更新できないかと考えました。

そこで、Python3YouTube Data Apiを使用して、動画の情報を更新するプログラムを書いてみました。

更新する動画の項目

このプログラムで更新する動画の設定項目の表になります。
※デフォルト値とは、動画をアップロードしたときにデフォルトで設定されている値のことです。

項目デフォルト値備考(本プログラムの動き)
タイトルファイル名
説明なし
タグなしタグの上書きと追加ができます。
カテゴリYouTubeの設定による([設定]→[アップロード動画のデフォルト設定]→[詳細設定]プログラム上では番号で指定します。
公開設定YouTubeの設定による([設定]→[アップロード動画のデフォルト設定]→[基本情報])即時公開か公開予約ができるようにします。
公開予定日なし公開予約の場合に日時を指定できるようにします。
カスタムサムネイルなしファイルのパスを指定することでサムネイルをセットします。
プレイリストなし事前に作成したプレイリストへの動画の追加ができるようにします

事前準備(Google Cloud)

Google Cloudの事前準備をします。

プロジェクトの作成

Google Cloudコンソールにログインします。
ここでは「My Project Update Video」という名前でプロジェクトを作成しました。
image.png

「YouTube Data API v3」ライブラリの有効化

「APIとサービス」から「ライブラリ」をクリックします。
image.png

“youtube”で検索して、「YouTube Data API v3」をクリックします。
image.png

「有効にする」をクリックします。
image.png

「OAuth同意画面」の作成

「認証情報」画面に行き、「認証情報を作成」から「OAuthクライアントID」をクリックします。
image.png

「同意画面を設定」をクリックします。
image.png

「外部」にチェックを入れて「作成」をクリックします。
image.png

「アプリ名」は”Update Video App”にしました。(任意のものを入力します)
「ユーザーサポートメール」をプルダウンから選びます。
image.png

「デベロッパーの連絡先情報」を入力して「保存して次へ」をクリックします。
image.png

スコープには「…/auth/youtube.force-ssl」を選択します。
「保存して次へ」をクリックします。
image.png

テストユーザーを追加して「保存して次へ」をクリックします。
image.png

「OAuth 2.0 クライアント ID」の作成

「認証情報」→「認証情報を作成」→「OAuthクライアントID」をクリックします。
image.png

「アプリケーションの種類」は”ウェブアプリケーション”を選択します。
「承認済みのリダイレクトURI」には”http://localhost:8080/”を入力して作成します。
image.png

作業の流れ(動画アップロードから〜プログラム実行まで)

動画のアップロードから、プログラム実行までの大まかな流れは以下になります。

  1. 手動で動画をYouTubeにアップロードする。
  2. アップロードした動画のIDをメモする。
  3. プレイリストを作成する。
  4. プレイリストのIDをメモする。
  5. 本件のプログラムに動画IDを渡しつつ、更新したい動画の項目を引数に添えて実行する。

プログラムと解説

プログラム

import argparse
import datetime
import os

import dateutil.parser
import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaFileUpload

scopes = ["https://www.googleapis.com/auth/youtube.force-ssl"]

# クレデンシャルを取得してAPIクライアントを作成する関数
def get_authenticated_service():
    api_service_name = "youtube"
    api_version = "v3"
    client_secrets_file = "client_secrets.json"

    flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
        client_secrets_file, scopes)
    credentials = flow.run_local_server()
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials)
    
    return youtube

# 動画の情報をアップデートする関数
def update_video(youtube, args):
    # videos.listメソッドでアップデート対象の動画のsnippetとstatusを取得する
    videos_list_response = youtube.videos().list(
        id=args.video_id,
        part='snippet,status'
    ).execute()

    # videos.list()メソッドのレスポンスに'items'アレイが無い場合は対象ビデオが見つからなかったので処理終了
    if not videos_list_response['items']:
        print('動画ID "{}" は見つかりませんでした。'.format(args.video_id))
        sys.exit(1)

    # 対象の動画が見つかった場合、snippetとstatusを展開する
    videos_list_snippet = videos_list_response['items'][0]['snippet']
    videos_list_status = videos_list_response['items'][0]['status']

    # '--title'が指定されている場合はタイトルを設定する
    if args.title:
        videos_list_snippet['title'] = args.title

    # '--description'が指定されている場合は説明を設定する
    if args.description:
        videos_list_snippet['description'] = args.description

    # '--tags'または'--add_tag'が指定されている場合の処理
    # 動画にタグがひとつもない場合は空のリストを作成する
    if 'tags' not in  videos_list_snippet:
        videos_list_snippet['tags'] = []
    # '--tags'に指定された引数で動画のタグを上書きする
    if args.tags:
        videos_list_snippet['tags'] = args.tags.split(',')
    # '--add_tag'に指定された引数をタグに追加する
    if args.add_tag:
        videos_list_snippet['tags'].append(args.add_tag)

    # '--category'がある場合は指定された番号でカテゴリを変更する
    if args.category:
        videos_list_snippet['categoryId'] = args.category

    # '--pulish_date'がある場合は指定されている日時で公開予約とする
    if args.publish_date:
        videos_list_status['publishAt'] = args.publish_date
        videos_list_status['uploadStatus'] = 'uploaded'
        videos_list_status['privacyStatus'] = 'private'
    else:
        videos_list_status['uploadStatus'] = 'uploaded'
        videos_list_status['privacyStatus'] = 'public'

    # videos.update()メソッドで対象ビデオのsnippetをアップデートする
    videos_update_snippet_response = youtube.videos().update(
        part='snippet',
        body=dict(
            snippet=videos_list_snippet,
            id=args.video_id,
        )
    ).execute()
    
    # videos.update()メソッドで対象ビデオのstatusをアップデートする
    videos_update_status_response = youtube.videos().update(
        part='status',
        body=dict(
            status=videos_list_status,
            id=args.video_id
        )
    ).execute()
    
    print('##### アップデート後の動画のsnippetレスポンス(ここから) #####')
    print(videos_update_snippet_response)
    print('##### アップデート後の動画のsnippetレスポンス(ここまで) #####')
    print()
    print('##### アップデート後の動画のstatusレスポンス(ここから) #####')
    print(videos_update_status_response)
    print('##### アップデート後の動画のstatusレスポンス(ここまで) #####')
    print()
    
    print('動画をアップデートしました。\n' + 
            'アップデートされた動画のメタデータを下に示します\n' +
            'タイトル: ' + videos_update_snippet_response['snippet']['title'])
    if videos_update_snippet_response['snippet']['description']:
        print ('説明: ' + videos_update_snippet_response['snippet']['description'])
    
    if not 'tags' in videos_update_snippet_response['snippet']:
        print ('タグ: タグはありません。')
    else:
        print ('タグ: ' + ','.join(videos_update_snippet_response['snippet']['tags']))
    
    if videos_update_snippet_response['snippet']['categoryId']:
        print('カテゴリ番号: ' + videos_update_snippet_response['snippet']['categoryId'])
    
    if videos_update_status_response['status']['publishAt']:
        utc_timestamp = videos_update_status_response['status']['publishAt']
        JST = datetime.timezone(datetime.timedelta(hours=+9), 'JST')
        jtc_timestamp = dateutil.parser.parse(utc_timestamp).astimezone(JST)
        print('公開予定日: ' + str(jtc_timestamp))
        print('\n')

# サムネイルをセットする関数
def set_thumbnail(youtube, args):
    thumnail_set_response = youtube.thumbnails().set(
        media_body=MediaFileUpload(args.thumbnail),
        videoId=args.video_id
    ).execute()

    print('##### サムネイル追加のレスポンス(ここから) #####')
    print(thumnail_set_response)
    print('##### サムネイル追加のレスポンス(ここまで) #####')
    print()
    print('サムネイルの追加が完了しました。')
    print('\n')

# 動画をプレイリストに追加する関数
def add_to_playlists(youtube, args):
    playlist_snippet_dict = dict(
        playlistId=args.playlist_id,
        resourceId=dict(
            kind="youtube#video",
            videoId=args.video_id
        )
    )

    add_to_playlists_response = youtube.playlistItems().insert(
        part='snippet',
        body=dict(
            snippet=playlist_snippet_dict,
        )
    ).execute()

    print('##### プレイリスト追加のレスポンス(ここから) #####')
    print(add_to_playlists_response)
    print('##### プレイリスト追加のレスポンス(ここまで) #####')
    print()
    print('プレイリストへの追加が完了しました。')
    print('プレイリストのURLを下に示します。\n' + 
        'https://www.youtube.com/playlist?list=' + args.playlist_id)
    print('\n')

if __name__ == '__main__':
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

    # 実行時の引数の処理
    parser = argparse.ArgumentParser()
    parser.add_argument('--video_id', 
        help='アップデートする動画のID', required=True)
    parser.add_argument('--tags',
        help='動画のタグ。カンマで区切って記載する。この引数は既存のタグを上書きする')
    parser.add_argument('--add_tag', 
        help='動画に追加したいタグ。この引数は既存のタグに影響を与えない')
    parser.add_argument('--title', 
        help='動画のタイトル')
    parser.add_argument('--description', 
        help='動画の説明')
    parser.add_argument('--category',
        help='動画のカテゴリ番号 ' +
                'カテゴリ番号の一覧: https://gist.github.com/dgp/1b24bf2961521bd75d6c')
    parser.add_argument('--publish_date',
        help='公開予約をする場合に日時をISO 8601形式で指定する。ex)2024-01-01T01:01+09:00')
    parser.add_argument('--thumbnail',
        help='サムネイルのファイルパス')
    parser.add_argument('--playlist_id',
        help='プレイリストのURL')
    args = parser.parse_args()

    youtube = get_authenticated_service()

    try:
        update_video(youtube, args)
        if args.thumbnail:
            set_thumbnail(youtube, args)
        if args.playlist_id:
            add_to_playlists(youtube, args)
    except HttpError as e:
        print('An HTTP error {} occurred:\n{}'.format(e.resp.status, e.content))
        print('Tag "{}" was added to video id "{}".'.format(args.add_tag, args.video_id))

プログラム解説

  • クレデンシャルの取得とAPIクライアントの作成はget_authenticated_service関数で実施しています。
  • タグタイトル説明カテゴリ公開予定日の更新はupdate_video関数で行います。
    • 実行時引数--video_idで渡された動画IDを検索して、存在した場合にのみ処理を続行します。
    • 実行時引数--titleが指定されていた場合はタイトルを更新します。
    • 実行時引数--descriptionが指定されていた場合は説明を更新します。
    • 実行時引数--tagsまたは--add_tagが指定されていた場合はタグを更新します。--tagsはタグを上書きします。--add_tagはタグを上書きせず追加します。
    • 実行時引数--categoryが指定されていた場合はカテゴリを更新します。カテゴリは番号で決められています。詳しくはこちら
    • 実行時引数--pulish_dateが指定されている場合は公開予約となります。指定されていない場合は即時公開となります。
  • カスタムサムネイルはset_thumbnail関数で行います。
    • この関数が呼び出されるのは、実行時引数--thumbnailが指定されているときのみです。
    • --thumbnailにはカスタムサムネイルのファイルパスを指定します。
  • 動画をプレイリストに追加するのはadd_to_playlists関数が行います。
    • この関数は実行時引数--playlist_idが指定されているときのみ呼び出されます。
    • --playlist_idにはプレイリストのIDを指定します。
  • 実行完了すると動画の更新後のメタデータ(タグ、タイトル、説明、カテゴリ番号、公開予約日)をターミナル上にprintで表示します。
  • 追加したプレイリストのURLもターミナル上に表示します。
  • 動画情報の更新、カスタムサムネイルのセット、そしてプレイリストへの追加について、最終的なレスポンスのJSONもターミナル上に表示しています。

実行と結果

実行

まず、--helpを指定すると引数の説明を表示できます。

% python update_video.py --help
usage: my_update_video.py [-h] --video_id VIDEO_ID [--tags TAGS] [--add_tag ADD_TAG] [--title TITLE] [--description DESCRIPTION] [--category CATEGORY] [--publish_date PUBLISH_DATE] [--thumbnail THUMBNAIL]
                          [--playlist_id PLAYLIST_ID]

optional arguments:
  -h, --help            show this help message and exit
  --video_id VIDEO_ID   アップデートする動画のID
  --tags TAGS           動画のタグ。カンマで区切って記載する。この引数は既存のタグを上書きする
  --add_tag ADD_TAG     動画に追加したいタグ。この引数は既存のタグに影響を与えない
  --title TITLE         動画のタイトル
  --description DESCRIPTION
                        動画の説明
  --category CATEGORY   動画のカテゴリ番号 カテゴリ番号の一覧: https://gist.github.com/dgp/1b24bf2961521bd75d6c
  --publish_date PUBLISH_DATE
                        公開予約をする場合に日時をISO 8601形式で指定する。ex)2024-01-01T01:01+09:00
  --thumbnail THUMBNAIL
                        サムネイルのファイルパス
  --playlist_id PLAYLIST_ID
                        プレイリストのURL

実行してみます。

  • Pythonファイルと同じディレクトリに「test.jpg」(サムネイル用ファイル)が保存されている状態です。
  • プレイリストは事前に作成しました。プレイリストのIDはURLの”list=”以降の部分。例)https://www.youtube.com/playlist?list=[ここ]
  • 動画は事前にアップロードしました。動画のIDはURLの”.be/”以降の部分。例)https://youtu.be/[ここ]
  • 公開予約にしてみました。日時はISO 8601形式で指定します。
% python update_video.py \
        --video_id "動画IDを入れる" \
        --title "UPDATE TITLE" \
        --description "UPDATE DESCRIPTION" \
        --category 10 \
        --publish_date 2024-10-01T01:01+09:00 \
        --tags aaa,"bbb ccc"\
        --add_tag ddd \
        --thumbnail test.jpg \
        --playlist_id "プレイリストのIDを入れる"

OAuthの同意画面が表示されるので、対象のアカウントを選択します。(アカウントが複数ある場合)
image.png

「The authentication flow has completed. You may close this window.」と表示されたら成功。ウィンドウを閉じます。
image.png

ターミナルに戻ると以下のような画面が表示されます。
image.png

結果確認

  • タイトルが更新されていました。
  • 説明が追加されています。
  • カスタムサムネイルがセットされています。
  • 再生リストに追加されています。
  • 公開予約となっています。
  • タグも入っていました。
    image.png

* カテゴリは音楽(カテゴリ番号は10)となっていました。
image.png

以上です。

参考情報

本プログラムは、下記の3つを参考にしたプログラムとなっています。

https://github.com/youtube/api-samples/blob/master/python/update_video.py

https://github.com/youtube/api-samples/blob/master/python/upload_thumbnail.py

https://github.com/youtube/api-samples/blob/master/python/playlist_updates.py

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!

コメント

コメントする

コメントは日本語で入力してください。(スパム対策)

CAPTCHA

もくじ
閉じる