Integrazione con l'API di YouTube: caricamento di video con Django

Pubblicato: 2022-03-11

Poco tempo fa, stavo lavorando per un cliente, integrando le recensioni video nel loro sito web. Come ogni sviluppatore motivato che risolve un nuovo problema, la prima cosa che ho fatto è stata Google e ho trovato una miriade di risposte inutili o fuorvianti su come ottenere pacchetti Python completamente diversi o obsoleti e non mantenuti. Alla fine, ho morso il proiettile e il team e ho costruito tutto da zero: abbiamo creato le viste, imparato l'API di Google, creato il client API e alla fine siamo riusciti a caricare video programmaticamente da Django.

In questo post, cercherò di guidarti passo dopo passo su come pubblicare video di YouTube dalla tua app Django. Ciò richiederà un po' di gioco con le credenziali dell'API di Google, prima con l'interfaccia web, quindi con il codice. La stessa parte di YouTube è molto semplice. Dobbiamo capire come funzionano le cose di Google perché a volte sono complicate e le informazioni vengono diffuse in molti luoghi.

Prerequisiti

Consiglio di familiarizzare con quanto segue prima di iniziare a lavorare:

  • YouTube Data API: Python Quickstart
  • API di dati di YouTube: riferimento all'API
  • YouTube Data API: esempi di codice
  • Libreria client dell'API di Google Python
  • Libreria client dell'API di Google Python: documento di riferimento
  • Libreria client dell'API di Google Python: esempi di codice
  • API di YouTube: esempi di codice Python

Un pezzo di codice interessante da notare è il seguente frammento di Python dai documenti API di Google YouTube:

 # 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')

Iniziare

Dopo aver letto i prerequisiti, è il momento di iniziare. Vediamo di cosa abbiamo bisogno.

Cintura portautensili

Fondamentalmente, creiamo un ambiente virtuale. Personalmente preferisco pyenv. La configurazione di entrambi non rientra nell'ambito di questo post, quindi posterò alcuni comandi pyenv di seguito e, se la tua preferenza è virtualenv , sentiti libero di sostituire i comandi di conseguenza.

In questo post userò Python 3.7 e 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

Mettiamo questo nei contenuti (solo se usi pyenv, quindi si attiva automaticamente quando entri nella cartella):

 djangoyt

Installazione delle dipendenze:

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

Ora è il momento di iniziare il nostro progetto django:

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

Pausa per alcune impostazioni di Google Config

Configuriamo ora le credenziali del nostro progetto in modo da poter utilizzare le API di Google.

Passaggio 1. Vai al seguente URL:

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

Passaggio 2. Crea un nuovo progetto.

Crea un nuovo progetto

Passaggio 3. Fai clic su "Abilita API e servizi".

Abilita API e servizi.

Passaggio 4. Cerca YouTube Data API v3 e fai clic su "Abilita".

Cerca YouTube Data API v3 e fai clic su "Abilita".

Passaggio 5. Dovresti ricevere un messaggio sulle credenziali.

un messaggio sulle credenziali

Passaggio 6. Fai clic sul pulsante blu "Crea credenziali" sul lato destro e dovresti ottenere la seguente schermata:

Fare clic sul pulsante blu "Crea credenziali".

Passaggio 7. Scegli il server Web, Dati utente:

Scegli Server Web, Dati utente

Passaggio 8. Aggiungi origini JS autorizzate e reindirizza URI. Continua fino alla fine:

Aggiungi origini JS autorizzate e reindirizza URI.

OK, abbiamo finito con le nostre credenziali impostate. Puoi scaricare le credenziali in un formato JSON o copiare l' ID client e il segreto client .

Torniamo a Django

Iniziamo la nostra prima app Django. Di solito lo chiamo "core":

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

Ora aggiungiamo quanto segue al nostro file root urls.py per instradare le richieste della home page alla nostra app principale:

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

Nell'app principale, abbiamo un altro file urls.py, con anche alcune configurazioni:

 # 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)

Vedi c'è un percorso vuoto che punta a HomePageView . È ora di aggiungere del codice.

Facciamo ora un semplice TemplateView solo per vederlo in esecuzione.

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

E ovviamente abbiamo bisogno di un modello di base:

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

Dobbiamo apportare alcune modifiche alle impostazioni:

 # 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/'

Creiamo ora un YoutubeForm e aggiungilo come form_class alla vista:

 # 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

Prova a eseguire la tua applicazione ora e la pagina sarà simile a questa:

Anteprima della pagina

Pausa per fare autorizzazione

Prima di tutto, devi creare un modello per memorizzare le tue credenziali. È possibile utilizzare un file, un sistema di cache o qualsiasi altra soluzione di archiviazione, ma un database sembra ragionevole e scalabile e, se lo si desidera, è possibile archiviare le credenziali per gli utenti.

Prima di procedere, è necessario apportare una regolazione: c'è un fork di oauth2client che supporta Django 2.1 che dobbiamo usare. Presto avremo il supporto ufficiale, ma nel frattempo puoi controllare le modifiche al fork. Sono molto semplici.

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

