GameSentenceMiner 2.8.12__py3-none-any.whl → 2.8.14__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.
@@ -175,13 +175,21 @@ current_ai_config: Ai | None = None
175
175
 
176
176
  def get_ai_prompt_result(lines: List[GameLine], sentence: str, current_line: GameLine, game_title: str = ""):
177
177
  global ai_manager, current_ai_config
178
- if not ai_manager or get_config().ai != current_ai_config:
179
- if get_config().ai.provider == AIType.GEMINI.value:
180
- ai_manager = GeminiAI(model=get_config().ai.gemini_model, api_key=get_config().ai.gemini_api_key, logger=logger)
181
- elif get_config().ai.provider == AIType.GROQ.value:
182
- ai_manager = GroqAI(model=get_config().ai.groq_model, api_key=get_config().ai.groq_api_key, logger=logger)
183
- current_ai_config = get_config().ai
184
- return ai_manager.process(lines, sentence, current_line, game_title)
178
+ try:
179
+ if not ai_manager or get_config().ai != current_ai_config:
180
+ if get_config().ai.provider == AIType.GEMINI.value:
181
+ ai_manager = GeminiAI(model=get_config().ai.gemini_model, api_key=get_config().ai.gemini_api_key, logger=logger)
182
+ elif get_config().ai.provider == AIType.GROQ.value:
183
+ ai_manager = GroqAI(model=get_config().ai.groq_model, api_key=get_config().ai.groq_api_key, logger=logger)
184
+ current_ai_config = get_config().ai
185
+ if not ai_manager:
186
+ logger.error("AI is enabled but the AI Manager did not initialize. Check your AI Config IN GSM.")
187
+ return ""
188
+ return ai_manager.process(lines, sentence, current_line, game_title)
189
+ except Exception as e:
190
+ logger.info("Error caught while trying to get AI prompt result. Check logs for more details.")
191
+ logger.debug(e)
192
+ return ""
185
193
 
186
194
  if __name__ == '__main__':
187
195
  lines = [
GameSentenceMiner/anki.py CHANGED
@@ -282,7 +282,7 @@ def update_new_card():
282
282
  texthooking_page.reset_checked_lines()
283
283
  else:
284
284
  logger.info("New card(s) detected! Added to Processing Queue!")
285
- card_queue.append(last_card)
285
+ card_queue.append((last_card, datetime.now()))
286
286
  try:
287
287
  obs.save_replay_buffer()
288
288
  except Exception as e:
@@ -203,6 +203,7 @@ class ConfigApp:
203
203
  multi_line_line_break=self.multi_line_line_break.get(),
204
204
  multi_line_sentence_storage_field=self.multi_line_sentence_storage_field.get(),
205
205
  ocr_sends_to_clipboard=self.ocr_sends_to_clipboard.get(),
206
+ use_anki_note_creation_time=self.use_anki_note_creation_time.get(),
206
207
  ),
