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.
- GameSentenceMiner/ai/ai_prompting.py +201 -0
- GameSentenceMiner/anki.py +4 -3
- GameSentenceMiner/config_gui.py +42 -12
- GameSentenceMiner/configuration.py +39 -15
- GameSentenceMiner/gametext.py +26 -34
- GameSentenceMiner/gsm.py +58 -42
- GameSentenceMiner/obs.py +47 -24
- GameSentenceMiner/ocr/owocr_area_selector.py +4 -2
- GameSentenceMiner/ocr/owocr_helper.py +32 -3
- GameSentenceMiner/owocr/owocr/config.py +3 -1
- GameSentenceMiner/owocr/owocr/run.py +78 -6
- GameSentenceMiner/web/{static → templates}/utility.html +130 -20
- GameSentenceMiner/web/texthooking_page.py +172 -15
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.8.dist-info}/METADATA +2 -1
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.8.dist-info}/RECORD +20 -20
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.8.dist-info}/WHEEL +1 -1
- GameSentenceMiner/ai/gemini.py +0 -143
- /GameSentenceMiner/web/{static → templates}/text_replacements.html +0 -0
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.8.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.8.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.8.dist-info}/top_level.txt +0 -0
@@ -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 =
|
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
|
-
|
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
|
169
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
+
📷
|
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
|
+
🔊
|
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
|
-
'
|
34
|
-
'
|
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,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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=
|
3
|
-
GameSentenceMiner/config_gui.py,sha256=
|
4
|
-
GameSentenceMiner/configuration.py,sha256=
|
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=
|
8
|
-
GameSentenceMiner/gsm.py,sha256=
|
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=
|
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/
|
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=
|
28
|
-
GameSentenceMiner/ocr/owocr_helper.py,sha256=
|
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=
|
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=
|
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=
|
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
|
-
|
54
|
-
|
55
|
-
gamesentenceminer-2.8.
|
56
|
-
gamesentenceminer-2.8.
|
57
|
-
gamesentenceminer-2.8.
|
58
|
-
gamesentenceminer-2.8.
|
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,,
|