GameSentenceMiner 2.11.6__tar.gz → 2.11.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/gsm.py +1 -1
  2. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/obs.py +2 -0
  3. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/ocr/gsm_ocr_config.py +10 -0
  4. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/ocr/owocr_area_selector.py +3 -3
  5. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/ocr/owocr_helper.py +73 -19
  6. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/owocr/owocr/ocr.py +29 -19
  7. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/owocr/owocr/run.py +159 -44
  8. gamesentenceminer-2.11.8/GameSentenceMiner/util/electron_config.py +256 -0
  9. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner.egg-info/PKG-INFO +7 -12
  10. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/PKG-INFO +7 -12
  11. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/README.md +6 -11
  12. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/pyproject.toml +1 -1
  13. gamesentenceminer-2.11.6/GameSentenceMiner/util/electron_config.py +0 -315
  14. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/__init__.py +0 -0
  15. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/ai/__init__.py +0 -0
  16. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/ai/ai_prompting.py +0 -0
  17. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/anki.py +0 -0
  18. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/assets/__init__.py +0 -0
  19. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/assets/icon.png +0 -0
  20. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/assets/icon128.png +0 -0
  21. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/assets/icon256.png +0 -0
  22. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/assets/icon32.png +0 -0
  23. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/assets/icon512.png +0 -0
  24. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/assets/icon64.png +0 -0
  25. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/assets/pickaxe.png +0 -0
  26. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/config_gui.py +0 -0
  27. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/gametext.py +0 -0
  28. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/ocr/__init__.py +0 -0
  29. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/ocr/ocrconfig.py +0 -0
  30. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/ocr/ss_picker.py +0 -0
  31. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/owocr/owocr/__init__.py +0 -0
  32. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/owocr/owocr/__main__.py +0 -0
  33. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/owocr/owocr/config.py +0 -0
  34. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -0
  35. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +0 -0
  36. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/__init__.py +0 -0
  37. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/audio_offset_selector.py +0 -0
  38. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/communication/__init__.py +0 -0
  39. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/communication/send.py +0 -0
  40. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/communication/websocket.py +0 -0
  41. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/configuration.py +0 -0
  42. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/downloader/Untitled_json.py +0 -0
  43. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/downloader/__init__.py +0 -0
  44. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/downloader/download_tools.py +0 -0
  45. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/downloader/oneocr_dl.py +0 -0
  46. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/ffmpeg.py +0 -0
  47. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/gsm_utils.py +0 -0
  48. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/model.py +0 -0
  49. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/notification.py +0 -0
  50. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/package.py +0 -0
  51. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/ss_selector.py +0 -0
  52. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/text_log.py +0 -0
  53. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/util/window_transparency.py +0 -0
  54. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/vad.py +0 -0
  55. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/__init__.py +0 -0
  56. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/service.py +0 -0
  57. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/__init__.py +0 -0
  58. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
  59. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/favicon-96x96.png +0 -0
  60. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/favicon.ico +0 -0
  61. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/favicon.svg +0 -0
  62. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/site.webmanifest +0 -0
  63. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/style.css +0 -0
  64. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
  65. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
  66. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/templates/__init__.py +0 -0
  67. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/templates/index.html +0 -0
  68. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/templates/text_replacements.html +0 -0
  69. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/templates/utility.html +0 -0
  70. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner/web/texthooking_page.py +0 -0
  71. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner.egg-info/SOURCES.txt +0 -0
  72. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner.egg-info/dependency_links.txt +0 -0
  73. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner.egg-info/entry_points.txt +0 -0
  74. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner.egg-info/requires.txt +0 -0
  75. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/GameSentenceMiner.egg-info/top_level.txt +0 -0
  76. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/LICENSE +0 -0
  77. {gamesentenceminer-2.11.6 → gamesentenceminer-2.11.8}/setup.cfg +0 -0
@@ -12,6 +12,7 @@ from GameSentenceMiner.util.communication.send import send_restart_signal
12
12
  from GameSentenceMiner.util.downloader.download_tools import download_obs_if_needed, download_ffmpeg_if_needed
13
13
  from GameSentenceMiner.vad import vad_processor
