[Add] websocket server
This commit is contained in:
4
src-python/models/websocket/__init__.py
Normal file
4
src-python/models/websocket/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# WebSocketサーバーモジュール
|
||||||
|
from .websocket_server import WebSocketServer
|
||||||
|
|
||||||
|
__all__ = ["WebSocketServer"]
|
||||||
103
src-python/models/websocket/model_part.py
Normal file
103
src-python/models/websocket/model_part.py
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import copy
|
||||||
|
import gc
|
||||||
|
from subprocess import Popen
|
||||||
|
from os import makedirs as os_makedirs
|
||||||
|
from os import path as os_path
|
||||||
|
from datetime import datetime
|
||||||
|
from time import sleep
|
||||||
|
from queue import Queue
|
||||||
|
from threading import Thread
|
||||||
|
from requests import get as requests_get
|
||||||
|
from typing import Callable
|
||||||
|
from packaging.version import parse
|
||||||
|
|
||||||
|
from flashtext import KeywordProcessor
|
||||||
|
from pykakasi import kakasi
|
||||||
|
|
||||||
|
from device_manager import device_manager
|
||||||
|
from config import config
|
||||||
|
|
||||||
|
from models.translation.translation_translator import Translator
|
||||||
|
from models.osc.osc import OSCHandler
|
||||||
|
from models.transcription.transcription_recorder import SelectedMicEnergyAndAudioRecorder, SelectedSpeakerEnergyAndAudioRecorder
|
||||||
|
from models.transcription.transcription_recorder import SelectedMicEnergyRecorder, SelectedSpeakerEnergyRecorder
|
||||||
|
from models.transcription.transcription_transcriber import AudioTranscriber
|
||||||
|
from models.translation.translation_languages import translation_lang
|
||||||
|
from models.transcription.transcription_languages import transcription_lang
|
||||||
|
from models.translation.translation_utils import checkCTranslate2Weight, downloadCTranslate2Weight, downloadCTranslate2Tokenizer
|
||||||
|
from models.transcription.transcription_whisper import checkWhisperWeight, downloadWhisperWeight
|
||||||
|
from models.overlay.overlay import Overlay
|
||||||
|
from models.overlay.overlay_image import OverlayImage
|
||||||
|
from models.watchdog.watchdog import Watchdog
|
||||||
|
from models.websocket.websocket_server import WebSocketServer
|
||||||
|
from utils import errorLogging, setupLogger
|
||||||
|
|
||||||
|
class threadFnc(Thread):
|
||||||
|
def __init__(self, fnc, end_fnc=None, daemon=True, *args, **kwargs):
|
||||||
|
super(threadFnc, self).__init__(daemon=daemon, target=fnc, *args, **kwargs)
|
||||||
|
self.fnc = fnc
|
||||||
|
self.end_fnc = end_fnc
|
||||||
|
self.loop = True
|
||||||
|
self._pause = False
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.loop = False
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
self._pause = True
|
||||||
|
|
||||||
|
def resume(self):
|
||||||
|
self._pause = False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self.loop:
|
||||||
|
self.fnc(*self._args, **self._kwargs)
|
||||||
|
while self._pause:
|
||||||
|
sleep(0.1)
|
||||||
|
|
||||||
|
if callable(self.end_fnc):
|
||||||
|
self.end_fnc()
|
||||||
|
return
|
||||||
|
|
||||||
|
class Model:
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = super(Model, cls).__new__(cls)
|
||||||
|
cls._instance.init()
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.logger = None
|
||||||
|
self.th_check_device = None
|
||||||
|
self.mic_print_transcript = None
|
||||||
|
self.mic_audio_recorder = None
|
||||||
|
self.mic_transcriber = None
|
||||||
|
self.mic_energy_recorder = None
|
||||||
|
self.mic_energy_plot_progressbar = None
|
||||||
|
self.speaker_print_transcript = None
|
||||||
|
self.speaker_audio_recorder = None
|
||||||
|
self.speaker_transcriber = None
|
||||||
|
self.speaker_energy_recorder = None
|
||||||
|
self.speaker_energy_plot_progressbar = None
|
||||||
|
|
||||||
|
self.previous_send_message = ""
|
||||||
|
self.previous_receive_message = ""
|
||||||
|
self.translator = Translator()
|
||||||
|
self.keyword_processor = KeywordProcessor()
|
||||||
|
overlay_small_log_settings = copy.deepcopy(config.OVERLAY_SMALL_LOG_SETTINGS)
|
||||||
|
overlay_large_log_settings = copy.deepcopy(config.OVERLAY_LARGE_LOG_SETTINGS)
|
||||||
|
overlay_large_log_settings["ui_scaling"] = overlay_large_log_settings["ui_scaling"] * 0.25
|
||||||
|
overlay_settings = {
|
||||||
|
"small": overlay_small_log_settings,
|
||||||
|
"large": overlay_large_log_settings,
|
||||||
|
}
|
||||||
|
self.overlay = Overlay(overlay_settings)
|
||||||
|
self.overlay_image = OverlayImage()
|
||||||
|
self.mic_audio_queue = None
|
||||||
|
self.mic_mute_status = None
|
||||||
|
self.kks = kakasi()
|
||||||
|
self.watchdog = Watchdog(config.WATCHDOG_TIMEOUT, config.WATCHDOG_INTERVAL)
|
||||||
|
self.osc_handler = OSCHandler(config.OSC_IP_ADDRESS, config.OSC_PORT)
|
||||||
|
self.websocket_server = WebSocketServer(host="0.0.0.0", port=8765)
|
||||||
102
src-python/models/websocket/websocket_server.py
Normal file
102
src-python/models/websocket/websocket_server.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from typing import Dict, Set, Optional
|
||||||
|
import websockets
|
||||||
|
from websockets.server import WebSocketServerProtocol
|
||||||
|
|
||||||
|
class WebSocketServer:
|
||||||
|
def __init__(self, host: str = "0.0.0.0", port: int = 8765):
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.clients: Set[WebSocketServerProtocol] = set()
|
||||||
|
self.server = None
|
||||||
|
self.is_running = False
|
||||||
|
self.logger = logging.getLogger('websocket_server')
|
||||||
|
|
||||||
|
async def register(self, websocket: WebSocketServerProtocol):
|
||||||
|
"""クライアント接続を登録する"""
|
||||||
|
self.clients.add(websocket)
|
||||||
|
self.logger.info(f"クライアント接続: {websocket.remote_address}, 現在の接続数: {len(self.clients)}")
|
||||||
|
|
||||||
|
async def unregister(self, websocket: WebSocketServerProtocol):
|
||||||
|
"""クライアント接続を解除する"""
|
||||||
|
self.clients.remove(websocket)
|
||||||
|
self.logger.info(f"クライアント切断: {websocket.remote_address}, 現在の接続数: {len(self.clients)}")
|
||||||
|
|
||||||
|
async def handler(self, websocket: WebSocketServerProtocol, path: str):
|
||||||
|
"""WebSocket接続ハンドラー"""
|
||||||
|
await self.register(websocket)
|
||||||
|
try:
|
||||||
|
async for message in websocket:
|
||||||
|
# クライアントからのメッセージを処理(必要に応じて)
|
||||||
|
# 現在はクライアントからのメッセージは特に処理していません
|
||||||
|
self.logger.debug(f"クライアントからのメッセージ: {message}")
|
||||||
|
except websockets.exceptions.ConnectionClosed:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
await self.unregister(websocket)
|
||||||
|
|
||||||
|
async def broadcast(self, message: Dict):
|
||||||
|
"""全クライアントにメッセージをブロードキャストする"""
|
||||||
|
if not self.clients:
|
||||||
|
return
|
||||||
|
|
||||||
|
json_message = json.dumps(message)
|
||||||
|
tasks = [client.send(json_message) for client in self.clients]
|
||||||
|
await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
def send_transcription(self, transcription_data: Dict):
|
||||||
|
"""文字起こし結果を送信する関数(通常のコードから呼び出し可能)"""
|
||||||
|
if not self.is_running or not self.clients:
|
||||||
|
return
|
||||||
|
|
||||||
|
# asyncioのイベントループがあれば利用し、なければ新たに作成
|
||||||
|
loop = asyncio.get_event_loop() if asyncio.get_event_loop().is_running() else asyncio.new_event_loop()
|
||||||
|
|
||||||
|
# ブロードキャスト関数を実行
|
||||||
|
asyncio.run_coroutine_threadsafe(
|
||||||
|
self.broadcast(transcription_data),
|
||||||
|
loop
|
||||||
|
)
|
||||||
|
|
||||||
|
async def start_server(self):
|
||||||
|
"""WebSocketサーバーを起動する"""
|
||||||
|
if not self.is_running:
|
||||||
|
self.server = await websockets.serve(self.handler, self.host, self.port)
|
||||||
|
self.is_running = True
|
||||||
|
self.logger.info(f"WebSocketサーバーを起動しました - {self.host}:{self.port}")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""非同期ループでWebSocketサーバーを起動する(通常のコードから呼び出し可能)"""
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
loop.run_until_complete(self.start_server())
|
||||||
|
# バックグラウンドでループを実行
|
||||||
|
self._task = asyncio.run_coroutine_threadsafe(self._run_forever(), loop)
|
||||||
|
|
||||||
|
async def _run_forever(self):
|
||||||
|
"""サーバーを永続的に実行する"""
|
||||||
|
while self.is_running:
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
async def stop_server(self):
|
||||||
|
"""WebSocketサーバーを停止する"""
|
||||||
|
if self.is_running and self.server:
|
||||||
|
self.server.close()
|
||||||
|
await self.server.wait_closed()
|
||||||
|
self.is_running = False
|
||||||
|
self.clients.clear()
|
||||||
|
self.logger.info("WebSocketサーバーを停止しました")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""WebSocketサーバーを停止する(通常のコードから呼び出し可能)"""
|
||||||
|
if not self.is_running:
|
||||||
|
return
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
asyncio.run_coroutine_threadsafe(self.stop_server(), loop)
|
||||||
Reference in New Issue
Block a user