GameSentenceMiner 2.8.5__py3-none-any.whl → 2.8.7__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/gsm.py CHANGED
@@ -1,36 +1,45 @@
1
- import os.path
2
- import signal
3
- from subprocess import Popen
4
-
5
- import keyboard
6
- import psutil
7
- import ttkbootstrap as ttk
8
- from PIL import Image, ImageDraw
9
- from pystray import Icon, Menu, MenuItem
10
- from watchdog.events import FileSystemEventHandler
11
- from watchdog.observers import Observer
12
-
13
-
14
- from GameSentenceMiner import anki
15
- from GameSentenceMiner import config_gui
16
- from GameSentenceMiner import configuration
17
- from GameSentenceMiner import ffmpeg
18
- from GameSentenceMiner import gametext
19
- from GameSentenceMiner import notification
20
- from GameSentenceMiner import obs
21
- from GameSentenceMiner import util
22
- from GameSentenceMiner.communication import Message
23
- from GameSentenceMiner.communication.send import send_restart_signal
24
- from GameSentenceMiner.communication.websocket import connect_websocket, register_websocket_message_handler, \
25
- FunctionName
26
- from GameSentenceMiner.configuration import *
27
- from GameSentenceMiner.downloader.download_tools import download_obs_if_needed, download_ffmpeg_if_needed
28
- from GameSentenceMiner.ffmpeg import get_audio_and_trim, get_video_timings
29
- from GameSentenceMiner.obs import check_obs_folder_is_correct
30
- from GameSentenceMiner.text_log import GameLine, get_text_event, get_mined_line, get_all_lines
31
- from GameSentenceMiner.util import *
32
- from GameSentenceMiner.web import texthooking_page
33
- from GameSentenceMiner.web.texthooking_page import start_web_server
1
+ import asyncio
2
+
3
+ try:
4
+ import os.path
5
+ import signal
6
+ from subprocess import Popen
7
+
8
+ import keyboard
9
+ import psutil
10
+ import ttkbootstrap as ttk
11
+ from PIL import Image, ImageDraw
12
+ from pystray import Icon, Menu, MenuItem
13
+ from watchdog.events import FileSystemEventHandler
14
+ from watchdog.observers import Observer
15
+
16
+
17
+ from GameSentenceMiner import anki
18
+ from GameSentenceMiner import config_gui
19
+ from GameSentenceMiner import configuration
20
+ from GameSentenceMiner import ffmpeg
21
+ from GameSentenceMiner import gametext
22
+ from GameSentenceMiner import notification
23
+ from GameSentenceMiner import obs
24
+ from GameSentenceMiner import util
25
+ from GameSentenceMiner.communication import Message
26
+ from GameSentenceMiner.communication.send import send_restart_signal
27
+ from GameSentenceMiner.communication.websocket import connect_websocket, register_websocket_message_handler, \
28
+ FunctionName
29
+ from GameSentenceMiner.configuration import *
30
+ from GameSentenceMiner.downloader.download_tools import download_obs_if_needed, download_ffmpeg_if_needed
31
+ from GameSentenceMiner.ffmpeg import get_audio_and_trim, get_video_timings
32
+ from GameSentenceMiner.obs import check_obs_folder_is_correct
33
+ from GameSentenceMiner.text_log import GameLine, get_text_event, get_mined_line, get_all_lines
34
+ from GameSentenceMiner.util import *
35
+ from GameSentenceMiner.web import texthooking_page
36
+ from GameSentenceMiner.web.texthooking_page import run_text_hooker_page
37
+ except Exception as e:
38
+ from GameSentenceMiner.configuration import logger
39
+ import time
40
+ logger.info("Something bad happened during import/initialization, closing in 5 seconds")
41
+ logger.exception(e)
42
+ time.sleep(5)
34
43
 
35
44
  if is_windows():
36
45
  import win32api
@@ -449,10 +458,12 @@ def run_tray():
449
458
 
450
459
  def close_obs():
