GameSentenceMiner 2.8.54__py3-none-any.whl → 2.9.1__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.
@@ -301,10 +301,10 @@
301
301
  async function toggleCheckbox(id, checked) {
302
302
  try {
303
303
  checkboxes_being_updated.add(id);
304
- const res = await fetch('/update', {
304
+ const res = await fetch('/update_checkbox', {
305
305
  method: 'POST',
306
306
  headers: { 'Content-Type': 'application/json' },
307
- body: JSON.stringify({ id, checked })
307
+ body: JSON.stringify({ id })
308
308
  });
309
309
  checkboxes_being_updated.delete(id);
310
310
  if (!res.ok) {
@@ -10,11 +10,11 @@ from dataclasses import dataclass
10
10
  import flask
11
11
  import websockets
12
12
 
13
- from GameSentenceMiner.text_log import GameLine, get_line_by_id, initial_time
13
+ from GameSentenceMiner.text_log import GameLine, get_line_by_id, initial_time, get_all_lines
14
14
  from flask import request, jsonify, send_from_directory
15
15
  import webbrowser
16
16
  from GameSentenceMiner import obs
17
- from GameSentenceMiner.configuration import logger, get_config, DB_PATH
17
+ from GameSentenceMiner.configuration import logger, get_config, DB_PATH, gsm_state
18
18
  from GameSentenceMiner.util import TEXT_REPLACEMENTS_FILE
19
19
 
20
20
  port = get_config().general.texthooker_port
@@ -52,8 +52,6 @@ class EventItem:
52
52
  class EventManager:
53
53
  events: list[EventItem]
54
54
  events_dict: dict[str, EventItem] = {}
55
- line_for_audio: GameLine = None
56
- line_for_screenshot: GameLine = None
57
55
 
58
56
  def __init__(self):
59
57
  self.events = []
@@ -100,7 +98,7 @@ class EventManager:
100
98
  self.events_dict[line.id] = new_event
101
99
  self.events.append(new_event)
102
100
  # self.store_to_db(new_event)
103
- event_queue.put(new_event)
101
+ # event_queue.put(new_event)
104
102
  return new_event
105
103
 
106
104
  def reset_checked_lines(self):
@@ -225,11 +223,11 @@ def serve_static(filename):
225
223
 
226
224
  @app.route('/')
227
225
  def index():
228
- return flask.render_template('utility.html', websocket_port=websocket_port)
226
+ return flask.render_template('utility.html' if get_config().general.use_old_texthooker else 'index.html', websocket_port=websocket_port)
229
227
 
230
228
  @app.route('/texthooker')
231
229
  def texthooker():
232
- return flask.render_template('utility.html', websocket_port=websocket_port)
230
+ return flask.render_template('utility.html' if get_config().general.use_old_texthooker else 'index.html', websocket_port=websocket_port)
233
231
 
234
232
  @app.route('/textreplacements')
235
233
  def textreplacements():
@@ -256,16 +254,15 @@ async def add_event_to_texthooker(line: GameLine):
256
254
  })
257
255
 
258
256
 
259
- @app.route('/update', methods=['POST'])
257
+ @app.route('/update_checkbox', methods=['POST'])
260
258
  def update_event():
261
259
  data = request.get_json()
262
260
  event_id = data.get('id')
263
- checked = data.get('checked')
264
261
 
265
- if event_id is None or checked is None:
266
- return jsonify({'error': 'Missing id or checked status'}), 400
267
-
268
- event_manager.get(event_id).checked = checked
262
+ if event_id is None:
263
+ return jsonify({'error': 'Missing id'}), 400
264
+ event = event_manager.get(event_id)
265
+ event_manager.get(event_id).checked = not event.checked
269
266
  return jsonify({'message': 'Event updated successfully'}), 200
270
267
 
271
268
  @app.route('/get-screenshot', methods=['Post'])
@@ -275,8 +272,11 @@ def get_screenshot():
275
272
  event_id = data.get('id')
276
273
  if event_id is None:
277
274
  return jsonify({'error': 'Missing id'}), 400
278
- event_manager.line_for_screenshot = get_line_by_id(event_id)
279
- obs.save_replay_buffer()
275
+ gsm_state.line_for_screenshot = get_line_by_id(event_id)
276
+ if gsm_state.previous_line_for_screenshot and gsm_state.line_for_screenshot.id == gsm_state.previous_line_for_screenshot.id:
277
+ open(os.path.join(get_config().paths.folder_to_watch, "previous.mkv"), 'a').close()
278
+ else:
279
+ obs.save_replay_buffer()
280
280
  return jsonify({}), 200
281
281
 
282
282
  @app.route('/play-audio', methods=['POST'])
@@ -286,8 +286,11 @@ def play_audio():
286
286
  event_id = data.get('id')
287
287
  if event_id is None:
288
288
  return jsonify({'error': 'Missing id'}), 400
289
- event_manager.line_for_audio = get_line_by_id(event_id)
290
- obs.save_replay_buffer()
289
+ gsm_state.line_for_audio = get_line_by_id(event_id)
290
+ if gsm_state.previous_line_for_audio and gsm_state.line_for_audio == gsm_state.previous_line_for_audio:
291
+ open(os.path.join(get_config().paths.folder_to_watch, "previous.mkv"), 'a').close()
292
+ else:
293
+ obs.save_replay_buffer()
291
294
  return jsonify({}), 200
292
295
 
293
296
 
@@ -319,8 +322,8 @@ async def websocket_handler(websocket):
319
322
 
320
323
  async def broadcast_message(message):
321
324
  if connected_clients:
322
- tasks = [client.send(json.dumps(message)) for client in connected_clients]
323
- await asyncio.gather(*tasks)
325
+ for client in connected_clients:
326
+ await client.send(json.dumps(message))
324
327
 
325
328
  # async def main():
326
329
  # async with websockets.serve(websocket_handler, "localhost", 8765): # Choose a port for WebSocket
@@ -356,7 +359,12 @@ def are_lines_selected():
356
359
  return any(item.checked for item in event_manager)
357
360
 
358
361
  def reset_checked_lines():
362
+ async def send_reset_message():
363
+ await broadcast_message({
364
+ 'event': 'reset_checkboxes',
365
+ })
359
366
  event_manager.reset_checked_lines()
367
+ asyncio.run(send_reset_message())
360
368
 
361
369
  def open_texthooker():
362
370
  webbrowser.open(url + '/texthooker')
@@ -373,19 +381,30 @@ def start_web_server():
373
381
 
374
382
  app.run(port=port, debug=False) # debug=True provides helpful error messages during development
375
383
 
376
- async def run_websocket_server(host="0.0.0.0", port=55001):
377
- global websocket_port
378
- while True:
379
- websocket_port = port
380
- try:
381
- async with websockets.serve(websocket_handler, host, port):
382
- logger.debug(f"WebSocket server started at ws://{host}:{port}/")
383
- await asyncio.Future() # Keep the WebSocket server running
384
- except OSError as e:
385
- logger.debug(f"Port {port} is in use. Trying the next port...")
386
- port += 1
384
+ import signal
387
385
 
386
+ async def run_websocket_server(host="0.0.0.0"):
387
+ global websocket_port
388
+ websocket = None
389
+ try:
390
+ websocket_port = get_config().advanced.texthooker_communication_websocket_port
391
+ websocket = await websockets.serve(websocket_handler, host, websocket_port)
392
+ logger.debug(f"WebSocket server started at ws://{host}:{websocket_port}/")
393
+ await asyncio.Future() # Keep the server running
394
+ except asyncio.CancelledError:
395
+ logger.info("WebSocket server shutting down...")
396
+ except OSError as e:
397
+ logger.error(f"TextHooker WebSocket server failed to start on port {websocket_port}: {e}")
398
+ logger.info("You may need to try a different port in GSM's advanced config, and then update that in the Texthooker's settings.")
399
+ finally:
400
+ if websocket:
401
+ websocket.close()
402
+ await asyncio.sleep(1) # Wait before retrying
388
403
 
404
+ def handle_exit_signal(loop):
405
+ logger.info("Received exit signal. Shutting down...")
406
+ for task in asyncio.all_tasks(loop):
407
+ task.cancel()
389
408
 
390
409
  async def texthooker_page_coro():
391
410
  # Run the WebSocket server in the asyncio event loop
@@ -397,7 +416,10 @@ async def texthooker_page_coro():
397
416
  await run_websocket_server()
398
417
 
399
418
  def run_text_hooker_page():
400
- asyncio.run(texthooker_page_coro())
419
+ try:
420
+ asyncio.run(texthooker_page_coro())
421
+ except KeyboardInterrupt:
422
+ logger.info("Shutting down due to KeyboardInterrupt.")
401
423
 
402
424
  if __name__ == '__main__':
403
- asyncio.run(run_text_hooker_page())
425
+ asyncio.run(texthooker_page_coro())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.8.54
3
+ Version: 2.9.1
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
@@ -38,6 +38,7 @@ Requires-Dist: pygetwindow; sys_platform == "win32"
38
38
  Requires-Dist: flask
39
39
  Requires-Dist: groq
40
40
  Requires-Dist: obsws-python
41
+ Requires-Dist: Flask-SocketIO
41
42
  Dynamic: license-file
42
43
 
43
44
  # GameSentenceMiner (GSM)
@@ -152,6 +153,8 @@ If you encounter issues, please ask for help in my [Discord](https://discord.gg/
152
153
 
153
154
  * [OBS](https://obsproject.com/) and [FFMPEG](https://ffmpeg.org/), without which GSM would not be possible.
154
155
 
156
+ * [Renji's Texthooker](https://github.com/Renji-XD/texthooker-ui)
157
+
155
158
  ## Donations
156
159
 
157
160
  If you've found this or any of my other projects helpful, please consider supporting my work through [GitHub Sponsors](https://github.com/sponsors/bpwhelan) or [Ko-fi](https://ko-fi.com/beangate).
@@ -1,24 +1,24 @@
1
1
  GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- GameSentenceMiner/anki.py,sha256=bChJ1YU80muhvR8fjY9KAJEs0M0bpe-X_uMhjJBUC4k,14530
3
- GameSentenceMiner/config_gui.py,sha256=vwW569M-ZQPZEkprluDuKlPRoYTm2UMs2nPMDZtlorE,77472
4
- GameSentenceMiner/configuration.py,sha256=EMqtnK9jHFmCht2vi3dsDRekidr3pIfdyZ-ycmH6aOg,22539
2
+ GameSentenceMiner/anki.py,sha256=JnVfFkLpEfWaPfOLngU0PSQq4vrgWuhd_VLYZEBqNTY,14608
3
+ GameSentenceMiner/config_gui.py,sha256=h4zz85gfhxSphaJ-IZSu9D4jR70mDlKecZ9JRCO5Noc,80927
4
+ GameSentenceMiner/configuration.py,sha256=8CfdTJ0ROJrxyzNg3NaElAVS1bwchg1ih6XfhfDZy1g,25492
5
5
  GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
6
- GameSentenceMiner/ffmpeg.py,sha256=DX-2J1KZBKOPC8syR73YhDHwKIR4oNPrGuPl4l8is-4,18255
7
- GameSentenceMiner/gametext.py,sha256=hcyZQ69B7xB5ZG85wLzM5au7ZPKxmeUXsmUD26oyk_0,5660
8
- GameSentenceMiner/gsm.py,sha256=-76Gyrk2m9Hqobt6YByvV_sw1BI9xH_B9l0-aFAxBvc,27629
6
+ GameSentenceMiner/ffmpeg.py,sha256=zVmLJOsXpy71zKb0cLBPrXJ6YpjPVRJmH0uRfd5O30k,18299
7
+ GameSentenceMiner/gametext.py,sha256=32e7w0qGXYNRRNpLzj9XIwtuto-E6ogEcyl6DC_MPbw,6200
8
+ GameSentenceMiner/gsm.py,sha256=_Mp_gZFomeFz9FTZqYEXIgqxbICYcAB06KUlFkClX5Q,29831
9
9
  GameSentenceMiner/model.py,sha256=1lRyJFf_LND_4O16h8CWVqDfosLgr0ZS6ufBZ3qJHpY,5699
10
10
  GameSentenceMiner/notification.py,sha256=pXKoLfmRQLH55IQ5G6uxdMuczqX7D6l3ubVEY1e6hXg,2859
11
- GameSentenceMiner/obs.py,sha256=JwcVPnjO-Lm0H5007o3rF-gMf4ypgIm5m8ntthfbTk8,14789
11
+ GameSentenceMiner/obs.py,sha256=DoUJk00Gk0Idley7CEldfIobqJ9na2UBlCv7nclZO4s,14793
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
- GameSentenceMiner/text_log.py,sha256=MD7LB5D-v4G0Bnm3uGvZQ0aV38Fcj4E0vgq7mmyQ7_4,5157
15
+ GameSentenceMiner/text_log.py,sha256=U2_g8THAYeexRiE2bLk_bCt_2ShiA8SQ9VdJsi4riHs,5181
16
16
  GameSentenceMiner/util.py,sha256=PrDNnxWiJZh1lGuwnp3DjWIlwbkVxweRTYWLtQk94Ao,9122
17
17
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
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
- GameSentenceMiner/communication/websocket.py,sha256=pTcUe_ZZRp9REdSU4qalhPmbT_1DKa7w18j6RfFLELA,3074
21
+ GameSentenceMiner/communication/websocket.py,sha256=8eFZaTtoFggEPdqw2Jl4zqHC2I7J3-Gk27CxVX7SyBo,3277
22
22
  GameSentenceMiner/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5FngZ7ILdA_J5TFYAXUQ,15272
23
23
  GameSentenceMiner/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  GameSentenceMiner/downloader/download_tools.py,sha256=aRfpCqEmKUFRVsGipwY-7PhY6AeWiFJanW4ZCB9e2iE,8124
@@ -27,21 +27,23 @@ 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=EMdq8jga3kKx8u19LSgqwnZIhR8_YDMqydACVzojUDo,17726
30
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=M4Is-Ki5O3r4ixYhILibfjrVGD6xDlOcR3YvVGmETQ4,17363
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=rtKoIonyzqAMRPK92GvfYgGsU5M2yIWcWz9MQngNstc,41602
35
+ GameSentenceMiner/owocr/owocr/ocr.py,sha256=V0HqVRQlaE1-12IH480IupfSv1BlDdEcwNPejhQZfS0,42292
36
36
  GameSentenceMiner/owocr/owocr/run.py,sha256=0UyjOKEP0MqSdCaagCUMGdqO-BMexPxCl7ZabGlic4E,54749
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
- GameSentenceMiner/vad/result.py,sha256=C08HsYH4qVjTRh_dvrWrskmXHJ950w0GWxPjGx_BfGY,275
40
- GameSentenceMiner/vad/silero_trim.py,sha256=InYsCy29WjK5EIB4e-KYb91rdHLCc5ZGbKtn9W5WmZI,2021
41
- GameSentenceMiner/vad/vosk_helper.py,sha256=3ea8P6SoXG2wOkqesFWPOsHfJNmyYTaQWsr_QLU-jZo,6467
42
- GameSentenceMiner/vad/whisper_helper.py,sha256=H4bmEMgUXYl_CxpYUcDa2hWGUy4vOVXZBoWhh5fwvaY,4015
39
+ GameSentenceMiner/vad/groq_trim.py,sha256=MDYiApduwF7oDx3r0TXL3xQrTkbUC1RinMwNKSbF5gw,3764
40
+ GameSentenceMiner/vad/result.py,sha256=aFlr2px90fn3qXj49dwF9BDXA5m4yXD_HYH01CVvP1U,799
41
+ GameSentenceMiner/vad/silero_trim.py,sha256=ACvIdfjeCku6XWeUdi1zThBDvynJ01gjMFpSPWQALjg,2240
42
+ GameSentenceMiner/vad/vad_utils.py,sha256=_YC6rW2eXSBeLnYbVl_F3na1KCRL90VrnOzKYJ9RhUE,391
43
+ GameSentenceMiner/vad/vosk_helper.py,sha256=h7yNHrzrzT-J74UniA0T2ZX8cHqhflCzwyDjoIdKLO4,6479
44
+ GameSentenceMiner/vad/whisper_helper.py,sha256=B64-Eq_ZMCIyQX_A8uvYz-c48hSXJAyz6tSXNRaLjtA,4020
43
45
  GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- GameSentenceMiner/web/texthooking_page.py,sha256=t58PCfqwnJ197CtIUbGbE_cOyNkR81e4oi84VayTj5g,13497
46
+ GameSentenceMiner/web/texthooking_page.py,sha256=alXNkpm2Kl-ewYIm_aw24_8RdcYedLB59y6YTkp_mrE,14789
45
47
  GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
48
  GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
47
49
  GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
@@ -52,11 +54,12 @@ GameSentenceMiner/web/static/style.css,sha256=bPZK0NVMuyRl5NNDuT7ZTzVLKlvSsdmeVH
52
54
  GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESbkGfYwbKzcjKOdzuWo18ABADfN974,51117
53
55
  GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
54
56
  GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
+ GameSentenceMiner/web/templates/index.html,sha256=HZKiIjiGJV8PGQ9T2aLDUNSfJn71qOwbYCjbRuSIjpY,213583
55
58
  GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
56
- GameSentenceMiner/web/templates/utility.html,sha256=1vN3nK3IT-iNhMEj-k6JRz9uc6D87UUL9k7Bp2Espiw,16946
57
- gamesentenceminer-2.8.54.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
58
- gamesentenceminer-2.8.54.dist-info/METADATA,sha256=X0Z7tHSkYajYiuz7HggBwRxQz93YJAiLFVjlHl_bhsQ,7218
59
- gamesentenceminer-2.8.54.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
60
- gamesentenceminer-2.8.54.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
61
- gamesentenceminer-2.8.54.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
62
- gamesentenceminer-2.8.54.dist-info/RECORD,,
59
+ GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
60
+ gamesentenceminer-2.9.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
61
+ gamesentenceminer-2.9.1.dist-info/METADATA,sha256=3SHKdq8q35tQXbi0x_4zQAekXgVPf-le5kKpSmdfSHY,7314
62
+ gamesentenceminer-2.9.1.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
63
+ gamesentenceminer-2.9.1.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
64
+ gamesentenceminer-2.9.1.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
65
+ gamesentenceminer-2.9.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5