iPhoneでショートカットappを使って、safariからYouTubeをダウンロードする
24/7/14に内容を大幅に変更しました。今まで使用していた人は更新をお願いします。
こんにちは、この記事が初のpythonを習いたての大学生です。
個人的な興味で題のようなものを作成したくなり、作るにあたってpythonを使用してみました。
使う際は当然自己責任でお願いします。
その中で躓いた点について、同じようなことをしようとしている、同じようなプログラミングレベルの人の役に立てばと思います。
プログラミングのぷの字に触れたばかりの人間ですので、コードが汚いだとか、例外処理がとかはご勘弁を
動作環境について
自分の確認している環境はiphone Xs ios17.1.2 a-shell ver1.12.5です。
それ以外の環境だったりはよくわからないので悪しからず
使用手順
まず最初に作ったものの使い方から。
1.「a-Shell」のインストール、設定等
今回の方法では「a-Shell」というアプリを使用します。ので、ダウンロード
2.実行ショートカットの作成
以下のショートカットをリックから作成します
※24/7/14更新
https://www.icloud.com/shortcuts/6d1be445060441cb9050f72fc29b4cf0
その後、youtubeの共有マークから、「Yt-dlp」を押してダウンロードします。
3.使用の際のオプション
「Yt-dlp」を使うと下のように選択するようでる。

