書くこと
YouTubeに動画をアップロードしたあと、普通ならYouTube Studioで動画のタイトルや説明、タグなどを追加していくと思います。
僕はこれがめんどくさくて、プログラムで動画の情報を更新できないかと考えました。
そこで、Python3のYouTube Data Apiを使用して、動画の情報を更新するプログラムを書いてみました。
更新する動画の項目
このプログラムで更新する動画の設定項目の表になります。
※デフォルト値とは、動画をアップロードしたときにデフォルトで設定されている値のことです。
項目 | デフォルト値 | 備考(本プログラムの動き) |
---|---|---|
タイトル | ファイル名 | |
説明 | なし | |
タグ | なし | タグの上書きと追加ができます。 |
カテゴリ | YouTubeの設定による([設定]→[アップロード動画のデフォルト設定]→[詳細設定] | プログラム上では番号で指定します。 |
公開設定 | YouTubeの設定による([設定]→[アップロード動画のデフォルト設定]→[基本情報]) | 即時公開か公開予約ができるようにします。 |
公開予定日 | なし | 公開予約の場合に日時を指定できるようにします。 |
カスタムサムネイル | なし | ファイルのパスを指定することでサムネイルをセットします。 |
プレイリスト | なし | 事前に作成したプレイリストへの動画の追加ができるようにします |
事前準備(Google Cloud)
Google Cloudの事前準備をします。
プロジェクトの作成
Google Cloudコンソールにログインします。
ここでは「My Project Update Video」という名前でプロジェクトを作成しました。
「YouTube Data API v3」ライブラリの有効化
「APIとサービス」から「ライブラリ」をクリックします。
“youtube”で検索して、「YouTube Data API v3」をクリックします。
「有効にする」をクリックします。
「OAuth同意画面」の作成
「認証情報」画面に行き、「認証情報を作成」から「OAuthクライアントID」をクリックします。
「同意画面を設定」をクリックします。
「外部」にチェックを入れて「作成」をクリックします。
「アプリ名」は”Update Video App”にしました。(任意のものを入力します)
「ユーザーサポートメール」をプルダウンから選びます。
「デベロッパーの連絡先情報」を入力して「保存して次へ」をクリックします。
スコープには「…/auth/youtube.force-ssl」を選択します。
「保存して次へ」をクリックします。
テストユーザーを追加して「保存して次へ」をクリックします。
「OAuth 2.0 クライアント ID」の作成
「認証情報」→「認証情報を作成」→「OAuthクライアントID」をクリックします。
「アプリケーションの種類」は”ウェブアプリケーション”を選択します。
「承認済みのリダイレクトURI」には”http://localhost:8080/”を入力して作成します。
作業の流れ(動画アップロードから〜プログラム実行まで)
動画のアップロードから、プログラム実行までの大まかな流れは以下になります。
- 手動で動画をYouTubeにアップロードする。
- アップロードした動画のIDをメモする。
- プレイリストを作成する。
- プレイリストのIDをメモする。
- 本件のプログラムに動画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の同意画面が表示されるので、対象のアカウントを選択します。(アカウントが複数ある場合)
「The authentication flow has completed. You may close this window.」と表示されたら成功。ウィンドウを閉じます。
ターミナルに戻ると以下のような画面が表示されます。
結果確認
- タイトルが更新されていました。
- 説明が追加されています。
- カスタムサムネイルがセットされています。
- 再生リストに追加されています。
- 公開予約となっています。
- タグも入っていました。
* カテゴリは音楽(カテゴリ番号は10)となっていました。
以上です。
参考情報
本プログラムは、下記の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
コメント