diff --git a/src-python/docs/details/clipboard.md b/src-python/docs/details/clipboard.md new file mode 100644 index 00000000..994fd808 --- /dev/null +++ b/src-python/docs/details/clipboard.md @@ -0,0 +1,250 @@ +# Clipboard 機能設計書 + +## 概要 +VRCT のクリップボード機能は、VRChat 内でのテキスト送信効率を向上させるため、翻訳結果をクリップボードにコピーし、ペースト機能を提供します。 + +## 主要機能 + +### 1. テキストコピー +- 翻訳結果をシステムクリップボードにコピー +- 複数のバックエンド対応(Windows clip、pyperclip、tkinter) +- UTF-16LE BOM 対応でクロスプラットフォーム互換性を確保 + +### 2. テキストペースト +- `Ctrl+V` による自動ペースト +- VRChat ウィンドウの自動フォーカス +- カウントダウン機能で準備時間を確保 + +### 3. VR アプリ検出 +- OpenVR を用いた VRChat アプリ名の自動検出 +- バックグラウンドスレッドによる非同期監視 +- SteamVR の起動を待機・検出 + +## クラス設計 + +### Clipboard クラス +```python +class Clipboard: + def __init__(self) + def enable(self) -> None + def disable(self) -> None + def copy(self, message: str) -> bool + def paste(self, window_name: str|None = None, countdown: int = 0) -> bool +``` + +#### メンバ変数 +- `is_enabled`: クリップボード機能の有効/無効状態 +- `app_name`: VRChat アプリケーション名(自動検出) +- `_vr_monitor_thread`: SteamVR 監視スレッド +- `_stop_monitoring`: スレッド停止フラグ + +### 主要メソッド + +#### `__init__()` +初期化処理: +1. VR 監視スレッドを起動 +2. SteamVR の起動を待機(10秒間隔で確認) + +#### `enable()` +クリップボード機能を有効化: +1. `is_enabled` フラグを True に設定 +2. `_initialize()` を呼び出し VR 監視を再開 + +#### `disable()` +クリップボード機能を無効化: +1. `is_enabled` フラグを False に設定 +2. `_stop_monitoring` フラグを設定しスレッド停止 +3. スレッド終了を待機(タイムアウト 1秒) + +#### `copy(message: str) -> bool` +**パラメータ:** +- `message`: クリップボードにコピーするテキスト + +**戻り値:** +- `True`: コピー成功 +- `False`: コピー失敗または無効状態 + +**動作:** +1. `is_enabled` チェック +2. `copy_to_clipboard()` 関数を呼び出し + +#### `paste(window_name: str|None = None, countdown: int = 0) -> bool` +**パラメータ:** +- `window_name`: フォーカス対象ウィンドウ(タイトル部分一致 or プロセス名) +- `countdown`: ペースト前の待機秒数 + +**戻り値:** +- `True`: ペースト送信成功 +- `False`: ペースト失敗または無効状態 + +**動作:** +1. `is_enabled` チェック +2. `window_name` 未指定時は自動検出した VRChat アプリ名を使用 +3. Windows 環境下でウィンドウをフォーカス(以下の順序) + - ウィンドウタイトルで部分一致検索 + - マッチしない場合、プロセス名で検索 +4. ウィンドウフォーカス成功時、`paste_via_pyautogui()` でペースト実行 + +### 内部メソッド + +#### `_initialize()` +VR 監視スレッドの初期化: +1. `_stop_monitoring` を False に設定 +2. デーモンスレッドで `_monitor_steamvr()` を起動 + +#### `_monitor_steamvr()` +バックグラウンドスレッド処理: +1. 10秒間隔で `checkSteamvrRunning()` を確認 +2. SteamVR 検出時に `_setup_vr_app_name()` を呼び出し +3. `_stop_monitoring` が True になるまで監視 + +#### `_setup_vr_app_name()` +OpenVR を用いた VR アプリ名検出: +1. OpenVR 初期化(`VRApplication_Background` モード) +2. すべてのアプリケーション情報を取得 +3. `steam.app` で始まるキーを検索し `app_name` に設定 +4. 例外発生時は `app_name` を None に設定 + +## 支援関数 + +### `checkSteamvrRunning() -> bool` +SteamVR が起動しているかを確認 +- プロセス名 `vrmonitor.exe`(Windows)または `vrmonitor`(その他)を検索 + +### `copy_to_clipboard(text: str) -> bool` +複数バックエンド対応のコピー関数 +1. Windows: `clip` コマンド + UTF-16LE BOM +2. 汎用: `pyperclip` ライブラリ +3. フォールバック: `tkinter` 使用 + +### `paste_via_pyautogui(countdown: int = 0) -> bool` +PyAutoGUI を用いたペースト +- `pyautogui.hotkey('ctrl', 'v')` で Ctrl+V を送信 +- カウントダウン実行 + +### ウィンドウ検索関数(Windows のみ) + +#### `find_windows_by_title_substring(substring: str) -> list` +ウィンドウタイトルで部分一致検索 + +#### `find_windows_by_process_name(proc_name: str) -> list` +プロセス名でウィンドウを検索 + +#### `focus_window(hwnd) -> bool` +指定ウィンドウをアクティブ化 + +## 依存ライブラリ + +| ライブラリ | 用途 | オプション | +|-----------|------|----------| +| pyautogui | キー入力シミュレーション | 必須(ペースト機能) | +| pyperclip | クリップボード操作 | オプション(フォールバック) | +| tkinter | クリップボード操作 | オプション(フォールバック) | +| openvr | VR アプリ名検出 | 必須 | +| psutil | プロセス検索 | 必須 | + +## 動作フロー + +### 初期化フロー +``` +Clipboard.__init__() + └─ _initialize() + └─ スレッド起動: _monitor_steamvr() + └─ 10秒間隔で SteamVR 監視 + └─ 検出時: _setup_vr_app_name() + └─ VRChat アプリ名を app_name に設定 +``` + +### コピー・ペーストフロー +``` +Model.setCopyToClipboard(text) + └─ clipboard.copy(text) + └─ copy_to_clipboard(text) [成功/失敗] + +Model.setPasteFromClipboard() + └─ clipboard.paste(window_name=self.app_name) + └─ find_windows_by_title_substring() [Windows] + or find_windows_by_process_name() + └─ focus_window(hwnd) + └─ paste_via_pyautogui(countdown) +``` + +## 有効/無効制御 + +### コンフィグ設定 +- `config.ENABLE_CLIPBOARD`: True/False でクリップボード機能を制御 +- Controller 側で `config.ENABLE_CLIPBOARD` をチェックし、True の場合のみ `setCopyToClipboard()` と `setPasteFromClipboard()` を呼び出し + +### 制御メソッド(Clipboard クラス内部) +```python +# 有効化(内部API、通常は使用しない) +clipboard.enable() + +# 無効化(内部API、通常は使用しない) +clipboard.disable() +``` + +**注意**: 現在の実装では、Controller は `clipboard.enable()/disable()` を呼び出さず、`config.ENABLE_CLIPBOARD` フラグのみで制御します。Clipboard インスタンスは常に初期化されており、VR 監視スレッドも稼働し続けます。 + +### API エンドポイント(Controller) +``` +/get/data/clipboard - 現在の有効/無効状態を取得 +/set/enable/clipboard - config.ENABLE_CLIPBOARD を True に設定 +/set/disable/clipboard - config.ENABLE_CLIPBOARD を False に設定 +``` + +## エラーハンドリング + +### 失敗時の動作 +- コピー失敗: `False` を返却、リトライなし +- ペースト失敗: `False` を返却、リトライなし +- VR アプリ名検出失敗: `app_name` を None に設定、現在フォーカス中のウィンドウにペースト + +### 例外処理 +- OpenVR 初期化失敗時は `app_name = None` に設定 +- プロセス検索例外も握りつぶし +- ウィンドウフォーカス失敗はログのみ出力 + +## パフォーマンス考慮 + +### スレッド設計 +- VR 監視スレッドは **デーモンスレッド**(アプリ終了時の強制停止可能) +- 10秒間隔で監視(CPU 負荷最小化) + +### クリップボード操作 +- コピー・ペースト操作はメインスレッド実行 +- ウィンドウフォーカス後に 0.2秒スリープで安定性向上 + +## セキュリティ・プライバシー + +### データ保護 +- クリップボードデータは一切ログ出力されない +- VR アプリ名のみ app_name として保持 + +### OS 依存性 +- Windows: `clip` コマンド、ctypes によるウィンドウ制御 +- その他 OS: pyperclip/tkinter フォールバック、自動フォーカス機能なし + +## 制限事項・既知の問題 + +1. **非 Windows 環境** + - ウィンドウフォーカス機能は Windows のみ対応 + - その他 OS では現在フォーカス中のウィンドウにペースト + +2. **VRChat アプリ名検出** + - OpenVR が利用可能で SteamVR が起動している必要がある + - `steam.app` 接頭辞のアプリのみ認識 + +3. **PyAutoGUI の制限** + - キー入力シミュレーションはOS依存 + - 一部アプリケーションではセキュリティ制限により失敗する可能性あり + +4. **クリップボードバッファ** + - Ctrl+V 送信のタイミングはユーザー判断(カウントダウン機能あり) + - クリップボード内容の永続化はしない + +## 参考資料 + +- [PyAutoGUI 公式ドキュメント](https://pyautogui.readthedocs.io/) +- [OpenVR Python バインディング](https://github.com/ValvePython/openvr) +- [psutil ドキュメント](https://psutil.readthedocs.io/) diff --git a/src-python/docs/details/config.md b/src-python/docs/details/config.md index 1c008b03..f01ba90e 100644 --- a/src-python/docs/details/config.md +++ b/src-python/docs/details/config.md @@ -189,6 +189,24 @@ def WEBSOCKET_PORT(self) -> int ``` - WebSocketサーバーポート +### クリップボード設定 + +```python +@property +def ENABLE_CLIPBOARD(self) -> bool +``` +- クリップボード機能(コピー・ペースト)の有効・無効 + +### テレメトリ設定 + +```python +@property +def ENABLE_TELEMETRY(self) -> bool +``` +- テレメトリ(Aptabase)の有効・無効 +- デフォルト: True(有効) +- ユーザーは設定から任意で無効化可能 + ### 計算デバイス設定 ```python @@ -341,6 +359,12 @@ config.saveConfig("ENABLE_TRANSLATION", True, immediate_save=True) - WebSocket ホスト: "127.0.0.1" - WebSocket ポート: 8765 +### クリップボード設定 +- クリップボード機能: 無効 + +### テレメトリ設定 +- テレメトリ: **有効(デフォルト)** + ## 依存関係 ### 必須依存関係 diff --git a/src-python/docs/details/controller.md b/src-python/docs/details/controller.md index 79f7e751..d86c5925 100644 --- a/src-python/docs/details/controller.md +++ b/src-python/docs/details/controller.md @@ -247,12 +247,39 @@ setDisableWebSocketServer(data) -> dict - WebSocketサーバーの制御 +### クリップボード制御 + +```python +getClipboard() -> dict +setEnableClipboard() -> dict +setDisableClipboard() -> dict +``` + +- クリップボード機能(コピー・ペースト)の状態取得・設定変更 +- `config.ENABLE_CLIPBOARD` フラグを変更(True/False) +- 取得結果は `{"status": 200, "result": bool}` +- **注意**: `model.clipboard.enable()/disable()` は呼び出されません + +### テレメトリ制御 + +```python +getTelemetry() -> dict +setEnableTelemetry() -> dict +setDisableTelemetry() -> dict +``` + +- テレメトリ(Aptabase)の有効化・無効化・状態取得 +- 有効化時は `telemetryInit()` を呼び出し +- 無効化時は `telemetryShutdown()` を呼び出し + +### WebSocket接続設定 + ```python setWebSocketHost(data) -> dict setWebSocketPort(data) -> dict ``` -- WebSocket接続設定 +- WebSocketホスト・ポートの設定 ### システム管理 diff --git a/src-python/docs/details/mainloop.md b/src-python/docs/details/mainloop.md index 02175264..8ce7d413 100644 --- a/src-python/docs/details/mainloop.md +++ b/src-python/docs/details/mainloop.md @@ -125,6 +125,18 @@ class Main: - `/set/data/websocket_host`: サーバーホスト設定 - `/set/data/websocket_port`: サーバーポート設定 +### クリップボード機能 + +- `/get/data/clipboard`: クリップボード機能の状態取得(`config.ENABLE_CLIPBOARD` の値) +- `/set/enable/clipboard`: `config.ENABLE_CLIPBOARD` を True に設定 +- `/set/disable/clipboard`: `config.ENABLE_CLIPBOARD` を False に設定 + +### テレメトリ機能 + +- `/get/data/telemetry`: テレメトリの状態取得 +- `/set/enable/telemetry`: テレメトリの有効化(Aptabase初期化) +- `/set/disable/telemetry`: テレメトリの無効化(シャットダウン) + ### システム管理 - `/run/update_software`: ソフトウェアアップデート diff --git a/src-python/docs/details/model.md b/src-python/docs/details/model.md index 9a7da932..9288f511 100644 --- a/src-python/docs/details/model.md +++ b/src-python/docs/details/model.md @@ -4,6 +4,25 @@ VRCTアプリケーションの中核となるModelクラスを定義するモジュールです。音声認識、翻訳、VRオーバーレイ、OSC通信、WebSocketサーバーなどの主要機能を統合管理し、システム全体の動作を制御します。 +## 最近の更新 (2026-01-11) + +### クリップボード機能 + +- `clipboard` インスタンスを Model 内に保持 +- `setCopyToClipboard(text: str) -> bool`: 翻訳結果をクリップボードにコピー +- `setPasteFromClipboard() -> bool`: Ctrl+V でペースト実行(VRChat ウィンドウ自動フォーカス) +- OpenVR による VRChat アプリ名の自動検出 + +### テレメトリ機能 + +- `telemetryInit(enabled: bool, app_version: str)`: Aptabase を用いたテレメトリ初期化 +- `telemetryShutdown()`: テレメトリのシャットダウン(app_closed イベント送信) +- `telemetryTouchActivity()`: ユーザーアクティビティの記録 +- `telemetry.*()`: イベント送信メソッド群 +- デフォルト有効、ユーザー制御可能な設計 + +### その他の更新 + ## 最近の更新 (2025-10-20) ### VRAMエラー検出とフォールバック @@ -315,6 +334,30 @@ message = {"type": "translation", "text": "Hello", "translation": "こんにち success = model.websocketSendMessage(message) ``` +### クリップボード機能の使用 + +```python +# テキストをクリップボードにコピー +text = "翻訳結果" +success = model.setCopyToClipboard(text) + +# ペースト実行(VRChat ウィンドウに自動フォーカス) +success = model.setPasteFromClipboard() +``` + +### テレメトリの初期化・シャットダウン + +```python +# テレメトリ初期化(アプリ起動時) +model.telemetryInit(enabled=config.ENABLE_TELEMETRY, app_version=config.VERSION) + +# テレメトリシャットダウン(アプリ終了時) +model.telemetryShutdown() + +# アクティビティ記録 +model.telemetryTouchActivity() +``` + ## 依存関係 ### 必須モジュール diff --git a/src-python/docs/詳細設計書.md b/src-python/docs/詳細設計書.md index 11b539ee..08343e89 100644 --- a/src-python/docs/詳細設計書.md +++ b/src-python/docs/詳細設計書.md @@ -8,6 +8,8 @@ - Main (mainloop) - DeviceManager - Utils +- Clipboard(クリップボード機能) +- Telemetry(テレメトリ機能) - モデルの重みダウンロードと整合性 ## Model @@ -18,20 +20,28 @@ - Overlay/OSChandler/WebSocket の操作 - キーワード検出(flashtext)と重複検出 - VRAM エラー検出とフォールバック + - クリップボード操作(copy/paste) + - テレメトリ初期化・シャットダウン - 重要属性(抜粋) - `translator` : Translator インスタンス - `overlay` / `overlay_image` : Overlay 系 - `mic_*`, `speaker_*` : 録音、トランスクリプタ、energy recorder - `watchdog` : Watchdog - `osc_handler`, `websocket_server` + - `clipboard` : Clipboard インスタンス(2026-01-11 追加) + - `telemetry` : テレメトリシステム(2026-01-11 追加) - スレッド制御 - threadFnc を用いて周期処理を回す。stop/pause/resume が可能。 + - Clipboard の VR 監視スレッドはデーモンスレッド(アプリ終了時に強制停止可能) ## Controller - GUI からの要求を受け、Model を操作して結果を run() コールバックへ返す。 - 各種設定変更 (/set/ や /get/ エンドポイント) を実装。 - 翻訳/文字起こし/オーバーレイ連携ロジックを持ち、メッセージ整形(messageFormatter)や OSC の送信を行う。 - ダウンロード作業は別スレッドで行い、進捗を run_mapping を通して通知。 +- クリップボード/テレメトリ制御エンドポイント追加(2026-01-11) + - `getClipboard()` / `setEnableClipboard()` / `setDisableClipboard()` + - `getTelemetry()` / `setEnableTelemetry()` / `setDisableTelemetry()` ## Main (mainloop.Main) - stdin を readline() で受け取り JSON を parse、endpoint と data をキューへ投入。 @@ -50,6 +60,84 @@ - `setupLogger()` / `printLog()` / `printResponse()` / `errorLogging()` : ログ、標準出力の整形、エラー記録。 - ネットワーク/ソケット/IP アドレス検査ユーティリティ。 +## Clipboard(クリップボード機能) + +### 概要(2026-01-11 追加) +VRChat 内でのテキスト送信効率向上のため、翻訳結果をクリップボードにコピーし、自動ペースト機能を提供。 + +### 設計 +- **シングルトン**: `models.clipboard.clipboard.Clipboard` クラス +- **初期化**: `Model.__init__()` で `self.clipboard = Clipboard()` インスタンス化 +- **VR監視スレッド**: デーモンスレッドで 10 秒間隔 SteamVR 監視、VRChat アプリ名を自動検出 +- **コピー・ペースト**: + - `setCopyToClipboard(text: str) -> bool`: 複数バックエンド対応(Windows clip、pyperclip、tkinter) + - `setPasteFromClipboard() -> bool`: PyAutoGUI による Ctrl+V 送信、VRChat ウィンドウ自動フォーカス + +### 依存ライブラリ +- `pyautogui==0.9.54`: キー入力シミュレーション(requirements.txt 追加) +- `openvr`: VR アプリ名検出 +- `psutil`: プロセス検索 +- `pyperclip`(オプション): クリップボード操作フォールバック + +### 設定・制御 +- `config.ENABLE_CLIPBOARD`: True/False で機能制御 +- Controller エンドポイント + - `/get/data/clipboard`: 状態取得(`config.ENABLE_CLIPBOARD` の値) + - `/set/enable/clipboard`: `config.ENABLE_CLIPBOARD` を True に設定 + - `/set/disable/clipboard`: `config.ENABLE_CLIPBOARD` を False に設定 + +### 動作フロー +1. Model 初期化時に Clipboard インスタンス生成(VR 監視スレッド起動) +2. Controller が `config.ENABLE_CLIPBOARD == True` をチェック +3. 有効時のみ `model.setCopyToClipboard(result)` と `model.setPasteFromClipboard()` を呼び出し +4. Clipboard インスタンス自体は常に稼働(`clipboard.is_enabled` は常に True) + +### エラーハンドリング +- コピー失敗: False を返却、リトライなし +- ペースト失敗: False を返却 +- VR アプリ名検出失敗: app_name = None、現在フォーカスウィンドウにペースト +- 例外はログ出力し握りつぶし + +## Telemetry(テレメトリ機能) + +### 概要(2026-01-11 追加) +Aptabase を用いた匿名な使用状況データ収集。デフォルト有効、ユーザー制御可能。 + +### 設計 +- **デフォルト有効**: `config.ENABLE_TELEMETRY = True` +- **初期化**: `model.telemetryInit(enabled=config.ENABLE_TELEMETRY, app_version=config.VERSION)` +- **シャットダウン**: `model.telemetryShutdown()` で app_closed イベント送信 +- **プライバシー重視**: 個人情報・入力内容・音声データ一切送信なし + +### イベント種別(送信固定) +- `app_started`: アプリ起動 +- `app_closed`: アプリ終了(最後のイベント) +- `session_heartbeat`: 5 分間隔アクティブ確認 +- `core_feature`: 機能開始(translation / mic_speech_to_text / speaker_speech_to_text / text_input) +- `settings_opened`: 設定画面開閉 +- `config_changed`: 設定変更 +- `error`: エラー発生 + +### 動作フロー +1. アプリ起動時に telemetryInit() 呼び出し +2. ユーザーアクティビティ検出時に telemetryTouchActivity() 呼び出し +3. 機能開始時に track_core_feature() で 1 セッション 1 回のみ送信 +4. アプリ終了時に telemetryShutdown() で app_closed 送信 +5. config.ENABLE_TELEMETRY = False で一切の通信・スレッド停止 + +### 設定・制御 +- `config.ENABLE_TELEMETRY`: True/False で機能制御 +- `model.telemetry.enabled`: 現在の有効状態 +- Controller エンドポイント + - `/get/data/telemetry`: 状態取得 + - `/set/enable/telemetry`: 有効化(telemetryInit 呼び出し) + - `/set/disable/telemetry`: 無効化(telemetryShutdown 呼び出し) + +### エラーハンドリング +- API 通信失敗時: 例外握りつぶし、アプリ動作に影響なし +- オフライン時: 機能停止のみ、再送・バッファリングなし +- 無効化時: 一切の通信・スレッド・処理停止 + ## モデル重みダウンロード - `models.translation.translation_utils` と `models.transcription.transcription_whisper` にダウンロード/チェック関数があり、チェックサムやファイル存在を検証する。 - GUI からの要求は Controller により非同期スレッドで実行され、進捗コールバックが run_mapping を介してフロントエンドに渡る。