GameSentenceMiner 2.7.9__py3-none-any.whl → 2.7.10__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.
@@ -52,7 +52,7 @@ def get_screenshot_for_line(video_file, game_line):
52
52
  return get_screenshot(video_file, get_screenshot_time(video_file, game_line))
53
53
 
54
54
 
55
- def get_screenshot_time(video_path, game_line, default_beginning=False, vad_beginning=None, vad_end=None):
55
+ def get_screenshot_time(video_path, game_line, default_beginning=False, vad_beginning=None, vad_end=None, doing_multi_line=False):
56
56
  if game_line:
57
57
  line_time = game_line.time
58
58
  else:
@@ -68,7 +68,7 @@ def get_screenshot_time(video_path, game_line, default_beginning=False, vad_begi
68
68
  screenshot_offset = get_config().screenshot.seconds_after_line
69
69
 
70
70
  # Calculate screenshot time from the beginning by adding the offset
71
- if vad_beginning and vad_end:
71
+ if vad_beginning and vad_end and not doing_multi_line:
72
72
  logger.debug("Using VAD to determine screenshot time")
73
73
  screenshot_time_from_beginning = line_timestamp_in_video + vad_end - screenshot_offset
74
74
  elif get_config().screenshot.use_new_screenshot_logic:
GameSentenceMiner/gsm.py CHANGED
@@ -141,7 +141,7 @@ class VideoToAudioHandler(FileSystemEventHandler):
141
141
 
142
142
  ss_timing = 1
143
143
  if mined_line and line_cutoff or mined_line and get_config().screenshot.use_beginning_of_line_as_screenshot:
144
- ss_timing = ffmpeg.get_screenshot_time(video_path, mined_line, vad_beginning, vad_end)
144
+ ss_timing = ffmpeg.get_screenshot_time(video_path, mined_line, vad_beginning, vad_end, bool(selected_lines))
145
145
 
146
146
  if get_config().anki.update_anki and last_note:
147
147
  anki.update_anki_card(last_note, note, audio_path=final_audio_output, video_path=video_path,
@@ -23,20 +23,4 @@ class Rectangle:
23
23
  class OCRConfig:
24
24
  scene: str
25
25
  rectangles: List[Rectangle]
26
- window: Optional[str] = None
27
-
28
- # Example of how you might use from_dict (assuming you have a dictionary called 'data')
29
- data = {
30
- "scene": "CODEVEIN",
31
- "window": "CODE VEIN",
32
- "rectangles": [
33
- {
34
- "monitor": {"left": 0, "top": 0, "width": 2560, "height": 1440, "index": 0},
35
- "coordinates": [749, 1178, 1100, 147],
36
- "is_excluded": False,
37
- }
38
- ],
39
- }
40
-
41
- config = OCRConfig.from_dict(data)
42
- print(config)
26
+ window: Optional[str] = None
@@ -88,7 +88,7 @@ class ScreenSelector:
88
88
  serializable_rects.append(rect_data)
89
89
 
90
90
  print(serializable_rects)
91
- with open(config_path, 'w') as f:
91
+ with open(config_path, 'w', encoding="utf-8") as f:
92
92
  json.dump({"scene": sanitize_filename(obs.get_current_scene()), "window": self.window_name, "rectangles": serializable_rects}, f, indent=4)
93
93
  print("Rectangles saved.")
94
94
  self.result['rectangles'] = self.rectangles.copy()
@@ -13,6 +13,7 @@ from tkinter import messagebox
13
13
 
14
14
  import mss
15
15
  import websockets
16
+ from rapidfuzz import fuzz
16
17
  from PIL import Image, ImageDraw
17
18
 
18
19
  from GameSentenceMiner import obs, util
@@ -75,7 +76,7 @@ def get_new_game_cords():
75
76
  return coords_list
76
77
 
77
78
 
78
- def get_ocr_config():
79
+ def get_ocr_config() -> OCRConfig:
79
80
  """Loads and updates screen capture areas from the corresponding JSON file."""
80
81
  app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
81
82
  ocr_config_dir = app_dir / "ocr_config"
@@ -85,7 +86,7 @@ def get_ocr_config():
85
86
  if not config_path.exists():
86
87
  raise Exception(f"No config file found at {config_path}.")
87
88
  try:
88
- with open(config_path, 'r') as f:
89
+ with open(config_path, 'r', encoding="utf-8") as f:
89
90
  config_data = json.load(f)
90
91
  if "rectangles" in config_data and isinstance(config_data["rectangles"], list) and all(
91
92
  isinstance(item, list) and len(item) == 4 for item in config_data["rectangles"]):
@@ -120,7 +121,7 @@ def get_ocr_config():
120
121
  })
