iOSショートカットAppでInstagramの画像動画保存機能を作ってみた

# はじめに
Instagramの写真や動画を保存できるサイト([Instadownloder](https://instadownloader.co/ja/)など)は存在しますが、投稿リンクをコピーしてサイトに貼り付けて、実行ボタンを押して、表示された画像をダウンロードといった手順が煩わしいと感じたためショートカットAppでの作成に取り掛かりました。このショートカットでは、投稿リンクをコピーして実行ボタンを押すだけで写真や動画のダウンロードまで行ってくれることを目指しました。

# 成果物
実際に作成したショートカットは[こちら](https://www.icloud.com/shortcuts/59f48abf287044ddb6387e715c32d80d)からダウンロードできます。
ダウンロードしたショートカットを使用するためには、設定から「信頼されていないショートカットを許可」を有効にする必要があります。
設定方法は、「設定>ショートカット>信頼されていないショートカットを許可」から行うことができます。尚、なんらかのショートカットを事前に一度実行させていないと信頼されていないショートカット許可を有効にできない仕様みたいなので、既存のショートカットを一度実行してから行うことをおすすめします。

# このショートカットでできること・できないこと

|できること|できないこと|
|:--|:--|
|- Instagramの公開アカウントの写真・動画・リールの保存<br> - 保存したいファイルの選択|- 非公開アカウントの写真・動画・リールの保存<br> - instagram以外のサイトのファイルの保存|

# ショートカットAppとは
iPhoneやiPadの複数の機能や操作を自動化してくれるiOSアプリのことを指します。
例えば、現在聴いている曲の内容を取得して、ワンタップでtwitterの文章を作成してくれたり、再生回数の多かった上位25曲のプレイリストを作成してくれたり、ミュージックのデータから音楽クイズを作成するなどのお遊び機能までできちゃいます。

スマートホームデバイスを使い、ショートカットAppに登録しておくと家電の操作などもワンタッチや声での操作で行うことができます。
僕は[Nature Remo mini](https://nature.global/jp/landing-page-dm-g/?ref=listing_ad_nature50&gclid=CjwKCAiA6aSABhApEiwA6Cbm_xf-GSsZK4TkWejGIbukO-8CSX4OrBc1TkoQNdo7Jm5d6Qvz-uAG2BoC1sMQAvD_BwE)を使って部屋のライトとエアコンの操作を行ったりしています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/8c4a3997-cd16-b64c-e7bb-1fb26ec9ff02.png" width="40%" position="center">

# Instagramの画像動画保存ショートカットの概要
ここでは、Webの仕組みを利用しています。Webでは暗号化などがされていなければ基本的に任意のデータまでアクセスすることができます。そのため、任意のデータがあるリンクを見つけてそこにアクセスすればいいわけです。

Webの検証を使ってInstagramのサイトの中身を見てみると...
![スクリーンショット 2021-01-22 14.14.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/e75fe3b1-e300-0f46-dcb8-0c69b8a30a49.png)


実際に画像のリンクが載っていることがわかります。この[リンク](https://scontent-sjc3-1.cdninstagram.com/v/t51.2885-15/e35/p1080x1080/135339003_315034783194511_5003849744750761457_n.jpg?_nc_ht=scontent-sjc3-1.cdninstagram.com&_nc_cat=107&_nc_ohc=6nutsuCGN8EAX-nWC06&tp=1&oh=b795690275513527b431feb5a85fe63d&oe=60356055)に飛んでみると画像が表示されること、またその画像が保存できることが確認できるはずです。

そこで、今回作成したショートカットではこの仕組みを利用して投稿されている画像や動画のリンクを取得し、そのリンクからデータを保存するという仕様になっています。

# ショートカットの中身
このショートカットは大きく6つのブロックとなっています。

> 1. クリップボードを取得し、Instagramのリンクか判定、でなければアラートを出して終了
> 2. クリップボードの内容を取得し、HTMLを作成
> 3. 画像や動画のリンクをターゲットとし、一致した場合は随時、リストに格納
> 4. 格納されたリストから'\u0026'を'&'に置換(iOSでは&がUnicodeにエンコードされていたため)
> 5. 写真や動画が1つだけだった場合と複数ある場合で条件分岐し、結果を`写真動画リスト`に格納
> 6. `写真動画リスト`から任意のファイルを選択し、選択されたファイルを保存


それぞれのブロックについて説明します。
### 1. クリップボードを取得し、Instagramのリンクか判定、でなければアラートを出して終了
ここでは、クリップボードに保存されているデータがInstagramの投稿リンクでなかった場合、投稿リンクをコピーするように促します。
![IMG_0662.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/dbb21201-5395-3668-df85-3898b83f574f.jpeg)

### 2. クリップボードの内容を取得し、HTMLを作成
投稿リンクからHTMLを取得します。
![IMG_0663.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/c7846dd2-0f83-707e-077e-8fe0af9ac6ee.jpeg)

### 3. 画像や動画のリンクをターゲットとし、一致した場合は随時、リストに格納
画像のリンクの前には、`"display_url":"https://scontent`という文字列が含まれているという規則を利用して、正規表現を用いて文字列の検索を行います。
また、基本的には1080pの画像となっているため、さらに1080x1080と一致する文字列を検索します。検索に引っかかった場合は取得リンクを`取得リンク`リストに追加します。
![IMG_0664.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/8a706ae1-0bbb-bc79-4a73-996d40ee79ee.jpeg)

一致しなかった場合は750pの画像のリンクを検索し、一致した場合は`取得リンク`リストに追加
750pの画像リンクも一致しなかった場合は、ピクセルを指定せずに検索して、`取得リンク`リストに追加します。
![IMG_0665.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/98745295-74dd-ebac-ddc8-522c2192cfa1.jpeg)

動画のリンクも検索して一致した場合は`取得リンク`リストに追加します。さらにリールのサムネイルを設定している場合もあるので、サムネイルも保存できるようにサムネイルのリンクも検索をかけて`取得リンク`リストに追加します。
※ここでは、サムネイルを動画の前に保存したいという謎のこだわりがでたため、一旦動画のリンクを`動画取得リンク`に格納して、サムネイルのリンクを`取得リンク`リストに追加した後、`動画取得リンク`を`取得リンク`に格納しています。
![IMG_0666.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/891aa0e1-86d6-886a-06cc-71b9be0d6783.jpeg)

### 4. 格納されたリストから'\u0026'を'&'に置換
iOSでHTMLを表示した時`&`がUnicodeでエンコードされていたため、リンクに飛ぶことができなかった。
そのため、`\u0026`を`&`に置換することで有効なリンクに変換している。
デコードする方法はよくわからなかったため、置換で対応した。
![IMG_0667.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/b0529519-e7d7-8e03-e092-8449d73e00f9.jpeg)

### 5. 写真や動画が1つだけだった場合と複数ある場合で条件分岐し、結果を'写真動画リスト'に格納
画像が一枚の時は取得するリンクが1つだが、複数会った場合投稿画像N枚に対してN+1つリンクが取得されるようになっていたため、画像一枚と複数ある場合で条件分けを行なっている。
画像が複数あった場合は、2つ目以降の項目から最後までを`写真動画リスト`に追加する仕様にしています。
![IMG_0668.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/02a4b02c-e7d4-d85f-3551-16e7cca362d7.jpeg)


### 6. `写真動画リスト`から任意のファイルを選択し、選択されたファイルを保存
これまでの作業で`写真動画リスト`に取得したいファイルのリンクを格納したため、'リストから選択'関数を用いて、任意のファイルを選択できるようにしている。
最後に選択された項目を'写真アルバムに保存'関数を使って写真フォルダに保存します。
![IMG_0669.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/869300/b0923af1-e149-b6a6-14bc-00ba12aec91d.jpeg)

ショートカットの内容は以上です。

# 感想
いままで簡単なショートカットは作ったことがありましたが、凝ったショートカットは初めて作ったのでちゃんと動かせたときは興奮しました。特にファイルを選択して保存するという機能は一つの関数だけで行え、思ったよりも簡単に実装できたのが驚きました。また、ショートカットの関数も充実しており、ほかにもさまざまなことに対応できそうということで、他のショートカットも作ってみたく思いました。気になった方は是非ショートカットを作ってみてください。最後までご閲覧いただきありがとうございました。

# 参考文献
[1] [iPhone『ショートカット』アプリが便利！使いこなし方やおすすめの設定法を紹介します (TIME & SPACE by KDDI)](https://time-space.kddi.com/mobile/20191120/2783)
[2] [iPad,iPhoneショートカットまとめ【あなたもすぐ使える】(YMK-BLOG)](https://ymkblog.com/shortcutcollection/)