GameSentenceMiner 2.14.8__py3-none-any.whl → 2.14.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. GameSentenceMiner/config_gui.py +19 -10
  2. GameSentenceMiner/gsm.py +68 -8
  3. GameSentenceMiner/locales/en_us.json +4 -0
  4. GameSentenceMiner/locales/ja_jp.json +4 -0
  5. GameSentenceMiner/locales/zh_cn.json +4 -0
  6. GameSentenceMiner/obs.py +12 -8
  7. {gamesentenceminer-2.14.8.dist-info → gamesentenceminer-2.14.9.dist-info}/METADATA +1 -2
  8. gamesentenceminer-2.14.9.dist-info/RECORD +24 -0
  9. GameSentenceMiner/ai/__init__.py +0 -0
  10. GameSentenceMiner/ai/ai_prompting.py +0 -473
  11. GameSentenceMiner/ocr/__init__.py +0 -0
  12. GameSentenceMiner/ocr/gsm_ocr_config.py +0 -174
  13. GameSentenceMiner/ocr/ocrconfig.py +0 -129
  14. GameSentenceMiner/ocr/owocr_area_selector.py +0 -629
  15. GameSentenceMiner/ocr/owocr_helper.py +0 -638
  16. GameSentenceMiner/ocr/ss_picker.py +0 -140
  17. GameSentenceMiner/owocr/owocr/__init__.py +0 -1
  18. GameSentenceMiner/owocr/owocr/__main__.py +0 -9
  19. GameSentenceMiner/owocr/owocr/config.py +0 -148
  20. GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -1238
  21. GameSentenceMiner/owocr/owocr/ocr.py +0 -1690
  22. GameSentenceMiner/owocr/owocr/run.py +0 -1817
  23. GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +0 -109
  24. GameSentenceMiner/tools/__init__.py +0 -0
  25. GameSentenceMiner/tools/audio_offset_selector.py +0 -215
  26. GameSentenceMiner/tools/ss_selector.py +0 -135
  27. GameSentenceMiner/tools/window_transparency.py +0 -214
  28. GameSentenceMiner/util/__init__.py +0 -0
  29. GameSentenceMiner/util/communication/__init__.py +0 -22
  30. GameSentenceMiner/util/communication/send.py +0 -7
  31. GameSentenceMiner/util/communication/websocket.py +0 -94
  32. GameSentenceMiner/util/configuration.py +0 -1198
  33. GameSentenceMiner/util/db.py +0 -408
  34. GameSentenceMiner/util/downloader/Untitled_json.py +0 -472
  35. GameSentenceMiner/util/downloader/__init__.py +0 -0
  36. GameSentenceMiner/util/downloader/download_tools.py +0 -194
  37. GameSentenceMiner/util/downloader/oneocr_dl.py +0 -250
  38. GameSentenceMiner/util/electron_config.py +0 -259
  39. GameSentenceMiner/util/ffmpeg.py +0 -571
  40. GameSentenceMiner/util/get_overlay_coords.py +0 -366
  41. GameSentenceMiner/util/gsm_utils.py +0 -323
  42. GameSentenceMiner/util/model.py +0 -206
  43. GameSentenceMiner/util/notification.py +0 -147
  44. GameSentenceMiner/util/text_log.py +0 -214
  45. GameSentenceMiner/web/__init__.py +0 -0
  46. GameSentenceMiner/web/service.py +0 -132
  47. GameSentenceMiner/web/static/__init__.py +0 -0
  48. GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
  49. GameSentenceMiner/web/static/favicon-96x96.png +0 -0
  50. GameSentenceMiner/web/static/favicon.ico +0 -0
  51. GameSentenceMiner/web/static/favicon.svg +0 -3
  52. GameSentenceMiner/web/static/site.webmanifest +0 -21
  53. GameSentenceMiner/web/static/style.css +0 -292
  54. GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
  55. GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
  56. GameSentenceMiner/web/templates/__init__.py +0 -0
  57. GameSentenceMiner/web/templates/index.html +0 -50
  58. GameSentenceMiner/web/templates/text_replacements.html +0 -238
  59. GameSentenceMiner/web/templates/utility.html +0 -483
  60. GameSentenceMiner/web/texthooking_page.py +0 -584
  61. GameSentenceMiner/wip/__init___.py +0 -0
  62. gamesentenceminer-2.14.8.dist-info/RECORD +0 -77
  63. {gamesentenceminer-2.14.8.dist-info → gamesentenceminer-2.14.9.dist-info}/WHEEL +0 -0
  64. {gamesentenceminer-2.14.8.dist-info → gamesentenceminer-2.14.9.dist-info}/entry_points.txt +0 -0
  65. {gamesentenceminer-2.14.8.dist-info → gamesentenceminer-2.14.9.dist-info}/licenses/LICENSE +0 -0
  66. {gamesentenceminer-2.14.8.dist-info → gamesentenceminer-2.14.9.dist-info}/top_level.txt +0 -0
