Django youtubeにアップロード する画面を作ってみよう

■目的

今目論んでいるアプリでは、ユーザーみんなで動画を共有出来たらいいなぁって考えてる。

ただ動画をサーバーにアップしてると容量も食うし、管理も大変そう。

なのでYoutubeにアップして、それのリンクをサイトでみられるようにする方針にしようと思う。

Youtubeをそのまま利用すればいいんじゃな?って意見もありそうですが、過去のデータとかを管理するのがやりにくそうなんで。

あとは純粋なAPIを使ってみたいと言う興味です!

 

■やってみよう

今回はこちらのサイトを参考にしました。

https://developers.google.com/youtube/v3/guides/uploading_a_video?hl=ja

 

どうやらAPIを使うにはGoogleのアカウントが必要らしい。

 

 

 

ログインして、以下に飛ぶ

https://console.developers.google.com/apis/

 

APIライブラリからYoutube Data API v3を有効化

 

f:id:kimihide1104:20190408205647p:plain

 

f:id:kimihide1104:20190408205549p:plain

次に認証情報の追加を求められるので入力してキー取得

 

f:id:kimihide1104:20190408210114p:plain


そこからさらに、使うドメインとか登録してOAuthクライアントIDを入手

 

f:id:kimihide1104:20190408212019p:plain

 

多分Googleアカウントの用意は出来たのでPythonさんにpip

pip install google-api-python-client

なんか古そうなのでこっちにした

google-api-python-client-py3

 

■上手くいかないので別のサイトを参考にしよう

https://qiita.com/mm_sys/items/4cd6822cbc59a9715ac7 

 

https://github.com/tokland/youtube-upload

 

 

■エラーは以下のような内容が出てた。


WARNING: Please configure OAuth 2.0

To make this sample run you will need to populate the client_secrets.json file
found at:

/Users/satoukinhide/PycharmProjects/django_app/sns/client_secret.json

with information from the Developers Console
https://console.developers.google.com/

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets

 

Googleのブラウザから落としたJSONファイルに"redirect_uris": ""付け足したらエラーなくなった。

 

 

 

 

サンプルサイトのソースはPython2っぽかったので、Python3用にちょこちょこ変更した

#!/usr/bin/python

import httplib2
import os
import random
import sys
import time

from apiclient.discovery import build
from apiclient.errors import HttpError
from apiclient.http import MediaFileUpload
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import argparser, run_flow


# Explicitly tell the underlying HTTP transport library not to retry, since
# we are handling retry logic ourselves.
httplib2.RETRIES = 1

# Maximum number of times to retry before giving up.
MAX_RETRIES = 10

# Always retry when these exceptions are raised.
RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError, httplib2.NotConnected,
httplib2.IncompleteRead, httplib2.ImproperConnectionState,
httplib2.CannotSendRequest, httplib2.CannotSendHeader,
httplib2.ResponseNotReady, httplib2.BadStatusLine)

# Always retry when an apiclient.errors.HttpError with one of these status
# codes is raised.
RETRIABLE_STATUS_CODES = [500, 502, 503, 504]

# The CLIENT_SECRETS_FILE variable specifies the name of a file that contains
# the OAuth 2.0 information for this application, including its client_id and
# client_secret. You can acquire an OAuth 2.0 client ID and client secret from
# the Google Developers Console at
# https://console.developers.google.com/.
# Please ensure that you have enabled the YouTube Data API for your project.
# For more information about using OAuth2 to access the YouTube Data API, see:
# https://developers.google.com/youtube/v3/guides/authentication
# For more information about the client_secrets.json file format, see:
# https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
CLIENT_SECRETS_FILE = "client_secret.json"

# This OAuth 2.0 access scope allows an application to upload files to the
# authenticated user's YouTube channel, but doesn't allow other types of access.
YOUTUBE_UPLOAD_SCOPE = "https://www.googleapis.com/auth/youtube.upload"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"

# This variable defines a message to display if the CLIENT_SECRETS_FILE is
# missing.
MISSING_CLIENT_SECRETS_MESSAGE = """
WARNING: Please configure OAuth 2.0

To make this sample run you will need to populate the client_secrets.json file
found at:

%s

with information from the Developers Console
https://console.developers.google.com/

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
""" % os.path.abspath(os.path.join(os.path.dirname(__file__),
CLIENT_SECRETS_FILE))

VALID_PRIVACY_STATUSES = ("public", "private", "unlisted")


def get_authenticated_service(args):
flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE,
scope=YOUTUBE_UPLOAD_SCOPE,
message=MISSING_CLIENT_SECRETS_MESSAGE)

storage = Storage("%s-oauth2.json" % sys.argv[0])
credentials = storage.get()

if credentials is None or credentials.invalid:
credentials = run_flow(flow, storage, args)

