YouTube API 集成:使用 Django 上傳視頻
已發表: 2022-03-11不久前,我在為一個客戶工作,在他們的網站上整合視頻評論。 就像任何有動力的開發人員解決一個新問題一樣,我做的第一件事就是谷歌它,我發現了大量關於如何實現完全不同的東西,或者過時和無人維護的 Python 包的無用或誤導性答案。 最終,我咬緊牙關,團隊和我從頭開始構建一切:我們創建了視圖,了解了 Google 的 API,創建了 API 客戶端,並最終成功地以編程方式從 Django 上傳視頻。
在這篇文章中,我將嘗試逐步指導您如何從您的 Django 應用發布 YouTube 視頻。 這將需要一些使用 Google API 憑據的操作——首先使用 Web 界面,然後使用代碼。 YouTube 部分本身非常簡單。 我們需要了解 Google 的東西是如何工作的,因為有時它很棘手,而且信息會傳播到很多地方。
先決條件
我建議在開始工作之前先熟悉以下內容:
- YouTube 數據 API:Python 快速入門
- YouTube 數據 API:API 參考
- YouTube 數據 API:代碼示例
- Google Python API 客戶端庫
- Google Python API 客戶端庫:參考文檔
- Google Python API 客戶端庫:代碼示例
- YouTube API:Python 代碼示例
需要注意的一個有趣的代碼是來自 Google YouTube API Docs 的以下 Python Snippet:
# 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
,請隨時相應地替換命令。
在這篇文章中,我將使用 Python 3.7 和 Django 2.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 和服務”。
第 4 步。查找 YouTube 數據 API v3,然後單擊“啟用”。
第 5 步。您應該收到有關憑據的消息。
步驟 6. 單擊右側的“創建憑據”藍色按鈕,您應該會看到以下屏幕:
步驟 7. 選擇 Web 服務器,用戶數據:
步驟 8. 添加授權的 JS 源和重定向 URI。 繼續到最後:
好的,我們完成了我們的憑據設置。 您可以下載 JSON 格式的憑證,也可以復制Client ID和Client Secret 。
回到姜戈
讓我們開始我們的第一個 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
現在嘗試運行您的應用程序,頁面將如下所示:
暫停授權
首先,您必須創建一個模型來存儲您的憑據。 您可以通過文件、緩存系統或任何其他存儲解決方案來實現,但數據庫似乎是合理且可擴展的,並且您還可以根據需要存儲每個用戶的憑據。
在繼續之前,需要進行調整——我們必須使用支持 Django 2.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/')'''
然後是第二部分:
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/
視圖。
然後,
好的,它嘗試重定向到我們很久以前使用 Google 配置的回調 URL。 現在我們需要實現回調視圖。
讓我們在 core/urls.py 中再添加一個條目:
# 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 帳戶 Studio 頁面(擁有一個頻道很重要):
瞧!
結語
代碼需要一些改進,但這是一個很好的起點。 我希望它能幫助解決大多數 Google 的 YouTube API 集成問題。 這裡有一些更重要的事情需要注意:
- 對於授權,重要的是需要用戶登錄和額外的權限才能授權您的應用程序上傳視頻。
- 需要將流變量從全局中移出。 在生產環境中是不安全的。 例如,最好根據訪問第一個視圖的用戶 ID 或會話進行緩存。
- Google 僅在您進行首次授權時提供刷新令牌。 因此,一段時間後,通常是一小時,您的令牌將過期,如果您沒有與他們的 API 交互,您將開始收到
invalid_grant
響應。 重新授權已授權客戶端的同一用戶將不能保證您的刷新令牌。 您必須在您的 Google 帳戶頁面中撤銷該申請,然後再次執行授權過程。 在某些情況下,您可能需要運行一個任務來不斷刷新令牌。 - 我們需要在視圖中要求登錄,因為我們使用的是與請求直接相關的用戶憑據。
上傳需要花費大量時間,並且在您的主應用程序進程中執行此操作可能會導致整個應用程序在上傳時阻塞。 正確的方法是將其移動到自己的進程中並異步處理上傳。
使困惑? 不要這樣,請閱讀在 Celery for Python 中編排後台作業工作流中的更多信息。