GameSentenceMiner 2.14.21__py3-none-any.whl → 2.15.1__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.
@@ -2192,6 +2192,9 @@ class ConfigApp:
2192
2192
  row=self.current_row, column=0)
2193
2193
  self.overlay_monitor = ttk.Combobox(overlay_frame, values=self.monitors, state="readonly")
2194
2194
  self.overlay_monitor.grid(row=self.current_row, column=1, sticky='EW', pady=2)
2195
+ # disable selection for now, default to value 1
2196
+ self.overlay_monitor.current(0)
2197
+ self.overlay_monitor.config(state="disabled")
2195
2198
  self.current_row += 1
2196
2199
 
2197
2200
  if self.monitors:
GameSentenceMiner/obs.py CHANGED
@@ -483,6 +483,9 @@ def set_fit_to_screen_for_scene_items(scene_name: str):
483
483
  try:
484
484
  # 1. Get the canvas (base) resolution from OBS video settings
485
485
  video_settings = client.get_video_settings()
486
+ if not hasattr(video_settings, 'base_width') or not hasattr(video_settings, 'base_height'):
487
+ logger.debug("Video settings do not have base_width or base_height attributes, probably weird websocket error issue? Idk what causes it..")
488
+ return
486
489
  canvas_width = video_settings.base_width
487
490
  canvas_height = video_settings.base_height
488
491
 
@@ -499,12 +502,49 @@ def set_fit_to_screen_for_scene_items(scene_name: str):
499
502
  item_id = item['sceneItemId']
500
503
  source_name = item['sourceName']
501
504
 
505
+ scene_item_transform = item.get('sceneItemTransform', {})
506
+
507
+ source_width = scene_item_transform.get('sourceWidth', None)
508
+ source_height = scene_item_transform.get('sourceHeight', None)
509
+
510
+ aspect_ratio_different = False
511
+ if source_width and source_height:
512
+ source_aspect_ratio = source_width / source_height
513
+ canvas_aspect_ratio = canvas_width / canvas_height
514
+ # Check if aspect ratio is different and if it's a standard aspect ratio
515
+ aspect_ratio_different = abs(source_aspect_ratio - canvas_aspect_ratio) > 0.01
516
+
517
+ # List of standard aspect ratios
518
+ standard_ratios = [
519
+ 4 / 3,
520
+ 16 / 9,
521
+ 16 / 10,
522
+ 21 / 9,
523
+ 32 / 9,
524
+ 5 / 4,
525
+ 3 / 2,
526
+ ]
527
+
528
+ def is_standard_ratio(ratio):
529
+ return any(abs(ratio - std) < 0.02 for std in standard_ratios)
530
+
531
+ # Only crop if both source and canvas are standard aspect ratios
532
+ if aspect_ratio_different:
533
+ if not (is_standard_ratio(source_aspect_ratio) and is_standard_ratio(canvas_aspect_ratio)):
534
+ aspect_ratio_different = False
535
+
502
536
  # This transform object is the equivalent of "Fit to Screen"
503
537
  fit_to_screen_transform = {
504
538
  'boundsType': 'OBS_BOUNDS_SCALE_INNER',
505
539
  'alignment': 5, # 5 = Center alignment (horizontal and vertical)
506
540
  'boundsWidth': canvas_width,
507
541
  'boundsHeight': canvas_height,
542
+ 'positionX': 0,
543
+ 'positionY': 0,
544
+ 'cropLeft': 0 if not aspect_ratio_different or canvas_width > source_width else (source_width - canvas_width) // 2,
545
+ 'cropRight': 0 if not aspect_ratio_different or canvas_width > source_width else (source_width - canvas_width) // 2,
546
+ 'cropTop': 0 if not aspect_ratio_different or canvas_height > source_height else (source_height - canvas_height) // 2,
547
+ 'cropBottom': 0 if not aspect_ratio_different or canvas_height > source_height else (source_height - canvas_height) // 2,
508
548
  }
509
549
 
510
550
  try:
@@ -83,7 +83,6 @@ class OCRConfig:
83
83
  ]
84
84
 
85
85
  def scale_to_custom_size(self, width, height):
