تكامل YouTube API: تحميل مقاطع الفيديو باستخدام Django

نشرت: 2022-03-11

منذ فترة وجيزة ، كنت أعمل لدى عميل ، وأقوم بدمج مراجعات الفيديو في موقعه على الويب. مثل أي مطور متحمس لحل مشكلة جديدة ، كان أول شيء فعلته هو Google ، ووجدت عددًا كبيرًا من الإجابات غير المفيدة أو المضللة حول كيفية تحقيق شيء مختلف تمامًا ، أو حزم بايثون قديمة وغير مقيدة. في النهاية ، قمت بتدوير الرصاصة والفريق وبنيت كل شيء من البداية: أنشأنا وجهات النظر ، وتعرفنا على واجهة برمجة تطبيقات Google ، وأنشأنا عميل واجهة برمجة التطبيقات ، ونجحنا في النهاية في تحميل مقاطع الفيديو برمجيًا من Django.

في هذا المنشور ، سأحاول إرشادك خطوة بخطوة حول كيفية نشر مقاطع فيديو YouTube من تطبيق Django. سيتطلب هذا القليل من التلاعب ببيانات اعتماد Google API - أولاً باستخدام واجهة الويب ، ثم باستخدام الرمز. جزء YouTube نفسه واضح جدًا. نحتاج إلى فهم كيفية عمل عناصر Google لأنه في بعض الأحيان يكون الأمر صعبًا وتنتشر المعلومات في العديد من الأماكن.

المتطلبات الأساسية

أوصي بالتعرف على ما يلي قبل أن نبدأ العمل:

  • YouTube Data API: Python Quickstart
  • YouTube Data API: مرجع API
  • YouTube Data API: نماذج التعليمات البرمجية
  • مكتبة عميل Google Python API
  • مكتبة عميل Google Python API: مرجع المستند
  • مكتبة عميل Google Python API: نماذج التعليمات البرمجية
  • YouTube API: نماذج كود Python

جزء مثير للاهتمام من التعليمات البرمجية يجب ملاحظته هو مقتطف Python التالي من مستندات Google YouTube API:

 # 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 APIs.

الخطوة 1. انتقل إلى عنوان URL التالي:

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

الخطوة 2. إنشاء مشروع جديد.

أنشئ مشروعًا جديدًا

الخطوة الثالثة. انقر على "تمكين واجهات برمجة التطبيقات والخدمات".

تمكين واجهات برمجة التطبيقات والخدمات.

الخطوة 4. ابحث عن YouTube Data API v3 ، وانقر على "تمكين".

ابحث عن YouTube Data API v3 ، وانقر على "تمكين".

الخطوة 5. يجب أن تتلقى رسالة حول بيانات الاعتماد.

رسالة حول أوراق الاعتماد

الخطوة 6. انقر فوق الزر الأزرق "إنشاء بيانات اعتماد" على الجانب الأيمن ، وستظهر لك الشاشة التالية:

انقر على الزر الأزرق "إنشاء بيانات الاعتماد"

الخطوة 7. اختر خادم الويب ، بيانات المستخدم:

اختر خادم الويب ، بيانات المستخدم

الخطوة 8. أضف أصول JS المصرح بها وأعد توجيه URIs. تابع حتى النهاية:

أضف أصول JS المصرح بها وأعد توجيه URIs.

حسنًا ، انتهينا من إعداد أوراق الاعتماد الخاصة بنا. يمكنك إما تنزيل بيانات الاعتماد بتنسيق JSON أو نسخ معرف العميل وسر العميل .

العودة إلى 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 يدعم Django 2.1 الذي يتعين علينا استخدامه. قريبًا ، سيكون لدينا دعم رسمي ، ولكن في غضون ذلك ، يمكنك فحص تغييرات الشوكة. إنها بسيطة للغاية.

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

انتقل إلى settings.py وضع معرف العميل وسر العميل الذي حصلت عليه من Google في الخطوات السابقة.

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

تحذير: لا يُنصح بتخزين الأسرار في التعليمات البرمجية الخاصة بك. أنا أفعل هذا ببساطة كمظاهرة. أوصي باستخدام متغيرات البيئة في تطبيق الإنتاج الخاص بك ، وليس تشفير الأسرار في ملفات التطبيق. بدلاً من ذلك ، إذا قمت بتنزيل JSON من Google ، فيمكنك أيضًا تحديد مساره بدلاً من الإعدادات أعلاه:

 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 خطأ

حسنًا ، لقد حاولت إعادة التوجيه إلى عنوان URL لرد الاتصال الذي قمنا بتكوينه منذ فترة طويلة مع Google. الآن نحن بحاجة إلى تنفيذ عرض رد الاتصال.

دعنا نضيف إدخالاً آخر إلى 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 هي الآن:

 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 (من المهم أن يكون لديك قناة):

فيديو تم تحميله

هاهو!

ملاحظات ختامية

تحتاج الشفرة إلى بعض التحسين ، لكنها نقطة انطلاق جيدة. آمل أن يكون قد ساعد في معظم مشاكل دمج YouTube API في Google. فيما يلي بعض الأشياء المهمة التي يجب ملاحظتها:

  • للحصول على إذن ، من المهم أن تطلب تسجيل الدخول وأذونات إضافية للمستخدم الذي سيفوض تطبيقك بتحميل مقاطع الفيديو.
  • يحتاج متغير التدفق إلى الانتقال بعيدًا عن كونه عالميًا. ليست آمنة في بيئة الإنتاج. من الأفضل التخزين المؤقت بناءً على معرف المستخدم أو الجلسة التي وصلت إلى العرض الأول ، على سبيل المثال.
  • لا توفر Google رمز التحديث المميز إلا عند إجراء التفويض الأول. لذلك بعد مرور بعض الوقت ، غالبًا ساعة واحدة ، ستنتهي صلاحية الرمز المميز الخاص بك وإذا لم تتفاعل مع واجهة برمجة التطبيقات الخاصة بهم ، فستبدأ في تلقي ردود غير invalid_grant . لن تضمن إعادة تفويض نفس المستخدم الذي قام بالفعل بتفويض عميل برمز التحديث الخاص بك. يجب عليك إبطال التطبيق في صفحة حسابات Google الخاصة بك ثم القيام بعملية التفويض مرة أخرى. في بعض الحالات ، قد تحتاج إلى تشغيل مهمة للاستمرار في تحديث الرمز المميز.
  • نحتاج إلى طلب تسجيل الدخول من وجهة نظرنا لأننا نستخدم بيانات اعتماد مستخدم مرتبطة مباشرة بالطلب.

خطأ FlowExchange

يستغرق التحميل وقتًا طويلاً ، وقد يؤدي القيام بذلك في عملية التطبيق الرئيسية إلى حظر التطبيق بالكامل أثناء حدوث التحميل. الطريقة الصحيحة هي نقلها إلى عمليتها الخاصة والتعامل مع التحميلات بشكل غير متزامن.

مشوش؟ لا تكن كذلك ، اقرأ المزيد في تنظيم سير عمل في الخلفية في Celery for Python .