YouTube API統合:Djangoを使用した動画のアップロード

公開: 2022-03-11

少し前、私はクライアントのために働いていて、彼らのウェブサイトにビデオレビューを統合していました。 やる気のある開発者が新しい問題を解決するのと同じように、私が最初にしたことはGoogleでした。まったく異なる、または時代遅れで保守されていないPythonパッケージを実現する方法について、役に立たない、または誤った答えがたくさん見つかりました。 最終的に、私は弾丸とチームを噛み、すべてをゼロから構築しました。ビューを作成し、GoogleのAPIについて学び、APIクライアントを作成し、最終的にDjangoからプログラムで動画をアップロードすることに成功しました。

この投稿では、DjangoアプリからYouTube動画を投稿する方法を段階的に説明します。 これには、Google APIのクレデンシャルを少し試してみる必要があります。最初にウェブインターフェースを使用し、次にコードを使用します。 YouTubeの部分自体は非常に簡単です。 グーグルのものがどのように機能するかを理解する必要があります。それは時々トリッキーであり、情報が多くの場所に広がっているからです。

前提条件

作業を開始する前に、次のことをよく理解しておくことをお勧めします。

  • YouTubeデータAPI:Pythonクイックスタート
  • YouTubeデータAPI:APIリファレンス
  • YouTubeデータAPI:コードサンプル
  • GooglePythonAPIクライアントライブラリ
  • Google Python APIクライアントライブラリ:リファレンスドキュメント
  • Google Python APIクライアントライブラリ:コードサンプル
  • YouTube API:Pythonコードサンプル

注意すべき興味深いコードは、GoogleYouTubeAPIドキュメントの次のPythonスニペットです。

 # Sample python code for videos.insert def videos_insert(client, properties, media_file, **kwargs): resource = build_resource(properties) # See full sample for function kwargs = remove_empty_kwargs(**kwargs) # See full sample for function request = client.videos().insert( body=resource, media_body=MediaFileUpload(media_file, chunksize=-1, resumable=True), **kwargs ) # See full sample for function return resumable_upload(request, 'video', 'insert') media_file = 'sample_video.flv' if not os.path.exists(media_file): exit('Please specify a valid file location.') videos_insert(client, {'snippet.categoryId': '22', 'snippet.defaultLanguage': '', 'snippet.description': 'Description of uploaded video.', 'snippet.tags[]': '', 'snippet.title': 'Test video upload', 'status.embeddable': '', 'status.license': '', 'status.privacyStatus': 'private', 'status.publicStatsViewable': ''}, media_file, part='snippet,status')

入門

前提条件を読んだら、始めましょう。 必要なものを見てみましょう。

ツールベルト

基本的に、仮想環境を作成しましょう。 私は個人的にpyenvが好きです。 両方の設定はこの投稿の範囲外なので、以下にいくつかのpyenvコマンドを投稿します。好みがvirtualenvの場合は、それに応じてコマンドを自由に置き換えてください。

この投稿では、Python3.7とDjango2.1を使用します。

 ➜ ~/projects $ mkdir django-youtube ➜ ~/projects $ cd django-youtube ➜ ~/projects/django-youtube $ pyenv virtualenv 3.7.0 djangoyt ➜ ~/projects/django-youtube $ vim .python-version

これをコンテンツに入れましょう(pyenvを使用している場合は、フォルダーに入ると自動的にアクティブになります)。

 djangoyt

依存関係のインストール:

 ➜ ~/projects/django-youtube $ pip install google-api-python-client google-auth\ google-auth-oauthlib google-auth-httplib2 oauth2client Django unipath jsonpickle

次に、djangoプロジェクトを開始します。

 ➜ ~/projects/django-youtube $ django-admin startproject django_youtube .

一部のGoogle構成を一時停止します

Google APIを使用できるように、プロジェクトのクレデンシャルを構成しましょう。

手順1.次のURLに移動します。

https://console.developers.google.com/apis/library/youtube.googleapis.com

ステップ2.新しいプロジェクトを作成します。

新しいプロジェクトを作成する

手順3.[APIとサービスを有効にする]をクリックします。

APIとサービスを有効にします。

ステップ4.YouTubeData API v3を探し、[有効にする]をクリックします。

YouTube Data API v3を探し、[有効にする]をクリックします。

ステップ5.資格情報に関するメッセージが表示されます。

クレデンシャルに関するメッセージ

ステップ6.右側にある[資格情報の作成]青いボタンをクリックすると、次の画面が表示されます。

