Python + Flask でアニメGIF ダウンロードサーバーを作る

# アニメGIF ダウンロードサーバーとは

- [フリーソフト9VAe](https://qiita.com/danjiro/items/253e5a33a38599098274)をベースに開発された、[PEASmotch!](https://9vae.blogspot.com/p/9vae.html) は、キッズプラザ大阪に５台設置され、[毎日150から200本のアニメが作られている。](https://9vae.blogspot.com/p/blog-page_15.html)
- 作成したベクトルアニメーションは、共有サーバーに保存され、[専用プレーヤー 9view](http://9vae.blogspot.com/p/9view.html) を使って、連続したアニメーションとして大型ディスプレイで上映されているが、かねてから、作ったアニメーションをスマホに入れて持ち帰りたいという要望があった。
- それを実現する方法として、共有サーバーの[アニメーションをアニメGIFに変換する python プログラム](https://qiita.com/danjiro/items/7d03223c85b3df317ab8)を公開した。
- 今回、Flask を用いて、スマホからアクセスできるサーバープログラムを作成したので公開する。
    - スマホにアニメをダウンロードできる
    - サイトのURLアドレスをQRコードで表示する
    - スマホアプリ（9VAe, PEASmotch）からアニメをアップロードできる

以下の記事も関連している

|項目|パソコン|スマホ|
|:--|:--|:--|
|[WiFiアクセスポイントの設置](https://qiita.com/danjiro/items/28bb17ab3e2aaf79e572)|〇|〇|
|[共有フォルダにアニメを保存、連続上映](https://qiita.com/danjiro/items/ce9dbc6eba5b49462873)|〇||
|作成したアニメをスマホにダウンロード（本記事）||〇|
|スマホアプリからアニメをアップロード（本記事）||〇|

以下はプログラムの実行例。表示されているURLをスマホでアクセスすれば同じ画面が表示され、アニメーションをダウンロードできる。

![サーバーを実行した図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67734/3bed0f33-3ccb-1236-1d82-fe8d7bce0b62.gif)
# Python のインストール

プログラムは python を使う。Windows と ラズベリーパイで作成してみた。

Windowsの場合

- [Python.jpのフルインストーラ版](https://www.python.jp/install/windows/index.html)から、ダウンロードするのが簡単。[ここ（pythonlinks.python.jp）](https://pythonlinks.python.jp/)から32bit/64bitなどを選んでダウンロードできる。

Windowsの場合(仮想環境)

- [こちらの記事の anaconda](https://yukituna.com/812/) は、仮想環境で実行できる。アプリごとにライブラリを使い分けるときに便利。
- 日本語を使いたいので、Python Ver3をダウンロード。（anacondaインストール後の「anaconda-navigator」の実行はWindowsロゴから行う必要があったが、ほかは記事どおりに実行できた）


ラズベリーパイの場合

- python(Ver2), python3(Ver3) が最初からはいっている


# Flask とQRコードライブラリのインストール

次のコマンドで、Flask とQRコード用ライブラリをインストールする。（ラズベリーパイの場合 pip3 、 python3 をつかう）pip が見つからない場合、pythonをインストールしたなかに、pip がはいっていると思われるので、そのフォルダに移動してから実行するとよい。

```
pip install Flask
pip install qrcode
pip install pillow
```
Linuxで上の命令で失敗した場合は、次の命令をためしてみてください

```
sudo apt-get install python-Flask
sudo apt-get install python-qrcode
sudo apt-get install python-pillow
```


# アニメGIF ダウンロードサーバー

環境が整ったのであとはプログラム

## フォルダ構成

Flask サンプルを参考に、以下のフォルダ構成とした。app.py がプログラム本体。ここに Flask を起動するコードがかかれている。templatesの中の設定データを利用して Web サイトの応答が行われる。

```
py　　　　
　　├──app.py　（プログラム本体）　　　
　　├──static　　　　
　　│　　　　├──qrcode.png(プログラムで作成される)　　　　
　　│　　　　└──setpath.ini(プログラムで作成される)　　　　
　　└──templates　　　　
　　　　　　　├──index.html　　　　
　　　　　　　└──layout.html　　　　
```

## python  Flask ソースコード

- ソースコードは、すべて UTF-8 で作成すること

### templates　　　　

Webサイトの内容が index.html に書かれている。

``` html:index.html
{% extends "layout.html" %}
{% block content %}
<img src="{{ url_for('static', filename='qrcode.png') }}" width="200">
<br>
画像を長押しすればダウンロードできます。<br>
Long press on the image to download.<br>
<div  align="right">
{{ message }} <br>
<a href="{{ url_for('static', filename='setpath.ini') }}">setpath.ini</A>
</div>
{% if images %}
  {% for path in images %}
    {% if '.eva.gif##' in path %} 
    {% elif '.eva.gif' in path %} 
      <div>
        <img src="images/{{ path }}" style="margin-top: 10px; vertical-align: bottom; width: 200px;">
        {{ path }}
      </div>
    {% endif %}
  {% endfor %}
{% endif %}

{% endblock %}
```
|記述|意味|補足|
|:--|:--|:--|
|{% extends "layout.html" %}|ページレイアウトの設定ファイル||
|{% block content %}...{% endblock %}|この部分が中身||
|<img src="{{ url_for('static', filename='qrcode.png') }}"|staticフォルダの中のQR画像を表示||
|{{ message }} |引数messageの文字列表示||
|{% if images %}|引数images（ファイル名リスト）があれば||
|{% for path in images %}|imagesリストの要素path（ファイル名）を順番に||
|{% if '.eva.gif' in path %}|.eva.gifが含まれるファイルだけ処理||
|<img src="images/{{ path }}"|images/フォルダのファイル||
|{{ path }}|ファイル名も表示||
|{% endif %}|ifの終わり||
|{% endfor %}|forの終わり||


``` html:layout.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>9VAe Anime Post</title>
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>
```
|記述|意味|補足|
|:--|:--|:--|
|{% block content %}{% endblock %}|この部分に中身がはいる||


### app.py

以下の値は環境に応じて設定

|定数|意味|補足|
|:--|:--|:--|
|EVA_FOLDER |アニメを保存するフォルダ|例では「/home/pi/2018」|
|QVIEW_EXE |9view.exe のフルパス|Windowsでもパスの区切りは'/'|
|HTTP_URL|公開するURLアドレス|最初は'127.0.0.1'でテストする|
|HTTP_PORT |公開するポート|最初は5000でテストする|

- アニメの保存フォルダについて、共有フォルダ直下でうまくいかない場合、共有フォルダの中のフォルダを指定してみてください。

``` python:app.py
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, make_response

import os         # ファイル操作
import subprocess # プログラム実行
import time       # 時間処理
import re         # 文字置換,正規表現
import webbrowser # ブラウザ
import threading  # multi thread
import qrcode     # QRコード生成

EVA_FOLDER = '/home/pi/xxxxxx'    #アニメを保存するフォルダ
QVIEW_EXE = '/home/pi/9va/9view'  #9view.exe のフルパス
HTTP_URL = '192.168.99.1'         #公開するURLアドレス
HTTP_PORT = 8080                  #公開するポート

app = Flask(__name__)

inpFolder = EVA_FOLDER

class MyThread(threading.Thread):
    def __init__(self):
        super(MyThread, self).__init__()

    def run(self):
        loop(self,False)

@app.route('/')
def index():
  global inpFolder
  return render_template('index.html', images=sorted(os.listdir(inpFolder)[::-1], reverse=True), message = inpFolder)

@app.route('/images/<path:path>')
def send_js(path):
  global inpFolder
  return send_from_directory(inpFolder, path)

@app.route('/',methods=["POST"]) #アニメのアップロード
def save_eva():
  global inpFolder
  mno = -1
  #print("Posted file: {}".format(request.files['file']))
  file = request.files['file']
  if(file.filename.endswith('.eva')):
      files = [f for f in os.listdir(inpFolder)]   #入力
      for fn in files:
        eva = os.path.join(inpFolder,fn)
        if(not eva.endswith('.eva')):
            continue
        if( '_編集中' in eva):     #自動保存ファイルは無視
            continue
        if( '_とちゅう' in eva):
            continue
        if( '_autosave' in eva):
            continue
        no = int(os.path.splitext(os.path.basename(fn))[0])
        if(no > mno):               #最終番号を探す
            mno = no

      savepath = os.path.join(inpFolder, '%04d.eva' % (mno+1))
      file.save(savepath)
  return str(mno+1)   #保存した番号を返す


# GIF変換ループ
def loop(self,askFolder):
  global inpFolder
  drmax = '0000-0000'
  while True:
    dirs = [f for f in os.listdir(EVA_FOLDER)] #一番新しい日付を取得
    drs = [s for s in dirs if re.match('[0-9]{4}-[0-9]{4}', s)]
    for dr in drs:
      if dr > drmax:
        drmax = dr
    inpFolder = EVA_FOLDER + '/' + drmax
    if(askFolder):
        return inpFolder

    files = [f for f in os.listdir(inpFolder)]   #入力
    for fn in files:
        if(os.path.splitext(fn)[1] != '.eva'):
            continue
        gif = fn + '.gif'  #GIFファイル名
        gif = os.path.join(inpFolder,gif)
        eva = os.path.join(inpFolder,fn)
        if( '_編集中' in eva):     #自動保存ファイルは無視
            continue
        if( '_とちゅう' in eva):
            continue
        if( '_autosave' in eva):
            continue
        if(os.path.exists(gif)):    #gifが存在,作成すみチェック
            if(os.path.getmtime(gif) > os.path.getmtime(eva)):
                continue
        cmd = (QVIEW_EXE , eva , '-gif') #-gif オプションでGIFに変換
        print(cmd)                  #変換したことを表示
        subprocess.run(cmd)         #gif作成
    time.sleep(1.0) #sleep(秒指定)
  return inpFolder


t = MyThread()
inpFolder = loop(t,True)            #evaが修正されたらgif作成、新フォルダへの移動
t.start()

html = 'http://' + HTTP_URL + ':' + str(HTTP_PORT)
img = qrcode.make(html)          #QR code作成
img.save('static/qrcode.png')
with open('static/setpath.ini', mode='w') as f:
    f.write(html)


if __name__ == '__main__':
    webbrowser.open(html)            #ブラウザ起動
    app.run(debug=True, host=HTTP_URL, port=HTTP_PORT)

```

|記述|意味|補足|
|:--|:--|:--|
|from flask import Flask, ...|Flask用拡張||
|import|関数を使えるようにする||
|app = Flask(__name__)|処理の名前||
|class MyThread(threading.Thread):|マルチスレッド定義|Flask内で同時実行する|
|def run(self):|MyThreadで実行する処理||
|@app.route('/')|ルートアクセスで表示される内容||
|def:|関数定義||
|global inpFolder|変数inpFolderは全体で共通||
|render_template('index.html'|Flaskのtemplates/index.html を表示||
|os.listdir(inpFolder)[::-1]|inpFolderのファイル全部|-1は最後の意味|
|sorted(xxx, reverse=True)|リストを逆順でソート||
|@app.route('/images/<path:path>')|/images/のアクセスで表示される内容|index.htmlから呼ばれる|
|send_from_directory(inpFolder, path)|inpFolderフォルダのpathファイル||
|@app.route('/',methods=["POST"])|POSTを受け取ったときの処理|アップロード用|
|request.files['file']|POSTされたファイル||
|if(file.filename.endswith('.eva')):|拡張子が.evaの場合||
|files = [f for f in os.listdir(inpFolder)] |inpFolderのファイルをリスト化||
|os.path.splitext(os.path.basename(fn))[0]|拡張子のないファイル名||
|int(文字列)|文字列を数値に変換||
|'%04d.eva' % (mno+1)|数値(mno+1)を文字列に変換||
|savepath = os.path.join(inpFolder,ファイル名)|フォルダとファイル名の結合||
|file.save(savepath)|ファイルをsavepathに保存|POSTされたファイルを別名で保存|
| if re.match('[0-9]{4}-[0-9]{4}', s)|s が「数字４桁-数字４桁」の場合|正規表現|
|if(os.path.exists(gif)):|ファイルが存在した場合||
|os.path.getmtime(gif)|ファイルの更新時間||
|cmd = (QVIEW_EXE , eva , '-gif')|コマンド作成|EVAアニメをGIFに変換|
|subprocess.run(cmd)|コマンド実行||
|time.sleep(1.0)|１秒停止||
|t = MyThread()<br>t.start()|マルチスレッドの実行||
|img = qrcode.make(html)|QRコード画像imgを作成||
|img.save('static/qrcode.png')|画像imgをstaticフォルダに保存||
|with open('static/setpath.ini', mode='w') as f:|書き込みファイルをオープン||
|f.write(html)|文字列の書き込み||
|webbrowser.open(html)|ブラウザ起動||
|app.run(debug=True, host=HTTP_URL, port=HTTP_PORT)|URL、Portを指定して実行||





# 使い方
ラズベリーパイで実行した例を示す

- ラズベリーパイで WiFi アクセスポイントを作成し、URL「192.168.99.1」で公開した。[やり方はこちら](https://qiita.com/danjiro/items/28bb17ab3e2aaf79e572)
- ターミナルでpyフォルダに移動。python3 app.py でプログラムを実行
- ブラウザが開き、以下のように表示される![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67734/3bed0f33-3ccb-1236-1d82-fe8d7bce0b62.gif)


## スマホにアニメをダウンロードする方法

1. スマホのWiFi設定で、ラズベリーパイアクセスポイントにWiFiをつなぐ
2. スマホでQRコードをみてURLをひらく ＞ 上と同じ画面が開く
3. アニメーションを長押しし、メニューから「ダウンロード」を選べばGIFアニメをダウンロードできます。

## スマホアプリ（PEASmotch!one）からアニメをアップロードする方法

1. ラズベリーパイアクセスポイントにWiFiをつなぐ
2. スマホでQRコードをみてURLをひらく ＞ 上と同じ画面が開く

setpath.iniの設定

1. setpath.ini を長押しし、「リンクをダウンロード」＞setpath.ini ファイルをダウンロードする
2. ファイルマネージャなどのアプリをつかって、setpath.ini ファイルを「PEASmotch」フォルダに入れる
3. PEASmotchを起動し、PEASロゴをタッチ、メニューの一番下に「アップロード」が追加されていればOK。

- メニューの最後の「アップロード」項目は、スマホが、9VAeアニメGIF POSTと接続しており、保存用フォルダにアクセスできるときだけ表示される。
- アニメを作成し「アップロード」をタッチすればアニメが保存され、自動的に gif ファイルに変換される。（上の app.py プログラムの　MyThread　が新しいファイルを検出し、EVAからGIFに変換を行う）
- スマホ版のアップロード機能は、Ver.0.6.12(200110) 以降の PEASmotch!one に搭載された。なお、パソコン版の場合は、9va_dataフォルダの中の setpath.ini に共有フォルダのフルパスを記載しておき、直接保存する機能が従来から搭載されている。

# 問題点と改良版

上のシステムを実際にキッズプラザ大阪に設置したところ、以下のような問題があった。

|問題点|修正|
|:--|:--|
|Windows7の共有フォルダにfopenではアクセスできたが、statがアクセスできなかった|ファイルの存在チェックを、statを使わずにfopenを使う関数に変更|
|ラズパイで共有フォルダ上でEVA-GIF変換を行うと、ほかの端末から共有フォルダへの接続ができなくなった。Windowsの記憶域不足エラーが発生|共有フォルダからラズパイに１日分のフォルダを転送し、ラズパイ上でEVA-GIF変換、Flaskサーバー応答を行うように変更|
|上のプログラムだと、EVA-GIF変換が２つ起動してしまう|Flaskの応答とEVA-GIF変換を別プログラムにして起動する|
|EVA-GIF変換の変換途中のファイルをFlaskサーバーが表示しようとしてエラー表示が出る|**変換途中の拡張子**を、.gif##とし、変換終了後に.gifにリネームする|
|EVAファイルを転送するときに、０バイトのファイルができることがある。|**０バイトのファイル**は削除する|
|Macから共有フォルダをアクセスしたときに、隠しファイルがいくつも作成される。そのファイルを処理しようとしてエラー終了する|**Macが作成する隠しファイル**は読み飛ばす処理が必要|
|POSTしても応答がない|２文字のPOSTに改行をつけないと、Linuxでは返事がこなかった。Windowsでは返事があった。|
|保存フォルダの読み書き属性。日付によってフォルダ変更する場合、外部端末によって日付フォルダを作成すると、POSTでデータが書けなくなった。|Pythonで、日付がかわったら、日付フォルダを作成し、すべてのユーザーに読み書き可能にするよう設定する|
|WindowsのPythonで動かすと、POSTの応答がうまくできない|セキュリティ設定が原因か？、firewall は、Pythonと Flask と両方許可したが、Webサイトは見えるが、POSTの応答がうまくいかない。Linuxだとちゃんと応答する。対策不明|

# 改良版プログラム

上の問題を修正したプログラム app-2.py, gif-2.py が以下

## ダウンロードサーバープログラム

サーバーは内部フォルダをみる。内部フォルダで、GIF変換を行う。内部フォルダを最新にするのは、GIF変換プログラム。

``` python:app-2.py
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, make_response

import os         # ファイル操作
import shutil     # ファイル操作
import subprocess # プログラム実行
import time       # 時間処理
import re         # 文字置換,正規表現
import webbrowser # ブラウザ
import threading  # multi thread
import qrcode     # QRコード生成
import datetime   # 日付、時刻

EVA_FOLDER = '/home/share/xxxxxx' #アニメを保存する内部フォルダ(１日分)
HTTP_URL = '192.168.99.1'         #公開するURLアドレス（例）
HTTP_PORT = 8080                  #公開するポート

app = Flask(__name__)

inpFolder = EVA_FOLDER

class MyThread(threading.Thread):
    def __init__(self):
        super(MyThread, self).__init__()

    def run(self):
        loop(self,False)

@app.route('/')
def index():
  global inpFolder
  return render_template('index.html', images=sorted(os.listdir(inpFolder)[::-1], reverse=True), message = inpFolder)

@app.route('/images/<path:path>')
def send_js(path):
  global inpFolder
  return send_from_directory(inpFolder, path)

@app.route('/',methods=["POST"]) #アニメのアップロード
def save_eva():
  global inpFolder
  mno = -1
  #print("Posted file: {}".format(request.files['file']))
  file = request.files['file']
  if(file.filename.endswith('.eva')):
      files = [f for f in os.listdir(inpFolder)]   #入力
      for fn in files:
        eva = os.path.join(inpFolder,fn)
        if(not eva.endswith('.eva')):
            continue
        if( '_編集中' in eva):     #自動保存ファイルは無視
            continue
        if( '_とちゅう' in eva):
            continue
        if( '_autosave' in eva):
            continue
        no = int(os.path.splitext(os.path.basename(fn))[0])
        if(no > mno):               #最終番号を探す
            mno = no

      savepath = os.path.join(inpFolder, '%04d.eva' % (mno+1))
      file.save(savepath)
  return str(mno+1)   #保存した番号を返す


# 新しいフォルダができていないか確認するループ
def loop(self,askFolder):
  global inpFolder
  drmax = '0000-0000'
  while True:
    dirs = [f for f in os.listdir(EVA_FOLDER)] #一番新しい日付を取得
    drs = [s for s in dirs if re.match('[0-9]{4}-[0-9]{4}', s)]
    for dr in drs:
      if dr > drmax:
        drmax = dr
    inpFolder = EVA_FOLDER + '/' + drmax

    if(not os.path.exists(inpFolder)):    #フォルダがなかったら作成
        os.mkdir(inpFolder)

    if(askFolder):
        return inpFolder

    dy = datetime.date.today()
    nm = '{:04d}-{:02d}{:02d}'.format(dy.year,dy.month,dy.day)
    dir = EVA_FOLDER + '/' + nm
    if not os.path.exists(dir): #今日のフォルダがなければ作成、読み書き可能にする
        os.mkdir(dir)
        os.chmod(dir, 0o777)
        inpFolder = dir

    time.sleep(5.0) #sleep(秒指定)
  return inpFolder


t = MyThread()
inpFolder = loop(t,True)            #新フォルダへの移動
t.start()

html = 'http://' + HTTP_URL + ':' + str(HTTP_PORT)
img = qrcode.make(html)          #QR code作成
img.save('static/qrcode.png')
with open('static/setpath.ini', mode='w') as f:
    f.write(html)


if __name__ == '__main__':
    webbrowser.open(html)            #ブラウザ起動
    app.run(debug=True, host=HTTP_URL, port=HTTP_PORT)

```

## EVA→GIF変換プログラム

``` python:gif-2.py
import os         # ファイル操作
import shutil     # ファイル操作
import subprocess # プログラム実行
import time       # 時間処理
import re         # 文字置換,正規表現

#ラズベリーパイ 例
ORG_FOLDER = '/home/pi/xxxxxx'    #アニメを保存するフォルダ（オリジナルをmount）
EVA_FOLDER = '/home/share/xxxxxx' #アニメを保存する内部フォルダ(１日分)
QVIEW_EXE = '/home/pi/9va/9view'  #9view.exe のフルパス
EVAorSVG = '.eva' #'.svg'

# Windows 例
#ORG_FOLDER = 'Q:/share/xxxxxx'    #ネットワークドライブで指定
#EVA_FOLDER = 'C:/share/xxxxxx'    #内部フォルダ

# Macintosh 例
#QVIEW_EXE = '/Volumes/9VAe1033a/9VAe.app/Contents/MacOS/9VAe'  #ダウンロードした 9VAeの場合


orgFolder = ORG_FOLDER
inpFolder = EVA_FOLDER

# GIF変換ループ
drmax = '0000-0000'
while True:
    dirs = [f for f in os.listdir(ORG_FOLDER)] #一番新しい日付を取得
    drs = [s for s in dirs if re.match('[0-9]{4}-[0-9]{4}', s)]
    for dr in drs:
      if dr > drmax:
        drmax = dr
    orgFolder = ORG_FOLDER + '/' + drmax
    inpFolder = EVA_FOLDER + '/' + drmax

    #ORGとINPを比較してデータ取得
    if(not os.path.exists(inpFolder)):    #フォルダがなかったら作成
        os.mkdir(inpFolder)

    files = [f for f in os.listdir(orgFolder)]   #入力
    for fn in files:
        eva = os.path.join(inpFolder,fn)
        if(not os.path.exists(eva)):    #ファイルがなかったら転送
          org = os.path.join(orgFolder,fn)
          shutil.copy(org, inpFolder)

    #不要フォルダ削除
    files = [f for f in os.listdir(EVA_FOLDER)]   
    for fn in files:
        if( '.DS_Store' in fn): # Mac用システムフォルダは無視
            continue
        if(fn != drmax):
            dr = os.path.join(EVA_FOLDER,fn)
            shutil.rmtree(dr)

    #サイズ０のEVAファイルは削除
    files = [f for f in os.listdir(inpFolder)]   #入力
    for fn in files:
        if(os.path.splitext(fn)[1] == EVAorSVG):
            dr = os.path.join(inpFolder,fn)
            if(os.path.getsize(dr)==0): #サイズが０のファイルは削除
                os.remove(dr)

    #GIF作成
    files = [f for f in os.listdir(inpFolder)]   #入力
    for fn in files:
        if(os.path.splitext(fn)[1] != EVAorSVG):
            continue
        gif = fn + '.gif'  #GIFファイル名
        gif = os.path.join(inpFolder,gif)
        eva = os.path.join(inpFolder,fn)
        if( '_編集中' in eva):     #自動保存ファイルは無視
            continue
        if( '_とちゅう' in eva):
            continue
        if( '_autosave' in eva):
            continue
        if(os.path.exists(gif)):    #gifが存在,作成すみチェック
            if(os.path.getmtime(gif) > os.path.getmtime(eva)):
                continue
        cmd = (QVIEW_EXE , eva , '-gif') #-gif オプションでGIFに変換
        #cmd = (QVIEW_EXE , eva , '-gif-trans') #-trans 背景透明にする場合
        print(cmd)                  #変換したことを表示
        subprocess.run(cmd)         #gif作成
        break;
    time.sleep(1.0) #sleep(秒指定)

```

# 起動方法（ラズベリーパイ）

起動時に、以下のシェルスクリプトで、EVAアニメを保存する共有フォルダをマウントし、その後、GIF変換、ダウンロードサーバーを起動しています。

```shell
#!/bin/sh 
sudo mount -t cifs //xx.xx.xx.xx/xx/xxxxxx /home/pi/xxxxxx -o user=xxxx,password=xxxx,sec=ntlmssp,nounix,noperm,rw 
sleep 10
cd /home/share/py
python3 /home/share/py/gif-2.py &
python3 /home/share/py/app-2.py
```

- [共有フォルダのマウント方法はこちら](https://qiita.com/danjiro/items/28bb17ab3e2aaf79e572#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E7%94%A8%E3%83%95%E3%82%A9%E3%83%AB%E3%83%80%E3%81%AE%E4%BD%9C%E6%88%90)
- [自動起動の設定はこちら](https://qiita.com/danjiro/items/28bb17ab3e2aaf79e572#7-2-lxde-pi%E3%83%93%E3%83%A5%E3%82%A2%E3%81%AE%E8%87%AA%E5%8B%95%E5%AE%9F%E8%A1%8C)

# 起動方法（Windows）

- Windowsの場合、EVAアニメの共有フォルダをネットワークドライブに割り当てれば、Pythonからアクセスできます。（以下の例では gif.bat の中のnet use 命令で、Qドライブに割り当て）
- GIF変換とサーバーの２つを別々のバッチファイルで起動します。

GIF変換の起動

``` bat:gif.bat
timeout /t 30
net use Q: \\xx.xx.xx.xx\xxxx パスワード /user:ユーザー名
cd C:\Users\xx\xx\xx\py
python gif.py
pause
```

ダウンロードサーバーの起動

``` bat:app.bat
timeout /t 50
cd C:\Users\xx\xx\xx\py
python app.py
pause
```
timeout は指定した時間（秒）待つ命令で、この２つをスタートアップフォルダに入れれば、先にGIF変換が起動し、そのあと、サーバーが起動します。

## 固定IPアドレスの割り当て

- ダウンロードサーバー用 WiFiアクセスポイントは、WiFiルーターを使用しました。
- Windows 機は、Wifi と、EVAアニメを保存する共有サーバーと２つのネットワークに接続します。それぞれ、固定IPアドレスを割り当てました。（コントロールパネル＞ネットワークとインターネット＞アダプターオプション＞WiFiやLANアダプタアイコンの右ボタンメニュー＞プロパティ＞インターネットプロトコル（TCP/IPV4）＞プロパティ＞「IPアドレスを自動的に取得する」から「次のIPアドレスを使う」に変更）
- 設定した固定IPアドレスを、Pythonプログラムの記載と一致させます。

# Macintoshの場合

ダウンロードしたドライブイメージをダブルクリックし、9VAe1033a　というフォルダの中に、9VAe アプリがある場合

```
QVIEW_EXE = '/Volumes/9VAe1033a/9VAe.app/Contents/MacOS/9VAe' 
```
のように書けばGIF変換できます。上のプログラムは、EVAファイルを変換しますが、EVAorSVG を'.svg'にすれば、SVGファイルを変換できます。（.evaのほうがサイズが小さいので、転送エラーがおこる場合は、EVAで運用したほうがよいでしょう）

## 設定例

1. [ここから 9VAeをダウンロード](https://drive.google.com/file/d/0B_jLDEPlHKVjUE0wWkh4THVsOFk/view?usp=sharing)（右上のダウンロードボタン）
1. ダブルクリックして展開。
1. 9VAeを右ボタンクリックから「開く」（２回目に実行できる）
1. ターミナルに9VAeをドラッグすると、9VAeのフルパスがわかります。
1. デスクトップなどに、9VAeアニメを集めるフォルダを作成します。
1. その中に、(1) 上の Python プログラム（gif-2.py）を作ります。
1. (2) 年号-月日というフォルダ名を作ります。例「2023-0505」
1. GIFを出力するフォルダをつくります。年号ー月日と同じ場所につくってもかまいません。
1. gif-2.py プログラムの下の４行を修正します。
1. ORG_FOLDER　年号-月日の親フォルダを指定します。
1. EVA_FOLDER　GIF出力するフォルダを指定します
1. QVIEW_EXE　　9VAeのパスを指定します（最後は9VAe.app/Contents/MacOS/9VAe）
1. EVAorSVG　　SVGファイルを変換する場合は'.svg'にします
1. ターミナルで、gif-2.py をいれたフォルダに移動（cd フォルダ）
1. python3 gif-2.py
1. 年号-月日フォルダが、ORG_FOLDER の中にある場合、その一番最後の月日が、EVA_FOLDERの中に作成され、データ入力待ちになります。最後の月日フォルダの中に、アニメが保存されると、自動的に変換フォルダにコピーされ、同じ名前のGIFファイルが作られます。