Touchdesignerでウェブサイトのキャプチャをリアルタイムレンダリング

## chromeで開いたページをTouchdesignerでリアルタイムレンダリングする

#### **動機**
大学の課題でTouchdesignerを用いた作品を制作する授業があった。そこで、読み込んだウェブサイトがリアルタイムにTouchdesigner上でレンダーされ、サイトの滞在時間によってウィンドウのサイズを変える仕組みを使うことで何か面白い可視化を行えるのではないかと考えた。
先日、講評が終わりひとまず課題は終了となったがまだシステムを設計したに過ぎない段階で作品として、可視化として意義のあるものとして成立させるにはまだまだ道は長い状況である。
しかし、いったんの区切りとして現在の状況をまとめ改めて振り返り、そして備忘録として残しておきたいと思う。また、作品が進展するたびに更新していけたらと思う。
※Touchdesignerのことも、プログラミングのこともそんなに詳しくないため正確でない表現や間違いがあるであろうことは先に言っておく。
(2024.07.15)

#### **実装概要**
1. chromeの拡張機能を作成し訪れたサイトのurlとタイトルとタイムスタンプを取得する。
2. chromeの拡張機能で取得した情報を、flask(正直flaskが何なのかはよくわかっていない、、、[参考サイト](https://aiacademy.jp/media/?p=57))を用いてデータベースに保存する。
3. データベースに保存されたデータをcsvに変換する
4. Touchdesignerでcsvを読み込み、web render topで表示、rectangle sopに適用させるconstant matでweb render topを読み込み3D空間上で取得したwebサイトのキャプチャを表示する。

#### **実装詳細**[プログラムファイル](https://github.com/camen89/window_visualizing/tree/main)
- **chromeの拡張機能を作成する。**
    chromeの拡張機能の作成方法の詳細については他の方が解説されている記事([Chrome拡張機能の実装（入門から公開まで）](https://qiita.com/Nozomuts/items/fc1d4f8fc995d830817d))を参考にする。
    
    ファイル構造は以下の通り
    ```
    browsing_history_tracker/
        ├── background.js
        └── manifest.json
    ```
    background.jsの中身
    ```js
    // background.js
    chrome.history.onVisited.addListener(function(result) {
      const url = result.url;
      const title = result.title || "No Title";
      const time = new Date(result.lastVisitTime).toISOString();
    
      console.log(`Visited URL: ${url}`);
      console.log(`Page Title: ${title}`);
      console.log(`Visit Time: ${time}`);
    
      fetch('http://localhost:5000/save_history', {
          method: 'POST',
          headers: {
              'Content-Type': 'application/json'
          },
          body: JSON.stringify({
              url: url,
              title: title,
              time: time
          })
      }).then(response => {
          if (!response.ok) {
              console.error('Failed to send data to server');
          }
      }).catch(error => {
          console.error('Error:', error);
      });
    });
    ```
    manifest.jsonの中身
    ```json
    {
      "manifest_version": 3,
      "name": "Browsing History Tracker",
      "description": "A Chrome extension to track browsing history.",
      "version": "1.0",
      "permissions": ["history", "tabs"],
      "background": {
          "service_worker": "background.js"
      }
    }

    ```
    ファイルは[このリンク先(github)](https://github.com/camen89/window_visualizing/tree/c33deeda89e120263af8f938565a358db359c150/browsing_history_tracker)に保存している。
    <br>

-  **取得した情報の処理を行う**
    この工程については、あまりよくわかっていない。そろそろ夏休みで時間があるからコードをしっかり読んでいきたいけど取り合え得ず今はよくわからないまま進んでいくことにする。
    
    フォルダ構造は以下の通り
    ```
    browsing_history_server/
            ├── fetch_all_history.py
            ├── formatting.py
            ├── init_db.py
            └── server.py
    ```
    fetch_all_history.pyの中身
    ```python
    import sqlite3
    
    def fetch_all_history():
        conn = sqlite3.connect('browsing_history.db')
        c = conn.cursor()
        c.execute("SELECT * FROM history")
        rows = c.fetchall()
        for row in rows:
            print(f"URL: {row[1]}\nTitle: {row[2]}\nTime: {row[3]}\n")
        conn.close()
    
    if __name__ == '__main__':
        fetch_all_history()
    ```
    formatting.pyの中身
    ここでは、csvの形を指定している、はず、、
    ```python
    import sqlite3
    import csv
    
    def update_csv():
        conn = sqlite3.connect('browsing_history.db')
        c = conn.cursor()
        c.execute("SELECT url, title, time FROM history")
        rows = c.fetchall()
        conn.close()
    
        # 3行n列のCSV形式で保存
        with open('browsing_history.csv', 'w', newline='', encoding='utf-8') as csvfile:
            csvwriter = csv.writer(csvfile)
            
            # 各エントリを3行で保存
            for row in rows:
                csvwriter.writerow(['url', row[0]])
                csvwriter.writerow(['title', row[1]])
                csvwriter.writerow(['timestamp', row[2]])
                csvwriter.writerow([])  # 空行を追加してエントリ間を区切る
    
    if __name__ == '__main__':
        update_csv()
    ```
    init_db.pyの中身
    ```python
    import sqlite3
    
    def init_db():
        conn = sqlite3.connect('browsing_history.db')
        c = conn.cursor()
        c.execute('''CREATE TABLE IF NOT EXISTS history
                     (url TEXT, title TEXT, time TIMESTAMP)''')
        conn.commit()
        conn.close()
    
    if __name__ == '__main__':
        init_db()
    ```
    server.pyの中身
    ```python
    from flask import Flask, request, jsonify
    from flask_cors import CORS
    import sqlite3
    from datetime import datetime, timedelta
    import csv
    from init_db import init_db
    
    app = Flask(__name__)
    CORS(app)  # CORSを有効にしてクロスオリジンリクエストを許可
    
    # アプリケーション起動時にデータベースを初期化
    with app.app_context():
        init_db()
    
    @app.route('/')
    def index():
        return 'Browsing History Tracker Server is running.'
    
    @app.route('/save_history', methods=['POST'])
    def save_history():
        data = request.json
        conn = sqlite3.connect('browsing_history.db')
        c = conn.cursor()
        # ISO 8601形式のタイムスタンプをパースしてdatetimeオブジェクトに変換
        visit_time = datetime.fromisoformat(data['time'].replace('Z', '+00:00'))
        # データを改行付きで保存
        c.execute("INSERT INTO history (url, title, time) VALUES (?, ?, ?)",
                  (data['url'], data['title'], visit_time))
        conn.commit()
        conn.close()
        # CSVファイルを更新
        update_csv()
        return jsonify({"status": "success"}), 200
    
    def update_csv():
        conn = sqlite3.connect('browsing_history.db')
        c = conn.cursor()
        c.execute("SELECT url, title, time FROM history")
        rows = c.fetchall()
        conn.close()
    
        with open('browsing_history.csv', 'w', newline='', encoding='utf-8') as csvfile:
            csvwriter = csv.writer(csvfile)
            # ヘッダー行を書く
            csvwriter.writerow(['url', 'title', 'timestamp', 'subtraction'])
            previous_time = None
            for row in rows:
                current_time = datetime.fromisoformat(row[2])
                if previous_time:
                    subtraction = (current_time - previous_time).total_seconds()
                else:
                    subtraction = timedelta(0)
                csvwriter.writerow([row[0], row[1], row[2], str(subtraction)])
                previous_time = current_time
    
    if __name__ == '__main__':
        app.run(debug=True)
    ```
    ファイルは[このリンク先(github)](https://github.com/camen89/window_visualizing/tree/main/browsing_history_server)に保存している。
    ※csvとデータベース(.dbファイル)についてはプログラムの実行後に自動で作成されるようになっているので用意する必要はない。
<br>

- **server.pyを実行する** 
    コマンドプロンプト(macの場合はターミナル)を用いてプログラムを実行する。
    ```batch
    C:\Users\Ikeda>cd C:\Users\Ikeda\OneDrive\デスクトップ\touchdesigner_最終講評\browsing_history_server
    C:\Users\Ikeda\OneDrive\デスクトップ\touchdesigner_最終講評\browsing_history_server>
    ```
    上記のように、``cd パス名``と書き、実行したいファイル(ここではserver.py)のある場所まで移動する。たぶんmacでも同じだった、はず。
    ```batch
    C:\Users\Ikeda\OneDrive\デスクトップ\touchdesigner_最終講評\browsing_history_server>python server.py
     * Serving Flask app 'server'
     * Debug mode: on
    WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
     * Running on http://127.0.0.1:5000
    Press CTRL+C to quit
     * Restarting with stat
     * Debugger is active!
     * Debugger PIN: 684-011-203
    ```
    上記のように、``python server.py``とコマンドをうつことでプログラムが実行される。このことで.dbファイルとcsvが作成され記録されていく。
    ※上手く実行されずこれをインストールしろとかいわれたらとりあえずしたがって、また``python server.py``を実行する。
    ※macの場合いろいろ違う感じだった。(普段macを使ってないからあまり詳しくはわからないけどmacで実行したときにはまったとこを記録しておく)
    1.実行したコードを停止するとき``CTRL+C``を打つけどctrlとcommandを混同しないように注意する。
    2.いろいろインストールさせられていよいよ実行ってときに毎回、前にインストールしたパッケージをインストールさせられた。これはプログラムを実行するときに前に作成した仮想環境を有効化していないことが原因だった。そのため、``cd パス名``でファイルがある場所に移動した後に仮想環境を有効化するために``. [環境名]/bin/activate``コマンドを打つ必要がある。ただし、``.``もあとは半角スペースが必要。[参考サイト](https://code-graffiti.com/virtual-environment-on-mac-with-venv-in-python/)
     3.windowsでは``python server.py``で実行できたのにmacではできないことがあった。これの原因ははっきとはよくわかっていないが、macで実行する場合は``python3 server.py``のようにpythonの後ろに3と表記することで動作するようになったので注意。
<br>

- **Touchdesignerでレンダリングする**
    現時点では、Touchdesignerでの実装は非常にシンプルでweb render topを用いて3D空間上でレンダリングしているだけ。
    [toeファイル](https://github.com/camen89/window_visualizing/blob/main/LinkSystem.176.toe)



（以上）