GameSentenceMiner 2.15.8__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.
@@ -1,201 +1,52 @@
1
1
  import asyncio
2
- from ctypes.util import test
3
2
  import datetime
4
3
  import json
5
4
  import os
6
- import queue
7
- import sqlite3
8
5
  import threading
9
- from dataclasses import dataclass
10
6
 
11
7
  import flask
12
- import websockets
8
+ import webbrowser
13
9
 
14
10
  from GameSentenceMiner.ai.ai_prompting import get_ai_prompt_result
15
11
  from GameSentenceMiner.obs import get_current_game
16
12
  from GameSentenceMiner.util.gsm_utils import TEXT_REPLACEMENTS_FILE
17
- from GameSentenceMiner.util.text_log import GameLine, get_line_by_id, initial_time, get_all_lines
18
- from flask import request, jsonify, send_from_directory
19
- import webbrowser
13
+ from GameSentenceMiner.util.text_log import get_line_by_id, get_all_lines
14
+ from flask import render_template, request, jsonify, send_from_directory
20
15
  from GameSentenceMiner import obs
21
- from GameSentenceMiner.util.configuration import logger, get_config, DB_PATH, gsm_state, gsm_status
16
+ from GameSentenceMiner.util.configuration import logger, get_config, gsm_state, gsm_status
22
17
  from GameSentenceMiner.web.service import handle_texthooker_button
23
18
 
19
+ # Import from new modules
20
+ from GameSentenceMiner.web.events import (
21
+ EventItem, EventManager, EventProcessor, event_manager, event_queue, event_processor
22
+ )
23
+ from GameSentenceMiner.web.stats import (
24
+ is_kanji, interpolate_color, get_gradient_color, calculate_kanji_frequency,
25
+ calculate_heatmap_data, calculate_total_chars_per_game, calculate_reading_time_per_game,
26
+ calculate_reading_speed_per_game, generate_game_colors, format_large_number,
27
+ calculate_actual_reading_time, calculate_daily_reading_time, calculate_time_based_streak,
28
+ format_time_human_readable, calculate_current_game_stats, calculate_all_games_stats
29
+ )
30
+ from GameSentenceMiner.web.websockets import (
31
+ WebsocketServerThread, websocket_queue, paused, websocket_server_thread,
32
+ plaintext_websocket_server_thread, overlay_server_thread, websocket_server_threads,
33
+ handle_exit_signal
34
+ )
35
+ from GameSentenceMiner.web.database_api import register_database_api_routes
36
+
37
+ # Global configuration
24
38
  port = get_config().general.texthooker_port
25
39
  url = f"http://localhost:{port}"
26
40
  websocket_port = 55001
27
41
 
