GameSentenceMiner 2.8.48__py3-none-any.whl → 2.8.49__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.
@@ -6,6 +6,7 @@ from enum import Enum
6
6
  from typing import List, Any, Optional
7
7
 
8
8
  import google.generativeai as genai
9
+ from google.generativeai import GenerationConfig
9
10
  from groq import Groq
10
11
 
11
12
  from GameSentenceMiner.configuration import get_config, Ai, logger
@@ -98,7 +99,14 @@ class GeminiAI(AIManager):
98
99
  try:
99
100
  genai.configure(api_key=self.ai_config.api_key)
100
101
  model_name = self.ai_config.model
101
- self.model = genai.GenerativeModel(model_name)
102
+ self.model = genai.GenerativeModel(model_name,
103
+ generation_config=GenerationConfig(
104
+ temperature=0.5,
105
+ max_output_tokens=1024,
106
+ top_p=1,
107
+ stop_sequences=None,
108
+ )
109
+ )
102
110
  self.logger.debug(f"GeminiAIManager initialized with model: {model_name}")
103
111
  except Exception as e:
104
112
  self.logger.error(f"Failed to initialize Gemini API: {e}")
GameSentenceMiner/obs.py CHANGED
@@ -18,6 +18,7 @@ obs_process_pid = None
18
18
  OBS_PID_FILE = os.path.join(configuration.get_app_directory(), 'obs-studio', 'obs_pid.txt')
19
19
  obs_connection_manager = None
20
20
  logging.getLogger("obsws_python").setLevel(logging.CRITICAL)
21
+ connecting = False
21
22
 
22
23
  class OBSConnectionManager(threading.Thread):
23
24
  def __init__(self):
@@ -29,9 +30,9 @@ class OBSConnectionManager(threading.Thread):
29
30
  while self.running:
30
31
  time.sleep(1)
31
32
  try:
32
- if not client or not client.get_version():
33
+ if not client or not client.get_version() and not connecting:
33
34
  logger.info("OBS WebSocket not connected. Attempting to reconnect...")
34
- connect_to_obs()
35
+ asyncio.run(connect_to_obs())
35
36
  except Exception as e:
36
37
  logger.debug(f"Error checking OBS connection: {e}")
37
38
 
@@ -135,14 +136,15 @@ def get_obs_websocket_config_values():
135
136
  reload_config()
136
137
 
137
138
  async def connect_to_obs(retry_count=0):
138
- global client, obs_connection_manager, event_client
139
- if not get_config().obs.enabled or client:
139
+ global client, obs_connection_manager, event_client, connecting
140
+ if not get_config().obs.enabled:
140
141
  return
141
142
 
142
143
  if util.is_windows():
143
144
  get_obs_websocket_config_values()
144
145
 
145
146
  while True:
147
+ connecting = True
146
148
  try:
147
149
  client = obs.ReqClient(
148
150
  host=get_config().obs.host,
@@ -156,6 +158,7 @@ async def connect_to_obs(retry_count=0):
156
158
  password=get_config().obs.password,
157
159
  timeout=1,
158
160
  )
161
+ logger.info("Connected to OBS WebSocket.")
159
162
  if not obs_connection_manager:
160
163
  obs_connection_manager = OBSConnectionManager()
161
164
  obs_connection_manager.start()
@@ -164,6 +167,7 @@ async def connect_to_obs(retry_count=0):
164
167
  except Exception as e:
165
168
  await asyncio.sleep(1)
166
169
  retry_count += 1
170
+ connecting = False
167
171
 
168
172
  def connect_to_obs_sync(retry_count=0):
169
173
  global client, obs_connection_manager, event_client
@@ -207,16 +207,20 @@ all_cords = None
207
207
  rectangles = None
208
208
  last_ocr2_result = ""
209
209
 
210
- def do_second_ocr(ocr1_text, time, img, filtering):
210
+ def do_second_ocr(ocr1_text, time, img, filtering, scrolling=False):
211
211
  global twopassocr, ocr2, last_ocr2_result
212
212
  try:
213
- orig_text, text = run.process_and_write_results(img, None, None, None, None,
213
+ orig_text, text = run.process_and_write_results(img, None, last_ocr2_result, filtering, None,
214
214
  engine=ocr2)
215
- if fuzz.ratio(last_ocr2_result, text) >= 80:
215
+ print(filtering)
216
+ print(last_ocr2_result)
217
+ if scrolling:
218
+ return text
219
+ if fuzz.ratio(last_ocr2_result, orig_text) >= 80:
216
220
  logger.info("Seems like the same text from previous ocr2 result, not sending")
217
221
  return
218
222
  save_result_image(img)
219
- last_ocr2_result = text
223
+ last_ocr2_result = orig_text
220
224
  send_result(text, time)
221
225
  except json.JSONDecodeError:
222
226
  print("Invalid JSON received.")
@@ -243,15 +247,19 @@ def send_result(text, time):
243
247
  websocket_server_thread.send_text(text, time)
244
248
 
245
249
 
250
+ previous_text_list = []
246
251
  previous_text = "" # Store last OCR result
252
+ previous_ocr1_result = "" # Store last OCR1 result
247
253
  last_oneocr_time = None # Store last OCR time
248
254
  text_stable_start_time = None # Store the start time when text becomes stable
249
255
  previous_img = None
250
256
  orig_text_result = "" # Store original text result
251
257
  TEXT_APPEARENCE_DELAY = get_ocr_scan_rate() * 1000 + 500 # Adjust as needed
258
+ force_stable = False
259
+ scrolling_text_images = []
252
260
 
253
261
  def text_callback(text, orig_text, time, img=None, came_from_ss=False, filtering=None):
254
- global twopassocr, ocr2, previous_text, last_oneocr_time, text_stable_start_time, orig_text_result, previous_img
262
+ global twopassocr, ocr2, previous_text, last_oneocr_time, text_stable_start_time, orig_text_result, previous_img, force_stable, previous_ocr1_result, scrolling_text_images, previous_text_list
255
263
  orig_text_string = ''.join([item for item in orig_text if item is not None]) if orig_text else ""
256
264
  if came_from_ss:
257
265
  save_result_image(img)
@@ -272,25 +280,48 @@ def text_callback(text, orig_text, time, img=None, came_from_ss=False, filtering
272
280
  text_stable_start_time = None
273
281
  last_oneocr_time = None
274
282
  return
275
- if not text:
283
+ if not text or force_stable:
284
+ # if scrolling_text_images:
285
+ # stable_time = text_stable_start_time
286
+ # full_text = "".join([do_second_ocr(orig_text_string, line_start_time, img, filtering, True) for img in scrolling_text_images])
287
+ # scrolling_text_images = []
288
+ # send_result(full_text, stable_time)
289
+ # orig_text_result = orig_text_string
290
+ # previous_text = previous_text
291
+ # previous_img = None
292
+ # text_stable_start_time = None
293
+ # last_oneocr_time = None
294
+ force_stable = False
276
295
  if previous_text:
277
296
  if text_stable_start_time:
278
297
  stable_time = text_stable_start_time
279
298
  previous_img_local = previous_img
280
- if fuzz.ratio(orig_text_string, previous_text) >= 80:
299
+ if fuzz.ratio(orig_text_string, previous_ocr1_result) >= 90:
281
300
  logger.info("Seems like Text we already sent, not doing anything.")
282
301
  return
283
302
  orig_text_result = orig_text_string
284
- previous_text = previous_text
303
+ previous_ocr1_result = previous_text
285
304
  do_second_ocr(previous_text, stable_time, previous_img_local, filtering)
286
305
  previous_img = None
287
306
  text_stable_start_time = None
288
307
  last_oneocr_time = None
289
308
  return
290
309
  return
310
+ # elif previous_text_list and all(
311
+ # fuzz.partial_ratio(token, prev_token) >= 95 for token in orig_text for prev_token in
312
+ # previous_text_list[1:]):
313
+ # logger.info(f"Previous text: {previous_text_list}. Current text: {orig_text}.")
314
+ # logger.info("Seems like Scrolling text potentially...")
315
+ # previous_img_local = previous_img
316
+ # scrolling_text_images.append(previous_img_local)
317
+ # previous_text_list = orig_text
318
+ # previous_text = orig_text_string
319
+ # return
320
+
291
321
  if not text_stable_start_time:
292
322
  text_stable_start_time = line_start_time
293
- previous_text = orig_text
323
+ previous_text = orig_text_string
324
+ previous_text_list = orig_text
294
325
  last_oneocr_time = line_start_time
295
326
  previous_img = img
296
327
 
@@ -321,6 +352,7 @@ def run_oneocr(ocr_config: OCRConfig, area=False):
321
352
  screen_capture_exclusions=exclusions,
322
353
  language=language,
323
354
  monitor_index=ocr_config.window,
355
+ ocr1=ocr1,
324
356
  ocr2=ocr2,
325
357
  gsm_ocr_config=ocr_config,
326
358
  screen_capture_areas=screen_areas)
@@ -341,6 +373,19 @@ def get_window(window_name):
341
373
  print(f"Error finding window '{window_name}': {e}")
342
374
  return None
343
375
 
376
+ def set_force_stable_hotkey():
377
+ import keyboard
378
+ global force_stable
379
+ def toggle_force_stable():
380
+ global force_stable
381
+ force_stable = not force_stable
382
+ if force_stable:
383
+ print("Force stable mode enabled.")
384
+ else:
385
+ print("Force stable mode disabled.")
386
+ keyboard.add_hotkey('p', toggle_force_stable)
387
+ print("Press Ctrl+Shift+F to toggle force stable mode.")
388
+
344
389
  if __name__ == "__main__":
345
390
  global ocr1, ocr2, twopassocr, language
346
391
  import sys
@@ -367,6 +412,7 @@ if __name__ == "__main__":
367
412
  ocr2 = "glens"
368
413
  twopassocr = True
369
414
  logger.info(f"Received arguments: ocr1={ocr1}, ocr2={ocr2}, twopassocr={twopassocr}")
415
+ # set_force_stable_hotkey()
370
416
  global ocr_config
371
417
  ocr_config: OCRConfig = get_ocr_config()
372
418
  set_dpi_awareness()
@@ -287,7 +287,7 @@ class GoogleLens:
287
287
  }