207
208
  ai=Ai(
208
209
  enabled=self.ai_enabled.get(),
@@ -997,6 +998,12 @@ class ConfigApp:
997
998
  self.add_label_and_increment_row(advanced_frame, "Enable to send OCR results to clipboard.", row=self.current_row, column=2)
998
999
 
999
1000
 
1001
+ ttk.Label(advanced_frame, text="Use Anki Creation Date for Audio Timing:").grid(row=self.current_row, column=0, sticky='W')
1002
+ self.use_anki_note_creation_time = tk.BooleanVar(value=self.settings.advanced.use_anki_note_creation_time)
1003
+ ttk.Checkbutton(advanced_frame, variable=self.use_anki_note_creation_time).grid(row=self.current_row, column=1, sticky='W')
1004
+ self.add_label_and_increment_row(advanced_frame, "Use the Anki note creation date for audio timing instead of the OBS replay time.", row=self.current_row, column=2)
1005
+
1006
+
1000
1007
 
1001
1008
  @new_tab
1002
1009
  def create_ai_tab(self):
@@ -181,6 +181,7 @@ class Advanced:
181
181
  multi_line_line_break: str = '<br>'
182
182
  multi_line_sentence_storage_field: str = ''
183
183
  ocr_sends_to_clipboard: bool = True
184
+ use_anki_note_creation_time: bool = False
184
185
 
185
186
  @dataclass_json
186
187
  @dataclass
@@ -200,7 +201,10 @@ class Ai:
200
201
  def __post_init__(self):
201
202
  if not self.gemini_api_key:
202
203
  self.gemini_api_key = self.api_key
203
-
204
+ if self.provider == 'gemini':
205
+ self.provider = AI_GEMINI
206
+ if self.provider == 'groq':
207
+ self.provider = AI_GROQ
204
208
 
205
209
  @dataclass_json
206
210
  @dataclass
@@ -57,6 +57,8 @@ def get_screenshot_time(video_path, game_line, default_beginning=False, vad_begi
57
57
  # Assuming initial_time is defined elsewhere if game_line is None
58
58
  line_time = initial_time
59
59
 
60
+ logger.info("Calculating screenshot time for line: " + str(game_line.text))
61
+
60
62
  file_length = get_video_duration(video_path)
61
63
  file_mod_time = get_file_modification_time(video_path)
62
64
 
@@ -150,7 +152,7 @@ def get_audio_codec(video_path):
150
152
  return None
151
153
 
152
154
 
153
- def get_audio_and_trim(video_path, game_line, next_line_time):
155
+ def get_audio_and_trim(video_path, game_line, next_line_time, anki_card_creation_time):
154
156
  supported_formats = {
155
157
  'opus': 'libopus',
156
158
  'mp3': 'libmp3lame',
@@ -184,7 +186,7 @@ def get_audio_and_trim(video_path, game_line, next_line_time):
184
186
 
185
187
  subprocess.run(command)
186
188
 
187
- return trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_line_time)
189
+ return trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_line_time, anki_card_creation_time)
188
190
 
189
191
 
190
192
  def get_video_duration(file_path):
@@ -202,10 +204,10 @@ def get_video_duration(file_path):
202
204
  return float(duration_info["format"]["duration"]) # Return the duration in seconds
203
205
 
204
206
 
205
- def trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_line):
207
+ def trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_line, anki_card_creation_time):
206
208
  trimmed_audio = tempfile.NamedTemporaryFile(dir=configuration.get_temporary_directory(),
207
209
  suffix=f".{get_config().audio.extension}").name
208
- start_trim_time, total_seconds, total_seconds_after_offset = get_video_timings(video_path, game_line)
210
+ start_trim_time, total_seconds, total_seconds_after_offset = get_video_timings(video_path, game_line, anki_card_creation_time)
209
211
 