return build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
http=credentials.authorize(httplib2.Http()))

def initialize_upload(youtube, options):
tags = None
if options.keywords:
tags = options.keywords.split(",")

body=dict(
snippet=dict(
title=options.title,
description=options.description,
tags=tags,
categoryId=options.category
),
status=dict(
privacyStatus=options.privacyStatus
)
)

# Call the API's videos.insert method to create and upload the video.
insert_request = youtube.videos().insert(
part=",".join(body.keys()),
body=body,
# The chunksize parameter specifies the size of each chunk of data, in
# bytes, that will be uploaded at a time. Set a higher value for
# reliable connections as fewer chunks lead to faster uploads. Set a lower
# value for better recovery on less reliable connections.
#
# Setting "chunksize" equal to -1 in the code below means that the entire
# file will be uploaded in a single HTTP request. (If the upload fails,
# it will still be retried where it left off.) This is usually a best
# practice, but if you're using Python older than 2.6 or if you're
# running on App Engine, you should set the chunksize to something like
# 1024 * 1024 (1 megabyte).
media_body=MediaFileUpload(options.file, chunksize=-1, resumable=True)
)

resumable_upload(insert_request)

# This method implements an exponential backoff strategy to resume a
# failed upload.
def resumable_upload(insert_request):
response = None
error = None
retry = 0
while response is None:
try:
print("Uploading file...")
status, response = insert_request.next_chunk()
if 'id' in response:
print("Video id '%s' was successfully uploaded." % response['id'])
else:
exit("The upload failed with an unexpected response: %s" % response)
except HttpError as e:
if e.resp.status in RETRIABLE_STATUS_CODES:
error = "A retriable HTTP error %d occurred:\n%s" % (e.resp.status,
e.content)
else:
raise
except RETRIABLE_EXCEPTIONS as e:
error = "A retriable error occurred: %s" % e

if error is not None:
print(error)
retry += 1
if retry > MAX_RETRIES:
exit("No longer attempting to retry.")

max_sleep = 2 ** retry
sleep_seconds = random.random() * max_sleep
print("Sleeping %f seconds and then retrying..." % sleep_seconds)
time.sleep(sleep_seconds)

if __name__ == '__main__':
argparser.add_argument("--file", required=True, help="Video file to upload")
argparser.add_argument("--title", help="Video title", default="Test Title")
argparser.add_argument("--description", help="Video description",
default="Test Description")
argparser.add_argument("--category", default="22",
help="Numeric video category. " +
"See https://developers.google.com/youtube/v3/docs/videoCategories/list")
argparser.add_argument("--keywords", help="Video keywords, comma separated",
default="")
argparser.add_argument("--privacyStatus", choices=VALID_PRIVACY_STATUSES,
default=VALID_PRIVACY_STATUSES[0], help="Video privacy status.")
args = argparser.parse_args()

if not os.path.exists(args.file):
exit("Please specify a valid file using the --file= parameter.")

youtube = get_authenticated_service(args)
try:
initialize_upload(youtube, args)
except HttpError as e:
print("An HTTP error %d occurred:\n%s" % (e.resp.status, e.content))
 

 

次のエラーはこれ

Uploading file...
An HTTP error 403 occurred:
b'{\n "error": {\n "errors": [\n {\n "domain": "youtube.quota",\n "reason": "quotaExceeded",\n "message": "The request cannot be completed because you have exceeded your \\u003ca href=\\"/youtube/v3/getting-started#quota\\"\\u003equota\\u003c/a\\u003e."\n }\n ],\n "code": 403,\n "message": "The request cannot be completed because you have exceeded your \\u003ca href=\\"/youtube/v3/getting-started#quota\\"\\u003equota\\u003c/a\\u003e."\n }\n}\n'

 

要約すると、1日にリクエストできる容量(quota)を超えたとのこと。

調べると一日あたり10,000 quotaで動画一個あたり1600quotaとのこと。

少ない。。。

quotaの容量を増やすためには、何か登録が必要とのこと。

めんどくさいのでAPIYoutubeアップロード はここで終焉とする。

 

Django + Herokuで画像がアップロード出来なかった問題

Djangoで作ったアプリをHerokuにデプロイできるようになってきた。

 

調子に乗ってきたので、画像をアップのアップロードも企てたところ、ローカルでは上手くいくがHerokuでは上手くいかない問題が発生した。

 

調べるとどうやらHerokuに直接は出来ないらしい。

解決した方がいたので、早速参考に取り掛かってみることにした。

 

参考記事

https://qiita.com/koki276/items/4f78ca421bea059d7b7a

 

 

cloudinaryとかいうサービスに画像を保存するそうだ。

https://cloudinary.com/console/welcome 

 

早速登録して、その後はpip

 

pip install django-cloudinary-storage

herokuにも対応するために、

