[Python] Google API를 이용하여 유튜브에 동영상 올리기

개요

이전 게시물에서 파이썬으로 만든 동영상을 Google API를 이용하여 유튜브에 올리고자 합니다. 필요한 기능은 단순히 유튜브에 동영상을 올리는 것 뿐이므로, 가이드에서 제공하는 예제를 이용하여 동영상 업로드만 테스트해보고자 합니다.

 

이곳에 나와있는 예제를 이용한 것입니다.

https://developers.google.com/youtube/v3/guides/uploading_a_video?hl=ko

 

예제를 따라 처음부터 하나씩 해보겠습니다.

 

내용

1) Python용 Google API 클라이언트 라이브러리(google-api-python-client)를 설치합니다.

https://developers.google.com/youtube/v3/guides/uploading_a_video?hl=ko

가이드에 따라 가상환경 먼저 구성하였으며, 이름은 'youtube'로 하였습니다. 

pip install virtualenv
virtualenv youtube
youtube\Scripts\activate
youtube\Scripts\pip.exe install google-api-python-client

가상환경 구성 및 API 라이브러리설치 화면

 

 

2) Google에 애플리케이션을 등록하여 사용자 데이터에 대한 액세스 권한을 부여할 때 OAuth 2.0 프로토콜을 사용할 수 있도록 합니다.

 

Google Cloud Platform

하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요.

accounts.google.com

developers.google.com/youtube/registering_an_application?hl=koconsole.developers.google.com/apis/credentials?hl=ko

 

 

 

애플리케이션 유형을 '웹 애플리케이션'으로 선택하고, 앱 이름을 임의로 입력합니다.

저는 '유튜브 동영상 업로드 테스트'로 하였습니다.

 

그리고 맨 아래에 URI를 입력합니다. 여기서는 로컬호스트에서 저 혼자만 로그인 하는 것이므로 로컬호스트 (http://localhost:8080/) 로 입력하였습니다. 만약 포트 8080으로 적었는데 이후에 스크립트 실행시 포트 관련 에러 발생하면 메시지에 따라서 8080 등으로 바꾸어야 합니다.

 

작성 후 아래의 만들기 버튼을 누르면 OAuth 클라이언트가 생성됩니다. 

여기서 나오는 ID와 Key가 다음 단계에서 이용됩니다.

 

 

 

 

3) 이 스크립트로 OAuth 2.0 단계를 사용하려면 APIs Console의 정보를 포함하는 client_secrets.json 파일을 만들어야 합니다. 이 파일은 스크립트와 같은 디렉토리에 있어야 합니다.

아래 내용에 따라 client_secrets.json 파일과 upload_video.py 파일을 작성합니다.

 


client_secrets.json 파일 내용

 -> 아래 파일에서 id와 secret은 이전 단계에서 받은 내용을 입력해야 합니다.

{
  "web": {
    "client_id": "[[INSERT CLIENT ID HERE]]",
    "client_secret": "[[INSERT CLIENT SECRET HERE]]",
    "redirect_uris": [],
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://accounts.google.com/o/oauth2/token"
  }
}

 

upload_video.py 파일 내용
 - 예제 코드를 파이썬3에 맞게 수정한 것입니다. (2to3 사용)

#!/usr/bin/python

import http.client
import httplib2
import os
import random
import sys
import time

from apiclient.discovery import build
from apiclient.errors import HttpError
from apiclient.http import MediaFileUpload
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import argparser, run_flow


# Explicitly tell the underlying HTTP transport library not to retry, since
# we are handling retry logic ourselves.
httplib2.RETRIES = 1

# Maximum number of times to retry before giving up.
MAX_RETRIES = 10

# Always retry when these exceptions are raised.
RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError, http.client.NotConnected,
  http.client.IncompleteRead, http.client.ImproperConnectionState,
  http.client.CannotSendRequest, http.client.CannotSendHeader,
  http.client.ResponseNotReady, http.client.BadStatusLine)

