GameSentenceMiner 2.13.10__py3-none-any.whl → 2.13.12__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/gsm.py +14 -14
- GameSentenceMiner/util/configuration.py +1 -1
- GameSentenceMiner/util/ffmpeg.py +12 -12
- GameSentenceMiner/web/service.py +6 -1
- GameSentenceMiner/web/texthooking_page.py +60 -18
- {gamesentenceminer-2.13.10.dist-info → gamesentenceminer-2.13.12.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.13.10.dist-info → gamesentenceminer-2.13.12.dist-info}/RECORD +11 -11
- {gamesentenceminer-2.13.10.dist-info → gamesentenceminer-2.13.12.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.13.10.dist-info → gamesentenceminer-2.13.12.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.13.10.dist-info → gamesentenceminer-2.13.12.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.13.10.dist-info → gamesentenceminer-2.13.12.dist-info}/top_level.txt +0 -0
GameSentenceMiner/gsm.py
CHANGED
@@ -53,7 +53,7 @@ try:
|
|
53
53
|
from GameSentenceMiner.util.text_log import GameLine, get_text_event, get_mined_line, get_all_lines, game_log
|
54
54
|
from GameSentenceMiner.util import *
|
55
55
|
from GameSentenceMiner.web import texthooking_page
|
56
|
-
from GameSentenceMiner.web.service import handle_texthooker_button
|
56
|
+
from GameSentenceMiner.web.service import handle_texthooker_button, set_get_audio_from_video_callback
|
57
57
|
from GameSentenceMiner.web.texthooking_page import run_text_hooker_page
|
58
58
|
except Exception as e:
|
59
59
|
from GameSentenceMiner.util.configuration import logger, is_linux, is_windows
|
@@ -91,10 +91,10 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
91
91
|
selected_lines = []
|
92
92
|
anki_card_creation_time = None
|
93
93
|
mined_line = None
|
94
|
-
|
94
|
+
start_time = 0
|
95
|
+
end_time = 0
|
95
96
|
if gsm_state.line_for_audio or gsm_state.line_for_screenshot:
|
96
|
-
handle_texthooker_button(
|
97
|
-
video_path, get_audio_from_video=VideoToAudioHandler.get_audio)
|
97
|
+
handle_texthooker_button(video_path)
|
98
98
|
return
|
99
99
|
try:
|
100
100
|
if anki.card_queue and len(anki.card_queue) > 0:
|
@@ -201,16 +201,14 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
201
201
|
logger.debug(
|
202
202
|
f"Some error was hit catching to allow further work to be done: {e}", exc_info=True)
|
203
203
|
notification.send_error_no_anki_update()
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
f"Error removing video file {video_path}: {e}", exc_info=True)
|
213
|
-
pass
|
204
|
+
if get_config().paths.remove_video and video_path and not skip_delete:
|
205
|
+
try:
|
206
|
+
if os.path.exists(video_path):
|
207
|
+
logger.debug(f"Removing video: {video_path}")
|
208
|
+
os.remove(video_path)
|
209
|
+
except Exception as e:
|
210
|
+
logger.error(
|
211
|
+
f"Error removing video file {video_path}: {e}", exc_info=True)
|
214
212
|
|
215
213
|
@staticmethod
|
216
214
|
def get_audio(game_line, next_line_time, video_path, anki_card_creation_time=None, temporary=False, timing_only=False, mined_line=None):
|
@@ -525,6 +523,7 @@ def handle_exit():
|
|
525
523
|
def initialize(reloading=False):
|
526
524
|
global obs_process
|
527
525
|
if not reloading:
|
526
|
+
get_temporary_directory(delete=True)
|
528
527
|
if is_windows():
|
529
528
|
download_obs_if_needed()
|
530
529
|
download_ffmpeg_if_needed()
|
@@ -538,6 +537,7 @@ def initialize(reloading=False):
|
|
538
537
|
# gametext.start_text_monitor()
|
539
538
|
os.makedirs(get_config().paths.folder_to_watch, exist_ok=True)
|
540
539
|
os.makedirs(get_config().paths.output_folder, exist_ok=True)
|
540
|
+
set_get_audio_from_video_callback(VideoToAudioHandler.get_audio)
|
541
541
|
initial_checks()
|
542
542
|
register_websocket_message_handler(handle_websocket_message)
|
543
543
|
# if get_config().vad.do_vad_postprocessing:
|
@@ -843,7 +843,7 @@ def get_log_path():
|
|
843
843
|
|
844
844
|
temp_directory = ''
|
845
845
|
|
846
|
-
def get_temporary_directory(delete=
|
846
|
+
def get_temporary_directory(delete=False):
|
847
847
|
global temp_directory
|
848
848
|
if not temp_directory:
|
849
849
|
temp_directory = os.path.join(get_app_directory(), 'temp')
|
GameSentenceMiner/util/ffmpeg.py
CHANGED
@@ -117,7 +117,7 @@ def get_screenshot(video_file, screenshot_timing, try_selector=False):
|
|
117
117
|
get_temporary_directory(),
|
118
118
|
f"{obs.get_current_game(sanitize=True)}.png"))
|
119
119
|
ffmpeg_command = ffmpeg_base_command_list + [
|
120
|
-
"-ss", f"{screenshot_timing}",
|
120
|
+
"-ss", f"{screenshot_timing}",
|
121
121
|
"-i", video_file,
|
122
122
|
"-vframes", "1",
|
123
123
|
output_image
|
@@ -222,7 +222,7 @@ def process_image(image_file):
|
|
222
222
|
break
|
223
223
|
except Exception as e:
|
224
224
|
logger.error(f"Error re-encoding screenshot: {e}. Defaulting to standard PNG.")
|
225
|
-
output_image = make_unique_file_name(get_temporary_directory(), f"{obs.get_current_game(sanitize=True)}.png")
|
225
|
+
output_image = make_unique_file_name(os.path.join(get_temporary_directory(), f"{obs.get_current_game(sanitize=True)}.png"))
|
226
226
|
shutil.move(image_file, output_image)
|
227
227
|
|
228
228
|
logger.info(f"Processed image saved to: {output_image}")
|
@@ -303,21 +303,21 @@ def trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_l
|
|
303
303
|
suffix=f".{get_config().audio.extension}").name
|
304
304
|
start_trim_time, total_seconds, total_seconds_after_offset, file_length = get_video_timings(video_path, game_line, anki_card_creation_time)
|
305
305
|
end_trim_time = 0
|
306
|
+
end_trim_seconds = 0
|
306
307
|
|
307
308
|
ffmpeg_command = ffmpeg_base_command_list + [
|
308
309
|
"-i", untrimmed_audio,
|
309
310
|
"-ss", str(start_trim_time)]
|
310
311
|
if next_line and next_line > game_line.time:
|
311
|
-
|
312
|
-
end_trim_time = f"{
|
312
|
+
end_trim_seconds = total_seconds + (next_line - game_line.time).total_seconds() + get_config().audio.pre_vad_end_offset
|
313
|
+
end_trim_time = f"{end_trim_seconds:.3f}"
|
313
314
|
ffmpeg_command.extend(['-to', end_trim_time])
|
314
315
|
logger.debug(
|
315
316
|
f"Looks Like this is mining from History, or Multiple Lines were selected Trimming end of audio to {end_trim_time} seconds")
|
316
317
|
elif get_config().audio.pre_vad_end_offset and get_config().audio.pre_vad_end_offset < 0:
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
logger.debug(f"Trimming end of audio to {end_trim_time} seconds due to pre-vad end offset")
|
318
|
+
end_trim_seconds = file_length + get_config().audio.pre_vad_end_offset
|
319
|
+
ffmpeg_command.extend(['-to', str(end_trim_seconds)])
|
320
|
+
logger.debug(f"Trimming end of audio to {end_trim_seconds} seconds due to pre-vad end offset")
|
321
321
|
|
322
322
|
ffmpeg_command.extend([
|
323
323
|
"-c", "copy", # Using copy to avoid re-encoding, adjust if needed
|
@@ -326,18 +326,18 @@ def trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_l
|
|
326
326
|
|
327
327
|
logger.debug(" ".join(ffmpeg_command))
|
328
328
|
subprocess.run(ffmpeg_command)
|
329
|
-
gsm_state.previous_trim_args = (untrimmed_audio, start_trim_time,
|
329
|
+
gsm_state.previous_trim_args = (untrimmed_audio, start_trim_time, end_trim_seconds)
|
330
330
|
|
331
331
|
logger.debug(f"{total_seconds_after_offset} trimmed off of beginning")
|
332
332
|
|
333
|
-
if
|
334
|
-
logger.info(f"Audio Extracted and trimmed to {start_trim_time} seconds with end time {
|
333
|
+
if end_trim_seconds:
|
334
|
+
logger.info(f"Audio Extracted and trimmed to {start_trim_time} seconds with end time {end_trim_seconds} seconds")
|
335
335
|
else:
|
336
336
|
logger.info(f"Audio Extracted and trimmed to {start_trim_time} seconds")
|
337
337
|
|
338
338
|
|
339
339
|
logger.debug(f"Audio trimmed and saved to {trimmed_audio}")
|
340
|
-
return trimmed_audio, start_trim_time,
|
340
|
+
return trimmed_audio, start_trim_time, end_trim_seconds
|
341
341
|
|
342
342
|
def get_video_timings(video_path, game_line, anki_card_creation_time=None):
|
343
343
|
if anki_card_creation_time:
|
GameSentenceMiner/web/service.py
CHANGED
@@ -11,7 +11,12 @@ from GameSentenceMiner.util.ffmpeg import get_video_timings
|
|
11
11
|
from GameSentenceMiner.util.text_log import GameLine
|
12
12
|
|
13
13
|
|
14
|
-
def
|
14
|
+
def set_get_audio_from_video_callback(func):
|
15
|
+
global get_audio_from_video
|
16
|
+
get_audio_from_video = func
|
17
|
+
|
18
|
+
|
19
|
+
def handle_texthooker_button(video_path=''):
|
15
20
|
try:
|
16
21
|
if gsm_state.line_for_audio:
|
17
22
|
line: GameLine = gsm_state.line_for_audio
|
@@ -52,6 +52,7 @@ class EventItem:
|
|
52
52
|
'history': self.history,
|
53
53
|
}
|
54
54
|
|
55
|
+
|
55
56
|
class EventManager:
|
56
57
|
events: list[EventItem]
|
57
58
|
events_dict: dict[str, EventItem] = {}
|
@@ -87,7 +88,8 @@ class EventManager:
|
|
87
88
|
event_id, line_id, text, timestamp = row
|
88
89
|
timestamp = datetime.datetime.fromisoformat(timestamp)
|
89
90
|
line = GameLine(line_id, text, timestamp, None, None, 0)
|
90
|
-
event = EventItem(line, event_id, text, timestamp,
|
91
|
+
event = EventItem(line, event_id, text, timestamp,
|
92
|
+
False, timestamp < initial_time)
|
91
93
|
self.events.append(event)
|
92
94
|
self.ids.append(event_id)
|
93
95
|
self.events_dict[event_id] = event
|
@@ -99,7 +101,8 @@ class EventManager:
|
|
99
101
|
self.events = new_events
|
100
102
|
|
101
103
|
def add_gameline(self, line: GameLine):
|
102
|
-
new_event = EventItem(line, line.id, line.text,
|
104
|
+
new_event = EventItem(line, line.id, line.text,
|
105
|
+
line.time, False, False)
|
103
106
|
self.events_dict[line.id] = new_event
|
104
107
|
self.ids.append(line.id)
|
105
108
|
self.events.append(new_event)
|
@@ -130,12 +133,16 @@ class EventManager:
|
|
130
133
|
self.conn.close()
|
131
134
|
|
132
135
|
def clear_history(self):
|
133
|
-
self.cursor.execute("DELETE FROM events WHERE time < ?",
|
136
|
+
self.cursor.execute("DELETE FROM events WHERE time < ?",
|
137
|
+
(initial_time.isoformat(),))
|
134
138
|
logger.info(f"Cleared history before {initial_time.isoformat()}")
|
135
139
|
self.conn.commit()
|
136
140
|
# Clear the in-memory events as well
|
137
|
-
event_manager.events = [
|
138
|
-
|
141
|
+
event_manager.events = [
|
142
|
+
event for event in event_manager if not event.history]
|
143
|
+
event_manager.events_dict = {
|
144
|
+
event.id: event for event in event_manager.events}
|
145
|
+
|
139
146
|
|
140
147
|
class EventProcessor(threading.Thread):
|
141
148
|
def __init__(self, event_queue, db_path):
|
@@ -173,6 +180,7 @@ class EventProcessor(threading.Thread):
|
|
173
180
|
if self.conn:
|
174
181
|
self.conn.close()
|
175
182
|
|
183
|
+
|
176
184
|
event_manager = EventManager()
|
177
185
|
event_queue = queue.Queue()
|
178
186
|
|
@@ -185,6 +193,8 @@ server_start_time = datetime.datetime.now().timestamp()
|
|
185
193
|
app = flask.Flask(__name__)
|
186
194
|
|
187
195
|
# Load data from the JSON file
|
196
|
+
|
197
|
+
|
188
198
|
def load_data_from_file():
|
189
199
|
if os.path.exists(TEXT_REPLACEMENTS_FILE):
|
190
200
|
with open(TEXT_REPLACEMENTS_FILE, 'r', encoding='utf-8') as file:
|
@@ -192,10 +202,13 @@ def load_data_from_file():
|
|
192
202
|
return {"enabled": True, "args": {"replacements": {}}}
|
193
203
|
|
194
204
|
# Save data to the JSON file
|
205
|
+
|
206
|
+
|
195
207
|
def save_data_to_file(data):
|
196
208
|
with open(TEXT_REPLACEMENTS_FILE, 'w', encoding='utf-8') as file:
|
197
209
|
json.dump(data, file, indent=4, ensure_ascii=False)
|
198
210
|
|
211
|
+
|
199
212
|
@app.route('/load-data', methods=['GET'])
|
200
213
|
def load_data():
|
201
214
|
try:
|
@@ -204,6 +217,7 @@ def load_data():
|
|
204
217
|
except Exception as e:
|
205
218
|
return jsonify({"error": f"Failed to load data: {str(e)}"}), 500
|
206
219
|
|
220
|
+
|
207
221
|
@app.route('/save-data', methods=['POST'])
|
208
222
|
def save_data():
|
209
223
|
try:
|
@@ -217,40 +231,49 @@ def save_data():
|
|
217
231
|
except Exception as e:
|
218
232
|
return jsonify({"error": f"Failed to save data: {str(e)}"}), 500
|
219
233
|
|
234
|
+
|
220
235
|
def inject_server_start_time(html_content, timestamp):
|
221
236
|
placeholder = '<script>'
|
222
237
|
replacement = f'<script>const serverStartTime = {timestamp};'
|
223
238
|
return html_content.replace(placeholder, replacement)
|
224
239
|
|
240
|
+
|
225
241
|
@app.route('/favicon.ico')
|
226
242
|
def favicon():
|
227
243
|
return send_from_directory(os.path.join(app.root_path, 'static'),
|
228
244
|
'favicon.ico', mimetype='image/vnd.microsoft.icon')
|
229
245
|
|
246
|
+
|
230
247
|
@app.route('/<path:filename>')
|
231
248
|
def serve_static(filename):
|
232
249
|
return send_from_directory('pages', filename)
|
233
250
|
|
251
|
+
|
234
252
|
@app.route('/')
|
235
253
|
def index():
|
236
254
|
return send_from_directory('templates', 'index.html')
|
237
255
|
|
256
|
+
|
238
257
|
@app.route('/texthooker')
|
239
258
|
def texthooker():
|
240
259
|
return send_from_directory('templates', 'index.html')
|
241
260
|
|
261
|
+
|
242
262
|
@app.route('/textreplacements')
|
243
263
|
def textreplacements():
|
244
264
|
return flask.render_template('text_replacements.html')
|
245
265
|
|
266
|
+
|
246
267
|
@app.route('/data', methods=['GET'])
|
247
268
|
def get_data():
|
248
269
|
return jsonify([event.to_dict() for event in event_manager])
|
249
270
|
|
271
|
+
|
250
272
|
@app.route('/get_ids', methods=['GET'])
|
251
273
|
def get_ids():
|
252
274
|
return jsonify(event_manager.get_ids())
|
253
275
|
|
276
|
+
|
254
277
|
@app.route('/clear_history', methods=['POST'])
|
255
278
|
def clear_history():
|
256
279
|
temp_em = EventManager()
|
@@ -268,8 +291,8 @@ async def add_event_to_texthooker(line: GameLine):
|
|
268
291
|
})
|
269
292
|
if get_config().advanced.plaintext_websocket_port:
|
270
293
|
await plaintext_websocket_server_thread.send_text(line.text)
|
271
|
-
|
272
|
-
|
294
|
+
|
295
|
+
|
273
296
|
async def send_word_coordinates_to_overlay(boxes):
|
274
297
|
if boxes and len(boxes) > 0 and overlay_server_thread:
|
275
298
|
await overlay_server_thread.send_text(boxes)
|
@@ -286,6 +309,7 @@ def update_event():
|
|
286
309
|
event_manager.get(event_id).checked = not event.checked
|
287
310
|
return jsonify({'message': 'Event updated successfully'}), 200
|
288
311
|
|
312
|
+
|
289
313
|
@app.route('/get-screenshot', methods=['Post'])
|
290
314
|
def get_screenshot():
|
291
315
|
"""Endpoint to get a screenshot of the current game screen."""
|
@@ -294,12 +318,13 @@ def get_screenshot():
|
|
294
318
|
if event_id is None:
|
295
319
|
return jsonify({'error': 'Missing id'}), 400
|
296
320
|
gsm_state.line_for_screenshot = get_line_by_id(event_id)
|
297
|
-
if gsm_state.previous_line_for_screenshot and gsm_state.line_for_screenshot
|
321
|
+
if gsm_state.previous_line_for_screenshot and gsm_state.line_for_screenshot == gsm_state.previous_line_for_screenshot or gsm_state.previous_line_for_audio and gsm_state.line_for_screenshot == gsm_state.previous_line_for_audio:
|
298
322
|
handle_texthooker_button(gsm_state.previous_replay)
|
299
323
|
else:
|
300
324
|
obs.save_replay_buffer()
|
301
325
|
return jsonify({}), 200
|
302
326
|
|
327
|
+
|
303
328
|
@app.route('/play-audio', methods=['POST'])
|
304
329
|
def play_audio():
|
305
330
|
"""Endpoint to play audio for a specific event."""
|
@@ -307,13 +332,16 @@ def play_audio():
|
|
307
332
|
event_id = data.get('id')
|
308
333
|
if event_id is None:
|
309
334
|
return jsonify({'error': 'Missing id'}), 400
|
335
|
+
print(f"Playing audio for event ID: {event_id}")
|
310
336
|
gsm_state.line_for_audio = get_line_by_id(event_id)
|
311
|
-
|
337
|
+
print(f"gsm_state.line_for_audio: {gsm_state.line_for_audio}")
|
338
|
+
if gsm_state.previous_line_for_audio and gsm_state.line_for_audio == gsm_state.previous_line_for_audio or gsm_state.previous_line_for_screenshot and gsm_state.line_for_audio == gsm_state.previous_line_for_screenshot:
|
312
339
|
handle_texthooker_button(gsm_state.previous_replay)
|
313
340
|
else:
|
314
341
|
obs.save_replay_buffer()
|
315
342
|
return jsonify({}), 200
|
316
343
|
|
344
|
+
|
317
345
|
@app.route("/translate-line", methods=['POST'])
|
318
346
|
def translate_line():
|
319
347
|
data = request.get_json()
|
@@ -362,9 +390,11 @@ def get_status():
|
|
362
390
|
def get_selected_lines():
|
363
391
|
return [item.line for item in event_manager if item.checked]
|
364
392
|
|
393
|
+
|
365
394
|
def are_lines_selected():
|
366
395
|
return any(item.checked for item in event_manager)
|
367
396
|
|
397
|
+
|
368
398
|
def reset_checked_lines():
|
369
399
|
async def send_reset_message():
|
370
400
|
await websocket_server_thread.send_text({
|
@@ -373,9 +403,11 @@ def reset_checked_lines():
|
|
373
403
|
event_manager.reset_checked_lines()
|
374
404
|
asyncio.run(send_reset_message())
|
375
405
|
|
406
|
+
|
376
407
|
def open_texthooker():
|
377
408
|
webbrowser.open(url + '/texthooker')
|
378
409
|
|
410
|
+
|
379
411
|
def start_web_server():
|
380
412
|
logger.debug("Starting web server...")
|
381
413
|
import logging
|
@@ -386,7 +418,8 @@ def start_web_server():
|
|
386
418
|
if get_config().general.open_multimine_on_startup:
|
387
419
|
open_texthooker()
|
388
420
|
|
389
|
-
|
421
|
+
# debug=True provides helpful error messages during development
|
422
|
+
app.run(host='0.0.0.0', port=port, debug=False)
|
390
423
|
|
391
424
|
|
392
425
|
websocket_queue = queue.Queue()
|
@@ -445,7 +478,7 @@ class WebsocketServerThread(threading.Thread):
|
|
445
478
|
text = json.dumps(text)
|
446
479
|
return asyncio.run_coroutine_threadsafe(
|
447
480
|
self.send_text_coroutine(text), self.loop)
|
448
|
-
|
481
|
+
|
449
482
|
def has_clients(self):
|
450
483
|
return len(self.clients) > 0
|
451
484
|
|
@@ -467,24 +500,30 @@ class WebsocketServerThread(threading.Thread):
|
|
467
500
|
await stop_event.wait()
|
468
501
|
return
|
469
502
|
except Exception as e:
|
470
|
-
logger.warning(
|
503
|
+
logger.warning(
|
504
|
+
f"WebSocket server encountered an error: {e}. Retrying...")
|
471
505
|
await asyncio.sleep(1)
|
472
506
|
|
473
507
|
asyncio.run(main())
|
474
508
|
|
509
|
+
|
475
510
|
def handle_exit_signal(loop):
|
476
511
|
logger.info("Received exit signal. Shutting down...")
|
477
512
|
for task in asyncio.all_tasks(loop):
|
478
513
|
task.cancel()
|
479
|
-
|
480
|
-
|
514
|
+
|
515
|
+
|
516
|
+
websocket_server_thread = WebsocketServerThread(read=True, get_ws_port_func=lambda: get_config(
|
517
|
+
).get_field_value('advanced', 'texthooker_communication_websocket_port'))
|
481
518
|
websocket_server_thread.start()
|
482
519
|
|
483
520
|
if get_config().advanced.plaintext_websocket_port:
|
484
|
-
plaintext_websocket_server_thread = WebsocketServerThread(
|
521
|
+
plaintext_websocket_server_thread = WebsocketServerThread(
|
522
|
+
read=False, get_ws_port_func=lambda: get_config().get_field_value('advanced', 'plaintext_websocket_port'))
|
485
523
|
plaintext_websocket_server_thread.start()
|
486
|
-
|
487
|
-
overlay_server_thread = WebsocketServerThread(
|
524
|
+
|
525
|
+
overlay_server_thread = WebsocketServerThread(
|
526
|
+
read=False, get_ws_port_func=lambda: get_config().get_field_value('wip', 'overlay_websocket_port'))
|
488
527
|
overlay_server_thread.start()
|
489
528
|
|
490
529
|
websocket_server_threads = [
|
@@ -493,6 +532,7 @@ websocket_server_threads = [
|
|
493
532
|
overlay_server_thread
|
494
533
|
]
|
495
534
|
|
535
|
+
|
496
536
|
async def texthooker_page_coro():
|
497
537
|
global websocket_server_thread, plaintext_websocket_server_thread, overlay_server_thread
|
498
538
|
# Run the WebSocket server in the asyncio event loop
|
@@ -502,11 +542,13 @@ async def texthooker_page_coro():
|
|
502
542
|
|
503
543
|
# Keep the main asyncio event loop running (for the WebSocket server)
|
504
544
|
|
545
|
+
|
505
546
|
def run_text_hooker_page():
|
506
547
|
try:
|
507
548
|
asyncio.run(texthooker_page_coro())
|
508
549
|
except KeyboardInterrupt:
|
509
550
|
logger.info("Shutting down due to KeyboardInterrupt.")
|
510
551
|
|
552
|
+
|
511
553
|
if __name__ == '__main__':
|
512
|
-
asyncio.run(texthooker_page_coro())
|
554
|
+
asyncio.run(texthooker_page_coro())
|
@@ -2,7 +2,7 @@ GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
2
2
|
GameSentenceMiner/anki.py,sha256=zBvKvu-LBaUmG1bUNLq9QXmVoPidqZCxJn4UPw6pUug,21041
|
3
3
|
GameSentenceMiner/config_gui.py,sha256=BeI5Mz9CqDsW08fn1BNzZNUegEmHZsxkDmST8rY2jqU,125738
|
4
4
|
GameSentenceMiner/gametext.py,sha256=h6IOGesK79X8IlvqqMmSzRkSVtkPAXDMHrkQsBwEV1E,10879
|
5
|
-
GameSentenceMiner/gsm.py,sha256=
|
5
|
+
GameSentenceMiner/gsm.py,sha256=daUAD_kr5nEG0WjPxMDq2PVSLXfx4dQ73OvTUWX9AlQ,28298
|
6
6
|
GameSentenceMiner/obs.py,sha256=smlP_BFuuMkASVDEPG3DjxJ6p617kZXuNTeQ0edtH64,18703
|
7
7
|
GameSentenceMiner/vad.py,sha256=zFReBMvNEEaQ_YEozCTCaMdV-o40FwtlxYRb17cYZio,19125
|
8
8
|
GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -33,9 +33,9 @@ GameSentenceMiner/owocr/owocr/run.py,sha256=0rsxeIlUys_bpwKDTfBwjER2FpgXKbfzM6lO
|
|
33
33
|
GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
|
34
34
|
GameSentenceMiner/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
35
35
|
GameSentenceMiner/util/audio_offset_selector.py,sha256=8Stk3BP-XVIuzRv9nl9Eqd2D-1yD3JrgU-CamBywJmY,8542
|
36
|
-
GameSentenceMiner/util/configuration.py,sha256=
|
36
|
+
GameSentenceMiner/util/configuration.py,sha256=p0ZnrPa0UTFzj59y__pNy0aWOxjlHz1fURZI33qWu74,37184
|
37
37
|
GameSentenceMiner/util/electron_config.py,sha256=9CA27nzEFlxezzDqOPHxeD4BdJ093AnSJ9DJTcwWPsM,8762
|
38
|
-
GameSentenceMiner/util/ffmpeg.py,sha256=
|
38
|
+
GameSentenceMiner/util/ffmpeg.py,sha256=HI2H_prbiZtLtLQgYOj1QNu0Ik_8Du_7gtoHftaz1g4,23520
|
39
39
|
GameSentenceMiner/util/gsm_utils.py,sha256=Piwv88Q9av2LBeN7M6QDi0Mp0_R2lNbkcI6ekK5hd2o,11851
|
40
40
|
GameSentenceMiner/util/model.py,sha256=hmA_seopP2bK40v9T4ulua9TrAeWtbkdCv-sTBPBQDk,6660
|
41
41
|
GameSentenceMiner/util/notification.py,sha256=-qk3kTKEERzmMxx5XMh084HCyFmbfqz0XjY1hTKhCeQ,4202
|
@@ -51,8 +51,8 @@ GameSentenceMiner/util/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
|
|
51
51
|
GameSentenceMiner/util/downloader/download_tools.py,sha256=zR-aEHiFVkyo-9oPoSx6nQ2K-_J8WBHLZyLoOhypsW4,8458
|
52
52
|
GameSentenceMiner/util/downloader/oneocr_dl.py,sha256=l3s9Z-x1b57GX048o5h-MVv0UTZo4H-Q-zb-JREkMLI,10439
|
53
53
|
GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
|
-
GameSentenceMiner/web/service.py,sha256=
|
55
|
-
GameSentenceMiner/web/texthooking_page.py,sha256=
|
54
|
+
GameSentenceMiner/web/service.py,sha256=YZchmScTn7AX_GkwV1ULEK6qjdOnJcpc3qfMwDf7cUE,5363
|
55
|
+
GameSentenceMiner/web/texthooking_page.py,sha256=qCNtWtWZAiC91uPrHD2LUWdHOufVuzDhTyTH8Z4oXXk,17927
|
56
56
|
GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
57
|
GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
|
58
58
|
GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
|
@@ -67,9 +67,9 @@ GameSentenceMiner/web/templates/index.html,sha256=Gv3CJvNnhAzIVV_QxhNq4OD-pXDt1v
|
|
67
67
|
GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
|
68
68
|
GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
|
69
69
|
GameSentenceMiner/wip/get_overlay_coords.py,sha256=nJRytHJwUBToXeAIkf45HP7Yv42YO-ILbP5h8GVeE2Q,19791
|
70
|
-
gamesentenceminer-2.13.
|
71
|
-
gamesentenceminer-2.13.
|
72
|
-
gamesentenceminer-2.13.
|
73
|
-
gamesentenceminer-2.13.
|
74
|
-
gamesentenceminer-2.13.
|
75
|
-
gamesentenceminer-2.13.
|
70
|
+
gamesentenceminer-2.13.12.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
71
|
+
gamesentenceminer-2.13.12.dist-info/METADATA,sha256=DzkTDM7jkS2G3xMLbBgdIs1sjhfdeUXFziip4sAGzlc,1464
|
72
|
+
gamesentenceminer-2.13.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
73
|
+
gamesentenceminer-2.13.12.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
|
74
|
+
gamesentenceminer-2.13.12.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
|
75
|
+
gamesentenceminer-2.13.12.dist-info/RECORD,,
|
File without changes
|
{gamesentenceminer-2.13.10.dist-info → gamesentenceminer-2.13.12.dist-info}/entry_points.txt
RENAMED
File without changes
|
{gamesentenceminer-2.13.10.dist-info → gamesentenceminer-2.13.12.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
File without changes
|