GameSentenceMiner 2.10.12__py3-none-any.whl → 2.10.14__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/obs.py CHANGED
@@ -135,7 +135,7 @@ def get_obs_websocket_config_values():
135
135
  full_config.save()
136
136
  reload_config()
137
137
 
138
- async def connect_to_obs(retry_count=0):
138
+ async def connect_to_obs(retry=5):
139
139
  global client, obs_connection_manager, event_client, connecting
140
140
  if not get_config().obs.enabled:
141
141
  return
@@ -166,11 +166,18 @@ async def connect_to_obs(retry_count=0):
166
166
  update_current_game()
167
167
  break # Exit the loop once connected
168
168
  except Exception as e:
169
+ if retry <= 0:
170
+ gsm_status.obs_connected = False
171
+ logger.error(f"Failed to connect to OBS WebSocket: {e}")
172
+ client = None
173
+ event_client = None
174
+ connecting = False
175
+ break
169
176
  await asyncio.sleep(1)
170
- retry_count += 1
177
+ retry -= 1
171
178
  connecting = False
172
179
 
173
- def connect_to_obs_sync(retry_count=0):
180
+ def connect_to_obs_sync(retry=2):
174
181
  global client, obs_connection_manager, event_client
175
182
  if not get_config().obs.enabled or client:
176
183
  return
@@ -198,8 +205,15 @@ def connect_to_obs_sync(retry_count=0):
198
205
  update_current_game()
199
206
  break # Exit the loop once connected
200
207
  except Exception as e:
208
+ if retry <= 0:
209
+ gsm_status.obs_connected = False
210
+ logger.error(f"Failed to connect to OBS WebSocket: {e}")
211
+ client = None
212
+ event_client = None
213
+ connecting = False
214
+ break
201
215
  time.sleep(1)
202
- retry_count += 1
216
+ retry -= 1
203
217
 
204
218
 
205
219
  def disconnect_from_obs():
@@ -34,7 +34,7 @@ COORD_SYSTEM_PERCENTAGE = "percentage"
34
34
 
35
35
 
36
36
  class ScreenSelector:
37
- def __init__(self, result, window_name):
37
+ def __init__(self, result, window_name, use_window_as_config):
38
38
  if not selector_available or not gw:
39
39
  raise RuntimeError("tkinter or pygetwindow is not available.")
40
40
  if not window_name:
@@ -60,6 +60,8 @@ class ScreenSelector:
60
60
  # ---
61
61
 
62
62
  self.root = None
63
+ self.scene = ''
64
+ self.use_window_as_config = use_window_as_config
63
65
  self.result = result
64
66
  self.rectangles = [] # Internal storage is ALWAYS absolute pixels for drawing
65
67
  self.drawn_rect_ids = []
@@ -94,11 +96,14 @@ class ScreenSelector:
94
96
  ocr_config_dir = app_dir / "ocr_config"
95
97
  ocr_config_dir.mkdir(parents=True, exist_ok=True)
96
98
  try:
97
- scene = sanitize_filename(obs.get_current_scene() or "default_scene")
99
+ if self.use_window_as_config:
100
+ self.scene = sanitize_filename(self.window_name)
101
+ else:
102
+ self.scene = sanitize_filename(obs.get_current_scene() or "")
98
103
  except Exception as e:
99
104
  print(f"Error getting OBS scene: {e}. Using default config name.")
100
- scene = "default_scene"
101
- return ocr_config_dir / f"{scene}.json"
105
+ self.scene = ""
106
+ return ocr_config_dir / f"{self.scene}.json"
102
107
 
103
108
  def load_existing_rectangles(self):
104
109
  """Loads rectangles from config, converting from percentage to absolute pixels for use."""
@@ -170,7 +175,7 @@ class ScreenSelector:
170
175
  })
171
176
 
172
177
  save_data = {
173
- "scene": obs.get_current_scene() or "default_scene",
178
+ "scene": self.scene or "",
174
179
  "window": self.window_name,
175
180
  "coordinate_system": COORD_SYSTEM_PERCENTAGE, # Always save as percentage
176
181
  "window_geometry": win_geom, # Save the geometry used for conversion
@@ -397,9 +402,9 @@ class ScreenSelector:
397
402
  self.root = None
398
403
 
399
404
 
400
- def run_screen_selector(result_dict, window_name):
405
+ def run_screen_selector(result_dict, window_name, use_window_as_config):
401
406
  try:
402
- selector = ScreenSelector(result_dict, window_name)
407
+ selector = ScreenSelector(result_dict, window_name, use_window_as_config)
403
408
  selector.start()
404
409
  except Exception as e:
405
410
  print(f"Error in selector process: {e}", file=sys.stderr)
@@ -408,7 +413,7 @@ def run_screen_selector(result_dict, window_name):
408
413
  result_dict['error'] = str(e)
409
414
 
410
415
 
411
- def get_screen_selection(window_name):
416
+ def get_screen_selection(window_name, use_window_as_config=False):
412
417
  if not selector_available or not gw: return None
413
418
  if not window_name:
414
419
  print("Error: A target window name must be provided.", file=sys.stderr)
@@ -416,7 +421,7 @@ def get_screen_selection(window_name):
416
421
 
417
422
  with Manager() as manager:
418
423
  result_data = manager.dict()
419
- process = Process(target=run_screen_selector, args=(result_data, window_name))
424
+ process = Process(target=run_screen_selector, args=(result_data, window_name, use_window_as_config))
420
425
  print(f"Starting ScreenSelector process...")
421
426
  process.start()
422
427
  process.join()
@@ -434,11 +439,15 @@ def get_screen_selection(window_name):
434
439
 
435
440
  if __name__ == "__main__":
436
441
  set_dpi_awareness()
437
- target_window_title = "Windowed Projector (Preview)" # Default
442
+ target_window_title = "YouTube - JP"
443
+ use_window_as_config = False
438
444
  if len(sys.argv) > 1:
439
445
  target_window_title = sys.argv[1]
446
+ if len(sys.argv) > 2:
447
+ use_window_as_config = True
448
+ target_window_title = sys.argv[1]
440
449
 
441
- selection_result = get_screen_selection(target_window_title)
450
+ selection_result = get_screen_selection(target_window_title, use_window_as_config)
442
451
 
443
452
  if selection_result is None:
444
453
  print("\n--- Screen selection failed. ---")
@@ -47,13 +47,16 @@ console_handler.setFormatter(formatter)
47
47
  logger.addHandler(console_handler)
48
48
 
49
49
 
50
- def get_ocr_config(window=None) -> OCRConfig:
50
+ def get_ocr_config(window=None, use_window_for_config=False) -> OCRConfig:
51
51
  """Loads and updates screen capture areas from the corresponding JSON file."""
52
52
  app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
53
53
  ocr_config_dir = app_dir / "ocr_config"
54
54
  os.makedirs(ocr_config_dir, exist_ok=True)
55
- obs.connect_to_obs_sync()
56
- scene = sanitize_filename(obs.get_current_scene())
55
+ obs.connect_to_obs_sync(retry=0)
56
+ if use_window_for_config and window:
57
+ scene = sanitize_filename(window)
58
+ else:
59
+ scene = sanitize_filename(obs.get_current_scene())
57
60
  config_path = ocr_config_dir / f"{scene}.json"
58
61
  if not config_path.exists():
59
62
  ocr_config = OCRConfig(scene=scene, window=window, rectangles=[], coordinate_system="percentage")
@@ -401,7 +404,7 @@ def set_force_stable_hotkey():
401
404
 
402
405
  if __name__ == "__main__":
403
406
  try:
404
- global ocr1, ocr2, twopassocr, language, ss_clipboard, ss, ocr_config, furigana_filter_sensitivity, area_select_ocr_hotkey, window, optimize_second_scan
407
+ global ocr1, ocr2, twopassocr, language, ss_clipboard, ss, ocr_config, furigana_filter_sensitivity, area_select_ocr_hotkey, window, optimize_second_scan, use_window_for_config
405
408
  import sys
406
409
 
407
410
  import argparse
@@ -423,6 +426,8 @@ if __name__ == "__main__":
423
426
  help="Hotkey for area selection OCR (default: ctrl+shift+o)")