pip freeze > requirements.txt

 

 

まあ詰まったので

以下のサイトを参考

https://programmer-jobs.blogspot.com/2012/12/herokucloudinarypythondjango.html

 

https://note.mu/k1ro/n/nd3a84b7e8cea

 

 

参考サイトにはImageFieldで、大丈夫って書いてあったんだけど、saveメソッドでこけてた。

最終的にはmodels.pyのimageFieldは諦めてCloudinaryField使ったらエラーなくなった。

image = CloudinaryField('image')

 

 

 

Django admin画面の変更

ログイン画面がダサいので変更方法について調べた

 

タイトルを変更するのは、url.pyに以下を追加するのが一番簡単

# 追加
admin.site.site_title = 'タイトルタグ' 
admin.site.site_header = 'サンプルアプリケーション' 
admin.site.index_title = 'メニュー'

qiita.com

 

 

CSS変更するのは以下のサイトの方法がわかりやすかった

www.sejuku.net

 

とりあえずタイトルだけ変えて実装しよう

Herokuでサーバーアップ

Herokuと言うPaasが無料でサーバー使えると言う情報をゲット

 

名前は聞いたことあったので、やってみることにした。

 

参考1

デプロイ! - workshop_tutorialJP

 

参考2

qiita.com

 

 

■引っかかりポイント1

参考1の手順でやっていくと、以下がモジュールなしのエラーになった。

import dj_database_url

 

調べると、以下のコマンドで必要なモジュールが全部取れるそうなので実行

pip install django-toolbelt

エラー解消された

 

 ■引っかかりポイント2

以下を追加する記載があったが、これもエラーとなった。

from whitenoise.django import DjangoWhiteNoise

解決方法は以下を参照

qiita.com

 

とやったが、参考1を読んでから参考2を読むと引っかかりポイントの解決方法が乗っていたので、ここでやり直した。

 

 

 

■gitへアップロードしHrokuへデプロイし直す時のコマンドめも

git add -A .                            
git commit -m "first commit" 

■Herokuにデプロイ
git push heroku master

git push heroku master heroku ps:scale web=1

 

 

Django 画像アップロード

DjangoCRUD処理はある程度コツを掴んだので、次は画像のアップロードに挑む

 

参考記事

 

qiita.com

 

qiita.com

 

Pillowをインストール

 

modelにImageを追加

image = models.ImageField(upload_to='images/', blank=True)

 

blank=Trueをつけなかったら、makemigrationsの時にエラー出て進めなかった。

 

 

 

 

 

 

■画像のアップロードは出来て、media/images/の下にもjpgが置かれる所まではスムーズに出来たが、表示の所で404!

Not Found: /media/images/sample.jpg
[28/Feb/2019 13:45:18] "GET /media/images/sample.jpg HTTP/1.1" 404 2116

 

どうやらurl.pyに👇が必要だったらしい。

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

f:id:kimihide1104:20190228225155p:plain

 

 

■表示でつまづいた点、その2

image.urlがブランクだとエラーになるっぽい

f:id:kimihide1104:20190228225759p:plain

 

解決方法

以下のようにすれば解決!

{% if item.image %}
<img src="{{ item.image.url}}" width="200" height="200" alt="">
{% endif %}

 

 

 

 

画像のサイズを編集する方法

 

qiita.com

 

 

Django-ImageKitとか言うのをインストールしてみた

上記リンク方法を試した結果 CACHEと言うフォルダが自動生成されてその中に、アップロードしたファイルの変換後ファイルが出来てた

 

f:id:kimihide1104:20190319231544p:plain


 

PythonでOpenCV 背景差分やってみた

前にOpenCV使ってカメラの起動までやったので、もう少しだけ処理を追加してみた。

 

今回は背景差分処理を追加

ソースは⬇︎こんな感じ

import numpy as np
# OpenCV のインポート
import cv2

# VideoCaptureのインスタンスを作成する。
# 引数でカメラを選べれる。
cap = cv2.VideoCapture(0)

cap2 = cv2.createBackgroundSubtractorMOG2()

while True:
# VideoCaptureから1フレーム読み込む
ret, frame = cap.read()

mask = cap2.apply(frame)

# 加工なし画像を表示する
cv2.imshow('Raw Frame', mask)

# キー入力を1ms待って、k が27(ESC)だったらBreakする
k = cv2.waitKey(1)
if k == 27:
break

# キャプチャをリリースして、ウィンドウをすべて閉じる
cap.release()
cv2.destroyAllWindows()

 

処理としては以下のメソッドで背景オブジェクトを取得
cap2 = cv2.createBackgroundSubtractorMOG2()

 

マスク(特定部分を抽出する)処理して
mask = cap2.apply(frame)

 

実行結果は、こんな感じ!

youtu.be

 

簡単!