Python3のYouTube Data Apiで動画を投稿/サムネイル追加/動画情報の編集/削除する

# やること
[YouTube Data Api](https://developers.google.com/youtube/v3/getting-started)を使用して、４つの作業を[Python3](https://www.python.org/downloads/)のプログラムで行ってみようと思います。
以下の順番で行います。
1. 動画のアップロード
1. サムネイルの追加
1. 動画情報の編集
1. 動画の削除

# 環境
実行する環境を以下に示します。
| 項目 | バージョン |
|:-|:-|
| OS | macOS Ventura 13.1 |
| Python | 3.9.9  |

# 手順
GCPの利用開始はされている前提とします。
## GCP関連
### プロジェクトの作成
[GCPコンソール](https://console.cloud.google.com/)にログインして、新しいプロジェクトを作成します。
<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/7bc56bb3-6d76-2b02-ec78-e6d7c7fa147d.png">

ここでは「Project YouTubeAPI」としました。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/4bd601e7-d852-c656-95bd-31ed1b13ccad.png">

### YouTube Data Apiの有効化
ライブラリで"YouTube"で検索して、「YouTube Data Api v3」をクリックします。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/a0bfbf58-953c-38c9-25cd-0daa83d8a61d.png">

「有効にする」ボタンをクリックして有効にします。
<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/dd82358f-82fc-3eb9-d70b-2283a8ba0858.png">

### 認証関連の作成
#### OAuth同意画面の作成
OAuth同意画面を作成します。プログラムを実行したときに表示される認証画面のカスタマイズです。
「User Type」は「外部」で「作成」をクリックします。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/44d9f0ad-9f3e-fc43-0991-76ea07310e79.png">

アプリ名はここでは「App YouTube Data Api」としました。
サポートメールをプルダウンから選択します。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/83fc9aa9-ed5e-dd29-cf21-2d81509683a5.png">

デベロッパーの連絡先情報に、メールアドレスを入力して「保存して次へ」をクリックします。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/c7606704-dad8-8838-c9e4-e13502f6c085.png">

スコープを追加します。
| やりたいこと | 必要なスコープ |
|:-|:-|
| 動画の投稿  | https://www.googleapis.com/auth/youtube.upload  |
| サムネイルの追加  | https://www.googleapis.com/auth/youtube.force-ssl  |
|動画の編集|同上|
|動画の削除|同上|

今回必要なスコープは上の表のとおりとなるので、二種類のスコープを追加しました。
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/c27852c0-aec0-5e49-6208-cacd490382bd.png">

テストに使用するユーザーを追加して、「保存して次へ」をクリックすると完了です。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/38c1821d-63bc-5e4c-5201-afc0593873c5.png">


#### OAuthクライアントIDの作成
「認証情報」から「認証情報を作成」→「OAuthクライアントID」をクリックします。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/7ff59369-7faa-a921-2b56-eee1f73b259b.png">

「アプリケーションの種類」は「ウェブアプリケーション」を選択し、「承認済みのリダイレクトURI」には`http://localhost:8080/`を入力します。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/c7239d24-50d7-1683-dd78-7f49c5664ffe.png">

OAuthクライアントIDの作成が完了するので、シークレットのJSONファイルをダウンロードしておきます。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/d8d8c956-6299-039d-3b1d-8ee939026e42.png">


## プログラム
### 準備
#### サンプルコードの取得方法
Python3で書かれたサンプルコードが公開されています。
YouTube Data Apiの[Reference](https://developers.google.com/youtube/v3/docs)にアクセスします。
例えば、動画の投稿のサンプルコードであれば、左側ペインから「Videos」→「insert」と展開し、画面右側にある「Try this method」の拡大ボタンをクリックします。
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/f4e61212-aee9-7e28-2c60-2a19f1b5a90a.png">

タブから「Python」を選択すれば、Pythonのサンプルコードをコピーすることができます。
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/c5cc91ca-7931-77eb-57e5-1e0c9504ba0a.png">

#### シークレットのJSONファイルの配置
これから動画の投稿、サムネイルの追加、動画情報の編集、動画の削除を行っていきますが、その際に実行するPythonファイル（ここではすべて`main.py`）と同じ階層に、先ほどダウンロードしたシークレット情報が記載されたJSONファイルを配置します。


#### 必要パッケージのダウンロード
PythonのGoogle APIs Client LibraryをPIPでダウンロードします。必要に応じてPython仮想環境を用意します。
```text
% python -m venv .venv
% source .venv/bin/activate
(.venv) % pip install --upgrade google-api-python-client
```

`google-auth-oauthlib`と`google-auth-httplib2`もダウンロードします。
```text
(.venv) % pip install --upgrade google-auth-oauthlib google-auth-httplib2
```

### 動画投稿
#### コード
まず、動画を投稿してみます。
Referenceの[Videos: insert](https://developers.google.com/youtube/v3/docs/videos/insert)からサンプルコードをコピーして修正しました。
概ね修正箇所は以下のとおりです。
* `client_secrets_file`変数にはシークレット情報が記載されたJSONファイル(ダウンロードしたもの)へのファイルパスを記載します。ここでは`main.py`と同じディレクトリに保存しているのでファイル名となっています。
* `youtube.videos().insert()`メソッドにアップロードする動画の情報などを記載していきますが、`part`パラメータと`body`パラメータが必要になってきます。
* `part`パラメータには`body`パラメータで使用するプロパティを代入します。ここでは、`snippet`と`status`のプロパティを使用しています。
* `snippet`プロパティでは以下の値を設定しています。

| プロパティ | 内容 |
|:-|:-|
| channelId  | 動画を投稿するチャンネルのID |
|  title | 動画のタイトル |
|description|動画の説明|
|tags|動画のタグ。リスト型で指定|
|categoryId|動画のカテゴリを数字で指定。ここでは「旅行とイベント」のカテゴリの19を指定|
|defaultLanguage|タイトルと説明の言語|
|defaultAudioLanguage|動画の言語|

* `status`プロパティでは以下の値を設定しています。

| プロパティ | 内容 |
|:-|:-|
|uploadStatus|アップロードされたビデオのステータス。公開予約のときはこのプロパティは使用しない。`publishAt`を使用する|
|privacyStatus|公開設定。ここでは非公開にしている|
|license|ライセンス。`youtube`は「標準のYouTubeライセンス」|
|embeddable|埋め込みを許可する|

* `media_body=MediaFileUpload("test.mp4")`の部分ですが、`MediaFileUpload`メソッドの引数に動画ファイルのパスを渡します。ここでは同じディレクトリにある`test.mp4`ファイルを指定しています。
* 最後に、コードの`flow`には`run_console()`はありませんでした。VS Codeで「型定義へ移動」で調べてみると`run_local_server()`だったため修正しています。

```python
# -*- coding: utf-8 -*-

# Sample Python code for youtube.videos.insert
# NOTES:
# 1. This sample code uploads a file and can't be executed via this interface.
#    To test this code, you must run it locally using your own API credentials.
#    See: https://developers.google.com/explorer-help/code-samples#python
# 2. This example makes a simple upload request. We recommend that you consider
#    using resumable uploads instead, particularly if you are transferring large
#    files or there's a high likelihood of a network interruption or other
#    transmission failure. To learn more about resumable uploads, see:
#    https://developers.google.com/api-client-library/python/guide/media_upload

import os

import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
from googleapiclient.http import MediaFileUpload

scopes = ["https://www.googleapis.com/auth/youtube.upload"]

def main():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

    api_service_name = "youtube"
    api_version = "v3"
    client_secrets_file = "client_secrets.json" # 修正(ダウンロードしたJSONファイル名)

    # Get credentials and create an API client
    flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
        client_secrets_file, scopes)
    credentials = flow.run_local_server() # 修正(「flow.run_console()」から「flow.run_local_server()」)
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials)

    request = youtube.videos().insert(
        part="snippet,status", # 追記
        body={
            ### 追記(ここから) ###
            "snippet": {
                "channelId": "XXXXXXXXXXXXXXXXXXXXXXXX", # 動画を投稿するチャンネルIDを記載
                "title": "タイトルのテスト", # 動画のタイトルを設定
                "description": "説明のテストです。", # 動画の説明を追加
                "tags": ["Tag 1", "Tag 2", "Tag 3"], # タグを追加
                "categoryId": "19", # "19"は「旅行とイベント」のカテゴリ。
                "defaultLanguage": "ja_JP", # タイトルと説明の言語
                "defaultAudioLanguage": "ja_JP", # 動画の言語
            },
            "status": {
                "uploadStatus": "uploaded", # アップロードされたビデオのステータス
                "privacyStatus": "private", # 公開設定は「非公開」
                "license": "youtube", # 標準のYouTubeライセンス
                "embeddable": "true", # 動画の埋め込みを許可する
            },
            ### 追記(ここまで) ###
        },
        
        # TODO: For this request to work, you must replace "YOUR_FILE"
        #       with a pointer to the actual file you are uploading.
        media_body=MediaFileUpload("test.mp4") # 修正(動画ファイルのパスを記載)
    )
    response = request.execute()

    print(response)

if __name__ == "__main__":
    main()
```

#### 動作確認
実行してみます。
```text
(.venv) % python main.py
```

OAuthの画面がブラウザで起動します。下は初めてログインするブラウザでの動きです。
使用するアカウントのメールアドレスを入力します。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/dce5b80c-2af0-facf-dec7-37233fae5edf.png">

パスワードを入力して「次へ」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/52f7544f-6a99-1034-baa6-5dbce420672f.png">

複数のアカウントが表示された場合は、チャンネルを保有するアカウントを選択します。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/34d49628-23d6-9ccf-eeb0-01fa0c6b4633.png">

下記の画面が表示されたら「続行」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/06073419-6f9c-c4a4-6723-8419fe3df104.png">

下記の画面が表示されたら「許可」をクリックします。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/d6b759c3-5fee-4439-8ac0-0fa61e443eeb.png">

「The authentication flow has completed. You may close this window.」と表示されたら、ブラウザの画面は閉じます。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/914c9f65-36d9-2b0a-60d7-76e3d3bad148.png">

ターミナルで以下のような結果が返ってきています。
```text
(.venv) % python main.py
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=(
（省略）&access_type=offline
{'kind': 'youtube#video', 'etag': 'XXXXXXXXXXXXXX_XXX_XX_XXXXX', 'id': 'XXXXXXXXXXX', 'snippet': {'publishedAt': '2023-02-16T05:00:35Z', 'channelId': 'UCxUKQ85KZ200o4ioltA2kJQ', 'title': 'タイトルのテスト', 'description': '説明のテストです。', 'thumbnails': {'default': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/default.jpg?sqp=CNDwtp8G&rs=AOn4CLA1gObdbPgQNTemcse9Hrul6EiVsg', 'width': 120, 'height': 90}, 'medium': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/mqdefault.jpg?sqp=CNDwtp8G&rs=AOn4CLCvezrVCeWzUdnDuLgJYDJsOudvkA', 'width': 320, 'height': 180}, 'high': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/hqdefault.jpg?sqp=CNDwtp8G&rs=AOn4CLBTfv9ceJNaIsL09wWQ-uCpaGwVLQ', 'width': 480, 'height': 360}}, 'channelTitle': 'XXXXX', 'tags': ['Tag 1', 'Tag 2', 'Tag 3'], 'categoryId': '19', 'liveBroadcastContent': 'none', 'defaultLanguage': 'ja', 'localized': {'title': 'タイトルのテスト', 'description': '説明のテストです。'}, 'defaultAudioLanguage': 'ja'}, 'status': {'uploadStatus': 'uploaded', 'privacyStatus': 'private', 'license': 'youtube', 'embeddable': True, 'publicStatsViewable': True}}
```

戻り値のJSONのみフォーマットしてみると以下のようなものでした。シングルクォーテーションをダブルクォーテーションに置換しています。
* 戻り値（参考）
アップロードした動画の設定値が返ってきていることがわかります。
```json
{
    "kind": "youtube#video",
    "etag": "XXXXXXXXXXXXXX_XXX_XX_XXXXX",
    "id": "XXXXXXXXXXX",
    "snippet": {
        "publishedAt": "2023-02-16T05: 00: 35Z",
        "channelId": "XXXXXXXXXXXXXXXXXXXXXXXX",
        "title": "タイトルのテスト",
        "description": "説明のテストです。",
        "thumbnails": {
            "default": {
                "url": "https: //i9.ytimg.com/vi/XXXXXXXXXXX/default.jpg?sqp=CNDwtp8G&rs=AOn4CLA1gObdbPgQNTemcse9Hrul6EiVsg",
                "width": 120,
                "height": 90
            },
            "medium": {
                "url": "https://i9.ytimg.com/vi/XXXXXXXXXXX/mqdefault.jpg?sqp=CNDwtp8G&rs=AOn4CLCvezrVCeWzUdnDuLgJYDJsOudvkA",
                "width": 320,
                "height": 180
            },
            "high": {
                "url": "https://i9.ytimg.com/vi/XXXXXXXXXXX/hqdefault.jpg?sqp=CNDwtp8G&rs=AOn4CLBTfv9ceJNaIsL09wWQ-uCpaGwVLQ",
                "width": 480,
                "height": 360
            }
        },
        "channelTitle": "[ここにはチャンネル名が表示されます]",
        "tags": [
            "Tag 1",
            "Tag 2",
            "Tag 3"
        ],
        "categoryId": "19",
        "liveBroadcastContent": "none",
        "defaultLanguage": "ja",
        "localized": {
            "title": "タイトルのテスト",
            "description": "説明のテストです。"
        },
        "defaultAudioLanguage": "ja"
    },
    "status": {
        "uploadStatus": "uploaded",
        "privacyStatus": "private",
        "license": "youtube",
        "embeddable": True,
        "publicStatsViewable": True
    }
}
```

実際にアップロードした動画を詳細画面は以下のようになっていました。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/459de0f7-42a8-fb06-5eab-afcd6b771f4a.png">
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/3ad3952e-6603-2f00-5c56-2cc5412c2f44.png">
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/ef3832c0-6639-58ee-f8ca-25c704a9021f.png">
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/41ff9145-36d7-c557-5931-83de3bbcbc33.png">


### サムネイルの追加
次は投稿した動画へ、カスタムのサムネイルを追加してみたいと思います。
#### コード
サンプルコードは[Reference](https://developers.google.com/youtube/v3/docs)の[Thumbnails: set](https://developers.google.com/youtube/v3/docs/thumbnails/set)の「Try this method」からコピーします。
修正箇所は以下になります。
* 動画投稿のときと同じで、Pythonファイルと同じディレクトリに`client_secrets.json`を配置しています。コード上の`client_secrets_file`に代入します。
* `videoId`パラメータを追記しています。動画のIDをString型で渡します。動画のIDは動画のURL(`https://youtu.be/XXXXXXXXXXX`)の`XXXXXXXXXXX`の部分です。
* 動画投稿のときと同じように、`flow`には`run_console()`はありませんでした。`run_local_server()`に修正しています。
* `MediaFileUpload`メソッドの引数にサムネイル画像のファイルパスを指定します。ここでは同じディレクトリの`test.jpg`を渡しています。ちなみに画像はフリー素材をダウンロードしました。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/ccc5ef52-0904-e812-77a9-e84c054d45b9.jpeg">


```python
# -*- coding: utf-8 -*-

# Sample Python code for youtube.thumbnails.set
# NOTES:
# 1. This sample code uploads a file and can't be executed via this interface.
#    To test this code, you must run it locally using your own API credentials.
#    See: https://developers.google.com/explorer-help/code-samples#python
# 2. This example makes a simple upload request. We recommend that you consider
#    using resumable uploads instead, particularly if you are transferring large
#    files or there's a high likelihood of a network interruption or other
#    transmission failure. To learn more about resumable uploads, see:
#    https://developers.google.com/api-client-library/python/guide/media_upload

import os

import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
from googleapiclient.http import MediaFileUpload

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

def main():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

    api_service_name = "youtube"
    api_version = "v3"
    client_secrets_file = "client_secrets.json" # 修正(ダウンロードしたJSONファイル名)

    # Get credentials and create an API client
    flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
        client_secrets_file, scopes)
    credentials = flow.run_local_server() # 修正(「flow.run_console()」から「flow.run_local_server()」)
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials)

    request = youtube.thumbnails().set(
        
        # TODO: For this request to work, you must replace "YOUR_FILE"
        #       with a pointer to the actual file you are uploading.
        media_body=MediaFileUpload("test.jpg"), # 修正(サムネイル画像のパス)
        videoId="XXXXXXXXXXX" # 追記(動画のIDを指定)
    )
    response = request.execute()

    print(response)

if __name__ == "__main__":
    main()
```

#### 動作確認
実行してみます。
```text
(.venv) % python main.py
```

動画投稿のときと同じようにブラウザで認証画面が表示されるので、同じように進みます。
認可が完了すると、値がJSONで返ってきます。
```text
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=(
（省略）&access_type=offline
{'kind': 'youtube#thumbnailSetResponse', 'etag': 'XXXXXXXXXXXXXXXXXXXXXXXXXXX', 'items': [{'default': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/default.jpg?sqp=CNiUuJ8G&rs=AOn4CLBaEMKtMTXACu0arWMChlQK3jAolw', 'width': 120, 'height': 90}, 'medium': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/mqdefault.jpg?sqp=CNiUuJ8G&rs=AOn4CLD4jdFlQCbNMuR9ytqrQBM7kbORlQ', 'width': 320, 'height': 180}, 'high': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/hqdefault.jpg?sqp=CNiUuJ8G&rs=AOn4CLB_3RzsRv94GQNfiuASGeAOQIMxng', 'width': 480, 'height': 360}, 'standard': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/sddefault.jpg?sqp=CNiUuJ8G&rs=AOn4CLDqwUTjD2gYJxkRpvHrUJys9JsyQw', 'width': 640, 'height': 480}}]}
```

YouTube Studioで動画の詳細を見てみます。
サムネイルがセットされていることが確認できました。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/b50995bc-22c8-cc7c-f779-d147a27e2a5e.png">

### 動画情報の編集
次は動画の情報の編集を行ってみます。
ここでは、タイトルの修正、説明の修正、タグの追加を行ってみたいと思います。
#### コード
サンプルコードは[Reference](https://developers.google.com/youtube/v3/docs)の[Videos: update](https://developers.google.com/youtube/v3/docs/videos/update)のTry this methodから取得します。
修正箇所は以下になります。
* 動画投稿のときと同じように、`client_secrets.json`を渡します。
* `run_console()`を`run_local_server()`に修正します。
* `part`パラメータが必要なため追記します。`body`パラメータ内で`id`と`snippet`を使用するため`part`に渡します。
* `id`は動画のIDになります。
* 動画投稿のときと同じように、`channelId`、`title`、`description`、`tags`を使用しています。修正したい文言に変えて渡しています。
※`categoryId`を指定しないとエラーになったため加えています。

```python
# -*- coding: utf-8 -*-

# Sample Python code for youtube.videos.update
# See instructions for running these code samples locally:
# https://developers.google.com/explorer-help/code-samples#python

import os

import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors

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

def main():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

    api_service_name = "youtube"
    api_version = "v3"
    client_secrets_file = "client_secrets.json"

    # Get credentials and create an API client
    flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
        client_secrets_file, scopes)
    credentials = flow.run_local_server() # 修正(「flow.run_console()」から「flow.run_local_server()」)
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials)

    request = youtube.videos().update(
        part="id,snippet",
        body={
            "id": "XXXXXXXXXXX", # 動画のIDを指定
            "snippet": {
                "channelId": "XXXXXXXXXXXXXXXXXXXXXXXX", # 動画を投稿するチャンネルIDを記載
                "title": "タイトルのテスト２", # 「タイトルのテスト２」に修正
                "description": "説明のテストです。説明に追記を行いました。", # 「説明に追記を行いました。」を追記
                "tags": ["Tag 1", "Tag 2", "Tag 3", "Tag 4", "Tag 5"], # "Tag 4"と"Tag 5"を追加
                "categoryId": "19" # カテゴリはそのまま
            }
        }
    )
    response = request.execute()

    print(response)

if __name__ == "__main__":
    main()
```

#### 動作確認
実行してみます。
OAuthの認証画面の操作は同じです。
```text
(.venv) % python main.py
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=（省略）&access_type=offline
{'kind': 'youtube#video', 'etag': 'XXXXXXXXXX_XXXXXXXXXXXXXXXX', 'id': 'XXXXXXXXXXX', 'snippet': {'publishedAt': '2023-02-16T05:00:35Z', 'channelId': 'XXXXXXXXXXXXXXXXXXXXXXXX', 'title': 'タイトルのテスト２', 'description': '説明のテストです。説明に追記を行いました。', 'thumbnails': {'default': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/default.jpg?sqp=COSpuJ8G&rs=AOn4CLBFhXnimgfVcqrVPlYaSBbuKqxt1Q', 'width': 120, 'height': 90}, 'medium': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/mqdefault.jpg?sqp=COSpuJ8G&rs=AOn4CLAeLDZwAPoSpR4D4Dd9u_RgUxwMHw', 'width': 320, 'height': 180}, 'high': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/hqdefault.jpg?sqp=COSpuJ8G&rs=AOn4CLBnEzWO9Pm9XGWJLoaHHWbEZRCx6A', 'width': 480, 'height': 360}, 'standard': {'url': 'https://i9.ytimg.com/vi/XXXXXXXXXXX/sddefault.jpg?sqp=COSpuJ8G&rs=AOn4CLAf5qcrYqq_z85JwJ5KKxZAisd4zQ', 'width': 640, 'height': 480}}, 'channelTitle': 'あつのすけ', 'tags': ['Tag 1', 'Tag 2', 'Tag 3', 'Tag 4', 'Tag 5'], 'categoryId': '19', 'liveBroadcastContent': 'none', 'defaultLanguage': 'ja', 'localized': {'title': 'タイトルのテスト２', 'description': '説明のテストです。説明に追記を行いました。'}, 'defaultAudioLanguage': 'ja'}}
```

YouTube Studioで確認しても変更されていることがわかりました。
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/1825c74f-a51a-7069-10a4-01fe9bcb6352.png">
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/7ff7534c-9064-57c9-c504-41547ee8c855.png">


### 動画の削除
最後に動画の削除を行います。
#### コード
サンプルコードは[Reference](https://developers.google.com/youtube/v3/docs)の[Videos: delete](https://developers.google.com/youtube/v3/docs/videos/delete)のTry this methodから取得します。
修正箇所は以下のとおりです。
* 動画投稿のときと同じように、`client_secrets.json`を渡します。
* `run_console()`を`run_local_server()`に修正します。
* `id`に動画のIDを渡します。

```python
# -*- coding: utf-8 -*-

# Sample Python code for youtube.videos.delete
# See instructions for running these code samples locally:
# https://developers.google.com/explorer-help/code-samples#python

import os

import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors

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

def main():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

    api_service_name = "youtube"
    api_version = "v3"
    client_secrets_file = "client_secrets.json" # 修正(ダウンロードしたJSONファイル名)

    # Get credentials and create an API client
    flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
        client_secrets_file, scopes)
    credentials = flow.run_local_server() # 修正(「flow.run_console()」から「flow.run_local_server()」)
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials)

    request = youtube.videos().delete(
        id="XXXXXXXXXXX" # 動画のIDを指定
    )
    request.execute()

if __name__ == "__main__":
    main()
```

#### 動作確認
実行します。
```text
(.venv) % python main.py 
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=（省略）&access_type=offline
```

動画が削除されました。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252459/2e79cefa-c310-a447-31d7-3831ec193215.png">

# 所感
一日に実行できるリミットはありますが、使いこなせれば便利なのではないかと思いました。YouTube Studioでポチポチボタンを押したりコピペを繰り返したりする必要がなくなるので。
また、Pythonプログラムを修正して、`args`などを使えばひとつのプログラムで異なった動画をアップロードすることができると思いました。
`args`とスプレッドシートやExcelの関数を使えば、スプレッドシートやExcelに動画の情報を書き込み、関数でPythonを実行するときのコマンドを作成し、コピペでさまざまな動画をアップロードできると感じました。
参考になれば幸いです。

# 参考サイト
https://www.home-movie.biz/free_movie.html

https://googleapis.github.io/google-api-python-client/docs/oauth.html

https://developers.google.com/youtube/v3/getting-started

https://developers.google.com/youtube/v3/docs