GameSentenceMiner 2.7.5__py3-none-any.whl → 2.7.6__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/gametext.py +4 -0
- GameSentenceMiner/owocr/owocr/__main__.py +3 -2
- GameSentenceMiner/owocr/owocr/ocr.py +79 -0
- GameSentenceMiner/owocr/owocr/run.py +25 -11
- {gamesentenceminer-2.7.5.dist-info → gamesentenceminer-2.7.6.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.7.5.dist-info → gamesentenceminer-2.7.6.dist-info}/RECORD +10 -10
- {gamesentenceminer-2.7.5.dist-info → gamesentenceminer-2.7.6.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.7.5.dist-info → gamesentenceminer-2.7.6.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.7.5.dist-info → gamesentenceminer-2.7.6.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.7.5.dist-info → gamesentenceminer-2.7.6.dist-info}/top_level.txt +0 -0
GameSentenceMiner/gametext.py
CHANGED
@@ -44,6 +44,9 @@ class GameLine:
|
|
44
44
|
return self.next.time
|
45
45
|
return 0
|
46
46
|
|
47
|
+
def __str__(self):
|
48
|
+
return str({"text": self.text, "time": self.time})
|
49
|
+
|
47
50
|
@dataclass
|
48
51
|
class GameText:
|
49
52
|
values: list[GameLine]
|
@@ -71,6 +74,7 @@ class GameText:
|
|
71
74
|
if not line_text:
|
72
75
|
return
|
73
76
|
new_line = GameLine(line_text, line_time if line_time else datetime.now(), self.values[-1] if self.values else None, None, self.game_line_index)
|
77
|
+
logger.debug(f"Adding line: {new_line}")
|
74
78
|
self.game_line_index += 1
|
75
79
|
if self.values:
|
76
80
|
self.values[-1].next = new_line
|
@@ -973,3 +973,82 @@ class OCRSpace:
|
|
973
973
|
|
974
974
|
def _preprocess(self, img):
|
975
975
|
return limit_image_size(img, self.max_byte_size)
|
976
|
+
|
977
|
+
|
978
|
+
class GeminiOCR:
|
979
|
+
name = 'gemini'
|
980
|
+
readable_name = 'Gemini'
|
981
|
+
key = 'm'
|
982
|
+
available = False
|
983
|
+
|
984
|
+
def __init__(self, config={'api_key': None}):
|
985
|
+
# if "google-generativeai" not in sys.modules:
|
986
|
+
# logger.warning('google-generativeai not available, GeminiOCR will not work!')
|
987
|
+
# else:
|
988
|
+
import google.generativeai as genai
|
989
|
+
try:
|
990
|
+
self.api_key = config['api_key']
|
991
|
+
if not self.api_key:
|
992
|
+
logger.warning('Gemini API key not provided, GeminiOCR will not work!')
|
993
|
+
else:
|
994
|
+
genai.configure(api_key=self.api_key)
|
995
|
+
self.model = genai.GenerativeModel(config['model'])
|
996
|
+
self.available = True
|
997
|
+
logger.info('Gemini (using google-generativeai) ready')
|
998
|
+
except KeyError:
|
999
|
+
logger.warning('Gemini API key not found in config, GeminiOCR will not work!')
|
1000
|
+
except Exception as e:
|
1001
|
+
logger.error(f'Error configuring google-generativeai: {e}')
|
1002
|
+
|
1003
|
+
def __call__(self, img_or_path):
|
1004
|
+
if not self.available:
|
1005
|
+
return (False, 'GeminiOCR is not available due to missing API key or configuration error.')
|
1006
|
+
|
1007
|
+
try:
|
1008
|
+
import google.generativeai as genai
|
1009
|
+
if isinstance(img_or_path, str) or isinstance(img_or_path, Path):
|
1010
|
+
img = Image.open(img_or_path).convert("RGB")
|
1011
|
+
elif isinstance(img_or_path, Image.Image):
|
1012
|
+
img = img_or_path.convert("RGB")
|
1013
|
+
else:
|
1014
|
+
raise ValueError(f'img_or_path must be a path or PIL.Image, instead got: {img_or_path}')
|
1015
|
+
|
1016
|
+
img_bytes = self._preprocess(img)
|
1017
|
+
if not img_bytes:
|
1018
|
+
return (False, 'Error processing image for Gemini.')
|
1019
|
+
|
1020
|
+
contents = [
|
1021
|
+
{
|
1022
|
+
'parts': [
|
1023
|
+
{
|
1024
|
+
'inline_data': {
|
1025
|
+
'mime_type': 'image/png',
|
1026
|
+
'data': base64.b64encode(img_bytes).decode('utf-8')
|
1027
|
+
}
|
1028
|
+
},
|
1029
|
+
{
|
1030
|
+
'text': 'As Quick as Possible, Give me the text from this image, no other output. If there is no text, return nothing.'
|
1031
|
+
}
|
1032
|
+
]
|
1033
|
+
}
|
1034
|
+
]
|
1035
|
+
|
1036
|
+
response = self.model.generate_content(contents)
|
1037
|
+
text_output = response.text.strip()
|
1038
|
+
|
1039
|
+
return (True, text_output)
|
1040
|
+
|
1041
|
+
except FileNotFoundError:
|
1042
|
+
return (False, f'File not found: {img_or_path}')
|
1043
|
+
except Exception as e:
|
1044
|
+
return (False, f'Gemini API request failed: {e}')
|
1045
|
+
|
1046
|
+
def _preprocess(self, img):
|
1047
|
+
try:
|
1048
|
+
from io import BytesIO
|
1049
|
+
img_io = BytesIO()
|
1050
|
+
img.save(img_io, 'PNG') # Save as PNG
|
1051
|
+
return img_io.getvalue()
|
1052
|
+
except Exception as e:
|
1053
|
+
logger.error(f'Error preprocessing image for Gemini: {e}')
|
1054
|
+
return None
|
@@ -381,7 +381,7 @@ class AutopauseTimer:
|
|
381
381
|
self.timer_thread.join()
|
382
382
|
|
383
383
|
def _countdown(self):
|
384
|
-
seconds = self.timeout
|
384
|
+
seconds = self.timeout if self.timeout else 1
|
385
385
|
while seconds > 0 and not self.stop_event.is_set():
|
386
386
|
time.sleep(1)
|
387
387
|
seconds -= 1
|
@@ -539,7 +539,7 @@ def are_images_identical(img1, img2):
|
|
539
539
|
return (img1.shape == img2.shape) and (img1 == img2).all()
|
540
540
|
|
541
541
|
|
542
|
-
def process_and_write_results(img_or_path, write_to, notifications, last_result, filtering, engine=None, rectangle=None,
|
542
|
+
def process_and_write_results(img_or_path, write_to, notifications, last_result, filtering, engine=None, rectangle=None, start_time=None):
|
543
543
|
global engine_index
|
544
544
|
if auto_pause_handler:
|
545
545
|
auto_pause_handler.stop()
|
@@ -571,7 +571,7 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
571
571
|
elif write_to == 'clipboard':
|
572
572
|
pyperclipfix.copy(text)
|
573
573
|
elif write_to == "callback":
|
574
|
-
txt_callback(text, rectangle,
|
574
|
+
txt_callback(text, rectangle, start_time)
|
575
575
|
elif write_to:
|
576
576
|
with Path(write_to).open('a', encoding='utf-8') as f:
|
577
577
|
f.write(text + '\n')
|
@@ -600,7 +600,7 @@ def run(read_from=None,
|
|
600
600
|
ignore_flag=None,
|
601
601
|
delete_images=None,
|
602
602
|
notifications=None,
|
603
|
-
auto_pause=
|
603
|
+
auto_pause=0,
|
604
604
|
combo_pause=None,
|
605
605
|
combo_engine_switch=None,
|
606
606
|
screen_capture_area=None,
|
@@ -638,6 +638,18 @@ def run(read_from=None,
|
|
638
638
|
: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
|
639
639
|
"""
|
640
640
|
|
641
|
+
if not read_from:
|
642
|
+
read_from = config.get_general('read_from')
|
643
|
+
|
644
|
+
if not screen_capture_area:
|
645
|
+
screen_capture_area = config.get_general('screen_capture_area')
|
646
|
+
|
647
|
+
if not screen_capture_only_active_windows:
|
648
|
+
screen_capture_only_active_windows = config.get_general('screen_capture_only_active_windows')
|
649
|
+
|
650
|
+
if not write_to:
|
651
|
+
write_to = config.get_general('write_to')
|
652
|
+
|
641
653
|
logger.configure(handlers=[{'sink': sys.stderr, 'format': config.get_general('logger_format')}])
|
642
654
|
|
643
655
|
if config.has_config:
|
@@ -682,6 +694,7 @@ def run(read_from=None,
|
|
682
694
|
global first_pressed
|
683
695
|
global notifier
|
684
696
|
global auto_pause_handler
|
697
|
+
custom_left = None
|
685
698
|
terminated = False
|
686
699
|
paused = pause_at_startup
|
687
700
|
just_unpaused = True
|
@@ -818,7 +831,8 @@ def run(read_from=None,
|
|
818
831
|
|
819
832
|
sct_params = {'top': coord_top, 'left': coord_left, 'width': coord_width, 'height': coord_height}
|
820
833
|
logger.opt(ansi=True).info(f'Selected coordinates: {coord_left},{coord_top},{coord_width},{coord_height}')
|
821
|
-
|
834
|
+
if len(screen_capture_area.split(',')) == 4:
|
835
|
+
custom_left, custom_top, custom_width, custom_height = [int(c.strip()) for c in screen_capture_area.split(',')]
|
822
836
|
if screencapture_mode == 2 or screen_capture_window:
|
823
837
|
area_invalid_error = '"screen_capture_area" must be empty, "screen_N" where N is a screen number starting from 1, a valid set of coordinates, or a valid window name'
|
824
838
|
if sys.platform == 'darwin':
|
@@ -907,7 +921,7 @@ def run(read_from=None,
|
|
907
921
|
logger.opt(ansi=True).info(f"Reading from {read_from_readable}, writing to {write_to_readable} using <{engine_color}>{engine_instances[engine_index].readable_name}</{engine_color}>{' (paused)' if paused else ''}")
|
908
922
|
|
909
923
|
while not terminated and not stop_running_flag:
|
910
|
-
|
924
|
+
start_time = datetime.datetime.now()
|
911
925
|
if read_from == 'websocket':
|
912
926
|
while True:
|
913
927
|
try:
|
@@ -917,7 +931,7 @@ def run(read_from=None,
|
|
917
931
|
else:
|
918
932
|
if not paused:
|
919
933
|
img = Image.open(io.BytesIO(item))
|
920
|
-
process_and_write_results(img, write_to, notifications, None, None,
|
934
|
+
process_and_write_results(img, write_to, notifications, None, None, start_time=start_time)
|
921
935
|
elif read_from == 'unixsocket':
|
922
936
|
while True:
|
923
937
|
try:
|
@@ -927,7 +941,7 @@ def run(read_from=None,
|
|
927
941
|
else:
|
928
942
|
if not paused:
|
929
943
|
img = Image.open(io.BytesIO(item))
|
930
|
-
process_and_write_results(img, write_to, notifications, None, None,
|
944
|
+
process_and_write_results(img, write_to, notifications, None, None, start_time=start_time)
|
931
945
|
elif read_from == 'clipboard':
|
932
946
|
process_clipboard = False
|
933
947
|
if windows_clipboard_polling:
|
@@ -975,7 +989,7 @@ def run(read_from=None,
|
|
975
989
|
process_clipboard = True
|
976
990
|
|
977
991
|
if process_clipboard:
|
978
|
-
process_and_write_results(img, write_to, notifications, None, None,
|
992
|
+
process_and_write_results(img, write_to, notifications, None, None, start_time=start_time)
|
979
993
|
|
980
994
|
just_unpaused = False
|
981
995
|
|
@@ -1054,7 +1068,7 @@ def run(read_from=None,
|
|
1054
1068
|
draw.rectangle((left, top, right, bottom), fill=(0, 0, 0, 0))
|
1055
1069
|
# draw.rectangle((left, top, right, bottom), fill=(0, 0, 0))
|
1056
1070
|
# img.save(os.path.join(get_temporary_directory(), 'screencapture.png'), 'png')
|
1057
|
-
res, _ = process_and_write_results(img, write_to, notifications, last_result, filtering, rectangle=rectangle,
|
1071
|
+
res, _ = process_and_write_results(img, write_to, notifications, last_result, filtering, rectangle=rectangle, start_time=start_time)
|
1058
1072
|
if res:
|
1059
1073
|
last_result = (res, engine_index)
|
1060
1074
|
delay = screen_capture_delay_secs
|
@@ -1077,7 +1091,7 @@ def run(read_from=None,
|
|
1077
1091
|
except (UnidentifiedImageError, OSError) as e:
|
1078
1092
|
logger.warning(f'Error while reading file {path}: {e}')
|
1079
1093
|
else:
|
1080
|
-
process_and_write_results(img, write_to, notifications, None, None,
|
1094
|
+
process_and_write_results(img, write_to, notifications, None, None, start_time=start_time)
|
1081
1095
|
img.close()
|
1082
1096
|
if delete_images:
|
1083
1097
|
Path.unlink(path)
|
@@ -4,7 +4,7 @@ GameSentenceMiner/config_gui.py,sha256=-1PanqdtTKiwItxeyt0piXrWo7lGMWwrC4iSo4NiP
|
|
4
4
|
GameSentenceMiner/configuration.py,sha256=TIL8yCr-FOScCA4OJt-BAtjEb50wtqFFrpmN-UlaQh4,20405
|
5
5
|
GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
|
6
6
|
GameSentenceMiner/ffmpeg.py,sha256=iUj-1uLLJla6jjGDKuc1FbcXjMogYpoDhrQqCbQFcWA,13359
|
7
|
-
GameSentenceMiner/gametext.py,sha256=
|
7
|
+
GameSentenceMiner/gametext.py,sha256=AAke4swwmN16da0IpyL5xMhU23nTz_c6z2kMfXPYv-8,9194
|
8
8
|
GameSentenceMiner/gsm.py,sha256=fcczDPDcC0vDLAPZtIY64mU3Di3RZjR40tuLIiwB5lE,24827
|
9
9
|
GameSentenceMiner/model.py,sha256=JdnkT4VoPOXmOpRgFdvERZ09c9wLN6tUJxdrKlGZcqo,5305
|
10
10
|
GameSentenceMiner/notification.py,sha256=FY39ChSRK0Y8TQ6lBGsLnpZUFPtFpSy2tweeXVoV7kc,2809
|
@@ -25,19 +25,19 @@ GameSentenceMiner/ocr/ocrconfig.py,sha256=hTROOZ3On2HngXKxwQFZvnr5AxlmlMV0mPxv-F
|
|
25
25
|
GameSentenceMiner/ocr/owocr_area_selector.py,sha256=6MQPCSAhuyOSEDEgc-w6jVdsELjxstzI2Zhz0JBkG8g,11703
|
26
26
|
GameSentenceMiner/ocr/owocr_helper.py,sha256=yR_W_tK6w7c_zDj48d1B6-9f61fBD3PPnt_FSSPleLs,11840
|
27
27
|
GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
|
28
|
-
GameSentenceMiner/owocr/owocr/__main__.py,sha256=
|
28
|
+
GameSentenceMiner/owocr/owocr/__main__.py,sha256=r8MI6RAmbkTWqOJ59uvXoDS7CSw5jX5war9ULGWELrA,128
|
29
29
|
GameSentenceMiner/owocr/owocr/config.py,sha256=738QCJHEWpFhMh966plOcXYWwcshSiRsxjjIwldeTtI,7461
|
30
30
|
GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
|
31
|
-
GameSentenceMiner/owocr/owocr/ocr.py,sha256=
|
32
|
-
GameSentenceMiner/owocr/owocr/run.py,sha256=
|
31
|
+
GameSentenceMiner/owocr/owocr/ocr.py,sha256=t0kU2GQyW0gf0NGqaYiOO7SjYgX8mQXLaNKJ8Eup6mg,39704
|
32
|
+
GameSentenceMiner/owocr/owocr/run.py,sha256=HE5plzuX9297q16b6ivSeGn1wZ4A3-hR-XfoSuM6PQs,47272
|
33
33
|
GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=f_YIV4-2OEZ21CAEZ_pbqeTb5D28PS82Tig4ZSnkY4Q,3096
|
34
34
|
GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
35
35
|
GameSentenceMiner/vad/silero_trim.py,sha256=ULf3zwS-JMsY82cKF7gZxREHw8L6lgpWF2U1YqgE9Oc,1681
|
36
36
|
GameSentenceMiner/vad/vosk_helper.py,sha256=125X8C9NxFPlWWpoNsbOnEqKx8RCjXN109zNx_QXhyg,6070
|
37
37
|
GameSentenceMiner/vad/whisper_helper.py,sha256=JJ-iltCh813XdjyEw0Wn5DaErf6PDqfH0Efu1Md8cIY,3543
|
38
|
-
gamesentenceminer-2.7.
|
39
|
-
gamesentenceminer-2.7.
|
40
|
-
gamesentenceminer-2.7.
|
41
|
-
gamesentenceminer-2.7.
|
42
|
-
gamesentenceminer-2.7.
|
43
|
-
gamesentenceminer-2.7.
|
38
|
+
gamesentenceminer-2.7.6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
39
|
+
gamesentenceminer-2.7.6.dist-info/METADATA,sha256=MdR2tq4Bl2c5u4NPw_6bS-dOe3npgNU3Zzv8c9qcKEo,5839
|
40
|
+
gamesentenceminer-2.7.6.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
41
|
+
gamesentenceminer-2.7.6.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
|
42
|
+
gamesentenceminer-2.7.6.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
|
43
|
+
gamesentenceminer-2.7.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|