451
460
  obs.disconnect_from_obs()
452
- if obs.obs_process:
461
+ if obs.obs_process_pid:
453
462
  try:
454
- subprocess.run(["taskkill", "/PID", str(obs.obs_process.pid), "/F"], check=True, capture_output=True, text=True)
455
- print(f"OBS (PID {obs.obs_process.pid}) has been terminated.")
463
+ subprocess.run(["taskkill", "/PID", str(obs.obs_process_pid), "/F"], check=True, capture_output=True, text=True)
464
+ print(f"OBS (PID {obs.obs_process_pid}) has been terminated.")
465
+ if os.path.exists(obs.OBS_PID_FILE):
466
+ os.remove(obs.OBS_PID_FILE)
456
467
  except subprocess.CalledProcessError as e:
457
468
  print(f"Error terminating OBS: {e.stderr}")
458
469
  else:
@@ -460,9 +471,9 @@ def close_obs():
460
471
 
461
472
 
462
473
  def restart_obs():
463
- if obs.obs_process:
474
+ if obs.obs_process_pid:
464
475
  close_obs()
465
- time.sleep(2)
476
+ time.sleep(1)
466
477
  obs.start_obs()
467
478
  obs.connect_to_obs()
468
479
 
@@ -532,7 +543,7 @@ def initialize(reloading=False):
532
543
  # whisper_helper.initialize_whisper_model()
533
544
 
534
545
  def initialize_async():
535
- tasks = [gametext.start_text_monitor, connect_websocket, run_tray]
546
+ tasks = [connect_websocket, run_tray]
536
547
  threads = []
537
548
  tasks.append(anki.start_monitoring_anki)
538
549
  for task in tasks:
@@ -555,7 +566,6 @@ def post_init():
555
566
  whisper_helper.initialize_whisper_model()
556
567
  if get_config().vad.is_silero():
557
568
  from GameSentenceMiner.vad import silero_trim
558
- start_web_server()
559
569
 
560
570
  util.run_new_thread(do_post_init)
561
571
 
@@ -572,8 +582,10 @@ def handle_websocket_message(message: Message):
572
582
  case _:
573
583
  logger.debug(f"unknown message from electron websocket: {message.to_json()}")
574
584
 
585
+ def post_init2():
586
+ asyncio.run(gametext.start_text_monitor())
575
587
 
576
- def main(reloading=False):
588
+ async def main(reloading=False):
577
589
  global root, settings_window
578
590
  logger.info("Script started.")
579
591
  root = ttk.Window(themename='darkly')
@@ -586,12 +598,16 @@ def main(reloading=False):
586
598
  if not is_linux():
587
599
  register_hotkeys()
588
600
 
601
+ util.run_new_thread(post_init2)
602
+ util.run_new_thread(run_text_hooker_page)
603
+
589
604
  # Register signal handlers for graceful shutdown
590
605
  signal.signal(signal.SIGTERM, handle_exit()) # Handle `kill` commands
591
606
  signal.signal(signal.SIGINT, handle_exit()) # Handle Ctrl+C
592
607
  if is_windows():
593
608
  win32api.SetConsoleCtrlHandler(handle_exit())
594
609
 
610
+
595
611
  try:
596
612
  # if get_config().general.open_config_on_startup:
597
613
  # root.after(0, settings_window.show)
@@ -612,7 +628,7 @@ def main(reloading=False):
612
628
  if __name__ == "__main__":
613
629
  logger.info("Starting GSM")
614
630
  try:
615
- main()
631
+ asyncio.run(main())
616
632
  except Exception as e:
617
633
  logger.exception(e)
618
634
  time.sleep(5)
GameSentenceMiner/obs.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import os.path
2
2
  import subprocess
3
3
  import time
4
+ import psutil
4
5
 
5
6
  from obswebsocket import obsws, requests
6
7
 
@@ -9,8 +10,9 @@ from GameSentenceMiner.configuration import *
9
10
  from GameSentenceMiner.model import *