210
212
  ffmpeg_command = ffmpeg_base_command_list + [
211
213
  "-i", untrimmed_audio,
@@ -232,8 +234,11 @@ def trim_audio_based_on_last_line(untrimmed_audio, video_path, game_line, next_l
232
234
  logger.info(f"Audio trimmed and saved to {trimmed_audio}")
233
235
  return trimmed_audio
234
236
 
235
- def get_video_timings(video_path, game_line):
236
- file_mod_time = get_file_modification_time(video_path)
237
+ def get_video_timings(video_path, game_line, anki_card_creation_time):
238
+ if anki_card_creation_time and get_config().advanced.use_anki_note_creation_time:
239
+ file_mod_time = anki_card_creation_time
240
+ else:
241
+ file_mod_time = get_file_modification_time(video_path)
237
242
  file_length = get_video_duration(video_path)
238
243
  time_delta = file_mod_time - game_line.time
239
244
  # Convert time_delta to FFmpeg-friendly format (HH:MM:SS.milliseconds)
GameSentenceMiner/gsm.py CHANGED
@@ -90,8 +90,9 @@ class VideoToAudioHandler(FileSystemEventHandler):
90
90
  return
91
91
  try:
92
92
  last_note = None
93
+ anki_card_creation_time = None
93
94
  if anki.card_queue and len(anki.card_queue) > 0:
94
- last_note = anki.card_queue.pop(0)
95
+ last_note, anki_card_creation_time = anki.card_queue.pop(0)
95
96
  with util.lock:
96
97
  util.set_last_mined_line(anki.get_sentence(last_note))
97
98
  if os.path.exists(video_path) and os.access(video_path, os.R_OK):
@@ -136,7 +137,8 @@ class VideoToAudioHandler(FileSystemEventHandler):
136
137
  final_audio_output, should_update_audio, vad_trimmed_audio, vad_beginning, vad_end = VideoToAudioHandler.get_audio(
137
138
  start_line,
138
139
  line_cutoff,
139
- video_path)
140
+ video_path,
141
+ anki_card_creation_time)
140
142
  else:
141
143
  final_audio_output = ""
142
144
  should_update_audio = False
@@ -148,7 +150,7 @@ class VideoToAudioHandler(FileSystemEventHandler):
148
150
  elif not get_config().anki.sentence_audio_field:
149
151
  logger.info("No SentenceAudio Field in config, skipping audio processing!")
150
152
 
151
- ss_timing = ffmpeg.get_screenshot_time(video_path, mined_line, vad_beginning, vad_end, bool(selected_lines))
153
+ ss_timing = ffmpeg.get_screenshot_time(video_path, mined_line, vad_beginning=vad_beginning, vad_end=vad_end, doing_multi_line=bool(selected_lines))
152
154
 
153
155
  if get_config().anki.update_anki and last_note:
154
156
  anki.update_anki_card(last_note, note, audio_path=final_audio_output, video_path=video_path,
@@ -171,8 +173,8 @@ class VideoToAudioHandler(FileSystemEventHandler):
171
173
 
172
174
 
173
175
  @staticmethod
174
- def get_audio(game_line, next_line_time, video_path, temporary=False):
175
- trimmed_audio = get_audio_and_trim(video_path, game_line, next_line_time)
176
+ def get_audio(game_line, next_line_time, video_path, anki_card_creation_time,temporary=False):
177
+ trimmed_audio = get_audio_and_trim(video_path, game_line, next_line_time, anki_card_creation_time)
176
178
  if temporary:
177
179
  return trimmed_audio
178
180
  vad_trimmed_audio = make_unique_file_name(
@@ -43,6 +43,7 @@ class OCRConfig:
43
43
  coordinate_system: str = None
44
44
  window_geometry: Optional[WindowGeometry] = None
45
45
  window: Optional[str] = None
46
+ language: str = "ja"
46
47
 
47
48
  def __post_init__(self):
48
49
  if self.coordinate_system and self.coordinate_system == "percentage" and self.window:
@@ -320,7 +320,7 @@ def run_oneocr(ocr_config: OCRConfig, i, area=False):
320
320
  text_callback=text_callback,
321
321
  screen_capture_exclusions=exclusions,
322
322
  rectangle=i,
323
- language="ja")
323
+ language=language)
324
324
  done = True
325
325
 
326
326
 
@@ -339,23 +339,27 @@ def get_window(window_name):
339
339
  return None
340
340
 
341
341
  if __name__ == "__main__":
342
- global ocr1, ocr2, twopassocr
342
+ global ocr1, ocr2, twopassocr, language
343
343
  import sys
344
344
 
345
345
  args = sys.argv[1:]
346
- if len(args) == 3:
347
- ocr1 = args[0]
348
- ocr2 = args[1]
349
- twopassocr = bool(int(args[2]))
350
- elif len(args) == 2:
351
- ocr1 = args[0]
352
- ocr2 = args[1]
346
+ if len(args) == 4:
347
+ language = args[0]
348
+ ocr1 = args[1]
349
+ ocr2 = args[2]
350
+ twopassocr = bool(int(args[3]))
351
+ elif len(args) == 3:
352
+ language = args[0]
353
+ ocr1 = args[1]
354
+ ocr2 = args[2]
353
355
  twopassocr = True
354
- elif len(args) == 1:
355
- ocr1 = args[0]
356
+ elif len(args) == 2:
357
+ language = args[0]
358
+ ocr1 = args[1]
356
359
  ocr2 = None
357
360
  twopassocr = False
358
361
  else:
362
+ language = "ja"
359
363
  ocr1 = "oneocr"
360
364
  ocr2 = "glens"
361
365
  twopassocr = True
GameSentenceMiner/util.py CHANGED
@@ -58,7 +58,7 @@ def timedelta_to_ffmpeg_friendly_format(td_obj):
58
58
 
59
59
 
60
60
  def get_file_modification_time(file_path):
61
- mod_time_epoch = os.path.getctime(file_path)
61
+ mod_time_epoch = os.path.getmtime(file_path)
62
62
  mod_time = datetime.fromtimestamp(mod_time_epoch)
63
63
  return mod_time
64
64
 
@@ -255,12 +255,12 @@ os.makedirs(os.path.dirname(TEXT_REPLACEMENTS_FILE), exist_ok=True)
255
255
 
256
256
  import urllib.request
257
257
 
258
- if not os.path.exists(OCR_REPLACEMENTS_FILE):
259
- url = "https://raw.githubusercontent.com/bpwhelan/GameSentenceMiner/refs/heads/main/electron-src/assets/ocr_replacements.json"
260
- try:
261
- with urllib.request.urlopen(url) as response:
262
- data = response.read().decode('utf-8')
263
- with open(OCR_REPLACEMENTS_FILE, 'w', encoding='utf-8') as f:
264
- f.write(data)
265
- except Exception as e:
266
- logger.error(f"Failed to fetch JSON from {url}: {e}")
258
+ # if not os.path.exists(OCR_REPLACEMENTS_FILE):
259
+ # url = "https://raw.githubusercontent.com/bpwhelan/GameSentenceMiner/refs/heads/main/electron-src/assets/ocr_replacements.json"
260
+ # try:
261
+ # with urllib.request.urlopen(url) as response:
262
+ # data = response.read().decode('utf-8')
263
+ # with open(OCR_REPLACEMENTS_FILE, 'w', encoding='utf-8') as f:
264
+ # f.write(data)
265
+ # except Exception as e:
266
+ # logger.error(f"Failed to fetch JSON from {url}: {e}")
@@ -107,26 +107,30 @@
107
107
  </style>
108
108
  </head>
109
109
  <body>
110
+ <div style="position: fixed; top: 20px; right: 20px; display: flex; gap: 10px;">
111
+ <button onclick="window.location.href='/textreplacements'" style="background-color: #1a73e8; color: #ffffff; border: none; padding: 10px 20px; font-size: 12px; cursor: pointer; transition: background-color 0.3s; border-radius: 5px;">
112
+ Text Replacements
113
+ </button>
114
+ <button id="delete-history" style="background-color: #1a73e8; color: #ffffff; border: none; padding: 10px 20px; font-size: 12px; cursor: pointer; transition: background-color 0.3s; border-radius: 5px;">
115
+ Clear History
116
+ </button>
117
+ </div>
110
118
  <div id="initial-events">
111
119
 
112
120
  </div>
113
121
  <hr class="initial-events-separator" id="initial-events-separator" style="display: none;">
114
122
  <div id="session-events">
115
123
 
116
- </div>
117
- <div>
118
- <button onclick="window.location.href='/textreplacements'" style="margin-top: 20px; background-color: #1a73e8; color: #ffffff; border: none; padding: 10px 20px; font-size: 16px; cursor: pointer; transition: background-color 0.3s; border-radius: 5px;">
119
- Text Replacements
120
- </button>
121
124
  </div>
122
125
  <script>
123
126
  let mainStyle = document.querySelector('head style');
127
+ let deleteHistoryButton = document.getElementById('delete-history');
124
128
  console.log(mainStyle);
125
129
  let displayedEventIds = new Set();
126
130
  let isTabActive = true;
127
131
  let isFetching = false; // Flag to track if a fetch is in progress
128
132
  let intervalId = 0;
129
- const fetchInterval = 5000; // Define the interval as a constant
133
+ const fetchInterval = 100; // Define the interval as a constant
130
134
  const websocketPort = {{ websocket_port }} || 55001;
131
135
 
132
136
  // Drag selection variables
@@ -135,11 +139,13 @@
135
139
  let newCheckboxState = false;
136
140
  let hoveredCheckboxes = new Set();
137
141
  let checkboxes = []; // Will hold all checkbox elements
142
+ let checkboxMap = {};
138
143
  let checkboxes_being_updated = new Set();
139
144
 
140
145
  // Shift click selection variable
141
146
  let lastChecked = null;
142
147
 
148
+
143
149
  async function fetchEvents() {
144
150
  if (document.hidden || isFetching) {
145
151
  return;
@@ -161,12 +167,17 @@
161
167
  addNewEvent(ev)
162
168
  }
163
169
  }
164
- const checkbox = document.querySelector(`[data-event-id="${ev.id}"]`);
165
- if (checkbox && !checkboxes_being_updated.has(ev.id)) {
166
- checkbox.checked = ev.checked;
170
+ if (!ev.history) {
171
+ console.log(checkboxMap[ev.id])
172
+ if (!checkboxes_being_updated.has(ev.id)) {
173
+ const checkbox = checkboxMap[ev.id];
174
+ if (checkbox) {
175
+ checkbox.checked = ev.checked;
176
+ }
177
+ }
167
178
  }
168
179
  });
