Python
YouTube
pytube
Pythonista

PythonistaでYouTubeの動画をフォトライブラリーに追加する

はじめに

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

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

割とテンプレートのコピペ

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
今回はこれを一部変更して使っております。

add_video.py
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

単位を付けるだけのメソッドです。

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をダウンロードできるスクリプトになりました。

感想

これを友人に見せたところ反応が薄くあまり面白くありませんでした。そのため
この度自己承認欲求が高まったために投稿しました。ということで、ストックやいいね、お願いします。また、Qiita初心者であるので、多少のミスは勘弁ください。もし何かあればコメントお願いします。
ついでにツイッターもフォローして頂けたら幸いです。@yutashx