GameSentenceMiner 2.15.9__py3-none-any.whl → 2.15.10__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/ocr/gsm_ocr_config.py +1 -1
- GameSentenceMiner/ocr/owocr_helper.py +21 -12
- GameSentenceMiner/owocr/owocr/run.py +2 -2
- GameSentenceMiner/util/configuration.py +3 -0
- GameSentenceMiner/util/text_log.py +2 -2
- GameSentenceMiner/web/database_api.py +783 -0
- GameSentenceMiner/web/events.py +178 -0
- GameSentenceMiner/web/stats.py +582 -0
- GameSentenceMiner/web/templates/database.html +277 -0
- GameSentenceMiner/web/templates/search.html +103 -0
- GameSentenceMiner/web/templates/stats.html +330 -0
- GameSentenceMiner/web/templates/text_replacements.html +211 -0
- GameSentenceMiner/web/templates/utility.html +2 -2
- GameSentenceMiner/web/texthooking_page.py +58 -316
- GameSentenceMiner/web/websockets.py +120 -0
- {gamesentenceminer-2.15.9.dist-info → gamesentenceminer-2.15.10.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.15.9.dist-info → gamesentenceminer-2.15.10.dist-info}/RECORD +21 -15
- GameSentenceMiner/web/templates/__init__.py +0 -0
- {gamesentenceminer-2.15.9.dist-info → gamesentenceminer-2.15.10.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.15.9.dist-info → gamesentenceminer-2.15.10.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.15.9.dist-info → gamesentenceminer-2.15.10.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.15.9.dist-info → gamesentenceminer-2.15.10.dist-info}/top_level.txt +0 -0
@@ -83,7 +83,7 @@ class OCRConfig:
|
|
83
83
|
]
|
84
84
|
|
85
85
|
def scale_to_custom_size(self, width, height):
|
86
|
-
self.rectangles = self.pre_scale_rectangles
|
86
|
+
self.rectangles = deepcopy(self.pre_scale_rectangles)
|
87
87
|
if self.coordinate_system and self.coordinate_system == "percentage":
|
88
88
|
for rectangle in self.rectangles:
|
89
89
|
rectangle.coordinates = [
|
@@ -424,13 +424,14 @@ def get_ocr2_image(crop_coords, og_image, ocr2_engine=None):
|
|
424
424
|
if not crop_coords or not get_ocr_optimize_second_scan():
|
425
425
|
return og_image
|
426
426
|
x1, y1, x2, y2 = crop_coords
|
427
|
-
x1 = max(0,
|
428
|
-
y1 = max(0,
|
429
|
-
x2 = max(
|
430
|
-
y2 = max(
|
427
|
+
x1 = min(max(0, x1), img.width)
|
428
|
+
y1 = min(max(0, y1), img.height)
|
429
|
+
x2 = min(max(0, x2), img.width)
|
430
|
+
y2 = min(max(0, y2), img.height)
|
431
431
|
og_image.save(os.path.join(get_temporary_directory(), "pre_oneocrcrop.png"))
|
432
432
|
return og_image.crop((x1, y1, x2, y2))
|
433
433
|
|
434
|
+
# TODO Get rid of this check, and just always convert to full res
|
434
435
|
LOCAL_OCR_ENGINES = ['easyocr', 'oneocr', 'rapidocr', 'mangaocr', 'winrtocr']
|
435
436
|
local_ocr = ocr2_engine in LOCAL_OCR_ENGINES
|
436
437
|
ocr_config_local = copy(ocr_config)
|
@@ -444,10 +445,17 @@ def get_ocr2_image(crop_coords, og_image, ocr2_engine=None):
|
|
444
445
|
obs_height = getattr(run.obs_screenshot_thread, 'height', None)
|
445
446
|
if not obs_width or not obs_height:
|
446
447
|
return return_original_image()
|
448
|
+
|
447
449
|
logger.debug(f"Getting OCR2 image with OBS dimensions: {obs_width}x{obs_height}")
|
448
450
|
|
449
451
|
img = obs.get_screenshot_PIL(compression=100, img_format="jpg")
|
452
|
+
|
450
453
|
ocr_config_local.scale_to_custom_size(img.width, img.height)
|
454
|
+
|
455
|
+
# If img.width and height is the same as obs, no need to scale coords, tolerance of .1%
|
456
|
+
if abs(img.width - obs_width) <= 0.1 * obs_width and abs(img.height - obs_height) <= 0.1 * obs_height:
|
457
|
+
logger.info("Image size matches OBS size, no need to scale coordinates.")
|
458
|
+
return return_original_image()
|
451
459
|
|
452
460
|
# If no crop or optimization, just apply config and return
|
453
461
|
if not crop_coords or not get_ocr_optimize_second_scan():
|
@@ -464,18 +472,19 @@ def get_ocr2_image(crop_coords, og_image, ocr2_engine=None):
|
|
464
472
|
y1 = int(crop_coords[1] * height_ratio)
|
465
473
|
x2 = int(crop_coords[2] * width_ratio)
|
466
474
|
y2 = int(crop_coords[3] * height_ratio)
|
467
|
-
logger.debug(f"Scaled crop coordinates: {(x1, y1, x2, y2)}")
|
468
475
|
|
469
476
|
# Clamp coordinates to image bounds
|
470
|
-
x1 = max(0,
|
471
|
-
y1 = max(0,
|
472
|
-
x2 = max(
|
473
|
-
y2 = max(
|
474
|
-
|
475
|
-
img = run.apply_ocr_config_to_image(img, ocr_config_local, is_secondary=False)
|
477
|
+
x1 = min(max(0, x1), img.width)
|
478
|
+
y1 = min(max(0, y1), img.height)
|
479
|
+
x2 = min(max(0, x2), img.width)
|
480
|
+
y2 = min(max(0, y2), img.height)
|
476
481
|
|
482
|
+
logger.debug(f"Scaled crop coordinates: {(x1, y1, x2, y2)}")
|
483
|
+
|
484
|
+
img = run.apply_ocr_config_to_image(img, ocr_config_local, is_secondary=False)
|
477
485
|
|
478
|
-
|
486
|
+
ret = img.crop((x1, y1, x2, y2))
|
487
|
+
return ret
|
479
488
|
|
480
489
|
def process_task_queue():
|
481
490
|
while True:
|
@@ -1029,11 +1029,11 @@ class OBSScreenshotThread(threading.Thread):
|
|
1029
1029
|
"sceneItemTransform").get("sourceWidth") or self.width
|
1030
1030
|
self.source_height = self.current_source.get(
|
1031
1031
|
"sceneItemTransform").get("sourceHeight") or self.height
|
1032
|
-
if self.source_width and self.source_height and not self.is_manual_ocr and
|
1032
|
+
if self.source_width and self.source_height and not self.is_manual_ocr and get_ocr_two_pass_ocr():
|
1033
1033
|
self.width, self.height = scale_down_width_height(
|
1034
1034
|
self.source_width, self.source_height)
|
1035
1035
|
logger.info(
|
1036
|
-
f"Using OBS source dimensions: {self.
|
1036
|
+
f"Using OBS source dimensions: {self.width}x{self.height}")
|
1037
1037
|
else:
|
1038
1038
|
self.width = self.source_width or 1280
|
1039
1039
|
self.height = self.source_height or 720
|
@@ -585,6 +585,9 @@ class Advanced:
|
|
585
585
|
multi_line_sentence_storage_field: str = ''
|
586
586
|
ocr_websocket_port: int = 9002
|
587
587
|
texthooker_communication_websocket_port: int = 55001
|
588
|
+
afk_timer_seconds: int = 120
|
589
|
+
session_gap_seconds: int = 3600
|
590
|
+
streak_requirement_hours: float = 0.01 # 1 second required per day to keep your streak by default
|
588
591
|
|
589
592
|
def __post_init__(self):
|
590
593
|
if self.plaintext_websocket_port == -1:
|
@@ -119,7 +119,7 @@ def strip_whitespace_and_punctuation(text: str) -> str:
|
|
119
119
|
return re.sub(r'[\s 、。「」【】《》., ]', '', text).strip()
|
120
120
|
|
121
121
|
|
122
|
-
def lines_match(texthooker_sentence, anki_sentence):
|
122
|
+
def lines_match(texthooker_sentence, anki_sentence, similarity_threshold=80) -> bool:
|
123
123
|
# Replace newlines, spaces, other whitespace characters, AND japanese punctuation
|
124
124
|
texthooker_sentence = strip_whitespace_and_punctuation(texthooker_sentence)
|
125
125
|
anki_sentence = strip_whitespace_and_punctuation(anki_sentence)
|
@@ -129,7 +129,7 @@ def lines_match(texthooker_sentence, anki_sentence):
|
|
129
129
|
logger.debug(f"One contains the other: {texthooker_sentence} in {anki_sentence} - Similarity: {similarity}")
|
130
130
|
elif anki_sentence in texthooker_sentence:
|
131
131
|
logger.debug(f"One contains the other: {anki_sentence} in {texthooker_sentence} - Similarity: {similarity}")
|
132
|
-
return (anki_sentence in texthooker_sentence) or (texthooker_sentence in anki_sentence) or (similarity >=
|
132
|
+
return (anki_sentence in texthooker_sentence) or (texthooker_sentence in anki_sentence) or (similarity >= similarity_threshold)
|
133
133
|
|
134
134
|
|
135
135
|
def get_text_event(last_note) -> GameLine:
|