[資格情報の作成]青いボタンをクリックします

ステップ7.Webサーバー、ユーザーデータを選択します。

Webサーバー、ユーザーデータを選択します

ステップ8.許可されたJSオリジンとリダイレクトURIを追加します。 最後まで続けます:

承認されたJSオリジンとリダイレクトURIを追加します。

OK、クレデンシャルの設定は完了です。 クレデンシャルをJSON形式でダウンロードするか、クライアントIDクライアントシークレットをコピーすることができます。

Djangoに戻る

最初のDjangoアプリを始めましょう。 私は通常それを「コア」と名付けます:

 (djangoyt) ➜ ~/projects/django-youtube $ python manage.py startapp core

次に、ルートurls.pyファイルに以下を追加して、ホームページリクエストをコアアプリにルーティングします。

 # <root>/urls.py from django.urls import path, include path('', include(('core.urls', 'core'), namespace='core')),

コアアプリで、別のurls.pyファイルを作成し、いくつかの構成も行います。

 # core/urls.py from django.conf import settings from django.conf.urls.static import static from django.urls import path from .views import HomePageView urlpatterns = [ path('', HomePageView.as_view(), name='home') ] if settings.DEBUG: urlpatterns += static( settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static( settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

HomePageViewを指す空のパスがあることを確認してください。 コードを追加します。

実行されていることを確認するために、単純なTemplateViewを実行してみましょう。

 # core/views.py from django.shortcuts import render from django.views.generic import TemplateView class HomePageView(TemplateView): template_name = 'core/home.html'

そしてもちろん、基本的なテンプレートが必要です。

 # core/templates/core/home.html <!DOCTYPE html> <html> <body> <h1>My First Heading</h1> <p>My first paragraph.</p> </body> </html>

いくつかの設定を微調整する必要があります。

 # settings.py from unipath import Path # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = Path(__file__).parent INSTALLED_APPS 'core', STATIC_ROOT = BASE_DIR.parent.child('staticfiles') STATIC_URL = '/static/' MEDIA_ROOT = BASE_DIR.parent.child('uploads') MEDIA_URL = '/media/'

ここでYoutubeFormを作成し、それをform_classとしてビューに追加しましょう。

 # core/views.py from django import forms from django.views.generic.edit import FormView class YouTubeForm(forms.Form): pass class HomePageView(FormView): template_name = 'core/home.html' form_class = YouTubeForm

今すぐアプリケーションを実行してみてください。ページは次のようになります。

ページプレビュー

承認を行うために一時停止

まず、資格情報を保存するためのモデルを作成する必要があります。 ファイル、キャッシュシステム、またはその他のストレージソリューションを使用することもできますが、データベースは合理的でスケーラブルなようであり、必要に応じてユーザーごとにクレデンシャルを保存することもできます。

先に進む前に、調整を行う必要があります。使用する必要があるDjango2.1をサポートするoauth2clientのフォークがあります。 間もなく正式なサポートがありますが、その間にフォークの変更を確認できます。 それらは非常に単純です。

 pip install -e git://github.com/Schweigi/[email protected]#egg=oauth2client Because of compatibility with Django 2.1

settings.pyに移動し、前の手順でGoogleから取得したクライアントIDクライアントシークレットを配置します。

 # settings.py GOOGLE_OAUTH2_CLIENT_ GOOGLE_OAUTH2_CLIENT_SECRET = '<your client secret>'

注意:コードにシークレットを保存することはお勧めしません。 私はこれを単にデモンストレーションとして行っています。 アプリケーションファイルにシークレットをハードコーディングするのではなく、本番アプリで環境変数を使用することをお勧めします。 または、GoogleからJSONをダウンロードした場合は、上記の設定の代わりにそのパスを指定することもできます。

 GOOGLE_OAUTH2_CLIENT_SECRETS_JSON = '/path/to/client_id.json'

oauth2clientパッケージはすでに多くの機能を提供しており、使用できるCredentialsFieldがすでに実行されています。 外部キーや作成/変更された日付などのフィールドを追加して、より堅牢にすることができますが、シンプルにしましょう。

クレデンシャルを保存するための単純なモデル:

 # core/models.py from django.db import models from oauth2client.contrib.django_util.models import CredentialsField class CredentialsModel(models.Model): credential = CredentialsField()

移行を作成して移行する時間:

 (djangoyt) ➜ ~/projects/django-youtube $ ./manage.py makemigrations core (djangoyt) ➜ ~/projects/django-youtube $ ./manage.py migrate

