GameSentenceMiner 2.16.12__py3-none-any.whl → 2.16.13__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.
@@ -417,10 +417,10 @@ class ConfigApp:
417
417
  self.create_screenshot_tab()
418
418
  self.create_audio_tab()
419
419
  self.create_obs_tab()
420
- self.create_profiles_tab()
421
420
  self.create_ai_tab()
422
421
  self.create_advanced_tab()
423
422
  self.create_overlay_tab()
423
+ self.create_profiles_tab()
424
424
  # self.create_wip_tab()
425
425
 
426
426
  def add_reset_button(self, frame, category, row, column=0, recreate_tab=None):
@@ -9,11 +9,11 @@ import time
9
9
  from PIL import Image
10
10
  from typing import Dict, Any, List, Tuple
11
11
  import json
12
- from rapidfuzz.distance import Levenshtein
12
+ from rapidfuzz import fuzz
13
13
 
14
14
  # Local application imports
15
15
  from GameSentenceMiner.ocr.gsm_ocr_config import set_dpi_awareness
16
- from GameSentenceMiner.util.configuration import OverlayEngine, get_config, is_windows, is_beangate, logger
16
+ from GameSentenceMiner.util.configuration import OverlayEngine, get_config, get_temporary_directory, is_windows, is_beangate, logger
17
17
  from GameSentenceMiner.util.electron_config import get_ocr_language
18
18
  from GameSentenceMiner.obs import get_screenshot_PIL
19
19
  from GameSentenceMiner.web.texthooking_page import send_word_coordinates_to_overlay
@@ -93,7 +93,8 @@ class OverlayThread(threading.Thread):
93
93
  super().__init__()
94
94
  self.overlay_processor = OverlayProcessor()
95
95
  self.loop = asyncio.new_event_loop()
96
- self.daemon = True # Ensure thread exits when main program exits
96
+ self.daemon = True
97
+ self.first_time_run = True
97
98
 
98
99
  def run(self):
99
100
  """Runs the overlay processing loop."""
@@ -103,11 +104,18 @@ class OverlayThread(threading.Thread):
103
104
  async def overlay_loop(self):
104
105
  """Main loop to periodically process and send overlay data."""
105
106
  while True:
106
- if get_config().overlay.periodic and overlay_server_thread.has_clients():
107
- await self.overlay_processor.find_box_and_send_to_overlay('')
108
- await asyncio.sleep(get_config().overlay.periodic_interval) # Adjust the interval as needed
107
+ if overlay_server_thread.has_clients():
108
+ if get_config().overlay.periodic:
109
+ await self.overlay_processor.find_box_and_send_to_overlay('')
110
+ await asyncio.sleep(get_config().overlay.periodic_interval)
111
+ elif self.first_time_run:
112
+ await self.overlay_processor.find_box_and_send_to_overlay('')
113
+ self.first_time_run = False
114
+ else:
115
+ await asyncio.sleep(3)
109
116
  else:
110
- await asyncio.sleep(3) # Sleep briefly when not active
117
+ self.first_time_run = True
118
+ await asyncio.sleep(3)
111
119
 
112
120
  class OverlayProcessor:
113
121
  """
@@ -125,6 +133,8 @@ class OverlayProcessor:
125
133
  self.lens = None
126
134
  self.regex = None
127
135
  self.ready = False
136
+ self.last_oneocr_result = None
137
+ self.last_lens_result = None
128
138
 
129
139
  try:
130
140
  if self.config.overlay.websocket_port and all([GoogleLens, get_regex]):
@@ -221,6 +231,8 @@ class OverlayProcessor:
221
231
  monitor = self.get_monitor_workarea(0) # Get primary monitor work area
222
232
  sct_img = sct.grab(monitor)
223
233
  img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
234
+
235
+ img.save(os.path.join(get_temporary_directory(), "latest_overlay_screenshot.png"))
224
236
 
225
237
  return img, monitor['width'], monitor['height']
226
238
 
@@ -261,6 +273,8 @@ class OverlayProcessor:
261
273
  paste_x = math.floor(x1)
262
274
  paste_y = math.floor(y1)
263
275
  composite_img.paste(cropped_image, (paste_x, paste_y))
276
+
277
+ composite_img.save(os.path.join(get_temporary_directory(), "latest_overlay_screenshot_trimmed.png"))
264
278
 
265
279
  return composite_img
266
280
 
@@ -277,7 +291,7 @@ class OverlayProcessor:
277
291
  return []
278
292
  if self.oneocr:
279
293
  # 2. Use OneOCR to find general text areas (fast)
280
- _, _, oneocr_results, crop_coords_list = self.oneocr(
294
+ res, text, oneocr_results, crop_coords_list = self.oneocr(
281
295
  full_screenshot,
282
296
  return_coords=True,
283
297
  multiple_crop_coords=True,
@@ -285,8 +299,19 @@ class OverlayProcessor:
285
299
  furigana_filter_sensitivity=None, # Disable furigana filtering
286
300
  )
287
301
 
302
+ text_str = "".join([text for text in text if self.regex.match(text)])
303
+
304
+ # RapidFuzz fuzzy match 90% to not send the same results repeatedly
305
+ if self.last_oneocr_result:
306
+
307
+ score = fuzz.ratio(text_str, self.last_oneocr_result)
308
+ if score >= 80:
309
+ logger.info("OneOCR results are similar to the last results (score: %d). Skipping overlay update.", score)
310
+ return
311
+ self.last_oneocr_result = text_str
312
+
288
313
  logger.info("Sending OneOCR results to overlay.")
289
- await send_word_coordinates_to_overlay(oneocr_results)
314
+ await send_word_coordinates_to_overlay(self._convert_oneocr_results_to_percentages(oneocr_results, monitor_width, monitor_height))
290
315
 
291
316
  # If User Home is beangate
292
317
  if is_beangate:
@@ -317,9 +342,19 @@ class OverlayProcessor:
317
342
  if len(res) != 3:
318
343
  return
319
344
 
320
- _, _, coords = res
345
+ success, text_list, coords = res
346
+
347
+ text_str = "".join([text for text in text_list if self.regex.match(text)])
348
+
349
+ # RapidFuzz fuzzy match 90% to not send the same results repeatedly
350
+ if self.last_lens_result:
351
+ score = fuzz.ratio(text_str, self.last_lens_result)
352
+ if score >= 80:
353
+ logger.info("Google Lens results are similar to the last results (score: %d). Skipping overlay update.", score)
354
+ return
355
+ self.last_lens_result = text_str
321
356
 
322
- if not res or not coords:
357
+ if not success or not coords:
323
358
  return
324
359
 
325
360
  # 5. Process the high-accuracy results into the desired format
@@ -433,6 +468,38 @@ class OverlayProcessor:
433
468
  "x3": center_x + half_w, "y3": center_y + half_h,
434
469
  "x4": center_x - half_w, "y4": center_y + half_h,
435
470
  }
471
+
472
+ def _convert_oneocr_results_to_percentages(
473
+ self,
474
+ oneocr_results: List[Dict[str, Any]],
475
+ monitor_width: int,
476
+ monitor_height: int
477
+ ) -> List[Dict[str, Any]]:
478
+ """
479
+ Converts OneOCR results with pixel coordinates to percentages relative to the monitor size.
480
+ """
481
+ converted_results = []
482
+ for item in oneocr_results:
483
+ bbox = item.get("bounding_rect", {})
484
+ if not bbox:
485
+ continue
486
+ # Convert each coordinate to a percentage of the monitor dimensions
487
+ converted_bbox = {
488
+ key: (value / monitor_width if "x" in key else value / monitor_height)
489
+ for key, value in bbox.items()
490
+ }
491
+ converted_item = item.copy()
492
+ converted_item["bounding_rect"] = converted_bbox
493
+ converted_results.append(converted_item)
494
+ for word in converted_item.get("words", []):
495
+ word_bbox = word.get("bounding_rect", {})
496
+ if word_bbox:
497
+ word["bounding_rect"] = {
498
+ key: (value / monitor_width if "x" in key else value / monitor_height)
499
+ for key, value in word_bbox.items()
500
+ }
501
+ # logger.info(f"Converted OneOCR results to percentages: {converted_results}")
502
+ return converted_results
436
503
 
437
504
  async def main_test_screenshot():
438
505
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.16.12
3
+ Version: 2.16.13
4
4
  Summary: A tool for mining sentences from games. Update: Overlay?
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  GameSentenceMiner/anki.py,sha256=Qq03nxYCA0bXS8IR1vnEB9cv2vxo6Ruy-UuiojC4ad0,26518
3
- GameSentenceMiner/config_gui.py,sha256=iOsi3IVati6JHCnoCIzLAxlrbZt4j4pL5KNFzSq4gJo,146121
3
+ GameSentenceMiner/config_gui.py,sha256=kyAK3s1tkYmjO_zdJDYWR0rubAsS0vHiT46HdMTxJGk,146121
4
4
  GameSentenceMiner/gametext.py,sha256=fgBgLchezpauWELE9Y5G3kVCLfAneD0X4lJFoI3FYbs,10351
5
5
  GameSentenceMiner/gsm.py,sha256=1eq5nkYulfm85749g8g2s_WkqqiQWDopUXyimJLIy6M,33814
6
6
  GameSentenceMiner/obs.py,sha256=EyAYhaLvMjoeC-3j7fuvkqZN5logFFanPfb8Wn1C6m0,27296
@@ -41,7 +41,7 @@ GameSentenceMiner/util/configuration.py,sha256=qATOwZahVQNP8-ZnWiAKuR7UJLW25QNDm
41
41
  GameSentenceMiner/util/db.py,sha256=B2Qwg7i0Qn_yxov-NhcT9QdFkF218Cqea_V7ZPzYBzM,21365
42
42
  GameSentenceMiner/util/electron_config.py,sha256=KfeJToeFFVw0IR5MKa-gBzpzaGrU-lyJbR9z-sDEHYU,8767
43
43
  GameSentenceMiner/util/ffmpeg.py,sha256=g3v1aJnv3qWekDnO0FdYozB-MG9di4WUvPA3NyXY9Ws,28998
44
- GameSentenceMiner/util/get_overlay_coords.py,sha256=pkG8_3l3fuhm3DzFLuNwSeaYTkRMtyU3O7DB6s372QM,18961
44
+ GameSentenceMiner/util/get_overlay_coords.py,sha256=lUD2YU4iVoXRHvQzkCN0Re3IQkYKemU2NV81MtQ94Uk,22028
45
45
  GameSentenceMiner/util/gsm_utils.py,sha256=Piwv88Q9av2LBeN7M6QDi0Mp0_R2lNbkcI6ekK5hd2o,11851
46
46
  GameSentenceMiner/util/model.py,sha256=R-_RYTYLSDNgBoVTPuPBcIHeOznIqi_vBzQ7VQ20WYk,6727
47
47
  GameSentenceMiner/util/notification.py,sha256=YBhf_mSo_i3cjBz-pmeTPx3wchKiG9BK2VBdZSa2prQ,4597
@@ -90,9 +90,9 @@ GameSentenceMiner/web/templates/utility.html,sha256=KtqnZUMAYs5XsEdC9Tlsd40NKAVi
90
90
  GameSentenceMiner/web/templates/components/navigation.html,sha256=6y9PvM3nh8LY6JWrZb6zVOm0vqkBLDc6d3gB9X5lT_w,1055
91
91
  GameSentenceMiner/web/templates/components/theme-styles.html,sha256=hiq3zdJljpRjQO1iUA7gfFKwXebltG-IWW-gnKS4GHA,3439
92
92
  GameSentenceMiner/wip/__init___.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
- gamesentenceminer-2.16.12.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
94
- gamesentenceminer-2.16.12.dist-info/METADATA,sha256=SgT5FkcLIkIfeGJbk_hyrvw0fZCNY27li0gD42HsKrs,7349
95
- gamesentenceminer-2.16.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
96
- gamesentenceminer-2.16.12.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
97
- gamesentenceminer-2.16.12.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
98
- gamesentenceminer-2.16.12.dist-info/RECORD,,
93
+ gamesentenceminer-2.16.13.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
94
+ gamesentenceminer-2.16.13.dist-info/METADATA,sha256=Y5s0ewCxQEPwTEo4MTRyUqaDrfWOiiVfL1qtWDmo-ew,7349
95
+ gamesentenceminer-2.16.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
96
+ gamesentenceminer-2.16.13.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
97
+ gamesentenceminer-2.16.13.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
98
+ gamesentenceminer-2.16.13.dist-info/RECORD,,