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 和服务”。

启用 API 和服务。

第 4 步。查找 YouTube 数据 API v3,然后单击“启用”。

查找 YouTube Data API v3,然后单击“启用”。

第 5 步。您应该收到有关凭据的消息。

关于凭据的消息

步骤 6. 单击右侧的“创建凭据”蓝色按钮,您应该会看到以下屏幕:

单击“创建凭据”蓝色按钮

步骤 7. 选择 Web 服务器,用户数据:

选择 Web 服务器、用户数据

步骤 8. 添加授权的 JS 源和重定向 URI。 继续到最后:

添加授权的 JS 来源和重定向 URI。

好的,我们完成了我们的凭据设置。 您可以下载 JSON 格式的凭证,也可以复制Client IDClient 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/视图。

让我们登录

授权

然后,

404 错误

好的,它尝试重定向到我们很久以前使用 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 中编排后台作业工作流中的更多信息。