次に、APIビューを変更して、アプリケーションを承認できるようにします。

core/urls.pyファイルに、最初の認証ビューの別のエントリを追加しましょう。

 # core/urls.py from .views import AuthorizeView, HomePageView urlpatterns = [ # [...] path('authorize/', AuthorizeView.as_view(), name='authorize'), ]

したがって、AuthorizeViewの最初の部分は次のようになります。

 # core/views.py from django.conf import settings from django.shortcuts import render, redirect from django.views.generic.base import View from oauth2client.client import flow_from_clientsecrets, OAuth2WebServerFlow from oauth2client.contrib import xsrfutil from oauth2client.contrib.django_util.storage import DjangoORMStorage from .models import CredentialsModel # [...] class AuthorizeView(View): def get(self, request, *args, **kwargs): storage = DjangoORMStorage( CredentialsModel, 'id', request.user.id, 'credential') credential = storage.get() flow = OAuth2WebServerFlow( client_id=settings.GOOGLE_OAUTH2_CLIENT_ID, client_secret=settings.GOOGLE_OAUTH2_CLIENT_SECRET, scope='https://www.googleapis.com/auth/youtube', redirect_uri='http://localhost:8888/oauth2callback/') # or if you downloaded the client_secrets file '''flow = flow_from_clientsecrets( settings.GOOGLE_OAUTH2_CLIENT_SECRETS_JSON, scope='https://www.googleapis.com/auth/youtube', redirect_uri='http://localhost:8888/oauth2callback/')'''

そして2番目の部分:

 if credential is None or credential.invalid == True: flow.params['state'] = xsrfutil.generate_token( settings.SECRET_KEY, request.user) authorize_url = flow.step1_get_authorize_url() return redirect(authorize_url) return redirect('/')

したがって、クレデンシャルがない場合、またはクレデンシャルが無効な場合は、クレデンシャルを生成してから、承認URLにリダイレクトします。 それ以外の場合は、ホームページにアクセスして、ビデオをアップロードしてください。

今すぐビューにアクセスして、何が起こるか見てみましょう。

承認エラー

次に、そのページに移動する前に、ユーザーを作成しましょう。

 (djangoyt) ➜ ~/projects/django-youtube $ python manage.py createsuperuser Username (leave blank to use 'ivan'): ivan Email address: ivan***@mail.com Password: Password (again): This password is too short. It must contain at least 8 characters. Bypass password validation and create user anyway? [y/N]: y Superuser created successfully.

/adminからもログインしてみましょう。 その後、 /authorize/ビューにもう一度アクセスしましょう。

ログインしましょう

承認

それで、

404エラー

OK、Googleでずっと前に設定したコールバックURLにリダイレクトしようとしました。 次に、コールバックビューを実装する必要があります。

core/urls.pyにもう1つエントリを追加しましょう。

 # core/urls.py from .views import AuthorizeView, HomePageView, Oauth2CallbackView urlpatterns = [ # [...] path('oauth2callback/', Oauth2CallbackView.as_view(), name='oauth2callback') ]

そして別の見方:

 # core/views.py # the following variable stays as global for now flow = OAuth2WebServerFlow( client_id=settings.GOOGLE_OAUTH2_CLIENT_ID, client_secret=settings.GOOGLE_OAUTH2_CLIENT_SECRET, scope='https://www.googleapis.com/auth/youtube', redirect_uri='http://localhost:8888/oauth2callback/') # or if you downloaded the client_secrets file '''flow = flow_from_clientsecrets( settings.GOOGLE_OAUTH2_CLIENT_SECRETS_JSON, scope='https://www.googleapis.com/auth/youtube', redirect_uri='http://localhost:8888/oauth2callback/')''' # [...] class Oauth2CallbackView(View): def get(self, request, *args, **kwargs): if not xsrfutil.validate_token( settings.SECRET_KEY, request.GET.get('state').encode(), request.user): return HttpResponseBadRequest() credential = flow.step2_exchange(request.GET) storage = DjangoORMStorage( CredentialsModel, 'id', request.user.id, 'credential') storage.put(credential) return redirect('/')

注:フローはAuthorizeViewの外部に移動され、グローバルになりました。 理想的には、AuthorizeViewの下に作成してキャッシュに保存してから、コールバックで取得する必要があります。 しかし、それはこの投稿の範囲外です。

