GameSentenceMiner 2.8.6__py3-none-any.whl → 2.8.8__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.
@@ -59,6 +59,7 @@
59
59
  }
60
60
 
61
61
  .textline-buttons {
62
+ margin-left: auto; /* Align buttons to the right */
62
63
  display: flex;
63
64
  gap: 10px;
64
65
  }
@@ -109,7 +110,7 @@
109
110
  <div id="initial-events">
110
111
 
111
112
  </div>
112
- <hr class="initial-events-separator" style="display: none;">
113
+ <hr class="initial-events-separator" id="initial-events-separator" style="display: none;">
113
114
  <div id="session-events">
114
115
 
115
116
  </div>
@@ -119,11 +120,14 @@
119
120
  </button>
120
121
  </div>
121
122
  <script>
123
+ let mainStyle = document.querySelector('head style');
124
+ console.log(mainStyle);
122
125
  let displayedEventIds = new Set();
123
126
  let isTabActive = true;
124
127
  let isFetching = false; // Flag to track if a fetch is in progress
125
128
  let intervalId = 0;
126
- const fetchInterval = 100; // Define the interval as a constant
129
+ const fetchInterval = 5000; // Define the interval as a constant
130
+ const websocketPort = {{ websocket_port }} || 55001;
127
131
 
128
132
  // Drag selection variables
129
133
  let isDragging = false;
@@ -150,7 +154,12 @@
150
154
 