10
11
 
11
12
  client: obsws = None
12
- obs_process = None
13
+ obs_process_pid = None
13
14
  logging.getLogger('obswebsocket').setLevel(logging.CRITICAL)
15
+ OBS_PID_FILE = os.path.join(configuration.get_app_directory(), 'obs-studio', 'obs_pid.txt')
14
16
 
15
17
  # REFERENCE: https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md
16
18
 
@@ -18,20 +20,43 @@ logging.getLogger('obswebsocket').setLevel(logging.CRITICAL)
18
20
  def get_obs_path():
19
21
  return os.path.join(configuration.get_app_directory(), 'obs-studio/bin/64bit/obs64.exe')
20
22
 
23
+ def is_process_running(pid):
24
+ try:
25
+ process = psutil.Process(pid)
26
+ return 'obs' in process.exe()
27
+ except (psutil.NoSuchProcess, psutil.AccessDenied, OSError):
28
+ if os.path.exists(OBS_PID_FILE):
29
+ os.remove(OBS_PID_FILE)
30
+ return False
31
+
21
32
  def start_obs():
22
- global obs_process
33
+ global obs_process_pid
34
+ if os.path.exists(OBS_PID_FILE):
35
+ with open(OBS_PID_FILE, "r") as f:
36
+ try:
37
+ obs_process_pid = int(f.read().strip())
38
+ if is_process_running(obs_process_pid):
39
+ print(f"OBS is already running with PID: {obs_process_pid}")
40
+ connect_to_obs()
41
+ return obs_process_pid
42
+ except ValueError:
43
+ print("Invalid PID found in file. Launching new OBS instance.")
44
+ except OSError:
45
+ print("No process found with the stored PID. Launching new OBS instance.")
46
+
23
47
  obs_path = get_obs_path()
24
48
  if not os.path.exists(obs_path):
25
- logger.error(f"OBS not found at {obs_path}. Please install OBS.")
49
+ print(f"OBS not found at {obs_path}. Please install OBS.")
26
50
  return None
27
-
28
51
  try:
29
- obs_process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable', '--startreplaybuffer'], cwd=os.path.dirname(obs_path))
30
-
31
- logger.info("OBS launched")
32
- return obs_process.pid
52
+ obs_process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable', '--startreplaybuffer', ], cwd=os.path.dirname(obs_path))
53
+ obs_process_pid = obs_process.pid
54
+ with open(OBS_PID_FILE, "w") as f:
55
+ f.write(str(obs_process_pid))
56
+ print(f"OBS launched with PID: {obs_process_pid}")
57
+ return obs_process_pid
33
58
  except Exception as e:
34
- logger.error(f"Error launching OBS: {e}")
59
+ print(f"Error launching OBS: {e}")
35
60
  return None
36
61
 
37
62
  def check_obs_folder_is_correct():
@@ -69,33 +94,32 @@ def get_obs_websocket_config_values():
69
94
 
70
95
  if get_config().obs.password == 'your_password':
71
96
  logger.info("OBS WebSocket password is not set. Setting it now...")
72
- config = get_master_config()
73
- config.get_config().port = server_port
74
- config.get_config().password = server_password
75
- with open(get_config_path(), 'w') as file:
76
- json.dump(config.to_dict(), file, indent=4)
97
+ full_config = get_master_config()
98
+ full_config.get_config().obs.port = server_port
99
+ full_config.get_config().obs.password = server_password
100
+ full_config.sync_shared_fields()
101
+ full_config.save()
77
102
  reload_config()
78
103
 
79
104
 
80
- reconnecting = False
105
+ connected = False
81
106
 
82
107
  def on_connect(obs):
83
- global reconnecting
108
+ global connected
84
109
  logger.info("Reconnected to OBS WebSocket.")
85
- if reconnecting:
86
- start_replay_buffer()
87
- reconnecting = False
110
+ start_replay_buffer()
111
+ connected = True
88
112
 