AuthorizeViewのgetメソッドは次のようになりました。

 def get(self, request, *args, **kwargs): storage = DjangoORMStorage( CredentialsModel, 'id', request.user.id, 'credential') credential = storage.get() if credential is None or credential.invalid == True: flow.params['state'] = xsrfutil.generate_token( settings.SECRET_KEY, request.user) authorize_url = flow.step1_get_authorize_url() return redirect(authorize_url) return redirect('/')

ここで同様の実装を見ることができます。 oauth2clientパッケージ自体はビューを提供しますが、私は特にカスタムOauthビューを実装することを好みます。

  • https://github.com/google/google-api-python-client/blob/master/samples/django_sample/plus/views.py
  • https://github.com/google/oauth2client/blob/master/oauth2client/contrib/django_util/views.py

ここで、 /authorize/ URLを再試行すると、OAuthフローが機能するはずです。 この作品が価値があるかどうかを確認し、私たちのビデオをアップロードする時が来ました! HomePageViewは最初に資格情報をチェックし、それがすべて問題ない場合は、ビデオをアップロードする準備ができています。

HomePageViewの新しいコードがどのように表示されるかを確認しましょう。

 import tempfile from django.http import HttpResponse, HttpResponseBadRequest from googleapiclient.discovery import build from googleapiclient.http import MediaFileUpload class HomePageView(FormView): template_name = 'core/home.html' form_class = YouTubeForm def form_valid(self, form): fname = form.cleaned_data['video'].temporary_file_path() storage = DjangoORMStorage( CredentialsModel, 'id', self.request.user.id, 'credential') credentials = storage.get() client = build('youtube', 'v3', credentials=credentials) body = { 'snippet': { 'title': 'My Django Youtube Video', 'description': 'My Django Youtube Video Description', 'tags': 'django,howto,video,api', 'categoryId': '27' }, 'status': { 'privacyStatus': 'unlisted' } } with tempfile.NamedTemporaryFile('wb', suffix='yt-django') as tmpfile: with open(fname, 'rb') as fileobj: tmpfile.write(fileobj.read()) insert_request = client.videos().insert( part=','.join(body.keys()), body=body, media_body=MediaFileUpload( tmpfile.name, chunksize=-1, resumable=True) ) insert_request.execute() return HttpResponse('It worked!')

そして新しいテンプレート:

 {# core/templates/core/home.html #} <!DOCTYPE html> <html> <body> <h1>Upload your video</h1> <p>Here is the form:</p> <form action="." method="post" enctype="multipart/form-data"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="Submit"> </form> </body> </html>

YouTubeFormにビデオフィールドを追加することを忘れないでください:

 class YouTubeForm(forms.Form): video = forms.FileField()

どうぞ!

フォームをアップロード

次に、YouTubeアカウントのスタジオページで確認します(チャンネルを用意することが重要です)。

アップロードされたビデオ

出来上がり!

クロージングノート

コードには多少の改善が必要ですが、それは良い出発点です。 GoogleのYouTubeAPI統合の問題のほとんどに役立つことを願っています。 注意すべき重要な点がいくつかあります。

  • 承認するには、アプリケーションが動画をアップロードすることを承認するユーザーにログインと追加の権限を要求することが重要です。
  • フロー変数は、グローバルから移動する必要があります。 実稼働環境では安全ではありません。 たとえば、最初のビューにアクセスしたユーザーIDまたはセッションに基づいてキャッシュすることをお勧めします。
  • Googleは、最初の認証を行ったときにのみ更新トークンを提供します。 そのため、しばらくすると、ほとんどの場合1時間でトークンの有効期限が切れ、APIを操作しなかった場合は、 invalid_grant応答の受信を開始します。 すでにクライアントを承認した同じユーザーを再承認しても、更新トークンは保証されません。 Googleアカウントページでアプリケーションを取り消してから、承認プロセスを再度実行する必要があります。 場合によっては、トークンを更新し続けるためにタスクを実行する必要があります。
  • リクエストに直接関連するユーザークレデンシャルを使用しているため、ビューでログインを要求する必要があります。

FlowExchangeエラー

アップロードには時間がかかります。メインのアプリケーションプロセスでアップロードを行うと、アップロード中にアプリケーション全体がブロックされる可能性があります。 正しい方法は、それを独自のプロセスに移動し、非同期でアップロードを処理することです。

混乱している? 詳細については、CeleryforPythonでのバックグラウンドジョブワークフローの調整を参照してください。