TouchDesignerでもFirebaseのRealtime Databaseがしたい！

## TouchDesignerでもFirebaseのRealtime Databaseがしたい！

TouchDesigner Advent Calendar 2018 12/24の記事です。
クリスマスイブですね。
寒いです。
みなさんGPUで暖を取りながらこの記事をみましょう。
さて本題に入りましょう。

## Firebaseとは
Googleが運営しているmBaaSで、iOS/AndroidアプリからWebサービスまで幅広く使えます。
https://firebase.google.com/?hl=ja

mBaaSってなんやねんというかた
https://boxil.jp/mag/a3651/

色々機能はありますが、今回使うのは
**Cloud Firestore（Realtime Database）**
　リアルタイムで同期ができるデータベース。現時点ではまだベータ版。
　似たものでRealtime Databaseというものがあります。

**Cloud Storage**
　Firebase向けのGoogle Cloud Storage
　ユーザー作成した画像やファイルをアップロード/ダウンロードできます。

## やること
**特定のウェブサイトからFirebaseにアクセス、データベースにデータプッシュし、
TouchDesigner側で表示するコンテンツ**

これだけだと、「いや、全部Webでやったほうが楽だ。」とか言い出す無粋な人もいますが、
無駄なことに趣があるのですよ。

## とりあえず動かす

###環境は
・Mac book pro
・TouchDesigner non-commercial
・anaconda
・python3系


今回は誰でも試せるようにnon-commercialで、
Firebaseも無料枠でやります。
anacondaは無くていいと思います。とりあえず、python3系が動けばおk

**筆者はPythonには全く詳しくないので、コードの書き方は適当です（無責任）**

TouchDesigner内部で使えるプログム言語は、non-commercialだとPythonだけのはず（違ったらごめんなさい）
なので、PythonでFirebaseにアクセスします。

Pythonのライブラリが何個かあります。
https://firebase.google.com/docs/database/rest/start?hl=ja
>James Childs-Maidment 氏による Pyrebase
>Özgür Vatansever 氏による python-firebase
>Michael Huynh 氏による python-firebase

今回はPyrebaseを使います。
Pyrebaseのインストールにはpipを使います。
https://www.python.jp/install/windows/pip.html

```
$ pip3 install Pyrebase
```
これでok


次にfirebaseにログインして「コンソールからプロジェクトを追加」をします。
<img width="526" alt="スクリーンショット 2018-12-05 14.56.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/152896/5976f37d-c21e-79ed-c623-d53ce31be371.png">

プロジェクト作成後、「ウェブアプリにFirebaseを追加」
からconfig情報を取得しましょう。
firebaseの場合、apikeyを晒してもセキュリティのリスクはないらしいですが、一応隠しておきます。

```
var config = {
  apiKey: "●●●●●●●●●●●●●●●●●●●●●●●●●●●●●",
  authDomain: "td-qiita-firebase.firebaseapp.com",
  databaseURL: "https://td-qiita-firebase.firebaseio.com",
  projectId: "td-qiita-firebase",
  storageBucket: "td-qiita-firebase.appspot.com",
  messagingSenderId: "●●●●●●●●●●●●●●●●●"
};
```
右の開発からdatabaseに行って、databaseを作成します。
database作成時にテストモードにします。
databaseのルールは適当に、誰でも読み書きできる設定にしておきます。

```
{
  "rules": {
    ".read": true,
    ".write": true
  }
}
```


次にTouchDesignerにPythonのモジュール（Pyrebase）を読み込ませます。
Generalタブ内のPython 64-bit Module Pathに 64-bit Pythonのsite-packagesを追加するだけですね。

外部moduleまでpathがわからない場合は

```
$ pip3 show Pyrebase

Name: Pyrebase
Version: 3.0.27
Summary: A simple python wrapper for the Firebase API
Home-page: https://github.com/thisbejim/Pyrebase
Author: James Childs-Maidment
Author-email: UNKNOWN
License: MIT
Location: /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages
Requires: requests, gcloud, oauth2client, requests-toolbelt, python-jwt, pycryptodome
```
でわかります。

Moduleの詳しい話はこちらで
https://qiita.com/oishihiroaki/items/e11dfa90638a13e2a5f5

↑これでうまく行かない場合、スクリプトでパスを指定してあげましょう
text.datを作成
setModule と名前をつけます。

```
import sys
mypath= '/Users/machida-yosuke/.pyenv/versions/anaconda3-4.2.0/lib/python3.5/site-packages'
if mypath not in sys.path:
	sys.path.append(mypath)
print(sys.path)
```

Pyrebaseを読み込ませることができるか確かめましょう。
text.datを作成
initPyrebase と名前をつけます。

```
import pyrebase

# firebaseのconfigから一部をコピペ
config = {
  "apiKey": "●●●●●●●●●●●●●●●●●",
  "authDomain": "td-qiita-firebase.firebaseapp.com",
  "databaseURL": "https://td-qiita-firebase.firebaseio.com",
  "storageBucket": "td-qiita-firebase.appspot.com"
}

firebase = pyrebase.initialize_app(config)
db = firebase.database()
print(db)
```
⌘ + Rで実行すると、