86
- print(self.pre_scale_rectangles)
87
86
  self.rectangles = self.pre_scale_rectangles.copy()
88
87
  if self.coordinate_system and self.coordinate_system == "percentage":
89
88
  for rectangle in self.rectangles:
@@ -228,7 +228,6 @@ def save_result_image(img, pre_crop_image=None):
228
228
 
229
229
  async def send_result(text, time):
230
230
  if text:
231
- text = do_text_replacements(text, OCR_REPLACEMENTS_FILE)
232
231
  if get_ocr_send_to_clipboard():
233
232
  import pyperclip
234
233
  pyperclip.copy(text)
@@ -106,6 +106,7 @@ def empty_post_process(text):
106
106
 
107
107
  def post_process(text, keep_blank_lines=False):
108
108
  import jaconv
109
+ text = text.replace("\"", "")
109
110
  if keep_blank_lines:
110
111
  text = '\n'.join([''.join(i.split()) for i in text.splitlines()])
111
112
  else:
@@ -354,8 +355,8 @@ class GoogleLens:
354
355
  response_proto = LensOverlayServerResponse().FromString(res.content)
355
356
  response_dict = response_proto.to_dict(betterproto.Casing.SNAKE)
356
357
 
357
- if os.path.exists(r"C:\Users\Beangate\GSM\Electron App\test"):
358
- with open(os.path.join(r"C:\Users\Beangate\GSM\Electron App\test", 'glens_response.json'), 'w', encoding='utf-8') as f:
358
+ if os.path.exists(r"C:\Users\Beangate\GSM\test"):
359
+ with open(os.path.join(r"C:\Users\Beangate\GSM\test", 'glens_response.json'), 'w', encoding='utf-8') as f:
359
360
  json.dump(response_dict, f, indent=4, ensure_ascii=False)
360
361
  res = ''
361
362
  text = response_dict['objects_response']['text']
@@ -1433,10 +1434,10 @@ class localLLMOCR:
1433
1434
  self.keep_warm = config.get('keep_warm', True)
1434
1435
  self.custom_prompt = config.get('prompt', None)
1435
1436
  self.available = True
