GameSentenceMiner 2.9.3__tar.gz → 2.9.4__tar.gz

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 (77) hide show
  1. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/anki.py +4 -0
  2. gamesentenceminer-2.9.4/GameSentenceMiner/assets/icon.png +0 -0
  3. gamesentenceminer-2.9.4/GameSentenceMiner/assets/icon128.png +0 -0
  4. gamesentenceminer-2.9.4/GameSentenceMiner/assets/icon256.png +0 -0
  5. gamesentenceminer-2.9.4/GameSentenceMiner/assets/icon32.png +0 -0
  6. gamesentenceminer-2.9.4/GameSentenceMiner/assets/icon512.png +0 -0
  7. gamesentenceminer-2.9.4/GameSentenceMiner/assets/icon64.png +0 -0
  8. gamesentenceminer-2.9.4/GameSentenceMiner/assets/pickaxe.png +0 -0
  9. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/notification.py +21 -0
  10. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/obs.py +0 -1
  11. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/package.py +0 -1
  12. gamesentenceminer-2.9.4/GameSentenceMiner/web/templates/__init__.py +0 -0
  13. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/texthooking_page.py +69 -51
  14. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner.egg-info/PKG-INFO +1 -1
  15. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner.egg-info/SOURCES.txt +8 -0
  16. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/PKG-INFO +1 -1
  17. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/pyproject.toml +2 -2
  18. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/__init__.py +0 -0
  19. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/ai/__init__.py +0 -0
  20. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/ai/ai_prompting.py +0 -0
  21. {gamesentenceminer-2.9.3/GameSentenceMiner/downloader → gamesentenceminer-2.9.4/GameSentenceMiner/assets}/__init__.py +0 -0
  22. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/communication/__init__.py +0 -0
  23. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/communication/send.py +0 -0
  24. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/communication/websocket.py +0 -0
  25. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/config_gui.py +0 -0
  26. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/configuration.py +0 -0
  27. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/downloader/Untitled_json.py +0 -0
  28. {gamesentenceminer-2.9.3/GameSentenceMiner/ocr → gamesentenceminer-2.9.4/GameSentenceMiner/downloader}/__init__.py +0 -0
  29. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/downloader/download_tools.py +0 -0
  30. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/downloader/oneocr_dl.py +0 -0
  31. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/electron_config.py +0 -0
  32. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/ffmpeg.py +0 -0
  33. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/gametext.py +0 -0
  34. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/gsm.py +0 -0
  35. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/model.py +0 -0
  36. {gamesentenceminer-2.9.3/GameSentenceMiner/vad → gamesentenceminer-2.9.4/GameSentenceMiner/ocr}/__init__.py +0 -0
  37. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/ocr/gsm_ocr_config.py +0 -0
  38. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/ocr/ocrconfig.py +0 -0
  39. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/ocr/owocr_area_selector.py +0 -0
  40. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/ocr/owocr_helper.py +0 -0
  41. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/owocr/owocr/__init__.py +0 -0
  42. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/owocr/owocr/__main__.py +0 -0
  43. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/owocr/owocr/config.py +0 -0
  44. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -0
  45. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/owocr/owocr/ocr.py +0 -0
  46. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/owocr/owocr/run.py +0 -0
  47. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +0 -0
  48. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/ss_selector.py +0 -0
  49. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/text_log.py +0 -0
  50. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/util.py +0 -0
  51. {gamesentenceminer-2.9.3/GameSentenceMiner/web → gamesentenceminer-2.9.4/GameSentenceMiner/vad}/__init__.py +0 -0
  52. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/vad/groq_trim.py +0 -0
  53. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/vad/result.py +0 -0
  54. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/vad/silero_trim.py +0 -0
  55. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/vad/vad_utils.py +0 -0
  56. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/vad/vosk_helper.py +0 -0
  57. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/vad/whisper_helper.py +0 -0
  58. {gamesentenceminer-2.9.3/GameSentenceMiner/web/static → gamesentenceminer-2.9.4/GameSentenceMiner/web}/__init__.py +0 -0
  59. {gamesentenceminer-2.9.3/GameSentenceMiner/web/templates → gamesentenceminer-2.9.4/GameSentenceMiner/web/static}/__init__.py +0 -0
  60. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
  61. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/static/favicon-96x96.png +0 -0
  62. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/static/favicon.ico +0 -0
  63. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/static/favicon.svg +0 -0
  64. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/static/site.webmanifest +0 -0
  65. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/static/style.css +0 -0
  66. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
  67. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
  68. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/templates/index.html +0 -0
  69. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/templates/text_replacements.html +0 -0
  70. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner/web/templates/utility.html +0 -0
  71. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner.egg-info/dependency_links.txt +0 -0
  72. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner.egg-info/entry_points.txt +0 -0
  73. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner.egg-info/requires.txt +0 -0
  74. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/GameSentenceMiner.egg-info/top_level.txt +0 -0
  75. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/LICENSE +0 -0
  76. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/README.md +0 -0
  77. {gamesentenceminer-2.9.3 → gamesentenceminer-2.9.4}/setup.cfg +0 -0
