1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-07-10

なぜGIFアニメか

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

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

なぜフォルダ監視か

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

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

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

image.png

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

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

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

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

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

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

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

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

github.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というライブラリを使うのでpipコマンドでインストールします。

pip install watchdog

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

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

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

mkdir ~/movie

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

python watchdog.py

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

watch.gif

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

補足

ffmpegをインストールする

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

brew install ffmpeg

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

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

Docker化する場合の注意点

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

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

ビルドして実行する。

docker-compose build
docker-compose up

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

class MovieFileSystemEventHandler(FileSystemEventHandler):

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

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

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?