89
113
 
90
114
  def on_disconnect(obs):
91
- global reconnecting
115
+ global connected
92
116
  logger.error("OBS Connection Lost!")
93
- reconnecting = True
117
+ connected = False
94
118
 
95
119
 
96
120
  def connect_to_obs():
97
121
  global client
98
- if get_config().obs.enabled:
122
+ if get_config().obs.enabled and not client:
99
123
  if util.is_windows():
100
124
  get_obs_websocket_config_values()
101
125
  client = obsws(host=get_config().obs.host, port=get_config().obs.port,
@@ -133,7 +157,6 @@ def do_obs_call(request, from_dict = None, retry=10):
133
157
  time.sleep(1)
134
158
  return do_obs_call(request, from_dict, retry - 1)
135
159
  else:
136
- logger.error(f"Error doing obs call: {e}")
137
160
  raise e
138
161
  return None
139
162
 
@@ -187,7 +210,7 @@ def get_current_scene():
187
210
  try:
188
211
  return do_obs_call(requests.GetCurrentProgramScene(), SceneInfo.from_dict, retry=0).sceneName
189
212
  except Exception as e:
190
- logger.error(f"Couldn't get scene: {e}")
213
+ logger.debug(f"Couldn't get scene: {e}")
191
214
  return ''
192
215
 
193
216
 
@@ -88,7 +88,6 @@ class ScreenSelector:
88
88
  try:
89
89
  windows = gw.getWindowsWithTitle(self.window_name)
90
90
  if windows:
91
- # TODO: Handle multiple matches if necessary (e.g., let user choose?)
92
91
  if len(windows) > 1:
93
92
  print(f"Warning: Multiple windows found with title '{self.window_name}'. Using the first one.")
94
93
  return windows[0]
@@ -235,7 +234,7 @@ class ScreenSelector:
235
234
  # --- End Conversion ---
236
235
 
237
236
  # Validate size using the final absolute pixel coordinates
238
- if abs_coords and abs_coords[2] >= MIN_RECT_WIDTH and abs_coords[3] >= MIN_RECT_HEIGHT:
237
+ if coordinate_system == COORD_SYSTEM_PERCENTAGE or (abs_coords and abs_coords[2] >= MIN_RECT_WIDTH and abs_coords[3] >= MIN_RECT_HEIGHT):
239
238
  # Find the correct monitor dict from self.monitors based on index
240
239
  monitor_index = monitor_data['index']
241
240
  target_monitor = next((m for m in self.monitors if m['index'] == monitor_index), None)
@@ -863,6 +862,9 @@ if __name__ == "__main__":
863
862
  # if not target_window_title:
864
863
  # target_window_title = get_ocr_config().window
865
864
 
865
+ if not target_window_title:
866
+ target_window_title = "Windowed Projector (Preview)"
867
+
866
868
  # Get the selection result
867
869
  selection_result = get_screen_selection(target_window_title)
868
870
 
@@ -254,8 +254,13 @@ def text_callback(text, orig_text, rectangle_index, time, img=None):
254
254
  # if orig_text:
255
255
  # print(orig_text_string)
256
256
  if not twopassocr:
257
+ if previous_orig_text and fuzz.ratio(orig_text_string, previous_orig_text) >= 80:
258
+ logger.info("Seems like Text we already sent, not doing anything.")
259
+ return
257
260
  img.save(os.path.join(get_temporary_directory(), "last_successful_ocr.png"))
258
261
  send_result(text, time)
262
+ orig_text_results[rectangle_index] = orig_text_string
263
+ last_ocr1_results[rectangle_index] = previous_text
259
264
  if not text:
260
265
  if previous_text:
261
266
  if rectangle_index in text_stable_start_times:
@@ -269,8 +274,8 @@ def text_callback(text, orig_text, rectangle_index, time, img=None):
269
274
  logger.info("Seems like Text we already sent, not doing anything.")
270
275
  return
271
276
  orig_text_results[rectangle_index] = orig_text_string
272
- do_second_ocr(previous_text, rectangle_index, stable_time, previous_img)
273
277
  last_ocr1_results[rectangle_index] = previous_text
278
+ do_second_ocr(previous_text, rectangle_index, stable_time, previous_img)
274
279
  return
275
280
  return
276
281
 
@@ -315,10 +320,24 @@ def run_oneocr(ocr_config: OCRConfig, i, area=False):
315
320
  text_callback=text_callback,
316
321
  screen_capture_exclusions=exclusions,
317
322
  rectangle=i,
318
- ignore_window_visible=not get_requires_open_window())
323
+ language="ja")
319
324
  done = True
