GameSentenceMiner 2.7.4__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.
@@ -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
@@ -153,7 +153,7 @@ def text_callback(text, rectangle, time):
153
153
  if not text:
154
154
  return
155
155
  if not twopassocr or not ocr2:
156
- websocket_server_thread.send_text(text, datetime.now())
156
+ websocket_server_thread.send_text(text, time if time else datetime.now())
157
157
  return
158
158
  with mss.mss() as sct:
159
159
  line_time = time if time else datetime.now()
@@ -1,8 +1,9 @@
1
- from .run import run
1
+ from .run import run, init_config
2
+
2
3
 
3
4
  def main():
4
5
  run()
5
6
 
6
-
7
7
  if __name__ == '__main__':
8
+ init_config(True)
8
9
  main()
@@ -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, time=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, time)
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=None,
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
- custom_left, custom_top, custom_width, custom_height = [int(c.strip()) for c in screen_capture_area.split(',')]
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
- time = datetime.datetime.now()
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, time=time)
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, time=time)
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, time=time)
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, time=time)
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, time=time)
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.7.4
3
+ Version: 2.7.6
4
4
  Summary: A tool for mining sentences from games. Update: Multi-Line Mining! Fixed!
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -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=brybSe7hRy13JAUwuSs6WqOPFH6to0detbIPVhnkinA,9062
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
@@ -23,21 +23,21 @@ GameSentenceMiner/downloader/download_tools.py,sha256=mI1u_FGBmBqDIpCH3jOv8DOoZ3
23
23
  GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  GameSentenceMiner/ocr/ocrconfig.py,sha256=hTROOZ3On2HngXKxwQFZvnr5AxlmlMV0mPxv-F3NbMg,6476
25
25
  GameSentenceMiner/ocr/owocr_area_selector.py,sha256=6MQPCSAhuyOSEDEgc-w6jVdsELjxstzI2Zhz0JBkG8g,11703
26
- GameSentenceMiner/ocr/owocr_helper.py,sha256=UsweqRllC_Tda4swamAcO2ZkuG0LUGHaJI5YXmTDUCQ,11822
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=nkaZGTCZsPDKcej2O0S1SHkyzHJEboTUBc9izyGPvqw,92
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=d0uLAQotZ6xhUSWWEXW07c0-EYjeTmumHxIEsT01Iuo,36604
32
- GameSentenceMiner/owocr/owocr/run.py,sha256=dkGNC5DtUN1jmon-m--f_nMJTuXHT6muKgM_7hXEAtA,46678
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.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
39
- gamesentenceminer-2.7.4.dist-info/METADATA,sha256=91-FbrZOoHnvNM2FjS-2vgRUpk1__KcqmBEAiNnI1A8,5839
40
- gamesentenceminer-2.7.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
41
- gamesentenceminer-2.7.4.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
42
- gamesentenceminer-2.7.4.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
43
- gamesentenceminer-2.7.4.dist-info/RECORD,,
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,,