-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwebserver.py
More file actions
143 lines (122 loc) · 4.66 KB
/
webserver.py
File metadata and controls
143 lines (122 loc) · 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# webserver.py
from threading import Thread
from werkzeug.serving import make_server
from flask import Flask, Response, send_from_directory
import time
import os
import sys
# Определение базовой директории
if getattr(sys, 'frozen', False):
BASE_DIR = os.path.dirname(sys.executable)
else:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# Импортируем логирование из utils
from utils import setup_logging
logger = setup_logging('webserver')
# Отключение логирования Flask
import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
class WebServer:
def __init__(self, renderer, host="0.0.0.0", port=6969):
self.renderer = renderer
self.host = host
self.port = port
self._thread = None
self._server = None
self.is_running = False
if getattr(sys, 'frozen', False):
self.base_dir = os.path.dirname(sys.executable)
else:
self.base_dir = os.path.dirname(os.path.abspath(__file__))
self.app = Flask("WebPNGTuberStream")
self.app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
self.app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
@self.app.route("/stream")
def stream():
logger.info(f"Stream connection established on port {self.port}")
return Response(
self.mjpeg_generator(),
mimetype="multipart/x-mixed-replace; boundary=frame"
)
@self.app.route("/")
def index():
logger.info(f"Index page requested on port {self.port}")
return """<html>
<head>
<title>WebPNGTuber</title>
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<style>body { margin: 0; background: #000; }</style>
</head>
<body>
<img src="/stream" style="width:100vw; height:100vh; object-fit:contain;"/>
</body>
</html>"""
@self.app.route("/favicon.ico")
def favicon():
return send_from_directory(
self.base_dir,
'favicon.ico',
mimetype='image/vnd.microsoft.icon'
)
def mjpeg_generator(self):
"""Оптимизированный генератор MJPEG потока"""
last_frame = None
last_frame_hash = None
while self.is_running:
if not self.renderer or not self.renderer.model:
time.sleep(0.1)
continue
frame = self.renderer.get_frame_bytes()
if frame:
frame_hash = hash(frame)
if frame_hash == last_frame_hash and last_frame:
yield last_frame
else:
frame_data = (
b"--frame\r\n"
b"Content-Type: image/png\r\n"
b"Content-Length: " + str(len(frame)).encode() + b"\r\n\r\n" +
frame + b"\r\n"
)
last_frame = frame_data
last_frame_hash = frame_hash
yield frame_data
time.sleep(1.0 / self.renderer.fps)
def start(self):
"""Запуск веб-сервера"""
if self.is_running:
return
def run():
try:
self._server = make_server(
self.host,
self.port,
self.app,
threaded=True
)
self.is_running = True
logger.info(f"Web server starting on {self.host}:{self.port}")
self._server.serve_forever()
except Exception as e:
logger.error(f"Web server error on port {self.port}: {e}")
self.is_running = False
finally:
if self._server:
self._server.server_close()
logger.info(f"Web server on port {self.port} stopped completely")
self._thread = Thread(target=run, daemon=True)
self._thread.start()
time.sleep(0.5)
def stop(self):
"""Остановка веб-сервера"""
if not self.is_running or not self._server:
return
logger.info(f"Web server stop requested for port {self.port}")
self.is_running = False
try:
if self._server:
self._server.shutdown()
time.sleep(0.5)
except Exception as e:
logger.error(f"Error stopping server: {e}")