1436
- # if any(x in self.api_url for x in ['localhost', '127.0.0.1']):
1437
- # if not self.check_connection(self.api_url):
1438
- # logger.warning('Local LLM OCR API is not reachable')
1439
- # return
1437
+ if any(x in self.api_url for x in ['localhost', '127.0.0.1']):
1438
+ if not self.check_connection(self.api_url):
1439
+ logger.warning('Local LLM OCR API is not reachable')
1440
+ return
1440
1441
  self.client = openai.OpenAI(
1441
1442
  base_url=self.api_url.replace('/v1/chat/completions', '/v1'),
1442
1443
  api_key=self.api_key
@@ -1493,7 +1494,7 @@ class localLLMOCR:
1493
1494
  prompt = self.custom_prompt.strip()
1494
1495
  else:
1495
1496
  prompt = f"""
1496
- Extract all {CommonLanguages.from_code(get_ocr_language()).name} Text from Image. Ignore all Furigana. Do not return any commentary, just the text in the image. If there is no text in the image, return "" (Empty String).
1497
+ Extract all {CommonLanguages.from_code(get_ocr_language()).name} Text from Image. Ignore all Furigana. Do not return any commentary, just the text in the image. Do not Translate. If there is no text in the image, return "" (Empty String).
1497
1498
  """
1498
1499
 
1499
1500
  response = self.client.chat.completions.create(
@@ -1,5 +1,6 @@
1
- from ...ocr.gsm_ocr_config import set_dpi_awareness, get_scene_ocr_config_path, OCRConfig, get_scene_ocr_config
2
- from ...util.electron_config import *
1
+ from GameSentenceMiner.ocr.gsm_ocr_config import set_dpi_awareness, get_scene_ocr_config
2
+ from GameSentenceMiner.util.electron_config import * # noqa: F403
3
+ from GameSentenceMiner.util.gsm_utils import do_text_replacements, OCR_REPLACEMENTS_FILE
3
4
 
4
5
  try:
5
6
  import win32gui
@@ -28,7 +29,6 @@ import signal
28
29
  import threading
29
30
  from pathlib import Path
30
31
  import queue
31
- import io
32
32
  import re
33
33
  import logging
34
34
  import inspect
@@ -39,23 +39,22 @@ import mss
39
39
  import asyncio
40
40
  import websockets
41
41
  import socketserver
42
- import queue
42
+ import cv2
43
+ import numpy as np
43
44
 
44
45
  from datetime import datetime, timedelta
45
- from PIL import Image, ImageDraw, UnidentifiedImageError
46
+ from PIL import Image, ImageDraw
46
47
  from loguru import logger
47
48
  from desktop_notifier import DesktopNotifierSync
48
49
  import psutil
49
50
 
50
- import inspect
51
- from .ocr import *
52
- try:
53
- from .secret import *
54
- except ImportError:
55
- pass
51
+ from .ocr import * # noqa: F403
56
52
  from .config import Config
57
53
  from .screen_coordinate_picker import get_screen_selection
58
- from GameSentenceMiner.util.configuration import get_temporary_directory, get_config
54
+ from GameSentenceMiner.util.configuration import get_temporary_directory
55
+
56
+ from skimage.metrics import structural_similarity as ssim
57
+ from typing import Union
59
58
 
60
59
  config = None
61
60
  last_image = None
@@ -799,8 +798,6 @@ class ScreenshotThread(threading.Thread):
799
798
  self.windows_window_tracker_instance.join()
800
799
 
801
800
 
802
- import cv2
803
- import numpy as np
804
801
 
805
802
  def apply_adaptive_threshold_filter(img):
806
803
  img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
@@ -865,11 +862,6 @@ def are_images_identical(img1, img2, img2_np=None):
865
862
  return (img1_np.shape == img2_np.shape) and np.array_equal(img1_np, img2_np)
866
863
 
867
864
 
868
- import cv2
869
- import numpy as np
870
- from skimage.metrics import structural_similarity as ssim
871
- from typing import Union
872
-
873
865
  ImageType = Union[np.ndarray, Image.Image]
874
866
 
875
867
  def _prepare_image(image: ImageType) -> np.ndarray:
@@ -1319,6 +1311,10 @@ def on_screenshot_combo():
1319
1311
  def on_window_minimized(minimized):
1320
1312
  global screencapture_window_visible
1321
1313
  screencapture_window_visible = not minimized
1314
+
1315
+
1316
+ def do_configured_ocr_replacements(text: str) -> str:
1317
+ return do_text_replacements(text, OCR_REPLACEMENTS_FILE)
1322
1318
 
1323
1319
 
1324
1320
  def process_and_write_results(img_or_path, write_to=None, last_result=None, filtering=None, notify=None, engine=None, ocr_start_time=None, furigana_filter_sensitivity=0):
@@ -1364,14 +1360,21 @@ def process_and_write_results(img_or_path, write_to=None, last_result=None, filt
1364
1360
  # print(engine_index)
1365
1361
 
1366
1362
  if res:
1363
+ text = do_configured_ocr_replacements(text)
1367
1364
  if filtering:
1368
1365
  text, orig_text = filtering(text, last_result)
1369
1366
  if get_ocr_language() == "ja" or get_ocr_language() == "zh":
1370
1367
  text = post_process(text, keep_blank_lines=get_ocr_keep_newline())
1371
- logger.opt(ansi=True).info(
1372
- f'Text recognized in {end_time - start_time:0.03f}s using <{engine_color}>{engine_instance.readable_name}</{engine_color}>: {text}')
1373
1368
  if notify and config.get_general('notifications'):
1374
1369
  notifier.send(title='owocr', message='Text recognized: ' + text)
1370
+
1371
+ if text and write_to is not None:
1372
+ if check_text_is_all_menu(text, crop_coords):
1373
+ logger.opt(ansi=True).info('Text is identified as all menu items, skipping further processing.')
1374
+ return orig_text, ''
1375
+
1376
+ logger.opt(ansi=True).info(
1377
+ f'Text recognized in {end_time - start_time:0.03f}s using <{engine_color}>{engine_instance.readable_name}</{engine_color}>: {text}')
1375
1378
 
1376
1379
  if write_to == 'websocket':
1377
1380
  websocket_server_thread.send_text(text)
@@ -1395,6 +1398,83 @@ def process_and_write_results(img_or_path, write_to=None, last_result=None, filt
1395
1398
 
1396
1399
  return orig_text, text
1397
1400
 
1401
+ def check_text_is_all_menu(text: str, crop_coords: tuple) -> bool:
1402
+ """
1403
+ Checks if the recognized text consists entirely of menu items.
1404
+ This function checks if the detected text area falls entirely within secondary rectangles (menu areas).
1405
+
1406
+ :param text: The recognized text from OCR.
1407
+ :param crop_coords: Tuple containing (x, y, width, height) of the detected text area relative to the cropped image.
1408
+ :return: True if the text is all menu items (within secondary rectangles), False otherwise.
1409
+ """
1410
+ if not text or not crop_coords:
1411
+ return False
1412
+
1413
+ original_width = obs_screenshot_thread.width
1414
+ original_height = obs_screenshot_thread.height
1415
+ crop_x, crop_y, crop_w, crop_h = crop_coords
1416
+
1417
+ ocr_config = get_scene_ocr_config()
1418
+
1419
+ if not any(rect.is_secondary for rect in ocr_config.rectangles):
1420
+ return False
1421
+
1422
+ ocr_config.scale_to_custom_size(original_width, original_height)
1423
+ if not ocr_config or not ocr_config.rectangles:
1424
+ return False
1425
+
1426
+ primary_rectangles = [rect for rect in ocr_config.rectangles if not rect.is_excluded and not rect.is_secondary]
1427
+ menu_rectangles = [rect for rect in ocr_config.rectangles if rect.is_secondary and not rect.is_excluded]
1428
+
1429
+ if not menu_rectangles:
1430
+ return False
1431
+
1432
+ if not primary_rectangles:
1433
+ if crop_x < 0 or crop_y < 0 or crop_x + crop_w > original_width or crop_y + crop_h > original_height:
1434
+ return False
1435
+ for menu_rect in menu_rectangles:
1436
+ rect_left, rect_top, rect_width, rect_height = menu_rect.coordinates
1437
+ rect_right = rect_left + rect_width
1438
+ rect_bottom = rect_top + rect_height
1439
+ if (crop_x >= rect_left and crop_y >= rect_top and
1440
+ crop_x + crop_w <= rect_right and crop_y + crop_h <= rect_bottom):
1441
+ return True
1442
+ return False
1443
+
1444
+ primary_rectangles.sort(key=lambda r: r.coordinates[1])
1445
+
1446
+ if len(primary_rectangles) == 1:
1447
+ primary_rect = primary_rectangles[0]
1448
+ primary_left, primary_top = primary_rect.coordinates[0], primary_rect.coordinates[1]
1449
+ original_x = crop_x + primary_left
1450
+ original_y = crop_y + primary_top
1451
+ else:
1452
+ current_y_offset = 0
1453
+ original_x = None
1454
+ original_y = None
1455
+ for i, primary_rect in enumerate(primary_rectangles):
1456
+ primary_left, primary_top, primary_width, primary_height = primary_rect.coordinates
1457
+ section_height = primary_height
1458
+ if crop_y >= current_y_offset and crop_y < current_y_offset + section_height:
1459
+ original_x = crop_x + primary_left
1460
+ original_y = (crop_y - current_y_offset) + primary_top
1461
+ break
1462
+ current_y_offset += section_height + 50
1463
+ if original_x is None or original_y is None:
1464
+ return False
1465
+
1466
+ if original_x < 0 or original_y < 0 or original_x > original_width or original_y > original_height:
1467
+ return False
1468
+
1469
+ for menu_rect in menu_rectangles:
1470
+ rect_left, rect_top, rect_width, rect_height = menu_rect.coordinates
1471
+ rect_right = rect_left + rect_width
1472
+ rect_bottom = rect_top + rect_height
1473
+ if (original_x >= rect_left and original_y >= rect_top and
1474
+ original_x <= rect_right and original_y <= rect_bottom):
1475
+ return True
1476
+
1477
+ return False
1398
1478
 
1399
1479
  def get_path_key(path):
1400
1480
  return path, path.lstat().st_mtime
@@ -94,26 +94,59 @@ class OverlayProcessor:
94
94
  except Exception as e:
95
95
  logger.error(f"Error during OCR processing: {e}", exc_info=True)
96
96
  return []
97
+
98
+ @staticmethod
99
+ def get_monitor_workarea(monitor_index=0):
100
+ """
101
+ Return MSS-style dict for monitor area.
102
+ For primary monitor, excludes taskbar. For others, returns full monitor area.
103
+ monitor_index: 0 = primary monitor, 1+ = others (as in mss.monitors).
104
+ """
105
+ with mss.mss() as sct:
106
+ monitors = sct.monitors[1:]
107
+ print(monitors)
108
+ if is_windows() and monitor_index == 0:
109
+ from ctypes import wintypes
110
+ import ctypes
111
+ # Get work area for primary monitor (ignores taskbar)
112
+ SPI_GETWORKAREA = 0x0030
113
+ rect = wintypes.RECT()
114
+ res = ctypes.windll.user32.SystemParametersInfoW(
115
+ SPI_GETWORKAREA, 0, ctypes.byref(rect), 0
116
+ )
117
+ if not res:
118
+ raise ctypes.WinError()
119
+
120
+ return {
121
+ "left": rect.left,
122
+ "top": rect.top,
123
+ "width": rect.right - rect.left,
124
+ "height": rect.bottom - rect.top,
125
+ }
126
+ elif is_windows() and monitor_index > 0:
127
+ # Secondary monitors: just return with a guess of how tall the taskbar is
128
+ taskbar_height_guess = 48 # A common taskbar height, may vary
129
+ mon = monitors[monitor_index]
130
+ return {
131
+ "left": mon["left"],
132
+ "top": mon["top"],
133
+ "width": mon["width"],
134
+ "height": mon["height"] - taskbar_height_guess
135
+ }
136
+ else:
137
+ # For non-Windows systems or unspecified monitors, return the monitor area as-is
138
+ return monitors[monitor_index] if 0 <= monitor_index < len(monitors) else monitors[0]
139
+
97
140
 
98
141
  def _get_full_screenshot(self) -> Tuple[Image.Image | None, int, int]:
99
142
  """Captures a screenshot of the configured monitor."""
100
143
  if not mss:
101
144
  raise RuntimeError("MSS screenshot library is not installed.")
102
-
103
145
  with mss.mss() as sct:
104
- monitors = sct.monitors
105
- # Index 0 is the 'all monitors' virtual screen, so we skip it.
106
- monitor_list = monitors[1:] if len(monitors) > 1 else [monitors[0]]
107
-
108
- monitor_index = self.config.overlay.monitor_to_capture
109
- if monitor_index >= len(monitor_list):
110
- logger.error(f"Monitor index {monitor_index} is out of bounds. Found {len(monitor_list)} monitors.")
111
- return None, 0, 0
112
-
113
- monitor = monitor_list[monitor_index]
146
+ monitor = self.get_monitor_workarea(0) # Get primary monitor work area
114
147
  sct_img = sct.grab(monitor)
115
148
  img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
116
-
149
+
117
150
  return img, monitor['width'], monitor['height']
118
151
 
119
152
  def _create_composite_image(
@@ -210,7 +243,8 @@ class OverlayProcessor:
210
243
  crop_x=0,
211
244
  crop_y=0,
212
245
  crop_width=composite_image.width,
213
- crop_height=composite_image.height
246
+ crop_height=composite_image.height,
247
+ use_percentages=True
214
248
  )
215
249
 
216
250
  return extracted_data
@@ -223,7 +257,8 @@ class OverlayProcessor:
223
257
  crop_x: int,
224
258
  crop_y: int,
225
259
  crop_width: int,
226
- crop_height: int
260
+ crop_height: int,
261
+ use_percentages: bool
227
262
  ) -> List[Dict[str, Any]]:
228
263
  """
229
264
  Parses Google Lens API response and converts normalized coordinates
@@ -244,9 +279,10 @@ class OverlayProcessor:
244
279
  word_text = word.get("plain_text", "")
245
280
  line_text_parts.append(word_text)
246
281
 
247
- word_box = self._convert_box_to_pixels_v2(
282
+ word_box = self._convert_box_to_overlay_coords(
248
283
  word["geometry"]["bounding_box"],
249
- crop_x, crop_y, crop_width, crop_height
284
+ crop_x, crop_y, crop_width, crop_height,
285
+ use_percentage=use_percentages
250
286
  )
251
287
 
252
288
  word_list.append({
@@ -258,9 +294,9 @@ class OverlayProcessor:
258
294
  continue
259
295
 
260
296
  full_line_text = "".join(line_text_parts)
261
- line_box = self._convert_box_to_pixels_v2(
297
+ line_box = self._convert_box_to_overlay_coords(
262
298
  line["geometry"]["bounding_box"],
263
- crop_x, crop_y, crop_width, crop_height
299
+ crop_x, crop_y, crop_width, crop_height, use_percentage=use_percentages
264
300
  )
265
301
 
266
302
  results.append({
@@ -270,36 +306,45 @@ class OverlayProcessor:
270
306
  })
271
307
  return results
272
308
 
273
- def _convert_box_to_pixels_v2(
309
+ def _convert_box_to_overlay_coords(
274
310
  self,
275
311
  bbox_data: Dict[str, float],
276
312
  crop_x: int,
277
313
  crop_y: int,
278
314
  crop_width: int,
279
- crop_height: int
315
+ crop_height: int,
316
+ use_percentage: bool
280
317
  ) -> Dict[str, float]:
281
318
  """
282
319
  Simplified conversion: scales normalized bbox to pixel coordinates within
283
320
  the cropped region, then offsets by the crop position. Ignores rotation.
321
+ If use_percentage is True, returns coordinates as percentages of the crop dimensions.
284
322
  """
285
323
  cx, cy = bbox_data['center_x'], bbox_data['center_y']
286
324
  w, h = bbox_data['width'], bbox_data['height']
287
325
 
288
- # Scale normalized coordinates to pixel coordinates relative to the crop area
289
- box_width_px = w * crop_width
290
- box_height_px = h * crop_height
291
-
292
- # Calculate center within the cropped area and then add the crop offset
293
- center_x_px = (cx * crop_width) + crop_x
294
- center_y_px = (cy * crop_height) + crop_y
326
+ if use_percentage:
327
+ # Return coordinates as percentages of the crop dimensions
328
+ box_width = w
329
+ box_height = h
330
+ center_x = cx
331
+ center_y = cy
332
+ else:
333
+ # Scale normalized coordinates to pixel coordinates relative to the crop area
334
+ box_width = w * crop_width
335
+ box_height = h * crop_height
336
+
337
+ # Calculate center within the cropped area and then add the crop offset
338
+ center_x = (cx * crop_width) + crop_x
339
+ center_y = (cy * crop_height) + crop_y
295
340
 
296
341
  # Calculate corners (unrotated)
297
- half_w_px, half_h_px = box_width_px / 2, box_height_px / 2
342
+ half_w, half_h = box_width / 2, box_height / 2
298
343
  return {
299
- "x1": center_x_px - half_w_px, "y1": center_y_px - half_h_px,
300
- "x2": center_x_px + half_w_px, "y2": center_y_px - half_h_px,
301
- "x3": center_x_px + half_w_px, "y3": center_y_px + half_h_px,
302
- "x4": center_x_px - half_w_px, "y4": center_y_px + half_h_px,
344
+ "x1": center_x - half_w, "y1": center_y - half_h,
345
+ "x2": center_x + half_w, "y2": center_y - half_h,
346
+ "x3": center_x + half_w, "y3": center_y + half_h,
347
+ "x4": center_x - half_w, "y4": center_y + half_h,
303
348
  }
304
349
 
305
350
  async def main_test_screenshot():
@@ -334,22 +379,10 @@ async def main_run_ocr():
334
379
  """
335
380
  Main function to demonstrate running the full OCR process.
336
381
  """
337
- processor = OverlayProcessor()
338
- results, _ = await processor.find_box_for_sentence()
339
- if results:
340
- import json
341
- print("OCR process completed successfully.")
342
- # print(json.dumps(results, indent=2, ensure_ascii=False))
343
- # Find first result with some text
344
- for result in results:
345
- if result.get("text"):
346
- print(f"Found line: '{result['text']}'")
347
- print(f" - Line BBox: {result['bounding_rect']}")
348
- if result.get("words"):
349
- print(f" - First word: '{result['words'][0]['text']}' BBox: {result['words'][0]['bounding_rect']}")
350
- break
351
- else:
352
- print("OCR process did not find any text.")
382
+ overlay_processor = OverlayProcessor()
383
+ while True:
384
+ await overlay_processor.find_box_and_send_to_overlay('')
385
+ await asyncio.sleep(10)
353
386
 
354
387
 
355
388
  if __name__ == '__main__':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.14.21
3
+ Version: 2.15.1
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
@@ -57,7 +57,7 @@ An application designed to assist with language learning through games.
57
57
 
58
58
  Short Demo (Watch this first): https://www.youtube.com/watch?v=FeFBL7py6HY
59
59
 
60
- Installation: https://youtu.be/h5ksXallc-o
60
+ Installation: https://www.youtube.com/watch?v=sVL9omRbGc4
61
61
 
62
62
  Discord: https://discord.gg/yP8Qse6bb8
63
63
 
@@ -1,9 +1,9 @@
1
1
  GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  GameSentenceMiner/anki.py,sha256=4Tq6OGjfN-5tYorYRWiih7FZjSKMG6amrLv6DFKkFQc,25344
3
- GameSentenceMiner/config_gui.py,sha256=SDZ4TEkdhvXxOM_pEG1LABt12M1QkKjMHcQX7bG0OAs,139519
3
+ GameSentenceMiner/config_gui.py,sha256=G36MLR1UcdiLB6V3T25_ggGVnt5B4Vj1pS76nQQMqpk,139669
4
4
  GameSentenceMiner/gametext.py,sha256=fgBgLchezpauWELE9Y5G3kVCLfAneD0X4lJFoI3FYbs,10351
5
5
  GameSentenceMiner/gsm.py,sha256=4mJn5v4WKqKAJEtph5e0v4YPVDOpvFN1ylV2vQvf_Dg,31913
6
- GameSentenceMiner/obs.py,sha256=oUeQgspkdk2iVTZ3Mi7mDJxAJMYbeKQCf2HviPTl3Hs,23579
6
+ GameSentenceMiner/obs.py,sha256=pr3n2Z9S80vYlwaYi516iwQL4DZKLx6owQofbwUMPnQ,25837
7
7
  GameSentenceMiner/vad.py,sha256=YCn4ZIc6_Q3IGOr5QNMiheVT3Ma5nisn8-V8xD53Mw4,19236
8
8
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  GameSentenceMiner/ai/ai_prompting.py,sha256=41xdBzE88Jlt12A0D-T_cMfLO5j6MSxfniOptpwNZm0,24068
@@ -19,17 +19,17 @@ GameSentenceMiner/locales/en_us.json,sha256=FAnvsCfsFzWyxYKZKh8HKHsAahi3Oa4wGVek
19
19
  GameSentenceMiner/locales/ja_jp.json,sha256=-v0ng0psD88-C4XjYazJL0Rn0gwQU7b2VYspvdatDO4,28326
20
20
  GameSentenceMiner/locales/zh_cn.json,sha256=X5nw6tsu7ACaZIuSUDSUUjG8qPUwmqyG3TKcPbWSIYw,24654
21
21
  GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=QyBarjb6Wjt-oyor1sIT9hG-KrGdi6wODLyCrHXun6E,6008
22
+ GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=DfcR3bHTu26JJerLzqfW_KpdgUBSrRV4hqSy_LYclps,5967
23
23
  GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
24
24
  GameSentenceMiner/ocr/owocr_area_selector.py,sha256=Rm1_nuZotJhfOfoJ_3mesh9udtOBjYqKhnAvSief6fo,29181
25
- GameSentenceMiner/ocr/owocr_helper.py,sha256=vquG3O1-2LdAuSt9yQ5hFzCv2E5rDnfqw7eaY33zsYM,28502
25
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=8aIpAHSPPByox5qcU4SX-_ECDgQZVEwrXdj4A8AQZ6U,28437
26
26
  GameSentenceMiner/ocr/ss_picker.py,sha256=0IhxUdaKruFpZyBL-8SpxWg7bPrlGpy3lhTcMMZ5rwo,5224
27
27
  GameSentenceMiner/owocr/owocr/__init__.py,sha256=87hfN5u_PbL_onLfMACbc0F5j4KyIK9lKnRCj6oZgR0,49
28
28
  GameSentenceMiner/owocr/owocr/__main__.py,sha256=XQaqZY99EKoCpU-gWQjNbTs7Kg17HvBVE7JY8LqIE0o,157
29
29
  GameSentenceMiner/owocr/owocr/config.py,sha256=qM7kISHdUhuygGXOxmgU6Ef2nwBShrZtdqu4InDCViE,8103
30
30
  GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
31
- GameSentenceMiner/owocr/owocr/ocr.py,sha256=kyRDBbid7HmZ3giHcuEISEOSjRL8vDeaN9eMSfxe32I,71002
32
- GameSentenceMiner/owocr/owocr/run.py,sha256=6CU5R3MM4tTHh1vpec6tSZ5I90D22KUOg7XrR-ywm2Q,76155
31
+ GameSentenceMiner/owocr/owocr/ocr.py,sha256=rJKmB8xtSVrayq8XcZ4JKBULyPKDqfJDGKgLjICbUX8,71021
32
+ GameSentenceMiner/owocr/owocr/run.py,sha256=xbBpyFCVfITZDztsRLT8_sX6BGf1o5LxOPxE9zUWfQc,79975
33
33
  GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
34
34
  GameSentenceMiner/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  GameSentenceMiner/tools/audio_offset_selector.py,sha256=8Stk3BP-XVIuzRv9nl9Eqd2D-1yD3JrgU-CamBywJmY,8542
@@ -41,7 +41,7 @@ GameSentenceMiner/util/configuration.py,sha256=JVwaqvfrUrOUiA0kZcznDsCo9hJkJqBpV
41
41
  GameSentenceMiner/util/db.py,sha256=2bO0rD4i8A1hhsRBER-wgZy9IK17ibRbI8DHxdKvYsI,16598
42
42
  GameSentenceMiner/util/electron_config.py,sha256=KfeJToeFFVw0IR5MKa-gBzpzaGrU-lyJbR9z-sDEHYU,8767
43
43
  GameSentenceMiner/util/ffmpeg.py,sha256=jA-cFtCmdCWrUSPpdtFSLr-GSoqs4qNUzW20v4HPHf0,28715
44
- GameSentenceMiner/util/get_overlay_coords.py,sha256=VtrT25DOjqMgo-eRFRMovuq6LUnoY8GrpbkfDsvgSjM,13855
44
+ GameSentenceMiner/util/get_overlay_coords.py,sha256=bSvBSvLFmABogPp-quXcQrN-meWvl5NRB6gFEluoNNg,15142
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
@@ -72,9 +72,9 @@ GameSentenceMiner/web/templates/index.html,sha256=LqXZx7-NE42pXSpHNZ3To680rD-vt9
72
72
  GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
73
73
  GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
74
74
  GameSentenceMiner/wip/__init___.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- gamesentenceminer-2.14.21.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
76
- gamesentenceminer-2.14.21.dist-info/METADATA,sha256=G9vL0piNQqJcNMhTzcRHYT0DOUzJS_b3cujW0jldaXU,7303
77
- gamesentenceminer-2.14.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
78
- gamesentenceminer-2.14.21.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
79
- gamesentenceminer-2.14.21.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
80
- gamesentenceminer-2.14.21.dist-info/RECORD,,
75
+ gamesentenceminer-2.15.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
76
+ gamesentenceminer-2.15.1.dist-info/METADATA,sha256=bGgKYTzm7t0Ko5S8johUdqLaSDJrqtmiFSrkjiwd1wk,7317
77
+ gamesentenceminer-2.15.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
78
+ gamesentenceminer-2.15.1.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
79
+ gamesentenceminer-2.15.1.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
80
+ gamesentenceminer-2.15.1.dist-info/RECORD,,