#はじめに
Pythonistaとpytubeを使ってYouTube Downloaderをつくることにしました。既存のiOSアプリはアプリ内に動画をダウンロードしますが、今回はiOS純正のフォトライブラリーにダウンロードしたいと思います。
#方針
YouTubeアプリでダウンロードしたい動画のページへ行き、その動画の共有からPythonistaのスクリプトを実行します。スクリプトは大まかに、pytubeを使い動画をダウンロードし、それをフォトライブラリーに追加する工程に分かれます。
#環境設定
Stashをインストールした後、pipでpytubeをインストールする。
#実装
ファイルは以下のように分割します。
どうやらiCloud Driveでこれらのファイルを実行するとエラーが出る(Dropboxは未確認)。なのでローカルでこれらのファイルを作成し、実行してください。
YouTube—-main.py
|-url_get.py
|-add_video.py
|-byte2si.py
|-Download
main.pyはその名の通りメインファイルである。
url_get.pyはYouTubeアプリからURLを取得する。
add_video.pyはフォトライブラリーに動画を追加する。
byte2si.pyは取得する動画のバイト数を補助単位つきのものに変換する。(ex:1,000B→1KB)
Downloadディレクトリは一時的に動画を保存する
##main.py
from pytube import YouTube
import add_video
import byte2si
import url_get
import os
import glob
import time
def absfpath(path):
# ”Download”ディレクトリ下のファイルを取得する
flist = glob.glob(path + '/*')
if flist:
# “Download”ディレクトリ下の1つしかない動画ファイルを取得する
fpath = os.path.abspath(flist[0])
# “Download”ディレクトリ下にある動画ファイルの絶対パスを返す
return fpath
else:
raise OSError('The file does not exist.')
def main():
#プログレスバー
print('Now executing...')
# YouTubeアプリからurlを取得
url = url_get.yturl()
if not url:
raise ('Not YouTube')
# pytube.YouTubeを使い、urlから動画リストを取得し、そのリストの最初の動画をダウンロードする
yt = YouTube(url)
stream = yt.streams.first()
# main.pyと同一ディレクトリ下にある"Download"のパスを取得
drpath = os.getcwd() + 'Download'
# ダウンロードする
stream.download(drpath)
# その動画の絶対パスを取得
abspath = absfpath(drpath)
# その動画のサイズを取得
size = os.path.getsize(abspath)
sibyte = byte2si.byte2si(size)
#ファイルサイズを表示
print('Size:', sibyte)
# 動画のパスを渡し、フォトライブラリーに追加する
add_video.add_video(abspath)
# “Download”ディレクトリにはファイルを残さない、残すと”absfpath()”でバグる
os.remove(abspath)
if __name__ == '__main__':
# 開始時間を取得
start = time.time()
main()
#終了時間を少数第3位まで取得
finish = round(time.time() - start, 3)
#実行所要時間を表示
print('Execute Time:', finish)
#終了
print('Done')
##url_get.py
割とテンプレートのコピペ
import appex
import re
def trns():
if not appex.is_running_extension():
raise RuntimeError(
'This script is intended to be run from the sharing extension.')
url = appex.get_text()
if not url:
raise ValueError('No input text found.')
elif re.search(r'youtube', url):
return url
raise ValueError('Not YouTube')
def yturl():
geturl = trns()
# 取得したurlの余分なテキストを置換し、なくす
url = geturl.replace('&feature=share', '')
return url
if __name__ == '__main__':
print(yturl())
##add_video.py
これはPythonistaのライブラリにはない機能でした。ということで、Pythonistaのフォーラムで質問したところ、cvpさんに回答を頂けました。ありがとうございます。
https://forum.omz-software.com/topic/4829/how-videos-add-to-the-camera-roll
今回はこれを一部変更して使っております。
from objc_util import *
import threading
def add_video(path):
PHPhotoLibrary = ObjCClass('PHPhotoLibrary')
PHAssetChangeRequest = ObjCClass('PHAssetChangeRequest')
lib = PHPhotoLibrary.sharedPhotoLibrary()
url = nsurl(path)
# path to local video file
def change_block():
req = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL_(url)
def perform_changes():
lib.performChangesAndWait_error_(change_block, None)
t = threading.Thread(target=perform_changes)
t.start()
t.join()
if __name__ == '__main__':
add_video()
##byte2si.py
単位を付けるだけのメソッドです。
import math
def main(byte):
#byteの桁数を取得して、3桁刻みに単位を変えてくだけ
if 0 <= byte:
dig = int(math.log10(byte))
if 0 <= dig < 3:
bytes = byte / pow(10, 0)
return bytes, 'B'
elif 3 <= dig < 6:
bytes = byte / pow(10, 3)
return bytes, 'KB'
elif 6 <= dig < 9:
bytes = byte / pow(10, 6)
return bytes, 'MB'
elif 9 <= dig < 12:
bytes = byte / pow(10, 9)
return bytes, 'GB'
elif 12 <= dig <= 15:
bytes = byte / pow(10, 12)
return bytes, 'TB'
else:
raise ValueError('Not num')
def byte2si(byte):
num, unit = main(byte)
#少数第2位まで丸める
renum = round(num, 2)
return str(renum) + unit
#仕上げ
PythonistaのExtentionにmain.pyを結びつける。そして適当なアイコンを選択して完成。
#結果
こんな感じで作り上げたら、爆速でYouTubeをダウンロードできるスクリプトになりました。