320
325
 
321
326
 
327
+ def get_window(window_name):
328
+ import pygetwindow as gw
329
+ try:
330
+ windows = gw.getWindowsWithTitle(window_name)
331
+ if windows:
332
+ if len(windows) > 1:
333
+ print(f"Warning: Multiple windows found with title '{window_name}'. Using the first one.")
334
+ return windows[0]
335
+ else:
336
+ return None
337
+ except Exception as e:
338
+ print(f"Error finding window '{self.window_name}': {e}")
339
+ return None
340
+
322
341
  if __name__ == "__main__":
323
342
  global ocr1, ocr2, twopassocr
324
343
  import sys
@@ -343,7 +362,17 @@ if __name__ == "__main__":
343
362
  logger.info(f"Received arguments: ocr1={ocr1}, ocr2={ocr2}, twopassocr={twopassocr}")
344
363
  global ocr_config
345
364
  ocr_config: OCRConfig = get_ocr_config()
346
- print(ocr_config)
365
+ if ocr_config:
366
+ if ocr_config.window:
367
+ start_time = time.time()
368
+ while time.time() - start_time < 30:
369
+ if get_window(ocr_config.window):
370
+ break
371
+ logger.info(f"Window: {ocr_config.window} Could not be found, retrying in 1 second...")
372
+ time.sleep(1)
373
+ else:
374
+ logger.error(f"Window '{ocr_config.window}' not found within 30 seconds.")
375
+ sys.exit(1)
347
376
  logger.info(f"Starting OCR with configuration: Window: {ocr_config.window}, Rectangles: {len(ocr_config.rectangles)}, Engine 1: {ocr1}, Engine 2: {ocr2}, Two-pass OCR: {twopassocr}")
348
377
  if ocr_config:
349
378
  rectangles = list(filter(lambda rect: not rect.is_excluded, ocr_config.rectangles))
@@ -114,12 +114,14 @@ class Config:
114
114
  for sub_key in config[key]:
115
115
  self.__engine_config[key.lower()][sub_key.lower()] = self.__parse(config[key][sub_key])
116
116
 
117
- def get_general(self, value):
117
+ def get_general(self, value, default_value=None):
118
118
  if self.__provided_cli_args.get(value, None) is not None:
119
119
  return self.__provided_cli_args[value]
120
120
  try:
121
121
  return self.__general_config[value]
122
122
  except KeyError:
123
+ if default_value:
124
+ return default_value
123
125
  if value in self.__default_config:
124
126
  return self.__default_config[value]
125
127
  else:
@@ -222,9 +222,6 @@ class WindowsWindowTracker(threading.Thread):
222
222
  if self.window_active != is_active:
223
223
  on_window_activated(is_active)
224
224
  self.window_active = is_active
225
- elif ignore_window_visiblity:
226
- on_window_minimized(False)
227
- self.window_minimized = False
228
225
  else:
229
226
  is_minimized = win32gui.IsIconic(self.window_handle)
230
227
  if self.window_minimized != is_minimized:
@@ -308,8 +305,21 @@ class TextFiltering:
308
305
 
309
306
  def __init__(self):
310
307
  from pysbd import Segmenter