14
14
  from GameSentenceMiner.util.model import VADResult
15
+ import time
15
16
 
16
17
  try:
17
18
  import os.path
@@ -45,7 +46,6 @@ try:
45
46
  from GameSentenceMiner.web.texthooking_page import run_text_hooker_page
46
47
  except Exception as e:
47
48
  from GameSentenceMiner.util.configuration import logger, is_linux, is_windows
48
- import time
49
49
  logger.info("Something bad happened during import/initialization, closing in 5 seconds")
50
50
  logger.exception(e)
51
51
  time.sleep(5)
@@ -334,6 +334,7 @@ async def register_scene_change_callback(callback):
334
334
 
335
335
  logger.info("Scene change callback registered.")
336
336
 
337
+
337
338
  def get_screenshot(compression=-1):
338
339
  try:
339
340
  screenshot = os.path.join(configuration.get_temporary_directory(), make_unique_file_name('screenshot.png'))
@@ -377,6 +378,7 @@ def get_screenshot_base64(compression=0, width=None, height=None):
377
378
  logger.error(f"Error getting screenshot: {e}")
378
379
  return None
379
380
 
381
+
380
382
  def update_current_game():
381
383
  gsm_state.current_game = get_current_scene()
382
384
 
@@ -131,6 +131,16 @@ def set_dpi_awareness():
131
131
  ctypes.windll.shcore.SetProcessDpiAwareness(per_monitor_awareness)
132
132
 
133
133
  def get_scene_ocr_config(use_window_as_config=False, window=""):
134
+ path = get_scene_ocr_config_path(use_window_as_config, window)
135
+ if not os.path.exists(path):
136
+ return None
137
+ with open(path, "r", encoding="utf-8") as f:
138
+ from json import load
139
+ data = load(f)
140
+ ocr_config = OCRConfig.from_dict(data)
141
+ return ocr_config
142
+
143
+ def get_scene_ocr_config_path(use_window_as_config=False, window=""):
134
144
  ocr_config_dir = get_ocr_config_path()
135
145
  try:
136
146
  if use_window_as_config:
@@ -11,7 +11,7 @@ from PIL import Image, ImageTk
11
11
 
12
12
  # Assuming a mock or real obs module exists in this path
13
13
  from GameSentenceMiner import obs
14
- from GameSentenceMiner.ocr.gsm_ocr_config import set_dpi_awareness, get_window, get_scene_ocr_config
14
+ from GameSentenceMiner.ocr.gsm_ocr_config import set_dpi_awareness, get_window, get_scene_ocr_config_path
15
15
  from GameSentenceMiner.util.gsm_utils import sanitize_filename
16
16
 
17
17
  try:
@@ -123,7 +123,7 @@ class ScreenSelector:
123
123
 
124
124
  def load_existing_rectangles(self):
125
125
  """Loads rectangles from config, converting from percentage to absolute pixels for use."""
126
- config_path = get_scene_ocr_config(self.use_window_as_config, self.window_name)
126
+ config_path = get_scene_ocr_config_path(self.use_window_as_config, self.window_name)
127
127
  win_geom = self.target_window_geometry # Use current geometry for conversion
128
128
  win_w, win_h, win_l, win_t = win_geom['width'], win_geom['height'], win_geom['left'], win_geom['top']
129
129
 
@@ -168,7 +168,7 @@ class ScreenSelector:
168
168
 
169
169
  def save_rects(self, event=None):
170
170
  """Saves rectangles to config, converting from absolute pixels to percentages."""
171
- config_path = get_scene_ocr_config(self.use_window_as_config, self.window_name)
171
+ config_path = get_scene_ocr_config_path(self.use_window_as_config, self.window_name)
172
172
  win_geom = self.target_window_geometry
173
173
  win_l, win_t, win_w, win_h = win_geom['left'], win_geom['top'], win_geom['width'], win_geom['height']
174
174
  print(f"Saving rectangles to: {config_path} relative to window: {win_geom}")
@@ -18,13 +18,15 @@ from PIL import Image
18
18
  from rapidfuzz import fuzz
19
19
 
20
20
  from GameSentenceMiner import obs