424
427
  parser.add_argument("--optimize_second_scan", action="store_true",
425
428
  help="Optimize second scan by cropping based on first scan results")
429
+ parser.add_argument("--use_window_for_config", action="store_true",
430
+ help="Use the specified window for loading OCR configuration")
426
431
 
427
432
  args = parser.parse_args()
428
433
 
@@ -440,11 +445,12 @@ if __name__ == "__main__":
440
445
  "alt", "<alt>") if args.manual_ocr_hotkey else None
441
446
  clipboard_output = args.clipboard_output
442
447
  optimize_second_scan = args.optimize_second_scan
448
+ use_window_for_config = args.use_window_for_config
443
449
 
444
450
  window = None
445
451
  logger.info(f"Received arguments: {vars(args)}")
446
452
  # set_force_stable_hotkey()
447
- ocr_config: OCRConfig = get_ocr_config(window=window_name)
453
+ ocr_config: OCRConfig = get_ocr_config(window=window_name, use_window_for_config=use_window_for_config)
448
454
  if ocr_config:
449
455
  if ocr_config.window:
450
456
  start_time = time.time()
@@ -18,6 +18,8 @@ from google.generativeai import GenerationConfig
18
18
  from loguru import logger
19
19
  import requests
20
20
 
21
+ # from GameSentenceMiner.util.configuration import get_temporary_directory
22
+
21
23
  try:
22
24
  from manga_ocr import MangaOcr as MOCR
23
25
  except ImportError:
@@ -77,7 +79,6 @@ try:
77
79
  from GameSentenceMiner.owocr.owocr.lens_betterproto import *
78
80
  import random
79
81
  except ImportError:
80
- print('Google Lens not available, please install betterproto package!')
81
82
  pass
82
83
 
83
84
  try:
@@ -100,11 +101,13 @@ def post_process(text):
100
101
 
101
102
 
102
103
  def input_to_pil_image(img):
104
+ is_path = False
103
105
  if isinstance(img, Image.Image):
104
106
  pil_image = img
105
107
  elif isinstance(img, (bytes, bytearray)):
106
108
  pil_image = Image.open(io.BytesIO(img))
107
109
  elif isinstance(img, Path):
110
+ is_path = True
108
111
  try:
109
112
  pil_image = Image.open(img)
110
113
  pil_image.load()
@@ -112,7 +115,7 @@ def input_to_pil_image(img):
112
115
  return None
113
116
  else:
114
117
  raise ValueError(f'img must be a path, PIL.Image or bytes object, instead got: {img}')
115
- return pil_image
118
+ return pil_image, is_path
116
119
 
117
120
 
118
121
  def pil_image_to_bytes(img, img_format='png', png_compression=6, jpeg_quality=80, optimize=False):
@@ -163,7 +166,7 @@ class MangaOcr:
163
166
  key = 'm'
164
167
  available = False
165
168
 
166
- def __init__(self, config={'pretrained_model_name_or_path':'kha-white/manga-ocr-base','force_cpu': False}):
169
+ def __init__(self, config={'pretrained_model_name_or_path':'kha-white/manga-ocr-base','force_cpu': False}, lang='ja'):
167
170
  if 'manga_ocr' not in sys.modules:
168
171
  logger.warning('manga-ocr not available, Manga OCR will not work!')
169
172
  else:
@@ -177,7 +180,7 @@ class MangaOcr:
177
180
  logger.info('Manga OCR ready')
178
181
 
179
182
  def __call__(self, img, furigana_filter_sensitivity=0):
180
- img = input_to_pil_image(img)
183
+ img, is_path = input_to_pil_image(img)
181
184
  if not img:
182
185
  return (False, 'Invalid image provided')
183
186
 
@@ -192,7 +195,7 @@ class GoogleVision:
192
195
  key = 'g'
193
196
  available = False
194
197
 
195
- def __init__(self):
198
+ def __init__(self, lang='ja'):
196
199
  if 'google.cloud' not in sys.modules:
197
200
  logger.warning('google-cloud-vision not available, Google Vision will not work!')
198
201
  else:
@@ -207,7 +210,7 @@ class GoogleVision:
207
210
  logger.warning('Error parsing Google credentials, Google Vision will not work!')
208
211
 
209
212
  def __call__(self, img, furigana_filter_sensitivity=0):
210
- img = input_to_pil_image(img)
213
+ img, is_path = input_to_pil_image(img)
211
214
  if not img:
212
215
  return (False, 'Invalid image provided')
213
216
 
@@ -235,7 +238,7 @@ class GoogleLens:
235
238
  key = 'l'
236
239
  available = False
237
240
 
238
- def __init__(self):
241
+ def __init__(self, lang='ja'):
239
242
  self.kana_kanji_regex = re.compile(r'[\u3041-\u3096\u30A1-\u30FA\u4E00-\u9FFF]')
240
243
  if 'betterproto' not in sys.modules:
241
244
  logger.warning('betterproto not available, Google Lens will not work!')
@@ -244,7 +247,7 @@ class GoogleLens:
244
247
  logger.info('Google Lens ready')
245
248
 
246
249
  def __call__(self, img, furigana_filter_sensitivity=0):