28
-
29
- @dataclass
30
- class EventItem:
31
- line: 'GameLine'
32
- id: str
33
- text: str
34
- time: datetime.datetime
35
- checked: bool = False
36
- history: bool = False
37
-
38
- def to_dict(self):
39
- return {
40
- 'id': self.id,
41
- 'text': self.text,
42
- 'time': self.time,
43
- 'checked': self.checked,
44
- 'history': self.history,
45
- }
46
-
47
- def to_serializable(self):
48
- return {
49
- 'id': self.id,
50
- 'text': self.text,
51
- 'time': self.time.isoformat(),
52
- 'checked': self.checked,
53
- 'history': self.history,
54
- }
55
-
56
-
57
- class EventManager:
58
- events: list[EventItem]
59
- events_dict: dict[str, EventItem] = {}
60
-
61
- def __init__(self):
62
- self.events = []
63
- self.ids = []
64
- self.events_dict = {}
65
- self._connect()
66
- self._create_table()
67
- self._load_events_from_db()
68
- # self.close_connection()
69
-
70
- def _connect(self):
71
- self.conn = sqlite3.connect(DB_PATH)
72
- self.cursor = self.conn.cursor()
73
-
74
- def _create_table(self):
75
- self.cursor.execute("""
76
- CREATE TABLE IF NOT EXISTS events (
77
- event_id TEXT PRIMARY KEY,
78
- line_id TEXT,
79
- text TEXT,
80
- time TEXT
81
- )
82
- """)
83
- self.conn.commit()
84
-
85
- def _load_events_from_db(self):
86
- self.cursor.execute("SELECT * FROM events")
87
- rows = self.cursor.fetchall()
88
- for row in rows:
89
- event_id, line_id, text, timestamp = row
90
- timestamp = datetime.datetime.fromisoformat(timestamp)
91
- line = GameLine(line_id, text, timestamp, None, None, 0)
92
- event = EventItem(line, event_id, text, timestamp,
93
- False, timestamp < initial_time)
94
- self.events.append(event)
95
- self.ids.append(event_id)
96
- self.events_dict[event_id] = event
97
-
98
- def __iter__(self):
99
- return iter(self.events)
100
-
101
- def replace_events(self, new_events: list[EventItem]):
102
- self.events = new_events
103
-
104
- def add_gameline(self, line: GameLine):
105
- new_event = EventItem(line, line.id, line.text,
106
- line.time, False, False)
107
- self.events_dict[line.id] = new_event
108
- self.ids.append(line.id)
109
- self.events.append(new_event)
110
- # self.store_to_db(new_event)
111
- # event_queue.put(new_event)
112
- return new_event
113
-
114
- def reset_checked_lines(self):
115
- for event in self.events:
116
- event.checked = False
117
-
118
- def get_events(self):
119
- return self.events
120
-
121
- def add_event(self, event):
122
- self.events.append(event)
123
- self.ids.append(event.id)
124
- event_queue.put(event)
125
-
126
- def get(self, event_id):
127
- return self.events_dict.get(event_id)
128
-
129
- def get_ids(self):
130
- return self.ids
131
-
132
- def close_connection(self):
133
- if self.conn:
134
- self.conn.close()
135
-
136
- def clear_history(self):
137
- self.cursor.execute("DELETE FROM events WHERE time < ?",
138
- (initial_time.isoformat(),))
139
- logger.info(f"Cleared history before {initial_time.isoformat()}")
140
- self.conn.commit()
141
- # Clear the in-memory events as well
142
- event_manager.events = [
143
- event for event in event_manager if not event.history]
144
- event_manager.events_dict = {
145
- event.id: event for event in event_manager.events}
146
-
147
-
148
- class EventProcessor(threading.Thread):
149
- def __init__(self, event_queue, db_path):
150
- super().__init__()
151
- self.event_queue = event_queue
152
- self.db_path = db_path
153
- self.conn = None
154
- self.cursor = None
155
- self.daemon = True
156
-
157
- def _connect(self):
158
- self.conn = sqlite3.connect(self.db_path)
159
- self.cursor = self.conn.cursor()
160
-
161
- def run(self):
162
- self._connect()
163
- while True:
164
- try:
165
- event = self.event_queue.get()
166
- if event is None: # Exit signal
167
- break
168
- self._store_to_db(event)
169
- except Exception as e:
170
- logger.error(f"Error processing event: {e}")
171
- self._close_connection()
172
-
173
- def _store_to_db(self, event):
174
- self.cursor.execute("""
175
- INSERT INTO events (event_id, line_id, text, time)
176
- VALUES (?, ?, ?, ?)
177
- """, (event.id, event.line.id, event.text, event.time.isoformat()))
178
- self.conn.commit()
179
-
180
- def _close_connection(self):
181
- if self.conn:
182
- self.conn.close()
183
-
184
-
185
- event_manager = EventManager()
186
- event_queue = queue.Queue()
187
-
188
- # Initialize the EventProcessor with the queue and event manager
189
- event_processor = EventProcessor(event_queue, DB_PATH)
190
- event_processor.start()
191
-
192
42
  server_start_time = datetime.datetime.now().timestamp()
193
43
 
194
44
  app = flask.Flask(__name__)
195
45
 
196
- # Load data from the JSON file
197
-
46
+ # Register database API routes
47
+ register_database_api_routes(app)
198
48
 
49
+ # Load data from the JSON file
199
50
  def load_data_from_file():
200
51
  if os.path.exists(TEXT_REPLACEMENTS_FILE):
201
52
  with open(TEXT_REPLACEMENTS_FILE, 'r', encoding='utf-8') as file:
@@ -203,8 +54,6 @@ def load_data_from_file():
203
54
  return {"enabled": True, "args": {"replacements": {}}}
204
55
 
205
56
  # Save data to the JSON file
206
-
207
-
208
57
  def save_data_to_file(data):
209
58
  with open(TEXT_REPLACEMENTS_FILE, 'w', encoding='utf-8') as file:
210
59
  json.dump(data, file, indent=4, ensure_ascii=False)
@@ -262,7 +111,19 @@ def texthooker():
262
111
 
263
112
  @app.route('/textreplacements')
264
113
  def textreplacements():
265
- return flask.render_template('text_replacements.html')
114
+ # Serve the text replacements data as JSON for compatibility
115
+ try:
116
+ if not os.path.exists(TEXT_REPLACEMENTS_FILE):
117
+ return jsonify({"error": "Text replacements file not found."}), 404
118
+ with open(TEXT_REPLACEMENTS_FILE, "r", encoding="utf-8") as f:
119
+ data = json.load(f)
120
+ return jsonify(data)
121
+ except Exception as e:
122
+ return jsonify({"error": f"Failed to load text replacements: {str(e)}"}), 500
123
+
124
+ @app.route('/database')
125
+ def database():
126
+ return flask.render_template('database.html')
266
127
 
267
128
 
268
129
  @app.route('/data', methods=['GET'])
@@ -283,7 +144,7 @@ def clear_history():
283
144
  return jsonify({'message': 'History cleared successfully'}), 200
284
145
 
285
146
 
286
- async def add_event_to_texthooker(line: GameLine):
147
+ async def add_event_to_texthooker(line):
287
148
  new_event = event_manager.add_gameline(line)