288
288
 
289
289
  try:
290
- res = requests.post('https://lensfrontend-pa.googleapis.com/v1/crupload', data=payload, headers=headers, timeout=20)
290
+ res = requests.post('https://lensfrontend-pa.googleapis.com/v1/crupload', data=payload, headers=headers, timeout=5)
291
291
  except requests.exceptions.Timeout:
292
292
  return (False, 'Request timeout!')
293
293
  except requests.exceptions.ConnectionError:
@@ -366,7 +366,7 @@ class GoogleLensWeb:
366
366
  cookies = {'SOCS': 'CAESEwgDEgk0ODE3Nzk3MjQaAmVuIAEaBgiA_LyaBg'}
367
367
 
368
368
  try:
369
- res = self.requests_session.post(url, files=files, headers=headers, cookies=cookies, timeout=20, allow_redirects=False)
369
+ res = self.requests_session.post(url, files=files, headers=headers, cookies=cookies, timeout=5, allow_redirects=False)
370
370
  except requests.exceptions.Timeout:
371
371
  return (False, 'Request timeout!')
372
372
  except requests.exceptions.ConnectionError:
@@ -386,7 +386,7 @@ class GoogleLensWeb:
386
386
  return (False, 'Unknown error!')
387
387
 
388
388
  try:
389
- res = self.requests_session.get(f"https://lens.google.com/qfmetadata?vsrid={query_params['vsrid'][0]}&gsessionid={query_params['gsessionid'][0]}", timeout=20)
389
+ res = self.requests_session.get(f"https://lens.google.com/qfmetadata?vsrid={query_params['vsrid'][0]}&gsessionid={query_params['gsessionid'][0]}", timeout=5)
390
390
  except requests.exceptions.Timeout:
391
391
  return (False, 'Request timeout!')
392
392
  except requests.exceptions.ConnectionError:
@@ -458,7 +458,7 @@ class Bing:
458
458
  for _ in range(2):
459
459
  api_host = urlparse(upload_url).netloc
460
460
  try:
461
- res = self.requests_session.post(upload_url, headers=upload_headers, files=files, timeout=20, allow_redirects=False)
461
+ res = self.requests_session.post(upload_url, headers=upload_headers, files=files, timeout=5, allow_redirects=False)
462
462
  except requests.exceptions.Timeout:
463
463
  return (False, 'Request timeout!')
464
464
  except requests.exceptions.ConnectionError:
@@ -499,7 +499,7 @@ class Bing:
499
499
  }
500
500
 
501
501
  try:
502
- res = self.requests_session.post(api_url, headers=api_headers, files=files, timeout=20)
502
+ res = self.requests_session.post(api_url, headers=api_headers, files=files, timeout=5)
503
503
  except requests.exceptions.Timeout:
504
504
  return (False, 'Request timeout!')
505
505
  except requests.exceptions.ConnectionError:
@@ -961,7 +961,7 @@ class OCRSpace:
961
961
  files = {'file': ('image.' + img_extension, img_bytes, 'image/' + img_extension)}
962
962
 
963
963
  try:
964
- res = requests.post('https://api.ocr.space/parse/image', data=data, files=files, timeout=20)
964
+ res = requests.post('https://api.ocr.space/parse/image', data=data, files=files, timeout=5)
965
965
  except requests.exceptions.Timeout:
966
966
  return (False, 'Request timeout!')
967
967
  except requests.exceptions.ConnectionError:
@@ -381,7 +381,12 @@ class TextFiltering:
381
381
  else:
382
382
  orig_text_filtered.append(None)
383
383
 
384
- if last_result and last_result[1] == engine_index:
384
+ if not isinstance(last_result, tuple):
385
+ print(type(last_result))
386
+ if isinstance(last_result, list):
387
+ print("last_result is a list")
388
+ last_text = last_result
389
+ elif last_result and last_result[1] == engine_index:
385
390
  last_text = last_result[0]
386
391
  else:
387
392
  last_text = []
@@ -884,18 +889,28 @@ def process_and_write_results(img_or_path, write_to=None, last_result=None, filt
884
889
  for i, instance in enumerate(engine_instances):
885
890
  if instance.name.lower() in engine.lower():
886
891
  engine_instance = instance
887
- if last_result:
888
- last_result = (last_result[0], i)
889
892
  break
890
893
  else:
891
894
  engine_instance = engine_instances[engine_index]
892
895
 
896
+
897
+ engine_color = config.get_general('engine_color')
898
+
893
899
  start_time = time.time()
894
900
  res, text = engine_instance(img_or_path)
895
901
  end_time = time.time()
896
902
 
903
+ if not res and ocr_2 == engine:
904
+ logger.opt(ansi=True).info(f"<{engine_color}>{engine_instance.readable_name}</{engine_color}> failed with message: {text}, trying <{engine_color}>{ocr_1}</{engine_color}>")
905
+ for i, instance in enumerate(engine_instances):
906
+ if instance.name.lower() in ocr_1.lower():
907
+ engine_instance = instance
908
+ if last_result:
909
+ last_result = []
910
+ break
911
+ res, text = engine_instance(img_or_path)
912
+
897
913
  orig_text = []
898
- engine_color = config.get_general('engine_color')
899
914
  # print(filtering)
900
915
  #
901
916
  #
@@ -966,6 +981,7 @@ def run(read_from=None,
966
981
  text_callback=None,
967
982
  language=None,
968
983
  monitor_index=None,
984
+ ocr1=None,
969
985
  ocr2=None,
970
986
  gsm_ocr_config=None,
971
987
  ):
@@ -1083,6 +1099,10 @@ def run(read_from=None,
1083
1099
  global notifier
1084
1100
  global websocket_server_thread
1085
1101
  global image_queue
1102
+ global ocr_1
1103
+ global ocr_2
1104
+ ocr_1 = ocr1
1105
+ ocr_2 = ocr2
1086
1106
  custom_left = None
1087
1107
  terminated = False
1088
1108
  paused = pause_at_startup
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.8.48
3
+ Version: 2.8.49
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
@@ -8,14 +8,14 @@ GameSentenceMiner/gametext.py,sha256=hcyZQ69B7xB5ZG85wLzM5au7ZPKxmeUXsmUD26oyk_0
8
8
  GameSentenceMiner/gsm.py,sha256=dZt2Gnwqwj-i-yRpGJWBmCBRaBjEUCzi3L3P-gcVkfA,27266
9
9
  GameSentenceMiner/model.py,sha256=1lRyJFf_LND_4O16h8CWVqDfosLgr0ZS6ufBZ3qJHpY,5699
10
10
  GameSentenceMiner/notification.py,sha256=pXKoLfmRQLH55IQ5G6uxdMuczqX7D6l3ubVEY1e6hXg,2859
11
- GameSentenceMiner/obs.py,sha256=_u3HinIfxJIL7gN1KueOHCQYPDc3l9ctaX4kaV1qgC0,14632
11
+ GameSentenceMiner/obs.py,sha256=JwcVPnjO-Lm0H5007o3rF-gMf4ypgIm5m8ntthfbTk8,14789
12
12
  GameSentenceMiner/obs_back.py,sha256=_N_UV7Nh5cyy3mnH5lOUOzhgZwHMACeFEuBo1Z-bNzg,10894
13
13
  GameSentenceMiner/package.py,sha256=YlS6QRMuVlm6mdXx0rlXv9_3erTGS21jaP3PNNWfAH0,1250
14
14
  GameSentenceMiner/ss_selector.py,sha256=csey9H3561-guRJcT6gQN6hXxvylP0CBI0dp2-kwo2Q,4446
15
15
  GameSentenceMiner/text_log.py,sha256=MD7LB5D-v4G0Bnm3uGvZQ0aV38Fcj4E0vgq7mmyQ7_4,5157
16
16
  GameSentenceMiner/util.py,sha256=PrDNnxWiJZh1lGuwnp3DjWIlwbkVxweRTYWLtQk94Ao,9122
17
17
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- GameSentenceMiner/ai/ai_prompting.py,sha256=O1QBgCL6AkkDyhzxZuW8FPCKgUDfkl_ZlKGcEUfbRnk,9508
18
+ GameSentenceMiner/ai/ai_prompting.py,sha256=xw8et6XNwQiDXOXZnw8iIntVSg8lni4YYZbgWsK7qDE,10013
19
19
  GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
20
20
  GameSentenceMiner/communication/send.py,sha256=X0MytGv5hY-uUvkfvdCqQA_ljZFmV6UkJ6in1TA1bUE,217
21
21
  GameSentenceMiner/communication/websocket.py,sha256=pTcUe_ZZRp9REdSU4qalhPmbT_1DKa7w18j6RfFLELA,3074
@@ -27,13 +27,13 @@ GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
27
27
  GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=fEQ2o2NXksGRHpueO8c4TfAp75GEdAtAr1ngTFOsdpg,2257
28
28
  GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
29
29
  GameSentenceMiner/ocr/owocr_area_selector.py,sha256=Q8ETMHL7BKMA1mbtjrntDLyqCQB0lZ5T4RCZsodjH7Y,47186
30
- GameSentenceMiner/ocr/owocr_helper.py,sha256=mcmljTQYfRHyoqtCVOHjyVAEGWwEkdUAYXY3Z85J4eg,16415
30
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=GzTpy3f8xbiaNoPNh3NotreYVFFPFnfoO732iDu9XqU,18402
31
31
  GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
32
32
  GameSentenceMiner/owocr/owocr/__main__.py,sha256=XQaqZY99EKoCpU-gWQjNbTs7Kg17HvBVE7JY8LqIE0o,157
33
33
  GameSentenceMiner/owocr/owocr/config.py,sha256=qM7kISHdUhuygGXOxmgU6Ef2nwBShrZtdqu4InDCViE,8103
34
34
  GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
35
- GameSentenceMiner/owocr/owocr/ocr.py,sha256=6P6zSoykrsJnYqngQI9ULc98zXeTMJCfAoCMedtphzw,41132
36
- GameSentenceMiner/owocr/owocr/run.py,sha256=oESHucfOXO_HXCjmbhEeRQ6CKeIo-sRN877EjdoKCgA,54169
35
+ GameSentenceMiner/owocr/owocr/ocr.py,sha256=qLlqaxWNhkoAhPmZgIDI70lxf6l_eKji4suqNoUThdw,41126
36
+ GameSentenceMiner/owocr/owocr/run.py,sha256=o7X44H4FQVw_Kgi7Zdsz9RBeHtUjmObBdoOlX2TXpiw,54911
37
37
  GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
38
38
  GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  GameSentenceMiner/vad/result.py,sha256=C08HsYH4qVjTRh_dvrWrskmXHJ950w0GWxPjGx_BfGY,275
@@ -54,9 +54,9 @@ GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRX
54
54
  GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
56
56
  GameSentenceMiner/web/templates/utility.html,sha256=P659ZU2j7tcbJ5xPO3p7E_SQpkp3CrrFtSvvXJNNuLI,16330
57
- gamesentenceminer-2.8.48.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
58
- gamesentenceminer-2.8.48.dist-info/METADATA,sha256=u7fFMnmpN0JaK_OMxFjfSug5xJ_hVWr0dA4VPb-kJe0,7218
59
- gamesentenceminer-2.8.48.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
60
- gamesentenceminer-2.8.48.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
61
- gamesentenceminer-2.8.48.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
62
- gamesentenceminer-2.8.48.dist-info/RECORD,,
57
+ gamesentenceminer-2.8.49.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
58
+ gamesentenceminer-2.8.49.dist-info/METADATA,sha256=YW_c4ozhtyb_JkAFBkMatRc_ORbwraoLkBKzKGiaM7Q,7218
59
+ gamesentenceminer-2.8.49.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
60
+ gamesentenceminer-2.8.49.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
61
+ gamesentenceminer-2.8.49.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
62
+ gamesentenceminer-2.8.49.dist-info/RECORD,,