現在は画像とは少し変わっていて
- オーディオファイル(mp3)
いっちゃん音質のいいものをmp3でダウンロード - オーディオファイル(opus)
いっちゃん音質のいいものをopusでダウンロード(わからなければ使わない方がいい) - 720p,mp4(h264,mp4a)
720pの動画を、30fps以下になるようにmp4でダウンロード - 最高画質,音質のmp4(h264,mp4a)
名前の通り、 - 最高画質,音質のmp4(vp9,opus)
非推奨
サムネイル付きで、Fileアプリの、このiPhone内/a-Shellにダウンロードされます。
「a-Shell」について
iOS上でシンプルなUnixライクな端末を使える。コマンド解釈にios_systemを使用するそう。
cを使ったものに関しては外から入れても動かないようですが、ffmpegに関しては要望が多かったのか、専用にチューニングしたものが最初から入っているようです。ので、今回pythonで使うためにラッパーである「ffmpeg-python」を入れていますが、ffmpeg本体は追加でインストールする必要はないです(.wasmで入れなきゃいけないなどと書かれているのは昔の話です)。
参考:https://github.com/holzschu/a-shell/issues/8#issuecomment-923821182
pythonで実行するわけ
ここからは作るにあたって躓いた点について書き残す、メモとなります。
まず、「a-Shell」の存在を知り、Yt-dlpで直接スマホでyoutubeをダウンロードできるんじゃ?となり、いつもターミナルで実行している文を入れてみると、「--embed-thumbnail」がうまく動作せず、毎回別れたままになってしまいました。
※「--embed-thumbnail」:サムネイルをyoutubeから取ってきて動画にくっつける
また、youtubeのタイトルをそのまま動画ファイルのタイトルにすると、保存するとき「/」で勝手にホルダーを作ってしまうなどの不便さがありました。
そこで、複数行にわたっての実行を試す際、実行結果の変数への入れ方がどうにも分かりませんでした(以下は試した)。
x=$(~~)
ので、pythonの実行ファイルを作って、それをa-shellから実行するという形をとりました。
「yt_dlp_expand.py」の中身
以下の順で実行しています。
- 1.情報の整理
- 2.Yt_dlpを使ってタイトルの取得
- 3.Yt_dlpを使ってサムネのダウンロード
- 4.Yt_dlpで動画orオーディオのダウンロード
- 5.(audioのみ)サムネを正方形に
- 6.サムネと動画を合体
1.情報の整理
一つはpathを正確にしています。
よく~/Downloadsのように書きますが、これが問題になっていそうな部分があったので、
self.output_path=pathlib.Path(path).expanduser()
の部分でフルパスに直しています。ちなみに、iphoneのファイルアプリの中では各アプリのフォルダーが並んでいるように見えますが、実際には各々のアプリで違うところにあるものがエイリアスとしてまとまっています。
また、pathは
import os
os.getcwd()
で見ることができ、/private/var/mobile/Containers/Data/Application/A2106A88-15D1-2255-4752-4C98962G3C1
のように意味不明な文字列が入り、さらにこれがアプリのアップデートごとに変わるらしい?です。
また、a-shellから他のアプリのファイルにはアクセスできないらしいです。
もう一つの情報の整理は、実行OSの決定です。iphoneの他にショートカット全体はmacでも、pythonコードはwinやlinuxでも動くようにしようと思っています。
def device_info(self):
import platform
os_name=platform.system()
if not os_name in ["Darwin","Linux","Windows",]:
raise
self.is_pc=True
if os_name=="Darwin":
device=platform.platform().split("-")[2]
if ("iPhone" in device)|("iPad" in device):
self.is_pc=False
2.Yt_dlpを使ってタイトルの取得
Yt_dlpを使って(参考①)タイトルの取得を取得して、入ると困る文字列を他の文字に置き換えて(参考②)います。
基本以下以降ではsubprocessを使ってYt_dlpを流しています。
※なぜか、サムネのダウンロードだけで「'」が無視されることがわかりました。
具体的には、サムネのダウンロード時にファイル名に「'」を使おうとするとこれが消えます。
9/10の更新で問題は多分解決しています
参考にしたサイト
①https://vlike-vlife.netlify.app/posts/cli_yt-dlp
②https://www.kabegiwablog.com/entry/2018/06/25/100000
③(最終的に使わなかった)https://note.com/note_fumi/n/n72b755dac88a
def getTitle(self):
import subprocess
script=f"yt-dlp '{self.download_url}' --skip-download --print 'title' --no-check-certificate --no-playlist"
cp = subprocess.run(script, encoding='utf-8', stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
self.title=cp.stdout.replace("\n","")
replaceList={':' : '-','[' : '「',']' : '」','/' : '/','\n': ' ',}
for key, value in replaceList.items():
self.title=self.title.replace(key, value)
3.Yt_dlpを使ってサムネのダウンロード
そのままですね
注意 : どうやらmp3にffmpegでサムネをつける際にはwebpではいけないようです。
mp4にはwebpのままで大丈夫なので、何で何だろう、、、
参考にしたサイト
https://lightrun.com/answers/ytdl-org-youtube-dl-how-can-i-download-thumbnails-in-pngjpg-format-
def download_thumbnail_jpg(self):
import subprocess
self.thumbnail_title=self.title.replace("'","")# 9/6 変更
self.thumbnail_path=f'{self.output_path}/{self.thumbnail_title}.jpg'
script=f"yt-dlp '{self.download_url}' --no-check-certificate --no-playlist --skip-download "\
f"--write-thumbnail --convert-thumbnails jpg --output '{self.output_path}/{self.thumbnail_title}'"# 9/6 変更
subprocess.run(script, stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
print(f"download_thumbnail_jpg Done")
4.Yt_dlpで動画orオーディオのダウンロード
1.と同じサイトを参考にダウンロード用のものを作ってます。
下のサイトでダウンロードしたものの拡張子を表示するための「.extract_info」を見つけました。
def crop_thumbnail_square(self):
import ffmpeg
probe = ffmpeg.probe(self.thumbnail_path)
width=min(probe["streams"][0]['width'],probe["streams"][0]['height'])
ffmpeg.input(self.thumbnail_path).filter('crop',width,width).output(self.thumbnail_path).run(overwrite_output=True)
5.(audioのみ)サムネを正方形に
なんかオーディオファイルのサムネって正方形だよね、
名前の通り。「overwrite_output=True」は上書きokかどうか
「**{'qscale:a': 1}」は音の品質みたいな、、、
ちなみに、pillowを使うのが一般的だと思うのですけど、a-shellでは使えません(多分)。
def crop_thumbnail_square(self):
import ffmpeg
probe = ffmpeg.probe(self.thumbnail_path)
width=min(probe["streams"][0]['width'],probe["streams"][0]['height'])
ffmpeg.input(self.thumbnail_path).filter('crop',width,width).output(self.thumbnail_path).run(overwrite_output=True)
参考にしたサイト
https://github.com/kkroening/ffmpeg-python
6.サムネと動画を合体
名前の通り。ここが一番難しい。ちなみに、yt-dlpの--embed-thumbnailが使えればyt-dlpのみで完結可能
以下、mp3,opus,mp4で結合の仕方がかわる。
- mp3
mp3ではmutagenを使用しました。
def marge_file_thumbnail_mp3(self):
from mutagen.id3 import ID3
tags = ID3(self.file_path)
with open(self.thumbnail_path, "rb") as img_file:
cover_img_byte_str = img_file.read()
tags.mime="image/jpeg"
tags.type=3
tags.data=cover_img_byte_str
tags.save()
参考:https://qiita.com/moshi/items/0fd2cd8c394ffa927239
- opus
opusでもmutagenを使用しましたが、やり方はyt-dlpの中覗いてそれっぽい部分を持ってきただけです。
def marge_file_thumbnail_opus(self):
from mutagen.oggopus import OggOpus
import base64
from mutagen.flac import Picture
pic = Picture()
f=OggOpus(self.file_path)
pic.mime = f'image/jpeg'
with open(self.thumbnail_path, 'rb') as thumbfile:
pic.data = thumbfile.read()
pic.type = 3 # front cover
f['METADATA_BLOCK_PICTURE'] = base64.b64encode(pic.write()).decode('ascii')
f.save()
参考:https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/postprocessor/embedthumbnail.py
- mp4
なぜか、inputとoutputは違う名前じゃないといけない。
「overwrite_output=True」は上書きokかどうかなのだが、上のような状態なので、意味があるのか、、、
def marge_file_thumbnail_mp4(self):
import ffmpeg
video = ffmpeg.input(self.file_path)
cover = ffmpeg.input(self.thumbnail_path)# 9/6 変更
(
ffmpeg
.output(video, cover,f'{self.output_path}/{self.title}.mp4', c='copy', **{'c:v:1': 'mjpeg'}, **{'disposition:v:1': 'attached_pic'})
.global_args('-map', '0')
.global_args('-map', '1')
.global_args('-loglevel', 'error')
.run(overwrite_output=True)
)
全体を通して使い終わったファイルの削除もやっているが、まあ特に説明はいいかなと
ショートカットにて
まず、初回に上記pythonファイルの作成
そのあと、20日以上経ったら以下のように使うモジュールをアップデートがないか確認
pip install --upgrade pip
pip install -U "yt-dlp[default]"
pip install -U mutagen
pip install -U ffmpeg-python
あとは選択肢として
オーディオファイル(mp3),オーディオファイル(opus),720p,mp4(h264,mp4a),最高画質,音質のmp4(h264,mp4a),最高画質,音質のmp4(vp9,opus)を用意して、DownloadModeに数字を入れる。
などして、a-shellで実行してます。
youtubeに対策されたのか、動かなくなることがある件について
この前、HTTP Error 403: Forbiddenとか言って、動かなくなることがありました。
多分ですがyoutubeが拒否していたのかなと思います。
既にyt-dlpが対応しているので、アップデートしたら動くようになります。
「a-Shell」アプリを開いて
pip install -U yt-dlp
を実行し、念の為「a-Shell」アプリを再起動。そのあとは普通に使えるようになるかと思います。
それで同じようにダメな場合、全く別の原因か、まだyt-dlpが対応できていないかだと思うので、しばらく待ちましょう。
https://github.com/yt-dlp/yt-dlp/issues/7984
Comments
こんにちは
まず、作ってくださりありがとうございます。
一つ目のショートカットの質問なのですが、ショートカットを実行してもエラーが出てしまいました。
それどころか
[このiPhone/a-Shell]の中に、[Documents]
のフォルダもありませんでした。
どのようにすれば対処できるのでしょうかIMG_0699.png
ちょっと見て欲しいのが、ファイルアプリで、このiphone内/a-Shellの中に「trach」というファイルはありますか?
もしないようでしたら、a-Shellで
cd ~/Documents
mkdir trach
の実行をお願いします。
できました!ありがとうございます!
記事の中に貼られているショートカットをダウンロードするためのリンクがどちらもpyファイルをつくるためのショートカットに見えます
をするためのショートカットをダウンロードしないといけない気がするのですが、私のやり方が間違っているのでしょうか?
@moma226 さんと同じ現象です…
申し訳ないです、変更しました
これでできるんじゃないかと思います
iOSのショートカットを用いた処理の中で最高のソリューションだと思います!
何個か気になる点がありましたのでお手好きの際に確認いただけると嬉しいです。
その1
・~/Documents内にサムネイルとマージ前の動画ファイルが保存されてしまい、マージ後もサムネイルとマージ前の動画ファイルが削除されないまま残ってしまいます。
・サムネイル等はtrachディレクトリに保存されることを想定してますでしょうか?trachディレクトリに一時ファイルを保存する処理が含まれていないように見受けられるのですがtrachディレクトリはどのように使われているのでしょうか。
その2
・Youtubeから「最高画質,音質のmp4」を選択して動画を保存した場合、mp4に変換されていないようでiphoneでは再生できないファイルが作成されます(拡張子のみ.mp4になっていますが)
・soun1218さんの環境ではそのようなことはありませんでしょうか?
その3
・Youtube側の仕様変更時のメンテナンスを不要にするため、個人的に「Yt-dlp」ショートカット内の冒頭に
pip install -U yt-dlp > /dev/nullを追加して使用しています。・だからなんだという話ですが良き案だと思っていただければ採用下さい。
自分がインフラ側の人間なのでpythonの内容があまり分からず、、、お恥ずかしい限りですがコメントいただけると幸いです。
@yosidaさんへ
コメントありがとうございます!!!
自分もあまり専門的なことはわからないのですが、一応直せるところは直して、よくしていけたらと思うので、ご意見ありがたいです!!!
その1(中間ファイル削除)
ご指摘のとおりです。削除されていなかったので、変更いたします
自分はmacでこのコードをよく使っていて、それとごっちゃになってしまっていました。
そもそもどっちでもちゃんと動くようにしたほうがいいなということで、両対応に変更します。
その3(メンテ)
先に3について回答させてもらいます
確かに、いちいち使えなくなっていては面倒なので、
pip install -U ~~を追加しようと思います。そこで、
> /dev/nullで出力を消すということかと思うのですが、これはどっちの方がいいかなあと思っていて、ご意見欲しいです。消してしまうと、何もやっていないのに始まらない、、、となってしまう人がいるかなとおも思ってしまって、、、
その2(コーデック問題)
以下、詳しくない人にもわかるよう、冗長な説明が入りますが、ご容赦ください。
最近のYouTubeでは、ビデオコーデックにvp9とavc1(H.264)、オーディオコーデックにopusとmp4aを使っているようです。
ちなみに、圧縮率がvp9>avc1,opus>mp4aなため、ビットレートで多少変動しますが、クオリティはvp9>avc1,opus>mp4a
ただし、vp9はavc1に比べ展開が重たく、自分のiphoneXSでは4Kがカクカクします。
mp4,webmはコンテナで、中ではビデオとオーディオが別で入っています。
ところで現在(ios17.1.2)、iosではvp9はちゃんとは対応してない、といった具合のようです。
というのも自分のiphoneでは、例えばDocumentsなどのアプリを使えば、vp9の動画をひらけます。
また、safariも対応しているとのこと。
ただ、ファイルアプリでは開けない、写真アプリにも飛ばせない、といった具合です。
なので、じきに大丈夫になるのかなと思うのですが、使い勝手が悪いので、モードを追加しようと思います。
ついでに、opusも追加しています。同じ音質ならmp3よりかなり容量削減になります。
opusのサムネはiphoneではちょっと難しい感じなので、macでのみ対応です。
ただ、対応していないソフトが大半な感じです。foobar2000とflacboxはサムネまで見ることができました。
@soun1218 さん
確認及び対応ありがとうございます!
コーデックの解説もありがとうございました大変勉強になりました。
フォーマット形式とコーデックがごっちゃになっておりました。
pip install -U ~~の> /dev/nullですが、iPhoneだと画面いっぱい文字が表示されてしまうのが目についたので> /dev/nullにしておりました。ただ、ご指摘の通りアップデートがかかった際などに「なぜか処理が進まない・・・」って勘違いしてしまう可能性もあるので無しで問題ないかと思います!
@yosidaさん
貴重なご意見ありがとうございました。
また何かありましたら、よろしくお願いします!
The_1256さんのような状態になってしまいました ios17.5.1です
24/7/14に内容を大幅に変更しました。今まで使用していた人は更新をお願いします。
@orihimesdollさん
@The_1256さんの画像と全く同じ場合、改善方法は一旦a-shellアプリを開いて、何かファイルを保存するとかでいけると思うのですが、そういったことが必要ないように改変したつもりです。
一度記事の通りにもう一度やってみて、その結果を教えていただけないかなと思います。
制作ありがとうございます。
このショートカットをプレイリストのダウンロードに対応してただくことは可能でしょうか?
歌ってみたをダンロードするとなるとかなりの回数になるので…
勝手ですがご検討のほどよろしくお願いいたします
了解です、考えてみます。
宴べっぃんぐが使えないので、結構中身変えないといけないかも?です
Let's comment your feelings that are more than good