169
- checkboxes = Array.from(document.querySelectorAll('#session-events input[type="checkbox"]')); // Update checkboxes array after new events
180
+ // checkboxes = Array.from(document.querySelectorAll('#session-events input[type="checkbox"]')); // Update checkboxes array after new events
170
181
  } catch (error) {
171
182
  console.error("Error fetching events:", error);
172
183
  } finally {
@@ -196,7 +207,6 @@
196
207
  container.appendChild(div);
197
208
  window.scrollTo({
198
209
  top: document.documentElement.scrollHeight,
199
- behavior: 'smooth'
200
210
  });
201
211
  }
202
212
 
@@ -235,10 +245,13 @@
235
245
  shadowRoot.appendChild(style);
236
246
  shadowRoot.appendChild(wrapper);
237
247
 
248
+ let checkbox = shadowRoot.querySelector('.multi-line-checkbox')
249
+ checkboxes.push(checkbox);
250
+ checkboxMap[event.id] = checkbox; // Store the checkbox in the map for easy access
251
+
238
252
  container.appendChild(div);
239
253
  window.scrollTo({
240
254
  top: document.documentElement.scrollHeight,
241
- behavior: 'smooth'
242
255
  });
243
256
  }
244
257
 
@@ -344,10 +357,34 @@
344
357
  lastChecked = e.target;
