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 +18 -4
- GameSentenceMiner/ocr/owocr_area_selector.py +20 -11
- GameSentenceMiner/ocr/owocr_helper.py +11 -5
- GameSentenceMiner/owocr/owocr/ocr.py +66 -53
- GameSentenceMiner/owocr/owocr/run.py +143 -124
- {gamesentenceminer-2.10.12.dist-info → gamesentenceminer-2.10.14.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.10.12.dist-info → gamesentenceminer-2.10.14.dist-info}/RECORD +11 -11
- {gamesentenceminer-2.10.12.dist-info → gamesentenceminer-2.10.14.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.10.12.dist-info → gamesentenceminer-2.10.14.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.10.12.dist-info → gamesentenceminer-2.10.14.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.10.12.dist-info → gamesentenceminer-2.10.14.dist-info}/top_level.txt +0 -0
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(
|
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
|
-
|
177
|
+
retry -= 1
|
171
178
|
connecting = False
|
172
179
|
|
173
|
-
def connect_to_obs_sync(
|
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
|
-
|
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
|
-
|
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 = "
|
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":
|
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 = "
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
832
|
-
|
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
|
-
|
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.
|
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
|
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
|
-
|
446
|
+
sct = mss.mss()
|
445
447
|
|
446
448
|
if self.screencapture_mode == 1:
|
447
|
-
mon =
|
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
|
651
|
-
if self.
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
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
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
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
|
-
|
671
|
-
|
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
|
-
|
676
|
-
|
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
|
-
|
680
|
-
|
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
|
-
|
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
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
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
|
-
|
831
|
-
|
832
|
-
|
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
|
-
|
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
|
-
|
1169
|
-
|
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
|
1237
|
-
|
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()
|
@@ -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=
|
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=
|
22
|
-
GameSentenceMiner/ocr/owocr_helper.py,sha256=
|
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=
|
29
|
-
GameSentenceMiner/owocr/owocr/run.py,sha256=
|
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.
|
66
|
-
gamesentenceminer-2.10.
|
67
|
-
gamesentenceminer-2.10.
|
68
|
-
gamesentenceminer-2.10.
|
69
|
-
gamesentenceminer-2.10.
|
70
|
-
gamesentenceminer-2.10.
|
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,,
|
File without changes
|
{gamesentenceminer-2.10.12.dist-info → gamesentenceminer-2.10.14.dist-info}/entry_points.txt
RENAMED
File without changes
|
{gamesentenceminer-2.10.12.dist-info → gamesentenceminer-2.10.14.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
File without changes
|