247
- img = input_to_pil_image(img)
250
+ img, is_path = input_to_pil_image(img)
248
251
  if not img:
249
252
  return (False, 'Invalid image provided')
250
253
 
@@ -369,9 +372,7 @@ class GoogleLens:
369
372
  aspect_ratio = img.width / img.height
370
373
  new_w = int(sqrt(3000000 * aspect_ratio))
371
374
  new_h = int(new_w / aspect_ratio)
372
- img_resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
373
- # img.close()
374
- img = img_resized
375
+ img = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
375
376
 
376
377
  return (pil_image_to_bytes(img), img.width, img.height)
377
378
 
@@ -381,7 +382,7 @@ class GoogleLensWeb:
381
382
  key = 'k'
382
383
  available = False
383
384
 
384
- def __init__(self):
385
+ def __init__(self, lang='ja'):
385
386
  if 'pyjson5' not in sys.modules:
386
387
  logger.warning('pyjson5 not available, Google Lens (web) will not work!')
387
388
  else:
@@ -390,7 +391,7 @@ class GoogleLensWeb:
390
391
  logger.info('Google Lens (web) ready')
391
392
 
392
393
  def __call__(self, img, furigana_filter_sensitivity=0):
393
- img = input_to_pil_image(img)
394
+ img, is_path = input_to_pil_image(img)
394
395
  if not img:
395
396
  return (False, 'Invalid image provided')
396
397
 
@@ -465,9 +466,7 @@ class GoogleLensWeb:
465
466
  aspect_ratio = img.width / img.height
466
467
  new_w = int(sqrt(3000000 * aspect_ratio))
467
468
  new_h = int(new_w / aspect_ratio)
468
- img_resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
469
- # img.close()
470
- img = img_resized
469
+ img = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
471
470
 
472
471
  return pil_image_to_bytes(img)
473
472
 
@@ -477,13 +476,13 @@ class Bing:
477
476
  key = 'b'
478
477
  available = False
479
478
 
480
- def __init__(self):
479
+ def __init__(self, lang='ja'):
481
480
  self.requests_session = requests.Session()
482
481
  self.available = True
483
482
  logger.info('Bing ready')
484
483
 
485
484
  def __call__(self, img, furigana_filter_sensitivity=0):
486
- img = input_to_pil_image(img)
485
+ img, is_path = input_to_pil_image(img)
487
486
  if not img:
488
487
  return (False, 'Invalid image provided')
489
488
 
@@ -544,7 +543,7 @@ class Bing:
544
543
  'imageInfo': {'imageInsightsToken': image_insights_token, 'source': 'Url'},
545
544
  'knowledgeRequest': {'invokedSkills': ['OCR'], 'index': 1}
546
545
  }