# Always retry when an apiclient.errors.HttpError with one of these status
# codes is raised.
RETRIABLE_STATUS_CODES = [500, 502, 503, 504]

# The CLIENT_SECRETS_FILE variable specifies the name of a file that contains
# the OAuth 2.0 information for this application, including its client_id and
# client_secret. You can acquire an OAuth 2.0 client ID and client secret from
# the Google API Console at
# https://console.developers.google.com/.
# Please ensure that you have enabled the YouTube Data API for your project.
# For more information about using OAuth2 to access the YouTube Data API, see:
#   https://developers.google.com/youtube/v3/guides/authentication
# For more information about the client_secrets.json file format, see:
#   https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
CLIENT_SECRETS_FILE = "client_secrets.json"

# This OAuth 2.0 access scope allows an application to upload files to the
# authenticated user's YouTube channel, but doesn't allow other types of access.
YOUTUBE_UPLOAD_SCOPE = "https://www.googleapis.com/auth/youtube.upload"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"

# This variable defines a message to display if the CLIENT_SECRETS_FILE is
# missing.
MISSING_CLIENT_SECRETS_MESSAGE = """
WARNING: Please configure OAuth 2.0

To make this sample run you will need to populate the client_secrets.json file
found at:

   %s

with information from the API Console
https://console.developers.google.com/

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
""" % os.path.abspath(os.path.join(os.path.dirname(__file__),
                                   CLIENT_SECRETS_FILE))

VALID_PRIVACY_STATUSES = ("public", "private", "unlisted")


def get_authenticated_service(args):
  flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE,
    scope=YOUTUBE_UPLOAD_SCOPE,
    message=MISSING_CLIENT_SECRETS_MESSAGE)

  storage = Storage("%s-oauth2.json" % sys.argv[0])
  credentials = storage.get()

  if credentials is None or credentials.invalid:
    credentials = run_flow(flow, storage, args)

  return build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
    http=credentials.authorize(httplib2.Http()))

def initialize_upload(youtube, options):
  tags = None
  if options.keywords:
    tags = options.keywords.split(",")

  body=dict(
    snippet=dict(
      title=options.title,
      description=options.description,
      tags=tags,
      categoryId=options.category
    ),
    status=dict(
      privacyStatus=options.privacyStatus
    )
  )

  # Call the API's videos.insert method to create and upload the video.
  insert_request = youtube.videos().insert(
    part=",".join(list(body.keys())),
    body=body,
    # The chunksize parameter specifies the size of each chunk of data, in
    # bytes, that will be uploaded at a time. Set a higher value for
    # reliable connections as fewer chunks lead to faster uploads. Set a lower
    # value for better recovery on less reliable connections.
    #
    # Setting "chunksize" equal to -1 in the code below means that the entire
    # file will be uploaded in a single HTTP request. (If the upload fails,
    # it will still be retried where it left off.) This is usually a best
    # practice, but if you're using Python older than 2.6 or if you're
    # running on App Engine, you should set the chunksize to something like
    # 1024 * 1024 (1 megabyte).
    media_body=MediaFileUpload(options.file, chunksize=-1, resumable=True)
  )

  resumable_upload(insert_request)

# This method implements an exponential backoff strategy to resume a
# failed upload.
def resumable_upload(insert_request):
  response = None
  error = None
  retry = 0
  while response is None:
    try:
      print("Uploading file...")
      status, response = insert_request.next_chunk()
      if response is not None:
        if 'id' in response:
          print(("Video id '%s' was successfully uploaded." % response['id']))
        else:
          exit("The upload failed with an unexpected response: %s" % response)
    except HttpError as e:
      if e.resp.status in RETRIABLE_STATUS_CODES:
        error = "A retriable HTTP error %d occurred:\n%s" % (e.resp.status,
                                                             e.content)
      else:
        raise
    except RETRIABLE_EXCEPTIONS as e:
      error = "A retriable error occurred: %s" % e

    if error is not None:
      print(error)
      retry += 1
      if retry > MAX_RETRIES:
        exit("No longer attempting to retry.")

      max_sleep = 2 ** retry
      sleep_seconds = random.random() * max_sleep
      print(("Sleeping %f seconds and then retrying..." % sleep_seconds))
      time.sleep(sleep_seconds)