151
155
  events.forEach(ev => {
152
156
  if (!displayedEventIds.has(ev.id)) {
153
- addNewEvent(ev)
157
+ if (ev.history) {
158
+ addNewEventToHistory(ev)
159
+ document.getElementById('initial-events-separator').style.display = 'block';
160
+ } else {
161
+ addNewEvent(ev)
162
+ }
154
163
  }
155
164
  const checkbox = document.querySelector(`[data-event-id="${ev.id}"]`);
156
165
  if (checkbox && !checkboxes_being_updated.has(ev.id)) {
@@ -165,27 +174,68 @@
165
174
  }
166
175
  }
167
176
 
168
- function addNewEvent(event) {
169
- const container = document.getElementById('session-events');
177
+ function addNewEventToHistory(event) {
178
+ displayedEventIds.add(event.id);
179
+ const container = document.getElementById('initial-events');
170
180
  const div = document.createElement('div');
171
- div.className = 'textline';
172
- div.innerHTML = `
173
- <input type="checkbox"
174
- class="multi-line-checkbox"
175
- id="multi-line-checkbox-${event.id}"
176
- ${event.checked ? 'checked' : ''}
177
- aria-label="Mark item"
178
- data-event-id="${event.id}"
179
- onchange="toggleCheckbox('${event.id}', this.checked)">
180
- <p>${event.text}</p>
181
- <em>${event.time.replace(" GMT", "")}</em>
182
- <div class="textline-buttons">
183
- <button onclick="buttonClick('${event.id}', 'Screenshot')">Screenshot</button>
184
- <button onclick="buttonClick('${event.id}', 'Audio')">Audio</button>
185
- </div>
181
+ // div.className = 'textline';
182
+
183
+ const shadowRoot = div.attachShadow({ mode: 'open' });
184
+
185
+ const wrapper = document.createElement('div');
186
+ wrapper.className = 'textline';
187
+ wrapper.innerHTML = `<p>${event.text}</p>
188
+ <em class="clock-icon">${event.time.replace(' GMT', '')}</em>
186
189
  `;
190
+
191
+ const style = document.createElement('style');
192
+ style.textContent = mainStyle.innerHTML;
193
+ shadowRoot.appendChild(style);
194
+ shadowRoot.appendChild(wrapper);
195
+
187
196
  container.appendChild(div);
197
+ window.scrollTo({
198
+ top: document.documentElement.scrollHeight,
199
+ behavior: 'smooth'
200
+ });
201
+ }
202
+
203
+ function addNewEvent(event) {
188
204
  displayedEventIds.add(event.id);
205
+ const container = document.getElementById('session-events');
206
+ const div = document.createElement('div');
207
+ // div.className = 'textline';
208
+
209
+ const shadowRoot = div.attachShadow({ mode: 'open' }); // 'open' allows access from the main DOM
210
+
211
+ const wrapper = document.createElement('div');
212
+ wrapper.className = 'textline';
213
+ wrapper.innerHTML = `
214
+ <input type="checkbox"
215
+ class="multi-line-checkbox"
216
+ id="multi-line-checkbox-${event.id}"
217
+ ${event.checked ? 'checked' : ''}
218
+ aria-label="Mark item"
219
+ data-event-id="${event.id}"
220
+ onchange="toggleCheckbox('${event.id}', this.checked)">
221
+ <p>${event.text}</p>
222
+ <div class="textline-buttons">
223
+ <button onclick="buttonClick('${event.id}', 'Screenshot')" title="Screenshot" style="background-color: #333; color: #fff; border: 1px solid #555; padding: 6px 10px; font-size: 10px; border-radius: 4px; cursor: pointer; transition: background-color 0.3s;">
224
+ &#x1F4F7;
225
+ </button>
226
+ <button onclick="buttonClick('${event.id}', 'Audio')" title="Audio" style="background-color: #333; color: #fff; border: 1px solid #555; padding: 6px 10px; font-size: 10px; border-radius: 4px; cursor: pointer; transition: background-color 0.3s;">
227
+ &#x1F50A;
228
+ </button>
229
+ </div>
230
+ `;
231
+
232
+ // Apply your component's styles within the shadow DOM
233
+ const style = document.createElement('style');
234
+ style.textContent = mainStyle.innerHTML;
235
+ shadowRoot.appendChild(style);
236
+ shadowRoot.appendChild(wrapper);
237
+
238
+ container.appendChild(div);
189
239
  window.scrollTo({
190
240
  top: document.documentElement.scrollHeight,
191
241
  behavior: 'smooth'
@@ -299,6 +349,66 @@
299
349
  document.addEventListener('mouseover', handleMouseOver);
300
350
  document.addEventListener('click', handleCheckboxClick);
301
351
 
352
+ const websocketURL = 'ws://localhost:' + websocketPort;
353
+ let websocket = {};
354
+ let reconnectInterval = 1000; // Time in milliseconds to wait before attempting to reconnect
355
+
356
+ const connectWebSocket = () => {
357
+ if (websocket && websocket.readyState === WebSocket.OPEN) {
358
+ console.log('WebSocket already open, no need to reconnect.');
359
+ return;
360
+ }
361
+ if (websocket && websocket.readyState === WebSocket.CONNECTING) {
362
+ console.log('WebSocket is currently connecting, waiting...');
363
+ return;
364
+ }
365
+
366
+ websocket = new WebSocket(websocketURL);
367
+
368
+ websocket.onopen = (event) => {
369
+ console.log('WebSocket connection opened');
370
+ websocket.send(JSON.stringify({ type: 'initial_data_request' }));
371
+ };
372
+
373
+ websocket.onmessage = (event) => {
374
+ const data = JSON.parse(event.data);
375
+ console.log('Received message:', data);
376
+ if (data.event === 'text_received') {
377
+ console.log("Adding new event:", data.data);
378
+ addNewEvent(data.data);
379
+ } else {
380
+ console.log('Other message:', data);
381
+ }
382
+ };
383
+
384
+ websocket.onclose = (event) => {
385
+ console.log(`WebSocket connection closed. Attempting to reconnect in ${reconnectInterval / 1000} seconds...`);
386
+ // Only attempt to reconnect if the current websocket object is the one that closed
387
+ if (websocket === event.target) {
388
+ // Clear the current websocket reference to allow for a new connection
389
+ websocket = null;
390
+ setTimeout(connectWebSocket, reconnectInterval);
391
+ }
392
+ };
393
+
394
+ websocket.onerror = (error) => {
395
+ console.error('WebSocket error:', error);
396
+ // Optionally attempt to reconnect on error as well, ensuring we don't have an active connection
397
+ if (websocket === error.target || websocket === null) {
398
+ console.log(`Attempting to reconnect in ${reconnectInterval / 1000} seconds...`);
399
+ // Clear the current websocket reference
400
+ websocket = null;
401
+ setTimeout(connectWebSocket, reconnectInterval);
402
+ }
403
+ };
404
+ return websocket;
405
+ };
406
+
407
+ connectWebSocket();
408
+
409
+
410
+ fetchEvents();
411
+
302
412
  console.log("Initial load, fetching events and starting interval...");
303
413
  fetchEvents();
304
414
  intervalId = setInterval(async () => {
@@ -1,19 +1,25 @@
1
+ import asyncio
1
2
  import datetime
2
3
  import json
3
4
  import os
5
+ import queue
6
+ import sqlite3
7
+ import threading
4
8
  from dataclasses import dataclass
5
9
 
6
10
  import flask
11
+ import websockets
7
12
 
8
- from GameSentenceMiner.text_log import GameLine, get_line_by_id
13
+ from GameSentenceMiner.text_log import GameLine, get_line_by_id, initial_time
9
14
  from flask import request, jsonify, send_from_directory
10
15
  import webbrowser
11
16
  from GameSentenceMiner import obs
12
- from GameSentenceMiner.configuration import logger, get_config
17
+ from GameSentenceMiner.configuration import logger, get_config, DB_PATH
13
18
  from GameSentenceMiner.util import TEXT_REPLACEMENTS_FILE
14
19
 
15
20
  port = get_config().general.texthooker_port
16
21
  url = f"http://localhost:{port}"
22
+ websocket_port = 55001
17
23
 
18
24
 
19
25
  @dataclass
@@ -22,16 +28,25 @@ class EventItem:
22
28
  id: str
23
29
  text: str
24
30
  time: datetime.datetime
25
- timestamp: float
26
31
  checked: bool = False
32
+ history: bool = False
27
33
 
28
34
  def to_dict(self):
29
35
  return {
30
36
  'id': self.id,
31
37
  'text': self.text,
32
38
  'time': self.time,
33
- 'timestamp': self.timestamp,
34
- 'checked': self.checked
39
+ 'checked': self.checked,
40
+ 'history': self.history,
41
+ }
42
+
43
+ def to_serializable(self):
44
+ return {
45
+ 'id': self.id,
46
+ 'text': self.text,
47
+ 'time': self.time.isoformat(),
48
+ 'checked': self.checked,
49
+ 'history': self.history,
35
50
  }
36
51
 
37
52
  class EventManager:
@@ -43,6 +58,36 @@ class EventManager:
43
58
  def __init__(self):
44
59
  self.events = []
45
60
  self.events_dict = {}
61
+ self._connect()
62
+ self._create_table()
63
+ self._load_events_from_db()
64
+ # self.close_connection()
65
+
66
+ def _connect(self):
67
+ self.conn = sqlite3.connect(DB_PATH)
68
+ self.cursor = self.conn.cursor()
69
+
70
+ def _create_table(self):
71
+ self.cursor.execute("""
72
+ CREATE TABLE IF NOT EXISTS events (
73
+ event_id TEXT PRIMARY KEY,
74
+ line_id TEXT,
75
+ text TEXT,
76
+ time TEXT
77
+ )
78
+ """)
79
+ self.conn.commit()
80
+
81
+ def _load_events_from_db(self):
82
+ self.cursor.execute("SELECT * FROM events")
83
+ rows = self.cursor.fetchall()
84
+ for row in rows:
85
+ event_id, line_id, text, timestamp = row
86
+ timestamp = datetime.datetime.fromisoformat(timestamp)
87
+ line = GameLine(line_id, text, timestamp, None, None, 0)
88
+ event = EventItem(line, event_id, text, timestamp, False, timestamp < initial_time)
89
+ self.events.append(event)
90
+ self.events_dict[event_id] = event
46
91
 
47
92
  def __iter__(self):
48
93
  return iter(self.events)
@@ -51,20 +96,69 @@ class EventManager:
51
96
  self.events = new_events
52
97
 
53
98
  def add_gameline(self, line: GameLine):
54
- new_event = EventItem(line, line.id, line.text, line.time, line.time.timestamp(), False)
99
+ new_event = EventItem(line, line.id, line.text, line.time, False, False)
55
100
  self.events_dict[line.id] = new_event
56
101
  self.events.append(new_event)
102
+ # self.store_to_db(new_event)
103
+ event_queue.put(new_event)
104
+ return new_event
57
105
 
58
106
  def get_events(self):
59
107
  return self.events
60
108
 
61
109
  def add_event(self, event):
62
110
  self.events.append(event)
111
+ event_queue.put(event)
63
112
 
64
113
  def get(self, event_id):
65
114
  return self.events_dict.get(event_id)
66
115
 
116
+ def close_connection(self):
117
+ if self.conn:
118
+ self.conn.close()
119
+
120
+ class EventProcessor(threading.Thread):
121
+ def __init__(self, event_queue, db_path):
122
+ super().__init__()
123
+ self.event_queue = event_queue
124
+ self.db_path = db_path
125
+ self.conn = None
126
+ self.cursor = None
127
+ self.daemon = True
128
+
129
+ def _connect(self):
130
+ self.conn = sqlite3.connect(self.db_path)
131
+ self.cursor = self.conn.cursor()
132
+
133
+ def run(self):
134
+ self._connect()
135
+ while True:
136
+ try:
137
+ event = self.event_queue.get()
138
+ if event is None: # Exit signal
139
+ break
140
+ self._store_to_db(event)
141
+ except Exception as e:
142
+ logger.error(f"Error processing event: {e}")
143
+ self._close_connection()
144
+
145
+ def _store_to_db(self, event):
146
+ self.cursor.execute("""
147
+ INSERT INTO events (event_id, line_id, text, time)
148
+ VALUES (?, ?, ?, ?)
149
+ """, (event.id, event.line.id, event.text, event.time.isoformat()))
150
+ self.conn.commit()
151
+
152
+ def _close_connection(self):
153
+ if self.conn:
154
+ self.conn.close()
155
+
67
156
  event_manager = EventManager()
157
+ event_queue = queue.Queue()
158
+
159
+ # Initialize the EventProcessor with the queue and event manager
160
+ event_processor = EventProcessor(event_queue, DB_PATH)
161
+ event_processor.start()
68
162
 
69
163
  server_start_time = datetime.datetime.now().timestamp()
70
164
 
@@ -119,27 +213,28 @@ def serve_static(filename):
119
213
 
120
214
  @app.route('/')
121
215
  def index():
122
- with open(os.path.join(app.root_path, 'static', 'utility.html'), encoding='utf-8') as file:
123
- return file.read()
216
+ return flask.render_template('utility.html', websocket_port=websocket_port)
124
217
 
125
218
  @app.route('/texthooker')
126
219
  def texthooker():
127
- with open(os.path.join(app.root_path, 'static', 'utility.html'), encoding='utf-8') as file:
128
- return file.read()
220
+ return flask.render_template('utility.html', websocket_port=websocket_port)
129
221
 
130
222
  @app.route('/textreplacements')
131
223
  def textreplacements():
132
- with open(os.path.join(app.root_path, 'static', 'text_replacements.html'), encoding='utf-8') as file:
133
- return file.read()
224
+ return flask.render_template('text_replacements.html')
134
225
 
135
226
  @app.route('/data', methods=['GET'])
136
227
  def get_data():
137
228
  return jsonify([event.to_dict() for event in event_manager])
138
229
 
139
230
 
140
- def add_event_to_texthooker(line: GameLine):
231
+ async def add_event_to_texthooker(line: GameLine):
141
232
  logger.info("Adding event to web server: %s", line.text)
142
- event_manager.add_gameline(line)
233
+ new_event = event_manager.add_gameline(line)
234
+ await broadcast_message({
235
+ 'event': 'text_received',
236
+ 'data': new_event.to_serializable()
237
+ })
143
238
 
144
239
 
145
240
  @app.route('/update', methods=['POST'])
@@ -178,6 +273,42 @@ def play_audio():
178
273
  return jsonify({}), 200
179
274
 
180
275
 
276
+ connected_clients = set()
277
+
278
+ async def websocket_handler(websocket):
279
+ logger.debug(f"Client connected: {websocket.remote_address}")
280
+ connected_clients.add(websocket)
281
+ try:
282
+ async for message in websocket:
283
+ try:
284
+ data = json.loads(message)
285
+ if 'type' in data and data['type'] == 'get_events':
286
+ initial_events = [{'id': 1, 'text': 'Initial event from WebSocket'}, {'id': 2, 'text': 'Another initial event'}]
287
+ await websocket.send(json.dumps({'event': 'initial_events', 'payload': initial_events}))
288
+ elif 'update_checkbox' in data:
289
+ print(f"Received checkbox update: {data}")
290
+ # Handle checkbox update logic
291
+ pass
292
+ await websocket.send(json.dumps({'response': f'Server received: {message}'}))
293
+ except json.JSONDecodeError:
294
+ await websocket.send(json.dumps({'error': 'Invalid JSON format'}))
295
+ except websockets.exceptions.ConnectionClosedError:
296
+ print(f"Client disconnected abruptly: {websocket.remote_address}")
297
+ except websockets.exceptions.ConnectionClosedOK:
298
+ print(f"Client disconnected gracefully: {websocket.remote_address}")
299
+ finally:
300
+ connected_clients.discard(websocket)
301
+
302
+ async def broadcast_message(message):
303
+ if connected_clients:
304
+ tasks = [client.send(json.dumps(message)) for client in connected_clients]
305
+ await asyncio.gather(*tasks)
306
+
307
+ # async def main():
308
+ # async with websockets.serve(websocket_handler, "localhost", 8765): # Choose a port for WebSocket
309
+ # print("WebSocket server started on ws://localhost:8765/ws (adjust as needed)")
310
+ # await asyncio.Future() # Keep the server running
311
+
181
312
  # @app.route('/store-events', methods=['POST'])
182
313
  # def store_events():
183
314
  # data = request.get_json()
@@ -225,5 +356,31 @@ def start_web_server():
225
356
 
226
357
  app.run(port=port, debug=False) # debug=True provides helpful error messages during development
227
358
 
359
+ async def run_websocket_server(host="0.0.0.0", port=55001):
360
+ global websocket_port
361
+ while True:
362
+ websocket_port = port
363
+ try:
364
+ async with websockets.serve(websocket_handler, host, port):
365
+ logger.debug(f"WebSocket server started at ws://{host}:{port}/")
366
+ await asyncio.Future() # Keep the WebSocket server running
367
+ except OSError as e:
368
+ logger.debug(f"Port {port} is in use. Trying the next port...")
369
+ port += 1
370
+
371
+
372
+
373
+ async def texthooker_page_coro():
374
+ # Run the WebSocket server in the asyncio event loop
375
+ flask_thread = threading.Thread(target=start_web_server)
376
+ flask_thread.daemon = True
377
+ flask_thread.start()
378
+
379
+ # Keep the main asyncio event loop running (for the WebSocket server)
380
+ await run_websocket_server()
381
+
382
+ def run_text_hooker_page():
383
+ asyncio.run(texthooker_page_coro())
384
+
228
385
  if __name__ == '__main__':
229
- start_web_server()
386
+ asyncio.run(run_text_hooker_page())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.8.6
3
+ Version: 2.8.8
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
@@ -36,6 +36,7 @@ Requires-Dist: pywin32; sys_platform == "win32"
36
36
  Requires-Dist: google-generativeai
37
37
  Requires-Dist: pygetwindow; sys_platform == "win32"
38
38
  Requires-Dist: flask
39
+ Requires-Dist: groq
39
40
  Dynamic: license-file
40
41
 
41
42
  # Game Sentence Miner
@@ -1,19 +1,19 @@
1
1
  GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- GameSentenceMiner/anki.py,sha256=2Oy3a8Od8DC3vs1-ktS6KF1iUj74Ptd0SCe-U0cZcfc,14206
3
- GameSentenceMiner/config_gui.py,sha256=8Wc5YdAyaYT09T4pvHiL5Q4NiKRaL19CS3A0JhnSu6k,68822
4
- GameSentenceMiner/configuration.py,sha256=NQMsM23p4_keTFXEaKMTMacIx0TCdk4-TIl4usZkJy4,20801
2
+ GameSentenceMiner/anki.py,sha256=pSkf-il17vKZIBt1ZHHDMfvfO9M-GdF1zox-c--KkAY,14208
3
+ GameSentenceMiner/config_gui.py,sha256=mOsk_9rqQ09VUdUNuSMx-gHTAS2o41T3kD7qpYq3TJA,70928
4
+ GameSentenceMiner/configuration.py,sha256=nV2FLrT-XbedPakHDKo8TddtnWj9Q2RCPFGcVs_S4RY,21673
5
5
  GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
6
6
  GameSentenceMiner/ffmpeg.py,sha256=mcEcJnYl06oJGbLaymFUfqClFiHf6Hhf2SXo3UV9tvM,13378
7
- GameSentenceMiner/gametext.py,sha256=pPJO-NYW15SqAG-IQ0ZHpPt8py4g0yL1Lx0UlR-en-4,5431
8
- GameSentenceMiner/gsm.py,sha256=6BcCYNV9tGQ8FgDh6Y8bPbZ66KWP57oBUqIoVF_nnXg,24667
7
+ GameSentenceMiner/gametext.py,sha256=ClSpOeohBWG17MRVIbhXfNDnkUdxU9mTspGv9975uEc,5422
8
+ GameSentenceMiner/gsm.py,sha256=vCEPg7rE68AUD2u6DesjLxuZSy1gJzbiA0fig673WRM,25254
9
9
  GameSentenceMiner/model.py,sha256=JdnkT4VoPOXmOpRgFdvERZ09c9wLN6tUJxdrKlGZcqo,5305
10
10
  GameSentenceMiner/notification.py,sha256=FY39ChSRK0Y8TQ6lBGsLnpZUFPtFpSy2tweeXVoV7kc,2809
11
- GameSentenceMiner/obs.py,sha256=DLTcK48Dqcg-5BYC2Fp-tPCO4dVI9FuVYbwIawECkwQ,8977
11
+ GameSentenceMiner/obs.py,sha256=GPlsFrcv1eYelXyJfpspGK0iZK5AXPkoFsIGdB7eJrk,10002
12
12
  GameSentenceMiner/package.py,sha256=YlS6QRMuVlm6mdXx0rlXv9_3erTGS21jaP3PNNWfAH0,1250
13
13
  GameSentenceMiner/text_log.py,sha256=tBuZ8ElUgPtiQV8U6U90kmRxposwIkL3fjOYejdzikc,5153
14
14
  GameSentenceMiner/util.py,sha256=bb75EQdk4Nf0i9t3XMznjd6NxTWfkOVeE5bZFstbCbU,8910
15
15
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- GameSentenceMiner/ai/gemini.py,sha256=6kTQPuRH16D-1srhrWa5uPGIy-jBqNm9eRdKBSbpiXA,5423
16
+ GameSentenceMiner/ai/ai_prompting.py,sha256=tu7MxItzuJ5iSyieWIt1YHrMDEUKYAAlur6WWLZ61D4,9137
17
17
  GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
18
18
  GameSentenceMiner/communication/send.py,sha256=oOJdCS6-LNX90amkRn5FL2xqx6THGm56zHR2ntVIFTE,229
19
19
  GameSentenceMiner/communication/websocket.py,sha256=pTcUe_ZZRp9REdSU4qalhPmbT_1DKa7w18j6RfFLELA,3074
@@ -24,21 +24,21 @@ GameSentenceMiner/downloader/oneocr_dl.py,sha256=o3ANp5IodEQoQ8GPcJdg9Y8JzA_lict
24
24
  GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=zagsB4UD9mmZX_r6dFBCXZqdDa0XGk-RvIqbKoPB9lQ,1932
26
26
  GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
27
- GameSentenceMiner/ocr/owocr_area_selector.py,sha256=a2i4kW9g9_DJKt6OJ5dlSiqJGiXkKyqAQaAiXBdrq8U,46680
28
- GameSentenceMiner/ocr/owocr_helper.py,sha256=J6FrcxikTbfoHnnvQfp5uvGIragASrB4I3oepBCcziE,16076
27
+ GameSentenceMiner/ocr/owocr_area_selector.py,sha256=gwYOz-fA5qoL63wh77eyGJtBtO7YVvWyO5cHb3D0Oz4,46738
28
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=PCsPbqrECSbi4u8NIq3PJotStaYVBBbfccHu-DrqpwU,17269
29
29
  GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
30
30
  GameSentenceMiner/owocr/owocr/__main__.py,sha256=r8MI6RAmbkTWqOJ59uvXoDS7CSw5jX5war9ULGWELrA,128
31
- GameSentenceMiner/owocr/owocr/config.py,sha256=738QCJHEWpFhMh966plOcXYWwcshSiRsxjjIwldeTtI,7461
31
+ GameSentenceMiner/owocr/owocr/config.py,sha256=n-xtVylb2Q_H84jb1ZsIGxPQjTNnyvnRny1RhtaLJM8,7550
32
32
  GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
33
33
  GameSentenceMiner/owocr/owocr/ocr.py,sha256=n24Xg8Z8dbcgLpq1u4d22z3tLV1evmf0dK3-Xocv3vs,39878
34
- GameSentenceMiner/owocr/owocr/run.py,sha256=WP3YTocRnZh4AHysIgLokemcaowatMjwfxS0IcxIHps,47859
34
+ GameSentenceMiner/owocr/owocr/run.py,sha256=fzRUo1iYDZffolYHffqCaIYpvBLO2r_FbXgoQ35Eiqk,51151
35
35
  GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=fjJ3CSXLti3WboGPpmsa7MWOwIXsfpHC8N4zKahGGY0,3346
36
36
  GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  GameSentenceMiner/vad/silero_trim.py,sha256=ULf3zwS-JMsY82cKF7gZxREHw8L6lgpWF2U1YqgE9Oc,1681
38
38
  GameSentenceMiner/vad/vosk_helper.py,sha256=125X8C9NxFPlWWpoNsbOnEqKx8RCjXN109zNx_QXhyg,6070
39
39
  GameSentenceMiner/vad/whisper_helper.py,sha256=JJ-iltCh813XdjyEw0Wn5DaErf6PDqfH0Efu1Md8cIY,3543
40
40
  GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- GameSentenceMiner/web/texthooking_page.py,sha256=INCBzEUk0I_452OpDnfDMeGMaMCAJWSmq14S4V1ToHA,7227
41
+ GameSentenceMiner/web/texthooking_page.py,sha256=bI1itAIY4iPjGlFTmtrLbcjo6pvvp_dFlSOYrEDWPJ0,12760
42
42
  GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
44
44
  GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
@@ -46,13 +46,13 @@ GameSentenceMiner/web/static/favicon.ico,sha256=7d25r_FBqRSNsAoEHpSzNoT7zyVt2DJR
46
46
  GameSentenceMiner/web/static/favicon.svg,sha256=x305AP6WlXGtrXIZlaQspdLmwteoFYUoe5FyJ9MYlJ8,11517
47
47
  GameSentenceMiner/web/static/site.webmanifest,sha256=kaeNT-FjFt-T7JGzOhXH7YSqsrDeiplZ2kDxCN_CFU4,436
48
48
  GameSentenceMiner/web/static/style.css,sha256=bPZK0NVMuyRl5NNDuT7ZTzVLKlvSsdmeVHmAW4y5FM0,7001
49
- GameSentenceMiner/web/static/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
50
- GameSentenceMiner/web/static/utility.html,sha256=58br1whazdfrevWCHEzYNa1_VdBvebDDX2tWlX2QrFs,9964
51
49
  GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESbkGfYwbKzcjKOdzuWo18ABADfN974,51117
52
50
  GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
53
- gamesentenceminer-2.8.6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
54
- gamesentenceminer-2.8.6.dist-info/METADATA,sha256=-y0KqwQGu8y7OVhrpmzL0JSPusDvd4YtcxSip82DND4,5912
55
- gamesentenceminer-2.8.6.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
56
- gamesentenceminer-2.8.6.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
57
- gamesentenceminer-2.8.6.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
58
- gamesentenceminer-2.8.6.dist-info/RECORD,,
51
+ GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
52
+ GameSentenceMiner/web/templates/utility.html,sha256=8yJvmHqkHoMN06qWDckhGoguWyP7LUVZ2oms0tBRdEw,14297
53
+ gamesentenceminer-2.8.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
54
+ gamesentenceminer-2.8.8.dist-info/METADATA,sha256=RPgmXLVp04j9a3UP9jNjqlcqpaSVdgzkGRuVRqy6ksg,5932
55
+ gamesentenceminer-2.8.8.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
56
+ gamesentenceminer-2.8.8.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
57
+ gamesentenceminer-2.8.8.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
58
+ gamesentenceminer-2.8.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5