GameSentenceMiner 2.9.3__py3-none-any.whl → 2.9.4__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.
GameSentenceMiner/anki.py CHANGED
@@ -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
 
File without changes
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -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"
GameSentenceMiner/obs.py CHANGED
@@ -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
@@ -1,5 +1,5 @@
1
1
  GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- GameSentenceMiner/anki.py,sha256=JnVfFkLpEfWaPfOLngU0PSQq4vrgWuhd_VLYZEBqNTY,14608
2
+ GameSentenceMiner/anki.py,sha256=HAYblBjkth5p0tx9Z0LNQn6zuQC8_54RIB2BMzN8-LE,14705
3
3
  GameSentenceMiner/config_gui.py,sha256=h4zz85gfhxSphaJ-IZSu9D4jR70mDlKecZ9JRCO5Noc,80927
4
4
  GameSentenceMiner/configuration.py,sha256=KKW6fmpxya4dmXx9cERFVrzsKCTw0vmZrF2HAJDURBU,25667
5
5
  GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
@@ -7,14 +7,22 @@ GameSentenceMiner/ffmpeg.py,sha256=APa2vNdAgxYdG96_Z3Xdh1WqOiWaK6gTLJqzEvCMMeU,1
7
7
  GameSentenceMiner/gametext.py,sha256=sll-6Pficd4ZXYy8yL8hBrEOSpfa53TOye7vtHHKFN4,6218
8
8
  GameSentenceMiner/gsm.py,sha256=olG3BIWjbVHWTsRKmeDVE5X8XrgppWke73Fy1J15dxA,29868
9
9
  GameSentenceMiner/model.py,sha256=1lRyJFf_LND_4O16h8CWVqDfosLgr0ZS6ufBZ3qJHpY,5699
10
- GameSentenceMiner/notification.py,sha256=fPTbZJG82YLsppjC0sByQ0SX3bSsvDWXzG498HBEGMY,2823
11
- GameSentenceMiner/obs.py,sha256=OntCO14G2KjzMk6bztqhQWiDiNzjzPpL_KHKXasxJq4,14886
12
- GameSentenceMiner/package.py,sha256=YlS6QRMuVlm6mdXx0rlXv9_3erTGS21jaP3PNNWfAH0,1250
10
+ GameSentenceMiner/notification.py,sha256=e6TOzZJD7RTvMgxaY-V01r5OiocHhdqEIVdAnj4MGSw,3437
11
+ GameSentenceMiner/obs.py,sha256=JiydRMpfSpNZ0nDAzH8OOECbYbsxMNSGz46lO8lZbvA,14871
12
+ GameSentenceMiner/package.py,sha256=uu3Yb3pqu8vN5ISzP87YCHlFNR9wxMMf5hPRncTr7ws,1181
13
13
  GameSentenceMiner/ss_selector.py,sha256=csey9H3561-guRJcT6gQN6hXxvylP0CBI0dp2-kwo2Q,4446
14
14
  GameSentenceMiner/text_log.py,sha256=U2_g8THAYeexRiE2bLk_bCt_2ShiA8SQ9VdJsi4riHs,5181
15
15
  GameSentenceMiner/util.py,sha256=ZbK7i1UeOzKyc5WtCcttiGljR_stfu7qpnEpgqFBwro,8976
16
16
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  GameSentenceMiner/ai/ai_prompting.py,sha256=xw8et6XNwQiDXOXZnw8iIntVSg8lni4YYZbgWsK7qDE,10013
18
+ GameSentenceMiner/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ GameSentenceMiner/assets/icon.png,sha256=9GRL8uXUAgkUSlvbm9Pv9o2poFVRGdW6s2ub_DeUD9M,937624
20
+ GameSentenceMiner/assets/icon128.png,sha256=l90j7biwdz5ahwOd5wZ-406ryEV9Pan93dquJQ3e1CI,18395
21
+ GameSentenceMiner/assets/icon256.png,sha256=JEW46wOrG1KR-907rvFaEdNbPtj5gu0HJmG7qUnIHxQ,51874
22
+ GameSentenceMiner/assets/icon32.png,sha256=Kww0hU_qke9_22wBuO_Nq0Dv2SfnOLwMhCyGgbgXdg8,6089
23
+ GameSentenceMiner/assets/icon512.png,sha256=HxUj2GHjyQsk8NV433256UxU9phPhtjCY-YB_7W4sqs,192487
24
+ GameSentenceMiner/assets/icon64.png,sha256=N8xgdZXvhqVQP9QUK3wX5iqxX9LxHljD7c-Bmgim6tM,9301
25
+ GameSentenceMiner/assets/pickaxe.png,sha256=VfIGyXyIZdzEnVcc4PmG3wszPMO1W4KCT7Q_nFK6eSE,1403829
18
26
  GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
19
27
  GameSentenceMiner/communication/send.py,sha256=X0MytGv5hY-uUvkfvdCqQA_ljZFmV6UkJ6in1TA1bUE,217
20
28
  GameSentenceMiner/communication/websocket.py,sha256=8eFZaTtoFggEPdqw2Jl4zqHC2I7J3-Gk27CxVX7SyBo,3277
@@ -42,7 +50,7 @@ GameSentenceMiner/vad/vad_utils.py,sha256=_YC6rW2eXSBeLnYbVl_F3na1KCRL90VrnOzKYJ
42
50
  GameSentenceMiner/vad/vosk_helper.py,sha256=h7yNHrzrzT-J74UniA0T2ZX8cHqhflCzwyDjoIdKLO4,6479
43
51
  GameSentenceMiner/vad/whisper_helper.py,sha256=B64-Eq_ZMCIyQX_A8uvYz-c48hSXJAyz6tSXNRaLjtA,4020
44
52
  GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
- GameSentenceMiner/web/texthooking_page.py,sha256=alXNkpm2Kl-ewYIm_aw24_8RdcYedLB59y6YTkp_mrE,14789
53
+ GameSentenceMiner/web/texthooking_page.py,sha256=7Z7TGDcnj-94Y9ws7bQph4oIXrqf8Q9qVKowKimHWxM,14749
46
54
  GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
55
  GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
48
56
  GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
@@ -56,9 +64,9 @@ GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
56
64
  GameSentenceMiner/web/templates/index.html,sha256=HZKiIjiGJV8PGQ9T2aLDUNSfJn71qOwbYCjbRuSIjpY,213583
57
65
  GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
58
66
  GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
59
- gamesentenceminer-2.9.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
60
- gamesentenceminer-2.9.3.dist-info/METADATA,sha256=XlzEKeMFA9JKtiEU_pxShJbopSnIKKdL7cRsg25JxqQ,7280
61
- gamesentenceminer-2.9.3.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
62
- gamesentenceminer-2.9.3.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
63
- gamesentenceminer-2.9.3.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
64
- gamesentenceminer-2.9.3.dist-info/RECORD,,
67
+ gamesentenceminer-2.9.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
68
+ gamesentenceminer-2.9.4.dist-info/METADATA,sha256=jNhOj4IaiTEJqGlm4-VCtviW5hmN8J5XnvPBGWgcY0Y,7280
69
+ gamesentenceminer-2.9.4.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
70
+ gamesentenceminer-2.9.4.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
71
+ gamesentenceminer-2.9.4.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
72
+ gamesentenceminer-2.9.4.dist-info/RECORD,,