GameSentenceMiner 2.8.6__py3-none-any.whl → 2.8.7__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/texthooking_page.py +172 -15
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/METADATA +2 -1
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/RECORD +18 -20
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/WHEEL +1 -1
- GameSentenceMiner/ai/gemini.py +0 -143
- GameSentenceMiner/web/static/text_replacements.html +0 -238
- GameSentenceMiner/web/static/utility.html +0 -316
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/top_level.txt +0 -0
@@ -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.7
|
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,11 @@ 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.
|
54
|
-
gamesentenceminer-2.8.
|
55
|
-
gamesentenceminer-2.8.
|
56
|
-
gamesentenceminer-2.8.
|
57
|
-
gamesentenceminer-2.8.
|
58
|
-
gamesentenceminer-2.8.
|
51
|
+
gamesentenceminer-2.8.7.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
52
|
+
gamesentenceminer-2.8.7.dist-info/METADATA,sha256=D_BRnBo7BO5pHItwjMraVgSCcZAfuJdjhhId2ouPwaA,5932
|
53
|
+
gamesentenceminer-2.8.7.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
|
54
|
+
gamesentenceminer-2.8.7.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
|
55
|
+
gamesentenceminer-2.8.7.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
|
56
|
+
gamesentenceminer-2.8.7.dist-info/RECORD,,
|
GameSentenceMiner/ai/gemini.py
DELETED
@@ -1,143 +0,0 @@
|
|
1
|
-
import google.generativeai as genai
|
2
|
-
|
3
|
-
from GameSentenceMiner.configuration import get_config, logger
|
4
|
-
|
5
|
-
MODEL = "gemini-2.0-flash" # or "gemini-pro-vision" if you need image support
|
6
|
-
|
7
|
-
genai.configure(api_key=get_config().ai.api_key)
|
8
|
-
model = genai.GenerativeModel(MODEL)
|
9
|
-
|
10
|
-
def translate_with_context(lines, sentence, current_line_index, game_title=""):
|
11
|
-
"""
|
12
|
-
Translates a line of dialogue with context from surrounding lines.
|
13
|
-
|
14
|
-
Args:
|
15
|
-
lines: A list of strings representing the dialogue lines.
|
16
|
-
sentence: Sentence to get translation for
|
17
|
-
current_line_index: The index of the line to translate.
|
18
|
-
game_title: Optional title of the game for added context.
|
19
|
-
|
20
|
-
Returns:
|
21
|
-
A string containing the translated sentence with context.
|
22
|
-
"""
|
23
|
-
|
24
|
-
if not lines or current_line_index < 0 or current_line_index >= len(lines):
|
25
|
-
return "Invalid input."
|
26
|
-
|
27
|
-
context_lines = []
|
28
|
-
|
29
|
-
# Get the previous 10 lines (or fewer if at the beginning)
|
30
|
-
for i in range(max(0, current_line_index - 10), current_line_index):
|
31
|
-
context_lines.append(lines[i].text)
|
32
|
-
|
33
|
-
# Get the current line
|
34
|
-
current_line = lines[current_line_index]
|
35
|
-
context_lines.append(current_line.text)
|
36
|
-
|
37
|
-
#Get the next 10 lines (or fewer if at the end)
|
38
|
-
for i in range(current_line_index + 1, min(current_line_index + 11, len(lines))):
|
39
|
-
context_lines.append(lines[i].text)
|
40
|
-
|
41
|
-
ai_config = get_config().ai
|
42
|
-
|
43
|
-
#this is ugly, but prettier in the output... so idk
|
44
|
-
if ai_config.use_canned_translation_prompt:
|
45
|
-
prompt_to_use = \
|
46
|
-
f"""
|
47
|
-
Translate the following Japanese dialogue from the game {game_title} into natural, context-aware English. Focus on preserving the tone, intent, and emotional nuance of the original text, paying close attention to the context provided by surrounding lines. The dialogue may include slang, idioms, implied meanings, or game-specific terminology that should be adapted naturally for English-speaking players. Ensure the translation feels immersive and aligns with the game's narrative style and character voices.
|
48
|
-
Translate only the specified line below, providing a single result. Do not include additional text, explanations, or other lines unless explicitly requested. Allow expletives if more natural. Allow HTML tags for emphasis, italics, and other formatting as needed. Please also try to preserve existing HTML tags from the specified sentence if appropriate.
|
49
|
-
|
50
|
-
Line to Translate:
|
51
|
-
"""
|
52
|
-
elif ai_config.use_canned_context_prompt:
|
53
|
-
prompt_to_use = \
|
54
|
-
f"""
|
55
|
-
Provide a very brief summary of the scene in English based on the provided Japanese dialogue and context. Focus on the characters' actions and the immediate situation being described.
|
56
|
-
|
57
|
-
Current Sentence:
|
58
|
-
"""
|
59
|
-
else:
|
60
|
-
prompt_to_use = ai_config.custom_prompt
|
61
|
-
|
62
|
-
|
63
|
-
prompt = \
|
64
|
-
f"""
|
65
|
-
Dialogue Context:
|
66
|
-
|
67
|
-
{chr(10).join(context_lines)}
|
68
|
-
|
69
|
-
I am playing the game {game_title}. With that, and the above dialogue context in mind, answer the following prompt.
|
70
|
-
|
71
|
-
{prompt_to_use}
|
72
|
-
|
73
|
-
{sentence}
|
74
|
-
"""
|
75
|
-
|
76
|
-
logger.debug(prompt)
|
77
|
-
try:
|
78
|
-
response = model.generate_content(prompt)
|
79
|
-
return response.text.strip()
|
80
|
-
except Exception as e:
|
81
|
-
return f"Translation failed: {e}"
|
82
|
-
|
83
|
-
# Example Usage: Zero Escape: 999 examples
|
84
|
-
|
85
|
-
# zero_escape_dialogue1 = [
|
86
|
-
# "扉は開いた…?",
|
87
|
-
# "まさか、こんな仕掛けが…",
|
88
|
-
# "一体、何が起こっているんだ?",
|
89
|
-
# "この数字の意味は…?",
|
90
|
-
# "落ち着いて、考えるんだ。",
|
91
|
-
# "でも、時間が…",
|
92
|
-
# "まだ、諦めるな!",
|
93
|
-
# "一体、誰が…?",
|
94
|
-
# "まさか、あの人が…?",
|
95
|
-
# "もう、ダメだ…",
|
96
|
-
# "まだ、希望はある!",
|
97
|
-
# "この部屋から、脱出するんだ!",
|
98
|
-
# "でも、どうやって…?",
|
99
|
-
# "何か、手がかりがあるはずだ!",
|
100
|
-
# "早く、見つけないと…",
|
101
|
-
# ]
|
102
|
-
#
|
103
|
-
#
|
104
|
-
# current_line_index = 3
|
105
|
-
# translation = translate_with_context(zero_escape_dialogue1, current_line_index, "Zero Escape: 999")
|
106
|
-
# print(f"Original: {zero_escape_dialogue1[current_line_index]}")
|
107
|
-
# print(f"Translation: {translation}")
|
108
|
-
#
|
109
|
-
# # Example with fewer context lines at the beginning.
|
110
|
-
# zero_escape_dialogue2 = [
|
111
|
-
# "このアミュレット…",
|
112
|
-
# "何かを感じる…",
|
113
|
-
# "この数字は…",
|
114
|
-
# "9…?",
|
115
|
-
# "まさか、これは…",
|
116
|
-
# "何かの手がかり…?",
|
117
|
-
# "急がないと…",
|
118
|
-
# "時間がない…",
|
119
|
-
# "早く、脱出を…",
|
120
|
-
# ]
|
121
|
-
#
|
122
|
-
# current_line_index = 3
|
123
|
-
# translation = translate_with_context(zero_escape_dialogue2, current_line_index, "Zero Escape: 999")
|
124
|
-
# print(f"Original: {zero_escape_dialogue2[current_line_index]}")
|
125
|
-
# print(f"Translation: {translation}")
|
126
|
-
#
|
127
|
-
# #example with fewer context lines at the end.
|
128
|
-
# zero_escape_dialogue3 = [
|
129
|
-
# "この状況、理解できない。",
|
130
|
-
# "誰かが、私たちを閉じ込めたのか?",
|
131
|
-
# "なぜ、こんなことを…?",
|
132
|
-
# "このゲームの目的は…?",
|
133
|
-
# "一体、何が真実なんだ?",
|
134
|
-
# "信じられるのは、誰…?",
|
135
|
-
# "疑心暗鬼になるな。",
|
136
|
-
# "でも、どうすれば…?",
|
137
|
-
# "とにかく、進むしかない。"
|
138
|
-
# ]
|
139
|
-
#
|
140
|
-
# current_line_index = 4
|
141
|
-
# translation = translate_with_context(zero_escape_dialogue3, current_line_index, "Zero Escape: 999")
|
142
|
-
# print(f"Original: {zero_escape_dialogue3[current_line_index]}")
|
143
|
-
# print(f"Translation: {translation}")
|