345
358
  }
346
359
 
360
+ function deleteHistory(e) {
361
+ e.preventDefault();
362
+ if (confirm("Are you sure you want to delete the history? This action cannot be undone.")) {
363
+ fetch('/clear_history', {
364
+ method: 'POST',
365
+ headers: { 'Content-Type': 'application/json' }
366
+ })
367
+ .then(response => {
368
+ if (!response.ok) {
369
+ throw new Error(`HTTP error! Status: ${response.status}`);
370
+ }
371
+ // Clear the displayed events
372
+ displayedEventIds.clear();
373
+ document.getElementById('initial-events').innerHTML = '';
374
+ document.getElementById('session-events').innerHTML = '';
375
+ document.getElementById('initial-events-separator').style.display = 'none';
376
+ })
377
+ .catch(error => {
378
+ console.error("Error deleting history:", error);
379
+ });
380
+ }
381
+ }
382
+
347
383
  document.addEventListener('mousedown', handleMouseDown);
348
384
  document.addEventListener('mouseup', handleMouseUp);
349
385
  document.addEventListener('mouseover', handleMouseOver);
350
386
  document.addEventListener('click', handleCheckboxClick);
387
+ deleteHistoryButton.addEventListener('click', deleteHistory);
351
388
 
352
389
  const websocketURL = 'ws://localhost:' + websocketPort;
353
390
  let websocket = {};
@@ -404,7 +441,7 @@
404
441
  return websocket;
405
442
  };
406
443
 
407
- connectWebSocket();
444
+ // connectWebSocket();
408
445
 
409
446
 
410
447
  fetchEvents();
@@ -419,7 +456,6 @@
419
456
 
420
457
  window.scrollTo({
421
458
  top: document.documentElement.scrollHeight,
422
- behavior: 'smooth'
423
459
  });