311
- self.segmenter = Segmenter(language='ja', clean=True)
308
+ self.segmenter = Segmenter(language=lang, clean=True)
312
309
  self.kana_kanji_regex = re.compile(r'[\u3041-\u3096\u30A1-\u30FA\u4E00-\u9FFF]')
310
+ self.chinese_common_regex = re.compile(r'[\u4E00-\u9FFF]')
311
+ self.english_regex = re.compile(r'[a-zA-Z0-9.,!?;:"\'()\[\]{}]')
312
+ self.kana_kanji_regex = re.compile(r'[\u3041-\u3096\u30A1-\u30FA\u4E00-\u9FFF]')
313
+ self.chinese_common_regex = re.compile(r'[\u4E00-\u9FFF]')
314
+ self.english_regex = re.compile(r'[a-zA-Z0-9.,!?;:"\'()\[\]{}]')
315
+ self.korean_regex = re.compile(r'[\uAC00-\uD7AF]')
316
+ self.arabic_regex = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
317
+ self.russian_regex = re.compile(r'[\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u1C80-\u1C8F]')
318
+ self.greek_regex = re.compile(r'[\u0370-\u03FF\u1F00-\u1FFF]')
319
+ self.hebrew_regex = re.compile(r'[\u0590-\u05FF\uFB1D-\uFB4F]')
320
+ self.thai_regex = re.compile(r'[\u0E00-\u0E7F]')
321
+ self.latin_extended_regex = re.compile(
322
+ r'[a-zA-Z\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u1D00-\u1D7F\u1D80-\u1DBF\u1E00-\u1EFF\u2C60-\u2C7F\uA720-\uA7FF\uAB30-\uAB6F]')
313
323
  try:
314
324
  from transformers import pipeline, AutoTokenizer
315
325
  import torch
@@ -337,7 +347,28 @@ class TextFiltering:
337
347
 
338
348
  orig_text_filtered = []
339
349
  for block in orig_text:
340
- block_filtered = self.kana_kanji_regex.findall(block)
350
+ if lang == "ja":
351
+ block_filtered = self.kana_kanji_regex.findall(block)
352
+ elif lang == "zh":
353
+ block_filtered = self.chinese_common_regex.findall(block)
354
+ elif lang == "ko":
355
+ block_filtered = self.korean_regex.findall(block)
356
+ elif lang == "ar":
357
+ block_filtered = self.arabic_regex.findall(block)
358
+ elif lang == "ru":
359
+ block_filtered = self.russian_regex.findall(block)
360
+ elif lang == "el":
361
+ block_filtered = self.greek_regex.findall(block)
362
+ elif lang == "he":
363
+ block_filtered = self.hebrew_regex.findall(block)
364
+ elif lang == "th":
365
+ block_filtered = self.thai_regex.findall(block)
366
+ elif lang in ["en", "fr", "de", "es", "it", "pt", "nl", "sv", "da", "no",
367
+ "fi"]: # Many European languages use extended Latin
368
+ block_filtered = self.latin_extended_regex.findall(block)
369
+ else:
370
+ block_filtered = self.english_regex.findall(block)
371
+
341
372
  if block_filtered:
342
373
  orig_text_filtered.append(''.join(block_filtered))
343
374
  else:
@@ -358,12 +389,12 @@ class TextFiltering:
358
389
  detection_results = self.pipe(new_blocks, top_k=3, truncation=True)
359
390
  for idx, block in enumerate(new_blocks):
360
391
  for result in detection_results[idx]:
361
- if result['label'] == 'ja':
392
+ if result['label'] == lang:
362
393
  final_blocks.append(block)
363
394
  break
364
395
  else:
365
396
  for block in new_blocks:
366
- if self.classify(block)[0] == 'ja':
397
+ if self.classify(block)[0] == lang:
367
398
  final_blocks.append(block)
368
399
 
369
400
  text = '\n'.join(final_blocks)
@@ -565,10 +596,16 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
565
596
 
566
597
  orig_text = []
567
598
  engine_color = config.get_general('engine_color')
