フォルダ監視でGIFアニメを作る

# なぜGIFアニメか

スマホアプリ開発やフロントエンド開発をやっている方はGithubのIssuesやPull requestsに動画を載せたい時があると思います。しかしそられはmp4等の動画ファイルのアップロードに対応していません。mp4ファイルをアップロードしようとするとこのようなエラーが出ます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/1d5aafd7-2b0c-7421-8bc6-b65541c4f8a7.png" width="640px">

しかしGIFファイルのアップロードには対応しており、GIFにはアニメーション機能があります。256色までといった画質面の妥協点はありますが、それを使うと動画を載せることができます。

# なぜフォルダ監視か

私は普段、Androidアプリ開発をしていますが、現状のGIFアニメ作成の工程に非効率を感じていました。
(工程はMacを前提にしています)

**工程1** Android端末の画面録画を行う

1. Android StudioのLogcat下タブをクリック
1. 録画する端末のLogcatを表示しているか確認する
1. Logcat左下の `Screen Record` ボタンをクリック
1. `Start Recording` ボタンをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/6c962bda-4e24-73dd-751b-48b3fb86f6bf.png)

コマンドライン派の方は補足参照。

**工程2**  mp4形式のファイルを保存する

1. 保存フォルダを選択
1. 必要に応じてファイル名を設定
1. `Save` ボタンクリック

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/dc0df8a4-9138-6ada-1b8f-37e5c11dd8df.png" width="640px">

**工程3** GIFアニメ形式にエンコードする。

ffmepgコマンドでmp4ファイルをgifファイルに変換します。
Githubにはアップロード画像の10MB制限があるので、横幅320px、10fpsにして容量を小さくしています。

```sh
ffmpeg -i input.mp4 -vf scale=320:-1 -r 10 output.gif
```


**工程4** Githubにアップロードする

Githubのコメント入力欄にドラッグアンドドロップすると、GIFアニメをアップロード出来ます。

![github.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/4bb1fe26-4b47-ce9d-d020-f828bfb343a7.gif)


## 効率化

フォルダ監視を行えば工程 - ファイル保存をトリガーにして工程3- GIFアニメ形式にエンコードを自動で行うことが出来て、作業を効率化できます。

# フォルダ監視を行う

このようなPythonプログラムを作成しました。

```watch.py
import os
import time
from watchdog.events import FileSystemEventHandler
from watchdog.events import FileCreatedEvent
from watchdog.observers import Observer
from pathlib import PurePath
import subprocess
import signal


class MovieFileSystemEventHandler(FileSystemEventHandler):

    def on_any_event(self, event):
        if(type(event) == FileCreatedEvent):
            src_path = event.src_path
            pp = PurePath(src_path)
            if pp.suffix != '.gif':
                output_path = "%s/%s.gif" % (os.path.dirname(src_path), pp.stem)
                subprocess.run(['ffmpeg', '-i', event.src_path, '-vf',
                                'scale=320:-1', '-r', '10', output_path])


exit_flag = False


def handle_exit(sig, frame):
    global exit_flag
    exit_flag = True


signal.signal(signal.SIGINT, handle_exit)
signal.signal(signal.SIGTERM, handle_exit)


path = "%s/movie" % os.environ['HOME']
event_handler = MovieFileSystemEventHandler()
observer = Observer()
observer.schedule(event_handler, path, recursive=True)
observer.start()
while exit_flag == False:
    time.sleep(1)

observer.stop()
observer.join()
```

このプログラムでは[Watchdog](https://github.com/gorakhargosh/watchdog)というライブラリを使うのでpipコマンドでインストールします。

```sh
pip install watchdog
```

また、このプログラムでは ~/movie を監視して、エンコード結果のgifアニメも同じ場所に保存しますが、別のフォルダが良い場合はお好みに応じてこちらの箇所を書き換えてください。

```python
path = "%s/movie" % os.environ['HOME']
```

監視フォルダを作製します。

```sh
mkdir ~/movie
```

Pythonプログラムを実行します。常駐プログラムになります。

```sh
python watchdog.py
```

これでフォルダが監視されます。動画ファイルが監視フォルダに保存されてると、同じ名前のgifアニメファイルが自動で生成されます。

![watch.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334798/dd89d305-6162-acc5-30c4-4f53a334ab2a.gif)

常駐を終了するときはコンソールでCtrl + Cキーを入力します。

# 補足

## ffmpegをインストールする

brewを使えばインストール出来ます。

```sh
brew install ffmpeg
```

## Androidの画面録画をコマンドラインで行う

```sh
adb shell
adb /mnt/sdcard
# 録画開始
screenrecord output.mp4
# Ctrl + C で録画終了
exit
adb pull /mnt/sdcard/output.mp4
```

## Docker化する場合の注意点

Docker化も可能です。しかし正常に動作しないケースがあります。Android Studioから監視ディレクトリへの直接保存は問題無いですが、他のフォルダからの移動の場合は正常に動作しませんでした。

```Dockerfile
FROM python:3.7-alpine
WORKDIR /root/
RUN apk add --update --no-cache ffmpeg
RUN pip install watchdog
COPY watch.py .
CMD python watch.py
```

```docker-compose.yml
version: '3'
services:
    main:
        build: .
        volumes:
            - ~/movie:/root/movie
```

ビルドして実行する。

```sh
docker-compose build
docker-compose up
```

### 正常に動作しないケースの詳細


```python
class MovieFileSystemEventHandler(FileSystemEventHandler):

    def on_any_event(self, event):
        # type(event) を確認する
```

他のフォルダから監視フォルダへファイルを移動すると、FileCreatedEventではなくFileModifiedEventが発生しました。FileModifiedEventでも動画のGIFアニメ変換を行おうと思いましたが、他のフォルダからのコピーの場合はFileCreatedEventの後にFileModifiedEventが発生してしまい、適切なトリガーを作るのが難しと思いました。