GameSentenceMiner 2.3.2__py3-none-any.whl → 2.3.4__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 +15 -15
- GameSentenceMiner/config_gui.py +7 -1
- GameSentenceMiner/configuration.py +2 -1
- GameSentenceMiner/gametext.py +10 -8
- GameSentenceMiner/gsm.py +35 -11
- GameSentenceMiner/obs.py +15 -4
- GameSentenceMiner/utility_gui.py +3 -3
- {GameSentenceMiner-2.3.2.dist-info → GameSentenceMiner-2.3.4.dist-info}/METADATA +1 -1
- {GameSentenceMiner-2.3.2.dist-info → GameSentenceMiner-2.3.4.dist-info}/RECORD +12 -12
- {GameSentenceMiner-2.3.2.dist-info → GameSentenceMiner-2.3.4.dist-info}/WHEEL +0 -0
- {GameSentenceMiner-2.3.2.dist-info → GameSentenceMiner-2.3.4.dist-info}/entry_points.txt +0 -0
- {GameSentenceMiner-2.3.2.dist-info → GameSentenceMiner-2.3.4.dist-info}/top_level.txt +0 -0
GameSentenceMiner/anki.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import base64
|
2
|
+
import queue
|
2
3
|
import subprocess
|
3
4
|
import threading
|
4
5
|
import time
|
@@ -20,6 +21,7 @@ screenshot_in_anki = None
|
|
20
21
|
# Global variables to track state
|
21
22
|
previous_note_ids = set()
|
22
23
|
first_run = True
|
24
|
+
card_queue = []
|
23
25
|
|
24
26
|
|
25
27
|
def update_anki_card(last_note, note=None, audio_path='', video_path='', tango='', reuse_audio=False,
|
@@ -40,6 +42,7 @@ def update_anki_card(last_note, note=None, audio_path='', video_path='', tango='
|
|
40
42
|
screenshot_in_anki = store_media_file(screenshot)
|
41
43
|
if get_config().paths.remove_screenshot:
|
42
44
|
os.remove(screenshot)
|
45
|
+
util.set_last_mined_line(get_sentence(last_note))
|
43
46
|
audio_html = f"[sound:{audio_in_anki}]"
|
44
47
|
image_html = f"<img src=\"{screenshot_in_anki}\">"
|
45
48
|
|
@@ -70,8 +73,6 @@ def update_anki_card(last_note, note=None, audio_path='', video_path='', tango='
|
|
70
73
|
if get_config().features.open_anki_edit:
|
71
74
|
notification.open_anki_card(last_note['noteId'])
|
72
75
|
|
73
|
-
util.set_last_mined_line(get_sentence(last_note))
|
74
|
-
|
75
76
|
if get_config().audio.external_tool:
|
76
77
|
open_audio_in_external(f"{get_config().audio.anki_media_collection}/{audio_in_anki}")
|
77
78
|
|
@@ -211,20 +212,18 @@ def update_new_card():
|
|
211
212
|
last_card = get_last_anki_card()
|
212
213
|
if not check_tags_for_should_update(last_card):
|
213
214
|
return
|
215
|
+
use_prev_audio = sentence_is_same_as_previous(last_card)
|
216
|
+
logger.info(f"last mined line: {util.get_last_mined_line()}, current sentence: {get_sentence(last_card)}")
|
217
|
+
logger.info(f"use previous audio: {use_prev_audio}")
|
218
|
+
if get_config().obs.get_game_from_scene:
|
219
|
+
obs.update_current_game()
|
220
|
+
if use_prev_audio:
|
221
|
+
update_anki_card(last_card, note=get_initial_card_info(last_card, []), reuse_audio=True)
|
222
|
+
else:
|
223
|
+
logger.info("New card(s) detected! Added to Processing Queue!")
|
224
|
+
card_queue.append(last_card)
|
225
|
+
obs.save_replay_buffer()
|
214
226
|
|
215
|
-
if util.lock.locked():
|
216
|
-
logger.info("Audio still being Trimmed, Card Queued!")
|
217
|
-
with util.lock:
|
218
|
-
use_prev_audio = sentence_is_same_as_previous(last_card)
|
219
|
-
logger.info(f"last mined line: {util.get_last_mined_line()}, current sentence: {get_sentence(last_card)}")
|
220
|
-
logger.info(f"use previous audio: {use_prev_audio}")
|
221
|
-
if get_config().obs.get_game_from_scene:
|
222
|
-
obs.update_current_game()
|
223
|
-
if use_prev_audio:
|
224
|
-
update_anki_card(last_card, note=get_initial_card_info(last_card, []), reuse_audio=True)
|
225
|
-
else:
|
226
|
-
logger.info("New card(s) detected!")
|
227
|
-
obs.save_replay_buffer()
|
228
227
|
|
229
228
|
def sentence_is_same_as_previous(last_card):
|
230
229
|
if not util.get_last_mined_line():
|
@@ -276,3 +275,4 @@ def start_monitoring_anki():
|
|
276
275
|
obs_thread = threading.Thread(target=monitor_anki)
|
277
276
|
obs_thread.daemon = True # Ensures the thread will exit when the main program exits
|
278
277
|
obs_thread.start()
|
278
|
+
|
GameSentenceMiner/config_gui.py
CHANGED
@@ -171,7 +171,8 @@ class ConfigApp:
|
|
171
171
|
extension=self.screenshot_extension.get(),
|
172
172
|
custom_ffmpeg_settings=self.screenshot_custom_ffmpeg_settings.get(),
|
173
173
|
screenshot_hotkey_updates_anki=self.screenshot_hotkey_update_anki.get(),
|
174
|
-
seconds_after_line = self.seconds_after_line.get()
|
174
|
+
seconds_after_line = self.seconds_after_line.get(),
|
175
|
+
use_beginning_of_line_as_screenshot=self.use_beginning_of_line_as_screenshot.get()
|
175
176
|
),
|
176
177
|
audio=Audio(
|
177
178
|
extension=self.audio_extension.get(),
|
@@ -700,6 +701,11 @@ class ConfigApp:
|
|
700
701
|
self.add_label_and_increment_row(screenshot_frame, "This is only used for mining from lines from history (not current line)", row=self.current_row,
|
701
702
|
column=2)
|
702
703
|
|
704
|
+
ttk.Label(screenshot_frame, text="Use Beginning of Line as Screenshot:").grid(row=self.current_row, column=0, sticky='W')
|
705
|
+
self.use_beginning_of_line_as_screenshot = tk.BooleanVar(value=self.settings.screenshot.use_beginning_of_line_as_screenshot)
|
706
|
+
ttk.Checkbutton(screenshot_frame, variable=self.use_beginning_of_line_as_screenshot).grid(row=self.current_row, column=1, sticky='W')
|
707
|
+
self.add_label_and_increment_row(screenshot_frame, "Enable to use the beginning of the line as the screenshot point. Adjust the above setting to fine-tine timing.", row=self.current_row, column=2)
|
708
|
+
|
703
709
|
@new_tab
|
704
710
|
def create_audio_tab(self):
|
705
711
|
audio_frame = ttk.Frame(self.notebook)
|
@@ -102,7 +102,8 @@ class Screenshot:
|
|
102
102
|
extension: str = "webp"
|
103
103
|
custom_ffmpeg_settings: str = ''
|
104
104
|
screenshot_hotkey_updates_anki: bool = False
|
105
|
-
seconds_after_line:
|
105
|
+
seconds_after_line: float = 1.0
|
106
|
+
use_beginning_of_line_as_screenshot: bool = True
|
106
107
|
|
107
108
|
|
108
109
|
@dataclass_json
|
GameSentenceMiner/gametext.py
CHANGED
@@ -9,6 +9,7 @@ from typing import Callable
|
|
9
9
|
import pyperclip
|
10
10
|
import websockets
|
11
11
|
|
12
|
+
import util
|
12
13
|
from GameSentenceMiner.configuration import *
|
13
14
|
from GameSentenceMiner.configuration import get_config, logger
|
14
15
|
from GameSentenceMiner.util import remove_html_tags
|
@@ -88,6 +89,7 @@ def reset_line_hotkey_pressed():
|
|
88
89
|
logger.info("LINE RESET HOTKEY PRESSED")
|
89
90
|
current_line_time = datetime.now()
|
90
91
|
line_history[current_line_after_regex] = current_line_time
|
92
|
+
util.set_last_mined_line("")
|
91
93
|
|
92
94
|
|
93
95
|
def run_websocket_listener():
|
@@ -139,8 +141,8 @@ def get_last_two_sentences(last_note):
|
|
139
141
|
if not last_note:
|
140
142
|
return lines[-1][0] if lines else '', lines[-2][0] if len(lines) > 1 else ''
|
141
143
|
|
142
|
-
|
143
|
-
|
144
|
+
current = ""
|
145
|
+
previous = ""
|
144
146
|
|
145
147
|
sentence = last_note['fields'][get_config().anki.sentence_field]['value']
|
146
148
|
if sentence:
|
@@ -149,20 +151,20 @@ def get_last_two_sentences(last_note):
|
|
149
151
|
similarity = similar(remove_html_tags(sentence), line)
|
150
152
|
logger.debug(f"Comparing: {remove_html_tags(sentence)} with {line} - Similarity: {similarity}")
|
151
153
|
if found:
|
152
|
-
|
154
|
+
previous = line
|
153
155
|
break
|
154
156
|
if similarity >= 0.60 or line in remove_html_tags(sentence): # 80% similarity threshold
|
155
157
|
found = True
|
156
|
-
|
158
|
+
current = line
|
157
159
|
|
158
|
-
logger.debug(f"Current Line: {
|
159
|
-
logger.debug(f"Previous Line: {
|
160
|
+
logger.debug(f"Current Line: {current}")
|
161
|
+
logger.debug(f"Previous Line: {previous}")
|
160
162
|
|
161
|
-
if not
|
163
|
+
if not current or not previous:
|
162
164
|
logger.debug("Couldn't find lines in history, using last two lines")
|
163
165
|
return lines[-1][0] if lines else '', lines[-2][0] if len(lines) > 1 else ''
|
164
166
|
|
165
|
-
return
|
167
|
+
return current, previous
|
166
168
|
|
167
169
|
|
168
170
|
def get_line_and_future_lines(last_note):
|
GameSentenceMiner/gsm.py
CHANGED
@@ -31,7 +31,7 @@ from GameSentenceMiner.util import *
|
|
31
31
|
if is_windows():
|
32
32
|
import win32api
|
33
33
|
|
34
|
-
obs_process
|
34
|
+
obs_process = None
|
35
35
|
procs_to_close = []
|
36
36
|
settings_window: config_gui.ConfigApp = None
|
37
37
|
utility_window: utility_gui.UtilityApp = None
|
@@ -54,6 +54,8 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
54
54
|
def convert_to_audio(video_path):
|
55
55
|
try:
|
56
56
|
with util.lock:
|
57
|
+
if anki.card_queue and len(anki.card_queue) > 0:
|
58
|
+
last_note = anki.card_queue.pop(0)
|
57
59
|
if os.path.exists(video_path) and os.access(video_path, os.R_OK):
|
58
60
|
logger.debug(f"Video found and is readable: {video_path}")
|
59
61
|
|
@@ -64,17 +66,17 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
64
66
|
logger.error(
|
65
67
|
f"Video was unusually small, potentially empty! Check OBS for Correct Scene Settings! Path: {video_path}")
|
66
68
|
return
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
if not last_note:
|
70
|
+
logger.debug("Attempting to get last anki card")
|
71
|
+
if get_config().anki.update_anki:
|
72
|
+
last_note = anki.get_last_anki_card()
|
73
|
+
if get_config().features.backfill_audio:
|
74
|
+
last_note = anki.get_cards_by_sentence(gametext.current_line)
|
73
75
|
line_time, next_line_time = get_line_timing(last_note)
|
74
76
|
if utility_window.lines_selected():
|
75
77
|
line_time, next_line_time = utility_window.get_selected_times()
|
76
78
|
ss_timing = 0
|
77
|
-
if line_time and next_line_time:
|
79
|
+
if line_time and next_line_time or line_time and get_config().screenshot.use_beginning_of_line_as_screenshot:
|
78
80
|
ss_timing = ffmpeg.get_screenshot_time(video_path, line_time)
|
79
81
|
if last_note:
|
80
82
|
logger.debug(json.dumps(last_note))
|
@@ -322,11 +324,33 @@ def run_tray():
|
|
322
324
|
icon = Icon("TrayApp", create_image(), "Game Sentence Miner", menu)
|
323
325
|
icon.run()
|
324
326
|
|
327
|
+
# def close_obs():
|
328
|
+
# if obs_process:
|
329
|
+
# logger.info("Closing OBS")
|
330
|
+
# proc = None
|
331
|
+
# if obs_process:
|
332
|
+
# try:
|
333
|
+
# logger.info("Closing OBS")
|
334
|
+
# proc = psutil.Process(obs_process)
|
335
|
+
# proc.send_signal(signal.CTRL_BREAK_EVENT)
|
336
|
+
# proc.wait(timeout=5)
|
337
|
+
# logger.info("Process closed gracefully.")
|
338
|
+
# except psutil.NoSuchProcess:
|
339
|
+
# logger.info("PID already closed.")
|
340
|
+
# except psutil.TimeoutExpired:
|
341
|
+
# logger.info("Process did not close gracefully, terminating.")
|
342
|
+
# proc.terminate()
|
343
|
+
# proc.wait()
|
344
|
+
|
325
345
|
def close_obs():
|
326
346
|
if obs_process:
|
327
|
-
|
328
|
-
|
329
|
-
|
347
|
+
try:
|
348
|
+
subprocess.run(["taskkill", "/PID", str(obs_process), "/F"], check=True, capture_output=True, text=True)
|
349
|
+
print(f"OBS (PID {obs_process}) has been terminated.")
|
350
|
+
except subprocess.CalledProcessError as e:
|
351
|
+
print(f"Error terminating OBS: {e.stderr}")
|
352
|
+
else:
|
353
|
+
print("OBS is not running.")
|
330
354
|
|
331
355
|
def restart_obs():
|
332
356
|
global obs_process
|
GameSentenceMiner/obs.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import subprocess
|
2
2
|
import time
|
3
3
|
|
4
|
+
import psutil
|
4
5
|
from obswebsocket import obsws, requests
|
5
6
|
|
6
7
|
from GameSentenceMiner import util, configuration
|
@@ -22,15 +23,25 @@ def start_obs():
|
|
22
23
|
return None
|
23
24
|
|
24
25
|
try:
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
obs_pid = is_obs_running(obs_path)
|
27
|
+
if obs_pid:
|
28
|
+
return obs_pid
|
29
|
+
process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable'], cwd=os.path.dirname(obs_path))
|
28
30
|
logger.info("OBS launched")
|
29
|
-
return process
|
31
|
+
return process.pid
|
30
32
|
except Exception as e:
|
31
33
|
logger.error(f"Error launching OBS: {e}")
|
32
34
|
return None
|
33
35
|
|
36
|
+
def is_obs_running(obs_path):
|
37
|
+
obs_path = os.path.abspath(obs_path) # Normalize path
|
38
|
+
for process in psutil.process_iter(['exe']):
|
39
|
+
try:
|
40
|
+
if process.info['exe'] and os.path.abspath(process.info['exe']) == obs_path:
|
41
|
+
return process.pid
|
42
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
43
|
+
continue
|
44
|
+
return False
|
34
45
|
|
35
46
|
def get_obs_websocket_config_values():
|
36
47
|
config_path = os.path.join(get_app_directory(), 'obs-studio', 'config', 'obs-studio', 'plugin_config', 'obs-websocket', 'config.json')
|
GameSentenceMiner/utility_gui.py
CHANGED
@@ -46,10 +46,10 @@ class UtilityApp:
|
|
46
46
|
var = tk.BooleanVar()
|
47
47
|
self.items.append((text, var, time))
|
48
48
|
|
49
|
-
# Remove the first checkbox if there are more than 10
|
50
49
|
if len(self.items) > 10:
|
51
|
-
self.checkboxes
|
52
|
-
|
50
|
+
if self.checkboxes:
|
51
|
+
self.checkboxes[0].destroy()
|
52
|
+
self.checkboxes.pop(0)
|
53
53
|
self.items.pop(0)
|
54
54
|
|
55
55
|
if self.multi_mine_window and tk.Toplevel.winfo_exists(self.multi_mine_window):
|
@@ -1,16 +1,16 @@
|
|
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=OHtzFSwfO3GNPFNfxOyPtOy9ATuM6IpeqPPIJX1x7j8,10116
|
3
|
+
GameSentenceMiner/config_gui.py,sha256=10VaSYc2OGdRT219HLrNkBpUONMUtQG5DKAQf1phano,54171
|
4
|
+
GameSentenceMiner/configuration.py,sha256=_nS-9sWNn97Zdv2V4Ypt_hf4PL6oLa5oK2Z7rWrqEmc,14405
|
5
5
|
GameSentenceMiner/ffmpeg.py,sha256=VExJYWSFhYuWukIXgOiHufsoSROEDA8LnVQFG8srRGc,10924
|
6
|
-
GameSentenceMiner/gametext.py,sha256=
|
7
|
-
GameSentenceMiner/gsm.py,sha256=
|
6
|
+
GameSentenceMiner/gametext.py,sha256=peP_1psoOF1SkcPqhYJkz8MLM-8XyEj8Yd3wU0sCwSs,6478
|
7
|
+
GameSentenceMiner/gsm.py,sha256=X93WBDGn6Tj1PeHjUm1QNrb6E91MdylbCYsrUe1posU,18259
|
8
8
|
GameSentenceMiner/model.py,sha256=oh8VVT8T1UKekbmP6MGNgQ8jIuQ_7Rg4GPzDCn2kJo8,1999
|
9
9
|
GameSentenceMiner/notification.py,sha256=WBaQWoPNhW4XqdPBUmxPBgjk0ngzH_4v9zMQ-XQAKC8,2010
|
10
|
-
GameSentenceMiner/obs.py,sha256=
|
10
|
+
GameSentenceMiner/obs.py,sha256=8ImXAVUWa4JdzwcBOEFShlZRZzh1dCvdpD1aEGhQfbU,6566
|
11
11
|
GameSentenceMiner/package_updater.py,sha256=0uaLAp0WrWqostNTBWRS0laITjI9aN9Yt_6GXosS4NQ,1278
|
12
12
|
GameSentenceMiner/util.py,sha256=MITweiFYaefWQF5nR8tZ9yE6vd_b-fLuP0MP1Y1U4K0,4720
|
13
|
-
GameSentenceMiner/utility_gui.py,sha256
|
13
|
+
GameSentenceMiner/utility_gui.py,sha256=Wa1YLitHzjbzkvvOtt3HbmFnplfajKR3N4aB2fTOlnM,4679
|
14
14
|
GameSentenceMiner/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5FngZ7ILdA_J5TFYAXUQ,15272
|
15
15
|
GameSentenceMiner/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
16
|
GameSentenceMiner/downloader/download_tools.py,sha256=M7vLo6_0QMuk1Ji4CsZqk1C2g7Bq6PyM29r7XNoP6Rw,6406
|
@@ -18,8 +18,8 @@ GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
18
18
|
GameSentenceMiner/vad/silero_trim.py,sha256=syDJX_KbFmdyFFtnQqYTD0tICsUCJizYhs-atPgXtxA,1549
|
19
19
|
GameSentenceMiner/vad/vosk_helper.py,sha256=-AAwK0cgOC5rK3_gL0sQgrPJ75E49g_PxZR4d5ckwc4,5826
|
20
20
|
GameSentenceMiner/vad/whisper_helper.py,sha256=bpR1HVnJRn9H5u8XaHBqBJ6JwIjzqn-Fajps8QmQ4zc,3411
|
21
|
-
GameSentenceMiner-2.3.
|
22
|
-
GameSentenceMiner-2.3.
|
23
|
-
GameSentenceMiner-2.3.
|
24
|
-
GameSentenceMiner-2.3.
|
25
|
-
GameSentenceMiner-2.3.
|
21
|
+
GameSentenceMiner-2.3.4.dist-info/METADATA,sha256=MU-wVhAYAf5j6zzfCJosoxrxv9QF-oKZv90eWoH0Frs,10120
|
22
|
+
GameSentenceMiner-2.3.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
23
|
+
GameSentenceMiner-2.3.4.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
|
24
|
+
GameSentenceMiner-2.3.4.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
|
25
|
+
GameSentenceMiner-2.3.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|