21
+ from GameSentenceMiner.util.electron_config import *
21
22
  from GameSentenceMiner.ocr.ss_picker import ScreenCropper
22
23
  from GameSentenceMiner.owocr.owocr.run import TextFiltering
23
24
  from GameSentenceMiner.util.configuration import get_config, get_app_directory, get_temporary_directory
24
- from GameSentenceMiner.util.electron_config import get_ocr_scan_rate, get_requires_open_window
25
25
  from GameSentenceMiner.ocr.gsm_ocr_config import OCRConfig, set_dpi_awareness, get_window, get_ocr_config_path
26
26
  from GameSentenceMiner.owocr.owocr import screen_coordinate_picker, run
27
27
  from GameSentenceMiner.util.gsm_utils import sanitize_filename, do_text_replacements, OCR_REPLACEMENTS_FILE
28
+ import threading
29
+ import time
28
30
 
29
31
  CONFIG_FILE = Path("ocr_config.json")
30
32
  DEFAULT_IMAGE_PATH = r"C:\Users\Beangate\Pictures\msedge_acbl8GL7Ax.jpg" # CHANGE THIS
@@ -193,17 +195,17 @@ all_cords = None
193
195
  rectangles = None
194
196
  last_ocr2_result = []
195
197
 
196
- def do_second_ocr(ocr1_text, time, img, filtering, ignore_furigana_filter=False, ignore_previous_result=False):
198
+ def do_second_ocr(ocr1_text, time, img, filtering, pre_crop_image=None, ignore_furigana_filter=False, ignore_previous_result=False):
197
199
  global twopassocr, ocr2, last_ocr2_result
198
200
  try:
199
201
  orig_text, text = run.process_and_write_results(img, None, last_ocr2_result if not ignore_previous_result else None, filtering, None,
200
- engine=ocr2, furigana_filter_sensitivity=furigana_filter_sensitivity if not ignore_furigana_filter else 0)
202
+ engine=get_ocr_ocr2(), furigana_filter_sensitivity=furigana_filter_sensitivity if not ignore_furigana_filter else 0)
201
203
 
202
204
  if compare_ocr_results(last_ocr2_result, orig_text):
203
205
  if text:
204
206
  logger.info("Seems like Text we already sent, not doing anything.")
205
207
  return
206
- save_result_image(img)
208
+ save_result_image(img, pre_crop_image=pre_crop_image)
207
209
  last_ocr2_result = orig_text
208
210
  asyncio.run(send_result(text, time))
209
211
  except json.JSONDecodeError:
@@ -213,19 +215,19 @@ def do_second_ocr(ocr1_text, time, img, filtering, ignore_furigana_filter=False,
213
215
  print(f"Error processing message: {e}")
214
216
 
215
217
 
216
- def save_result_image(img):
218
+ def save_result_image(img, pre_crop_image=None):
217
219
  if isinstance(img, bytes):
218
220
  with open(os.path.join(get_temporary_directory(), "last_successful_ocr.png"), "wb") as f:
219
221
  f.write(img)
220
222
  else:
221
223
  img.save(os.path.join(get_temporary_directory(), "last_successful_ocr.png"))
222
- img.close()
224
+ run.set_last_image(pre_crop_image if pre_crop_image else img)
223
225
 
224
226
 
225
227
  async def send_result(text, time):
226
228
  if text:
227
229
  text = do_text_replacements(text, OCR_REPLACEMENTS_FILE)
228
- if clipboard_output:
230
+ if get_ocr_send_to_clipboard():
229
231
  import pyperclip
230
232
  pyperclip.copy(text)
231
233
  try:
@@ -244,6 +246,50 @@ previous_orig_text = "" # Store original text result
244
246
  TEXT_APPEARENCE_DELAY = get_ocr_scan_rate() * 1000 + 500 # Adjust as needed
245
247
  force_stable = False
246
248
 
249
+ class ConfigChangeCheckThread(threading.Thread):
250
+ def __init__(self):
251
+ super().__init__(daemon=True)
252
+ self.last_changes = None
253
+ self.callbacks = []
254
+
255
+ def run(self):
256
+ global ocr_config
257
+ while True:
258
+ try:
259
+ section_changed, changes = has_ocr_config_changed()
260
+ if section_changed:
261
+ reload_electron_config()
262
+ self.last_changes = changes
263
+ # Only run this block after a change has occurred and then the section is stable (no change)
264
+ if self.last_changes is not None and not section_changed:
265
+ logger.info(f"Detected config changes: {self.last_changes}")
266
+ for cb in self.callbacks:
267
+ cb(self.last_changes)
268
+ if hasattr(run, 'handle_config_change'):
269
+ run.handle_config_change()
270
+ if any(c in self.last_changes for c in ('ocr1', 'ocr2', 'language', 'furigana_filter_sensitivity')):
271
+ reset_callback_vars()
272
+ self.last_changes = None
273
+ except Exception as e:
274
+ logger.debug(f"ConfigChangeCheckThread error: {e}")
275
+ time.sleep(0.25) # Lowered to 0.25s for more responsiveness
276
+
277
+ def add_callback(self, callback):
278
+ self.callbacks.append(callback)
279
+
280
+ def reset_callback_vars():
281
+ global previous_text, last_oneocr_time, text_stable_start_time, previous_orig_text, previous_img, force_stable, previous_ocr1_result, previous_text_list, last_ocr2_result
282
+ previous_text = None
283
+ previous_orig_text = ""
284
+ previous_img = None
285
+ text_stable_start_time = None
286
+ last_oneocr_time = None
287
+ force_stable = False
288
+ previous_ocr1_result = ""
289
+ previous_text_list = []
290
+ last_ocr2_result = ""
291
+ run.set_last_image(None)
292
+
247
293
  def text_callback(text, orig_text, time, img=None, came_from_ss=False, filtering=None, crop_coords=None):
248
294
  global twopassocr, ocr2, previous_text, last_oneocr_time, text_stable_start_time, previous_orig_text, previous_img, force_stable, previous_ocr1_result, previous_text_list
249
295
  orig_text_string = ''.join([item for item in orig_text if item is not None]) if orig_text else ""
@@ -251,10 +297,13 @@ def text_callback(text, orig_text, time, img=None, came_from_ss=False, filtering
251
297
  save_result_image(img)
252
298
  asyncio.run(send_result(text, time))
253
299
  return
300
+
301
+ if not text:
302
+ run.set_last_image(img)
254
303
 
255
304
  line_start_time = time if time else datetime.now()
256
305
 
257
- if manual or not twopassocr:
306
+ if manual or not get_ocr_two_pass_ocr():
258
307
  if compare_ocr_results(previous_orig_text, orig_text_string):
259
308
  if text:
260
309
  logger.info("Seems like Text we already sent, not doing anything.")
@@ -274,6 +323,7 @@ def text_callback(text, orig_text, time, img=None, came_from_ss=False, filtering
274
323
  if previous_text and text_stable_start_time:
275
324
  stable_time = text_stable_start_time
276
325
  previous_img_local = previous_img
326
+ pre_crop_image = previous_img_local
277
327
  if compare_ocr_results(previous_orig_text, orig_text_string):
278
328
  if text:
279
329
  logger.info("Seems like Text we already sent, not doing anything.")
@@ -281,10 +331,10 @@ def text_callback(text, orig_text, time, img=None, came_from_ss=False, filtering
281
331
  return
282
332
  previous_orig_text = orig_text_string
283
333
  previous_ocr1_result = previous_text
284
- if crop_coords and optimize_second_scan:
334
+ if crop_coords and get_ocr_optimize_second_scan():
285
335
  previous_img_local.save(os.path.join(get_temporary_directory(), "pre_oneocrcrop.png"))
286
336
  previous_img_local = previous_img_local.crop(crop_coords)
287
- second_ocr_queue.put((previous_text, stable_time, previous_img_local, filtering))
337
+ second_ocr_queue.put((previous_text, stable_time, previous_img_local, filtering, pre_crop_image))
288
338
  # threading.Thread(target=do_second_ocr, args=(previous_text, stable_time, previous_img_local, filtering), daemon=True).start()
289
339
  previous_img = None
290
340
  previous_text = None
@@ -294,7 +344,7 @@ def text_callback(text, orig_text, time, img=None, came_from_ss=False, filtering
294
344
  return
295
345
 
296
346
  # Make sure it's an actual new line before starting the timer
297
- if compare_ocr_results(orig_text_string, previous_orig_text):
347
+ if text and compare_ocr_results(orig_text_string, previous_orig_text):
298
348
  return
299
349
 
300
350
  if not text_stable_start_time:
@@ -315,15 +365,15 @@ def process_task_queue():
315
365
  task = second_ocr_queue.get()
316
366
  if task is None: # Exit signal
317
367
  break
318
- ocr1_text, stable_time, previous_img_local, filtering = task
319
- do_second_ocr(ocr1_text, stable_time, previous_img_local, filtering)
368
+ ocr1_text, stable_time, previous_img_local, filtering, pre_crop_image = task
369
+ do_second_ocr(ocr1_text, stable_time, previous_img_local, filtering, pre_crop_image)
320
370
  except Exception as e:
321
371
  logger.exception(f"Error processing task: {e}")
322
372
  finally:
323
373
  second_ocr_queue.task_done()
324
374
 
325
375
 
326
- def run_oneocr(ocr_config: OCRConfig, rectangles):
376
+ def run_oneocr(ocr_config: OCRConfig, rectangles, config_check_thread):
327
377
  global done
328
378
  print("Running OneOCR")
329
379
  screen_area = None
@@ -344,11 +394,9 @@ def run_oneocr(ocr_config: OCRConfig, rectangles):
344
394
  screen_capture_area=screen_area,
345
395
  # screen_capture_monitor=monitor_config['index'],
346
396
  screen_capture_window=ocr_config.window if ocr_config and ocr_config.window else None,
347
- screen_capture_only_active_windows=get_requires_open_window(),
348
397
  screen_capture_delay_secs=get_ocr_scan_rate(), engine=ocr1,
349
398
  text_callback=text_callback,
350
399
  screen_capture_exclusions=exclusions,
351
- language=language,
352
400
  monitor_index=None,
353
401
  ocr1=ocr1,
354
402
  ocr2=ocr2,
@@ -356,7 +404,7 @@ def run_oneocr(ocr_config: OCRConfig, rectangles):
356
404
  screen_capture_areas=screen_areas,
357
405
  furigana_filter_sensitivity=furigana_filter_sensitivity,
358
406
  screen_capture_combo=manual_ocr_hotkey if manual_ocr_hotkey and manual else None,
359
- keep_line_breaks=keep_newline)
407
+ config_check_thread=config_check_thread)
360
408
  except Exception as e:
361
409
  logger.exception(f"Error running OneOCR: {e}")
362
410
  done = True
@@ -366,7 +414,7 @@ def run_oneocr(ocr_config: OCRConfig, rectangles):
366
414
  def add_ss_hotkey(ss_hotkey="ctrl+shift+g"):
367
415
  import keyboard
368
416
  secret_ss_hotkey = "F14"
369
- filtering = TextFiltering(lang=language)
417
+ filtering = TextFiltering(lang=get_ocr_language())
370
418
  cropper = ScreenCropper()
371
419
  def capture():
372
420
  print("Taking screenshot...")
@@ -462,6 +510,12 @@ if __name__ == "__main__":
462
510
  use_window_for_config = args.use_window_for_config
463
511
  keep_newline = args.keep_newline
464
512
  obs_ocr = args.obs_ocr
513
+
514
+ # Start config change checker thread
515
+ config_check_thread = ConfigChangeCheckThread()
516
+ config_check_thread.start()
517
+ # Example: add a callback to config_check_thread if needed
518
+ # config_check_thread.add_callback(lambda: print("Config changed!"))
465
519
 
466
520
  window = None
467
521
  logger.info(f"Received arguments: {vars(args)}")
@@ -487,7 +541,7 @@ if __name__ == "__main__":
487
541
  if manual or ocr_config:
488
542
  rectangles = ocr_config.rectangles if ocr_config and ocr_config.rectangles else []
489
543
  oneocr_threads = []
490
- ocr_thread = threading.Thread(target=run_oneocr, args=(ocr_config, rectangles), daemon=True)
544
+ ocr_thread = threading.Thread(target=run_oneocr, args=(ocr_config, rectangles, config_check_thread), daemon=True)
491
545
  ocr_thread.start()
492
546
  if not manual:
493
547
  worker_thread = threading.Thread(target=process_task_queue, daemon=True)
@@ -17,6 +17,8 @@ from PIL import Image
17
17
  from loguru import logger
18
18
  import requests
19
19
 
20
+ from GameSentenceMiner.util.electron_config import get_ocr_language
21
+
20
22
  # from GameSentenceMiner.util.configuration import get_temporary_directory
21
23
 
22
24
  try:
@@ -809,25 +811,8 @@ class OneOCR:
809
811
  available = False
810
812
 
811
813
  def __init__(self, config={}, lang='ja'):
812
- if lang == "ja":
813
- self.regex = re.compile(r'[\u3041-\u3096\u30A1-\u30FA\u4E00-\u9FFF]')
814
- elif lang == "zh":
815
- self.regex = re.compile(r'[\u4E00-\u9FFF]')
816
- elif lang == "ko":
817
- self.regex = re.compile(r'[\uAC00-\uD7AF]')
818
- elif lang == "ar":
819
- self.regex = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
820
- elif lang == "ru":
821
- self.regex = re.compile(r'[\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u1C80-\u1C8F]')
822
- elif lang == "el":
823
- self.regex = re.compile(r'[\u0370-\u03FF\u1F00-\u1FFF]')
824
- elif lang == "he":
825
- self.regex = re.compile(r'[\u0590-\u05FF\uFB1D-\uFB4F]')
826
- elif lang == "th":
827
- self.regex = re.compile(r'[\u0E00-\u0E7F]')
828
- else:
829
- self.regex = re.compile(
830
- r'[a-zA-Z\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u1D00-\u1D7F\u1D80-\u1DBF\u1E00-\u1EFF\u2C60-\u2C7F\uA720-\uA7FF\uAB30-\uAB6F]')
814
+ self.initial_lang = lang
815
+ self.get_regex(lang)
831
816
  if sys.platform == 'win32':
832
817
  if int(platform.release()) < 10:
833
818
  logger.warning('OneOCR is not supported on Windows older than 10!')
@@ -849,7 +834,32 @@ class OneOCR:
849
834
  except:
850
835
  logger.warning('Error reading URL from config, OneOCR will not work!')
851
836
 
837
+ def get_regex(self, lang):
838
+ if lang == "ja":
839
+ self.regex = re.compile(r'[\u3041-\u3096\u30A1-\u30FA\u4E00-\u9FFF]')
840
+ elif lang == "zh":
841
+ self.regex = re.compile(r'[\u4E00-\u9FFF]')
842
+ elif lang == "ko":
843
+ self.regex = re.compile(r'[\uAC00-\uD7AF]')
844
+ elif lang == "ar":
845
+ self.regex = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
846
+ elif lang == "ru":
847
+ self.regex = re.compile(r'[\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u1C80-\u1C8F]')
848
+ elif lang == "el":
849
+ self.regex = re.compile(r'[\u0370-\u03FF\u1F00-\u1FFF]')
850
+ elif lang == "he":
851
+ self.regex = re.compile(r'[\u0590-\u05FF\uFB1D-\uFB4F]')
852
+ elif lang == "th":
853
+ self.regex = re.compile(r'[\u0E00-\u0E7F]')
854
+ else:
855
+ self.regex = re.compile(
856
+ r'[a-zA-Z\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u1D00-\u1D7F\u1D80-\u1DBF\u1E00-\u1EFF\u2C60-\u2C7F\uA720-\uA7FF\uAB30-\uAB6F]')
857
+
852
858
  def __call__(self, img, furigana_filter_sensitivity=0):
859
+ lang = get_ocr_language()
860
+ if lang != self.initial_lang:
861
+ self.initial_lang = lang
862
+ self.get_regex(lang)
853
863
  img, is_path = input_to_pil_image(img)
854
864
  if img.width < 51 or img.height < 51:
855
865
  new_width = max(img.width, 51)