424
460
  </script>
425
461
  </body>
@@ -103,6 +103,10 @@ class EventManager:
103
103
  event_queue.put(new_event)
104
104
  return new_event
105
105
 
106
+ def reset_checked_lines(self):
107
+ for event in self.events:
108
+ event.checked = False
109
+
106
110
  def get_events(self):
107
111
  return self.events
108
112
 
@@ -117,6 +121,14 @@ class EventManager:
117
121
  if self.conn:
118
122
  self.conn.close()
119
123
 
124
+ def clear_history(self):
125
+ self.cursor.execute("DELETE FROM events WHERE time < ?", (initial_time.isoformat(),))
126
+ logger.info(f"Cleared history before {initial_time.isoformat()}")
127
+ self.conn.commit()
128
+ # Clear the in-memory events as well
129
+ event_manager.events = [event for event in event_manager if not event.history]
130
+ event_manager.events_dict = {event.id: event for event in event_manager.events}
131
+
120
132
  class EventProcessor(threading.Thread):
121
133
  def __init__(self, event_queue, db_path):
122
134
  super().__init__()
@@ -227,12 +239,20 @@ def textreplacements():
227
239
  def get_data():
228
240
  return jsonify([event.to_dict() for event in event_manager])
229
241
 
242
+ @app.route('/clear_history', methods=['POST'])
243
+ def clear_history():
244
+ temp_em = EventManager()
245
+ temp_em.clear_history()
246
+ temp_em.close_connection()
247
+ return jsonify({'message': 'History cleared successfully'}), 200
248
+
230
249
 
231
250
  async def add_event_to_texthooker(line: GameLine):
232
251
  logger.info("Adding event to web server: %s", line.text)
233
252
  new_event = event_manager.add_gameline(line)
234
253
  await broadcast_message({
235
254
  'event': 'text_received',
255
+ 'sentence': line.text,
236
256
  'data': new_event.to_serializable()
237
257
  })
238
258
 
@@ -247,8 +267,7 @@ def update_event():
247
267
  return jsonify({'error': 'Missing id or checked status'}), 400
248
268
 
249
269
  event_manager.get(event_id).checked = checked
250
-
251
- return jsonify({'error': 'Event not found'}), 404
270
+ return jsonify({'message': 'Event updated successfully'}), 200
252
271
 
253
272
  @app.route('/get-screenshot', methods=['Post'])
254
273
  def get_screenshot():
@@ -338,8 +357,7 @@ def are_lines_selected():
338
357
  return any(item.checked for item in event_manager)
339
358
 
340
359
  def reset_checked_lines():
341
- for item in event_manager:
342
- item.checked = False
360
+ event_manager.reset_checked_lines()
343
361
 
344
362
  def open_texthooker():
345
363
  webbrowser.open(url + '/texthooker')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.8.12
3
+ Version: 2.8.14
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
@@ -1,19 +1,19 @@
1
1
  GameSentenceMiner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- GameSentenceMiner/anki.py,sha256=pSkf-il17vKZIBt1ZHHDMfvfO9M-GdF1zox-c--KkAY,14208
3
- GameSentenceMiner/config_gui.py,sha256=6TkDLW-0SPWMeNAwzb5WT7aVJ8mxPqmhAQcS1BHzTik,70979
4
- GameSentenceMiner/configuration.py,sha256=6qhYdtQ9-EWCsgnkLGVsWf66twWTltBNTgYoDzCujl8,21713
2
+ GameSentenceMiner/anki.py,sha256=OCLgZa-iEp93v-R0zKFkDCjule_EAoP5rIqtnMHLnOw,14226
3
+ GameSentenceMiner/config_gui.py,sha256=RHiiQxALMuVNg3ydNO49q7dtrYudGJIbLOXMk2NYoOs,71617
4
+ GameSentenceMiner/configuration.py,sha256=jRuEgIM78W4rDrbAr8fABbNKeGlcS7P2JZP-Q1cBUV8,21922
5
5
  GameSentenceMiner/electron_config.py,sha256=dGcPYCISPehXubYSzsDuI2Gl092MYK0u3bTnkL9Jh1Y,9787