599
+ # print(filtering)
600
+ #
601
+ #
602
+ # print(lang)
603
+
568
604
  if res:
569
605
  if filtering:
570
606
  text, orig_text = filtering(text, last_result)
571
- text = post_process(text)
607
+ if lang == "ja" or lang == "zh":
608
+ text = post_process(text)
572
609
  logger.opt(ansi=True).info(f'Text recognized in {t1 - t0:0.03f}s using <{engine_color}>{engine_instance.readable_name}</{engine_color}>: {text}')
573
610
  if notifications:
574
611
  notifier.send(title='owocr', message='Text recognized: ' + text)
@@ -588,6 +625,9 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
588
625
  else:
589
626
  logger.opt(ansi=True).info(f'<{engine_color}>{engine_instance.readable_name}</{engine_color}> reported an error after {t1 - t0:0.03f}s: {text}')
590
627
 
628
+ # print(orig_text)
629
+ # print(text)
630
+
591
631
  return orig_text, text
592
632
 
593
633
 
@@ -620,7 +660,7 @@ def run(read_from=None,
620
660
  screen_capture_event_bus=None,
621
661
  rectangle=None,
622
662
  text_callback=None,
623
- ignore_window_visible=False,
663
+ language=None,
624
664
  ):
625
665
  """
626
666
  Japanese OCR client
@@ -646,18 +686,45 @@ def run(read_from=None,
646
686
  :param screen_capture_combo: When reading with screen capture, specifies a combo to wait on for taking a screenshot instead of using the delay. As an example: "<ctrl>+<shift>+s". The list of keys can be found here: https://pynput.readthedocs.io/en/latest/keyboard.html#pynput.keyboard.Key
647
687
  """
648
688
 
649
- if not read_from:
689
+ if read_from is None:
650
690
  read_from = config.get_general('read_from')
651
691
 
652
- if not screen_capture_area:
692
+ if screen_capture_area is None:
653
693
  screen_capture_area = config.get_general('screen_capture_area')
654
694
 
655
- if not screen_capture_only_active_windows:
695
+ if screen_capture_only_active_windows is None:
656
696
  screen_capture_only_active_windows = config.get_general('screen_capture_only_active_windows')
657
697
 
658
- if not write_to:
698
+ if screen_capture_exclusions is None:
699
+ screen_capture_exclusions = config.get_general('screen_capture_exclusions')
700
+
701
+ if screen_capture_window is None:
702
+ screen_capture_window = config.get_general('screen_capture_window')
703
+
704
+ if screen_capture_delay_secs is None:
705
+ screen_capture_delay_secs = config.get_general('screen_capture_delay_secs')
706
+
707
+ if screen_capture_combo is None:
708
+ screen_capture_combo = config.get_general('screen_capture_combo')
709
+
710
+ if stop_running_flag is None:
711
+ stop_running_flag = config.get_general('stop_running_flag')
712
+
713
+ if screen_capture_event_bus is None:
714
+ screen_capture_event_bus = config.get_general('screen_capture_event_bus')
715
+
716
+ if rectangle is None:
717
+ rectangle = config.get_general('rectangle')
718
+
719
+ if text_callback is None:
720
+ text_callback = config.get_general('text_callback')
721
+
722
+ if write_to is None:
659
723
  write_to = config.get_general('write_to')
660
724
 
725
+ if language is None:
726
+ language = config.get_general('language', "ja")
727
+
661
728
  logger.configure(handlers=[{'sink': sys.stderr, 'format': config.get_general('logger_format')}])
662
729
 
663
730
  if config.has_config:
@@ -669,8 +736,8 @@ def run(read_from=None,
669
736
 
670
737
  global engine_instances
671
738
  global engine_keys
672
- global ignore_window_visiblity
673
- ignore_window_visiblity = ignore_window_visible
739
+ global lang
740
+ lang = language
674
741
  engine_instances = []
675
742
  config_engines = []
676
743
  engine_keys = []