547
- files = {
546
+ files = {
548
547
  'knowledgeRequest': (None, json.dumps(api_data_json), 'application/json')
549
548
  }
550
549
 
@@ -577,7 +576,7 @@ class Bing:
577
576
  for region in regions:
578
577
  for line in region.get('lines', []):
579
578
  res += line['text'] + '\n'
580
-
579
+
581
580
  x = (True, res)
582
581
 
583
582
  # img.close()
@@ -592,9 +591,7 @@ class Bing:
592
591
  resize_factor = max(max_pixel_size / img.width, max_pixel_size / img.height)
593
592
  new_w = int(img.width * resize_factor)
594
593
  new_h = int(img.height * resize_factor)
595
- img_resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
596
- # img.close()
597
- img = img_resized
594
+ img = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
598
595
 
599
596
  img_bytes, _ = limit_image_size(img, max_byte_size)
600
597
 
@@ -609,7 +606,7 @@ class AppleVision:
609
606
  key = 'a'
610
607
  available = False
611
608
 
612
- def __init__(self):
609
+ def __init__(self, lang='ja'):
613
610
  if sys.platform != 'darwin':
614
611
  logger.warning('Apple Vision is not supported on non-macOS platforms!')
615
612
  elif int(platform.mac_ver()[0].split('.')[0]) < 13:
@@ -619,7 +616,7 @@ class AppleVision:
619
616
  logger.info('Apple Vision ready')
620
617
 
621
618
  def __call__(self, img, furigana_filter_sensitivity=0):
622
- img = input_to_pil_image(img)
619
+ img, is_path = input_to_pil_image(img)
623
620
  if not img:
624
621
  return (False, 'Invalid image provided')
625
622
 
@@ -657,7 +654,7 @@ class AppleLiveText:
657
654
  key = 'd'
658
655
  available = False
659
656
 
660
- def __init__(self):
657
+ def __init__(self, lang='ja'):
661
658
  if sys.platform != 'darwin':
662
659
  logger.warning('Apple Live Text is not supported on non-macOS platforms!')
663
660
  elif int(platform.mac_ver()[0].split('.')[0]) < 13:
@@ -698,7 +695,7 @@ class AppleLiveText:
698
695
  logger.info('Apple Live Text ready')
699
696
 
700
697
  def __call__(self, img, furigana_filter_sensitivity=0):
701
- img = input_to_pil_image(img)
698
+ img, is_path = input_to_pil_image(img)
702
699
  if not img:
703
700
  return (False, 'Invalid image provided')
704
701
 
@@ -737,7 +734,7 @@ class WinRTOCR:
737
734
  key = 'w'
738
735
  available = False
739
736
 
740
- def __init__(self, config={}):
737
+ def __init__(self, config={}, lang='ja'):
741
738
  if sys.platform == 'win32':
742
739
  if int(platform.release()) < 10:
743
740
  logger.warning('WinRT OCR is not supported on Windows older than 10!')
@@ -755,7 +752,7 @@ class WinRTOCR:
755
752
  logger.warning('Error reading URL from config, WinRT OCR will not work!')
756
753
 
757
754
  def __call__(self, img, furigana_filter_sensitivity=0):
758
- img = input_to_pil_image(img)
755
+ img, is_path = input_to_pil_image(img)
759
756
  if not img:
760
757
  return (False, 'Invalid image provided')
761
758
 
@@ -790,7 +787,26 @@ class OneOCR:
790
787
  key = 'z'
791
788
  available = False
792
789
 
793
- def __init__(self, config={}):
790
+ def __init__(self, config={}, lang='ja'):
791
+ if lang == "ja":
792
+ self.regex = re.compile(r'[\u3041-\u3096\u30A1-\u30FA\u4E00-\u9FFF]')
793
+ elif lang == "zh":
794
+ self.regex = re.compile(r'[\u4E00-\u9FFF]')
795
+ elif lang == "ko":
796
+ self.regex = re.compile(r'[\uAC00-\uD7AF]')
797
+ elif lang == "ar":
798
+ self.regex = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
799
+ elif lang == "ru":
800
+ self.regex = re.compile(r'[\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u1C80-\u1C8F]')
801
+ elif lang == "el":
802
+ self.regex = re.compile(r'[\u0370-\u03FF\u1F00-\u1FFF]')
803
+ elif lang == "he":
804
+ self.regex = re.compile(r'[\u0590-\u05FF\uFB1D-\uFB4F]')
805
+ elif lang == "th":
806
+ self.regex = re.compile(r'[\u0E00-\u0E7F]')
807
+ else:
808
+ self.regex = re.compile(
809
+ r'[a-zA-Z\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u1D00-\u1D7F\u1D80-\u1DBF\u1E00-\u1EFF\u2C60-\u2C7F\uA720-\uA7FF\uAB30-\uAB6F]')
794
810
  if sys.platform == 'win32':
795
811
  if int(platform.release()) < 10:
796
812
  logger.warning('OneOCR is not supported on Windows older than 10!')
@@ -813,14 +829,13 @@ class OneOCR:
813
829
  logger.warning('Error reading URL from config, OneOCR will not work!')
814
830
 
815
831
  def __call__(self, img, furigana_filter_sensitivity=0):
816
- img = input_to_pil_image(img)
832
+ img, is_path = input_to_pil_image(img)
817
833
  if img.width < 51 or img.height < 51:
818
834
  new_width = max(img.width, 51)
819
835
  new_height = max(img.height, 51)
820
836
  new_img = Image.new("RGBA", (new_width, new_height), (0, 0, 0, 0))
821
837
  new_img.paste(img, ((new_width - img.width) // 2, (new_height - img.height) // 2))
822
838
  img = new_img
823
-
824
839
  if not img:
825
840
  return (False, 'Invalid image provided')
826
841
  crop_coords = None
@@ -828,8 +843,9 @@ class OneOCR:
828
843
  try:
829
844
  ocr_resp = self.model.recognize_pil(img)
830
845
  # print(json.dumps(ocr_resp))
831
- x_coords = [line['bounding_rect'][f'x{i}'] for line in ocr_resp['lines'] for i in range(1, 5)]
832
- y_coords = [line['bounding_rect'][f'y{i}'] for line in ocr_resp['lines'] for i in range(1, 5)]
846
+ filtered_lines = [line for line in ocr_resp['lines'] if self.regex.search(line['text'])]
847
+ x_coords = [line['bounding_rect'][f'x{i}'] for line in filtered_lines for i in range(1, 5)]
848
+ y_coords = [line['bounding_rect'][f'y{i}'] for line in filtered_lines for i in range(1, 5)]
833
849
  if x_coords and y_coords:
834
850
  crop_coords = (min(x_coords) - 5, min(y_coords) - 5, max(x_coords) + 5, max(y_coords) + 5)
835
851
 
@@ -902,7 +918,6 @@ class OneOCR:
902
918
  if res.status_code != 200:
903
919
  return (False, 'Unknown error!')
904
920
 
905
-
906
921
  res = res.json()['text']
907
922
 
908
923
  x = (True, res, crop_coords)
@@ -919,7 +934,7 @@ class AzureImageAnalysis:
919
934
  key = 'v'
920
935
  available = False
921
936
 
922
- def __init__(self, config={}):
937
+ def __init__(self, config={}, lang='ja'):
923
938
  if 'azure.ai.vision.imageanalysis' not in sys.modules:
924
939
  logger.warning('azure-ai-vision-imageanalysis not available, Azure Image Analysis will not work!')
925
940
  else:
@@ -932,7 +947,7 @@ class AzureImageAnalysis:
932
947
  logger.warning('Error parsing Azure credentials, Azure Image Analysis will not work!')
933
948
 
934
949
  def __call__(self, img, furigana_filter_sensitivity=0):
935
- img = input_to_pil_image(img)
950
+ img, is_path = input_to_pil_image(img)
936
951
  if not img:
937
952
  return (False, 'Invalid image provided')
938
953
 
@@ -961,9 +976,7 @@ class AzureImageAnalysis:
961
976
  resize_factor = max(50 / img.width, 50 / img.height)
962
977
  new_w = int(img.width * resize_factor)
963
978
  new_h = int(img.height * resize_factor)
964
- img_resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
965
- # img.close()
966
- img = img_resized
979
+ img = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
967
980
 
968
981
  return pil_image_to_bytes(img)
969
982
 
@@ -973,7 +986,7 @@ class EasyOCR:
973
986
  key = 'e'
974
987
  available = False
975
988
 
976
- def __init__(self, config={'gpu': True}):
989
+ def __init__(self, config={'gpu': True}, lang='ja'):
977
990
  if 'easyocr' not in sys.modules:
978
991
  logger.warning('easyocr not available, EasyOCR will not work!')
979
992
  else:
@@ -984,7 +997,7 @@ class EasyOCR:
984
997
  logger.info('EasyOCR ready')
985
998
 
986
999
  def __call__(self, img, furigana_filter_sensitivity=0):
987
- img = input_to_pil_image(img)
1000
+ img, is_path = input_to_pil_image(img)
988
1001
  if not img:
989
1002
  return (False, 'Invalid image provided')
990
1003
 
@@ -1007,7 +1020,7 @@ class RapidOCR:
1007
1020
  key = 'r'
1008
1021
  available = False
1009
1022
 
1010
- def __init__(self):
1023
+ def __init__(self, lang='ja'):
1011
1024
  if 'rapidocr_onnxruntime' not in sys.modules:
1012
1025
  logger.warning('rapidocr_onnxruntime not available, RapidOCR will not work!')
1013
1026
  else:
@@ -1030,7 +1043,7 @@ class RapidOCR:
1030
1043
  logger.info('RapidOCR ready')
1031
1044
 
1032
1045
  def __call__(self, img, furigana_filter_sensitivity=0):
1033
- img = input_to_pil_image(img)
1046
+ img, is_path = input_to_pil_image(img)
1034
1047
  if not img:
1035
1048
  return (False, 'Invalid image provided')
1036
1049
 
@@ -1054,7 +1067,7 @@ class OCRSpace:
1054
1067
  key = 'o'
1055
1068
  available = False
1056
1069
 
1057
- def __init__(self, config={}):
1070
+ def __init__(self, config={}, lang='ja'):
1058
1071
  try:
1059
1072
  self.api_key = config['api_key']
1060
1073
  self.max_byte_size = config.get('file_size_limit', 1000000)
@@ -1064,7 +1077,7 @@ class OCRSpace:
1064
1077
  logger.warning('Error reading API key from config, OCRSpace will not work!')
1065
1078
 
1066
1079
  def __call__(self, img, furigana_filter_sensitivity=0):
1067
- img = input_to_pil_image(img)
1080
+ img, is_path = input_to_pil_image(img)
1068
1081
  if not img:
1069
1082
  return (False, 'Invalid image provided')
1070
1083
 
@@ -1101,7 +1114,7 @@ class OCRSpace:
1101
1114
  # img.close()
1102
1115
  return x
1103
1116
 
1104
- def _preprocess(self, img):
1117
+ def _preprocess(self, img):
1105
1118
  return limit_image_size(img, self.max_byte_size)
1106
1119
 
1107
1120
 
@@ -1111,7 +1124,7 @@ class GeminiOCR:
1111
1124
  key = 'm'
1112
1125
  available = False
1113
1126
 
1114
- def __init__(self, config={'api_key': None}):
1127
+ def __init__(self, config={'api_key': None}, lang='ja'):
1115
1128
  # if "google-generativeai" not in sys.modules:
1116
1129
  # logger.warning('google-generativeai not available, GeminiOCR will not work!')
1117
1130
  # else:
@@ -1138,7 +1151,7 @@ class GeminiOCR:
1138
1151
  return (False, 'GeminiOCR is not available due to missing API key or configuration error.')
1139
1152
 
1140
1153
  try:
1141
- img = input_to_pil_image(img)
1154
+ img, is_path = input_to_pil_image(img)
1142
1155
  import google.generativeai as genai
1143
1156
  img_bytes = self._preprocess(img)
1144
1157
  if not img_bytes:
@@ -1180,7 +1193,7 @@ class GroqOCR:
1180
1193
  key = 'j'
1181
1194
  available = False
1182
1195
 
1183
- def __init__(self, config={'api_key': None}):
1196
+ def __init__(self, config={'api_key': None}, lang='ja'):
1184
1197
  try:
1185
1198
  import groq
1186
1199
  self.api_key = config['api_key']
@@ -1200,7 +1213,7 @@ class GroqOCR:
1200
1213
  return (False, 'GroqOCR is not available due to missing API key or configuration error.')
1201
1214
 
1202
1215
  try:
1203
- img = input_to_pil_image(img)
1216
+ img, is_path = input_to_pil_image(img)
1204
1217
 
1205
1218
  img_base64 = self._preprocess(img)
1206
1219
  if not img_base64:
@@ -1252,7 +1265,7 @@ class GroqOCR:
1252
1265
  # key = 'q'
1253
1266
  # available = False
1254
1267
  #
1255
- # def __init__(self, config={}):
1268
+ # def __init__(self, config={}, lang='ja'):
1256
1269
  # try:
1257
1270
  # import torch
1258
1271
  # from transformers import Qwen2VLForConditionalGeneration, AutoProcessor
@@ -1314,7 +1327,7 @@ class GroqOCR:
1314
1327
  # key = '-'
1315
1328
  # available = False
1316
1329
  #
1317
- # def __init__(self):
1330
+ # def __init__(self, lang='ja'):
1318
1331
  # self.requests_session = requests.Session()
1319
1332
  # self.available = True
1320
1333
  # # logger.info('Local OCR ready') # Uncomment if you have a logger defined
@@ -373,7 +373,7 @@ class TextFiltering:
373
373
  "fi"]: # Many European languages use extended Latin
374
374
  block_filtered = self.latin_extended_regex.findall(block)
375
375
  else:
376
- block_filtered = self.english_regex.findall(block)
376
+ block_filtered = self.latin_extended_regex.findall(block)
377
377
 
378
378
  if block_filtered:
379
379
  orig_text_filtered.append(''.join(block_filtered))
@@ -414,8 +414,9 @@ class TextFiltering:
414
414
  return text, orig_text_filtered
415
415
 
416
416
 
417
- class ScreenshotClass:
418
- def __init__(self, screen_capture_area, screen_capture_window, screen_capture_exclusions, screen_capture_only_active_windows, screen_capture_areas):
417
+ class ScreenshotThread(threading.Thread):
418
+ def __init__(self, screen_capture_area, screen_capture_window, screen_capture_exclusions, screen_capture_only_active_windows, screen_capture_areas, screen_capture_on_combo):
419
+ super().__init__(daemon=True)
419
420
  self.macos_window_tracker_instance = None
420
421
  self.windows_window_tracker_instance = None
421
422
  self.screencapture_window_active = True
@@ -424,6 +425,7 @@ class ScreenshotClass:
424
425
  self.screen_capture_exclusions = screen_capture_exclusions
425
426
  self.screen_capture_window = screen_capture_window
426
427
  self.areas = []
428
+ self.use_periodic_queue = not screen_capture_on_combo
427
429
  if screen_capture_area == '':
428
430
  self.screencapture_mode = 0
429
431
  elif screen_capture_area.startswith('screen_'):
@@ -441,10 +443,10 @@ class ScreenshotClass:
441
443
  self.screencapture_mode = 2
442
444
 
443
445
  if self.screencapture_mode != 2:
444
- self.sct = mss.mss()
446
+ sct = mss.mss()
445
447
 
446
448
  if self.screencapture_mode == 1:
447
- mon = self.sct.monitors
449
+ mon = sct.monitors
448
450
  if len(mon) <= screen_capture_monitor:
449
451
  raise ValueError('Invalid monitor number in screen_capture_area')
450
452
  coord_left = mon[screen_capture_monitor]['left']
@@ -535,12 +537,6 @@ class ScreenshotClass:
535
537
  else:
536
538
  raise ValueError('Window capture is only currently supported on Windows and macOS')
537
539
 
538
- def __del__(self):
539
- if self.macos_window_tracker_instance:
540
- self.macos_window_tracker_instance.join()
541
- elif self.windows_window_tracker_instance:
542
- self.windows_window_tracker_instance.join()
543
-
544
540
  def get_windows_window_handle(self, window_title):
545
541
  def callback(hwnd, window_title_part):
546
542
  window_title = win32gui.GetWindowText(hwnd)
@@ -647,114 +643,121 @@ class ScreenshotClass:
647
643
  if not found:
648
644
  on_window_closed(False)
649
645
 
650
- def __call__(self):
651
- if self.screencapture_mode == 2 or self.screen_capture_window:
652
- if sys.platform == 'darwin':
653
- with objc.autorelease_pool():
654
- if self.old_macos_screenshot_api:
655
- cg_image = CGWindowListCreateImageFromArray(CGRectNull, [self.window_id], kCGWindowImageBoundsIgnoreFraming)
656
- else:
657
- self.capture_macos_window_screenshot(self.window_id)
658
- try:
659
- cg_image = self.screencapturekit_queue.get(timeout=0.5)
660
- except queue.Empty:
661
- cg_image = None
662
- if not cg_image:
646
+ def write_result(self, result):
647
+ if self.use_periodic_queue:
648
+ periodic_screenshot_queue.put(result)
649
+ else:
650
+ image_queue.put((result, True))
651
+
652
+ def run(self):
653
+ if self.screencapture_mode != 2:
654
+ sct = mss.mss()
655
+ while not terminated:
656
+ if not screenshot_event.wait(timeout=0.1):
657
+ continue
658
+ if self.screencapture_mode == 2 or self.screen_capture_window:
659
+ if sys.platform == 'darwin':
660
+ with objc.autorelease_pool():
661
+ if self.old_macos_screenshot_api:
662
+ cg_image = CGWindowListCreateImageFromArray(CGRectNull, [self.window_id],
663
+ kCGWindowImageBoundsIgnoreFraming)
664
+ else:
665
+ self.capture_macos_window_screenshot(self.window_id)
666
+ try:
667
+ cg_image = self.screencapturekit_queue.get(timeout=0.5)
668
+ except queue.Empty:
669
+ cg_image = None
670
+ if not cg_image:
671
+ return 0
672
+ width = CGImageGetWidth(cg_image)
673
+ height = CGImageGetHeight(cg_image)
674
+ raw_data = CGDataProviderCopyData(CGImageGetDataProvider(cg_image))
675
+ bpr = CGImageGetBytesPerRow(cg_image)
676
+ img = Image.frombuffer('RGBA', (width, height), raw_data, 'raw', 'BGRA', bpr, 1)
677
+ else:
678
+ try:
679
+ coord_left, coord_top, right, bottom = win32gui.GetWindowRect(self.window_handle)
680
+ coord_width = right - coord_left
681
+ coord_height = bottom - coord_top
682
+
683
+ hwnd_dc = win32gui.GetWindowDC(self.window_handle)
684
+ mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
685
+ save_dc = mfc_dc.CreateCompatibleDC()
686
+
687
+ save_bitmap = win32ui.CreateBitmap()
688
+ save_bitmap.CreateCompatibleBitmap(mfc_dc, coord_width, coord_height)
689
+ save_dc.SelectObject(save_bitmap)
690
+
691
+ result = ctypes.windll.user32.PrintWindow(self.window_handle, save_dc.GetSafeHdc(), 2)
692
+
693
+ bmpinfo = save_bitmap.GetInfo()
694
+ bmpstr = save_bitmap.GetBitmapBits(True)
695
+ except pywintypes.error:
663
696
  return 0
664
- width = CGImageGetWidth(cg_image)
665
- height = CGImageGetHeight(cg_image)
666
- raw_data = CGDataProviderCopyData(CGImageGetDataProvider(cg_image))
667
- bpr = CGImageGetBytesPerRow(cg_image)
668
- img = Image.frombuffer('RGBA', (width, height), raw_data, 'raw', 'BGRA', bpr, 1)
697
+ img = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0,
698
+ 1)
699
+ try:
700
+ win32gui.DeleteObject(save_bitmap.GetHandle())
701
+ except:
702
+ pass
703
+ try:
704
+ save_dc.DeleteDC()
705
+ except:
706
+ pass
707
+ try:
708
+ mfc_dc.DeleteDC()
709
+ except:
710
+ pass
711
+ try:
712
+ win32gui.ReleaseDC(self.window_handle, hwnd_dc)
713
+ except:
714
+ pass
669
715
  else:
670
- try:
671
- coord_left, coord_top, right, bottom = win32gui.GetWindowRect(self.window_handle)
672
- coord_width = right - coord_left
673
- coord_height = bottom - coord_top
716
+ sct_img = sct.grab(self.sct_params)
717
+ img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
674
718
 
675
- hwnd_dc = win32gui.GetWindowDC(self.window_handle)
676
- mfc_dc = win32ui.CreateDCFromHandle(hwnd_dc)
677
- save_dc = mfc_dc.CreateCompatibleDC()
719
+ import random # Ensure this is imported at the top of the file if not already
720
+ rand_int = random.randint(1, 20) # Executes only once out of 10 times
678
721
 
679
- save_bitmap = win32ui.CreateBitmap()
680
- save_bitmap.CreateCompatibleBitmap(mfc_dc, coord_width, coord_height)
681
- save_dc.SelectObject(save_bitmap)
722
+ if rand_int == 1: # Executes only once out of 10 times
723
+ img.save(os.path.join(get_temporary_directory(), 'before_crop.png'), 'PNG')
682
724
 
683
- result = ctypes.windll.user32.PrintWindow(self.window_handle, save_dc.GetSafeHdc(), 2)
725
+ if self.screen_capture_exclusions:
726
+ img = img.convert("RGBA")
727
+ draw = ImageDraw.Draw(img)
728
+ for exclusion in self.screen_capture_exclusions:
729
+ left, top, width, height = exclusion
730
+ draw.rectangle((left, top, left + width, top + height), fill=(0, 0, 0, 0))
684
731
 
685
- bmpinfo = save_bitmap.GetInfo()
686
- bmpstr = save_bitmap.GetBitmapBits(True)
687
- except pywintypes.error:
688
- return 0
689
- img = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
690
- try:
691
- win32gui.DeleteObject(save_bitmap.GetHandle())
692
- except:
693
- pass
694
- try:
695
- save_dc.DeleteDC()
696
- except:
697
- pass
698
- try:
699
- mfc_dc.DeleteDC()
700
- except:
701
- pass
702
- try:
703
- win32gui.ReleaseDC(self.window_handle, hwnd_dc)
704
- except:
705
- pass
706
- else:
707
- sct_img = self.sct.grab(self.sct_params)
708
- img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
709
-
710
- import random # Ensure this is imported at the top of the file if not already
711
- rand_int = random.randint(1, 20) # Executes only once out of 10 times
712
-
713
- if rand_int == 1: # Executes only once out of 10 times
714
- img.save(os.path.join(get_temporary_directory(), 'before_crop.png'), 'PNG')
715
-
716
- if self.screen_capture_exclusions:
717
- img = img.convert("RGBA")
718
- draw = ImageDraw.Draw(img)
719
- for exclusion in self.screen_capture_exclusions:
720
- left, top, width, height = exclusion
721
- draw.rectangle((left, top, left + width, top + height), fill=(0, 0, 0, 0))
722
-
723
- cropped_sections = []
724
- start = time.time()
725
- for area in self.areas:
726
- cropped_sections.append(img.crop((area[0], area[1], area[0] + area[2], area[1] + area[3])))
727
-
728
- # if len(cropped_sections) > 1:
729
- # combined_width = sum(section.width for section in cropped_sections)
730
- # combined_height = max(section.height for section in cropped_sections)
731
- # combined_img = Image.new("RGBA", (combined_width, combined_height))
732
- #
733
- # x_offset = 0
734
- # for section in cropped_sections:
735
- # combined_img.paste(section, (x_offset, 0))
736
- # x_offset += section.width
737
- #
738
- # img = combined_img
739
- if len(cropped_sections) > 1:
740
- combined_width = max(section.width for section in cropped_sections)
741
- combined_height = sum(section.height for section in cropped_sections) + (len(cropped_sections) - 1) * 10 # Add space for gaps
742
- combined_img = Image.new("RGBA", (combined_width, combined_height))
743
-
744
- y_offset = 0
745
- for section in cropped_sections:
746
- combined_img.paste(section, (0, y_offset))
747
- y_offset += section.height + 50 # Add gap between sections
748
-
749
- img = combined_img
750
- elif cropped_sections:
751
- img = cropped_sections[0]
752
-
753
- if rand_int == 1:
754
- img.save(os.path.join(get_temporary_directory(), 'after_crop.png'), 'PNG')
755
-
756
- return img
732
+ cropped_sections = []
733
+ for area in self.areas:
734
+ cropped_sections.append(img.crop((area[0], area[1], area[0] + area[2], area[1] + area[3])))
735
+
736
+ if len(cropped_sections) > 1:
737
+ combined_width = max(section.width for section in cropped_sections)
738
+ combined_height = sum(section.height for section in cropped_sections) + (
739
+ len(cropped_sections) - 1) * 10 # Add space for gaps
740
+ combined_img = Image.new("RGBA", (combined_width, combined_height))
741
+
742
+ y_offset = 0
743
+ for section in cropped_sections:
744
+ combined_img.paste(section, (0, y_offset))
745
+ y_offset += section.height + 50 # Add gap between sections
757
746
 
747
+ img = combined_img
748
+ elif cropped_sections:
749
+ img = cropped_sections[0]
750
+
751
+ if rand_int == 1:
752
+ img.save(os.path.join(get_temporary_directory(), 'after_crop.png'), 'PNG')
753
+
754
+ self.write_result(img)
755
+ screenshot_event.clear()
756
+
757
+ if self.macos_window_tracker_instance:
758
+ self.macos_window_tracker_instance.join()
759
+ elif self.windows_window_tracker_instance:
760
+ self.windows_window_tracker_instance.join()
758
761
 
759
762
  class AutopauseTimer:
760
763
  def __init__(self, timeout):
@@ -823,21 +826,28 @@ def user_input_thread_run():
823
826
  global terminated
824
827
  logger.info('Terminated!')
825
828
  terminated = True
829
+ import sys
826
830
 
827
831
  if sys.platform == 'win32':
828
832
  import msvcrt
829
833
  while not terminated:
830
- user_input_bytes = msvcrt.getch()
831
- try:
832
- user_input = user_input_bytes.decode()
834
+ user_input = None
835
+ if msvcrt.kbhit(): # Check if a key is pressed
836
+ user_input_bytes = msvcrt.getch()
837
+ try:
838
+ user_input = user_input_bytes.decode()
839
+ except UnicodeDecodeError:
840
+ pass
841
+ if not user_input: # If no input from msvcrt, check stdin
842
+ import sys
843
+ user_input = sys.stdin.read(1)
844
+
833
845
  if user_input.lower() in 'tq':
834
846
  _terminate_handler()
835
847
  elif user_input.lower() == 'p':
836
848
  pause_handler(False)
837
849
  else:
838
850
  engine_change_handler(user_input, False)
839
- except UnicodeDecodeError:
840
- pass
841
851
  else:
842
852
  import tty, termios
843
853
  fd = sys.stdin.fileno()
@@ -871,8 +881,7 @@ def on_window_closed(alive):
871
881
 
872
882
  def on_screenshot_combo():
873
883
  if not paused:
874
- img = take_screenshot()
875
- image_queue.put((img, True))
884
+ screenshot_event.set()
876
885
 
877
886
 
878
887
  def on_window_minimized(minimized):
@@ -1084,7 +1093,7 @@ def run(read_from=None,
1084
1093
  if config.get_engine(engine_class.name) == None:
1085
1094
  engine_instance = engine_class()
1086
1095
  else:
1087
- engine_instance = engine_class(config.get_engine(engine_class.name))
1096
+ engine_instance = engine_class(config.get_engine(engine_class.name), lang=lang)
1088
1097
 
1089
1098
  if engine_instance.available:
1090
1099
  engine_instances.append(engine_instance)
@@ -1104,6 +1113,7 @@ def run(read_from=None,
1104
1113
  global auto_pause_handler
1105
1114
  global notifier
1106
1115
  global websocket_server_thread
1116
+ global screenshot_thread
1107
1117
  global image_queue
1108
1118
  global ocr_1
1109
1119
  global ocr_2
@@ -1128,6 +1138,7 @@ def run(read_from=None,
1128
1138
  auto_pause = config.get_general('auto_pause')
1129
1139
  clipboard_thread = None
1130
1140
  websocket_server_thread = None
1141
+ screenshot_thread = None
1131
1142
  directory_watcher_thread = None
1132
1143
  unix_socket_server = None
1133
1144
  key_combo_listener = None
@@ -1160,13 +1171,18 @@ def run(read_from=None,
1160
1171
 
1161
1172
  if 'screencapture' in (read_from, read_from_secondary):
1162
1173
  global take_screenshot
1174
+ global screenshot_event
1163
1175
  last_screenshot_time = 0
1164
1176
  last_result = ([], engine_index)
1165
1177
  if screen_capture_combo != '':
1166
1178
  screen_capture_on_combo = True
1167
1179
  key_combos[screen_capture_combo] = on_screenshot_combo
1168
- take_screenshot = ScreenshotClass(screen_capture_area, screen_capture_window, screen_capture_exclusions, screen_capture_only_active_windows, screen_capture_areas)
1169
- # global_take_screenshot = ScreenshotClass(screen_capture_area, screen_capture_window, screen_capture_exclusions, screen_capture_only_active_windows, rectangle)
1180
+ else:
1181
+ global periodic_screenshot_queue
1182
+ periodic_screenshot_queue = queue.Queue()
1183
+ screenshot_event = threading.Event()
1184
+ screenshot_thread = ScreenshotThread(screen_capture_area, screen_capture_window, screen_capture_exclusions, screen_capture_only_active_windows, screen_capture_areas, screen_capture_on_combo)
1185
+ screenshot_thread.start()
1170
1186
  filtering = TextFiltering()
1171
1187
  read_from_readable.append('screen capture')
1172
1188
  if 'websocket' in (read_from, read_from_secondary):
@@ -1233,8 +1249,9 @@ def run(read_from=None,
1233
1249
  pass
1234
1250
 
1235
1251
  if (not img) and process_screenshots:
1236
- if (not paused) and take_screenshot.screencapture_window_active and take_screenshot.screencapture_window_visible and (time.time() - last_screenshot_time) > screen_capture_delay_secs:
1237
- img = take_screenshot()
1252
+ if (not paused) and screenshot_thread.screencapture_window_active and screenshot_thread.screencapture_window_visible and (time.time() - last_screenshot_time) > screen_capture_delay_secs:
1253
+ screenshot_event.set()
1254
+ img = periodic_screenshot_queue.get()
1238
1255
  filter_img = True
1239
1256
  notify = False
1240
1257
  last_screenshot_time = time.time()
@@ -1270,5 +1287,7 @@ def run(read_from=None,
1270
1287
  if unix_socket_server:
1271
1288
  unix_socket_server.shutdown()
1272
1289
  unix_socket_server_thread.join()
1290
+ if screenshot_thread:
1291
+ screenshot_thread.join()
1273
1292
  if key_combo_listener:
1274
1293
  key_combo_listener.stop()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.10.12
3
+ Version: 2.10.14
4
4
  Summary: A tool for mining sentences from games. Update: Full UI Re-design
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -3,7 +3,7 @@ GameSentenceMiner/anki.py,sha256=kWw3PV_Jj5-lHcttCB3lRXejHlaAbiJ2Ag_NAGX-RI8,166
3
3
  GameSentenceMiner/config_gui.py,sha256=Xa_a-sdQzht3kzR-Z9gkLy4qnaPyP1bdVadYTHp5lUQ,91018
4
4
  GameSentenceMiner/gametext.py,sha256=6VkjmBeiuZfPk8T6PHFdIAElBH2Y_oLVYvmcafqN7RM,6747
5
5
  GameSentenceMiner/gsm.py,sha256=p4DVa_Jx1EOsgUxAAdC7st7VXLKWnP2BLDGT78ToO8w,24864
6
- GameSentenceMiner/obs.py,sha256=ZV9Vk39hrsJLT-AlIxa3qgncKxXaL3Myl33vVJEDEoA,14670
6
+ GameSentenceMiner/obs.py,sha256=o_I6213VZvXqYkZDdUBgUg2KWi9SbnNZZjjUnKnQkK4,15190
7
7
  GameSentenceMiner/vad.py,sha256=G0NkaWFJaIfKQAV7LOFxyKoih7pPNYHDuy4SzeFVCkI,16389
8
8
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  GameSentenceMiner/ai/ai_prompting.py,sha256=0jBAnngNwmc3dqJiVWe_QRy4Syr-muV-ML2rq0FiUtU,10215
@@ -18,15 +18,15 @@ GameSentenceMiner/assets/pickaxe.png,sha256=VfIGyXyIZdzEnVcc4PmG3wszPMO1W4KCT7Q_
18
18
  GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=jtTzAWtMAx8GuA1XIJ_BmyNn3aYaO3u_c5Q7m5D4gS8,4056
20
20
  GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
21
- GameSentenceMiner/ocr/owocr_area_selector.py,sha256=boAqarX17jvFscu-7s6C9rqesjQ54s-kfuW0bjCru-M,19834
22
- GameSentenceMiner/ocr/owocr_helper.py,sha256=VDcuBfyZ1B7TN6yImJVuNxqWY7pr95R2cRM9jgD5Rk8,21670
21
+ GameSentenceMiner/ocr/owocr_area_selector.py,sha256=lHMVZuEE_-_wICfDr6jDJNSJIyZd2PnF7dIajknaHCU,20255
22
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=Iidu2T6z-eewFC40t3jUlLukankVsQB_HUEhr1L7HCU,22097
23
23
  GameSentenceMiner/ocr/ss_picker.py,sha256=0IhxUdaKruFpZyBL-8SpxWg7bPrlGpy3lhTcMMZ5rwo,5224
24
24
  GameSentenceMiner/owocr/owocr/__init__.py,sha256=87hfN5u_PbL_onLfMACbc0F5j4KyIK9lKnRCj6oZgR0,49
25
25
  GameSentenceMiner/owocr/owocr/__main__.py,sha256=XQaqZY99EKoCpU-gWQjNbTs7Kg17HvBVE7JY8LqIE0o,157
26
26
  GameSentenceMiner/owocr/owocr/config.py,sha256=qM7kISHdUhuygGXOxmgU6Ef2nwBShrZtdqu4InDCViE,8103
27
27
  GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
28
- GameSentenceMiner/owocr/owocr/ocr.py,sha256=Mri_zB_COk7x9GmolyhYCINJ-lQlD45GuJ4m4M0IBFM,55328
29
- GameSentenceMiner/owocr/owocr/run.py,sha256=mZIGDm3fGYrYbSNuFOk7Sbslfgi36YN0YqfC1xYh_eY,55286
28
+ GameSentenceMiner/owocr/owocr/ocr.py,sha256=siEqZLXhvFX-l311a19nCs-a0PxY9iwpaOoSV5lzVj4,56562
29
+ GameSentenceMiner/owocr/owocr/run.py,sha256=goOZSO3a7z8GxjYAcWjHsPxdzM60Nt3vxjcUzy1fnZg,56242
30
30
  GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
31
31
  GameSentenceMiner/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
32
  GameSentenceMiner/util/audio_offset_selector.py,sha256=8Stk3BP-XVIuzRv9nl9Eqd2D-1yD3JrgU-CamBywJmY,8542
@@ -62,9 +62,9 @@ GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
62
62
  GameSentenceMiner/web/templates/index.html,sha256=n0J-dV8eksj8JXUuaCTIh0fIxIjfgm2EvxGBdQ6gWoM,214113
63
63
  GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
64
64
  GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
65
- gamesentenceminer-2.10.12.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
66
- gamesentenceminer-2.10.12.dist-info/METADATA,sha256=YzGn0pkP-I00xGsRHt-5GK8x9pCKpoKR5lHBYL_z8Ho,7355
67
- gamesentenceminer-2.10.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
68
- gamesentenceminer-2.10.12.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
69
- gamesentenceminer-2.10.12.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
70
- gamesentenceminer-2.10.12.dist-info/RECORD,,
65
+ gamesentenceminer-2.10.14.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
66
+ gamesentenceminer-2.10.14.dist-info/METADATA,sha256=vnV_Sx7Xcs1DLFjkW4iKpwPNPf_ZFt83yVj7FSYtxAU,7355
67
+ gamesentenceminer-2.10.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
68
+ gamesentenceminer-2.10.14.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
69
+ gamesentenceminer-2.10.14.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
70
+ gamesentenceminer-2.10.14.dist-info/RECORD,,