```
<pyrebase.pyrebase.Database object at 0x121a80748>
```
おっ、アクセスできました。

つぎにdatabaseにデータを突っ込みましょう。
text.datを作成
pushDatabase と名前をつけます。

```
import initPyrebase
def push():	
	data = {
		"name": "TD kanzen ni rikai sita",
		'time': absTime.frame
	}
	initPyrebase.db.child("users").push(data)
push()
```
⌘ + R連打で実行すると、、、、
![名称未設定.gif](https://qiita-image-store.s3.amazonaws.com/0/152896/7f303301-badb-d2fe-d2d2-d0a92ab4e529.gif)

おっ、動きますね。TouchDesigner側からのpushは成功です。
まあ、TouchDesigner側からfirebaseにpostすることはほぼないでしょう。


次にdatabase（/users）に変更があった場合、getするようにしましょう。

**ここで問題があり、なぜかpostやget時にフレームレートがかなり落ちます。**
**なので、Pyrebaseを使わず、node.jsとOSCでdatabaseの変更をTouchDesigner側に送ります。**
**さよならPyrebase**

まず、適当に画像と名前をpostできるwebサイトと、node.jsでoscを飛ばします。
webサイト

``` index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <div class="input-text"><input type="text" /></div>
    <div class="input-image"><input type="file" accept="image/*" /></div>
    <img class="imege-preview"></img>
    <div class="button">送信</div>
    <script src="./bundle.js"></script>
  </body>
</html>
```

```  script.js
import * as firebase from "firebase/app";
import "firebase/database";
import "firebase/storage";
import config from "./config";
import "./style.scss";

firebase.initializeApp(config);
let filename;
let filetype;
let blob;
let inputValue;
const storageRef = firebase.storage().ref();
const databaseRef = firebase.database().ref("/users");

const reader = new FileReader();
const button = document.querySelector(".button");
const preview = document.querySelector(".imege-preview");
const imgaeInput = document.querySelector(".input-image input");

imgaeInput.addEventListener(
  "change",
  e => {
    const file = e.target.files[0];
    filename = file.name;
    filetype = file.type;
    reader.readAsDataURL(file);
  },
  false
);

reader.addEventListener(
  "load",
  () => {
    const ImageBase64 = reader.result;
    blob = toBlob(ImageBase64);
    preview.src = window.URL.createObjectURL(blob);
  },
  false
);

button.addEventListener(
  "click",
  () => {
    inputValue = document.querySelector(".input-text input").value;
    if (inputValue.length === 0 || !preview.src) {
      return;
    }
    postImage();
  },
  false
);

async function postImage() {
  const uploadRef = await storageRef.child(filename);
  const snapshot = await uploadRef.put(blob);
  const url = await uploadRef.getDownloadURL();
  const createdTime = Date.now();
  databaseRef.push({
    url,
    name: inputValue,
    time: createdTime
  });
}

function toBlob(base64) {
  var bin = atob(base64.replace(/^.*,/, ""));
  var buffer = new Uint8Array(bin.length);
  for (var i = 0; i < bin.length; i++) {
    buffer[i] = bin.charCodeAt(i);
  }
  try {
    var blob = new Blob([buffer.buffer], {
      type: filetype
    });
  } catch (e) {
    return false;
  }
  return blob;
}
```

node.js側
node-oscを使います。

``` script.js
var osc = require("node-osc");
var firebase = require("firebase");
var config = require("./config.js");

var firebaseConfig = config.config;
var dt = Date.now();

var app = firebase.initializeApp(firebaseConfig);
var oscClient = new osc.Client("localhost", 6000);
var messagesRef = app
  .database()
  .ref()
  .child("users");

messagesRef.on("child_added", function(snapshot) {
  var msg = snapshot.val();
  if (dt > msg.time) {
    return;
  }
  console.log("追加されたぞ", msg);
  oscClient.send("/", msg.url, msg.name, function() {});
});

```

TouchDesigner側は、
text topと
moviefilein　topと
DatからOSC　Inを追加します。
oscin_callbackに

```
def onReceiveOSC(dat, rowIndex, message, bytes, timeStamp, address, args, peer):
	op('text1').par.text = args[1]
	op('moviefilein1').par.file = args[0]
	return
```
と書けば終わりです。

webサイトからfirebaseに画像と文字列postして、
nodeでdatabaseの変更を感知して、oscでTouchDesignerにおくり、
TouchDesignerで表示させましょう。

![名称未設定.gif](https://qiita-image-store.s3.amazonaws.com/0/152896/5272e822-e2e9-d2ce-cf09-5aad786a9abd.gif)

できた。

画像ロード時にフレームレートが落ちてしまうので、moviefileinを二つ用意して、ロードが終わったらSwitchで切り替えるとかしたほうがいいですね。

githubはこちら
https://github.com/machida-yosuke/node-osc

## まとめ
OSCは便利ということ