288
149
  await websocket_server_thread.send_text({
289
150
  'event': 'text_received',
@@ -389,32 +250,24 @@ Translate the following lines of game dialogue into natural-sounding, context-aw
389
250
  def get_status():
390
251
  return jsonify(gsm_status.to_dict()), 200
391
252
 
253
+ @app.template_filter('datetimeformat')
254
+ def datetimeformat(value, format='%Y-%m-%d %H:%M:%S'):
255
+ """Formats a timestamp into a human-readable string."""
256
+ if value is None:
257
+ return ""
258
+ return datetime.datetime.fromtimestamp(float(value)).strftime(format)
259
+
392
260
 
393
- # async def main():
394
- # async with websockets.serve(websocket_handler, "localhost", 8765): # Choose a port for WebSocket
395
- # print("WebSocket server started on ws://localhost:8765/ws (adjust as needed)")
396
- # await asyncio.Future() # Keep the server running
397
-
398
- # @app.route('/store-events', methods=['POST'])
399
- # def store_events():
400
- # data = request.get_json()
401
- # events_data = data.get('events', [])
402
- #
403
- # if not isinstance(events_data, list):
404
- # return jsonify({'error': 'Invalid data format. Expected an array of events.'}), 400
405
- #
406
- # for event_data in events_data:
407
- # if not all(k in event_data for k in ('id', 'text', 'time', 'checked')):
408
- # return jsonify({'error': 'Invalid event structure. Missing keys.'}), 400
409
- # if not (isinstance(event_data['id'], (int, float)) and
410
- # isinstance(event_data['text'], str) and
411
- # isinstance(event_data['time'], str) and
412
- # isinstance(event_data['checked'], bool)):
413
- # return jsonify({'error': 'Invalid event structure. Incorrect data types.'}), 400
414
- #
415
- # event_manager.replace_events([EventItem(item['id'], item['text'], item['time'], item.get(['timestamp'], 0), item['checked']) for item in data])
416
- #
417
- # return jsonify({'message': 'Events successfully stored on server.', 'receivedEvents': data}), 200
261
+ @app.route('/stats')
262
+ def stats():
263
+ """Renders the stats page."""
264
+ return render_template('stats.html')
265
+
266
+
267
+ @app.route('/search')
268
+ def search():
269
+ """Renders the search page."""
270
+ return render_template('search.html')
418
271
 
419
272
 
420
273
  def get_selected_lines():
@@ -452,117 +305,6 @@ def start_web_server():
452
305
  app.run(host='0.0.0.0', port=port, debug=False)
453
306
 
454
307
 
455
- websocket_queue = queue.Queue()
456
- paused = False
457
-
458
-
459
- class WebsocketServerThread(threading.Thread):
460
- def __init__(self, read, get_ws_port_func):
461
- super().__init__(daemon=True)
462
- self._loop = None
463
- self.read = read
464
- self.clients = set()
465
- self._event = threading.Event()
466
- self.get_ws_port_func = get_ws_port_func
467
- self.backedup_text = []
468
-
469
- @property
470
- def loop(self):
471
- self._event.wait()
472
- return self._loop
473
-
474
- async def send_text_coroutine(self, message):
475
- if not self.clients:
476
- self.backedup_text.append(message)
477
- return
478
- for client in self.clients:
479
- await client.send(message)
480
-
481
- async def server_handler(self, websocket):
482
- self.clients.add(websocket)
483
- try:
484
- if self.backedup_text:
485
- for message in self.backedup_text:
486
- await websocket.send(message)
487
- self.backedup_text.clear()
488
- async for message in websocket:
489
- if self.read and not paused:
490
- websocket_queue.put(message)
491
- try:
492
- await websocket.send('True')
493
- except websockets.exceptions.ConnectionClosedOK:
494
- pass
495
- else:
496
- try:
497
- await websocket.send('False')
498
- except websockets.exceptions.ConnectionClosedOK:
499
- pass
500
- except websockets.exceptions.ConnectionClosedError:
501
- pass
502
- finally:
503
- self.clients.remove(websocket)
504
-
505
- async def send_text(self, text):
506
- if text:
507
- if isinstance(text, dict) or isinstance(text, list):
508
- text = json.dumps(text)
509
- return asyncio.run_coroutine_threadsafe(
510
- self.send_text_coroutine(text), self.loop)
511
-
512
- def has_clients(self):
513
- return len(self.clients) > 0
514
-
515
- def stop_server(self):
516
- self.loop.call_soon_threadsafe(self._stop_event.set)
517
-
518
- def run(self):
519
- async def main():
520
- self._loop = asyncio.get_running_loop()
521
- self._stop_event = stop_event = asyncio.Event()
522
- self._event.set()
523
- while True:
524
- try:
525
- self.server = start_server = websockets.serve(self.server_handler,
526
- "0.0.0.0",
527
- self.get_ws_port_func(),
528
- max_size=1000000000)
529
- async with start_server:
530
- await stop_event.wait()
531
- return
532
- except Exception as e:
533
- logger.warning(
534
- f"WebSocket server encountered an error: {e}. Retrying...")
535
- await asyncio.sleep(1)
536
-
537
- asyncio.run(main())
538
-
539
-
540
- def handle_exit_signal(loop):
541
- logger.info("Received exit signal. Shutting down...")
542
- for task in asyncio.all_tasks(loop):
543
- task.cancel()
544
-
545
-
546
- websocket_server_thread = WebsocketServerThread(read=True, get_ws_port_func=lambda: get_config(
547
- ).get_field_value('advanced', 'texthooker_communication_websocket_port'))
548
- websocket_server_thread.start()
549
-
550
- if get_config().advanced.plaintext_websocket_port:
551
- plaintext_websocket_server_thread = WebsocketServerThread(
552
- read=False, get_ws_port_func=lambda: get_config().get_field_value('advanced', 'plaintext_websocket_port'))
553
- plaintext_websocket_server_thread.start()
554
-
555
- overlay_server_thread = WebsocketServerThread(
556
- read=False, get_ws_port_func=lambda: get_config().get_field_value('overlay', 'websocket_port'))
557
- overlay_server_thread.start()
558
-
559
- websocket_server_threads = [
560
- websocket_server_thread,
561
- plaintext_websocket_server_thread,
562
- overlay_server_thread
563
- ]
564
-
565
-
566
308
  async def texthooker_page_coro():
567
309
  global websocket_server_thread, plaintext_websocket_server_thread, overlay_server_thread
568
310
  # Run the WebSocket server in the asyncio event loop
@@ -581,4 +323,4 @@ def run_text_hooker_page():
581
323
 
582
324
 
583
325
  if __name__ == '__main__':
584
- asyncio.run(texthooker_page_coro())
326
+ asyncio.run(texthooker_page_coro())
@@ -0,0 +1,120 @@
1
+ import asyncio
2
+ import json
3
+ import queue
4
+ import threading
5
+ import websockets
6
+
7
+ from GameSentenceMiner.util.configuration import logger, get_config
8
+
9
+
10
+ websocket_queue = queue.Queue()
11
+ paused = False
12
+
13
+
14
+ class WebsocketServerThread(threading.Thread):
15
+ def __init__(self, read, get_ws_port_func):
16
+ super().__init__(daemon=True)
17
+ self._loop = None
18
+ self.read = read
19
+ self.clients = set()
20
+ self._event = threading.Event()
21
+ self.get_ws_port_func = get_ws_port_func
22
+ self.backedup_text = []
23
+
24
+ @property
25
+ def loop(self):
26
+ self._event.wait()
27
+ return self._loop
28
+
29
+ async def send_text_coroutine(self, message):
30
+ if not self.clients:
31
+ self.backedup_text.append(message)
32
+ return
33
+ for client in self.clients:
34
+ await client.send(message)
35
+
36
+ async def server_handler(self, websocket):
37
+ self.clients.add(websocket)
38
+ try:
39
+ if self.backedup_text:
40
+ for message in self.backedup_text:
41
+ await websocket.send(message)
42
+ self.backedup_text.clear()
43
+ async for message in websocket:
44
+ if self.read and not paused:
45
+ websocket_queue.put(message)
46
+ try:
47
+ await websocket.send('True')
48
+ except websockets.exceptions.ConnectionClosedOK:
49
+ pass
50
+ else:
51
+ try:
52
+ await websocket.send('False')
53
+ except websockets.exceptions.ConnectionClosedOK:
54
+ pass
55
+ except websockets.exceptions.ConnectionClosedError:
56
+ pass
57
+ finally:
58
+ self.clients.remove(websocket)
59
+
60
+ async def send_text(self, text):
61
+ if text:
62
+ if isinstance(text, dict) or isinstance(text, list):
63
+ text = json.dumps(text)
64
+ return asyncio.run_coroutine_threadsafe(
65
+ self.send_text_coroutine(text), self.loop)
66
+
67
+ def has_clients(self):
68
+ return len(self.clients) > 0
69
+
70
+ def stop_server(self):
71
+ self.loop.call_soon_threadsafe(self._stop_event.set)
72
+
73
+ def run(self):
74
+ async def main():
75
+ self._loop = asyncio.get_running_loop()
76
+ self._stop_event = stop_event = asyncio.Event()
77
+ self._event.set()
78
+ while True:
79
+ try:
80
+ self.server = start_server = websockets.serve(self.server_handler,
81
+ "0.0.0.0",
82
+ self.get_ws_port_func(),
83
+ max_size=1000000000)
84
+ async with start_server:
85
+ await stop_event.wait()
86
+ return
87
+ except Exception as e:
88
+ logger.warning(
89
+ f"WebSocket server encountered an error: {e}. Retrying...")
90
+ await asyncio.sleep(1)
91
+
92
+ asyncio.run(main())
93
+
94
+
95
+ def handle_exit_signal(loop):
96
+ logger.info("Received exit signal. Shutting down...")
97
+ for task in asyncio.all_tasks(loop):
98
+ task.cancel()
99
+
100
+
101
+ # Initialize WebSocket server threads
102
+ websocket_server_thread = WebsocketServerThread(read=True, get_ws_port_func=lambda: get_config(
103
+ ).get_field_value('advanced', 'texthooker_communication_websocket_port'))
104
+ websocket_server_thread.start()
105
+
106
+ plaintext_websocket_server_thread = None
107
+ if get_config().advanced.plaintext_websocket_port:
108
+ plaintext_websocket_server_thread = WebsocketServerThread(
109
+ read=False, get_ws_port_func=lambda: get_config().get_field_value('advanced', 'plaintext_websocket_port'))
110
+ plaintext_websocket_server_thread.start()
111
+
112
+ overlay_server_thread = WebsocketServerThread(
113
+ read=False, get_ws_port_func=lambda: get_config().get_field_value('overlay', 'websocket_port'))
114
+ overlay_server_thread.start()
115
+
116
+ websocket_server_threads = [
117
+ websocket_server_thread,
118
+ plaintext_websocket_server_thread,
119
+ overlay_server_thread
120
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.15.8
3
+ Version: 2.15.10
4
4
  Summary: A tool for mining sentences from games. Update: Overlay?
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ GameSentenceMiner/config_gui.py,sha256=i79PrY2pP8_VKvIL7uoDv5cgHvCCQBIe0mS_YnX2A
4
4
  GameSentenceMiner/gametext.py,sha256=fgBgLchezpauWELE9Y5G3kVCLfAneD0X4lJFoI3FYbs,10351
5
5
  GameSentenceMiner/gsm.py,sha256=t2GAhMwVEHUzCdqM4tIgAzBUvNmt_Gec515iePacD6k,31945
6
6
  GameSentenceMiner/obs.py,sha256=EyAYhaLvMjoeC-3j7fuvkqZN5logFFanPfb8Wn1C6m0,27296
7
- GameSentenceMiner/vad.py,sha256=BqwvKF0lRB_ys1On_Ua1vvimUFmkHO-D9gBbCkaBaxc,20193
7
+ GameSentenceMiner/vad.py,sha256=N-urlVOT6ayk4BFuIeZhxK1VxLD4Flxu-IStuy8FAVc,20190
8
8
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  GameSentenceMiner/ai/ai_prompting.py,sha256=41xdBzE88Jlt12A0D-T_cMfLO5j6MSxfniOptpwNZm0,24068
10
10
  GameSentenceMiner/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -19,17 +19,17 @@ GameSentenceMiner/locales/en_us.json,sha256=4lCV34FnDOe0c02qHlHnfujQedmqHSL-feN3
19
19
  GameSentenceMiner/locales/ja_jp.json,sha256=LNLo2qIugMcDGiPbSo018zVAU8K_HG8Q4zvIcsHUzTA,28517
20
20
  GameSentenceMiner/locales/zh_cn.json,sha256=lZYB3HAcxhVCSVWcnvuepuCvn6_Y2mvd0-SKJEYx_ko,24829
21
21
  GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=DfcR3bHTu26JJerLzqfW_KpdgUBSrRV4hqSy_LYclps,5967
22
+ GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=Ov04c-nKzh3sADxO-5JyZWVe4DlrHM9edM9tc7-97Jo,5970
23
23
  GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
24
24
  GameSentenceMiner/ocr/owocr_area_selector.py,sha256=Rm1_nuZotJhfOfoJ_3mesh9udtOBjYqKhnAvSief6fo,29181
25
- GameSentenceMiner/ocr/owocr_helper.py,sha256=v1xIfKUwKZNEUUvFNzzalTmIl_Qj_ygFEX4AJszcAVA,31290
25
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=LoGKfVd1PL8R9UgOA_S1TiKT1i_b3Yb8quWl5ls1MEI,31689
26
26
  GameSentenceMiner/ocr/ss_picker.py,sha256=0IhxUdaKruFpZyBL-8SpxWg7bPrlGpy3lhTcMMZ5rwo,5224
27
27
  GameSentenceMiner/owocr/owocr/__init__.py,sha256=87hfN5u_PbL_onLfMACbc0F5j4KyIK9lKnRCj6oZgR0,49
28
28
  GameSentenceMiner/owocr/owocr/__main__.py,sha256=XQaqZY99EKoCpU-gWQjNbTs7Kg17HvBVE7JY8LqIE0o,157
29
29
  GameSentenceMiner/owocr/owocr/config.py,sha256=qM7kISHdUhuygGXOxmgU6Ef2nwBShrZtdqu4InDCViE,8103
30
30
  GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
31
31
  GameSentenceMiner/owocr/owocr/ocr.py,sha256=vRTMKLzi6GDBFZWCyf0tYi6es3cP1cvQOBjZqaZmnBg,70482
32
- GameSentenceMiner/owocr/owocr/run.py,sha256=ctzAhX8nwTYy7E-tqiYFHK51XfY8Y_ze7rBHbDkVPqI,81240
32
+ GameSentenceMiner/owocr/owocr/run.py,sha256=9Z_gIMCZkP5AGY6Lqz3QamSc6YOa5PeweXz9f4o8Msg,81222
33
33
  GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
34
34
  GameSentenceMiner/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  GameSentenceMiner/tools/audio_offset_selector.py,sha256=8Stk3BP-XVIuzRv9nl9Eqd2D-1yD3JrgU-CamBywJmY,8542
@@ -37,7 +37,7 @@ GameSentenceMiner/tools/furigana_filter_preview.py,sha256=BXv7FChPEJW_VeG5XYt6su
37
37
  GameSentenceMiner/tools/ss_selector.py,sha256=cbjMxiKOCuOfbRvLR_PCRlykBrGtm1LXd6u5czPqkmc,4793
38
38
  GameSentenceMiner/tools/window_transparency.py,sha256=GtbxbmZg0-UYPXhfHff-7IKZyY2DKe4B9GdyovfmpeM,8166
39
39
  GameSentenceMiner/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- GameSentenceMiner/util/configuration.py,sha256=cpgusOu4lItCBsZyB8QoAJaXjDovIXNlqyKzh2GMYzw,40241
40
+ GameSentenceMiner/util/configuration.py,sha256=0S8rf_TTy7qbUZc7PLUXEZVScx27tV0cDZR_GcM1QKk,40412
41
41
  GameSentenceMiner/util/db.py,sha256=2bO0rD4i8A1hhsRBER-wgZy9IK17ibRbI8DHxdKvYsI,16598
42
42
  GameSentenceMiner/util/electron_config.py,sha256=KfeJToeFFVw0IR5MKa-gBzpzaGrU-lyJbR9z-sDEHYU,8767
43
43
  GameSentenceMiner/util/ffmpeg.py,sha256=jA-cFtCmdCWrUSPpdtFSLr-GSoqs4qNUzW20v4HPHf0,28715
@@ -45,7 +45,7 @@ GameSentenceMiner/util/get_overlay_coords.py,sha256=P5tI7H0cnveGs33aQdvJGy9DV6aI
45
45
  GameSentenceMiner/util/gsm_utils.py,sha256=Piwv88Q9av2LBeN7M6QDi0Mp0_R2lNbkcI6ekK5hd2o,11851
46
46
  GameSentenceMiner/util/model.py,sha256=R-_RYTYLSDNgBoVTPuPBcIHeOznIqi_vBzQ7VQ20WYk,6727
47
47
  GameSentenceMiner/util/notification.py,sha256=YBhf_mSo_i3cjBz-pmeTPx3wchKiG9BK2VBdZSa2prQ,4597
48
- GameSentenceMiner/util/text_log.py,sha256=zB9--7J_Wwck74IOEI4aWhmCYouqjSE6Sm0sCznF63Q,6731
48
+ GameSentenceMiner/util/text_log.py,sha256=eqLchRrRsWeuex13f5IoxENLWMhjfZtJgUnh9N5jnJQ,6782
49
49
  GameSentenceMiner/util/communication/__init__.py,sha256=xh__yn2MhzXi9eLi89PeZWlJPn-cbBSjskhi1BRraXg,643
50
50
  GameSentenceMiner/util/communication/send.py,sha256=Wki9qIY2CgYnuHbmnyKVIYkcKAN_oYS4up93XMikBaI,222
51
51
  GameSentenceMiner/util/communication/websocket.py,sha256=Zpnqsy8RUeYxMFNGVUaPrWrlbAHjuNxCsn908iWL_kU,3344
@@ -56,8 +56,12 @@ GameSentenceMiner/util/downloader/oneocr_dl.py,sha256=l3s9Z-x1b57GX048o5h-MVv0UT
56
56
  GameSentenceMiner/util/win10toast/__init__.py,sha256=6TL2w6rzNmpJEp6_v2cAJP_7ExA3UsKzwdM08pNcVfE,5341
57
57
  GameSentenceMiner/util/win10toast/__main__.py,sha256=5MYnBcFj8y_6Dyc1kiPd0_FsUuh4yl1cv5wsleU6V4w,668
58
58
  GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ GameSentenceMiner/web/database_api.py,sha256=kcyTWPuw_qtrK5qBzCFTIP0tqIvPmL-NEtiRL9BNbe8,35079
60
+ GameSentenceMiner/web/events.py,sha256=6Vyz5c9MdpMIa7Zqljqhap2XFQnAVYJ0CdQV64TSZsA,5119
59
61
  GameSentenceMiner/web/service.py,sha256=YZchmScTn7AX_GkwV1ULEK6qjdOnJcpc3qfMwDf7cUE,5363
60
- GameSentenceMiner/web/texthooking_page.py,sha256=JhuApKUeQGwqGUO_92Z6yMY0_-kqHQceT4FGzhbGlGE,19269
62
+ GameSentenceMiner/web/stats.py,sha256=daSSxWlumAyqVVtX10qHESF-tZYwCcFMp8qZA5AE0nI,22066
63
+ GameSentenceMiner/web/texthooking_page.py,sha256=hkKu3SIi0V-wktiKhFhQEnhJe6PUWVZs_FSa1-wOYFQ,11134
64
+ GameSentenceMiner/web/websockets.py,sha256=IwwQo6VtgPqeOuc-datgfJyLpX3LwB2MISDqA6EkiSA,4131
61
65
  GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
66
  GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
63
67
  GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
@@ -67,14 +71,16 @@ GameSentenceMiner/web/static/site.webmanifest,sha256=kaeNT-FjFt-T7JGzOhXH7YSqsrD
67
71
  GameSentenceMiner/web/static/style.css,sha256=bPZK0NVMuyRl5NNDuT7ZTzVLKlvSsdmeVHmAW4y5FM0,7001
68
72
  GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESbkGfYwbKzcjKOdzuWo18ABADfN974,51117
69
73
  GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
70
- GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ GameSentenceMiner/web/templates/database.html,sha256=iEJWQvvH_RGWmHuFx0iwNeamBV5FoVxZgFKgfm-4zc4,13582
71
75
  GameSentenceMiner/web/templates/index.html,sha256=LqXZx7-NE42pXSpHNZ3To680rD-vt9wEJoFYBlgp1qU,216923
72
- GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
73
- GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
76
+ GameSentenceMiner/web/templates/search.html,sha256=Fat3hOjQwkYBbdFhgWzRzZ5iEB78-2_0LpT7uK2aURE,3701
77
+ GameSentenceMiner/web/templates/stats.html,sha256=I-3eb2521r7fvDHfCktOB79fhWj2_2lcjFJi9qWAbgA,16668
78
+ GameSentenceMiner/web/templates/text_replacements.html,sha256=rB6mUvzzdbAlNV0dEukZlec0sXgRarBZw8Qh_eWRErE,16694
79
+ GameSentenceMiner/web/templates/utility.html,sha256=KtqnZUMAYs5XsEdC9Tlsd40NKAVic0mu6sh-ReMDJpU,16940
74
80
  GameSentenceMiner/wip/__init___.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- gamesentenceminer-2.15.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
76
- gamesentenceminer-2.15.8.dist-info/METADATA,sha256=bXBIX71jQ-PLrLVxpjrANJmu_ET8bA0EjRbgbgBl-AM,7348
77
- gamesentenceminer-2.15.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
78
- gamesentenceminer-2.15.8.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
79
- gamesentenceminer-2.15.8.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
80
- gamesentenceminer-2.15.8.dist-info/RECORD,,
81
+ gamesentenceminer-2.15.10.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
82
+ gamesentenceminer-2.15.10.dist-info/METADATA,sha256=T-Ik7ri7Xjt4D_J-Yd3fOVK0yf_rOrIe-oChyHlgOA8,7349
83
+ gamesentenceminer-2.15.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
84
+ gamesentenceminer-2.15.10.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
85
+ gamesentenceminer-2.15.10.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
86
+ gamesentenceminer-2.15.10.dist-info/RECORD,,
File without changes