GameSentenceMiner 2.10.0__py3-none-any.whl → 2.10.2__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/anki.py +7 -5
- GameSentenceMiner/gsm.py +9 -2
- GameSentenceMiner/util/audio_offset_selector.py +11 -1
- GameSentenceMiner/util/gsm_utils.py +10 -6
- GameSentenceMiner/util/ss_selector.py +17 -4
- GameSentenceMiner/web/service.py +10 -7
- GameSentenceMiner/web/templates/index.html +10 -10
- GameSentenceMiner/web/texthooking_page.py +11 -0
- {gamesentenceminer-2.10.0.dist-info → gamesentenceminer-2.10.2.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.10.0.dist-info → gamesentenceminer-2.10.2.dist-info}/RECORD +14 -14
- {gamesentenceminer-2.10.0.dist-info → gamesentenceminer-2.10.2.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.10.0.dist-info → gamesentenceminer-2.10.2.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.10.0.dist-info → gamesentenceminer-2.10.2.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.10.0.dist-info → gamesentenceminer-2.10.2.dist-info}/top_level.txt +0 -0
GameSentenceMiner/anki.py
CHANGED
@@ -261,11 +261,13 @@ def get_cards_by_sentence(sentence):
|
|
261
261
|
logger.warning(f'Found more than 1, and not updating cards for query: \n{query}')
|
262
262
|
return {}
|
263
263
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
264
|
+
card_dict = invoke('notesInfo', notes=[card_ids[-1]])[0]
|
265
|
+
try:
|
266
|
+
return AnkiCard.from_dict(card_dict)
|
267
|
+
except Exception as e:
|
268
|
+
logger.error(f"Error fetching last card: {e}")
|
269
|
+
logger.info(card_dict)
|
270
|
+
raise e
|
269
271
|
|
270
272
|
last_connection_error = datetime.now()
|
271
273
|
|
GameSentenceMiner/gsm.py
CHANGED
@@ -80,6 +80,9 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
80
80
|
vad_trimmed_audio = ''
|
81
81
|
final_audio_output = ''
|
82
82
|
skip_delete = False
|
83
|
+
selected_lines = []
|
84
|
+
anki_card_creation_time = None
|
85
|
+
mined_line = None
|
83
86
|
gsm_state.previous_replay = video_path
|
84
87
|
if gsm_state.line_for_audio or gsm_state.line_for_screenshot:
|
85
88
|
handle_texthooker_button(video_path, get_audio_from_video=VideoToAudioHandler.get_audio)
|
@@ -87,6 +90,8 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
87
90
|
try:
|
88
91
|
if anki.card_queue and len(anki.card_queue) > 0:
|
89
92
|
last_note, anki_card_creation_time, selected_lines = anki.card_queue.pop(0)
|
93
|
+
elif get_config().features.backfill_audio:
|
94
|
+
last_note = anki.get_cards_by_sentence(gametext.current_line_after_regex)
|
90
95
|
else:
|
91
96
|
logger.info("Replay buffer initiated externally. Skipping processing.")
|
92
97
|
skip_delete = True
|
@@ -103,8 +108,9 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
103
108
|
logger.error(
|
104
109
|
f"Video was unusually small, potentially empty! Check OBS for Correct Scene Settings! Path: {video_path}")
|
105
110
|
return
|
111
|
+
|
112
|
+
# Just for safety
|
106
113
|
if not last_note:
|
107
|
-
logger.debug("Attempting to get last anki card")
|
108
114
|
if get_config().anki.update_anki:
|
109
115
|
last_note = anki.get_last_anki_card()
|
110
116
|
if get_config().features.backfill_audio:
|
@@ -165,7 +171,8 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
165
171
|
elif get_config().features.notify_on_update and vad_result.success:
|
166
172
|
notification.send_audio_generated_notification(vad_trimmed_audio)
|
167
173
|
except Exception as e:
|
168
|
-
|
174
|
+
if mined_line:
|
175
|
+
anki_results[mined_line.id] = AnkiUpdateResult.failure()
|
169
176
|
logger.error(f"Failed Processing and/or adding to Anki: Reason {e}")
|
170
177
|
logger.debug(f"Some error was hit catching to allow further work to be done: {e}", exc_info=True)
|
171
178
|
notification.send_error_no_anki_update()
|
@@ -1,4 +1,11 @@
|
|
1
|
+
|
1
2
|
import os
|
3
|
+
import sys
|
4
|
+
|
5
|
+
sys_stdout = sys.stdout
|
6
|
+
sys_stderr = sys.stderr
|
7
|
+
sys.stdout = open(os.devnull, 'w')
|
8
|
+
sys.stderr = open(os.devnull, 'w')
|
2
9
|
import tkinter as tk
|
3
10
|
from tkinter import filedialog, messagebox
|
4
11
|
import soundfile as sf
|
@@ -175,7 +182,10 @@ class AudioOffsetGUI:
|
|
175
182
|
|
176
183
|
beg_offset = self.beg_slider.get() - 5.0
|
177
184
|
end_offset = self.duration # End offset is always full duration
|
178
|
-
|
185
|
+
sys.stdout.close()
|
186
|
+
sys.stderr.close()
|
187
|
+
sys.stdout = sys_stdout
|
188
|
+
sys.stderr = sys_stderr
|
179
189
|
print(f"{beg_offset:.2f}")
|
180
190
|
exit(0)
|
181
191
|
|
@@ -223,10 +223,14 @@ def do_text_replacements(text, replacements_json):
|
|
223
223
|
|
224
224
|
def open_audio_in_external(fileabspath, shell=False):
|
225
225
|
logger.info(f"Opening audio in external program...")
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
226
|
+
try:
|
227
|
+
if shell:
|
228
|
+
subprocess.Popen(f' "{get_config().audio.external_tool}" "{fileabspath}" ', shell=True)
|
229
|
+
else:
|
230
|
+
subprocess.Popen([get_config().audio.external_tool, fileabspath])
|
231
|
+
except Exception as e:
|
232
|
+
logger.error(f"Failed to open audio in external program: {e}")
|
233
|
+
return False
|
230
234
|
|
231
235
|
def is_connected():
|
232
236
|
try:
|
@@ -271,7 +275,7 @@ def remove_github_replacements_from_local_ocr():
|
|
271
275
|
return
|
272
276
|
|
273
277
|
if not os.path.exists(OCR_REPLACEMENTS_FILE):
|
274
|
-
logger.
|
278
|
+
logger.debug(f"Local file {OCR_REPLACEMENTS_FILE} does not exist. No replacements to remove.")
|
275
279
|
return
|
276
280
|
|
277
281
|
try:
|
@@ -303,4 +307,4 @@ def remove_github_replacements_from_local_ocr():
|
|
303
307
|
logger.debug(f"An unexpected error occurred while processing {OCR_REPLACEMENTS_FILE}: {e}")
|
304
308
|
|
305
309
|
|
306
|
-
remove_github_replacements_from_local_ocr()
|
310
|
+
remove_github_replacements_from_local_ocr()
|
@@ -1,15 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
import subprocess
|
1
|
+
|
2
|
+
|
4
3
|
import os
|
5
4
|
import sys
|
5
|
+
import subprocess
|
6
6
|
|
7
|
+
# Suppress stdout and stderr during imports
|
8
|
+
sys_stdout = sys.stdout
|
9
|
+
sys_stderr = sys.stderr
|
10
|
+
sys.stdout = open(os.devnull, 'w')
|
11
|
+
sys.stderr = open(os.devnull, 'w')
|
12
|
+
|
13
|
+
import tkinter as tk
|
14
|
+
from PIL import Image, ImageTk
|
7
15
|
from GameSentenceMiner.util.gsm_utils import sanitize_filename
|
8
16
|
from GameSentenceMiner.util.configuration import get_temporary_directory, logger
|
9
17
|
from GameSentenceMiner.util.ffmpeg import ffmpeg_base_command_list
|
10
18
|
from GameSentenceMiner.util import ffmpeg
|
11
19
|
|
12
|
-
|
13
20
|
def extract_frames(video_path, timestamp, temp_dir, mode):
|
14
21
|
frame_paths = []
|
15
22
|
timestamp_number = float(timestamp)
|
@@ -98,6 +105,10 @@ def run_extraction_and_display(video_path, timestamp_str, mode):
|
|
98
105
|
if image_paths:
|
99
106
|
selected_image_path = display_images(image_paths, golden_frame)
|
100
107
|
if selected_image_path:
|
108
|
+
sys.stdout.close()
|
109
|
+
sys.stderr.close()
|
110
|
+
sys.stdout = sys_stdout
|
111
|
+
sys.stderr = sys_stderr
|
101
112
|
print(selected_image_path)
|
102
113
|
else:
|
103
114
|
logger.debug("No image was selected.")
|
@@ -105,6 +116,8 @@ def run_extraction_and_display(video_path, timestamp_str, mode):
|
|
105
116
|
logger.debug("Frame extraction failed.")
|
106
117
|
|
107
118
|
def main():
|
119
|
+
|
120
|
+
|
108
121
|
# if len(sys.argv) != 3:
|
109
122
|
# print("Usage: python script.py <video_path> <timestamp>")
|
110
123
|
# sys.exit(1)
|
GameSentenceMiner/web/service.py
CHANGED
@@ -23,8 +23,10 @@ def handle_texthooker_button(video_path='', get_audio_from_video=None):
|
|
23
23
|
elif get_config().advanced.video_player_path:
|
24
24
|
play_video_in_external(line, gsm_state.previous_audio)
|
25
25
|
else:
|
26
|
-
|
27
|
-
|
26
|
+
import sounddevice as sd
|
27
|
+
data, samplerate = gsm_state.previous_audio
|
28
|
+
sd.play(data, samplerate)
|
29
|
+
sd.wait()
|
28
30
|
return
|
29
31
|
gsm_state.previous_line_for_audio = line
|
30
32
|
if get_config().advanced.audio_player_path:
|
@@ -37,13 +39,14 @@ def handle_texthooker_button(video_path='', get_audio_from_video=None):
|
|
37
39
|
gsm_state.previous_audio = new_video_path
|
38
40
|
gsm_state.previous_replay = new_video_path
|
39
41
|
else:
|
40
|
-
import
|
42
|
+
import sounddevice as sd
|
43
|
+
import soundfile as sf
|
41
44
|
audio = get_audio_from_video(line, line.next.time if line.next else None, video_path,
|
42
45
|
temporary=True)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
gsm_state.previous_audio =
|
46
|
+
data, samplerate = sf.read(audio)
|
47
|
+
sd.play(data, samplerate)
|
48
|
+
sd.wait()
|
49
|
+
gsm_state.previous_audio = (data, samplerate)
|
47
50
|
return
|
48
51
|
if gsm_state.line_for_screenshot:
|
49
52
|
line: GameLine = gsm_state.line_for_screenshot
|