121
122
  new_config_data = {"scene": config_data.get("scene", scene), "window": config_data.get("window", None),
122
123
  "rectangles": new_rectangles}
123
- with open(config_path, 'w') as f:
124
+ with open(config_path, 'w', encoding="utf-8") as f:
124
125
  json.dump(new_config_data, f, indent=4)
125
126
  return OCRConfig.from_dict(new_config_data)
126
127
  elif "rectangles" in config_data and isinstance(config_data["rectangles"], list) and all(
@@ -206,63 +207,78 @@ class WebsocketServerThread(threading.Thread):
206
207
  all_cords = None
207
208
  rectangles = None
208
209
 
210
+ def do_second_ocr(ocr1_text, rectangle_index, time, img):
211
+ global twopassocr, ocr2, last_ocr1_results, last_ocr2_results
212
+ last_result = ([], -1)
213
+
214
+ previous_ocr1_text = last_ocr1_results[rectangle_index]
215
+ if fuzz.ratio(previous_ocr1_text, ocr1_text) >= 80:
216
+ logger.info("Seems like the same text, not doing 2nd pass")
217
+ last_ocr1_results[rectangle_index] = ocr1_text
218
+ try:
219
+ orig_text, text = run.process_and_write_results(img, None, None, last_result, TextFiltering(),
220
+ engine=ocr2)
221
+ previous_ocr2_text = last_ocr2_results[rectangle_index]
222
+ if fuzz.ratio(previous_ocr2_text, text) >= 80:
223
+ logger.info("Seems like the same text from previous ocr2 result, not sending")
224
+ return
225
+ last_ocr2_results[rectangle_index] = text
226
+ if get_config().advanced.ocr_sends_to_clipboard:
227
+ import pyperclip
228
+ pyperclip.copy(text)
229
+ websocket_server_thread.send_text(text, time)
230
+ except json.JSONDecodeError:
231
+ print("Invalid JSON received.")
232
+ except Exception as e:
233
+ logger.exception(e)
234
+ print(f"Error processing message: {e}")
235
+
236
+
237
+ last_oneocr_results_to_check = {} # Store last OCR result for each rectangle
238
+ last_oneocr_times = {} # Store last OCR time for each rectangle
239
+ text_stable_start_times = {} # Store the start time when text becomes stable for each rectangle
240
+ TEXT_APPEARENCE_DELAY = get_ocr_scan_rate() * 1000 + 500 # Adjust as needed
209
241
 
210
242
  def text_callback(text, rectangle_index, time, img=None):
211
- global twopassocr, ocr2, last_oneocr_results
243
+ global twopassocr, ocr2, last_oneocr_results_to_check, last_oneocr_times, text_stable_start_times
244
+
245
+ current_time = time if time else datetime.now()
246
+
247
+ previous_text = last_oneocr_results_to_check.get(rectangle_index, "")
248
+
212
249
  if not text:
250
+ if previous_text:
251
+ if rectangle_index in text_stable_start_times:
252
+ stable_time = text_stable_start_times[rectangle_index]
253
+ if twopassocr:
254
+ do_second_ocr(previous_text, rectangle_index, time, img)
255
+ else:
256
+ previous_ocr1_text = last_ocr1_results[rectangle_index]
257
+ if fuzz.ratio(previous_ocr1_text, text) >= 80:
258
+ logger.info("Seems like the same text, not sending")
259
+ last_ocr1_results[rectangle_index] = text
260
+ websocket_server_thread.send_text(previous_text, stable_time)
261
+ del text_stable_start_times[rectangle_index]
262
+ del last_oneocr_results_to_check[rectangle_index]
213
263
  return
214
- if not twopassocr or not ocr2:
215
- websocket_server_thread.send_text(text, time if time else datetime.now())
264
+
265
+ if rectangle_index not in last_oneocr_results_to_check:
266
+ last_oneocr_results_to_check[rectangle_index] = text
267
+ last_oneocr_times[rectangle_index] = current_time
268
+ text_stable_start_times[rectangle_index] = current_time
216
269
  return
217
- with mss.mss() as sct:
218
- line_time = time if time else datetime.now()
219
- logger.info(f"Received message: {text}, ATTEMPTING LENS OCR")
220
- if rectangles:
221
- rect_data: Rectangle = rectangles[rectangle_index]
222
- cords = rect_data.coordinates
223
- i = rectangle_index
224
- else:
225
- i = 0
226
- mon = sct.monitors
227
- cords = [mon[1]['left'], mon[1]['top'], mon[1]['width'], mon[1]['height']]
228
- similarity = difflib.SequenceMatcher(None, last_oneocr_results[i], text).ratio()
229
- if similarity > .8:
230
- return
231
- logger.debug(f"Similarity for region {i}: {similarity}")
232
- last_oneocr_results[i] = text
233
- last_result = ([], -1)
234
- try:
235
- # sct_params = {'left': cords[0], 'top': cords[1], 'width': cords[2], 'height': cords[3]}
236
- # sct_img = sct.grab(sct_params)
237
- # img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
238
- # img = img.convert("RGBA")
239
- # draw = ImageDraw.Draw(img)
240
- # excluded_rects = ocr_config.get("excluded_rectangles", [])
241
- # if isinstance(excluded_rects, list) and all(
242
- # isinstance(item, list) and len(item) == 4 for item in excluded_rects):
243
- # for exclusion in excluded_rects:
244
- # left, top, right, bottom = exclusion
245
- # draw.rectangle((left, top, right, bottom), fill=(0, 0, 0, 0))
246
- # elif isinstance(excluded_rects, list) and all(
247
- # isinstance(item, dict) and "coordinates" in item and isinstance(item["coordinates"], list) and len(
248
- # item["coordinates"]) == 4 for item in excluded_rects):
249
- # for exclusion in excluded_rects:
250
- # left, top, right, bottom = exclusion["coordinates"]
251
- # draw.rectangle((left, top, right, bottom), fill=(0, 0, 0, 0))
252
- orig_text, text = run.process_and_write_results(img, None, None, last_result, TextFiltering(),
253
- engine=ocr2)
254
- if ":gsm_prefix:" in text:
255
- text = text.split(":gsm_prefix:")[1]
256
- if get_config().advanced.ocr_sends_to_clipboard:
257
- import pyperclip
258
- pyperclip.copy(text)
259
- websocket_server_thread.send_text(text, line_time)
260
- except json.JSONDecodeError:
261
- print("Invalid JSON received.")
262
- except Exception as e:
263
- logger.exception(e)
264
- print(f"Error processing message: {e}")
265
270
 
271
+ stable = text_stable_start_times.get(rectangle_index)
272
+
273
+ if stable:
274
+ time_since_stable_ms = int((current_time - stable).total_seconds() * 1000)
275
+
276
+ if time_since_stable_ms >= TEXT_APPEARENCE_DELAY:
277
+ last_oneocr_results_to_check[rectangle_index] = text
278
+ last_oneocr_times[rectangle_index] = current_time
279
+ else:
280
+ last_oneocr_results_to_check[rectangle_index] = text
281
+ last_oneocr_times[rectangle_index] = current_time
266
282
 
267
283
  done = False
268
284
 
@@ -305,12 +321,15 @@ if __name__ == "__main__":
305
321
  twopassocr = False
306
322
  else:
307
323
  ocr1 = "oneocr"
324
+ ocr2 = "glens"
325
+ twopassocr = True
308
326
  logger.info(f"Received arguments: ocr1={ocr1}, ocr2={ocr2}, twopassocr={twopassocr}")
309
327
  global ocr_config
310
328
  ocr_config: OCRConfig = get_ocr_config()
311
329
  if ocr_config and ocr_config.rectangles:
312
330
  rectangles = ocr_config.rectangles
313
- last_oneocr_results = [""] * len(rectangles) if rectangles else [""]
331
+ last_ocr1_results = [""] * len(rectangles) if rectangles else [""]
332
+ last_ocr2_results = [""] * len(rectangles) if rectangles else [""]
314
333
  oneocr_threads = []
315
334
  run.init_config(False)
316
335
  if rectangles:
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  import os
3
3
  import tkinter as tk
4
- from tkinter import ttk
4
+ from tkinter import ttk, Scrollbar
5
5
 
6
6
  from GameSentenceMiner import obs
7
7
  from GameSentenceMiner.configuration import logger, get_app_directory, get_config
@@ -13,19 +13,19 @@ class UtilityApp:
13
13
  self.items = []
14
14
  self.play_audio_buttons = []
15
15
  self.get_screenshot_buttons = []
16
- self.checkboxes = []
16
+ self.checkbox_vars = []
17
17
  self.multi_mine_window = None # Store the multi-mine window reference
18
18
  self.checkbox_frame = None
19
+ self.canvas = None
20
+ self.scrollbar = None
19
21
  self.line_for_audio = None
20
22
  self.line_for_screenshot = None
21
- self.line_counter = 0
22
23
 
23
24
  style = ttk.Style()
24
25
  style.configure("TCheckbutton", font=("Arial", 20)) # Change the font and size
25
26
  self.config_file = os.path.join(get_app_directory(), "multi-mine-window-config.json")
26
27
  self.load_window_config()
27
28
 
28
-
29
29
  def save_window_config(self):
30
30
  if self.multi_mine_window:
31
31
  config = {
@@ -58,20 +58,33 @@ class UtilityApp:
58
58
  self.multi_mine_window.title("Multi Mine Window")
59
59
 
60
60
  self.multi_mine_window.geometry(f"{self.window_width}x{self.window_height}+{self.window_x}+{self.window_y}")
61
-
62
61
  self.multi_mine_window.minsize(800, 400)
63
62
 
64
- self.checkbox_frame = ttk.Frame(self.multi_mine_window)
65
- self.checkbox_frame.pack(padx=10, pady=10, fill="both", expand=True)
63
+ self.canvas = tk.Canvas(self.multi_mine_window)
64
+ self.scrollbar = Scrollbar(self.multi_mine_window, orient="vertical", command=self.canvas.yview)
65
+ self.checkbox_frame = ttk.Frame(self.canvas)
66
+
67
+ self.canvas.configure(yscrollcommand=self.scrollbar.set)
68
+
69
+ self.scrollbar.pack(side="right", fill="y")
70
+ self.canvas.pack(side="left", fill="both", expand=True)
71
+ self.canvas.create_window((0, 0), window=self.checkbox_frame, anchor="nw")
72
+
73
+ self.checkbox_frame.bind("<Configure>", self.on_frame_configure)
66
74
 
67
75
  for line, var in self.items:
68
76
  self.add_checkbox_to_gui(line, var)
77
+ self.scroll_to_bottom()
69
78
 
70
79
  self.multi_mine_window.protocol("WM_DELETE_WINDOW", self.on_close)
71
80
  else:
72
81
  self.multi_mine_window.deiconify()
73
82
  self.multi_mine_window.lift()
74
83
 
84
+ def on_frame_configure(self, event):
85
+ """Reset the scroll region to encompass the inner frame"""
86
+ self.canvas.configure(scrollregion=self.canvas.bbox("all"))
87
+
75
88
  def on_close(self):
76
89
  self.save_window_config()
77
90
  self.multi_mine_window.withdraw()
@@ -81,48 +94,44 @@ class UtilityApp:
81
94
  try:
82
95
  var = tk.BooleanVar()
83
96
  self.items.append((line, var))
97
+ if self.multi_mine_window and tk.Toplevel.winfo_exists(self.multi_mine_window):
98
+ self.add_checkbox_to_gui(line, var)
99
+ self.scroll_to_bottom()
84
100
  except Exception as e:
85
101
  logger.error(f"NOT AN ERROR: Attempted to add text to multi-mine window, before it was initialized: {e}")
86
102
  return
87
103
 
88
- if len(self.items) > 10:
89
- if self.checkboxes:
90
- self.checkboxes[0].destroy()
91
- self.checkboxes.pop(0)
92
- self.items.pop(0)
93
- if self.play_audio_buttons:
94
- self.play_audio_buttons[0].destroy()
95
- self.play_audio_buttons.pop(0)
96
- if self.get_screenshot_buttons:
97
- self.get_screenshot_buttons[0].destroy()
98
- self.get_screenshot_buttons.pop(0)
99
-
100
- if self.multi_mine_window and tk.Toplevel.winfo_exists(self.multi_mine_window):
101
- self.add_checkbox_to_gui(line, var)
102
104
  self.line_for_audio = None
103
105
  self.line_for_screenshot = None
104
106
 
105
107
  def add_checkbox_to_gui(self, line, var):
106
108
  """ Add a single checkbox without repainting everything. """
107
109
  if self.checkbox_frame:
110
+ row = len(self.checkbox_vars)
108
111
  column = 0
112
+
109
113
  if get_config().advanced.show_screenshot_buttons:
110
114
  get_screenshot_button = ttk.Button(self.checkbox_frame, text="📸", command=lambda: self.take_screenshot(line))
111
- get_screenshot_button.grid(row=self.line_counter, column=column, sticky='w', padx=5)
112
- self.get_screenshot_buttons.append(get_screenshot_button)
115
+ get_screenshot_button.grid(row=row, column=column, sticky='w', padx=5)
113
116
  column += 1
114
117
 
115
118
  if get_config().advanced.video_player_path or get_config().advanced.audio_player_path:
116
119
  play_audio_button = ttk.Button(self.checkbox_frame, text="🔊", command=lambda: self.play_audio(line))
117
- play_audio_button.grid(row=self.line_counter, column=column, sticky='w', padx=5)
118
- self.play_audio_buttons.append(play_audio_button)
120
+ play_audio_button.grid(row=row, column=column, sticky='w', padx=5)
119
121
  column += 1
120
122
 
121
123
  chk = ttk.Checkbutton(self.checkbox_frame, text=f"{line.time.strftime('%H:%M:%S')} - {line.text}", variable=var)
122
- chk.grid(row=self.line_counter, column=column, sticky='w', padx=5)
123
- self.checkboxes.append(chk)
124
+ chk.grid(row=row, column=column, sticky='w', padx=5)
125
+ self.checkbox_vars.append(var)
126
+
127
+ # Update scroll region after adding a new item
128
+ self.checkbox_frame.update_idletasks()
129
+ self.canvas.config(scrollregion=self.canvas.bbox("all"))
124
130
 
125
- self.line_counter += 1
131
+
132
+ def scroll_to_bottom(self):
133
+ """Scroll the canvas to the bottom"""
134
+ self.canvas.yview_moveto(1.0)
126
135
 
127
136
 
128
137
  def play_audio(self, line):
@@ -134,25 +143,16 @@ class UtilityApp:
134
143
  obs.save_replay_buffer()
135
144
 
136
145
 
137
- # def update_multi_mine_window(self):
138
- # for widget in self.multi_mine_window.winfo_children():
139
- # widget.destroy()
140
- #
141
- # for i, (text, var, time) in enumerate(self.items):
142
- # time: datetime
143
- # chk = ttk.Checkbutton(self.checkbox_frame, text=f"{time.strftime('%H:%M:%S')} - {text}", variable=var)
144
- # chk.pack(anchor='w')
145
-
146
146
  def get_selected_lines(self):
147
- filtered_items = [line for line, var in self.items if var.get()]
147
+ filtered_items = [line for (line, _), var in zip(self.items, self.checkbox_vars) if var.get()]
148
148
  return filtered_items if len(filtered_items) > 0 else []
149
149
 
150
150
 
151
151
  def get_next_line_timing(self):
152
- selected_lines = [line for line, var in self.items if var.get()]
152
+ selected_lines = [line for (line, _), var in zip(self.items, self.checkbox_vars) if var.get()]
153
153
 
154
154
  if len(selected_lines) >= 2:
155
- last_checked_index = max(i for i, (_, var) in enumerate(self.items) if var.get())
155
+ last_checked_index = max(i for i, var in enumerate(self.checkbox_vars) if var.get())
156
156
 
157
157
  if last_checked_index + 1 < len(self.items):
158
158
  next_time = self.items[last_checked_index + 1][0].time
@@ -167,30 +167,14 @@ class UtilityApp:
167
167
 
168
168
 
169
169
  def lines_selected(self):
170
- filter_times = [line.time for line, var in self.items if var.get()]
170
+ filter_times = [line.time for (line, _), var in zip(self.items, self.checkbox_vars) if var.get()]
171
171
  if len(filter_times) > 0:
172
172
  return True
173
173
  return False
174
174
 
175
- # def validate_checkboxes(self, *args):
176
- # logger.debug("Validating checkboxes")
177
- # found_checked = False
178
- # found_unchecked = False
179
- # for _, var in self.items:
180
- # if var.get():
181
- # if found_unchecked:
182
- # messagebox.showinfo("Invalid", "Can only select neighboring checkboxes.")
183
- # break
184
- # found_checked = True
185
- # if found_checked and not var.get():
186
- # found_unchecked = True
187
-
188
175
  def reset_checkboxes(self):
189
- for _, var in self.items:
176
+ for var in self.checkbox_vars:
190
177
  var.set(False)
191
- # if self.multi_mine_window:
192
- # for checkbox in self.checkboxes:
193
- # checkbox.set(False)
194
178
 
195
179
 
196
180
  def init_utility_window(root):
@@ -207,4 +191,14 @@ utility_window: UtilityApp = None
207
191
  if __name__ == "__main__":
208
192
  root = tk.Tk()
209
193
  app = UtilityApp(root)
210
- root.mainloop()
194
+
195
+ # Simulate adding a lot of lines
196
+ import datetime
197
+ now = datetime.datetime.now()
198
+ for i in range(100):
199
+ from GameSentenceMiner.gametext import GameLine
200
+ line = GameLine(f"This is line {i}", now + datetime.timedelta(seconds=i), prev=None, next=None)
201
+ app.add_text(line)
202
+ app.show()
203
+
204
+ root.mainloop()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.7.9
3
+ Version: 2.7.10
4
4
  Summary: A tool for mining sentences from games. Update: Multi-Line Mining! Fixed!
5
5
  Author-email: Beangate <bpwhelan95@gmail.com>
6
6
  License: MIT License
@@ -3,15 +3,15 @@ GameSentenceMiner/anki.py,sha256=9E9GRR2zylW3Gp4PNlwYS_Nn-mhojZkjFqfYlTazte8,140
3
3
  GameSentenceMiner/config_gui.py,sha256=-1PanqdtTKiwItxeyt0piXrWo7lGMWwrC4iSo4NiPz4,67521
4
4
  GameSentenceMiner/configuration.py,sha256=TIL8yCr-FOScCA4OJt-BAtjEb50wtqFFrpmN-UlaQh4,20405
5
5
  GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
6
- GameSentenceMiner/ffmpeg.py,sha256=iUj-1uLLJla6jjGDKuc1FbcXjMogYpoDhrQqCbQFcWA,13359
6
+ GameSentenceMiner/ffmpeg.py,sha256=Du31elvSmcbfeNlx_TvqkbkmSfXCHCf4mfklBt5rLaU,13408
7
7
  GameSentenceMiner/gametext.py,sha256=AAke4swwmN16da0IpyL5xMhU23nTz_c6z2kMfXPYv-8,9194
8
- GameSentenceMiner/gsm.py,sha256=fcczDPDcC0vDLAPZtIY64mU3Di3RZjR40tuLIiwB5lE,24827
8
+ GameSentenceMiner/gsm.py,sha256=aTJho7V9WTup7kxwMrpwCEXg1u_rsPn2XaRzClK2qI0,24849
9
9
  GameSentenceMiner/model.py,sha256=JdnkT4VoPOXmOpRgFdvERZ09c9wLN6tUJxdrKlGZcqo,5305
10
10
  GameSentenceMiner/notification.py,sha256=FY39ChSRK0Y8TQ6lBGsLnpZUFPtFpSy2tweeXVoV7kc,2809
11
11
  GameSentenceMiner/obs.py,sha256=3h1hh868zdXQFGXJ7_mQZs5kcudEMa3yBOrbCVckhCs,9139
12
12
  GameSentenceMiner/package.py,sha256=YlS6QRMuVlm6mdXx0rlXv9_3erTGS21jaP3PNNWfAH0,1250
13
13
  GameSentenceMiner/util.py,sha256=W49gqYlhbzZe-17zHMFcAjNeeteXrv_USHT7aBDKSqM,6825
14
- GameSentenceMiner/utility_gui.py,sha256=H4aOddlsrVR768RwbMzYScCziuOz1JeySUigNrPlaac,7692
14
+ GameSentenceMiner/utility_gui.py,sha256=JcNjk9Wrz_Au7P4ITslSBqXBU-o6nA2sc2vJvglAk4o,7432
15
15
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  GameSentenceMiner/ai/gemini.py,sha256=6kTQPuRH16D-1srhrWa5uPGIy-jBqNm9eRdKBSbpiXA,5423
17
17
  GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
@@ -21,10 +21,10 @@ GameSentenceMiner/downloader/Untitled_json.py,sha256=RUUl2bbbCpUDUUS0fP0tdvf5Fng
21
21
  GameSentenceMiner/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  GameSentenceMiner/downloader/download_tools.py,sha256=mI1u_FGBmBqDIpCH3jOv8DOoZ3obgP5pIf9o9SVfX2Q,8131
23
23
  GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=UnkAqn_9EoJlzElHLjKYjNsDBM_MnTlsSshqbRAlXIg,890
24
+ GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=FZ3CCgtpVawJ2mq3EP0KheJjYte5PlyOO1VnGND-__Y,467
25
25
  GameSentenceMiner/ocr/ocrconfig.py,sha256=hTROOZ3On2HngXKxwQFZvnr5AxlmlMV0mPxv-F3NbMg,6476
26
- GameSentenceMiner/ocr/owocr_area_selector.py,sha256=fpPE6FOFSSfgc2feur8Czve91Jl1zBG_nwUSRY6lTbg,11934
27
- GameSentenceMiner/ocr/owocr_helper.py,sha256=_mcjF9TA9GrselOzJQdtXhImj8nlLGih9TIiuXsaEdo,14089
26
+ GameSentenceMiner/ocr/owocr_area_selector.py,sha256=MggSnJSUQhs7SD6YTKRNnVhEZEliLgaUTOPkBUCjAss,11952
27
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=wU6zXr27XzH8cw2xkAJ6HtFSPRVpWOWGuhpC775NMQQ,14742
28
28
  GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
29
29
  GameSentenceMiner/owocr/owocr/__main__.py,sha256=r8MI6RAmbkTWqOJ59uvXoDS7CSw5jX5war9ULGWELrA,128
30
30
  GameSentenceMiner/owocr/owocr/config.py,sha256=738QCJHEWpFhMh966plOcXYWwcshSiRsxjjIwldeTtI,7461
@@ -36,9 +36,9 @@ GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
36
36
  GameSentenceMiner/vad/silero_trim.py,sha256=ULf3zwS-JMsY82cKF7gZxREHw8L6lgpWF2U1YqgE9Oc,1681
37
37
  GameSentenceMiner/vad/vosk_helper.py,sha256=125X8C9NxFPlWWpoNsbOnEqKx8RCjXN109zNx_QXhyg,6070
38
38
  GameSentenceMiner/vad/whisper_helper.py,sha256=JJ-iltCh813XdjyEw0Wn5DaErf6PDqfH0Efu1Md8cIY,3543
39
- gamesentenceminer-2.7.9.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
40
- gamesentenceminer-2.7.9.dist-info/METADATA,sha256=hFDJEaZNflRL93x_WRMZwDxjGSnO3ylogfdij2ZwVhs,5839
41
- gamesentenceminer-2.7.9.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
42
- gamesentenceminer-2.7.9.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
43
- gamesentenceminer-2.7.9.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
44
- gamesentenceminer-2.7.9.dist-info/RECORD,,
39
+ gamesentenceminer-2.7.10.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
40
+ gamesentenceminer-2.7.10.dist-info/METADATA,sha256=mfPx9X0zpeSfjd_nLr3ZXx-twqQE-BjS7h6YFMIRMlw,5840
41
+ gamesentenceminer-2.7.10.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
42
+ gamesentenceminer-2.7.10.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
43
+ gamesentenceminer-2.7.10.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
44
+ gamesentenceminer-2.7.10.dist-info/RECORD,,