GameSentenceMiner 2.10.14__py3-none-any.whl → 2.10.16__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 +2 -2
- GameSentenceMiner/config_gui.py +267 -85
- GameSentenceMiner/gsm.py +1 -1
- GameSentenceMiner/util/configuration.py +6 -5
- GameSentenceMiner/util/downloader/download_tools.py +9 -2
- GameSentenceMiner/util/gsm_utils.py +15 -10
- GameSentenceMiner/util/text_log.py +9 -9
- {gamesentenceminer-2.10.14.dist-info → gamesentenceminer-2.10.16.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.10.14.dist-info → gamesentenceminer-2.10.16.dist-info}/RECORD +13 -13
- {gamesentenceminer-2.10.14.dist-info → gamesentenceminer-2.10.16.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.10.14.dist-info → gamesentenceminer-2.10.16.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.10.14.dist-info → gamesentenceminer-2.10.16.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.10.14.dist-info → gamesentenceminer-2.10.16.dist-info}/top_level.txt +0 -0
GameSentenceMiner/anki.py
CHANGED
@@ -16,7 +16,7 @@ from GameSentenceMiner.util import ffmpeg, notification
|
|
16
16
|
from GameSentenceMiner.util.configuration import *
|
17
17
|
from GameSentenceMiner.util.configuration import get_config
|
18
18
|
from GameSentenceMiner.util.model import AnkiCard
|
19
|
-
from GameSentenceMiner.util.text_log import get_all_lines, get_text_event, get_mined_line
|
19
|
+
from GameSentenceMiner.util.text_log import get_all_lines, get_text_event, get_mined_line, lines_match
|
20
20
|
from GameSentenceMiner.obs import get_current_game
|
21
21
|
from GameSentenceMiner.web import texthooking_page
|
22
22
|
|
@@ -162,7 +162,7 @@ def get_initial_card_info(last_note: AnkiCard, selected_lines):
|
|
162
162
|
sentence_in_anki = last_note.get_field(get_config().anki.sentence_field)
|
163
163
|
logger.info(f"Attempting Preserve HTML for multi-line")
|
164
164
|
for line in selected_lines:
|
165
|
-
if remove_html_and_cloze_tags(sentence_in_anki)
|
165
|
+
if lines_match(line.text, remove_html_and_cloze_tags(sentence_in_anki)):
|
166
166
|
sentences.append(sentence_in_anki)
|
167
167
|
logger.info("Found matching line in Anki, Preserving HTML!")
|
168
168
|
else:
|
GameSentenceMiner/config_gui.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import asyncio
|
2
2
|
import subprocess
|
3
|
+
import time
|
3
4
|
import tkinter as tk
|
4
5
|
from tkinter import filedialog, messagebox, simpledialog, scrolledtext, font
|
5
6
|
|
@@ -51,8 +52,10 @@ class HoverInfoWidget:
|
|
51
52
|
self.tooltip.destroy()
|
52
53
|
self.tooltip = None
|
53
54
|
|
55
|
+
|
54
56
|
class HoverInfoLabelWidget:
|
55
|
-
def __init__(self, parent, text, tooltip, row, column, padx=5, pady=2, foreground="white", sticky='W',
|
57
|
+
def __init__(self, parent, text, tooltip, row, column, padx=5, pady=2, foreground="white", sticky='W',
|
58
|
+
bootstyle=None, font=("Arial", 10, "normal")):
|
56
59
|
self.label = ttk.Label(parent, text=text, foreground=foreground, cursor="hand2", bootstyle=bootstyle, font=font)
|
57
60
|
self.label.grid(row=row, column=column, padx=(0, padx), pady=0, sticky=sticky)
|
58
61
|
self.label.bind("<Enter>", lambda e: self.show_info_box(tooltip))
|
@@ -76,6 +79,31 @@ class HoverInfoLabelWidget:
|
|
76
79
|
self.tooltip = None
|
77
80
|
|
78
81
|
|
82
|
+
class ResetToDefaultButton(ttk.Button):
|
83
|
+
def __init__(self, parent, command, text="Reset to Default", bootstyle="danger", **kwargs):
|
84
|
+
super().__init__(parent, text=text, command=command, bootstyle=bootstyle, **kwargs)
|
85
|
+
self.tooltip = None
|
86
|
+
self.bind("<Enter>", self.show_tooltip)
|
87
|
+
self.bind("<Leave>", self.hide_tooltip)
|
88
|
+
|
89
|
+
def show_tooltip(self, event):
|
90
|
+
if not self.tooltip:
|
91
|
+
x = self.winfo_rootx() + 20
|
92
|
+
y = self.winfo_rooty() + 20
|
93
|
+
self.tooltip = tk.Toplevel(self)
|
94
|
+
self.tooltip.wm_overrideredirect(True)
|
95
|
+
self.tooltip.wm_geometry(f"+{x}+{y}")
|
96
|
+
label = ttk.Label(self.tooltip, text="Reset Current Tab Settings to default values.", relief="solid",
|
97
|
+
borderwidth=1,
|
98
|
+
font=("tahoma", "12", "normal"))
|
99
|
+
label.pack(ipadx=1)
|
100
|
+
|
101
|
+
def hide_tooltip(self, event):
|
102
|
+
if self.tooltip:
|
103
|
+
self.tooltip.destroy()
|
104
|
+
self.tooltip = None
|
105
|
+
|
106
|
+
|
79
107
|
class ConfigApp:
|
80
108
|
def __init__(self, root):
|
81
109
|
self.window = root
|
@@ -92,21 +120,26 @@ class ConfigApp:
|
|
92
120
|
self.master_config: Config = configuration.load_config()
|
93
121
|
|
94
122
|
self.settings = self.master_config.get_config()
|
123
|
+
self.default_master_settings = Config.new()
|
124
|
+
self.default_settings = self.default_master_settings.get_config()
|
95
125
|
|
96
126
|
self.notebook = ttk.Notebook(self.window)
|
97
127
|
self.notebook.pack(pady=10, expand=True, fill='both')
|
98
128
|
|
99
|
-
self.
|
100
|
-
self.
|
101
|
-
self.
|
102
|
-
self.
|
103
|
-
self.
|
104
|
-
self.
|
105
|
-
self.
|
106
|
-
self.
|
107
|
-
self.
|
108
|
-
self.
|
109
|
-
self.
|
129
|
+
self.general_tab = None
|
130
|
+
self.paths_tab = None
|
131
|
+
self.anki_tab = None
|
132
|
+
self.vad_tab = None
|
133
|
+
self.features_tab = None
|
134
|
+
self.screenshot_tab = None
|
135
|
+
self.audio_tab = None
|
136
|
+
self.obs_tab = None
|
137
|
+
self.profiles_tab = None
|
138
|
+
self.ai_tab = None
|
139
|
+
self.advanced_tab = None
|
140
|
+
|
141
|
+
self.create_tabs()
|
142
|
+
|
110
143
|
# self.create_help_tab()
|
111
144
|
|
112
145
|
self.notebook.bind("<<NotebookTabChanged>>", self.on_profiles_tab_selected)
|
@@ -127,6 +160,44 @@ class ConfigApp:
|
|
127
160
|
|
128
161
|
self.window.withdraw()
|
129
162
|
|
163
|
+
def create_tabs(self):
|
164
|
+
self.create_general_tab()
|
165
|
+
self.create_paths_tab()
|
166
|
+
self.create_anki_tab()
|
167
|
+
self.create_vad_tab()
|
168
|
+
self.create_features_tab()
|
169
|
+
self.create_screenshot_tab()
|
170
|
+
self.create_audio_tab()
|
171
|
+
self.create_obs_tab()
|
172
|
+
self.create_profiles_tab()
|
173
|
+
self.create_ai_tab()
|
174
|
+
self.create_advanced_tab()
|
175
|
+
|
176
|
+
def add_reset_button(self, frame, category, row, column=0, recreate_tab=None):
|
177
|
+
"""
|
178
|
+
Adds a reset button to the given frame that resets the settings in the frame to default values.
|
179
|
+
"""
|
180
|
+
reset_button = ResetToDefaultButton(frame, command=lambda: self.reset_to_default(category, recreate_tab),
|
181
|
+
text="Reset to Default")
|
182
|
+
reset_button.grid(row=row, column=column, sticky='W', padx=5, pady=5)
|
183
|
+
return reset_button
|
184
|
+
|
185
|
+
# Category is the dataclass name of the settings being reset, default is a default instance of that dataclass
|
186
|
+
def reset_to_default(self, category, recreate_tab):
|
187
|
+
"""
|
188
|
+
Resets the settings in the current tab to default values.
|
189
|
+
"""
|
190
|
+
if not messagebox.askyesno("Reset to Default",
|
191
|
+
"Are you sure you want to reset all settings in this tab to default?"):
|
192
|
+
return
|
193
|
+
|
194
|
+
default_category_config = getattr(self.default_settings, category)
|
195
|
+
|
196
|
+
setattr(self.settings, category, default_category_config)
|
197
|
+
recreate_tab()
|
198
|
+
self.save_settings(profile_change=False)
|
199
|
+
self.reload_settings()
|
200
|
+
|
130
201
|
def show_scene_selection(self, matched_configs):
|
131
202
|
selected_scene = None
|
132
203
|
if matched_configs:
|
@@ -313,6 +384,14 @@ class ConfigApp:
|
|
313
384
|
self.master_config.current_profile = current_profile
|
314
385
|
self.master_config.set_config_for_profile(current_profile, config)
|
315
386
|
|
387
|
+
|
388
|
+
config_backup_folder = os.path.join(get_app_directory(), "backup", "config")
|
389
|
+
os.makedirs(config_backup_folder, exist_ok=True)
|
390
|
+
# write a timesstamped backup of the current config before saving
|
391
|
+
timestamp = time.strftime("%Y-%m-%d_%H-%M-%S")
|
392
|
+
with open(os.path.join(config_backup_folder, f"config_backup_{timestamp}.json"), 'w') as backup_file:
|
393
|
+
backup_file.write(self.master_config.to_json(indent=4))
|
394
|
+
|
316
395
|
self.master_config = self.master_config.sync_shared_fields()
|
317
396
|
|
318
397
|
if sync_changes:
|
@@ -347,17 +426,20 @@ class ConfigApp:
|
|
347
426
|
for frame in self.notebook.winfo_children():
|
348
427
|
frame.destroy()
|
349
428
|
|
350
|
-
|
351
|
-
self.
|
352
|
-
self.
|
353
|
-
self.
|
354
|
-
self.
|
355
|
-
self.
|
356
|
-
self.
|
357
|
-
self.
|
358
|
-
self.
|
359
|
-
self.
|
360
|
-
self.
|
429
|
+
# Reset tab frames so they are recreated
|
430
|
+
self.general_tab = None
|
431
|
+
self.paths_tab = None
|
432
|
+
self.anki_tab = None
|
433
|
+
self.vad_tab = None
|
434
|
+
self.features_tab = None
|
435
|
+
self.screenshot_tab = None
|
436
|
+
self.audio_tab = None
|
437
|
+
self.obs_tab = None
|
438
|
+
self.profiles_tab = None
|
439
|
+
self.ai_tab = None
|
440
|
+
self.advanced_tab = None
|
441
|
+
|
442
|
+
self.create_tabs()
|
361
443
|
|
362
444
|
def increment_row(self):
|
363
445
|
"""Increment the current row index and return the new value."""
|
@@ -373,111 +455,120 @@ class ConfigApp:
|
|
373
455
|
|
374
456
|
@new_tab
|
375
457
|
def create_general_tab(self):
|
376
|
-
|
377
|
-
|
458
|
+
if self.general_tab is None:
|
459
|
+
self.general_tab = ttk.Frame(self.notebook, padding=15)
|
460
|
+
self.notebook.add(self.general_tab, text='General')
|
461
|
+
else:
|
462
|
+
for widget in self.general_tab.winfo_children():
|
463
|
+
widget.destroy()
|
378
464
|
|
379
|
-
HoverInfoLabelWidget(
|
465
|
+
HoverInfoLabelWidget(self.general_tab, text="Websocket Enabled:",
|
380
466
|
foreground="dark orange", font=("Helvetica", 10, "bold"),
|
381
467
|
tooltip="Enable or disable WebSocket communication. Enabling this will disable the clipboard monitor.",
|
382
468
|
row=self.current_row, column=0)
|
383
469
|
self.websocket_enabled = tk.BooleanVar(value=self.settings.general.use_websocket)
|
384
|
-
ttk.Checkbutton(
|
470
|
+
ttk.Checkbutton(self.general_tab, variable=self.websocket_enabled, bootstyle="round-toggle").grid(
|
385
471
|
row=self.current_row, column=1,
|
386
472
|
sticky='W', pady=2)
|
387
473
|
self.current_row += 1
|
388
474
|
|
389
|
-
HoverInfoLabelWidget(
|
475
|
+
HoverInfoLabelWidget(self.general_tab, text="Clipboard Enabled:",
|
390
476
|
foreground="dark orange", font=("Helvetica", 10, "bold"),
|
391
477
|
tooltip="Enable or disable Clipboard monitoring.", row=self.current_row, column=0)
|
392
478
|
self.clipboard_enabled = tk.BooleanVar(value=self.settings.general.use_clipboard)
|
393
|
-
ttk.Checkbutton(
|
479
|
+
ttk.Checkbutton(self.general_tab, variable=self.clipboard_enabled, bootstyle="round-toggle").grid(
|
394
480
|
row=self.current_row, column=1,
|
395
481
|
sticky='W', pady=2)
|
396
482
|
self.current_row += 1
|
397
483
|
|
398
|
-
HoverInfoLabelWidget(
|
484
|
+
HoverInfoLabelWidget(self.general_tab, text="Allow Both Simultaneously:",
|
399
485
|
foreground="red", font=("Helvetica", 10, "bold"),
|
400
486
|
tooltip="Enable to allow GSM to accept both clipboard and websocket input at the same time.",
|
401
487
|
row=self.current_row, column=0)
|
402
488
|
self.use_both_clipboard_and_websocket = tk.BooleanVar(
|
403
489
|
value=self.settings.general.use_both_clipboard_and_websocket)
|
404
|
-
ttk.Checkbutton(
|
490
|
+
ttk.Checkbutton(self.general_tab, variable=self.use_both_clipboard_and_websocket,
|
491
|
+
bootstyle="round-toggle").grid(
|
405
492
|
row=self.current_row, column=1,
|
406
493
|
sticky='W', pady=2)
|
407
494
|
self.current_row += 1
|
408
495
|
|
409
|
-
HoverInfoLabelWidget(
|
496
|
+
HoverInfoLabelWidget(self.general_tab, text="Websocket URI(s):",
|
410
497
|
tooltip="WebSocket URI for connecting. Allows Comma Separated Values for Connecting Multiple.",
|
411
498
|
row=self.current_row, column=0)
|
412
|
-
self.websocket_uri = ttk.Entry(
|
499
|
+
self.websocket_uri = ttk.Entry(self.general_tab, width=50)
|
413
500
|
self.websocket_uri.insert(0, self.settings.general.websocket_uri)
|
414
501
|
self.websocket_uri.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
415
502
|
self.current_row += 1
|
416
503
|
|
417
|
-
HoverInfoLabelWidget(
|
504
|
+
HoverInfoLabelWidget(self.general_tab, text="TextHook Replacement Regex:",
|
418
505
|
tooltip="Regex to run replacement on texthook input, set this to the same as what you may have in your texthook page.",
|
419
506
|
row=self.current_row, column=0)
|
420
|
-
self.texthook_replacement_regex = ttk.Entry(
|
507
|
+
self.texthook_replacement_regex = ttk.Entry(self.general_tab)
|
421
508
|
self.texthook_replacement_regex.insert(0, self.settings.general.texthook_replacement_regex)
|
422
509
|
self.texthook_replacement_regex.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
423
510
|
self.current_row += 1
|
424
511
|
|
425
|
-
HoverInfoLabelWidget(
|
512
|
+
HoverInfoLabelWidget(self.general_tab, text="Open Config on Startup:",
|
426
513
|
tooltip="Whether to open config when the script starts.", row=self.current_row, column=0)
|
427
514
|
self.open_config_on_startup = tk.BooleanVar(value=self.settings.general.open_config_on_startup)
|
428
|
-
ttk.Checkbutton(
|
515
|
+
ttk.Checkbutton(self.general_tab, variable=self.open_config_on_startup, bootstyle="round-toggle").grid(
|
429
516
|
row=self.current_row, column=1,
|
430
517
|
sticky='W', pady=2)
|
431
518
|
self.current_row += 1
|
432
519
|
|
433
|
-
HoverInfoLabelWidget(
|
520
|
+
HoverInfoLabelWidget(self.general_tab, text="Open GSM Texthooker on Startup:",
|
434
521
|
tooltip="Whether to open Texthooking page when the script starts.", row=self.current_row,
|
435
522
|
column=0)
|
436
523
|
self.open_multimine_on_startup = tk.BooleanVar(value=self.settings.general.open_multimine_on_startup)
|
437
|
-
ttk.Checkbutton(
|
524
|
+
ttk.Checkbutton(self.general_tab, variable=self.open_multimine_on_startup, bootstyle="round-toggle").grid(
|
438
525
|
row=self.current_row, column=1,
|
439
526
|
sticky='W', pady=2)
|
440
527
|
self.current_row += 1
|
441
528
|
|
442
|
-
HoverInfoLabelWidget(
|
529
|
+
HoverInfoLabelWidget(self.general_tab, text="GSM Texthooker Port:",
|
443
530
|
tooltip="Port for the Texthooker to run on. Only change if you know what you are doing.",
|
444
531
|
row=self.current_row, column=0)
|
445
|
-
self.texthooker_port = ttk.Entry(
|
532
|
+
self.texthooker_port = ttk.Entry(self.general_tab)
|
446
533
|
self.texthooker_port.insert(0, str(self.settings.general.texthooker_port))
|
447
534
|
self.texthooker_port.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
448
535
|
self.current_row += 1
|
449
536
|
|
450
|
-
HoverInfoLabelWidget(
|
537
|
+
HoverInfoLabelWidget(self.general_tab, text="Current Version:", bootstyle="secondary",
|
451
538
|
tooltip="The current version of the application.", row=self.current_row, column=0)
|
452
|
-
self.current_version = ttk.Label(
|
539
|
+
self.current_version = ttk.Label(self.general_tab, text=get_current_version(), bootstyle="secondary")
|
453
540
|
self.current_version.grid(row=self.current_row, column=1, sticky='W', pady=2)
|
454
541
|
self.current_row += 1
|
455
542
|
|
456
|
-
HoverInfoLabelWidget(
|
543
|
+
HoverInfoLabelWidget(self.general_tab, text="Latest Version:", bootstyle="secondary",
|
457
544
|
tooltip="The latest available version of the application.", row=self.current_row, column=0)
|
458
|
-
self.latest_version = ttk.Label(
|
545
|
+
self.latest_version = ttk.Label(self.general_tab, text=get_latest_version(), bootstyle="secondary")
|
459
546
|
self.latest_version.grid(row=self.current_row, column=1, sticky='W', pady=2)
|
460
547
|
self.current_row += 1
|
461
548
|
|
462
|
-
ttk.Label(
|
549
|
+
ttk.Label(self.general_tab, text="Indicates important/required settings.", foreground="dark orange",
|
463
550
|
font=("Helvetica", 10, "bold")).grid(row=self.current_row, column=0, columnspan=2, sticky='W', pady=2)
|
464
551
|
self.current_row += 1
|
465
|
-
ttk.Label(
|
552
|
+
ttk.Label(self.general_tab, text="Highlights Advanced Features that may break things.", foreground="red",
|
466
553
|
font=("Helvetica", 10, "bold")).grid(row=self.current_row, column=0, columnspan=2, sticky='W', pady=2)
|
467
554
|
self.current_row += 1
|
468
|
-
ttk.Label(
|
555
|
+
ttk.Label(self.general_tab, text="Indicates Recommended, but completely optional settings.", foreground="green",
|
469
556
|
font=("Helvetica", 10, "bold")).grid(row=self.current_row, column=0, columnspan=2, sticky='W', pady=2)
|
470
557
|
self.current_row += 1
|
471
|
-
ttk.Label(
|
472
|
-
|
558
|
+
ttk.Label(self.general_tab,
|
559
|
+
text="Every Label in settings has a tooltip with more information if you hover over them.",
|
560
|
+
font=("Helvetica", 10, "bold")).grid(row=self.current_row, column=0, columnspan=2, sticky='W', pady=2)
|
473
561
|
self.current_row += 1
|
474
562
|
|
475
|
-
|
476
|
-
|
563
|
+
# Add Reset to Default button
|
564
|
+
self.add_reset_button(self.general_tab, "general", self.current_row, column=0, recreate_tab=self.create_general_tab)
|
565
|
+
|
566
|
+
self.general_tab.grid_columnconfigure(0, weight=0) # No expansion for the label column
|
567
|
+
self.general_tab.grid_columnconfigure(1, weight=0) # Entry column gets more space
|
477
568
|
for row in range(self.current_row):
|
478
|
-
|
569
|
+
self.general_tab.grid_rowconfigure(row, minsize=30)
|
479
570
|
|
480
|
-
return
|
571
|
+
return self.general_tab
|
481
572
|
|
482
573
|
@new_tab
|
483
574
|
def create_required_settings_tab(self):
|
@@ -487,8 +578,14 @@ class ConfigApp:
|
|
487
578
|
|
488
579
|
@new_tab
|
489
580
|
def create_vad_tab(self):
|
490
|
-
|
491
|
-
|
581
|
+
if self.vad_tab is None:
|
582
|
+
self.vad_tab = ttk.Frame(self.notebook, padding=15)
|
583
|
+
self.notebook.add(self.vad_tab, text='VAD')
|
584
|
+
else:
|
585
|
+
for widget in self.vad_tab.winfo_children():
|
586
|
+
widget.destroy()
|
587
|
+
|
588
|
+
vad_frame = self.vad_tab
|
492
589
|
|
493
590
|
HoverInfoLabelWidget(vad_frame, text="Voice Detection Postprocessing:",
|
494
591
|
tooltip="Enable post-processing of audio to trim just the voiceline.",
|
@@ -575,16 +672,26 @@ class ConfigApp:
|
|
575
672
|
self.splice_padding.grid(row=self.current_row, column=3, sticky='EW', pady=2)
|
576
673
|
self.current_row += 1
|
577
674
|
|
675
|
+
self.add_reset_button(vad_frame, "vad", self.current_row, 0, self.create_vad_tab)
|
676
|
+
|
578
677
|
for col in range(5):
|
579
678
|
vad_frame.grid_columnconfigure(col, weight=0)
|
580
679
|
|
581
680
|
for row in range(self.current_row):
|
582
681
|
vad_frame.grid_rowconfigure(row, minsize=30)
|
583
682
|
|
683
|
+
return vad_frame
|
684
|
+
|
584
685
|
@new_tab
|
585
686
|
def create_paths_tab(self):
|
586
|
-
|
587
|
-
|
687
|
+
if self.paths_tab is None:
|
688
|
+
self.paths_tab = ttk.Frame(self.notebook, padding=15)
|
689
|
+
self.notebook.add(self.paths_tab, text='Paths')
|
690
|
+
else:
|
691
|
+
for widget in self.paths_tab.winfo_children():
|
692
|
+
widget.destroy()
|
693
|
+
|
694
|
+
paths_frame = self.paths_tab
|
588
695
|
|
589
696
|
HoverInfoLabelWidget(paths_frame, text="Folder to Watch:", tooltip="Path where the OBS Replays will be saved.",
|
590
697
|
foreground="dark orange", font=("Helvetica", 10, "bold"), row=self.current_row, column=0)
|
@@ -642,6 +749,8 @@ class ConfigApp:
|
|
642
749
|
row=self.current_row, column=1, sticky='W', pady=2)
|
643
750
|
self.current_row += 1
|
644
751
|
|
752
|
+
self.add_reset_button(paths_frame, "paths", self.current_row, 0, self.create_paths_tab)
|
753
|
+
|
645
754
|
paths_frame.grid_columnconfigure(0, weight=0)
|
646
755
|
paths_frame.grid_columnconfigure(1, weight=0)
|
647
756
|
paths_frame.grid_columnconfigure(2, weight=0)
|
@@ -665,8 +774,14 @@ class ConfigApp:
|
|
665
774
|
|
666
775
|
@new_tab
|
667
776
|
def create_anki_tab(self):
|
668
|
-
|
669
|
-
|
777
|
+
if self.anki_tab is None:
|
778
|
+
self.anki_tab = ttk.Frame(self.notebook, padding=15)
|
779
|
+
self.notebook.add(self.anki_tab, text='Anki')
|
780
|
+
else:
|
781
|
+
for widget in self.anki_tab.winfo_children():
|
782
|
+
widget.destroy()
|
783
|
+
|
784
|
+
anki_frame = self.anki_tab
|
670
785
|
|
671
786
|
HoverInfoLabelWidget(anki_frame, text="Update Anki:", tooltip="Automatically update Anki with new data.",
|
672
787
|
row=self.current_row, column=0)
|
@@ -761,7 +876,6 @@ class ConfigApp:
|
|
761
876
|
self.parent_tag.insert(0, self.settings.anki.parent_tag)
|
762
877
|
self.parent_tag.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
763
878
|
|
764
|
-
|
765
879
|
self.current_row += 1
|
766
880
|
|
767
881
|
HoverInfoLabelWidget(anki_frame, text="Overwrite Audio:", tooltip="Overwrite existing audio in Anki cards.",
|
@@ -790,6 +904,8 @@ class ConfigApp:
|
|
790
904
|
row=self.current_row, column=1, sticky='W', pady=2)
|
791
905
|
self.current_row += 1
|
792
906
|
|
907
|
+
self.add_reset_button(anki_frame, "anki", self.current_row, 0, self.create_anki_tab)
|
908
|
+
|
793
909
|
anki_frame.grid_columnconfigure(0, weight=0)
|
794
910
|
anki_frame.grid_columnconfigure(1, weight=0)
|
795
911
|
|
@@ -807,8 +923,14 @@ class ConfigApp:
|
|
807
923
|
|
808
924
|
@new_tab
|
809
925
|
def create_features_tab(self):
|
810
|
-
|
811
|
-
|
926
|
+
if self.features_tab is None:
|
927
|
+
self.features_tab = ttk.Frame(self.notebook, padding=15)
|
928
|
+
self.notebook.add(self.features_tab, text='Features')
|
929
|
+
else:
|
930
|
+
for widget in self.features_tab.winfo_children():
|
931
|
+
widget.destroy()
|
932
|
+
|
933
|
+
features_frame = self.features_tab
|
812
934
|
|
813
935
|
HoverInfoLabelWidget(features_frame, text="Notify on Update:", tooltip="Notify the user when an update occurs.",
|
814
936
|
row=self.current_row, column=0)
|
@@ -856,16 +978,26 @@ class ConfigApp:
|
|
856
978
|
pady=2)
|
857
979
|
self.current_row += 1
|
858
980
|
|
981
|
+
self.add_reset_button(features_frame, "features", self.current_row, 0, self.create_features_tab)
|
982
|
+
|
859
983
|
for col in range(3):
|
860
984
|
features_frame.grid_columnconfigure(col, weight=0)
|
861
985
|
|
862
986
|
for row in range(self.current_row):
|
863
987
|
features_frame.grid_rowconfigure(row, minsize=30)
|
864
988
|
|
989
|
+
return features_frame
|
990
|
+
|
865
991
|
@new_tab
|
866
992
|
def create_screenshot_tab(self):
|
867
|
-
|
868
|
-
|
993
|
+
if self.screenshot_tab is None:
|
994
|
+
self.screenshot_tab = ttk.Frame(self.notebook, padding=15)
|
995
|
+
self.notebook.add(self.screenshot_tab, text='Screenshot')
|
996
|
+
else:
|
997
|
+
for widget in self.screenshot_tab.winfo_children():
|
998
|
+
widget.destroy()
|
999
|
+
|
1000
|
+
screenshot_frame = self.screenshot_tab
|
869
1001
|
|
870
1002
|
HoverInfoLabelWidget(screenshot_frame, text="Enabled:", tooltip="Enable or disable screenshot processing.",
|
871
1003
|
row=self.current_row, column=0)
|
@@ -951,12 +1083,16 @@ class ConfigApp:
|
|
951
1083
|
row=self.current_row, column=1, sticky='W', pady=2)
|
952
1084
|
self.current_row += 1
|
953
1085
|
|
1086
|
+
self.add_reset_button(screenshot_frame, "screenshot", self.current_row, 0, self.create_screenshot_tab)
|
1087
|
+
|
954
1088
|
for col in range(3):
|
955
1089
|
screenshot_frame.grid_columnconfigure(col, weight=0)
|
956
1090
|
|
957
1091
|
for row in range(self.current_row):
|
958
1092
|
screenshot_frame.grid_rowconfigure(row, minsize=30)
|
959
1093
|
|
1094
|
+
return screenshot_frame
|
1095
|
+
|
960
1096
|
def update_audio_ffmpeg_settings(self, event):
|
961
1097
|
selected_option = self.ffmpeg_audio_preset_options.get()
|
962
1098
|
if selected_option in self.ffmpeg_audio_preset_options_map:
|
@@ -968,8 +1104,14 @@ class ConfigApp:
|
|
968
1104
|
|
969
1105
|
@new_tab
|
970
1106
|
def create_audio_tab(self):
|
971
|
-
|
972
|
-
|
1107
|
+
if self.audio_tab is None:
|
1108
|
+
self.audio_tab = ttk.Frame(self.notebook, padding=15)
|
1109
|
+
self.notebook.add(self.audio_tab, text='Audio')
|
1110
|
+
else:
|
1111
|
+
for widget in self.audio_tab.winfo_children():
|
1112
|
+
widget.destroy()
|
1113
|
+
|
1114
|
+
audio_frame = self.audio_tab
|
973
1115
|
|
974
1116
|
HoverInfoLabelWidget(audio_frame, text="Enabled:", tooltip="Enable or disable audio processing.",
|
975
1117
|
row=self.current_row, column=0)
|
@@ -993,12 +1135,12 @@ class ConfigApp:
|
|
993
1135
|
self.beginning_offset.insert(0, str(self.settings.audio.beginning_offset))
|
994
1136
|
self.beginning_offset.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
995
1137
|
|
996
|
-
ttk.Button(audio_frame, text="Find Offset (WIP)", command=self.call_audio_offset_selector,
|
1138
|
+
ttk.Button(audio_frame, text="Find Offset (WIP)", command=self.call_audio_offset_selector,
|
1139
|
+
bootstyle="info").grid(
|
997
1140
|
row=self.current_row, column=2, sticky='EW', pady=2, padx=5)
|
998
1141
|
|
999
1142
|
self.current_row += 1
|
1000
1143
|
|
1001
|
-
|
1002
1144
|
HoverInfoLabelWidget(audio_frame, text="Audio Extraction End Offset:",
|
1003
1145
|
tooltip="Offset in seconds to trim from the end before VAD processing starts. Warning: May Result in lost audio if negative.",
|
1004
1146
|
foreground="red", font=("Helvetica", 10, "bold"), row=self.current_row, column=0)
|
@@ -1069,12 +1211,15 @@ class ConfigApp:
|
|
1069
1211
|
column=1, pady=5)
|
1070
1212
|
self.current_row += 1
|
1071
1213
|
|
1214
|
+
self.add_reset_button(audio_frame, "audio", self.current_row, 0, self.create_audio_tab)
|
1215
|
+
|
1072
1216
|
for col in range(5):
|
1073
1217
|
audio_frame.grid_columnconfigure(col, weight=0)
|
1074
1218
|
|
1075
1219
|
for row in range(self.current_row):
|
1076
1220
|
audio_frame.grid_rowconfigure(row, minsize=30)
|
1077
1221
|
|
1222
|
+
return audio_frame
|
1078
1223
|
|
1079
1224
|
def call_audio_offset_selector(self):
|
1080
1225
|
try:
|
@@ -1120,11 +1265,16 @@ class ConfigApp:
|
|
1120
1265
|
logger.error(f"An unexpected error occurred: {e}")
|
1121
1266
|
return None
|
1122
1267
|
|
1123
|
-
|
1124
1268
|
@new_tab
|
1125
1269
|
def create_obs_tab(self):
|
1126
|
-
|
1127
|
-
|
1270
|
+
if self.obs_tab is None:
|
1271
|
+
self.obs_tab = ttk.Frame(self.notebook, padding=15)
|
1272
|
+
self.notebook.add(self.obs_tab, text='OBS')
|
1273
|
+
else:
|
1274
|
+
for widget in self.obs_tab.winfo_children():
|
1275
|
+
widget.destroy()
|
1276
|
+
|
1277
|
+
obs_frame = self.obs_tab
|
1128
1278
|
|
1129
1279
|
HoverInfoLabelWidget(obs_frame, text="Enabled:", tooltip="Enable or disable OBS integration.",
|
1130
1280
|
row=self.current_row, column=0)
|
@@ -1185,16 +1335,26 @@ class ConfigApp:
|
|
1185
1335
|
self.minimum_replay_size.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
1186
1336
|
self.current_row += 1
|
1187
1337
|
|
1338
|
+
self.add_reset_button(obs_frame, "obs", self.current_row, 0, self.create_obs_tab)
|
1339
|
+
|
1188
1340
|
for col in range(3):
|
1189
1341
|
obs_frame.grid_columnconfigure(col, weight=0)
|
1190
1342
|
|
1191
1343
|
for row in range(self.current_row):
|
1192
1344
|
obs_frame.grid_rowconfigure(row, minsize=30)
|
1193
1345
|
|
1346
|
+
return obs_frame
|
1347
|
+
|
1194
1348
|
@new_tab
|
1195
1349
|
def create_profiles_tab(self):
|
1196
|
-
|
1197
|
-
|
1350
|
+
if self.profiles_tab is None:
|
1351
|
+
self.profiles_tab = ttk.Frame(self.notebook, padding=15)
|
1352
|
+
self.notebook.add(self.profiles_tab, text='Profiles')
|
1353
|
+
else:
|
1354
|
+
for widget in self.profiles_tab.winfo_children():
|
1355
|
+
widget.destroy()
|
1356
|
+
|
1357
|
+
profiles_frame = self.profiles_tab
|
1198
1358
|
|
1199
1359
|
HoverInfoLabelWidget(profiles_frame, text="Select Profile:", tooltip="Select a profile to load its settings.",
|
1200
1360
|
row=self.current_row, column=0)
|
@@ -1244,9 +1404,11 @@ class ConfigApp:
|
|
1244
1404
|
for row in range(self.current_row):
|
1245
1405
|
profiles_frame.grid_rowconfigure(row, minsize=30)
|
1246
1406
|
|
1407
|
+
return profiles_frame
|
1408
|
+
|
1247
1409
|
def on_obs_scene_select(self, event):
|
1248
1410
|
self.settings.scenes = [self.obs_scene_listbox.get(i) for i in
|
1249
|
-
|
1411
|
+
self.obs_scene_listbox.curselection()]
|
1250
1412
|
self.obs_scene_listbox_changed = True
|
1251
1413
|
|
1252
1414
|
def refresh_obs_scenes(self):
|
@@ -1263,8 +1425,14 @@ class ConfigApp:
|
|
1263
1425
|
|
1264
1426
|
@new_tab
|
1265
1427
|
def create_advanced_tab(self):
|
1266
|
-
|
1267
|
-
|
1428
|
+
if self.advanced_tab is None:
|
1429
|
+
self.advanced_tab = ttk.Frame(self.notebook, padding=15)
|
1430
|
+
self.notebook.add(self.advanced_tab, text='Advanced')
|
1431
|
+
else:
|
1432
|
+
for widget in self.advanced_tab.winfo_children():
|
1433
|
+
widget.destroy()
|
1434
|
+
|
1435
|
+
advanced_frame = self.advanced_tab
|
1268
1436
|
|
1269
1437
|
ttk.Label(advanced_frame, text="Note: Only one of these will take effect, prioritizing audio.",
|
1270
1438
|
foreground="red", font=("Helvetica", 10, "bold")).grid(row=self.current_row, column=0, columnspan=3,
|
@@ -1339,7 +1507,6 @@ class ConfigApp:
|
|
1339
1507
|
self.plaintext_websocket_export_port.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
1340
1508
|
self.current_row += 1
|
1341
1509
|
|
1342
|
-
|
1343
1510
|
# HoverInfoLabelWidget(advanced_frame, text="Use Anki Creation Date for Audio Timing:",
|
1344
1511
|
# tooltip="Use the Anki note creation date for audio timing instead of the OBS replay time.",
|
1345
1512
|
# row=self.current_row, column=0)
|
@@ -1366,20 +1533,31 @@ class ConfigApp:
|
|
1366
1533
|
HoverInfoLabelWidget(advanced_frame, text="Vosk URL:", tooltip="URL for connecting to the Vosk server.",
|
1367
1534
|
row=self.current_row, column=0)
|
1368
1535
|
self.vosk_url = ttk.Combobox(advanced_frame, values=[VOSK_BASE, VOSK_SMALL], state="readonly")
|
1369
|
-
self.vosk_url.set(
|
1536
|
+
self.vosk_url.set(
|
1537
|
+
VOSK_BASE if self.settings.vad.vosk_url == 'https://alphacephei.com/vosk/models/vosk-model-ja-0.22.zip' else VOSK_SMALL)
|
1370
1538
|
self.vosk_url.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
1371
1539
|
self.current_row += 1
|
1372
1540
|
|
1541
|
+
self.add_reset_button(advanced_frame, "advanced", self.current_row, 0, self.create_advanced_tab)
|
1542
|
+
|
1373
1543
|
for col in range(4):
|
1374
1544
|
advanced_frame.grid_columnconfigure(col, weight=0)
|
1375
1545
|
|
1376
1546
|
for row in range(self.current_row):
|
1377
1547
|
advanced_frame.grid_rowconfigure(row, minsize=30)
|
1378
1548
|
|
1549
|
+
return advanced_frame
|
1550
|
+
|
1379
1551
|
@new_tab
|
1380
1552
|
def create_ai_tab(self):
|
1381
|
-
|
1382
|
-
|
1553
|
+
if self.ai_tab is None:
|
1554
|
+
self.ai_tab = ttk.Frame(self.notebook, padding=15)
|
1555
|
+
self.notebook.add(self.ai_tab, text='AI')
|
1556
|
+
else:
|
1557
|
+
for widget in self.ai_tab.winfo_children():
|
1558
|
+
widget.destroy()
|
1559
|
+
|
1560
|
+
ai_frame = self.ai_tab
|
1383
1561
|
|
1384
1562
|
HoverInfoLabelWidget(ai_frame, text="Enabled:", tooltip="Enable or disable AI integration.",
|
1385
1563
|
row=self.current_row, column=0)
|
@@ -1397,10 +1575,12 @@ class ConfigApp:
|
|
1397
1575
|
|
1398
1576
|
HoverInfoLabelWidget(ai_frame, text="Gemini AI Model:", tooltip="Select the AI model to use.",
|
1399
1577
|
row=self.current_row, column=0)
|
1400
|
-
self.gemini_model = ttk.Combobox(ai_frame, values=['gemini-2.0-flash', 'gemini-2.0-flash-lite',
|
1401
|
-
'gemini-2.5-
|
1402
|
-
|
1403
|
-
|
1578
|
+
self.gemini_model = ttk.Combobox(ai_frame, values=['gemini-2.5-flash', 'gemini-2.5-pro','gemini-2.0-flash', 'gemini-2.0-flash-lite',
|
1579
|
+
'gemini-2.5-flash-lite-preview-06-17'], state="readonly")
|
1580
|
+
try:
|
1581
|
+
self.gemini_model.set(self.settings.ai.gemini_model)
|
1582
|
+
except Exception:
|
1583
|
+
self.gemini_model.set('gemini-2.5-flash')
|
1404
1584
|
self.gemini_model.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
1405
1585
|
self.current_row += 1
|
1406
1586
|
|
@@ -1460,6 +1640,8 @@ class ConfigApp:
|
|
1460
1640
|
self.custom_prompt.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
1461
1641
|
self.current_row += 1
|
1462
1642
|
|
1643
|
+
self.add_reset_button(ai_frame, "ai", self.current_row, 0, self.create_ai_tab)
|
1644
|
+
|
1463
1645
|
for col in range(3):
|
1464
1646
|
ai_frame.grid_columnconfigure(col, weight=0)
|
1465
1647
|
|
GameSentenceMiner/gsm.py
CHANGED
@@ -173,7 +173,7 @@ class VideoToAudioHandler(FileSystemEventHandler):
|
|
173
173
|
except Exception as e:
|
174
174
|
if mined_line:
|
175
175
|
anki_results[mined_line.id] = AnkiUpdateResult.failure()
|
176
|
-
logger.error(f"Failed Processing and/or adding to Anki: Reason {e}")
|
176
|
+
logger.error(f"Failed Processing and/or adding to Anki: Reason {e}", exc_info=True)
|
177
177
|
logger.debug(f"Some error was hit catching to allow further work to be done: {e}", exc_info=True)
|
178
178
|
notification.send_error_no_anki_update()
|
179
179
|
finally:
|
@@ -184,9 +184,10 @@ class Audio:
|
|
184
184
|
|
185
185
|
def __post_init__(self):
|
186
186
|
self.ffmpeg_reencode_options_to_use = self.ffmpeg_reencode_options.replace("{format}", self.extension).replace("{encoder}", supported_formats.get(self.extension, ''))
|
187
|
-
|
188
|
-
|
189
|
-
|
187
|
+
if self.anki_media_collection:
|
188
|
+
self.anki_media_collection = os.path.normpath(self.anki_media_collection)
|
189
|
+
if self.external_tool:
|
190
|
+
self.external_tool = os.path.normpath(self.external_tool)
|
190
191
|
|
191
192
|
|
192
193
|
|
@@ -220,7 +221,7 @@ class VAD:
|
|
220
221
|
language: str = 'ja'
|
221
222
|
vosk_url: str = VOSK_BASE
|
222
223
|
selected_vad_model: str = WHISPER
|
223
|
-
backup_vad_model: str =
|
224
|
+
backup_vad_model: str = SILERO
|
224
225
|
trim_beginning: bool = False
|
225
226
|
beginning_offset: float = -0.25
|
226
227
|
add_audio_on_no_results: bool = False
|
@@ -264,7 +265,7 @@ class Ai:
|
|
264
265
|
enabled: bool = False
|
265
266
|
anki_field: str = ''
|
266
267
|
provider: str = AI_GEMINI
|
267
|
-
gemini_model: str = 'gemini-2.
|
268
|
+
gemini_model: str = 'gemini-2.5-flash'
|
268
269
|
groq_model: str = 'meta-llama/llama-4-scout-17b-16e-instruct'
|
269
270
|
api_key: str = '' # Deprecated
|
270
271
|
gemini_api_key: str = ''
|
@@ -40,12 +40,19 @@ def download_obs_if_needed():
|
|
40
40
|
logger.info("OBS directory exists but executable is missing. Re-downloading OBS...")
|
41
41
|
shutil.rmtree(obs_path)
|
42
42
|
|
43
|
+
def get_windows_obs_url():
|
44
|
+
machine = platform.machine().lower()
|
45
|
+
if machine in ['arm64', 'aarch64']:
|
46
|
+
return next(asset['browser_download_url'] for asset in latest_release['assets'] if
|
47
|
+
asset['name'].endswith('Windows-arm64.zip'))
|
48
|
+
return next(asset['browser_download_url'] for asset in latest_release['assets'] if
|
49
|
+
asset['name'].endswith('Windows-x64.zip'))
|
50
|
+
|
43
51
|
latest_release_url = "https://api.github.com/repos/obsproject/obs-studio/releases/latest"
|
44
52
|
with urllib.request.urlopen(latest_release_url) as response:
|
45
53
|
latest_release = json.load(response)
|
46
54
|
obs_url = {
|
47
|
-
"Windows":
|
48
|
-
asset['name'].endswith('Windows.zip')),
|
55
|
+
"Windows": get_windows_obs_url(),
|
49
56
|
"Linux": next(asset['browser_download_url'] for asset in latest_release['assets'] if
|
50
57
|
asset['name'].endswith('Ubuntu-24.04-x86_64.deb')),
|
51
58
|
"Darwin": next(asset['browser_download_url'] for asset in latest_release['assets'] if
|
@@ -157,27 +157,32 @@ def combine_dialogue(dialogue_lines, new_lines=None):
|
|
157
157
|
|
158
158
|
return new_lines
|
159
159
|
|
160
|
-
def wait_for_stable_file(file_path, timeout=10, check_interval=0.
|
160
|
+
def wait_for_stable_file(file_path, timeout=10, check_interval=0.5):
|
161
161
|
elapsed_time = 0
|
162
162
|
last_size = -1
|
163
163
|
|
164
|
+
logger.info(f"Waiting for file '{file_path}' to stabilize or become accessible...")
|
165
|
+
|
164
166
|
while elapsed_time < timeout:
|
165
167
|
try:
|
166
168
|
current_size = os.path.getsize(file_path)
|
167
169
|
if current_size == last_size:
|
168
170
|
try:
|
169
|
-
with open(file_path, 'rb')
|
171
|
+
with open(file_path, 'rb'):
|
170
172
|
return True
|
171
|
-
except
|
172
|
-
|
173
|
-
elapsed_time += check_interval
|
173
|
+
except IOError:
|
174
|
+
pass
|
174
175
|
last_size = current_size
|
175
|
-
|
176
|
-
|
176
|
+
except FileNotFoundError:
|
177
|
+
last_size = -1
|
177
178
|
except Exception as e:
|
178
|
-
logger.warning(f"Error checking file
|
179
|
-
|
180
|
-
|
179
|
+
logger.warning(f"Error checking file {file_path}, will retry: {e}")
|
180
|
+
last_size = -1
|
181
|
+
|
182
|
+
time.sleep(check_interval)
|
183
|
+
elapsed_time += check_interval
|
184
|
+
|
185
|
+
logger.warning(f"File '{file_path}' did not stabilize or become accessible within {timeout} seconds. Continuing...")
|
181
186
|
return False
|
182
187
|
|
183
188
|
def isascii(s: str):
|
@@ -4,7 +4,6 @@ from datetime import datetime
|
|
4
4
|
from difflib import SequenceMatcher
|
5
5
|
from typing import Optional
|
6
6
|
|
7
|
-
from GameSentenceMiner.obs import get_current_game
|
8
7
|
from GameSentenceMiner.util.gsm_utils import remove_html_and_cloze_tags
|
9
8
|
from GameSentenceMiner.util.configuration import logger, get_config, gsm_state
|
10
9
|
from GameSentenceMiner.util.model import AnkiCard
|
@@ -106,14 +105,15 @@ def similar(a, b):
|
|
106
105
|
return SequenceMatcher(None, a, b).ratio()
|
107
106
|
|
108
107
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
108
|
+
def lines_match(texthooker_sentence, anki_sentence):
|
109
|
+
texthooker_sentence = texthooker_sentence.replace("\n", "").replace("\r", "").strip()
|
110
|
+
anki_sentence = anki_sentence.replace("\n", "").replace("\r", "").strip()
|
111
|
+
similarity = similar(texthooker_sentence, anki_sentence)
|
112
|
+
if texthooker_sentence in anki_sentence:
|
113
|
+
logger.debug(f"One contains the other: {texthooker_sentence} in {anki_sentence} - Similarity: {similarity}")
|
114
|
+
elif anki_sentence in texthooker_sentence:
|
115
|
+
logger.debug(f"One contains the other: {anki_sentence} in {texthooker_sentence} - Similarity: {similarity}")
|
116
|
+
return (anki_sentence in texthooker_sentence) or (texthooker_sentence in anki_sentence and similarity > 0.8)
|
117
117
|
|
118
118
|
|
119
119
|
def get_text_event(last_note) -> GameLine:
|
@@ -1,8 +1,8 @@
|
|
1
1
|
GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
GameSentenceMiner/anki.py,sha256=
|
3
|
-
GameSentenceMiner/config_gui.py,sha256=
|
2
|
+
GameSentenceMiner/anki.py,sha256=qi7yr9MI-sdgHPh9ZOnbCviwO2yl4gPB_z7hqxecSzI,16656
|
3
|
+
GameSentenceMiner/config_gui.py,sha256=RfxLGrVsDrZiZDseCX8OtK8s66zS4MJjxxwIZnq9zx0,97838
|
4
4
|
GameSentenceMiner/gametext.py,sha256=6VkjmBeiuZfPk8T6PHFdIAElBH2Y_oLVYvmcafqN7RM,6747
|
5
|
-
GameSentenceMiner/gsm.py,sha256=
|
5
|
+
GameSentenceMiner/gsm.py,sha256=SGB60IC5VPCnvr_cMh4krkR4khW1x7xZhEYVNl8TiHc,24879
|
6
6
|
GameSentenceMiner/obs.py,sha256=o_I6213VZvXqYkZDdUBgUg2KWi9SbnNZZjjUnKnQkK4,15190
|
7
7
|
GameSentenceMiner/vad.py,sha256=G0NkaWFJaIfKQAV7LOFxyKoih7pPNYHDuy4SzeFVCkI,16389
|
8
8
|
GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -30,21 +30,21 @@ GameSentenceMiner/owocr/owocr/run.py,sha256=goOZSO3a7z8GxjYAcWjHsPxdzM60Nt3vxjcU
|
|
30
30
|
GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=Na6XStbQBtpQUSdbN3QhEswtKuU1JjReFk_K8t5ezQE,3395
|
31
31
|
GameSentenceMiner/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
32
|
GameSentenceMiner/util/audio_offset_selector.py,sha256=8Stk3BP-XVIuzRv9nl9Eqd2D-1yD3JrgU-CamBywJmY,8542
|
33
|
-
GameSentenceMiner/util/configuration.py,sha256=
|
33
|
+
GameSentenceMiner/util/configuration.py,sha256=ys7DsbVjgVxJ2c3HqiBF3_nZOTX1QYu3k2Rv99XuZig,28897
|
34
34
|
GameSentenceMiner/util/electron_config.py,sha256=3VmIrcXhC-wIMMc4uqV85NrNenRl4ZUbnQfSjWEwuig,9852
|
35
35
|
GameSentenceMiner/util/ffmpeg.py,sha256=t0tflxq170n8PZKkdw8fTZIUQfXD0p_qARa9JTdhBTc,21530
|
36
|
-
GameSentenceMiner/util/gsm_utils.py,sha256=
|
36
|
+
GameSentenceMiner/util/gsm_utils.py,sha256=iRyLVcodMptRhkCzLf3hyqc6_RCktXnwApi6mLju6oQ,11565
|
37
37
|
GameSentenceMiner/util/model.py,sha256=AaOzgqSbaN7yks_rr1dQpLQR45FpBYdoLebMbrIYm34,6638
|
38
38
|
GameSentenceMiner/util/notification.py,sha256=0OnEYjn3DUEZ6c6OtPjdVZe-DG-QSoMAl9fetjjCvNU,3874
|
39
39
|
GameSentenceMiner/util/package.py,sha256=u1ym5z869lw5EHvIviC9h9uH97bzUXSXXA8KIn8rUvk,1157
|
40
40
|
GameSentenceMiner/util/ss_selector.py,sha256=cbjMxiKOCuOfbRvLR_PCRlykBrGtm1LXd6u5czPqkmc,4793
|
41
|
-
GameSentenceMiner/util/text_log.py,sha256=
|
41
|
+
GameSentenceMiner/util/text_log.py,sha256=eVtSXUaiQvPkQ2Zjzk8yITUk8g92k3M72-ZkJxessmk,5837
|
42
42
|
GameSentenceMiner/util/communication/__init__.py,sha256=xh__yn2MhzXi9eLi89PeZWlJPn-cbBSjskhi1BRraXg,643
|
43
43
|
GameSentenceMiner/util/communication/send.py,sha256=Wki9qIY2CgYnuHbmnyKVIYkcKAN_oYS4up93XMikBaI,222
|
44
44
|
GameSentenceMiner/util/communication/websocket.py,sha256=TbphRGmxVrgEupS7tNdifsmQfWDfIp0Hio2cSiUKgsk,3317
|
45
45
|
GameSentenceMiner/util/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5FngZ7ILdA_J5TFYAXUQ,15272
|
46
46
|
GameSentenceMiner/util/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
|
-
GameSentenceMiner/util/downloader/download_tools.py,sha256=
|
47
|
+
GameSentenceMiner/util/downloader/download_tools.py,sha256=zR-aEHiFVkyo-9oPoSx6nQ2K-_J8WBHLZyLoOhypsW4,8458
|
48
48
|
GameSentenceMiner/util/downloader/oneocr_dl.py,sha256=EJbKISaZ9p2x9P4x0rpMM5nAInTTc9b7arraGBcd-SA,10381
|
49
49
|
GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
50
50
|
GameSentenceMiner/web/service.py,sha256=S7bYf2kSk08u-8R9Qpv7piM-pxfFjYZUvU825xupmuI,5279
|
@@ -62,9 +62,9 @@ GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
|
|
62
62
|
GameSentenceMiner/web/templates/index.html,sha256=n0J-dV8eksj8JXUuaCTIh0fIxIjfgm2EvxGBdQ6gWoM,214113
|
63
63
|
GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
|
64
64
|
GameSentenceMiner/web/templates/utility.html,sha256=3flZinKNqUJ7pvrZk6xu__v67z44rXnaK7UTZ303R-8,16946
|
65
|
-
gamesentenceminer-2.10.
|
66
|
-
gamesentenceminer-2.10.
|
67
|
-
gamesentenceminer-2.10.
|
68
|
-
gamesentenceminer-2.10.
|
69
|
-
gamesentenceminer-2.10.
|
70
|
-
gamesentenceminer-2.10.
|
65
|
+
gamesentenceminer-2.10.16.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
66
|
+
gamesentenceminer-2.10.16.dist-info/METADATA,sha256=ZO5dqi5iFcZ0AgknR8Pg2csSPhA8_8bnM7uHLRYpBWM,7355
|
67
|
+
gamesentenceminer-2.10.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
68
|
+
gamesentenceminer-2.10.16.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
|
69
|
+
gamesentenceminer-2.10.16.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
|
70
|
+
gamesentenceminer-2.10.16.dist-info/RECORD,,
|
File without changes
|
{gamesentenceminer-2.10.14.dist-info → gamesentenceminer-2.10.16.dist-info}/entry_points.txt
RENAMED
File without changes
|
{gamesentenceminer-2.10.14.dist-info → gamesentenceminer-2.10.16.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
File without changes
|