Vai su settings.py e inserisci il Client ID e il Client Secret che hai ricevuto da Google nei passaggi precedenti.

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

Attenzione: non è consigliabile memorizzare i segreti nel codice. Lo sto facendo semplicemente come dimostrazione. Consiglio di utilizzare le variabili di ambiente nell'app di produzione e non i segreti di hardcoding nei file dell'applicazione. In alternativa, se hai scaricato il JSON da Google, puoi anche specificarne il percorso invece delle impostazioni sopra:

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

Il pacchetto oauth2client fornisce già molte funzionalità, con un CredentialsField già creato che possiamo usare. È possibile aggiungere più campi, come una chiave esterna e date create/modificate in modo da diventare più robusti, ma restiamo semplici.

Modello semplice per memorizzare le credenziali:

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

Tempo per creare migrazioni e migrare:

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

Ora cambiamo le nostre viste API per poter autorizzare la nostra applicazione:

Nel nostro file core/urls.py , aggiungiamo un'altra voce per la prima visualizzazione dell'autorizzazione:

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

Quindi, la prima parte di AuthorizeView sarà:

 # 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/')'''

E poi la seconda parte:

 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('/')

Quindi, se non ci sono credenziali o la credenziale non è valida, generarne una e quindi reindirizzarla all'URL di autorizzazione. Altrimenti, vai alla home page così possiamo caricare un video!

Accediamo ora alla vista e vediamo cosa succede:

Errore di autorizzazione

Creiamo quindi un utente, prima di andare a quella pagina.

 (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.

Accediamo anche con esso tramite /admin . Dopo, accediamo nuovamente alla nostra visualizzazione /authorize/ .

Entriamo

Autorizzazione

Quindi,

Errore 404

OK, ha provato a reindirizzare all'URL di richiamata che abbiamo configurato molto tempo fa con Google. Ora dobbiamo implementare la vista di callback.

Aggiungiamo un'altra voce al nostro core/urls.py:

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

E un'altra vista:

 # 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('/')

Nota: il flusso è stato spostato all'esterno di AuthorizeView, diventando globale. Idealmente, dovresti crearlo in AuthorizeView e salvarlo in una cache, quindi recuperarlo nel callback. Ma questo esula dallo scopo di questo post.

Il metodo get di AuthorizeView è ora:

 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('/')

Puoi dare un'occhiata a implementazioni simili qui. Il pacchetto oauth2client stesso fornisce viste, ma in particolare preferisco implementare la mia vista Oauth personalizzata.

  • 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

Ora, se provi di nuovo l'URL /authorize/ , il flusso OAuth dovrebbe funzionare. È ora di vedere se questo lavoro vale la pena e caricare il nostro video! HomePageView verificherà prima le credenziali e se è tutto a posto, siamo pronti per caricare il nostro video.

Controlliamo come apparirà il nostro nuovo codice per 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!')

E il nuovo modello:

 {# 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>

Non dimenticare di aggiungere il campo video a YouTubeForm:

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

Eccoci qui!

Carica modulo

E poi, controllando nella pagina Studio del tuo account YouTube (è importante avere un canale):

Video caricato

Ecco!

Note di chiusura

Il codice necessita di alcuni miglioramenti, ma è un buon punto di partenza. Spero che abbia aiutato con la maggior parte dei problemi di integrazione dell'API YouTube di Google. Ecco alcune cose più importanti da notare:

  • Per l'autorizzazione, è importante richiedere l'accesso e autorizzazioni extra per l'utente che autorizzerà la tua applicazione a caricare video.
  • La variabile di flusso deve essere spostata dall'essere globale. Non è sicuro in un ambiente di produzione. È meglio memorizzare nella cache in base all'ID utente o alla sessione che ha avuto accesso alla prima visualizzazione, ad esempio.
  • Google fornisce un token di aggiornamento solo quando esegui la prima autorizzazione. Quindi dopo un po' di tempo, per lo più un'ora, il tuo token scadrà e se non hai interagito con la loro API inizierai a ricevere risposte invalid_grant . Riautorizzare lo stesso utente che ha già autorizzato un client non garantirà il tuo token di aggiornamento. Devi revocare l'applicazione nella pagina del tuo account Google e quindi ripetere la procedura di autorizzazione. In alcuni casi, potrebbe essere necessario eseguire un'attività per continuare ad aggiornare il token.
  • A nostro avviso, è necessario richiedere l'accesso poiché stiamo utilizzando una credenziale utente direttamente correlata alla richiesta.

Errore di scambio di flusso

Il caricamento richiede molto tempo e farlo nel processo di richiesta principale può causare il blocco dell'intera applicazione durante il caricamento. Il modo giusto sarebbe spostarlo nel proprio processo e gestire i caricamenti in modo asincrono.

Confuso? Non essere, leggi di più in Orchestrare un flusso di lavoro in background in Celery per Python .