本文概述
不久前, 我正在为一个客户工作, 将视频评论集成到他们的网站中。像解决新问题的任何积极进取的开发人员一样, 我所做的第一件事就是使用Google, 它发现了很多关于如何实现完全不同, 过时且无法维护的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文档中的以下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, 请随时替换相应的命令。
在本文中, 我将使用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 Config
现在配置我们的项目凭据, 以便我们能够使用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格式下载凭据, 也可以复制客户端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
立即尝试运行你的应用程序, 页面将如下所示:
暂停进行授权
首先, 你必须创建一个模型来存储你的凭据。你可以浏览文件, 缓存系统或任何其他存储解决方案, 但是数据库似乎是合理且可扩展的, 并且如果需要, 你还可以按用户存储凭据。
在继续之前, 需要进行调整-oauth2client的fork支持我们必须使用的Django 2.1。很快, 我们将获得官方支持, 但与此同时, 你可以检查货叉的变化。他们很简单。
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_ID = '<your client id>'
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中编排后台作业流程中的更多内容。
评论前必须登录!
注册