GameSentenceMiner 2.8.12__py3-none-any.whl → 2.8.14__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 +15 -7
- GameSentenceMiner/anki.py +1 -1
- GameSentenceMiner/config_gui.py +7 -0
- GameSentenceMiner/configuration.py +5 -1
- GameSentenceMiner/ffmpeg.py +11 -6
- GameSentenceMiner/gsm.py +7 -5
- GameSentenceMiner/ocr/gsm_ocr_config.py +1 -0
- GameSentenceMiner/ocr/owocr_helper.py +15 -11
- GameSentenceMiner/util.py +10 -10
- GameSentenceMiner/web/templates/utility.html +50 -14
- GameSentenceMiner/web/texthooking_page.py +22 -4
- {gamesentenceminer-2.8.12.dist-info → gamesentenceminer-2.8.14.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.8.12.dist-info → gamesentenceminer-2.8.14.dist-info}/RECORD +17 -17
- {gamesentenceminer-2.8.12.dist-info → gamesentenceminer-2.8.14.dist-info}/WHEEL +1 -1
- {gamesentenceminer-2.8.12.dist-info → gamesentenceminer-2.8.14.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.8.12.dist-info → gamesentenceminer-2.8.14.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.8.12.dist-info → gamesentenceminer-2.8.14.dist-info}/top_level.txt +0 -0
@@ -175,13 +175,21 @@ current_ai_config: Ai | None = None
|
|
175
175
|
|
176
176
|
def get_ai_prompt_result(lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str = ""):
|
177
177
|
global ai_manager, current_ai_config
|
178
|
-
|
179
|
-
if get_config().ai
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
178
|
+
try:
|
179
|
+
if not ai_manager or get_config().ai != current_ai_config:
|
180
|
+
if get_config().ai.provider == AIType.GEMINI.value:
|
181
|
+
ai_manager = GeminiAI(model=get_config().ai.gemini_model, api_key=get_config().ai.gemini_api_key, logger=logger)
|
182
|
+
elif get_config().ai.provider == AIType.GROQ.value:
|
183
|
+
ai_manager = GroqAI(model=get_config().ai.groq_model, api_key=get_config().ai.groq_api_key, logger=logger)
|
184
|
+
current_ai_config = get_config().ai
|
185
|
+
if not ai_manager:
|
186
|
+
logger.error("AI is enabled but the AI Manager did not initialize. Check your AI Config IN GSM.")
|
187
|
+
return ""
|
188
|
+
return ai_manager.process(lines, sentence, current_line, game_title)
|
189
|
+
except Exception as e:
|
190
|
+
logger.info("Error caught while trying to get AI prompt result. Check logs for more details.")
|
191
|
+
logger.debug(e)
|
192
|
+
return ""
|
185
193
|
|
186
194
|
if __name__ == '__main__':
|
187
195
|
lines = [
|
GameSentenceMiner/anki.py
CHANGED
@@ -282,7 +282,7 @@ def update_new_card():
|
|
282
282
|
texthooking_page.reset_checked_lines()
|
283
283
|
else:
|
284
284
|
logger.info("New card(s) detected! Added to Processing Queue!")
|
285
|
-
card_queue.append(last_card)
|
285
|
+
card_queue.append((last_card, datetime.now()))
|
286
286
|
try:
|
287
287
|
obs.save_replay_buffer()
|
288
288
|
except Exception as e:
|
GameSentenceMiner/config_gui.py
CHANGED
@@ -203,6 +203,7 @@ class ConfigApp:
|
|
203
203
|
multi_line_line_break=self.multi_line_line_break.get(),
|
204
204
|
multi_line_sentence_storage_field=self.multi_line_sentence_storage_field.get(),
|
205
205
|
ocr_sends_to_clipboard=self.ocr_sends_to_clipboard.get(),
|
206
|
+
use_anki_note_creation_time=self.use_anki_note_creation_time.get(),
|
206
207
|
),
|
207
208
|
ai=Ai(
|
208
209
|
enabled=self.ai_enabled.get(),
|
@@ -997,6 +998,12 @@ class ConfigApp:
|
|
997
998
|
self.add_label_and_increment_row(advanced_frame, "Enable to send OCR results to clipboard.", row=self.current_row, column=2)
|
998
999
|
|
999
1000
|
|
1001
|
+
ttk.Label(advanced_frame, text="Use Anki Creation Date for Audio Timing:").grid(row=self.current_row, column=0, sticky='W')
|
1002
|
+
self.use_anki_note_creation_time = tk.BooleanVar(value=self.settings.advanced.use_anki_note_creation_time)
|
1003
|
+
ttk.Checkbutton(advanced_frame, variable=self.use_anki_note_creation_time).grid(row=self.current_row, column=1, sticky='W')
|
1004
|
+
self.add_label_and_increment_row(advanced_frame, "Use the Anki note creation date for audio timing instead of the OBS replay time.", row=self.current_row, column=2)
|
1005
|
+
|
1006
|
+
|
1000
1007
|
|
1001
1008
|
@new_tab
|
1002
1009
|
def create_ai_tab(self):
|
@@ -181,6 +181,7 @@ class Advanced:
|
|
181
181
|
multi_line_line_break: str = '<br>'
|
182
182
|
multi_line_sentence_storage_field: str = ''
|
183
183
|
ocr_sends_to_clipboard: bool = True
|
184
|
+
use_anki_note_creation_time: bool = False
|
184
185
|
|
185
186
|
@dataclass_json
|
186
187
|
@dataclass
|
@@ -200,7 +201,10 @@ class Ai:
|
|
200
201
|
def __post_init__(self):
|
201
202
|
if not self.gemini_api_key:
|
202
203
|
self.gemini_api_key = self.api_key
|
203
|
-
|
204
|
+
if self.provider == 'gemini':
|
205
|
+
self.provider = AI_GEMINI
|
206
|
+
if self.provider == 'groq':
|
207
|
+
self.provider = AI_GROQ
|
204
208
|
|
205
209
|
@dataclass_json
|
206
210
|
@dataclass
|
GameSentenceMiner/ffmpeg.py
CHANGED
@@ -57,6 +57,8 @@ def get_screenshot_time(video_path, game_line, default_beginning=False, vad_begi
|
|
57
57
|
# Assuming initial_time is defined elsewhere if game_line is None
|
58
58
|
line_time = initial_time
|
59
59
|
|
60
|
+
logger.info("Calculating screenshot time for line: " + str(game_line.text))
|
61
|
+
|
60
62
|
file_length = get_video_duration(video_path)
|
61
63
|
file_mod_time = get_file_modification_time(video_path)
|
62
64
|
|
@@ -150,7 +152,7 @@ def get_audio_codec(video_path):
|
|
150
152
|
return None
|
151
153
|
|
152
154
|
|
153
|
-
def get_audio_and_trim(video_path, game_line, next_line_time):
|
155
|
+
def get_audio_and_trim(video_path, game_line, next_line_time, anki_card_creation_time):
|
154
156
|
supported_formats = {
|
155
157
|
'opus': 'libopus',
|
156
158
|
'mp3': 'libmp3lame',
|
@@ -184,7 +186,7 @@ def get_audio_and_trim(video_path, game_line, next_line_time):
|
|
184
186
|
|
185
187
|
subprocess.run(command)
|
186
188
|
|
187
|
-
return trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_line_time)
|
189
|
+
return trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_line_time, anki_card_creation_time)
|
188
190
|
|
189
191
|
|
190
192
|
def get_video_duration(file_path):
|
@@ -202,10 +204,10 @@ def get_video_duration(file_path):
|
|
202
204
|
return float(duration_info["format"]["duration"]) # Return the duration in seconds
|
203
205
|
|
204
206
|
|
205
|
-
def trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_line):
|
207
|
+
def trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_line, anki_card_creation_time):
|
206
208
|
trimmed_audio = tempfile.NamedTemporaryFile(dir=configuration.get_temporary_directory(),
|
207
209
|
suffix=f".{get_config().audio.extension}").name
|
208
|
-
start_trim_time, total_seconds, total_seconds_after_offset = get_video_timings(video_path, game_line)
|
210
|
+
start_trim_time, total_seconds, total_seconds_after_offset = get_video_timings(video_path, game_line, anki_card_creation_time)
|
209
211
|
|
210
212
|
ffmpeg_command = ffmpeg_base_command_list + [
|
211
213
|
"-i", untrimmed_audio,
|
@@ -232,8 +234,11 @@ def trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_l
|
|
232
234
|
logger.info(f"Audio trimmed and saved to {trimmed_audio}")
|
233
235
|
return trimmed_audio
|
234
236
|
|
235
|
-
def get_video_timings(video_path, game_line):
|
236
|
-
|
237
|
+
def get_video_timings(video_path, game_line, anki_card_creation_time):
|
238
|
+
if anki_card_creation_time and get_config().advanced.use_anki_note_creation_time:
|
239
|
+
file_mod_time = anki_card_creation_time
|
240
|
+
else:
|
241
|
+
file_mod_time = get_file_modification_time(video_path)
|
237
242
|
file_length = get_video_duration(video_path)
|
238
243
|
time_delta = file_mod_time - game_line.time
|
239
244
|
# Convert time_delta to FFmpeg-friendly format (HH:MM:SS.milliseconds)
|
GameSentenceMiner/gsm.py
CHANGED
@@ -90,8 +90,9 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
90
90
|
return
|
91
91
|
try:
|
92
92
|
last_note = None
|
93
|
+
anki_card_creation_time = None
|
93
94
|
if anki.card_queue and len(anki.card_queue) > 0:
|
94
|
-
last_note = anki.card_queue.pop(0)
|
95
|
+
last_note, anki_card_creation_time = anki.card_queue.pop(0)
|
95
96
|
with util.lock:
|
96
97
|
util.set_last_mined_line(anki.get_sentence(last_note))
|
97
98
|
if os.path.exists(video_path) and os.access(video_path, os.R_OK):
|
@@ -136,7 +137,8 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
136
137
|
final_audio_output, should_update_audio, vad_trimmed_audio, vad_beginning, vad_end = VideoToAudioHandler.get_audio(
|
137
138
|
start_line,
|
138
139
|
line_cutoff,
|
139
|
-
video_path
|
140
|
+
video_path,
|
141
|
+
anki_card_creation_time)
|
140
142
|
else:
|
141
143
|
final_audio_output = ""
|
142
144
|
should_update_audio = False
|
@@ -148,7 +150,7 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
148
150
|
elif not get_config().anki.sentence_audio_field:
|
149
151
|
logger.info("No SentenceAudio Field in config, skipping audio processing!")
|
150
152
|
|
151
|
-
ss_timing = ffmpeg.get_screenshot_time(video_path, mined_line, vad_beginning, vad_end, bool(selected_lines))
|
153
|
+
ss_timing = ffmpeg.get_screenshot_time(video_path, mined_line, vad_beginning=vad_beginning, vad_end=vad_end, doing_multi_line=bool(selected_lines))
|
152
154
|
|
153
155
|
if get_config().anki.update_anki and last_note:
|
154
156
|
anki.update_anki_card(last_note, note, audio_path=final_audio_output, video_path=video_path,
|
@@ -171,8 +173,8 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
171
173
|
|
172
174
|
|
173
175
|
@staticmethod
|
174
|
-
def get_audio(game_line, next_line_time, video_path, temporary=False):
|
175
|
-
trimmed_audio = get_audio_and_trim(video_path, game_line, next_line_time)
|
176
|
+
def get_audio(game_line, next_line_time, video_path, anki_card_creation_time,temporary=False):
|
177
|
+
trimmed_audio = get_audio_and_trim(video_path, game_line, next_line_time, anki_card_creation_time)
|
176
178
|
if temporary:
|
177
179
|
return trimmed_audio
|
178
180
|
vad_trimmed_audio = make_unique_file_name(
|
@@ -43,6 +43,7 @@ class OCRConfig:
|
|
43
43
|
coordinate_system: str = None
|
44
44
|
window_geometry: Optional[WindowGeometry] = None
|
45
45
|
window: Optional[str] = None
|
46
|
+
language: str = "ja"
|
46
47
|
|
47
48
|
def __post_init__(self):
|
48
49
|
if self.coordinate_system and self.coordinate_system == "percentage" and self.window:
|
@@ -320,7 +320,7 @@ def run_oneocr(ocr_config: OCRConfig, i, area=False):
|
|
320
320
|
text_callback=text_callback,
|
321
321
|
screen_capture_exclusions=exclusions,
|
322
322
|
rectangle=i,
|
323
|
-
language=
|
323
|
+
language=language)
|
324
324
|
done = True
|
325
325
|
|
326
326
|
|
@@ -339,23 +339,27 @@ def get_window(window_name):
|
|
339
339
|
return None
|
340
340
|
|
341
341
|
if __name__ == "__main__":
|
342
|
-
global ocr1, ocr2, twopassocr
|
342
|
+
global ocr1, ocr2, twopassocr, language
|
343
343
|
import sys
|
344
344
|
|
345
345
|
args = sys.argv[1:]
|
346
|
-
if len(args) ==
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
346
|
+
if len(args) == 4:
|
347
|
+
language = args[0]
|
348
|
+
ocr1 = args[1]
|
349
|
+
ocr2 = args[2]
|
350
|
+
twopassocr = bool(int(args[3]))
|
351
|
+
elif len(args) == 3:
|
352
|
+
language = args[0]
|
353
|
+
ocr1 = args[1]
|
354
|
+
ocr2 = args[2]
|
353
355
|
twopassocr = True
|
354
|
-
elif len(args) ==
|
355
|
-
|
356
|
+
elif len(args) == 2:
|
357
|
+
language = args[0]
|
358
|
+
ocr1 = args[1]
|
356
359
|
ocr2 = None
|
357
360
|
twopassocr = False
|
358
361
|
else:
|
362
|
+
language = "ja"
|
359
363
|
ocr1 = "oneocr"
|
360
364
|
ocr2 = "glens"
|
361
365
|
twopassocr = True
|
GameSentenceMiner/util.py
CHANGED
@@ -58,7 +58,7 @@ def timedelta_to_ffmpeg_friendly_format(td_obj):
|
|
58
58
|
|
59
59
|
|
60
60
|
def get_file_modification_time(file_path):
|
61
|
-
mod_time_epoch = os.path.
|
61
|
+
mod_time_epoch = os.path.getmtime(file_path)
|
62
62
|
mod_time = datetime.fromtimestamp(mod_time_epoch)
|
63
63
|
return mod_time
|
64
64
|
|
@@ -255,12 +255,12 @@ os.makedirs(os.path.dirname(TEXT_REPLACEMENTS_FILE), exist_ok=True)
|
|
255
255
|
|
256
256
|
import urllib.request
|
257
257
|
|
258
|
-
if not os.path.exists(OCR_REPLACEMENTS_FILE):
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
258
|
+
# if not os.path.exists(OCR_REPLACEMENTS_FILE):
|
259
|
+
# url = "https://raw.githubusercontent.com/bpwhelan/GameSentenceMiner/refs/heads/main/electron-src/assets/ocr_replacements.json"
|
260
|
+
# try:
|
261
|
+
# with urllib.request.urlopen(url) as response:
|
262
|
+
# data = response.read().decode('utf-8')
|
263
|
+
# with open(OCR_REPLACEMENTS_FILE, 'w', encoding='utf-8') as f:
|
264
|
+
# f.write(data)
|
265
|
+
# except Exception as e:
|
266
|
+
# logger.error(f"Failed to fetch JSON from {url}: {e}")
|
@@ -107,26 +107,30 @@
|
|
107
107
|
</style>
|
108
108
|
</head>
|
109
109
|
<body>
|
110
|
+
<div style="position: fixed; top: 20px; right: 20px; display: flex; gap: 10px;">
|
111
|
+
<button onclick="window.location.href='/textreplacements'" style="background-color: #1a73e8; color: #ffffff; border: none; padding: 10px 20px; font-size: 12px; cursor: pointer; transition: background-color 0.3s; border-radius: 5px;">
|
112
|
+
Text Replacements
|
113
|
+
</button>
|
114
|
+
<button id="delete-history" style="background-color: #1a73e8; color: #ffffff; border: none; padding: 10px 20px; font-size: 12px; cursor: pointer; transition: background-color 0.3s; border-radius: 5px;">
|
115
|
+
Clear History
|
116
|
+
</button>
|
117
|
+
</div>
|
110
118
|
<div id="initial-events">
|
111
119
|
|
112
120
|
</div>
|
113
121
|
<hr class="initial-events-separator" id="initial-events-separator" style="display: none;">
|
114
122
|
<div id="session-events">
|
115
123
|
|
116
|
-
</div>
|
117
|
-
<div>
|
118
|
-
<button onclick="window.location.href='/textreplacements'" style="margin-top: 20px; background-color: #1a73e8; color: #ffffff; border: none; padding: 10px 20px; font-size: 16px; cursor: pointer; transition: background-color 0.3s; border-radius: 5px;">
|
119
|
-
Text Replacements
|
120
|
-
</button>
|
121
124
|
</div>
|
122
125
|
<script>
|
123
126
|
let mainStyle = document.querySelector('head style');
|
127
|
+
let deleteHistoryButton = document.getElementById('delete-history');
|
124
128
|
console.log(mainStyle);
|
125
129
|
let displayedEventIds = new Set();
|
126
130
|
let isTabActive = true;
|
127
131
|
let isFetching = false; // Flag to track if a fetch is in progress
|
128
132
|
let intervalId = 0;
|
129
|
-
const fetchInterval =
|
133
|
+
const fetchInterval = 100; // Define the interval as a constant
|
130
134
|
const websocketPort = {{ websocket_port }} || 55001;
|
131
135
|
|
132
136
|
// Drag selection variables
|
@@ -135,11 +139,13 @@
|
|
135
139
|
let newCheckboxState = false;
|
136
140
|
let hoveredCheckboxes = new Set();
|
137
141
|
let checkboxes = []; // Will hold all checkbox elements
|
142
|
+
let checkboxMap = {};
|
138
143
|
let checkboxes_being_updated = new Set();
|
139
144
|
|
140
145
|
// Shift click selection variable
|
141
146
|
let lastChecked = null;
|
142
147
|
|
148
|
+
|
143
149
|
async function fetchEvents() {
|
144
150
|
if (document.hidden || isFetching) {
|
145
151
|
return;
|
@@ -161,12 +167,17 @@
|
|
161
167
|
addNewEvent(ev)
|
162
168
|
}
|
163
169
|
}
|
164
|
-
|
165
|
-
|
166
|
-
|
170
|
+
if (!ev.history) {
|
171
|
+
console.log(checkboxMap[ev.id])
|
172
|
+
if (!checkboxes_being_updated.has(ev.id)) {
|
173
|
+
const checkbox = checkboxMap[ev.id];
|
174
|
+
if (checkbox) {
|
175
|
+
checkbox.checked = ev.checked;
|
176
|
+
}
|
177
|
+
}
|
167
178
|
}
|
168
179
|
});
|
169
|
-
checkboxes = Array.from(document.querySelectorAll('#session-events input[type="checkbox"]')); // Update checkboxes array after new events
|
180
|
+
// checkboxes = Array.from(document.querySelectorAll('#session-events input[type="checkbox"]')); // Update checkboxes array after new events
|
170
181
|
} catch (error) {
|
171
182
|
console.error("Error fetching events:", error);
|
172
183
|
} finally {
|
@@ -196,7 +207,6 @@
|
|
196
207
|
container.appendChild(div);
|
197
208
|
window.scrollTo({
|
198
209
|
top: document.documentElement.scrollHeight,
|
199
|
-
behavior: 'smooth'
|
200
210
|
});
|
201
211
|
}
|
202
212
|
|
@@ -235,10 +245,13 @@
|
|
235
245
|
shadowRoot.appendChild(style);
|
236
246
|
shadowRoot.appendChild(wrapper);
|
237
247
|
|
248
|
+
let checkbox = shadowRoot.querySelector('.multi-line-checkbox')
|
249
|
+
checkboxes.push(checkbox);
|
250
|
+
checkboxMap[event.id] = checkbox; // Store the checkbox in the map for easy access
|
251
|
+
|
238
252
|
container.appendChild(div);
|
239
253
|
window.scrollTo({
|
240
254
|
top: document.documentElement.scrollHeight,
|
241
|
-
behavior: 'smooth'
|
242
255
|
});
|
243
256
|
}
|
244
257
|
|
@@ -344,10 +357,34 @@
|
|
344
357
|
lastChecked = e.target;
|
345
358
|
}
|
346
359
|
|
360
|
+
function deleteHistory(e) {
|
361
|
+
e.preventDefault();
|
362
|
+
if (confirm("Are you sure you want to delete the history? This action cannot be undone.")) {
|
363
|
+
fetch('/clear_history', {
|
364
|
+
method: 'POST',
|
365
|
+
headers: { 'Content-Type': 'application/json' }
|
366
|
+
})
|
367
|
+
.then(response => {
|
368
|
+
if (!response.ok) {
|
369
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
370
|
+
}
|
371
|
+
// Clear the displayed events
|
372
|
+
displayedEventIds.clear();
|
373
|
+
document.getElementById('initial-events').innerHTML = '';
|
374
|
+
document.getElementById('session-events').innerHTML = '';
|
375
|
+
document.getElementById('initial-events-separator').style.display = 'none';
|
376
|
+
})
|
377
|
+
.catch(error => {
|
378
|
+
console.error("Error deleting history:", error);
|
379
|
+
});
|
380
|
+
}
|
381
|
+
}
|
382
|
+
|
347
383
|
document.addEventListener('mousedown', handleMouseDown);
|
348
384
|
document.addEventListener('mouseup', handleMouseUp);
|
349
385
|
document.addEventListener('mouseover', handleMouseOver);
|
350
386
|
document.addEventListener('click', handleCheckboxClick);
|
387
|
+
deleteHistoryButton.addEventListener('click', deleteHistory);
|
351
388
|
|
352
389
|
const websocketURL = 'ws://localhost:' + websocketPort;
|
353
390
|
let websocket = {};
|
@@ -404,7 +441,7 @@
|
|
404
441
|
return websocket;
|
405
442
|
};
|
406
443
|
|
407
|
-
connectWebSocket();
|
444
|
+
// connectWebSocket();
|
408
445
|
|
409
446
|
|
410
447
|
fetchEvents();
|
@@ -419,7 +456,6 @@
|
|
419
456
|
|
420
457
|
window.scrollTo({
|
421
458
|
top: document.documentElement.scrollHeight,
|
422
|
-
behavior: 'smooth'
|
423
459
|
});
|
424
460
|
</script>
|
425
461
|
</body>
|
@@ -103,6 +103,10 @@ class EventManager:
|
|
103
103
|
event_queue.put(new_event)
|
104
104
|
return new_event
|
105
105
|
|
106
|
+
def reset_checked_lines(self):
|
107
|
+
for event in self.events:
|
108
|
+
event.checked = False
|
109
|
+
|
106
110
|
def get_events(self):
|
107
111
|
return self.events
|
108
112
|
|
@@ -117,6 +121,14 @@ class EventManager:
|
|
117
121
|
if self.conn:
|
118
122
|
self.conn.close()
|
119
123
|
|
124
|
+
def clear_history(self):
|
125
|
+
self.cursor.execute("DELETE FROM events WHERE time < ?", (initial_time.isoformat(),))
|
126
|
+
logger.info(f"Cleared history before {initial_time.isoformat()}")
|
127
|
+
self.conn.commit()
|
128
|
+
# Clear the in-memory events as well
|
129
|
+
event_manager.events = [event for event in event_manager if not event.history]
|
130
|
+
event_manager.events_dict = {event.id: event for event in event_manager.events}
|
131
|
+
|
120
132
|
class EventProcessor(threading.Thread):
|
121
133
|
def __init__(self, event_queue, db_path):
|
122
134
|
super().__init__()
|
@@ -227,12 +239,20 @@ def textreplacements():
|
|
227
239
|
def get_data():
|
228
240
|
return jsonify([event.to_dict() for event in event_manager])
|
229
241
|
|
242
|
+
@app.route('/clear_history', methods=['POST'])
|
243
|
+
def clear_history():
|
244
|
+
temp_em = EventManager()
|
245
|
+
temp_em.clear_history()
|
246
|
+
temp_em.close_connection()
|
247
|
+
return jsonify({'message': 'History cleared successfully'}), 200
|
248
|
+
|
230
249
|
|
231
250
|
async def add_event_to_texthooker(line: GameLine):
|
232
251
|
logger.info("Adding event to web server: %s", line.text)
|
233
252
|
new_event = event_manager.add_gameline(line)
|
234
253
|
await broadcast_message({
|
235
254
|
'event': 'text_received',
|
255
|
+
'sentence': line.text,
|
236
256
|
'data': new_event.to_serializable()
|
237
257
|
})
|
238
258
|
|
@@ -247,8 +267,7 @@ def update_event():
|
|
247
267
|
return jsonify({'error': 'Missing id or checked status'}), 400
|
248
268
|
|
249
269
|
event_manager.get(event_id).checked = checked
|
250
|
-
|
251
|
-
return jsonify({'error': 'Event not found'}), 404
|
270
|
+
return jsonify({'message': 'Event updated successfully'}), 200
|
252
271
|
|
253
272
|
@app.route('/get-screenshot', methods=['Post'])
|
254
273
|
def get_screenshot():
|
@@ -338,8 +357,7 @@ def are_lines_selected():
|
|
338
357
|
return any(item.checked for item in event_manager)
|
339
358
|
|
340
359
|
def reset_checked_lines():
|
341
|
-
|
342
|
-
item.checked = False
|
360
|
+
event_manager.reset_checked_lines()
|
343
361
|
|
344
362
|
def open_texthooker():
|
345
363
|
webbrowser.open(url + '/texthooker')
|
@@ -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=OCLgZa-iEp93v-R0zKFkDCjule_EAoP5rIqtnMHLnOw,14226
|
3
|
+
GameSentenceMiner/config_gui.py,sha256=RHiiQxALMuVNg3ydNO49q7dtrYudGJIbLOXMk2NYoOs,71617
|
4
|
+
GameSentenceMiner/configuration.py,sha256=jRuEgIM78W4rDrbAr8fABbNKeGlcS7P2JZP-Q1cBUV8,21922
|
5
5
|
GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
|
6
|
-
GameSentenceMiner/ffmpeg.py,sha256=
|
6
|
+
GameSentenceMiner/ffmpeg.py,sha256=MiraA1cYv0g5adwwI8f-zeFL0kG7sVuhxQSF_OuzhvU,13732
|
7
7
|
GameSentenceMiner/gametext.py,sha256=ClSpOeohBWG17MRVIbhXfNDnkUdxU9mTspGv9975uEc,5422
|
8
|
-
GameSentenceMiner/gsm.py,sha256=
|
8
|
+
GameSentenceMiner/gsm.py,sha256=BiCxMJN9_BoX3NL0UkNAuoZk5OnrkQ7PSFgSCzrDJFc,25459
|
9
9
|
GameSentenceMiner/model.py,sha256=JdnkT4VoPOXmOpRgFdvERZ09c9wLN6tUJxdrKlGZcqo,5305
|
10
10
|
GameSentenceMiner/notification.py,sha256=FY39ChSRK0Y8TQ6lBGsLnpZUFPtFpSy2tweeXVoV7kc,2809
|
11
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
|
-
GameSentenceMiner/util.py,sha256=
|
14
|
+
GameSentenceMiner/util.py,sha256=LzWGIDZb8NLv-RyrE_d6ycoQEwM1zpaDhWp0LKb6_Zc,8928
|
15
15
|
GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
GameSentenceMiner/ai/ai_prompting.py,sha256=
|
16
|
+
GameSentenceMiner/ai/ai_prompting.py,sha256=uTtXIDRpcQqPT4EC-R_pwRP9pBmZ64I6vMDOexhJRp8,9505
|
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
|
@@ -22,10 +22,10 @@ GameSentenceMiner/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
22
22
|
GameSentenceMiner/downloader/download_tools.py,sha256=mI1u_FGBmBqDIpCH3jOv8DOoZ3obgP5pIf9o9SVfX2Q,8131
|
23
23
|
GameSentenceMiner/downloader/oneocr_dl.py,sha256=o3ANp5IodEQoQ8GPcJdg9Y8JzA_lictwnebFPwwUZVk,10144
|
24
24
|
GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
|
-
GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=
|
25
|
+
GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=rQC6C8PKJXWoAvwCOYa363kodQQBwl1YNeYsD0bBbx4,1957
|
26
26
|
GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
|
27
27
|
GameSentenceMiner/ocr/owocr_area_selector.py,sha256=gwYOz-fA5qoL63wh77eyGJtBtO7YVvWyO5cHb3D0Oz4,46738
|
28
|
-
GameSentenceMiner/ocr/owocr_helper.py,sha256=
|
28
|
+
GameSentenceMiner/ocr/owocr_helper.py,sha256=ZGAoekdkcqzluA1_QkjbsTM7D4LdW1ceW19msUwlG78,17388
|
29
29
|
GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
|
30
30
|
GameSentenceMiner/owocr/owocr/__main__.py,sha256=r8MI6RAmbkTWqOJ59uvXoDS7CSw5jX5war9ULGWELrA,128
|
31
31
|
GameSentenceMiner/owocr/owocr/config.py,sha256=n-xtVylb2Q_H84jb1ZsIGxPQjTNnyvnRny1RhtaLJM8,7550
|
@@ -38,7 +38,7 @@ GameSentenceMiner/vad/silero_trim.py,sha256=ULf3zwS-JMsY82cKF7gZxREHw8L6lgpWF2U1
|
|
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=AtVV9RS7HC3XnOq4X0FIMqJrzFoGlfSHFvS_CfhuzuA,13558
|
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
|
@@ -50,10 +50,10 @@ GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESb
|
|
50
50
|
GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
|
51
51
|
GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
52
|
GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
|
53
|
-
GameSentenceMiner/web/templates/utility.html,sha256=
|
54
|
-
gamesentenceminer-2.8.
|
55
|
-
gamesentenceminer-2.8.
|
56
|
-
gamesentenceminer-2.8.
|
57
|
-
gamesentenceminer-2.8.
|
58
|
-
gamesentenceminer-2.8.
|
59
|
-
gamesentenceminer-2.8.
|
53
|
+
GameSentenceMiner/web/templates/utility.html,sha256=y3HS4ShvmsBHtK5RkA2Unq77SWCvbwkcSxuXzTZV1H8,15976
|
54
|
+
gamesentenceminer-2.8.14.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
55
|
+
gamesentenceminer-2.8.14.dist-info/METADATA,sha256=JSiTQV0z-Ko1VEI8hfp7Beo50mMIKOhohHU_fKRPUXY,5933
|
56
|
+
gamesentenceminer-2.8.14.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
|
57
|
+
gamesentenceminer-2.8.14.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
|
58
|
+
gamesentenceminer-2.8.14.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
|
59
|
+
gamesentenceminer-2.8.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|