Intégration de l'API YouTube : mise en ligne de vidéos avec Django
Publié: 2022-03-11Il y a peu de temps, je travaillais pour un client, intégrant des critiques vidéo sur son site Web. Comme tout développeur motivé résolvant un nouveau problème, la première chose que j'ai faite a été Google, et j'ai trouvé une pléthore de réponses inutiles ou erronées sur la façon de réaliser quelque chose de complètement différent, ou des packages Python obsolètes et non maintenus. Finalement, j'ai mordu la balle et l'équipe et j'ai tout construit à partir de zéro : nous avons créé les vues, découvert l'API de Google, créé le client API et finalement réussi à télécharger par programmation des vidéos depuis Django.
Dans cet article, je vais essayer de vous guider étape par étape pour publier des vidéos YouTube depuis votre application Django. Cela nécessitera un peu de jeu avec les informations d'identification de l'API Google, d'abord avec l'interface Web, puis avec le code. La partie YouTube elle-même est très simple. Nous devons comprendre le fonctionnement de Google, car c'est parfois délicat et les informations sont diffusées à de nombreux endroits.
Conditions préalables
Je vous recommande de vous familiariser avec les éléments suivants avant de commencer à travailler :
- API de données YouTube : démarrage rapide de Python
- API de données YouTube : référence de l'API
- API de données YouTube : exemples de code
- Bibliothèque cliente de l'API Google Python
- Bibliothèque cliente de l'API Google Python : document de référence
- Bibliothèque cliente de l'API Google Python : exemples de code
- API YouTube : exemples de code Python
Un extrait de code intéressant à noter est l'extrait de code Python suivant de Google YouTube API Docs :
# 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')
Commencer
Après avoir lu les prérequis, il est temps de commencer. Voyons ce dont nous avons besoin.
Ceinture à outils
Fondamentalement, créons un environnement virtuel. Personnellement, je préfère pyenv. La configuration des deux n'entre pas dans le cadre de cet article, je vais donc publier quelques commandes pyenv ci-dessous et, si votre préférence est virtualenv
, n'hésitez pas à remplacer les commandes en conséquence.
Je vais utiliser Python 3.7 et Django 2.1 dans cet article.
➜ ~/projects $ mkdir django-youtube ➜ ~/projects $ cd django-youtube ➜ ~/projects/django-youtube $ pyenv virtualenv 3.7.0 djangoyt ➜ ~/projects/django-youtube $ vim .python-version
Mettons ceci dans le contenu (juste si vous utilisez pyenv, donc il s'active automatiquement lorsque vous entrez dans le dossier):
djangoyt
Installation des dépendances :
➜ ~/projects/django-youtube $ pip install google-api-python-client google-auth\ google-auth-oauthlib google-auth-httplib2 oauth2client Django unipath jsonpickle
Il est maintenant temps de commencer notre projet Django :
➜ ~/projects/django-youtube $ django-admin startproject django_youtube .
Pause pour Google Config
Configurons maintenant les informations d'identification de notre projet afin de pouvoir utiliser les API Google.
Étape 1. Accédez à l'URL suivante :
https://console.developers.google.com/apis/library/youtube.googleapis.com
Étape 2. Créez un nouveau projet.
Étape 3. Cliquez sur "Activer les API et les services".
Étape 4. Recherchez YouTube Data API v3 et cliquez sur "Activer".
Étape 5. Vous devriez recevoir un message sur les informations d'identification.
Étape 6. Cliquez sur le bouton bleu « Créer des informations d'identification » sur le côté droit, et vous devriez obtenir l'écran suivant :
Étape 7. Choisissez le serveur Web, les données utilisateur :
Étape 8. Ajoutez des origines JS autorisées et redirigez les URI. Continuez jusqu'à la fin :
OK, nous avons terminé avec nos informations d'identification configurées. Vous pouvez soit télécharger les informations d'identification au format JSON, soit copier l' ID client et le secret client .
Retour à Django.
Commençons notre toute première application Django. Je l'appelle généralement "core":
(djangoyt) ➜ ~/projects/django-youtube $ python manage.py startapp core
Maintenant, ajoutons ce qui suit à notre fichier racine urls.py pour acheminer les demandes de page d'accueil vers notre application principale :
# <root>/urls.py from django.urls import path, include path('', include(('core.urls', 'core'), namespace='core')),
Dans l'application principale, prenons un autre fichier urls.py, avec quelques configurations également :
# 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)
Voir il y a un chemin vide pointant vers HomePageView
. Il est temps d'ajouter du code.
Faisons maintenant un simple TemplateView
juste pour le voir fonctionner.
# core/views.py from django.shortcuts import render from django.views.generic import TemplateView class HomePageView(TemplateView): template_name = 'core/home.html'
Et bien sûr, nous avons besoin d'un modèle de base :
# core/templates/core/home.html <!DOCTYPE html> <html> <body> <h1>My First Heading</h1> <p>My first paragraph.</p> </body> </html>
Nous devons faire quelques ajustements de paramètres :
# 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/'
Créons maintenant un YoutubeForm
et ajoutons-le en tant que form_class
à la vue :
# 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
Essayez d'exécuter votre application maintenant, et la page ressemblera à ceci :
Pause pour faire l'autorisation
Tout d'abord, vous devez créer un modèle pour stocker vos informations d'identification. Vous pouvez utiliser un fichier, un système de cache ou toute autre solution de stockage, mais une base de données semble raisonnable et évolutive, et vous pouvez également stocker les informations d'identification par utilisateur si vous le souhaitez.
Avant de continuer, un ajustement doit être fait - il y a un fork de oauth2client qui prend en charge Django 2.1 que nous devons utiliser. Bientôt, nous aurons un support officiel, mais en attendant, vous pouvez inspecter les changements de fourche. Ils sont très simples.
pip install -e git://github.com/Schweigi/[email protected]#egg=oauth2client Because of compatibility with Django 2.1
Accédez à votre settings.py
et placez l' ID client et le secret client que vous avez obtenus de Google lors des étapes précédentes.
# settings.py GOOGLE_OAUTH2_CLIENT_ GOOGLE_OAUTH2_CLIENT_SECRET = '<your client secret>'
Attention : stocker des secrets dans votre code n'est pas recommandé. Je fais cela simplement comme une démonstration. Je recommande d'utiliser des variables d'environnement dans votre application de production et de ne pas coder en dur les secrets dans les fichiers d'application. Alternativement, si vous avez téléchargé le JSON depuis Google, vous pouvez également spécifier son chemin au lieu des paramètres ci-dessus :
GOOGLE_OAUTH2_CLIENT_SECRETS_JSON = '/path/to/client_id.json'
Le package oauth2client fournit déjà de nombreuses fonctionnalités, avec un CredentialsField
déjà fait que nous pouvons utiliser. Il est possible d'ajouter plus de champs, comme une clé étrangère et des dates de création/modification pour que nous soyons plus robustes, mais restons simples.

Modèle simple pour stocker les identifiants :
# core/models.py from django.db import models from oauth2client.contrib.django_util.models import CredentialsField class CredentialsModel(models.Model): credential = CredentialsField()
Il est temps de créer des migrations et de migrer :
(djangoyt) ➜ ~/projects/django-youtube $ ./manage.py makemigrations core (djangoyt) ➜ ~/projects/django-youtube $ ./manage.py migrate
Modifions maintenant nos vues API pour pouvoir autoriser notre application :
Dans notre fichier core/urls.py
, ajoutons une autre entrée pour la première vue d'autorisation :
# core/urls.py from .views import AuthorizeView, HomePageView urlpatterns = [ # [...] path('authorize/', AuthorizeView.as_view(), name='authorize'), ]
Ainsi, la première partie de AuthorizeView sera :
# 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/')'''
Et puis la deuxième partie :
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('/')
Donc, s'il n'y a pas d'informations d'identification ou si les informations d'identification ne sont pas valides, générez-en une, puis redirigez-la vers l'URL d'autorisation. Sinon, rendez-vous simplement sur la page d'accueil pour que nous puissions télécharger une vidéo !
Accédons à la vue maintenant et voyons ce qui se passe :
Créons un utilisateur alors, avant d'aller sur cette page.
(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.
Connectons-nous également via /admin
. Après, accédons à nouveau à notre vue /authorize/
.
Puis,
OK, il a essayé de rediriger vers l'URL de rappel que nous avons configurée il y a longtemps avec Google. Nous devons maintenant implémenter la vue de rappel.
Ajoutons une autre entrée à notre core/urls.py :
# core/urls.py from .views import AuthorizeView, HomePageView, Oauth2CallbackView urlpatterns = [ # [...] path('oauth2callback/', Oauth2CallbackView.as_view(), name='oauth2callback') ]
Et une autre vue :
# 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('/')
Remarque : Le flux a été déplacé en dehors de AuthorizeView, devenant global. Idéalement, vous devriez le créer sous AuthorizeView et l'enregistrer dans un cache, puis le récupérer dans le rappel. Mais cela sort du cadre de cet article.
La méthode get de AuthorizeView est maintenant :
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('/')
Vous pouvez jeter un œil à des implémentations similaires ici. Le package oauth2client
lui-même fournit des vues mais je préfère particulièrement implémenter ma vue Oauth personnalisée.
- 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
Maintenant, si vous essayez à nouveau l'URL /authorize/
, le flux OAuth devrait fonctionner. Il est temps de voir si ce travail en vaut la peine et de télécharger notre vidéo ! Le HomePageView
vérifiera d'abord les informations d'identification et si tout va bien, nous sommes prêts à télécharger notre vidéo.
Voyons à quoi ressemblera notre nouveau code pour 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!')
Et le nouveau modèle :
{# 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>
N'oubliez pas d'ajouter le champ vidéo à YouTubeForm :
class YouTubeForm(forms.Form): video = forms.FileField()
Nous y voilà!
Et puis, en vérifiant sur la page Studio de votre compte YouTube (il est important d'avoir une chaîne) :
Voila !
Notes de clôture
Le code a besoin d'être amélioré, mais c'est un bon point de départ. J'espère que cela a aidé avec la plupart des problèmes d'intégration de l'API YouTube de Google. Voici quelques points plus importants à noter :
- Pour l'autorisation, il est important d'exiger une connexion et des autorisations supplémentaires pour l'utilisateur qui autorisera votre application à télécharger des vidéos.
- La variable de flux doit être déplacée pour ne plus être globale. Ce n'est pas sûr dans un environnement de production. Il est préférable de mettre en cache en fonction de l'ID utilisateur ou de la session qui a accédé à la première vue, par exemple.
- Google ne fournit un jeton d'actualisation que lorsque vous effectuez la première autorisation. Ainsi, après un certain temps, généralement une heure, votre jeton expirera et si vous n'avez pas interagi avec leur API, vous commencerez à recevoir des réponses
invalid_grant
. Réautoriser le même utilisateur qui a déjà autorisé un client ne garantira pas votre jeton d'actualisation. Vous devez révoquer la demande dans la page de votre compte Google, puis recommencer le processus d'autorisation. Dans certains cas, vous devrez peut-être exécuter une tâche pour continuer à actualiser le jeton. - Nous devons exiger une connexion à notre avis puisque nous utilisons un identifiant d'utilisateur directement lié à la demande.
Le téléchargement prend beaucoup de temps, et le faire dans votre processus de candidature principal peut entraîner le blocage de l'ensemble de l'application pendant le téléchargement. La bonne façon serait de le déplacer dans son propre processus et de gérer les téléchargements de manière asynchrone.
Confus? Ne le soyez pas, lisez plus dans Orchestrating a Background Job Workflow in Celery for Python .