GameSentenceMiner 2.0.0__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/__init__.py +0 -0
- GameSentenceMiner/anki.py +265 -0
- GameSentenceMiner/config_gui.py +803 -0
- GameSentenceMiner/configuration.py +359 -0
- GameSentenceMiner/ffmpeg.py +297 -0
- GameSentenceMiner/gametext.py +128 -0
- GameSentenceMiner/gsm.py +385 -0
- GameSentenceMiner/model.py +84 -0
- GameSentenceMiner/notification.py +69 -0
- GameSentenceMiner/obs.py +128 -0
- GameSentenceMiner/util.py +136 -0
- GameSentenceMiner/vad/__init__.py +0 -0
- GameSentenceMiner/vad/silero_trim.py +43 -0
- GameSentenceMiner/vad/vosk_helper.py +152 -0
- GameSentenceMiner/vad/whisper_helper.py +98 -0
- GameSentenceMiner-2.0.0.dist-info/METADATA +346 -0
- GameSentenceMiner-2.0.0.dist-info/RECORD +20 -0
- GameSentenceMiner-2.0.0.dist-info/WHEEL +5 -0
- GameSentenceMiner-2.0.0.dist-info/entry_points.txt +2 -0
- GameSentenceMiner-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,803 @@
|
|
1
|
+
import tkinter as tk
|
2
|
+
from tkinter import filedialog, messagebox, simpledialog
|
3
|
+
|
4
|
+
import ttkbootstrap as ttk
|
5
|
+
|
6
|
+
from . import configuration
|
7
|
+
from . import obs
|
8
|
+
from .configuration import *
|
9
|
+
|
10
|
+
TOML_CONFIG_FILE = '../../config.toml'
|
11
|
+
CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'config.json')
|
12
|
+
settings_saved = False
|
13
|
+
on_save = []
|
14
|
+
|
15
|
+
|
16
|
+
def new_tab(func):
|
17
|
+
def wrapper(self, *args, **kwargs):
|
18
|
+
self.current_row = 0 # Resetting row for the new tab
|
19
|
+
# Perform any other pre-initialization tasks here if needed
|
20
|
+
return func(self, *args, **kwargs)
|
21
|
+
|
22
|
+
return wrapper
|
23
|
+
|
24
|
+
|
25
|
+
class HoverInfoWidget:
|
26
|
+
def __init__(self, parent, text, row, column, padx=5, pady=2):
|
27
|
+
self.info_icon = ttk.Label(parent, text="ⓘ", foreground="blue", cursor="hand2")
|
28
|
+
self.info_icon.grid(row=row, column=column, padx=padx, pady=pady)
|
29
|
+
self.info_icon.bind("<Enter>", lambda e: self.show_info_box(text))
|
30
|
+
self.info_icon.bind("<Leave>", lambda e: self.hide_info_box())
|
31
|
+
self.tooltip = None
|
32
|
+
|
33
|
+
def show_info_box(self, text):
|
34
|
+
x, y, _, _ = self.info_icon.bbox("insert")
|
35
|
+
x += self.info_icon.winfo_rootx() + 25
|
36
|
+
y += self.info_icon.winfo_rooty() + 20
|
37
|
+
self.tooltip = tk.Toplevel(self.info_icon)
|
38
|
+
self.tooltip.wm_overrideredirect(True)
|
39
|
+
self.tooltip.wm_geometry(f"+{x}+{y}")
|
40
|
+
label = tk.Label(self.tooltip, text=text, background="yellow", relief="solid", borderwidth=1,
|
41
|
+
font=("tahoma", "8", "normal"))
|
42
|
+
label.pack(ipadx=1)
|
43
|
+
|
44
|
+
def hide_info_box(self):
|
45
|
+
if self.tooltip:
|
46
|
+
self.tooltip.destroy()
|
47
|
+
self.tooltip = None
|
48
|
+
|
49
|
+
|
50
|
+
class ConfigApp:
|
51
|
+
def __init__(self):
|
52
|
+
self.window = ttk.Window(themename='darkly')
|
53
|
+
self.window.title('GameSentenceMiner Configuration')
|
54
|
+
self.window.protocol("WM_DELETE_WINDOW", self.hide)
|
55
|
+
|
56
|
+
self.current_row = 0
|
57
|
+
|
58
|
+
self.master_config: Config = configuration.load_config()
|
59
|
+
|
60
|
+
self.settings = self.master_config.get_config()
|
61
|
+
|
62
|
+
self.notebook = ttk.Notebook(self.window)
|
63
|
+
self.notebook.pack(pady=10, expand=True)
|
64
|
+
|
65
|
+
self.general_frame = self.create_general_tab()
|
66
|
+
self.create_paths_tab()
|
67
|
+
self.create_anki_tab()
|
68
|
+
self.create_vad_tab()
|
69
|
+
self.create_features_tab()
|
70
|
+
self.create_screenshot_tab()
|
71
|
+
self.create_audio_tab()
|
72
|
+
self.create_obs_tab()
|
73
|
+
self.create_hotkeys_tab()
|
74
|
+
self.create_profiles_tab()
|
75
|
+
|
76
|
+
ttk.Button(self.window, text="Save Settings", command=self.save_settings).pack(pady=20)
|
77
|
+
|
78
|
+
self.window.withdraw()
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
def show(self):
|
83
|
+
obs.update_current_game()
|
84
|
+
self.reload_settings()
|
85
|
+
if self.window is not None:
|
86
|
+
self.window.deiconify()
|
87
|
+
self.window.lift()
|
88
|
+
return
|
89
|
+
|
90
|
+
def hide(self):
|
91
|
+
if self.window is not None:
|
92
|
+
self.window.withdraw()
|
93
|
+
|
94
|
+
def add_save_hook(self, func):
|
95
|
+
on_save.append(func)
|
96
|
+
|
97
|
+
def save_settings(self, profile_change=False):
|
98
|
+
global settings_saved
|
99
|
+
|
100
|
+
# Create a new Config instance
|
101
|
+
config = ProfileConfig(
|
102
|
+
general=General(
|
103
|
+
use_websocket=self.websocket_enabled.get(),
|
104
|
+
websocket_uri=self.websocket_uri.get(),
|
105
|
+
open_config_on_startup=self.open_config_on_startup.get()
|
106
|
+
),
|
107
|
+
paths=Paths(
|
108
|
+
folder_to_watch=self.folder_to_watch.get(),
|
109
|
+
audio_destination=self.audio_destination.get(),
|
110
|
+
screenshot_destination=self.screenshot_destination.get(),
|
111
|
+
remove_video=self.remove_video.get(),
|
112
|
+
remove_audio=self.remove_audio.get(),
|
113
|
+
remove_screenshot=self.remove_screenshot.get()
|
114
|
+
),
|
115
|
+
anki=Anki(
|
116
|
+
update_anki=self.update_anki.get(),
|
117
|
+
url=self.anki_url.get(),
|
118
|
+
sentence_field=self.sentence_field.get(),
|
119
|
+
sentence_audio_field=self.sentence_audio_field.get(),
|
120
|
+
picture_field=self.picture_field.get(),
|
121
|
+
word_field=self.word_field.get(),
|
122
|
+
previous_sentence_field=self.previous_sentence_field.get(),
|
123
|
+
custom_tags=[tag.strip() for tag in self.custom_tags.get().split(',') if tag.strip()],
|
124
|
+
tags_to_check=[tag.strip().lower() for tag in self.tags_to_check.get().split(',') if tag.strip()],
|
125
|
+
add_game_tag=self.add_game_tag.get(),
|
126
|
+
polling_rate=int(self.polling_rate.get()),
|
127
|
+
overwrite_audio=self.overwrite_audio.get(),
|
128
|
+
overwrite_picture=self.overwrite_picture.get(),
|
129
|
+
anki_custom_fields={
|
130
|
+
key_entry.get(): value_entry.get() for key_entry, value_entry, delete_button in
|
131
|
+
self.custom_field_entries if key_entry.get()
|
132
|
+
}
|
133
|
+
),
|
134
|
+
features=Features(
|
135
|
+
full_auto=self.full_auto.get(),
|
136
|
+
notify_on_update=self.notify_on_update.get(),
|
137
|
+
open_anki_edit=self.open_anki_edit.get(),
|
138
|
+
backfill_audio=self.backfill_audio.get()
|
139
|
+
),
|
140
|
+
screenshot=Screenshot(
|
141
|
+
width=self.screenshot_width.get(),
|
142
|
+
height=self.screenshot_height.get(),
|
143
|
+
quality=self.screenshot_quality.get(),
|
144
|
+
extension=self.screenshot_extension.get(),
|
145
|
+
custom_ffmpeg_settings=self.screenshot_custom_ffmpeg_settings.get(),
|
146
|
+
screenshot_hotkey_updates_anki=self.screenshot_hotkey_update_anki.get(),
|
147
|
+
seconds_after_line = self.seconds_after_line.get()
|
148
|
+
),
|
149
|
+
audio=Audio(
|
150
|
+
extension=self.audio_extension.get(),
|
151
|
+
beginning_offset=float(self.beginning_offset.get()),
|
152
|
+
end_offset=float(self.end_offset.get()),
|
153
|
+
ffmpeg_reencode_options=self.ffmpeg_reencode_options.get(),
|
154
|
+
external_tool = self.external_tool.get(),
|
155
|
+
anki_media_collection=self.anki_media_collection.get()
|
156
|
+
),
|
157
|
+
obs=OBS(
|
158
|
+
enabled=self.obs_enabled.get(),
|
159
|
+
host=self.obs_host.get(),
|
160
|
+
port=int(self.obs_port.get()),
|
161
|
+
password=self.obs_password.get(),
|
162
|
+
start_buffer=self.obs_start_buffer.get(),
|
163
|
+
get_game_from_scene=self.get_game_from_scene_name.get(),
|
164
|
+
minimum_replay_size=int(self.minimum_replay_size.get())
|
165
|
+
),
|
166
|
+
hotkeys=Hotkeys(
|
167
|
+
reset_line=self.reset_line_hotkey.get(),
|
168
|
+
take_screenshot=self.take_screenshot_hotkey.get()
|
169
|
+
),
|
170
|
+
vad=VAD(
|
171
|
+
whisper_model=self.whisper_model.get(),
|
172
|
+
do_vad_postprocessing=self.do_vad_postprocessing.get(),
|
173
|
+
vosk_url='https://alphacephei.com/vosk/models/vosk-model-ja-0.22.zip' if self.vosk_url.get() == VOSK_BASE else "https://alphacephei.com/vosk/models/vosk-model-small-ja-0.22.zip",
|
174
|
+
selected_vad_model=self.selected_vad_model.get(),
|
175
|
+
backup_vad_model=self.backup_vad_model.get(),
|
176
|
+
trim_beginning=self.vad_trim_beginning.get()
|
177
|
+
)
|
178
|
+
)
|
179
|
+
|
180
|
+
current_profile = self.profile_combobox.get()
|
181
|
+
|
182
|
+
if profile_change:
|
183
|
+
self.master_config.current_profile = current_profile
|
184
|
+
else:
|
185
|
+
self.master_config.current_profile = current_profile
|
186
|
+
self.master_config.set_config_for_profile(current_profile, config)
|
187
|
+
|
188
|
+
# Serialize the config instance to JSON
|
189
|
+
with open('../../config.json', 'w') as file:
|
190
|
+
file.write(self.master_config.to_json(indent=4))
|
191
|
+
|
192
|
+
print("Settings saved successfully!")
|
193
|
+
settings_saved = True
|
194
|
+
configuration.reload_config()
|
195
|
+
for func in on_save:
|
196
|
+
func()
|
197
|
+
|
198
|
+
|
199
|
+
def reload_settings(self):
|
200
|
+
new_config = configuration.load_config()
|
201
|
+
current_config = new_config.get_config()
|
202
|
+
|
203
|
+
self.window.title("GameSentenceMiner Configuration - " + current_config.name)
|
204
|
+
|
205
|
+
if current_config.name != self.settings.name:
|
206
|
+
logger.info("Profile changed, reloading settings.")
|
207
|
+
self.master_config = new_config
|
208
|
+
self.settings = current_config
|
209
|
+
for frame in self.notebook.winfo_children():
|
210
|
+
frame.destroy()
|
211
|
+
|
212
|
+
self.general_frame = self.create_general_tab()
|
213
|
+
self.create_paths_tab()
|
214
|
+
self.create_anki_tab()
|
215
|
+
self.create_vad_tab()
|
216
|
+
self.create_features_tab()
|
217
|
+
self.create_screenshot_tab()
|
218
|
+
self.create_audio_tab()
|
219
|
+
self.create_obs_tab()
|
220
|
+
self.create_hotkeys_tab()
|
221
|
+
self.create_profiles_tab()
|
222
|
+
|
223
|
+
|
224
|
+
def increment_row(self):
|
225
|
+
"""Increment the current row index and return the new value."""
|
226
|
+
self.current_row += 1
|
227
|
+
return self.current_row
|
228
|
+
|
229
|
+
def add_label_and_increment_row(self, root, label, row=0, column=0):
|
230
|
+
HoverInfoWidget(root, label, row=self.current_row, column=column)
|
231
|
+
self.increment_row()
|
232
|
+
|
233
|
+
@new_tab
|
234
|
+
def create_general_tab(self):
|
235
|
+
general_frame = ttk.Frame(self.notebook)
|
236
|
+
self.notebook.add(general_frame, text='General')
|
237
|
+
|
238
|
+
ttk.Label(general_frame, text="Websocket Enabled:").grid(row=self.current_row, column=0, sticky='W')
|
239
|
+
self.websocket_enabled = tk.BooleanVar(value=self.settings.general.use_websocket)
|
240
|
+
ttk.Checkbutton(general_frame, variable=self.websocket_enabled).grid(row=self.current_row, column=1,
|
241
|
+
sticky='W')
|
242
|
+
self.add_label_and_increment_row(general_frame, "Enable or disable WebSocket communication.",
|
243
|
+
row=self.current_row, column=2)
|
244
|
+
|
245
|
+
ttk.Label(general_frame, text="Websocket URI:").grid(row=self.current_row, column=0, sticky='W')
|
246
|
+
self.websocket_uri = ttk.Entry(general_frame)
|
247
|
+
self.websocket_uri.insert(0, self.settings.general.websocket_uri)
|
248
|
+
self.websocket_uri.grid(row=self.current_row, column=1)
|
249
|
+
self.add_label_and_increment_row(general_frame, "WebSocket URI for connecting.", row=self.current_row,
|
250
|
+
column=2)
|
251
|
+
|
252
|
+
ttk.Label(general_frame, text="Open Config on Startup:").grid(row=self.current_row, column=0, sticky='W')
|
253
|
+
self.open_config_on_startup = tk.BooleanVar(value=self.settings.general.open_config_on_startup)
|
254
|
+
ttk.Checkbutton(general_frame, variable=self.open_config_on_startup).grid(row=self.current_row, column=1,
|
255
|
+
sticky='W')
|
256
|
+
self.add_label_and_increment_row(general_frame, "Whether to open config when the script starts.",
|
257
|
+
row=self.current_row, column=2)
|
258
|
+
|
259
|
+
# ttk.Label(general_frame, text="Per Scene Config:").grid(row=self.current_row, column=0, sticky='W')
|
260
|
+
# self.per_scene_config = tk.BooleanVar(value=self.master_config.per_scene_config)
|
261
|
+
# ttk.Checkbutton(general_frame, variable=self.per_scene_config).grid(row=self.current_row, column=1,
|
262
|
+
# sticky='W')
|
263
|
+
# self.add_label_and_increment_row(general_frame, "Enable Per-Scene Config, REQUIRES RESTART. Disable to edit the DEFAULT Config.",
|
264
|
+
# row=self.current_row, column=2)
|
265
|
+
|
266
|
+
return general_frame
|
267
|
+
|
268
|
+
@new_tab
|
269
|
+
def create_vad_tab(self):
|
270
|
+
vad_frame = ttk.Frame(self.notebook)
|
271
|
+
self.notebook.add(vad_frame, text='VAD')
|
272
|
+
|
273
|
+
ttk.Label(vad_frame, text="Voice Detection Postprocessing:").grid(row=self.current_row, column=0, sticky='W')
|
274
|
+
self.do_vad_postprocessing = tk.BooleanVar(
|
275
|
+
value=self.settings.vad.do_vad_postprocessing)
|
276
|
+
ttk.Checkbutton(vad_frame, variable=self.do_vad_postprocessing).grid(row=self.current_row, column=1, sticky='W')
|
277
|
+
self.add_label_and_increment_row(vad_frame, "Enable post-processing of audio to trim just the voiceline.",
|
278
|
+
row=self.current_row, column=2)
|
279
|
+
|
280
|
+
ttk.Label(vad_frame, text="Whisper Model:").grid(row=self.current_row, column=0, sticky='W')
|
281
|
+
self.whisper_model = ttk.Combobox(vad_frame, values=[WHISPER_TINY, WHISPER_BASE, WHISPER_SMALL, WHISPER_MEDIUM,
|
282
|
+
WHSIPER_LARGE])
|
283
|
+
self.whisper_model.set(self.settings.vad.whisper_model)
|
284
|
+
self.whisper_model.grid(row=self.current_row, column=1)
|
285
|
+
self.add_label_and_increment_row(vad_frame, "Select the Whisper model size for VAD.", row=self.current_row,
|
286
|
+
column=2)
|
287
|
+
|
288
|
+
ttk.Label(vad_frame, text="Vosk URL:").grid(row=self.current_row, column=0, sticky='W')
|
289
|
+
self.vosk_url = ttk.Combobox(vad_frame, values=[VOSK_BASE, VOSK_SMALL])
|
290
|
+
self.vosk_url.insert(0,
|
291
|
+
VOSK_BASE if self.settings.vad.vosk_url == 'https://alphacephei.com/vosk/models/vosk-model-ja-0.22.zip' else VOSK_SMALL)
|
292
|
+
self.vosk_url.grid(row=self.current_row, column=1)
|
293
|
+
self.add_label_and_increment_row(vad_frame, "URL for connecting to the Vosk server.", row=self.current_row,
|
294
|
+
column=2)
|
295
|
+
|
296
|
+
ttk.Label(vad_frame, text="Select VAD Model:").grid(row=self.current_row, column=0, sticky='W')
|
297
|
+
self.selected_vad_model = ttk.Combobox(vad_frame, values=[VOSK, SILERO, WHISPER])
|
298
|
+
self.selected_vad_model.set(self.settings.vad.selected_vad_model)
|
299
|
+
self.selected_vad_model.grid(row=self.current_row, column=1)
|
300
|
+
self.add_label_and_increment_row(vad_frame, "Select which VAD model to use.", row=self.current_row, column=2)
|
301
|
+
|
302
|
+
ttk.Label(vad_frame, text="Backup VAD Model:").grid(row=self.current_row, column=0, sticky='W')
|
303
|
+
self.backup_vad_model = ttk.Combobox(vad_frame, values=[OFF, VOSK, SILERO, WHISPER])
|
304
|
+
self.backup_vad_model.set(self.settings.vad.backup_vad_model)
|
305
|
+
self.backup_vad_model.grid(row=self.current_row, column=1)
|
306
|
+
self.add_label_and_increment_row(vad_frame, "Select which model to use as a backup if no audio is found.",
|
307
|
+
row=self.current_row, column=2)
|
308
|
+
|
309
|
+
ttk.Label(vad_frame, text="Trim Beginning:").grid(row=self.current_row, column=0, sticky='W')
|
310
|
+
self.vad_trim_beginning = tk.BooleanVar(
|
311
|
+
value=self.settings.vad.trim_beginning)
|
312
|
+
ttk.Checkbutton(vad_frame, variable=self.vad_trim_beginning).grid(row=self.current_row, column=1, sticky='W')
|
313
|
+
self.add_label_and_increment_row(vad_frame, "Trim the beginning of the audio based on Voice Detection Results",
|
314
|
+
row=self.current_row, column=2)
|
315
|
+
|
316
|
+
|
317
|
+
@new_tab
|
318
|
+
def create_paths_tab(self):
|
319
|
+
paths_frame = ttk.Frame(self.notebook)
|
320
|
+
self.notebook.add(paths_frame, text='Paths')
|
321
|
+
|
322
|
+
ttk.Label(paths_frame, text="Folder to Watch:").grid(row=self.current_row, column=0, sticky='W')
|
323
|
+
self.folder_to_watch = ttk.Entry(paths_frame, width=50)
|
324
|
+
self.folder_to_watch.insert(0, self.settings.paths.folder_to_watch)
|
325
|
+
self.folder_to_watch.grid(row=self.current_row, column=1)
|
326
|
+
ttk.Button(paths_frame, text="Browse", command=lambda: self.browse_folder(self.folder_to_watch)).grid(
|
327
|
+
row=self.current_row,
|
328
|
+
column=2)
|
329
|
+
self.add_label_and_increment_row(paths_frame, "Path where the OBS Replays will be saved.", row=self.current_row,
|
330
|
+
column=3)
|
331
|
+
|
332
|
+
ttk.Label(paths_frame, text="Audio Destination:").grid(row=self.current_row, column=0, sticky='W')
|
333
|
+
self.audio_destination = ttk.Entry(paths_frame, width=50)
|
334
|
+
self.audio_destination.insert(0, self.settings.paths.audio_destination)
|
335
|
+
self.audio_destination.grid(row=self.current_row, column=1)
|
336
|
+
ttk.Button(paths_frame, text="Browse", command=lambda: self.browse_folder(self.audio_destination)).grid(
|
337
|
+
row=self.current_row,
|
338
|
+
column=2)
|
339
|
+
self.add_label_and_increment_row(paths_frame, "Path where the cut Audio will be saved.", row=self.current_row,
|
340
|
+
column=3)
|
341
|
+
|
342
|
+
ttk.Label(paths_frame, text="Screenshot Destination:").grid(row=self.current_row, column=0, sticky='W')
|
343
|
+
self.screenshot_destination = ttk.Entry(paths_frame, width=50)
|
344
|
+
self.screenshot_destination.insert(0, self.settings.paths.screenshot_destination)
|
345
|
+
self.screenshot_destination.grid(row=self.current_row, column=1)
|
346
|
+
ttk.Button(paths_frame, text="Browse", command=lambda: self.browse_folder(self.screenshot_destination)).grid(
|
347
|
+
row=self.current_row, column=2)
|
348
|
+
self.add_label_and_increment_row(paths_frame, "Path where the Screenshot will be saved.", row=self.current_row,
|
349
|
+
column=3)
|
350
|
+
|
351
|
+
ttk.Label(paths_frame, text="Remove Video:").grid(row=self.current_row, column=0, sticky='W')
|
352
|
+
self.remove_video = tk.BooleanVar(value=self.settings.paths.remove_video)
|
353
|
+
ttk.Checkbutton(paths_frame, variable=self.remove_video).grid(row=self.current_row, column=1, sticky='W')
|
354
|
+
self.add_label_and_increment_row(paths_frame, "Remove video from the output.", row=self.current_row, column=2)
|
355
|
+
|
356
|
+
ttk.Label(paths_frame, text="Remove Audio:").grid(row=self.current_row, column=0, sticky='W')
|
357
|
+
self.remove_audio = tk.BooleanVar(value=self.settings.paths.remove_audio)
|
358
|
+
ttk.Checkbutton(paths_frame, variable=self.remove_audio).grid(row=self.current_row, column=1, sticky='W')
|
359
|
+
self.add_label_and_increment_row(paths_frame, "Remove audio from the output.", row=self.current_row, column=2)
|
360
|
+
|
361
|
+
ttk.Label(paths_frame, text="Remove Screenshot:").grid(row=self.current_row, column=0, sticky='W')
|
362
|
+
self.remove_screenshot = tk.BooleanVar(value=self.settings.paths.remove_screenshot)
|
363
|
+
ttk.Checkbutton(paths_frame, variable=self.remove_screenshot).grid(row=self.current_row, column=1, sticky='W')
|
364
|
+
self.add_label_and_increment_row(paths_frame, "Remove screenshots after processing.", row=self.current_row,
|
365
|
+
column=2)
|
366
|
+
|
367
|
+
return paths_frame
|
368
|
+
|
369
|
+
def browse_folder(self, entry_widget):
|
370
|
+
folder_selected = filedialog.askdirectory()
|
371
|
+
if folder_selected:
|
372
|
+
entry_widget.delete(0, tk.END)
|
373
|
+
entry_widget.insert(0, folder_selected)
|
374
|
+
|
375
|
+
@new_tab
|
376
|
+
def create_anki_tab(self):
|
377
|
+
anki_frame = ttk.Frame(self.notebook)
|
378
|
+
self.notebook.add(anki_frame, text='Anki')
|
379
|
+
|
380
|
+
ttk.Label(anki_frame, text="Update Anki:").grid(row=self.current_row, column=0, sticky='W')
|
381
|
+
self.update_anki = tk.BooleanVar(value=self.settings.anki.update_anki)
|
382
|
+
ttk.Checkbutton(anki_frame, variable=self.update_anki).grid(row=self.current_row, column=1, sticky='W')
|
383
|
+
self.add_label_and_increment_row(anki_frame, "Automatically update Anki with new data.", row=self.current_row,
|
384
|
+
column=2)
|
385
|
+
|
386
|
+
ttk.Label(anki_frame, text="Anki URL:").grid(row=self.current_row, column=0, sticky='W')
|
387
|
+
self.anki_url = ttk.Entry(anki_frame, width=50)
|
388
|
+
self.anki_url.insert(0, self.settings.anki.url)
|
389
|
+
self.anki_url.grid(row=self.current_row, column=1)
|
390
|
+
self.add_label_and_increment_row(anki_frame, "The URL to connect to your Anki instance.", row=self.current_row,
|
391
|
+
column=2)
|
392
|
+
|
393
|
+
ttk.Label(anki_frame, text="Sentence Field:").grid(row=self.current_row, column=0, sticky='W')
|
394
|
+
self.sentence_field = ttk.Entry(anki_frame)
|
395
|
+
self.sentence_field.insert(0, self.settings.anki.sentence_field)
|
396
|
+
self.sentence_field.grid(row=self.current_row, column=1)
|
397
|
+
self.add_label_and_increment_row(anki_frame, "Field in Anki for the main sentence.", row=self.current_row,
|
398
|
+
column=2)
|
399
|
+
|
400
|
+
ttk.Label(anki_frame, text="Sentence Audio Field:").grid(row=self.current_row, column=0, sticky='W')
|
401
|
+
self.sentence_audio_field = ttk.Entry(anki_frame)
|
402
|
+
self.sentence_audio_field.insert(0, self.settings.anki.sentence_audio_field)
|
403
|
+
self.sentence_audio_field.grid(row=self.current_row, column=1)
|
404
|
+
self.add_label_and_increment_row(anki_frame,
|
405
|
+
"Field in Anki for audio associated with the sentence. Leave Blank to Disable Audio Processing.",
|
406
|
+
row=self.current_row, column=2)
|
407
|
+
|
408
|
+
ttk.Label(anki_frame, text="Picture Field:").grid(row=self.current_row, column=0, sticky='W')
|
409
|
+
self.picture_field = ttk.Entry(anki_frame)
|
410
|
+
self.picture_field.insert(0, self.settings.anki.picture_field)
|
411
|
+
self.picture_field.grid(row=self.current_row, column=1)
|
412
|
+
self.add_label_and_increment_row(anki_frame, "Field in Anki for associated pictures.", row=self.current_row,
|
413
|
+
column=2)
|
414
|
+
|
415
|
+
ttk.Label(anki_frame, text="Word Field:").grid(row=self.current_row, column=0, sticky='W')
|
416
|
+
self.word_field = ttk.Entry(anki_frame)
|
417
|
+
self.word_field.insert(0, self.settings.anki.word_field)
|
418
|
+
self.word_field.grid(row=self.current_row, column=1)
|
419
|
+
self.add_label_and_increment_row(anki_frame, "Field in Anki for individual words.", row=self.current_row,
|
420
|
+
column=2)
|
421
|
+
|
422
|
+
ttk.Label(anki_frame, text="Previous Sentence Field:").grid(row=self.current_row, column=0, sticky='W')
|
423
|
+
self.previous_sentence_field = ttk.Entry(anki_frame)
|
424
|
+
self.previous_sentence_field.insert(0, self.settings.anki.previous_sentence_field)
|
425
|
+
self.previous_sentence_field.grid(row=self.current_row, column=1)
|
426
|
+
self.add_label_and_increment_row(anki_frame,
|
427
|
+
"Field in Anki for the previous line of dialogue. If Empty, will not populate",
|
428
|
+
row=self.current_row,
|
429
|
+
column=2)
|
430
|
+
|
431
|
+
ttk.Label(anki_frame, text="Custom Tags:").grid(row=self.current_row, column=0, sticky='W')
|
432
|
+
self.custom_tags = ttk.Entry(anki_frame)
|
433
|
+
self.custom_tags.insert(0, ', '.join(self.settings.anki.custom_tags))
|
434
|
+
self.custom_tags.grid(row=self.current_row, column=1)
|
435
|
+
self.add_label_and_increment_row(anki_frame, "Comma-separated custom tags for the Anki cards.",
|
436
|
+
row=self.current_row, column=2)
|
437
|
+
|
438
|
+
ttk.Label(anki_frame, text="Tags to work on:").grid(row=self.current_row, column=0, sticky='W')
|
439
|
+
self.tags_to_check = ttk.Entry(anki_frame)
|
440
|
+
self.tags_to_check.insert(0, ', '.join(self.settings.anki.tags_to_check))
|
441
|
+
self.tags_to_check.grid(row=self.current_row, column=1)
|
442
|
+
self.add_label_and_increment_row(anki_frame,
|
443
|
+
"Comma-separated Tags, script will only do 1-click on cards with these tags (Recommend keep empty, or use Yomitan Profile to add custom tag from texthooker page)",
|
444
|
+
row=self.current_row, column=2)
|
445
|
+
|
446
|
+
ttk.Label(anki_frame, text="Add Game Tag:").grid(row=self.current_row, column=0, sticky='W')
|
447
|
+
self.add_game_tag = tk.BooleanVar(value=self.settings.anki.add_game_tag)
|
448
|
+
ttk.Checkbutton(anki_frame, variable=self.add_game_tag).grid(row=self.current_row, column=1, sticky='W')
|
449
|
+
self.add_label_and_increment_row(anki_frame, "Include a tag for the game on the Anki card.",
|
450
|
+
row=self.current_row, column=2)
|
451
|
+
|
452
|
+
ttk.Label(anki_frame, text="Polling Rate:").grid(row=self.current_row, column=0, sticky='W')
|
453
|
+
self.polling_rate = ttk.Entry(anki_frame)
|
454
|
+
self.polling_rate.insert(0, str(self.settings.anki.polling_rate))
|
455
|
+
self.polling_rate.grid(row=self.current_row, column=1)
|
456
|
+
self.add_label_and_increment_row(anki_frame, "Rate at which Anki will check for updates (in milliseconds).",
|
457
|
+
row=self.current_row, column=2)
|
458
|
+
|
459
|
+
ttk.Label(anki_frame, text="Overwrite Audio:").grid(row=self.current_row, column=0, sticky='W')
|
460
|
+
self.overwrite_audio = tk.BooleanVar(
|
461
|
+
value=self.settings.anki.overwrite_audio)
|
462
|
+
ttk.Checkbutton(anki_frame, variable=self.overwrite_audio).grid(row=self.current_row, column=1, sticky='W')
|
463
|
+
self.add_label_and_increment_row(anki_frame, "Overwrite existing audio in Anki cards.", row=self.current_row,
|
464
|
+
column=2)
|
465
|
+
|
466
|
+
ttk.Label(anki_frame, text="Overwrite Picture:").grid(row=self.current_row, column=0, sticky='W')
|
467
|
+
self.overwrite_picture = tk.BooleanVar(
|
468
|
+
value=self.settings.anki.overwrite_picture)
|
469
|
+
ttk.Checkbutton(anki_frame, variable=self.overwrite_picture).grid(row=self.current_row, column=1, sticky='W')
|
470
|
+
self.add_label_and_increment_row(anki_frame, "Overwrite existing pictures in Anki cards.", row=self.current_row,
|
471
|
+
column=2)
|
472
|
+
|
473
|
+
self.anki_custom_fields = self.settings.anki.anki_custom_fields
|
474
|
+
self.custom_field_entries = []
|
475
|
+
|
476
|
+
row_at_the_time = self.current_row + 1
|
477
|
+
|
478
|
+
ttk.Button(anki_frame, text="Add Field",
|
479
|
+
command=lambda: self.add_custom_field(anki_frame, row_at_the_time)).grid(row=self.current_row,
|
480
|
+
column=0, pady=5)
|
481
|
+
self.add_label_and_increment_row(anki_frame, "Add a new custom field for Anki cards.", row=self.current_row,
|
482
|
+
column=2)
|
483
|
+
self.display_custom_fields(anki_frame, self.current_row)
|
484
|
+
|
485
|
+
return anki_frame
|
486
|
+
|
487
|
+
def add_custom_field(self, frame, start_row):
|
488
|
+
row = len(self.custom_field_entries) + 1 + start_row
|
489
|
+
|
490
|
+
key_entry = ttk.Entry(frame)
|
491
|
+
key_entry.grid(row=row, column=0, padx=5, pady=2, sticky='W')
|
492
|
+
value_entry = ttk.Entry(frame)
|
493
|
+
value_entry.grid(row=row, column=1, padx=5, pady=2, sticky='W')
|
494
|
+
|
495
|
+
# Create a delete button for this custom field
|
496
|
+
delete_button = ttk.Button(frame, text="X",
|
497
|
+
command=lambda: self.delete_custom_field(row, key_entry, value_entry, delete_button))
|
498
|
+
delete_button.grid(row=row, column=2, padx=5, pady=2)
|
499
|
+
|
500
|
+
self.custom_field_entries.append((key_entry, value_entry, delete_button))
|
501
|
+
|
502
|
+
def display_custom_fields(self, frame, start_row):
|
503
|
+
for row, (key, value) in enumerate(self.anki_custom_fields.items()):
|
504
|
+
key_entry = ttk.Entry(frame)
|
505
|
+
key_entry.insert(0, key)
|
506
|
+
key_entry.grid(row=row + start_row, column=0, padx=5, pady=2, sticky='W')
|
507
|
+
|
508
|
+
value_entry = ttk.Entry(frame)
|
509
|
+
value_entry.insert(0, value)
|
510
|
+
value_entry.grid(row=row + start_row, column=1, padx=5, pady=2, sticky='W')
|
511
|
+
|
512
|
+
# Create a delete button for each existing custom field
|
513
|
+
delete_button = ttk.Button(frame, text="X",
|
514
|
+
command=lambda: self.delete_custom_field(row + start_row, key_entry, value_entry,
|
515
|
+
delete_button))
|
516
|
+
delete_button.grid(row=row + start_row, column=2, padx=5, pady=2)
|
517
|
+
|
518
|
+
self.custom_field_entries.append((key_entry, value_entry, delete_button))
|
519
|
+
|
520
|
+
def delete_custom_field(self, row, key_entry, value_entry, delete_button):
|
521
|
+
# Remove the entry from the GUI
|
522
|
+
key_entry.destroy()
|
523
|
+
value_entry.destroy()
|
524
|
+
delete_button.destroy()
|
525
|
+
|
526
|
+
# Remove the entry from the custom field entries list
|
527
|
+
self.custom_field_entries.remove((key_entry, value_entry, delete_button))
|
528
|
+
|
529
|
+
# Update the GUI rows below to fill the gap if necessary
|
530
|
+
# for (ke, ve, db) in self.custom_field_entries:
|
531
|
+
# if self.custom_field_entries.index((ke, ve, db)) > self.custom_field_entries.index(
|
532
|
+
# (key_entry, value_entry, delete_button)):
|
533
|
+
# ke.grid_configure(row=ke.grid_info()['row'] - 1)
|
534
|
+
# ve.grid_configure(row=ve.grid_info()['row'] - 1)
|
535
|
+
# db.grid_configure(row=db.grid_info()['row'] - 1)
|
536
|
+
|
537
|
+
@new_tab
|
538
|
+
def create_features_tab(self):
|
539
|
+
features_frame = ttk.Frame(self.notebook)
|
540
|
+
self.notebook.add(features_frame, text='Features')
|
541
|
+
|
542
|
+
ttk.Label(features_frame, text="Notify on Update:").grid(row=self.current_row, column=0, sticky='W')
|
543
|
+
self.notify_on_update = tk.BooleanVar(value=self.settings.features.notify_on_update)
|
544
|
+
ttk.Checkbutton(features_frame, variable=self.notify_on_update).grid(row=self.current_row, column=1, sticky='W')
|
545
|
+
self.add_label_and_increment_row(features_frame, "Notify the user when an update occurs.", row=self.current_row,
|
546
|
+
column=2)
|
547
|
+
|
548
|
+
ttk.Label(features_frame, text="Open Anki Edit:").grid(row=self.current_row, column=0, sticky='W')
|
549
|
+
self.open_anki_edit = tk.BooleanVar(value=self.settings.features.open_anki_edit)
|
550
|
+
ttk.Checkbutton(features_frame, variable=self.open_anki_edit).grid(row=self.current_row, column=1, sticky='W')
|
551
|
+
self.add_label_and_increment_row(features_frame, "Automatically open Anki for editing after updating.",
|
552
|
+
row=self.current_row, column=2)
|
553
|
+
|
554
|
+
ttk.Label(features_frame, text="Backfill Audio:").grid(row=self.current_row, column=0, sticky='W')
|
555
|
+
self.backfill_audio = tk.BooleanVar(value=self.settings.features.backfill_audio)
|
556
|
+
ttk.Checkbutton(features_frame, variable=self.backfill_audio).grid(row=self.current_row, column=1, sticky='W')
|
557
|
+
self.add_label_and_increment_row(features_frame, "Fill in audio data for existing entries.",
|
558
|
+
row=self.current_row, column=2)
|
559
|
+
|
560
|
+
ttk.Label(features_frame, text="Full Auto Mode:").grid(row=self.current_row, column=0, sticky='W')
|
561
|
+
self.full_auto = tk.BooleanVar(
|
562
|
+
value=self.settings.features.full_auto)
|
563
|
+
ttk.Checkbutton(features_frame, variable=self.full_auto).grid(row=self.current_row, column=1, sticky='W')
|
564
|
+
self.add_label_and_increment_row(features_frame, "Yomitan 1-click anki card creation.", row=self.current_row,
|
565
|
+
column=2)
|
566
|
+
|
567
|
+
@new_tab
|
568
|
+
def create_screenshot_tab(self):
|
569
|
+
screenshot_frame = ttk.Frame(self.notebook)
|
570
|
+
self.notebook.add(screenshot_frame, text='Screenshot')
|
571
|
+
|
572
|
+
ttk.Label(screenshot_frame, text="Width:").grid(row=self.current_row, column=0, sticky='W')
|
573
|
+
self.screenshot_width = ttk.Entry(screenshot_frame)
|
574
|
+
self.screenshot_width.insert(0, str(self.settings.screenshot.width))
|
575
|
+
self.screenshot_width.grid(row=self.current_row, column=1)
|
576
|
+
self.add_label_and_increment_row(screenshot_frame, "Width of the screenshot in pixels.", row=self.current_row,
|
577
|
+
column=2)
|
578
|
+
|
579
|
+
ttk.Label(screenshot_frame, text="Height:").grid(row=self.current_row, column=0, sticky='W')
|
580
|
+
self.screenshot_height = ttk.Entry(screenshot_frame)
|
581
|
+
self.screenshot_height.insert(0, str(self.settings.screenshot.height))
|
582
|
+
self.screenshot_height.grid(row=self.current_row, column=1)
|
583
|
+
self.add_label_and_increment_row(screenshot_frame, "Height of the screenshot in pixels.", row=self.current_row,
|
584
|
+
column=2)
|
585
|
+
|
586
|
+
ttk.Label(screenshot_frame, text="Quality:").grid(row=self.current_row, column=0, sticky='W')
|
587
|
+
self.screenshot_quality = ttk.Entry(screenshot_frame)
|
588
|
+
self.screenshot_quality.insert(0, str(self.settings.screenshot.quality))
|
589
|
+
self.screenshot_quality.grid(row=self.current_row, column=1)
|
590
|
+
self.add_label_and_increment_row(screenshot_frame, "Quality of the screenshot (0-100).", row=self.current_row,
|
591
|
+
column=2)
|
592
|
+
|
593
|
+
ttk.Label(screenshot_frame, text="Extension:").grid(row=self.current_row, column=0, sticky='W')
|
594
|
+
self.screenshot_extension = ttk.Combobox(screenshot_frame, values=['webp', 'avif', 'png', 'jpeg'])
|
595
|
+
self.screenshot_extension.insert(0, self.settings.screenshot.extension)
|
596
|
+
self.screenshot_extension.grid(row=self.current_row, column=1)
|
597
|
+
self.add_label_and_increment_row(screenshot_frame, "File extension for the screenshot format.",
|
598
|
+
row=self.current_row, column=2)
|
599
|
+
|
600
|
+
ttk.Label(screenshot_frame, text="FFmpeg Reencode Options:").grid(row=self.current_row, column=0, sticky='W')
|
601
|
+
self.screenshot_custom_ffmpeg_settings = ttk.Entry(screenshot_frame, width=50)
|
602
|
+
self.screenshot_custom_ffmpeg_settings.insert(0, self.settings.screenshot.custom_ffmpeg_settings)
|
603
|
+
self.screenshot_custom_ffmpeg_settings.grid(row=self.current_row, column=1)
|
604
|
+
self.add_label_and_increment_row(screenshot_frame, "Custom FFmpeg options for re-encoding screenshots.",
|
605
|
+
row=self.current_row, column=2)
|
606
|
+
|
607
|
+
ttk.Label(screenshot_frame, text="Screenshot Hotkey Updates Anki:").grid(row=self.current_row, column=0, sticky='W')
|
608
|
+
self.screenshot_hotkey_update_anki = tk.BooleanVar(value=self.settings.screenshot.screenshot_hotkey_updates_anki)
|
609
|
+
ttk.Checkbutton(screenshot_frame, variable=self.screenshot_hotkey_update_anki).grid(row=self.current_row, column=1, sticky='W')
|
610
|
+
self.add_label_and_increment_row(screenshot_frame, "Enable to allow Screenshot hotkey/button to update the latest anki card.", row=self.current_row,
|
611
|
+
column=2)
|
612
|
+
|
613
|
+
ttk.Label(screenshot_frame, text="Seconds After Line to SS:").grid(row=self.current_row, column=0, sticky='W')
|
614
|
+
self.seconds_after_line = ttk.Entry(screenshot_frame)
|
615
|
+
self.seconds_after_line.insert(0, str(self.settings.screenshot.seconds_after_line))
|
616
|
+
self.seconds_after_line.grid(row=self.current_row, column=1)
|
617
|
+
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,
|
618
|
+
column=2)
|
619
|
+
|
620
|
+
@new_tab
|
621
|
+
def create_audio_tab(self):
|
622
|
+
audio_frame = ttk.Frame(self.notebook)
|
623
|
+
self.notebook.add(audio_frame, text='Audio')
|
624
|
+
|
625
|
+
ttk.Label(audio_frame, text="Audio Extension:").grid(row=self.current_row, column=0, sticky='W')
|
626
|
+
self.audio_extension = ttk.Combobox(audio_frame, values=['opus', 'mp3', 'ogg', 'aac', 'm4a'])
|
627
|
+
self.audio_extension.insert(0, self.settings.audio.extension)
|
628
|
+
self.audio_extension.grid(row=self.current_row, column=1)
|
629
|
+
self.add_label_and_increment_row(audio_frame, "File extension for audio files.", row=self.current_row, column=2)
|
630
|
+
|
631
|
+
ttk.Label(audio_frame, text="Beginning Offset:").grid(row=self.current_row, column=0, sticky='W')
|
632
|
+
self.beginning_offset = ttk.Entry(audio_frame)
|
633
|
+
self.beginning_offset.insert(0, str(self.settings.audio.beginning_offset))
|
634
|
+
self.beginning_offset.grid(row=self.current_row, column=1)
|
635
|
+
self.add_label_and_increment_row(audio_frame, "Offset in seconds to start audio processing.",
|
636
|
+
row=self.current_row, column=2)
|
637
|
+
|
638
|
+
ttk.Label(audio_frame, text="End Offset:").grid(row=self.current_row, column=0, sticky='W')
|
639
|
+
self.end_offset = ttk.Entry(audio_frame)
|
640
|
+
self.end_offset.insert(0, str(self.settings.audio.end_offset))
|
641
|
+
self.end_offset.grid(row=self.current_row, column=1)
|
642
|
+
self.add_label_and_increment_row(audio_frame, "Offset in seconds to end audio processing.",
|
643
|
+
row=self.current_row, column=2)
|
644
|
+
|
645
|
+
ttk.Label(audio_frame, text="FFmpeg Reencode Options:").grid(row=self.current_row, column=0, sticky='W')
|
646
|
+
self.ffmpeg_reencode_options = ttk.Entry(audio_frame, width=50)
|
647
|
+
self.ffmpeg_reencode_options.insert(0, self.settings.audio.ffmpeg_reencode_options)
|
648
|
+
self.ffmpeg_reencode_options.grid(row=self.current_row, column=1)
|
649
|
+
self.add_label_and_increment_row(audio_frame, "Custom FFmpeg options for re-encoding audio files.",
|
650
|
+
row=self.current_row, column=2)
|
651
|
+
|
652
|
+
ttk.Label(audio_frame, text="Anki Media Collection:").grid(row=self.current_row, column=0, sticky='W')
|
653
|
+
self.anki_media_collection = ttk.Entry(audio_frame)
|
654
|
+
self.anki_media_collection.insert(0, self.settings.audio.anki_media_collection)
|
655
|
+
self.anki_media_collection.grid(row=self.current_row, column=1)
|
656
|
+
self.add_label_and_increment_row(audio_frame,
|
657
|
+
"Path of the Anki Media Collection, used for external Trimming tool. NO TRAILING SLASH",
|
658
|
+
row=self.current_row,
|
659
|
+
column=2)
|
660
|
+
|
661
|
+
ttk.Label(audio_frame, text="External Audio Editing Tool:").grid(row=self.current_row, column=0, sticky='W')
|
662
|
+
self.external_tool = ttk.Entry(audio_frame)
|
663
|
+
self.external_tool.insert(0, self.settings.audio.external_tool)
|
664
|
+
self.external_tool.grid(row=self.current_row, column=1)
|
665
|
+
self.add_label_and_increment_row(audio_frame,
|
666
|
+
"Path to External tool that opens the audio up for manual trimming. I recommend OcenAudio for in-place Editing.",
|
667
|
+
row=self.current_row,
|
668
|
+
column=2)
|
669
|
+
|
670
|
+
@new_tab
|
671
|
+
def create_obs_tab(self):
|
672
|
+
obs_frame = ttk.Frame(self.notebook)
|
673
|
+
self.notebook.add(obs_frame, text='OBS')
|
674
|
+
|
675
|
+
ttk.Label(obs_frame, text="Enabled:").grid(row=self.current_row, column=0, sticky='W')
|
676
|
+
self.obs_enabled = tk.BooleanVar(value=self.settings.obs.enabled)
|
677
|
+
ttk.Checkbutton(obs_frame, variable=self.obs_enabled).grid(row=self.current_row, column=1, sticky='W')
|
678
|
+
self.add_label_and_increment_row(obs_frame, "Enable or disable OBS integration.", row=self.current_row,
|
679
|
+
column=2)
|
680
|
+
|
681
|
+
ttk.Label(obs_frame, text="Host:").grid(row=self.current_row, column=0, sticky='W')
|
682
|
+
self.obs_host = ttk.Entry(obs_frame)
|
683
|
+
self.obs_host.insert(0, self.settings.obs.host)
|
684
|
+
self.obs_host.grid(row=self.current_row, column=1)
|
685
|
+
self.add_label_and_increment_row(obs_frame, "Host address for the OBS WebSocket server.", row=self.current_row,
|
686
|
+
column=2)
|
687
|
+
|
688
|
+
ttk.Label(obs_frame, text="Port:").grid(row=self.current_row, column=0, sticky='W')
|
689
|
+
self.obs_port = ttk.Entry(obs_frame)
|
690
|
+
self.obs_port.insert(0, str(self.settings.obs.port))
|
691
|
+
self.obs_port.grid(row=self.current_row, column=1)
|
692
|
+
self.add_label_and_increment_row(obs_frame, "Port number for the OBS WebSocket server.", row=self.current_row,
|
693
|
+
column=2)
|
694
|
+
|
695
|
+
ttk.Label(obs_frame, text="Password:").grid(row=self.current_row, column=0, sticky='W')
|
696
|
+
self.obs_password = ttk.Entry(obs_frame)
|
697
|
+
self.obs_password.insert(0, self.settings.obs.password)
|
698
|
+
self.obs_password.grid(row=self.current_row, column=1)
|
699
|
+
self.add_label_and_increment_row(obs_frame, "Password for the OBS WebSocket server.", row=self.current_row,
|
700
|
+
column=2)
|
701
|
+
|
702
|
+
ttk.Label(obs_frame, text="Start/Stop Buffer:").grid(row=self.current_row, column=0, sticky='W')
|
703
|
+
self.obs_start_buffer = tk.BooleanVar(value=self.settings.obs.start_buffer)
|
704
|
+
ttk.Checkbutton(obs_frame, variable=self.obs_start_buffer).grid(row=self.current_row, column=1, sticky='W')
|
705
|
+
self.add_label_and_increment_row(obs_frame, "Start and Stop the Buffer when Script runs.", row=self.current_row,
|
706
|
+
column=2)
|
707
|
+
|
708
|
+
ttk.Label(obs_frame, text="Get Game From Scene Name:").grid(row=self.current_row, column=0, sticky='W')
|
709
|
+
self.get_game_from_scene_name = tk.BooleanVar(value=self.settings.obs.get_game_from_scene)
|
710
|
+
ttk.Checkbutton(obs_frame, variable=self.get_game_from_scene_name).grid(row=self.current_row, column=1,
|
711
|
+
sticky='W')
|
712
|
+
self.add_label_and_increment_row(obs_frame, "Changes Current Game to Scene Name", row=self.current_row,
|
713
|
+
column=2)
|
714
|
+
|
715
|
+
ttk.Label(obs_frame, text="Minimum Replay Size (KB):").grid(row=self.current_row, column=0, sticky='W')
|
716
|
+
self.minimum_replay_size = ttk.Entry(obs_frame)
|
717
|
+
self.minimum_replay_size.insert(0, str(self.settings.obs.minimum_replay_size))
|
718
|
+
self.minimum_replay_size.grid(row=self.current_row, column=1)
|
719
|
+
self.add_label_and_increment_row(obs_frame, "Minimum Replay Size for OBS Replays in KB. If Replay is Under this, "
|
720
|
+
"Audio/Screenshot Will not be grabbed.", row=self.current_row,
|
721
|
+
column=2)
|
722
|
+
|
723
|
+
@new_tab
|
724
|
+
def create_hotkeys_tab(self):
|
725
|
+
hotkeys_frame = ttk.Frame(self.notebook)
|
726
|
+
self.notebook.add(hotkeys_frame, text='Hotkeys')
|
727
|
+
|
728
|
+
ttk.Label(hotkeys_frame, text="Reset Line Hotkey:").grid(row=self.current_row, column=0, sticky='W')
|
729
|
+
self.reset_line_hotkey = ttk.Entry(hotkeys_frame)
|
730
|
+
self.reset_line_hotkey.insert(0, self.settings.hotkeys.reset_line)
|
731
|
+
self.reset_line_hotkey.grid(row=self.current_row, column=1)
|
732
|
+
self.add_label_and_increment_row(hotkeys_frame, "Hotkey to reset the current line of dialogue.",
|
733
|
+
row=self.current_row, column=2)
|
734
|
+
|
735
|
+
ttk.Label(hotkeys_frame, text="Take Screenshot Hotkey:").grid(row=self.current_row, column=0, sticky='W')
|
736
|
+
self.take_screenshot_hotkey = ttk.Entry(hotkeys_frame)
|
737
|
+
self.take_screenshot_hotkey.insert(0, self.settings.hotkeys.take_screenshot)
|
738
|
+
self.take_screenshot_hotkey.grid(row=self.current_row, column=1)
|
739
|
+
self.add_label_and_increment_row(hotkeys_frame, "Hotkey to take a screenshot.", row=self.current_row, column=2)
|
740
|
+
|
741
|
+
|
742
|
+
@new_tab
|
743
|
+
def create_profiles_tab(self):
|
744
|
+
profiles_frame = ttk.Frame(self.notebook)
|
745
|
+
self.notebook.add(profiles_frame, text='Profiles')
|
746
|
+
|
747
|
+
ttk.Label(profiles_frame, text="Select Profile:").grid(row=self.current_row, column=0, sticky='W')
|
748
|
+
self.profile_var = tk.StringVar(value=self.settings.name)
|
749
|
+
self.profile_combobox = ttk.Combobox(profiles_frame, textvariable=self.profile_var, values=list(self.master_config.configs.keys()))
|
750
|
+
self.profile_combobox.grid(row=self.current_row, column=1)
|
751
|
+
self.profile_combobox.bind("<<ComboboxSelected>>", self.on_profile_change)
|
752
|
+
self.add_label_and_increment_row(profiles_frame, "Select a profile to load its settings.", row=self.current_row, column=2)
|
753
|
+
|
754
|
+
ttk.Button(profiles_frame, text="Add Profile", command=self.add_profile).grid(row=self.current_row, column=0, pady=5)
|
755
|
+
ttk.Button(profiles_frame, text="Copy Profile", command=self.copy_profile).grid(row=self.current_row, column=1, pady=5)
|
756
|
+
if self.master_config.current_profile != DEFAULT_CONFIG:
|
757
|
+
ttk.Button(profiles_frame, text="Delete Config", command=self.delete_profile).grid(row=self.current_row, column=2, pady=5)
|
758
|
+
|
759
|
+
|
760
|
+
def on_profile_change(self, event):
|
761
|
+
print("profile Changed!")
|
762
|
+
self.save_settings(profile_change=True)
|
763
|
+
self.reload_settings()
|
764
|
+
|
765
|
+
def add_profile(self):
|
766
|
+
new_profile_name = simpledialog.askstring("Input", "Enter new profile name:")
|
767
|
+
if new_profile_name:
|
768
|
+
self.master_config.configs[new_profile_name] = self.master_config.default_config
|
769
|
+
self.profile_combobox['values'] = list(self.master_config.configs.keys())
|
770
|
+
self.profile_combobox.set(new_profile_name)
|
771
|
+
self.save_settings()
|
772
|
+
self.reload_settings()
|
773
|
+
|
774
|
+
def copy_profile(self):
|
775
|
+
source_profile = self.profile_combobox.get()
|
776
|
+
new_profile_name = simpledialog.askstring("Input", "Enter new profile name:")
|
777
|
+
if new_profile_name and source_profile in self.master_config.configs:
|
778
|
+
self.master_config.configs[new_profile_name] = self.master_config.configs[source_profile]
|
779
|
+
self.profile_combobox['values'] = list(self.master_config.configs.keys())
|
780
|
+
self.profile_combobox.set(new_profile_name)
|
781
|
+
self.save_settings()
|
782
|
+
self.reload_settings()
|
783
|
+
|
784
|
+
def delete_profile(self):
|
785
|
+
profile_to_delete = self.profile_combobox.get()
|
786
|
+
if profile_to_delete == "Default":
|
787
|
+
messagebox.showerror("Error", "Cannot delete the Default profile.")
|
788
|
+
return
|
789
|
+
|
790
|
+
if profile_to_delete and profile_to_delete in self.master_config.configs:
|
791
|
+
confirm = messagebox.askyesno("Confirm Delete", f"Are you sure you want to delete the profile '{profile_to_delete}'?")
|
792
|
+
if confirm:
|
793
|
+
del self.master_config.configs[profile_to_delete]
|
794
|
+
self.profile_combobox['values'] = list(self.master_config.configs.keys())
|
795
|
+
self.profile_combobox.set("Default")
|
796
|
+
self.save_settings()
|
797
|
+
self.reload_settings()
|
798
|
+
|
799
|
+
|
800
|
+
if __name__ == '__main__':
|
801
|
+
window = ConfigApp()
|
802
|
+
window.show()
|
803
|
+
window.window.mainloop()
|