@@ -82,6 +82,8 @@ def update_anki_card(last_note: AnkiCard, note=None, audio_path='', video_path='
82
82
  for key, value in get_config().anki.anki_custom_fields.items():
83
83
  note['fields'][key] = str(value)
84
84
 
85
+
86
+ notification.open_browser_window(1)
85
87
  invoke("updateNoteFields", note=note)
86
88
  tags = []
87
89
  if get_config().anki.custom_tags:
@@ -94,9 +96,11 @@ def update_anki_card(last_note: AnkiCard, note=None, audio_path='', video_path='
94
96
  logger.info(f"UPDATED ANKI CARD FOR {last_note.noteId}")
95
97
  if get_config().features.notify_on_update:
96
98
  notification.send_note_updated(tango)
99
+ notification.open_browser_window(last_note.noteId)
97
100
  if get_config().features.open_anki_edit:
98
101
  notification.open_anki_card(last_note.noteId)
99
102
 
103
+
100
104
  if get_config().audio.external_tool and get_config().audio.external_tool_enabled and update_audio:
101
105
  open_audio_in_external(f"{get_config().audio.anki_media_collection}/{audio_in_anki}")
102
106
 
@@ -21,6 +21,27 @@ if is_windows():
21
21
  else:
22
22
  notifier = notification
23
23
 
24
+ def open_browser_window(note_id):
25
+ url = "http://localhost:8765"
26
+ headers = {'Content-Type': 'application/json'}
27
+
28
+ data = {
29
+ "action": "guiBrowse",
30
+ "version": 6,
31
+ "params": {
32
+ "query": f"nid:{note_id}"
33
+ }
34
+ }
35
+
36
+ try:
37
+ response = requests.post(url, json=data, headers=headers)
38
+ if response.status_code == 200:
39
+ logger.info(f"Opened Anki note with ID {note_id}")
40
+ else:
41
+ logger.error(f"Failed to open Anki note with ID {note_id}")
42
+ except Exception as e:
43
+ logger.info(f"Error connecting to AnkiConnect: {e}")
44
+
24
45
 
25
46
  def open_anki_card(note_id):
26
47
  url = "http://localhost:8765"
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- import logging
3
2
  import os.path
4
3
  import subprocess
5
4
  import threading
@@ -6,7 +6,6 @@ import requests
6
6
  from GameSentenceMiner.configuration import logger, get_app_directory
7
7
 
8
8
  PACKAGE_NAME = "GameSentenceMiner"
9
- VERSION_FILE_PATH = os.path.join(get_app_directory(), 'version.txt')
10
9
 
11
10
  def get_current_version():
12
11
  try:
@@ -247,7 +247,7 @@ def clear_history():
247
247
 
248
248
  async def add_event_to_texthooker(line: GameLine):
249
249
  new_event = event_manager.add_gameline(line)
250
- await broadcast_message({
250
+ await websocket_server_thread.send_text({
251
251
  'event': 'text_received',
252
252
  'sentence': line.text,
253
253
  'data': new_event.to_serializable()
@@ -294,37 +294,6 @@ def play_audio():
294
294
  return jsonify({}), 200
295
295
 
296
296
 
297
- connected_clients = set()
298
-
299
- async def websocket_handler(websocket):
300
- logger.debug(f"Client connected: {websocket.remote_address}")
301
- connected_clients.add(websocket)
302
- try:
303
- async for message in websocket:
304
- try:
305
- data = json.loads(message)
306
- if 'type' in data and data['type'] == 'get_events':
307
- initial_events = [{'id': 1, 'text': 'Initial event from WebSocket'}, {'id': 2, 'text': 'Another initial event'}]
308
- await websocket.send(json.dumps({'event': 'initial_events', 'payload': initial_events}))
309
- elif 'update_checkbox' in data:
310
- print(f"Received checkbox update: {data}")
311
- # Handle checkbox update logic
312
- pass
313
- await websocket.send(json.dumps({'response': f'Server received: {message}'}))
314
- except json.JSONDecodeError:
315
- await websocket.send(json.dumps({'error': 'Invalid JSON format'}))
316
- except websockets.exceptions.ConnectionClosedError:
317
- print(f"Client disconnected abruptly: {websocket.remote_address}")
318
- except websockets.exceptions.ConnectionClosedOK:
319
- print(f"Client disconnected gracefully: {websocket.remote_address}")
320
- finally:
321
- connected_clients.discard(websocket)
322
-
323
- async def broadcast_message(message):
324
- if connected_clients:
325
- for client in connected_clients:
326
- await client.send(json.dumps(message))
327
-
328
297
  # async def main():
329
298
  # async with websockets.serve(websocket_handler, "localhost", 8765): # Choose a port for WebSocket
330
299
  # print("WebSocket server started on ws://localhost:8765/ws (adjust as needed)")
@@ -360,7 +329,7 @@ def are_lines_selected():
360
329
 
361
330
  def reset_checked_lines():
362
331
  async def send_reset_message():
363
- await broadcast_message({
332
+ await websocket_server_thread.send_text({
364
333
  'event': 'reset_checkboxes',
365
334
  })
366
335
  event_manager.reset_checked_lines()
@@ -383,23 +352,69 @@ def start_web_server():
383
352
 
384
353
  import signal
385
354
 
386
- async def run_websocket_server(host="0.0.0.0"):
387
- global websocket_port
388
- websocket = None
389
- try:
390
- websocket_port = get_config().advanced.texthooker_communication_websocket_port
391
- websocket = await websockets.serve(websocket_handler, host, websocket_port)
392
- logger.debug(f"WebSocket server started at ws://{host}:{websocket_port}/")
393
- await asyncio.Future() # Keep the server running
394
- except asyncio.CancelledError:
395
- logger.info("WebSocket server shutting down...")
396
- except OSError as e:
397
- logger.error(f"TextHooker WebSocket server failed to start on port {websocket_port}: {e}")
398
- logger.info("You may need to try a different port in GSM's advanced config, and then update that in the Texthooker's settings.")
399
- finally:
400
- if websocket:
401
- websocket.close()
402
- await asyncio.sleep(1) # Wait before retrying
355
+ websocket_server_thread = None
356
+ websocket_queue = queue.Queue()
357
+ paused = False
358
+
359
+
360
+ class WebsocketServerThread(threading.Thread):
361
+ def __init__(self, read):
362
+ super().__init__(daemon=True)
363
+ self._loop = None
364
+ self.read = read
365
+ self.clients = set()
366
+ self._event = threading.Event()
367
+
368
+ @property
369
+ def loop(self):
370
+ self._event.wait()
371
+ return self._loop
372
+
373
+ async def send_text_coroutine(self, message):
374
+ for client in self.clients:
375
+ await client.send(message)
376
+
377
+ async def server_handler(self, websocket):
378
+ self.clients.add(websocket)
379
+ try:
380
+ async for message in websocket:
381
+ if self.read and not paused:
382
+ websocket_queue.put(message)
383
+ try:
384
+ await websocket.send('True')
385
+ except websockets.exceptions.ConnectionClosedOK:
386
+ pass
387
+ else:
388
+ try:
389
+ await websocket.send('False')
390
+ except websockets.exceptions.ConnectionClosedOK:
391
+ pass
392
+ except websockets.exceptions.ConnectionClosedError:
393
+ pass
394
+ finally:
395
+ self.clients.remove(websocket)
396
+
397
+ async def send_text(self, text):
398
+ if text:
399
+ return asyncio.run_coroutine_threadsafe(
400
+ self.send_text_coroutine(json.dumps(text)), self.loop)
401
+
402
+ def stop_server(self):
403
+ self.loop.call_soon_threadsafe(self._stop_event.set)
404
+
405
+ def run(self):
406
+ async def main():
407
+ self._loop = asyncio.get_running_loop()
408
+ self._stop_event = stop_event = asyncio.Event()
409
+ self._event.set()
410
+ self.server = start_server = websockets.serve(self.server_handler,
411
+ "0.0.0.0",
412
+ get_config().advanced.texthooker_communication_websocket_port,
413
+ max_size=1000000000)
414
+ async with start_server:
415
+ await stop_event.wait()
416
+
417
+ asyncio.run(main())
403
418
 
404
419
  def handle_exit_signal(loop):
405
420
  logger.info("Received exit signal. Shutting down...")
@@ -407,13 +422,16 @@ def handle_exit_signal(loop):
407
422
  task.cancel()
408
423
 
409
424
  async def texthooker_page_coro():
425
+ global websocket_server_thread
410
426
  # Run the WebSocket server in the asyncio event loop
411
427
  flask_thread = threading.Thread(target=start_web_server)
412
428
  flask_thread.daemon = True
413
429
  flask_thread.start()
414
430
 
431
+ websocket_server_thread = WebsocketServerThread(read=True)
432
+ websocket_server_thread.start()
433
+
415
434
  # Keep the main asyncio event loop running (for the WebSocket server)
416
- await run_websocket_server()
417
435
 
418
436
  def run_text_hooker_page():
419
437
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.9.3
3
+ Version: 2.9.4
4
4
  Summary: A tool for mining sentences from games.
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -24,6 +24,14 @@ GameSentenceMiner.egg-info/requires.txt
24
24
  GameSentenceMiner.egg-info/top_level.txt
25
25
  GameSentenceMiner/ai/__init__.py
26
26
  GameSentenceMiner/ai/ai_prompting.py
27
+ GameSentenceMiner/assets/__init__.py
28
+ GameSentenceMiner/assets/icon.png
29
+ GameSentenceMiner/assets/icon128.png
30
+ GameSentenceMiner/assets/icon256.png
31
+ GameSentenceMiner/assets/icon32.png
32
+ GameSentenceMiner/assets/icon512.png
33
+ GameSentenceMiner/assets/icon64.png
34
+ GameSentenceMiner/assets/pickaxe.png
27
35
  GameSentenceMiner/communication/__init__.py
28
36
  GameSentenceMiner/communication/send.py
29
37
  GameSentenceMiner/communication/websocket.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.9.3
3
+ Version: 2.9.4
4
4
  Summary: A tool for mining sentences from games.
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "GameSentenceMiner"
10
- version = "2.9.3"
10
+ version = "2.9.4"
11
11
  description = "A tool for mining sentences from games."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -62,4 +62,4 @@ packages = ["GameSentenceMiner", "GameSentenceMiner.vad", 'GameSentenceMiner.dow
62
62
 
63
63
  [tool.setuptools.package-data]
64
64
  "GameSentenceMiner.web" = ["static/*", "templates/*"]
65
- "GameSentenceMiner.assets" = ["*"]
65
+ "GameSentenceMiner" = ["assets/*"]