6
- GameSentenceMiner/ffmpeg.py,sha256=mcEcJnYl06oJGbLaymFUfqClFiHf6Hhf2SXo3UV9tvM,13378
6
+ GameSentenceMiner/ffmpeg.py,sha256=MiraA1cYv0g5adwwI8f-zeFL0kG7sVuhxQSF_OuzhvU,13732
7
7
  GameSentenceMiner/gametext.py,sha256=ClSpOeohBWG17MRVIbhXfNDnkUdxU9mTspGv9975uEc,5422
8
- GameSentenceMiner/gsm.py,sha256=vCEPg7rE68AUD2u6DesjLxuZSy1gJzbiA0fig673WRM,25254
8
+ GameSentenceMiner/gsm.py,sha256=BiCxMJN9_BoX3NL0UkNAuoZk5OnrkQ7PSFgSCzrDJFc,25459
9
9
  GameSentenceMiner/model.py,sha256=JdnkT4VoPOXmOpRgFdvERZ09c9wLN6tUJxdrKlGZcqo,5305
10
10
  GameSentenceMiner/notification.py,sha256=FY39ChSRK0Y8TQ6lBGsLnpZUFPtFpSy2tweeXVoV7kc,2809
11
11
  GameSentenceMiner/obs.py,sha256=GPlsFrcv1eYelXyJfpspGK0iZK5AXPkoFsIGdB7eJrk,10002
12
12
  GameSentenceMiner/package.py,sha256=YlS6QRMuVlm6mdXx0rlXv9_3erTGS21jaP3PNNWfAH0,1250
13
13
  GameSentenceMiner/text_log.py,sha256=tBuZ8ElUgPtiQV8U6U90kmRxposwIkL3fjOYejdzikc,5153
14
- GameSentenceMiner/util.py,sha256=bb75EQdk4Nf0i9t3XMznjd6NxTWfkOVeE5bZFstbCbU,8910
14
+ GameSentenceMiner/util.py,sha256=LzWGIDZb8NLv-RyrE_d6ycoQEwM1zpaDhWp0LKb6_Zc,8928
15
15
  GameSentenceMiner/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- GameSentenceMiner/ai/ai_prompting.py,sha256=tu7MxItzuJ5iSyieWIt1YHrMDEUKYAAlur6WWLZ61D4,9137
16
+ GameSentenceMiner/ai/ai_prompting.py,sha256=uTtXIDRpcQqPT4EC-R_pwRP9pBmZ64I6vMDOexhJRp8,9505
17
17
  GameSentenceMiner/communication/__init__.py,sha256=_jGn9PJxtOAOPtJ2rI-Qu9hEHVZVpIvWlxKvqk91_zI,638
18
18
  GameSentenceMiner/communication/send.py,sha256=oOJdCS6-LNX90amkRn5FL2xqx6THGm56zHR2ntVIFTE,229
19
19
  GameSentenceMiner/communication/websocket.py,sha256=pTcUe_ZZRp9REdSU4qalhPmbT_1DKa7w18j6RfFLELA,3074
@@ -22,10 +22,10 @@ GameSentenceMiner/downloader/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
22
22
  GameSentenceMiner/downloader/download_tools.py,sha256=mI1u_FGBmBqDIpCH3jOv8DOoZ3obgP5pIf9o9SVfX2Q,8131
23
23
  GameSentenceMiner/downloader/oneocr_dl.py,sha256=o3ANp5IodEQoQ8GPcJdg9Y8JzA_lictwnebFPwwUZVk,10144
24
24
  GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=zagsB4UD9mmZX_r6dFBCXZqdDa0XGk-RvIqbKoPB9lQ,1932
25
+ GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=rQC6C8PKJXWoAvwCOYa363kodQQBwl1YNeYsD0bBbx4,1957
26
26
  GameSentenceMiner/ocr/ocrconfig.py,sha256=_tY8mjnzHMJrLS8E5pHqYXZjMuLoGKYgJwdhYgN-ny4,6466
27
27
  GameSentenceMiner/ocr/owocr_area_selector.py,sha256=gwYOz-fA5qoL63wh77eyGJtBtO7YVvWyO5cHb3D0Oz4,46738