if __name__ == '__main__':
  argparser.add_argument("--file", required=True, help="Video file to upload")
  argparser.add_argument("--title", help="Video title", default="Test Title")
  argparser.add_argument("--description", help="Video description",
    default="Test Description")
  argparser.add_argument("--category", default="22",
    help="Numeric video category. " +
      "See https://developers.google.com/youtube/v3/docs/videoCategories/list")
  argparser.add_argument("--keywords", help="Video keywords, comma separated",
    default="")
  argparser.add_argument("--privacyStatus", choices=VALID_PRIVACY_STATUSES,
    default=VALID_PRIVACY_STATUSES[0], help="Video privacy status.")
  args = argparser.parse_args()

  if not os.path.exists(args.file):
    exit("Please specify a valid file using the --file= parameter.")

  youtube = get_authenticated_service(args)
  try:
    initialize_upload(youtube, args)
  except HttpError as e:
    print(("An HTTP error %d occurred:\n%s" % (e.resp.status, e.content)))

코드 실행 전에 oauth2client 설치가 필요합니다. 

pip install --upgrade oauth2client

 

4) 실행 단계 

이제 실행을 위한 준비는 끝났습니다. 

 

동영상 업로드를 위한 아래의 스크립트를 커맨드창에서 실행해보겠습니다. 위에서 언급한 것 처럼 client_secrets.json upload_video.py 는 같은 경로에 있어야 합니다.

python upload_video.py --file="test.mp4"  --title="Summer vacation in California" --description="Had a great time surfing in Santa Cruz"  --keywords="surfing,Santa Cruz" --category="22" --privacyStatus="private"

 

실행하면 인터넷 창에서 구글 로그인 창이 뜹니다. 로그인 후 사용할 계정을 선택하시면 됩니다. (스크린샷 생략)

 

계정을 선택하고 넘어가면, 아래와 같이 경고 문구가 뜹니다. 개인적으로 사용할 용도이므로 고급 버튼을 눌러 해당 앱으로 이동하면 됩니다. 앱 이름이 아까 임의로 지정한 이름임을 확인합니다.

 

 

넘어가면 해당 구글 계정에 업로드 권한을 부여할 것인지 묻습니다. 권한을 허용합니다.

완료되면 아래와 같은 화면으로 넘어갑니다.

이제 다시 커맨드창을 보면 아래와 같은 에러 메시지가 있습니다. 빨간 밑줄 친 부분으로 접속하여, 사용중인 프로젝트에 유튜브 API를 사용하도록 설정해주어야 합니다. (저는 기존에 만들어놓은 프로젝트가 있어서 이 단계까지 주욱 온 것 같은데, 없는 경우 프로젝트를 생성하여야 합니다.)

 

 

위 화면에서 사용 설정까지 해주면 이제 모든 준비가 끝났습니다.

 

이제 다시 동영상 업로드를 위한 스크립트를 실행해보면 업로드가 정상적으로 되는 것을 알 수 있습니다.

python upload_video.py --file="test.mp4"  --title="Summer vacation in California" --description="Had a great time surfing in Santa Cruz"  --keywords="surfing,Santa Cruz" --category="22" --privacyStatus="private"

 

 

추가적으로, 사용 한도가 정해져 있어 초과할 경우 아래와 같은 에러가 발생합니다. (quotaExceeded)

 

후기

사용 한도가 어느 정도 수준인지 와 닿지가 않네요. 다음에는 이전 게시글에서 이용한 동영상 만들기와 합쳐서 앨범 통째로 동영상으로 만들어 API를 이용하여 유튜브에 올리는 것 까지 하려고 합니다. 

댓글

Designed by JB FACTORY