[Python]Youtube Data API v3で再生リストに動画を追加する

Youtube Data API v3を使用して動画を再生リストに追加する手順についての覚え書き

# はじめに
Youtube Data API v3でYoutbeにアクセスするための作業はこちらで完了しているものとする

https://qiita.com/tatamyiwathy/items/7013dfd5dc6fa013134d


# 動画を追加する

下記APIを使用する

https://developers.google.com/youtube/v3/docs/playlistItems/insert?hl=ja

## サンプルコード
上記ページのサンプルコード

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

# Sample Python code for youtube.playlistItems.insert
# 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"
    "https://www.googleapis.com/auth/youtube.readonly"
]

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 = "secret.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(open_browser=False)
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials)

    request = youtube.playlistItems().insert(
        part="snippet",
        body={
          "snippet": {
            "playlistId": "PLAYLISTID",
            "position": 0,
            "resourceId": {
              "kind": "youtube#video",
              "videoId": "F12D4zPcUEA"
            }
          }
        }
    )
    response = request.execute()

    print(response)

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


# ハマリどころ

以下はまったところ

## スコープ
Youtube Data APIは実行できる場所（スコープ）が決まっていてそれを指定しないと```Insufficient Permission```って怒られる

```Python
Traceback (most recent call last):
  File "/mnt/c/Users/foobar/Projects/myproj/Youtube/test.py", line 52, in <module>
    main()
  File "/mnt/c/Users/foobar/Projects/myproj/Youtube/test.py", line 47, in main
    response = request.execute()
  File "/home/foobar/.local/lib/python3.10/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/home/foobar/.local/lib/python3.10/site-packages/googleapiclient/http.py", line 938, in execute
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&alt=json returned "Request had insufficient authentication scopes.". Details: "[{'message': 'Insufficient Permission', 'domain': 'global', 'reason': 'insufficientPermissions'}]">
```

スコープはコードのここで指定する

``` Python
scopes = [
    "https://www.googleapis.com/auth/youtube.force-ssl", #追加更新系APIで必要
    "https://www.googleapis.com/auth/youtube.readonly" # 取得系APIで必要
]
```

サンプルコードをそのまま実行していればハマらなかったところ。
自分は情報取得系APIのサンプルコードの流用で始めたのでスコープが取得系のままなのを気が付かずハマリ散らかした

## 認証

スコープに複数のURLがあると認証のとき以下のような確認がある
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35364/df80cdcd-7596-986d-0c5e-5ae959dae89b.png)
このとき```アクセスできる情報を選択してください```のところにチェックを入れないとアクセスに失敗する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35364/2853decb-25f4-71e2-6f58-db9549204480.png)

続行ボタンをクリックするとスクリプトでエラーが発生する

```Python
Traceback (most recent call last):
  File "/mnt/c/Users/foobar/Projects/myproj/Youtube/test.py", line 52, in <module>
    main()
  File "/mnt/c/Users/foobar/Projects/myproj/Youtube/test.py", line 30, in main
    credentials = flow.run_local_server(open_browser=False)
  File "/home/foobar/.local/lib/python3.10/site-packages/google_auth_oauthlib/flow.py", line 455, in run_local_server
    self.fetch_token(
  File "/home/foobar/.local/lib/python3.10/site-packages/google_auth_oauthlib/flow.py", line 285, in fetch_token
    return self.oauth2session.fetch_token(self.client_config["token_uri"], **kwargs)
  File "/home/foobar/.local/lib/python3.10/site-packages/requests_oauthlib/oauth2_session.py", line 271, in fetch_token
    self._client.parse_request_uri_response(
  File "/usr/lib/python3/dist-packages/oauthlib/oauth2/rfc6749/clients/web_application.py", line 220, in parse_request_uri_response
    response = parse_authorization_code_response(uri, state=state)
  File "/usr/lib/python3/dist-packages/oauthlib/oauth2/rfc6749/parameters.py", line 281, in parse_authorization_code_response
    raise_from_error(params.get('error'), params)
  File "/usr/lib/python3/dist-packages/oauthlib/oauth2/rfc6749/errors.py", line 399, in raise_from_error
    raise cls(**kwargs)
oauthlib.oauth2.rfc6749.errors.AccessDeniedError: (access_denied) 
```

## 再生リストの動画表示順序の設定

再生リストは「デフォルトの動画表示順序」という設定がある。これが「Youtube内で手動で並べ替え」以外になっているとサンプルコードはエラーを出す

```Python
Traceback (most recent call last):
  File "/mnt/c/Users/satoshi/Projects/myproj/Youtube/test.py", line 52, in <module>
    main()
  File "/mnt/c/Users/satoshi/Projects/myproj/Youtube/test.py", line 47, in main
    response = request.execute()
  File "/home/satoshi/.local/lib/python3.10/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/home/satoshi/.local/lib/python3.10/site-packages/googleapiclient/http.py", line 938, in execute
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&alt=json returned "Playlist should use manual sorting to support position.". Details: "[{'message': 'Playlist should use manual sorting to support position.', 'domain': 'youtube.playlistItem', 'reason': 'manualSortRequired'}]">
```

解決するにはplaylistitemリソースの```position```プロパティを削除するか、

```Python
       body={
          "snippet": {
            "playlistId": "PLAYLISTID",
            "position": 0, # エラーの原因
            "resourceId": {
              "kind": "youtube#video",
              "videoId": "F12D4zPcUEA"
            }
          }
        }
```

再生リストの「デフォルトの動画表示順序」設定を「Youtube内で手動で並べ替え」のものにする（Youtube側で設定）

# おわり

再生リストに動画を追加するだけなのに以外にもつまづきポイントが多くて泣きそう