28
- GameSentenceMiner/ocr/owocr_helper.py,sha256=PCsPbqrECSbi4u8NIq3PJotStaYVBBbfccHu-DrqpwU,17269
28
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=ZGAoekdkcqzluA1_QkjbsTM7D4LdW1ceW19msUwlG78,17388
29
29
  GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
30
30
  GameSentenceMiner/owocr/owocr/__main__.py,sha256=r8MI6RAmbkTWqOJ59uvXoDS7CSw5jX5war9ULGWELrA,128
31
31
  GameSentenceMiner/owocr/owocr/config.py,sha256=n-xtVylb2Q_H84jb1ZsIGxPQjTNnyvnRny1RhtaLJM8,7550
@@ -38,7 +38,7 @@ GameSentenceMiner/vad/silero_trim.py,sha256=ULf3zwS-JMsY82cKF7gZxREHw8L6lgpWF2U1
38
38
  GameSentenceMiner/vad/vosk_helper.py,sha256=125X8C9NxFPlWWpoNsbOnEqKx8RCjXN109zNx_QXhyg,6070
39
39
  GameSentenceMiner/vad/whisper_helper.py,sha256=JJ-iltCh813XdjyEw0Wn5DaErf6PDqfH0Efu1Md8cIY,3543
40
40
  GameSentenceMiner/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- GameSentenceMiner/web/texthooking_page.py,sha256=bI1itAIY4iPjGlFTmtrLbcjo6pvvp_dFlSOYrEDWPJ0,12760
41
+ GameSentenceMiner/web/texthooking_page.py,sha256=AtVV9RS7HC3XnOq4X0FIMqJrzFoGlfSHFvS_CfhuzuA,13558
42
42
  GameSentenceMiner/web/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  GameSentenceMiner/web/static/apple-touch-icon.png,sha256=OcMI8af_68DA_tweOsQ5LytTyMwm7-hPW07IfrOVgEs,46132
44
44
  GameSentenceMiner/web/static/favicon-96x96.png,sha256=lOePzjiKl1JY2J1kT_PMdyEnrlJmi5GWbmXJunM12B4,16502
@@ -50,10 +50,10 @@ GameSentenceMiner/web/static/web-app-manifest-192x192.png,sha256=EfSNnBmsSaLfESb
50
50
  GameSentenceMiner/web/static/web-app-manifest-512x512.png,sha256=wyqgCWCrLEUxSRXmaA3iJEESd-vM-ZmlTtZFBY4V8Pk,230819
51
51
  GameSentenceMiner/web/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  GameSentenceMiner/web/templates/text_replacements.html,sha256=tV5c8mCaWSt_vKuUpbdbLAzXZ3ATZeDvQ9PnnAfqY0M,8598
53
- GameSentenceMiner/web/templates/utility.html,sha256=8yJvmHqkHoMN06qWDckhGoguWyP7LUVZ2oms0tBRdEw,14297
54
- gamesentenceminer-2.8.12.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
55
- gamesentenceminer-2.8.12.dist-info/METADATA,sha256=aVn_Dm-aRX7cDVzcMIMtYIq3mHWHZYrhHBzod5NZql4,5933
56
- gamesentenceminer-2.8.12.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
57
- gamesentenceminer-2.8.12.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
58
- gamesentenceminer-2.8.12.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
59
- gamesentenceminer-2.8.12.dist-info/RECORD,,
53
+ GameSentenceMiner/web/templates/utility.html,sha256=y3HS4ShvmsBHtK5RkA2Unq77SWCvbwkcSxuXzTZV1H8,15976
54
+ gamesentenceminer-2.8.14.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
55
+ gamesentenceminer-2.8.14.dist-info/METADATA,sha256=JSiTQV0z-Ko1VEI8hfp7Beo50mMIKOhohHU_fKRPUXY,5933
56
+ gamesentenceminer-2.8.14.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
57
+ gamesentenceminer-2.8.14.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
58
+ gamesentenceminer-2.8.14.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
59
+ gamesentenceminer-2.8.14.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.0.0)
2
+ Generator: setuptools (80.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5