@@ -1,584 +0,0 @@
1
- import asyncio
2
- from ctypes.util import test
3
- import datetime
4
- import json
5
- import os
6
- import queue
7
- import sqlite3
8
- import threading
9
- from dataclasses import dataclass
10
-
11
- import flask
12
- import websockets
13
-
14
- from GameSentenceMiner.ai.ai_prompting import get_ai_prompt_result
15
- from GameSentenceMiner.obs import get_current_game
16
- from GameSentenceMiner.util.gsm_utils import TEXT_REPLACEMENTS_FILE
17
- from GameSentenceMiner.util.text_log import GameLine, get_line_by_id, initial_time, get_all_lines
18
- from flask import request, jsonify, send_from_directory
19
- import webbrowser
20
- from GameSentenceMiner import obs
21
- from GameSentenceMiner.util.configuration import logger, get_config, DB_PATH, gsm_state, gsm_status
22
- from GameSentenceMiner.web.service import handle_texthooker_button
23
-
24
- port = get_config().general.texthooker_port
25
- url = f"http://localhost:{port}"
26
- websocket_port = 55001
27
-
28
-
29
- @dataclass
30
- class EventItem:
31
- line: 'GameLine'
32
- id: str
33
- text: str
34
- time: datetime.datetime
35
- checked: bool = False
36
- history: bool = False
37
-
38
- def to_dict(self):
39
- return {
40
- 'id': self.id,
41
- 'text': self.text,
42
- 'time': self.time,
43
- 'checked': self.checked,
44
- 'history': self.history,
45
- }
46
-
47
- def to_serializable(self):
48
- return {
49
- 'id': self.id,
50
- 'text': self.text,
51
- 'time': self.time.isoformat(),
52
- 'checked': self.checked,
53
- 'history': self.history,
54
- }
55
-
56
-
57
- class EventManager:
58
- events: list[EventItem]
59
- events_dict: dict[str, EventItem] = {}
60
-
61
- def __init__(self):
62
- self.events = []
63
- self.ids = []
64
- self.events_dict = {}
65
- self._connect()
66
- self._create_table()
67
- self._load_events_from_db()
68
- # self.close_connection()
69
-
70
- def _connect(self):
71
- self.conn = sqlite3.connect(DB_PATH)
72
- self.cursor = self.conn.cursor()
73
-
74
- def _create_table(self):
75
- self.cursor.execute("""
76
- CREATE TABLE IF NOT EXISTS events (
77
- event_id TEXT PRIMARY KEY,
78
- line_id TEXT,
79
- text TEXT,
80
- time TEXT
81
- )
82
- """)
83
- self.conn.commit()
84
-
85
- def _load_events_from_db(self):
86
- self.cursor.execute("SELECT * FROM events")
87
- rows = self.cursor.fetchall()
88
- for row in rows:
89
- event_id, line_id, text, timestamp = row
90
- timestamp = datetime.datetime.fromisoformat(timestamp)
91
- line = GameLine(line_id, text, timestamp, None, None, 0)
92
- event = EventItem(line, event_id, text, timestamp,
93
- False, timestamp < initial_time)
94
- self.events.append(event)
95
- self.ids.append(event_id)
96
- self.events_dict[event_id] = event
97
-
98
- def __iter__(self):
99
- return iter(self.events)
100
-
101
- def replace_events(self, new_events: list[EventItem]):
102
- self.events = new_events
103
-
104
- def add_gameline(self, line: GameLine):
105
- new_event = EventItem(line, line.id, line.text,
106
- line.time, False, False)
107
- self.events_dict[line.id] = new_event
108
- self.ids.append(line.id)
109
- self.events.append(new_event)
110
- # self.store_to_db(new_event)
111
- # event_queue.put(new_event)
112
- return new_event
113
-
114
- def reset_checked_lines(self):
115
- for event in self.events:
116
- event.checked = False
117
-
118
- def get_events(self):
119
- return self.events
120
-
121
- def add_event(self, event):
122
- self.events.append(event)
123
- self.ids.append(event.id)
124
- event_queue.put(event)
125
-
126
- def get(self, event_id):
127
- return self.events_dict.get(event_id)
128
-
129
- def get_ids(self):
130
- return self.ids
131
-
132
- def close_connection(self):
133
- if self.conn:
134
- self.conn.close()
135
-
136
- def clear_history(self):
137
- self.cursor.execute("DELETE FROM events WHERE time < ?",
138
- (initial_time.isoformat(),))
139
- logger.info(f"Cleared history before {initial_time.isoformat()}")
140
- self.conn.commit()
141
- # Clear the in-memory events as well
142
- event_manager.events = [
143
- event for event in event_manager if not event.history]
144
- event_manager.events_dict = {
145
- event.id: event for event in event_manager.events}
146
-
147
-
148
- class EventProcessor(threading.Thread):
149
- def __init__(self, event_queue, db_path):
150
- super().__init__()
151
- self.event_queue = event_queue
152
- self.db_path = db_path
153
- self.conn = None
154
- self.cursor = None
155
- self.daemon = True
156
-
157
- def _connect(self):
158
- self.conn = sqlite3.connect(self.db_path)
159
- self.cursor = self.conn.cursor()
160
-
161
- def run(self):
162
- self._connect()
163
- while True:
164
- try:
165
- event = self.event_queue.get()
166
- if event is None: # Exit signal
167
- break
168
- self._store_to_db(event)
169
- except Exception as e:
170
- logger.error(f"Error processing event: {e}")
171
- self._close_connection()
172
-
173
- def _store_to_db(self, event):
174
- self.cursor.execute("""
175
- INSERT INTO events (event_id, line_id, text, time)
176
- VALUES (?, ?, ?, ?)
177
- """, (event.id, event.line.id, event.text, event.time.isoformat()))
178
- self.conn.commit()
179
-
180
- def _close_connection(self):
181
- if self.conn:
182
- self.conn.close()
183
-
184
-
185
- event_manager = EventManager()
186
- event_queue = queue.Queue()
187
-
188
- # Initialize the EventProcessor with the queue and event manager
189
- event_processor = EventProcessor(event_queue, DB_PATH)
190
- event_processor.start()
191
-
192
- server_start_time = datetime.datetime.now().timestamp()
193
-
194
- app = flask.Flask(__name__)
195
-
196
- # Load data from the JSON file
197
-
198
-
199
- def load_data_from_file():
200
- if os.path.exists(TEXT_REPLACEMENTS_FILE):
201
- with open(TEXT_REPLACEMENTS_FILE, 'r', encoding='utf-8') as file:
202
- return json.load(file)
203
- return {"enabled": True, "args": {"replacements": {}}}
204
-
205
- # Save data to the JSON file
206
-
207
-
208
- def save_data_to_file(data):
209
- with open(TEXT_REPLACEMENTS_FILE, 'w', encoding='utf-8') as file:
210
- json.dump(data, file, indent=4, ensure_ascii=False)
211
-
212
-
213
- @app.route('/load-data', methods=['GET'])
214
- def load_data():
215
- try:
216
- data = load_data_from_file()
217
- return jsonify(data), 200
218
- except Exception as e:
219
- return jsonify({"error": f"Failed to load data: {str(e)}"}), 500
220
-
221
-
222
- @app.route('/save-data', methods=['POST'])
223
- def save_data():
224
- try:
225
- data = request.get_json()
226
- if not isinstance(data, dict):
227
- return jsonify({"error": "Invalid data format"}), 400
228
-
229
- # Save updated data
230
- save_data_to_file(data)
231
- return jsonify({"message": "Data saved successfully"}), 200
232
- except Exception as e:
233
- return jsonify({"error": f"Failed to save data: {str(e)}"}), 500
234
-
235
-
236
- def inject_server_start_time(html_content, timestamp):
237
- placeholder = '<script>'
238
- replacement = f'<script>const serverStartTime = {timestamp};'
239
- return html_content.replace(placeholder, replacement)
240
-
241
-
242
- @app.route('/favicon.ico')
243
- def favicon():
244
- return send_from_directory(os.path.join(app.root_path, 'static'),
245
- 'favicon.ico', mimetype='image/vnd.microsoft.icon')
246
-
247
-
248
- @app.route('/<path:filename>')
249
- def serve_static(filename):
250
- return send_from_directory('pages', filename)
251
-
252
-
253
- @app.route('/')
254
- def index():
255
- return send_from_directory('templates', 'index.html')
256
-
257
-
258
- @app.route('/texthooker')
259
- def texthooker():
260
- return send_from_directory('templates', 'index.html')
261
-
262
-
263
- @app.route('/textreplacements')
264
- def textreplacements():
265
- return flask.render_template('text_replacements.html')
266
-
267
-
268
- @app.route('/data', methods=['GET'])
269
- def get_data():
270
- return jsonify([event.to_dict() for event in event_manager])
271
-
272
-
273
- @app.route('/get_ids', methods=['GET'])
274
- def get_ids():
275
- return jsonify(event_manager.get_ids())
276
-
277
-
278
- @app.route('/clear_history', methods=['POST'])
279
- def clear_history():
280
- temp_em = EventManager()
281
- temp_em.clear_history()
282
- temp_em.close_connection()
283
- return jsonify({'message': 'History cleared successfully'}), 200
284
-
285
-
286
- async def add_event_to_texthooker(line: GameLine):
287
- new_event = event_manager.add_gameline(line)
288
- await websocket_server_thread.send_text({
289
- 'event': 'text_received',
290
- 'sentence': line.text,
291
- 'data': new_event.to_serializable()
292
- })
293
- if get_config().advanced.plaintext_websocket_port:
294
- await plaintext_websocket_server_thread.send_text(line.text)
295
-
296
-
297
- async def send_word_coordinates_to_overlay(boxes):
298
- if boxes and len(boxes) > 0 and overlay_server_thread:
299
- await overlay_server_thread.send_text(boxes)
300
-
301
-
302
- @app.route('/update_checkbox', methods=['POST'])
303
- def update_event():
304
- data = request.get_json()
305
- event_id = data.get('id')
306
-
307
- if event_id is None:
308
- return jsonify({'error': 'Missing id'}), 400
309
- event = event_manager.get(event_id)
310
- event_manager.get(event_id).checked = not event.checked
311
- return jsonify({'message': 'Event updated successfully'}), 200
312
-
313
-
314
- @app.route('/get-screenshot', methods=['Post'])
315
- def get_screenshot():
316
- """Endpoint to get a screenshot of the current game screen."""
317
- data = request.get_json()
318
- event_id = data.get('id')
319
- if event_id is None:
320
- return jsonify({'error': 'Missing id'}), 400
321
- gsm_state.line_for_screenshot = get_line_by_id(event_id)
322
- if gsm_state.previous_line_for_screenshot and gsm_state.line_for_screenshot == gsm_state.previous_line_for_screenshot or gsm_state.previous_line_for_audio and gsm_state.line_for_screenshot == gsm_state.previous_line_for_audio:
323
- handle_texthooker_button(gsm_state.previous_replay)
324
- else:
325
- obs.save_replay_buffer()
326
- return jsonify({}), 200
327
-
328
-
329
- @app.route('/play-audio', methods=['POST'])
330
- def play_audio():
331
- """Endpoint to play audio for a specific event."""
332
- data = request.get_json()
333
- event_id = data.get('id')
334
- if event_id is None:
335
- return jsonify({'error': 'Missing id'}), 400
336
- print(f"Playing audio for event ID: {event_id}")
337
- gsm_state.line_for_audio = get_line_by_id(event_id)
338
- print(f"gsm_state.line_for_audio: {gsm_state.line_for_audio}")
339
- if gsm_state.previous_line_for_audio and gsm_state.line_for_audio == gsm_state.previous_line_for_audio or gsm_state.previous_line_for_screenshot and gsm_state.line_for_audio == gsm_state.previous_line_for_screenshot:
340
- handle_texthooker_button(gsm_state.previous_replay)
341
- else:
342
- obs.save_replay_buffer()
343
- return jsonify({}), 200
344
-
345
-
346
- @app.route("/translate-line", methods=['POST'])
347
- def translate_line():
348
- data = request.get_json()
349
- event_id = data.get('id')
350
- if event_id is None:
351
- return jsonify({'error': 'Missing id'}), 400
352
- line_to_translate = get_line_by_id(event_id)
353
- translation = get_ai_prompt_result(get_all_lines(), line_to_translate.text,
354
- line_to_translate, get_current_game())
355
- line_to_translate.set_TL(translation)
356
- return jsonify({'TL': translation}), 200
357
-
358
- @app.route('/translate-multiple', methods=['POST'])
359
- def translate_multiple():
360
- data = request.get_json()
361
- event_ids = data.get('ids', [])
362
- if not event_ids:
363
- return jsonify({'error': 'Missing ids'}), 400
364
-
365
- lines = [get_line_by_id(event_id) for event_id in event_ids if get_line_by_id(event_id) is not None]
366
-
367
- text = "\n".join(line.text for line in lines)
368
-
369
- translate_multiple_lines_prompt = f"""
370
- **Professional Game Localization Task**
371
- Translate the following lines of game dialogue into natural-sounding, context-aware {get_config().general.get_native_language_name()}:
372
-
373
- **Output Requirements**
374
- - Maintain the original tone and style of the dialogue.
375
- - Ensure that the translation is contextually appropriate for the game.
376
- - Pay attention to character names and any specific terminology used in the game.
377
- - Maintain Formatting and newline structure of the given lines. It should be very human readable as a dialogue.
378
- - Do not include any notes, alternatives, explanations, or any other surrounding text. Absolutely nothing but the translated lines.
379
-
380
- **Lines to Translate:**
381
- """
382
-
383
- translation = get_ai_prompt_result(get_all_lines(), text,
384
- lines[0], get_current_game(), custom_prompt=translate_multiple_lines_prompt)
385
-
386
- return translation, 200
387
-
388
- @app.route('/get_status', methods=['GET'])
389
- def get_status():
390
- return jsonify(gsm_status.to_dict()), 200
391
-
392
-
393
- # async def main():
394
- # async with websockets.serve(websocket_handler, "localhost", 8765): # Choose a port for WebSocket
395
- # print("WebSocket server started on ws://localhost:8765/ws (adjust as needed)")
396
- # await asyncio.Future() # Keep the server running
397
-
398
- # @app.route('/store-events', methods=['POST'])
399
- # def store_events():
400
- # data = request.get_json()
401
- # events_data = data.get('events', [])
402
- #
403
- # if not isinstance(events_data, list):
404
- # return jsonify({'error': 'Invalid data format. Expected an array of events.'}), 400
405
- #
406
- # for event_data in events_data:
407
- # if not all(k in event_data for k in ('id', 'text', 'time', 'checked')):
408
- # return jsonify({'error': 'Invalid event structure. Missing keys.'}), 400
409
- # if not (isinstance(event_data['id'], (int, float)) and
410
- # isinstance(event_data['text'], str) and
411
- # isinstance(event_data['time'], str) and
412
- # isinstance(event_data['checked'], bool)):
413
- # return jsonify({'error': 'Invalid event structure. Incorrect data types.'}), 400
414
- #
415
- # event_manager.replace_events([EventItem(item['id'], item['text'], item['time'], item.get(['timestamp'], 0), item['checked']) for item in data])
416
- #
417
- # return jsonify({'message': 'Events successfully stored on server.', 'receivedEvents': data}), 200
418
-
419
-
420
- def get_selected_lines():
421
- return [item.line for item in event_manager if item.checked]
422
-
423
-
424
- def are_lines_selected():
425
- return any(item.checked for item in event_manager)
426
-
427
-
428
- def reset_checked_lines():
429
- async def send_reset_message():
430
- await websocket_server_thread.send_text({
431
- 'event': 'reset_checkboxes',
432
- })
433
- event_manager.reset_checked_lines()
434
- asyncio.run(send_reset_message())
435
-
436
-
437
- def open_texthooker():
438
- webbrowser.open(url + '/texthooker')
439
-
440
-
441
- def start_web_server():
442
- logger.debug("Starting web server...")
443
- import logging
444
- log = logging.getLogger('werkzeug')
445
- log.setLevel(logging.ERROR) # Set to ERROR to suppress most logs
446
-
447
- # Open the default browser
448
- if get_config().general.open_multimine_on_startup:
449
- open_texthooker()
450
-
451
- # debug=True provides helpful error messages during development
452
- app.run(host='0.0.0.0', port=port, debug=False)
453
-
454
-
455
- websocket_queue = queue.Queue()
456
- paused = False
457
-
458
-
459
- class WebsocketServerThread(threading.Thread):
460
- def __init__(self, read, get_ws_port_func):
461
- super().__init__(daemon=True)
462
- self._loop = None
463
- self.read = read
464
- self.clients = set()
465
- self._event = threading.Event()
466
- self.get_ws_port_func = get_ws_port_func
467
- self.backedup_text = []
468
-
469
- @property
470
- def loop(self):
471
- self._event.wait()
472
- return self._loop
473
-
474
- async def send_text_coroutine(self, message):
475
- if not self.clients:
476
- self.backedup_text.append(message)
477
- return
478
- for client in self.clients:
479
- await client.send(message)
480
-
481
- async def server_handler(self, websocket):
482
- self.clients.add(websocket)
483
- try:
484
- if self.backedup_text:
485
- for message in self.backedup_text:
486
- await websocket.send(message)
487
- self.backedup_text.clear()
488
- async for message in websocket:
489
- if self.read and not paused:
490
- websocket_queue.put(message)
491
- try:
492
- await websocket.send('True')
493
- except websockets.exceptions.ConnectionClosedOK:
494
- pass
495
- else:
496
- try:
497
- await websocket.send('False')
498
- except websockets.exceptions.ConnectionClosedOK:
499
- pass
500
- except websockets.exceptions.ConnectionClosedError:
501
- pass
502
- finally:
503
- self.clients.remove(websocket)
504
-
505
- async def send_text(self, text):
506
- if text:
507
- if isinstance(text, dict) or isinstance(text, list):
508
- text = json.dumps(text)
509
- return asyncio.run_coroutine_threadsafe(
510
- self.send_text_coroutine(text), self.loop)
511
-
512
- def has_clients(self):
513
- return len(self.clients) > 0
514
-
515
- def stop_server(self):
516
- self.loop.call_soon_threadsafe(self._stop_event.set)
517
-
518
- def run(self):
519
- async def main():
520
- self._loop = asyncio.get_running_loop()
521
- self._stop_event = stop_event = asyncio.Event()
522
- self._event.set()
523
- while True:
524
- try:
525
- self.server = start_server = websockets.serve(self.server_handler,
526
- "0.0.0.0",
527
- self.get_ws_port_func(),
528
- max_size=1000000000)
529
- async with start_server:
530
- await stop_event.wait()
531
- return
532
- except Exception as e:
533
- logger.warning(
534
- f"WebSocket server encountered an error: {e}. Retrying...")
535
- await asyncio.sleep(1)
536
-
537
- asyncio.run(main())
538
-
539
-
540
- def handle_exit_signal(loop):
541
- logger.info("Received exit signal. Shutting down...")
542
- for task in asyncio.all_tasks(loop):
543
- task.cancel()
544
-
545
-
546
- websocket_server_thread = WebsocketServerThread(read=True, get_ws_port_func=lambda: get_config(
547
- ).get_field_value('advanced', 'texthooker_communication_websocket_port'))
548
- websocket_server_thread.start()
549
-
550
- if get_config().advanced.plaintext_websocket_port:
551
- plaintext_websocket_server_thread = WebsocketServerThread(
552
- read=False, get_ws_port_func=lambda: get_config().get_field_value('advanced', 'plaintext_websocket_port'))
553
- plaintext_websocket_server_thread.start()
554
-
555
- overlay_server_thread = WebsocketServerThread(
556
- read=False, get_ws_port_func=lambda: get_config().get_field_value('overlay', 'websocket_port'))
557
- overlay_server_thread.start()
558
-
559
- websocket_server_threads = [
560
- websocket_server_thread,
561
- plaintext_websocket_server_thread,
562
- overlay_server_thread
563
- ]
564
-
565
-
566
- async def texthooker_page_coro():
567
- global websocket_server_thread, plaintext_websocket_server_thread, overlay_server_thread
568
- # Run the WebSocket server in the asyncio event loop
569
- flask_thread = threading.Thread(target=start_web_server)
570
- flask_thread.daemon = True
571
- flask_thread.start()
572
-
573
- # Keep the main asyncio event loop running (for the WebSocket server)
574
-
575
-
576
- def run_text_hooker_page():
577
- try:
578
- asyncio.run(texthooker_page_coro())
579
- except KeyboardInterrupt:
580
- logger.info("Shutting down due to KeyboardInterrupt.")
581
-
582
-
583
- if __name__ == '__main__':
584
- asyncio.run(texthooker_page_coro())
File without changes
@@ -1,77 +0,0 @@
1
- GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- GameSentenceMiner/anki.py,sha256=xTXCACDM_u9natCFnbqdxEX24F9bTsdoI7PoskWbdkk,23012
3
- GameSentenceMiner/config_gui.py,sha256=dTYBe3ZI9tsOiOtT2WEMnodj3q5-U4Kj5l1l1qutlsw,137549
4
- GameSentenceMiner/gametext.py,sha256=fgBgLchezpauWELE9Y5G3kVCLfAneD0X4lJFoI3FYbs,10351
5
- GameSentenceMiner/gsm.py,sha256=GdqegZnKrTMVRvp43bK7oNlWj5OxLx2PNdVWyHL9Gc4,28282
6
- GameSentenceMiner/obs.py,sha256=JmtO7Ojcm14Q8P_bz8TdS24rwDdwQasg2gnO2J0KcuQ,20598
7
- GameSentenceMiner/vad.py,sha256=zFReBMvNEEaQ_YEozCTCaMdV-o40FwtlxYRb17cYZio,19125
8
- GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- GameSentenceMiner/ai/ai_prompting.py,sha256=D1KyHER_sj4jYvOKGRbp8FkKZSKWzAaXHbpmZLLFctg,23998
10
- GameSentenceMiner/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- GameSentenceMiner/assets/icon.png,sha256=9GRL8uXUAgkUSlvbm9Pv9o2poFVRGdW6s2ub_DeUD9M,937624
12
- GameSentenceMiner/assets/icon128.png,sha256=l90j7biwdz5ahwOd5wZ-406ryEV9Pan93dquJQ3e1CI,18395
13
- GameSentenceMiner/assets/icon256.png,sha256=JEW46wOrG1KR-907rvFaEdNbPtj5gu0HJmG7qUnIHxQ,51874
14
- GameSentenceMiner/assets/icon32.png,sha256=Kww0hU_qke9_22wBuO_Nq0Dv2SfnOLwMhCyGgbgXdg8,6089
15
- GameSentenceMiner/assets/icon512.png,sha256=HxUj2GHjyQsk8NV433256UxU9phPhtjCY-YB_7W4sqs,192487
16
- GameSentenceMiner/assets/icon64.png,sha256=N8xgdZXvhqVQP9QUK3wX5iqxX9LxHljD7c-Bmgim6tM,9301
17
- GameSentenceMiner/assets/pickaxe.png,sha256=VfIGyXyIZdzEnVcc4PmG3wszPMO1W4KCT7Q_nFK6eSE,1403829
18
- GameSentenceMiner/locales/en_us.json,sha256=B3CF3VvJ1PVLP_2Xv34ZFNUVAF8SQ8qDlOD20EOOJmE,25948
19
- GameSentenceMiner/locales/ja_jp.json,sha256=2I-aSs6KIbjJ7uNRA-dx_5oRP9JLTWWUIrMMZOkenlE,27520
20
- GameSentenceMiner/locales/zh_cn.json,sha256=tx9Szm6KEMn_YaYDWS1ph4pGALYR3iguVru2Dfj7S3o,24047
21
- GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=QyBarjb6Wjt-oyor1sIT9hG-KrGdi6wODLyCrHXun6E,6008
23
- GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
24
- GameSentenceMiner/ocr/owocr_area_selector.py,sha256=Rm1_nuZotJhfOfoJ_3mesh9udtOBjYqKhnAvSief6fo,29181
25
- GameSentenceMiner/ocr/owocr_helper.py,sha256=6R6IbdwpjWmIrXnUvyQ_0kXRYhJe2tphqyapoxc_PsQ,28857
26
- GameSentenceMiner/ocr/ss_picker.py,sha256=0IhxUdaKruFpZyBL-8SpxWg7bPrlGpy3lhTcMMZ5rwo,5224
27
- GameSentenceMiner/owocr/owocr/__init__.py,sha256=87hfN5u_PbL_onLfMACbc0F5j4KyIK9lKnRCj6oZgR0,49
28
- GameSentenceMiner/owocr/owocr/__main__.py,sha256=XQaqZY99EKoCpU-gWQjNbTs7Kg17HvBVE7JY8LqIE0o,157
29
- GameSentenceMiner/owocr/owocr/config.py,sha256=qM7kISHdUhuygGXOxmgU6Ef2nwBShrZtdqu4InDCViE,8103
30
- GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
31
- GameSentenceMiner/owocr/owocr/ocr.py,sha256=xU1PkkDkOZ36phzmaXdOrfw08zy0YkSNbKTgIDAqBYs,70556
32
- GameSentenceMiner/owocr/owocr/run.py,sha256=-aRSIcwc-qIn2s-JVaAMGvzJHjvqw4LsLQKLtswEfcE,75570
33
- GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
34
- GameSentenceMiner/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- GameSentenceMiner/tools/audio_offset_selector.py,sha256=8Stk3BP-XVIuzRv9nl9Eqd2D-1yD3JrgU-CamBywJmY,8542
36
- GameSentenceMiner/tools/ss_selector.py,sha256=cbjMxiKOCuOfbRvLR_PCRlykBrGtm1LXd6u5czPqkmc,4793
37
- GameSentenceMiner/tools/window_transparency.py,sha256=GtbxbmZg0-UYPXhfHff-7IKZyY2DKe4B9GdyovfmpeM,8166
38
- GameSentenceMiner/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- GameSentenceMiner/util/configuration.py,sha256=NnKcMyFV4y6gB6AwFUP-B-tdfk0_Awh5bJ6FHtXr6Vk,40017
40
- GameSentenceMiner/util/db.py,sha256=2bO0rD4i8A1hhsRBER-wgZy9IK17ibRbI8DHxdKvYsI,16598
41
- GameSentenceMiner/util/electron_config.py,sha256=KfeJToeFFVw0IR5MKa-gBzpzaGrU-lyJbR9z-sDEHYU,8767
42
- GameSentenceMiner/util/ffmpeg.py,sha256=iqsdp3TbBv6KMACJxkUF3e5VWak3jHPZdIEMrUdKFtE,23073
43
- GameSentenceMiner/util/get_overlay_coords.py,sha256=VtrT25DOjqMgo-eRFRMovuq6LUnoY8GrpbkfDsvgSjM,13855
44
- GameSentenceMiner/util/gsm_utils.py,sha256=Piwv88Q9av2LBeN7M6QDi0Mp0_R2lNbkcI6ekK5hd2o,11851
45
- GameSentenceMiner/util/model.py,sha256=R-_RYTYLSDNgBoVTPuPBcIHeOznIqi_vBzQ7VQ20WYk,6727
46
- GameSentenceMiner/util/notification.py,sha256=-qk3kTKEERzmMxx5XMh084HCyFmbfqz0XjY1hTKhCeQ,4202
47
- GameSentenceMiner/util/text_log.py,sha256=zB9--7J_Wwck74IOEI4aWhmCYouqjSE6Sm0sCznF63Q,6731
48
- GameSentenceMiner/util/communication/__init__.py,sha256=xh__yn2MhzXi9eLi89PeZWlJPn-cbBSjskhi1BRraXg,643
49
- GameSentenceMiner/util/communication/send.py,sha256=Wki9qIY2CgYnuHbmnyKVIYkcKAN_oYS4up93XMikBaI,222
50
- GameSentenceMiner/util/communication/websocket.py,sha256=TbphRGmxVrgEupS7tNdifsmQfWDfIp0Hio2cSiUKgsk,3317
51
- GameSentenceMiner/util/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5FngZ7ILdA_J5TFYAXUQ,15272
52
- GameSentenceMiner/util/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- GameSentenceMiner/util/downloader/download_tools.py,sha256=zR-aEHiFVkyo-9oPoSx6nQ2K-_J8WBHLZyLoOhypsW4,8458
54
- GameSentenceMiner/util/downloader/oneocr_dl.py,sha256=l3s9Z-x1b57GX048o5h-MVv0UTZo4H-Q-zb-JREkMLI,10439
55
- GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
- GameSentenceMiner/web/service.py,sha256=YZchmScTn7AX_GkwV1ULEK6qjdOnJcpc3qfMwDf7cUE,5363
57
- GameSentenceMiner/web/texthooking_page.py,sha256=JhuApKUeQGwqGUO_92Z6yMY0_-kqHQceT4FGzhbGlGE,19269
58
- GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
60
- GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
61
- GameSentenceMiner/web/static/favicon.ico,sha256=7d25r_FBqRSNsAoEHpSzNoT7zyVt2DJRLNDNq_HYoX8,15086
62
- GameSentenceMiner/web/static/favicon.svg,sha256=x305AP6WlXGtrXIZlaQspdLmwteoFYUoe5FyJ9MYlJ8,11517
63
- GameSentenceMiner/web/static/site.webmanifest,sha256=kaeNT-FjFt-T7JGzOhXH7YSqsrDeiplZ2kDxCN_CFU4,436
64
- GameSentenceMiner/web/static/style.css,sha256=bPZK0NVMuyRl5NNDuT7ZTzVLKlvSsdmeVHmAW4y5FM0,7001
65
- GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESbkGfYwbKzcjKOdzuWo18ABADfN974,51117
66
- GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
67
- GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- GameSentenceMiner/web/templates/index.html,sha256=VrhrkQKcOAPuoWRcilFLDXjvNf6WTOY8s_Cjj3vYdWY,216505
69
- GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
70
- GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
71
- GameSentenceMiner/wip/__init___.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
- gamesentenceminer-2.14.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
73
- gamesentenceminer-2.14.8.dist-info/METADATA,sha256=vvvPFxb_AjD9_1NkCTKCj5E9feTflY5TgINHIyEOd-0,7330
74
- gamesentenceminer-2.14.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
- gamesentenceminer-2.14.8.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
76
- gamesentenceminer-2.14.8.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
77
- gamesentenceminer-2.14.8.dist-info/RECORD,,