GameSentenceMiner 2.7.7__py3-none-any.whl → 2.7.9__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/obs.py CHANGED
@@ -60,7 +60,7 @@ def get_obs_websocket_config_values():
60
60
 
61
61
  # Extract values
62
62
  server_enabled = config.get("server_enabled", False)
63
- server_port = config.get("server_port", 4455) # Default to 4455 if not set
63
+ server_port = config.get("server_port", 7274) # Default to 4455 if not set
64
64
  server_password = config.get("server_password", None)
65
65
 
66
66
  if not server_enabled:
@@ -73,8 +73,8 @@ def get_obs_websocket_config_values():
73
73
  if get_config().obs.password == 'your_password':
74
74
  logger.info("OBS WebSocket password is not set. Setting it now...")
75
75
  config = get_master_config()
76
- config.obs.port = server_port
77
- config.obs.password = server_password
76
+ config.get_config().port = server_port
77
+ config.get_config().password = server_password
78
78
  with open(get_config_path(), 'w') as file:
79
79
  json.dump(config.to_dict(), file, indent=4)
80
80
  reload_config()
@@ -0,0 +1,42 @@
1
+ from dataclasses import dataclass
2
+ from dataclasses_json import dataclass_json
3
+ from typing import List, Optional
4
+
5
+ @dataclass_json
6
+ @dataclass
7
+ class Monitor:
8
+ left: int
9
+ top: int
10
+ width: int
11
+ height: int
12
+ index: int
13
+
14
+ @dataclass_json
15
+ @dataclass
16
+ class Rectangle:
17
+ monitor: Monitor
18
+ coordinates: List[int]
19
+ is_excluded: bool
20
+
21
+ @dataclass_json
22
+ @dataclass
23
+ class OCRConfig:
24
+ scene: str
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)
@@ -1,275 +1,294 @@
1
- import ctypes
2
1
  import sys
2
+ from multiprocessing import Process, Manager
3
+ import mss
4
+ from GameSentenceMiner import obs
5
+
6
+ from GameSentenceMiner.util import sanitize_filename
7
+ from PIL import Image, ImageTk
3
8
  import tkinter as tk
4
- from tkinter import messagebox, simpledialog
5
9
  import json
6
10
  from pathlib import Path
7
- import time # Optional: for debugging timing if needed
8
- from PIL import Image, ImageTk
9
- import io
10
- import keyboard
11
-
12
- from GameSentenceMiner import obs, util
13
-
14
- class DynamicAreaSelector(tk.Tk):
15
- def __init__(self, window_title):
16
- super().__init__()
17
- self.title("Dynamic Area Selector")
18
- self.attributes('-fullscreen', True)
19
- self.attributes("-topmost", True) # Make the window always on top
20
- self.attributes("-alpha", 0.20)
21
-
22
- self.window_title = window_title
23
- self.rects = []
11
+ import re
12
+
13
+ try:
14
+ import tkinter as tk
15
+ selector_available = True
16
+ except:
17
+ selector_available = False
18
+
19
+ MIN_RECT_WIDTH = 25 # Minimum width in pixels
20
+ MIN_RECT_HEIGHT = 25 # Minimum height in pixels
21
+
22
+ class ScreenSelector:
23
+ def __init__(self, result, window_name):
24
+ obs.connect_to_obs()
25
+ self.window_name = window_name
26
+ print(window_name)
27
+ self.sct = mss.mss()
28
+ self.monitors = self.sct.monitors[1:]
29
+ self.root = None
30
+ self.result = result
31
+ self.rectangles = [] # List to store (monitor, coordinates, is_excluded) tuples
32
+ self.drawn_rect_ids = [] # List to store canvas rectangle IDs
33
+ self.current_rect_id = None
24
34
  self.start_x = None
25
35
  self.start_y = None
26
- self.current_rect_id = None
27
- self.drawn_rect_ids = []
28
- self.saved_rect_coords = []
29
- self.excluded_rect_coords = [] # New list for excluded rectangles
30
- self.drawn_excluded_rect_ids = [] # New list for excluded rect ids
31
- self.image_mode = False
32
- self.image_item = None
33
- self.image_tk = None
34
- self.canvas = None
35
-
36
- if not self.initialize_ui():
37
- self.after(0, self.destroy)
38
- return
36
+ self.canvas_windows = {} # Dictionary to store canvas per monitor
37
+ self.load_existing_rectangles()
38
+ self.image_mode = True
39
+ self.monitor_windows = {}
40
+ self.redo_stack = []
39
41
 
40
- self.canvas.bind("<ButtonPress-1>", self.on_press)
41
- self.canvas.bind("<B1-Motion>", self.on_drag)
42
- self.canvas.bind("<ButtonRelease-1>", self.on_release)
43
- self.canvas.bind("<Button-3>", self.on_right_click) # Bind right-click
44
- self.bind("<Control-s>", self.save_rects_event)
45
- self.bind("<Control-z>", self.undo_last_rect)
46
- self.bind("<Escape>", self.quit_app)
47
- self.bind("<Control-i>", self.toggle_image_mode)
48
-
49
- self.protocol("WM_DELETE_WINDOW", self.on_close)
42
+ def get_scene_ocr_config(self):
43
+ """Return the path to the OCR config file in GameSentenceMiner/ocr_config."""
44
+ app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
45
+ ocr_config_dir = app_dir / "ocr_config"
46
+ ocr_config_dir.mkdir(parents=True, exist_ok=True)
47
+ scene = sanitize_filename(obs.get_current_scene())
48
+ config_path = ocr_config_dir / f"{scene}.json"
49
+ return config_path
50
50
 
51
- def initialize_ui(self):
51
+ def load_existing_rectangles(self):
52
+ config_path = self.get_scene_ocr_config()
52
53
  try:
53
- self.canvas = tk.Canvas(self, highlightthickness=0)
54
- self.canvas.pack(fill=tk.BOTH, expand=True)
55
-
56
- self.load_and_draw_existing_rects()
57
- return True
58
-
59
- except Exception as e:
60
- messagebox.showerror("Initialization Error", f"Failed: {e}")
61
- print(f"Initialization error details: {e}")
62
- return False
63
-
64
- def load_and_draw_existing_rects(self):
65
- if self.get_scene_ocr_config().exists():
66
- try:
67
- with open(self.get_scene_ocr_config(), 'r') as f:
68
- config_data = json.load(f)
69
- loaded_rects = []
70
- loaded_excluded_rects = []
71
- for r in config_data.get("rectangles", []):
72
- try:
73
- coords = tuple(map(float, r))
74
- if len(coords) == 4:
75
- loaded_rects.append(coords)
54
+ with open(config_path, 'r') as f:
55
+ config_data = json.load(f)
56
+ if "rectangles" in config_data:
57
+ self.rectangles = []
58
+ for rect_data in config_data["rectangles"]:
59
+ monitor_data = rect_data.get("monitor")
60
+ coords = rect_data.get("coordinates")
61
+ is_excluded = rect_data.get("is_excluded", False)
62
+ if monitor_data and isinstance(coords, list) and len(coords) == 4:
63
+ x, y, w, h = coords
64
+ if w >= MIN_RECT_WIDTH and h >= MIN_RECT_HEIGHT:
65
+ self.rectangles.append((monitor_data, tuple(coords), is_excluded))
76
66
  else:
77
- print(f"Skipping invalid rectangle data: {r}")
78
- except (ValueError, TypeError) as coord_err:
79
- print(f"Skipping invalid coordinate data in rectangle {r}: {coord_err}")
80
-
81
- for r in config_data.get("excluded_rectangles", []):
82
- try:
83
- coords = tuple(map(float, r))
84
- if len(coords) == 4:
85
- loaded_excluded_rects.append(coords)
86
- else:
87
- print(f"Skipping invalid excluded rectangle data: {r}")
88
- except (ValueError, TypeError) as coord_err:
89
- print(f"Skipping invalid coordinate data in excluded rectangle {r}: {coord_err}")
90
-
91
- self.saved_rect_coords = loaded_rects
92
- self.excluded_rect_coords = loaded_excluded_rects
93
- for rect_id in self.drawn_rect_ids:
94
- self.canvas.delete(rect_id)
95
- for rect_id in self.drawn_excluded_rect_ids:
96
- self.canvas.delete(rect_id)
97
- self.drawn_rect_ids = []
98
- self.drawn_excluded_rect_ids = []
99
-
100
- for rect in self.saved_rect_coords:
101
- x1, y1, x2, y2 = rect
102
- rect_id = self.canvas.create_rectangle(x1, y1, x2, y2, outline='blue', width=2)
103
- self.drawn_rect_ids.append(rect_id)
104
-
105
- for rect in self.excluded_rect_coords:
106
- x1, y1, x2, y2 = rect
107
- rect_id = self.canvas.create_rectangle(x1, y1, x2, y2, outline='orange', width=2)
108
- self.drawn_excluded_rect_ids.append(rect_id)
109
-
110
- except json.JSONDecodeError:
111
- messagebox.showwarning("Config Load Warning", f"Could not parse {self.get_scene_ocr_config()}. Starting with no rectangles.")
112
- self.saved_rect_coords = []
113
- self.excluded_rect_coords = []
114
- except FileNotFoundError:
115
- self.saved_rect_coords = []
116
- self.excluded_rect_coords = []
117
- except Exception as e:
118
- messagebox.showerror("Config Load Error", f"Error loading rectangles: {e}")
119
- self.saved_rect_coords = []
120
- self.excluded_rect_coords = []
121
-
122
- def on_press(self, event):
123
- if self.current_rect_id is None:
124
- self.start_x = self.canvas.canvasx(event.x)
125
- self.start_y = self.canvas.canvasy(event.y)
126
- outline_color = 'red' if not event.state & 0x0001 else 'purple' # check if shift is pressed
127
- self.current_rect_id = self.canvas.create_rectangle(
128
- self.start_x, self.start_y, self.start_x, self.start_y,
129
- outline=outline_color, width=2
130
- )
131
-
132
- def on_drag(self, event):
133
- if self.current_rect_id is not None:
134
- cur_x = self.canvas.canvasx(event.x)
135
- cur_y = self.canvas.canvasy(event.y)
136
- self.canvas.coords(self.current_rect_id, self.start_x, self.start_y, cur_x, cur_y)
137
-
138
- def on_release(self, event):
139
- if self.current_rect_id is not None:
140
- end_x = self.canvas.canvasx(event.x)
141
- end_y = self.canvas.canvasy(event.y)
142
- x1 = min(self.start_x, end_x)
143
- y1 = min(self.start_y, end_y)
144
- x2 = max(self.start_x, end_x)
145
- y2 = max(self.start_y, end_y)
146
-
147
- self.canvas.coords(self.current_rect_id, x1, y1, x2, y2)
148
- if event.state & 0x0001: # shift is pressed
149
- self.canvas.itemconfig(self.current_rect_id, outline='orange')
150
- self.excluded_rect_coords.append((x1, y1, x2, y2))
151
- self.drawn_excluded_rect_ids.append(self.current_rect_id)
152
- else:
153
- self.canvas.itemconfig(self.current_rect_id, outline='green')
154
- self.saved_rect_coords.append((x1, y1, x2, y2))
155
- self.drawn_rect_ids.append(self.current_rect_id)
156
-
157
- self.current_rect_id = None
158
- self.start_x = None
159
- self.start_y = None
67
+ print(f"Skipping small rectangle from config: {coords}")
68
+ print(f"Loaded existing rectangles from {config_path}")
69
+ except FileNotFoundError:
70
+ print(f"No existing config found at {config_path}")
71
+ except json.JSONDecodeError:
72
+ print(f"Error decoding JSON from {config_path}")
73
+ except Exception as e:
74
+ print(f"An error occurred while loading rectangles: {e}")
160
75
 
161
- def save_rects(self):
76
+ def save_rects(self, event=None):
162
77
  try:
163
- serializable_rects = [
164
- tuple(map(int, rect)) for rect in self.saved_rect_coords
165
- ]
166
- serializable_excluded_rects = [
167
- tuple(map(int, rect)) for rect in self.excluded_rect_coords
168
- ]
169
- with open(self.get_scene_ocr_config(), 'w') as f:
170
- json.dump({"window": self.window_title if self.window_title else "", "scene": obs.get_current_scene(), "rectangles": serializable_rects, "excluded_rectangles": serializable_excluded_rects}, f, indent=4)
171
- for rect_id in self.drawn_rect_ids:
172
- if self.canvas.winfo_exists():
173
- try:
174
- self.canvas.itemconfig(rect_id, outline='blue')
175
- except tk.TclError:
176
- pass
177
- for rect_id in self.drawn_excluded_rect_ids:
178
- if self.canvas.winfo_exists():
179
- try:
180
- self.canvas.itemconfig(rect_id, outline='orange')
181
- except tk.TclError:
182
- pass
78
+ print("Saving rectangles...")
79
+ config_path = self.get_scene_ocr_config()
80
+ print(config_path)
81
+ serializable_rects = []
82
+ for monitor, coords, is_excluded in self.rectangles:
83
+ rect_data = {
84
+ "monitor": monitor,
85
+ "coordinates": list(coords), # Convert tuple to list for JSON
86
+ "is_excluded": is_excluded
87
+ }
88
+ serializable_rects.append(rect_data)
89
+
90
+ print(serializable_rects)
91
+ with open(config_path, 'w') as f:
92
+ json.dump({"scene": sanitize_filename(obs.get_current_scene()), "window": self.window_name, "rectangles": serializable_rects}, f, indent=4)
183
93
  print("Rectangles saved.")
184
- self.on_close()
94
+ self.result['rectangles'] = self.rectangles.copy()
95
+ self.root.destroy()
185
96
  except Exception as e:
186
- messagebox.showerror("Error", f"Failed to save rectangles: {e}")
187
-
188
- def save_rects_event(self, event=None):
189
- self.save_rects()
97
+ print(f"Failed to save rectangles: {e}")
190
98
 
191
99
  def undo_last_rect(self, event=None):
192
- if self.saved_rect_coords and self.drawn_rect_ids:
193
- self.saved_rect_coords.pop()
100
+ if self.rectangles and self.drawn_rect_ids:
101
+ last_rect = self.rectangles.pop()
194
102
  last_rect_id = self.drawn_rect_ids.pop()
195
- if self.canvas.winfo_exists():
196
- self.canvas.delete(last_rect_id)
197
- elif self.excluded_rect_coords and self.drawn_excluded_rect_ids:
198
- self.excluded_rect_coords.pop()
199
- last_rect_id = self.drawn_excluded_rect_ids.pop()
200
- if self.canvas.winfo_exists():
201
- self.canvas.delete(last_rect_id)
103
+
104
+ monitor, coords, is_excluded = last_rect
105
+ self.redo_stack.append((monitor, coords, is_excluded, last_rect_id))
106
+
107
+ for canvas in self.canvas_windows.values():
108
+ try:
109
+ canvas.delete(last_rect_id)
110
+ except tk.TclError:
111
+ pass
202
112
  elif self.current_rect_id is not None:
203
- if self.canvas.winfo_exists():
204
- self.canvas.delete(self.current_rect_id)
113
+ for canvas in self.canvas_windows.values():
114
+ try:
115
+ canvas.delete(self.current_rect_id)
116
+ except tk.TclError:
117
+ pass
205
118
  self.current_rect_id = None
206
119
  self.start_x = None
207
120
  self.start_y = None
208
121
 
122
+
123
+ def redo_last_rect(self, event=None):
124
+ if not self.redo_stack:
125
+ return
126
+
127
+ monitor, coords, is_excluded, rect_id = self.redo_stack.pop()
128
+ canvas = self.canvas_windows.get(monitor['index'])
129
+ if canvas:
130
+ x, y, w, h = coords
131
+ outline_color = 'green' if not is_excluded else 'orange'
132
+ new_rect_id = canvas.create_rectangle(x, y, x + w, y + h, outline=outline_color, width=2)
133
+ self.rectangles.append((monitor, coords, is_excluded))
134
+ self.drawn_rect_ids.append(new_rect_id)
135
+
136
+
137
+ def create_window(self, monitor):
138
+ screenshot = self.sct.grab(monitor)
139
+ img = Image.frombytes('RGB', screenshot.size, screenshot.rgb)
140
+
141
+ if img.width != monitor['width']:
142
+ img = img.resize((monitor['width'], monitor['height']), Image.Resampling.LANCZOS)
143
+
144
+ AscendingScale = 1.0 # For semi-transparent background
145
+ window = tk.Toplevel(self.root)
146
+ window.geometry(f"{monitor['width']}x{monitor['height']}+{monitor['left']}+{monitor['top']}")
147
+ window.overrideredirect(1)
148
+ window.attributes('-topmost', 1)
149
+
150
+ img_tk = ImageTk.PhotoImage(img)
151
+
152
+ canvas = tk.Canvas(window, cursor='cross', highlightthickness=0)
153
+ canvas.pack(fill=tk.BOTH, expand=True)
154
+ canvas.image = img_tk
155
+ canvas.bg_img_id = canvas.create_image(0, 0, image=img_tk, anchor=tk.NW) # Store image ID
156
+ self.canvas_windows[monitor['index']] = canvas
157
+
158
+ # Save monitor and window references for refreshing
159
+ self.monitor_windows[monitor['index']] = {
160
+ 'window': window,
161
+ 'canvas': canvas,
162
+ 'bg_img_id': canvas.bg_img_id,
163
+ }
164
+
165
+ # Draw existing rectangles for this monitor
166
+ for mon, coords, is_excluded in self.rectangles:
167
+ if mon['index'] == monitor['index']:
168
+ x, y, w, h = coords
169
+ outline_color = 'green' if not is_excluded else 'orange'
170
+ rect_id = canvas.create_rectangle(x, y, x + w, y + h, outline=outline_color, width=2)
171
+ self.drawn_rect_ids.append(rect_id)
172
+
173
+ def on_click(event):
174
+ if self.current_rect_id is None:
175
+ self.start_x, self.start_y = event.x, event.y
176
+ outline_color = 'red' if not event.state & 0x0001 else 'purple' # Shift for exclusion
177
+ self.current_rect_id = canvas.create_rectangle(
178
+ self.start_x, self.start_y, self.start_x, self.start_y,
179
+ outline=outline_color, width=2
180
+ )
181
+
182
+ def on_drag(event):
183
+ if self.current_rect_id:
184
+ canvas.coords(self.current_rect_id, self.start_x, self.start_y, event.x, event.y)
185
+
186
+ def on_release(event):
187
+ if self.current_rect_id:
188
+ end_x, end_y = event.x, event.y
189
+ x1 = min(self.start_x, end_x)
190
+ y1 = min(self.start_y, end_y)
191
+ x2 = max(self.start_x, end_x)
192
+ y2 = max(self.start_y, end_y)
193
+ width = abs(x2 - x1)
194
+ height = abs(y2 - y1)
195
+ if width >= MIN_RECT_WIDTH and height >= MIN_RECT_HEIGHT:
196
+ is_excluded = bool(event.state & 0x0001) # Shift key for exclusion
197
+ canvas.itemconfig(self.current_rect_id, outline='green' if not is_excluded else 'orange')
198
+ self.rectangles.append((monitor, (x1, y1, width, height), is_excluded))
199
+ self.drawn_rect_ids.append(self.current_rect_id)
200
+ else:
201
+ canvas.delete(self.current_rect_id)
202
+ print(f"Skipping small rectangle: width={width}, height={height}")
203
+ self.current_rect_id = None
204
+ self.start_x = None
205
+ self.start_y = None
206
+
207
+ def on_right_click(event):
208
+ item = canvas.find_closest(event.x, event.y)
209
+ if item:
210
+ for idx, rect_id in enumerate(self.drawn_rect_ids):
211
+ if rect_id == item[0]:
212
+ canvas.delete(rect_id)
213
+ # Need to find the corresponding rectangle in self.rectangles and remove it
214
+ for i, (mon, coords, excluded) in enumerate(self.rectangles):
215
+ if mon['index'] == monitor['index']:
216
+ x_r, y_r, w_r, h_r = coords
217
+ item_coords = canvas.coords(item[0])
218
+ if x_r == item_coords[0] and y_r == item_coords[1] and x_r + w_r == item_coords[2] and y_r + h_r == item_coords[3]:
219
+ del self.rectangles[i]
220
+ break
221
+ del self.drawn_rect_ids[idx]
222
+ break
223
+
224
+ def toggle_image_mode(event=None):
225
+ self.image_mode = not self.image_mode
226
+ if self.image_mode:
227
+ window.attributes("-alpha", 1.0)
228
+ else:
229
+ window.attributes("-alpha", 0.20)
230
+
231
+
232
+ canvas.bind('<ButtonPress-1>', on_click)
233
+ canvas.bind('<B1-Motion>', on_drag)
234
+ canvas.bind('<ButtonRelease-1>', on_release)
235
+ canvas.bind('<Button-3>', on_right_click)
236
+ window.bind('<Control-s>', self.save_rects)
237
+ window.bind('<Control-z>', self.undo_last_rect)
238
+ window.bind('<s>', self.save_rects)
239
+ window.bind('<z>', self.undo_last_rect)
240
+ window.bind("<Escape>", self.quit_app)
241
+ window.bind("<m>", toggle_image_mode)
242
+ window.bind('<Control-y>', self.redo_last_rect)
243
+ window.bind('<y>', self.redo_last_rect)
244
+
245
+ def start(self):
246
+ self.root = tk.Tk()
247
+ self.root.withdraw()
248
+
249
+ for monitor in self.monitors:
250
+ monitor['index'] = self.monitors.index(monitor)
251
+ self.create_window(monitor)
252
+
253
+ self.root.mainloop()
254
+
209
255
  def quit_app(self, event=None):
210
256
  print("Escape pressed, closing application.")
211
257
  self.on_close()
212
258
 
213
259
  def on_close(self):
214
- self.destroy()
260
+ self.root.destroy()
215
261
 
216
- def get_scene_ocr_config(self):
217
- app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
218
- ocr_config_dir = app_dir / "ocr_config"
219
- ocr_config_dir.mkdir(parents=True, exist_ok=True)
220
- scene = util.sanitize_filename(obs.get_current_scene())
221
- config_path = ocr_config_dir / f"{scene}.json"
222
- return config_path
223
262
 
224
- def on_right_click(self, event):
225
- """Deletes the rectangle clicked with the right mouse button."""
226
- item = self.canvas.find_closest(event.x, event.y)
227
-
228
- if item:
229
- if item[0] in self.drawn_rect_ids: # Check if it's a saved rectangle
230
- index = self.drawn_rect_ids.index(item[0])
231
- self.canvas.delete(item[0])
232
- del self.drawn_rect_ids[index]
233
- del self.saved_rect_coords[index]
234
- elif item[0] in self.drawn_excluded_rect_ids:
235
- index = self.drawn_excluded_rect_ids.index(item[0])
236
- self.canvas.delete(item[0])
237
- del self.drawn_excluded_rect_ids[index]
238
- del self.excluded_rect_coords[index]
239
-
240
- def setup_hotkey(self):
241
- keyboard.add_hotkey('F13', self.lift_window) # Example hotkey
242
-
243
- def lift_window(self):
244
- self.lift() # Bring the window to the front
245
-
246
- def toggle_image_mode(self, event=None):
247
- self.image_mode = not self.image_mode
248
- if self.image_mode:
249
- self.attributes("-alpha", 1.0)
250
- self.load_image_from_obs()
251
- else:
252
- self.attributes("-alpha", 0.20)
253
- if self.image_item:
254
- self.canvas.delete(self.image_item)
255
- self.image_item = None
256
- self.image_tk = None
263
+ def run_screen_selector(result, window_name):
264
+ selector = ScreenSelector(result, window_name)
265
+ selector.start()
257
266
 
258
- def load_image_from_obs(self):
259
- try:
260
- image_path = obs.get_screenshot()
261
- image = Image.open(image_path)
262
- self.image_tk = ImageTk.PhotoImage(image)
263
- self.image_item = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.image_tk)
264
- self.canvas.tag_lower(self.image_item)
265
- except Exception as e:
266
- messagebox.showerror("Image Load Error", f"Failed to load image from OBS: {e}")
267
267
 
268
- def run_screen_picker(window_title):
269
- app = DynamicAreaSelector(window_title)
270
- app.mainloop()
268
+ def get_screen_selection(window_name):
269
+ if not selector_available:
270
+ raise ValueError('tkinter is not installed, unable to open picker')
271
+
272
+ with Manager() as manager:
273
+ res = manager.dict()
274
+ process = Process(target=run_screen_selector, args=(res,window_name))
275
+
276
+ process.start()
277
+ process.join()
278
+
279
+ if 'rectangles' in res:
280
+ return res.copy()
281
+ else:
282
+ return False
283
+
271
284
 
272
285
  if __name__ == "__main__":
273
286
  args = sys.argv[1:]
274
- obs.connect_to_obs()
275
- run_screen_picker(args[0] if len(args) > 0 else None)
287
+ window_name = args[0] if args else None
288
+ selection = get_screen_selection(window_name)
289
+ if selection:
290
+ print("Selected rectangles:")
291
+ for monitor, coords, is_excluded in selection['rectangles']:
292
+ print(f"Monitor: {monitor}, Coordinates: {coords}, Excluded: {is_excluded}")
293
+ else:
294
+ print("No selection made or process was interrupted.")
@@ -1,7 +1,6 @@
1
- # area_selector.py
2
1
  import asyncio
3
- import base64
4
2
  import difflib
3
+ import json
5
4
  import logging
6
5
  import os
7
6
  import queue
@@ -9,52 +8,61 @@ import threading
9
8
  import time
10
9
  from datetime import datetime
11
10
  from logging.handlers import RotatingFileHandler
11
+ from pathlib import Path
12
12
  from tkinter import messagebox
13
13
 
14
14
  import mss
15
15
  import websockets
16
16
  from PIL import Image, ImageDraw
17
- import json
18
- from pathlib import Path
19
17
 
20
18
  from GameSentenceMiner import obs, util
21
19
  from GameSentenceMiner.configuration import get_config, get_app_directory
22
- from GameSentenceMiner.gametext import get_line_history
20
+ from GameSentenceMiner.electron_config import get_ocr_scan_rate, get_requires_open_window
21
+ from GameSentenceMiner.ocr.gsm_ocr_config import OCRConfig, Rectangle
23
22
  from GameSentenceMiner.owocr.owocr import screen_coordinate_picker, run
24
23
  from GameSentenceMiner.owocr.owocr.run import TextFiltering
25
- from GameSentenceMiner.electron_config import store, get_ocr_scan_rate, get_requires_open_window
24
+
25
+ from dataclasses import dataclass
26
+ from typing import List, Optional
26
27
 
27
28
  CONFIG_FILE = Path("ocr_config.json")
28
29
  DEFAULT_IMAGE_PATH = r"C:\Users\Beangate\Pictures\msedge_acbl8GL7Ax.jpg" # CHANGE THIS
29
-
30
30
  logger = logging.getLogger("GSM_OCR")
31
31
  logger.setLevel(logging.DEBUG)
32
-
33
32
  # Create a file handler for logging
34
33
  log_file = os.path.join(get_app_directory(), "logs", "ocr_log.txt")
35
34
  os.makedirs(os.path.join(get_app_directory(), "logs"), exist_ok=True)
36
-
37
35
  file_handler = RotatingFileHandler(log_file, maxBytes=1024 * 1024, backupCount=5, encoding='utf-8')
38
36
  file_handler.setLevel(logging.DEBUG)
39
-
40
37
  # Create a formatter and set it for the handler
41
38
  formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
42
39
  file_handler.setFormatter(formatter)
43
-
44
40
  # Add the handler to the logger
45
41
  logger.addHandler(file_handler)
46
42
 
43
+ console_handler = logging.StreamHandler()
44
+ console_handler.setLevel(logging.INFO)
45
+ console_handler.setFormatter(formatter)
46
+ logger.addHandler(console_handler)
47
+
48
+
47
49
  def get_new_game_cords():
48
50
  """Allows multiple coordinate selections."""
49
51
  coords_list = []
50
- while True:
51
- cords = screen_coordinate_picker.get_screen_selection()
52
- coords_list.append({"coordinates": cords})
53
- if messagebox.askyesno("Add Another Region", "Do you want to add another region?"):
54
- continue
55
- else:
56
- break
57
-
52
+ with mss.mss() as sct:
53
+ monitors = sct.monitors
54
+ monitor_map = {i: mon for i, mon in enumerate(monitors)}
55
+ while True:
56
+ selected_monitor_index, cords = screen_coordinate_picker.get_screen_selection_with_monitor(monitor_map)
57
+ selected_monitor = monitor_map[selected_monitor_index]
58
+ coords_list.append({"monitor": {"left": selected_monitor["left"], "top": selected_monitor["top"],
59
+ "width": selected_monitor["width"], "height": selected_monitor["height"],
60
+ "index": selected_monitor_index}, "coordinates": cords,
61
+ "is_excluded": False})
62
+ if messagebox.askyesno("Add Another Region", "Do you want to add another region?"):
63
+ continue
64
+ else:
65
+ break
58
66
  app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
59
67
  ocr_config_dir = app_dir / "ocr_config"
60
68
  ocr_config_dir.mkdir(parents=True, exist_ok=True)
@@ -62,13 +70,13 @@ def get_new_game_cords():
62
70
  scene = util.sanitize_filename(obs.get_current_scene())
63
71
  config_path = ocr_config_dir / f"{scene}.json"
64
72
  with open(config_path, 'w') as f:
65
- json.dump(coords_list, f, indent=4)
73
+ json.dump({"scene": scene, "window": None, "rectangles": coords_list}, f, indent=4)
66
74
  print(f"Saved OCR config to {config_path}")
67
75
  return coords_list
68
76
 
69
77
 
70
78
  def get_ocr_config():
71
- """Loads multiple screen capture areas from the corresponding JSON file."""
79
+ """Loads and updates screen capture areas from the corresponding JSON file."""
72
80
  app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
73
81
  ocr_config_dir = app_dir / "ocr_config"
74
82
  obs.connect_to_obs()
@@ -76,14 +84,58 @@ def get_ocr_config():
76
84
  config_path = ocr_config_dir / f"{scene}.json"
77
85
  if not config_path.exists():
78
86
  raise Exception(f"No config file found at {config_path}.")
79
-
80
- if not config_path.exists():
81
- print("Config Screen picker failed to make file. Please run again.")
82
- return
83
-
84
- with open(config_path, 'r') as f:
85
- coords_list = json.load(f)
86
- return coords_list
87
+ try:
88
+ with open(config_path, 'r') as f:
89
+ config_data = json.load(f)
90
+ if "rectangles" in config_data and isinstance(config_data["rectangles"], list) and all(
91
+ isinstance(item, list) and len(item) == 4 for item in config_data["rectangles"]):
92
+ # Old config format, convert to new
93
+ new_rectangles = []
94
+ with mss.mss() as sct:
95
+ monitors = sct.monitors
96
+ default_monitor = monitors[1] if len(monitors) > 1 else monitors[0]
97
+ for rect in config_data["rectangles"]:
98
+ new_rectangles.append({
99
+ "monitor": {
100
+ "left": default_monitor["left"],
101
+ "top": default_monitor["top"],
102
+ "width": default_monitor["width"],
103
+ "height": default_monitor["height"],
104
+ "index": 0 # Assuming single monitor for old config
105
+ },
106
+ "coordinates": rect,
107
+ "is_excluded": False
108
+ })
109
+ for rect in config_data['excluded_rectangles']:
110
+ new_rectangles.append({
111
+ "monitor": {
112
+ "left": default_monitor["left"],
113
+ "top": default_monitor["top"],
114
+ "width": default_monitor["width"],
115
+ "height": default_monitor["height"],
116
+ "index": 0 # Assuming single monitor for old config
117
+ },
118
+ "coordinates": rect,
119
+ "is_excluded": True
120
+ })
121
+ new_config_data = {"scene": config_data.get("scene", scene), "window": config_data.get("window", None),
122
+ "rectangles": new_rectangles}
123
+ with open(config_path, 'w') as f:
124
+ json.dump(new_config_data, f, indent=4)
125
+ return OCRConfig.from_dict(new_config_data)
126
+ elif "rectangles" in config_data and isinstance(config_data["rectangles"], list) and all(
127
+ isinstance(item, dict) and "coordinates" in item for item in config_data["rectangles"]):
128
+ logger.info("Loading new OCR config format.")
129
+ logger.info(config_data)
130
+ return OCRConfig.from_dict(config_data)
131
+ else:
132
+ raise Exception(f"Invalid config format in {config_path}.")
133
+ except json.JSONDecodeError:
134
+ print("Error decoding JSON. Please check your config file.")
135
+ return None
136
+ except Exception as e:
137
+ print(f"Error loading config: {e}")
138
+ return None
87
139
 
88
140
 
89
141
  websocket_server_thread = None
@@ -130,7 +182,8 @@ class WebsocketServerThread(threading.Thread):
130
182
 
131
183
  def send_text(self, text, line_time: datetime):
132
184
  if text:
133
- return asyncio.run_coroutine_threadsafe(self.send_text_coroutine(json.dumps({"sentence": text, "time": line_time.isoformat()})), self.loop)
185
+ return asyncio.run_coroutine_threadsafe(
186
+ self.send_text_coroutine(json.dumps({"sentence": text, "time": line_time.isoformat()})), self.loop)
134
187
 
135
188
  def stop_server(self):
136
189
  self.loop.call_soon_threadsafe(self._stop_event.set)
@@ -140,15 +193,21 @@ class WebsocketServerThread(threading.Thread):
140
193
  self._loop = asyncio.get_running_loop()
141
194
  self._stop_event = stop_event = asyncio.Event()
142
195
  self._event.set()
143
- self.server = start_server = websockets.serve(self.server_handler, get_config().general.websocket_uri.split(":")[0], get_config().general.websocket_uri.split(":")[1], max_size=1000000000)
196
+ self.server = start_server = websockets.serve(self.server_handler,
197
+ get_config().general.websocket_uri.split(":")[0],
198
+ get_config().general.websocket_uri.split(":")[1],
199
+ max_size=1000000000)
144
200
  async with start_server:
145
201
  await stop_event.wait()
202
+
146
203
  asyncio.run(main())
147
204
 
205
+
148
206
  all_cords = None
149
207
  rectangles = None
150
208
 
151
- def text_callback(text, rectangle, time):
209
+
210
+ def text_callback(text, rectangle_index, time, img=None):
152
211
  global twopassocr, ocr2, last_oneocr_results
153
212
  if not text:
154
213
  return
@@ -159,8 +218,9 @@ def text_callback(text, rectangle, time):
159
218
  line_time = time if time else datetime.now()
160
219
  logger.info(f"Received message: {text}, ATTEMPTING LENS OCR")
161
220
  if rectangles:
162
- cords = rectangles[rectangle]
163
- i = rectangle
221
+ rect_data: Rectangle = rectangles[rectangle_index]
222
+ cords = rect_data.coordinates
223
+ i = rectangle_index
164
224
  else:
165
225
  i = 0
166
226
  mon = sct.monitors
@@ -172,15 +232,23 @@ def text_callback(text, rectangle, time):
172
232
  last_oneocr_results[i] = text
173
233
  last_result = ([], -1)
174
234
  try:
175
- sct_params = {'left': cords[0], 'top': cords[1], 'width': cords[2], 'height': cords[3]}
176
- sct_img = sct.grab(sct_params)
177
- img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
178
- img = img.convert("RGBA")
179
- draw = ImageDraw.Draw(img)
180
- for exclusion in ocr_config.get("excluded_rectangles", []):
181
- left, top, right, bottom = exclusion
182
- draw.rectangle((left, top, right, bottom), fill=(0, 0, 0, 0))
183
- # draw.rectangle((left, top, right, bottom), fill=(0,0,0))
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))
184
252
  orig_text, text = run.process_and_write_results(img, None, None, last_result, TextFiltering(),
185
253
  engine=ocr2)
186
254
  if ":gsm_prefix:" in text:
@@ -195,74 +263,34 @@ def text_callback(text, rectangle, time):
195
263
  logger.exception(e)
196
264
  print(f"Error processing message: {e}")
197
265
 
266
+
198
267
  done = False
199
268
 
200
- def run_oneocr(ocr_config, i):
269
+
270
+ def run_oneocr(ocr_config: OCRConfig, i):
201
271
  global done
272
+ rect_config = ocr_config.rectangles[i]
273
+ coords = rect_config.coordinates
274
+ monitor_config = rect_config.monitor
275
+ exclusions = (rect.coordinates for rect in list(filter(lambda x: x.is_excluded, ocr_config.rectangles)))
276
+ screen_area = ",".join(str(c) for c in coords)
202
277
  run.run(read_from="screencapture", write_to="callback",
203
- screen_capture_area=",".join(str(c) for c in ocr_config['rectangles'][i]) if ocr_config['rectangles'] else 'screen_1',
204
- screen_capture_window=ocr_config.get("window", None),
278
+ screen_capture_area=screen_area,
279
+ # screen_capture_monitor=monitor_config['index'],
280
+ screen_capture_window=ocr_config.window,
205
281
  screen_capture_only_active_windows=get_requires_open_window(),
206
282
  screen_capture_delay_secs=get_ocr_scan_rate(), engine=ocr1,
207
283
  text_callback=text_callback,
208
- screen_capture_exclusions=ocr_config.get('excluded_rectangles', None),
284
+ screen_capture_exclusions=exclusions,
209
285
  rectangle=i)
210
286
  done = True
211
287
 
212
288
 
213
- # async def websocket_client():
214
- # uri = "ws://localhost:7331" # Replace with your hosted websocket address
215
- # print("Connecting to WebSocket...")
216
- # async with websockets.connect(uri) as websocket:
217
- # print("Connected to WebSocket.")
218
- #
219
- # try:
220
- # while True:
221
- # message = await websocket.recv()
222
- # if not message:
223
- # continue
224
- # line_time = datetime.now()
225
- # get_line_history().add_secondary_line(message)
226
- # print(f"Received message: {message}, ATTEMPTING LENS OCR")
227
- # if ":gsm_prefix:" in message:
228
- # i = int(message.split(":gsm_prefix:")[0])
229
- # cords = all_cords[i] if i else all_cords[0]
230
- # similarity = difflib.SequenceMatcher(None, last_oneocr_results[i], message).ratio()
231
- # if similarity > .8:
232
- # continue
233
- # print(f"Similarity for region {i}: {similarity}")
234
- # last_oneocr_results[i] = message
235
- # last_result = ([], -1)
236
- # try:
237
- # sct_params = {'top': cords[1], 'left': cords[0], 'width': cords[2], 'height': cords[3]}
238
- # with mss.mss() as sct:
239
- # sct_img = sct.grab(sct_params)
240
- # img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
241
- # draw = ImageDraw.Draw(img)
242
- # for exclusion in ocr_config.get("excluded_rectangles", []):
243
- # exclusion = tuple(exclusion)
244
- # draw.rectangle(exclusion, fill="black")
245
- # orig_text, text = run.process_and_write_results(img, "results.txt", None, last_result, TextFiltering(), engine="glens")
246
- # if ":gsm_prefix:" in text:
247
- # text = text.split(":gsm_prefix:")[1]
248
- # websocket_server_thread.send_text(text, line_time)
249
- # except json.JSONDecodeError:
250
- # print("Invalid JSON received.")
251
- # except Exception as e:
252
- # logger.exception(e)
253
- # print(f"Error processing message: {e}")
254
- # except websockets.exceptions.ConnectionClosed:
255
- # print("WebSocket connection closed.")
256
- # except Exception as e:
257
- # print(f"WebSocket error: {e}")
258
-
259
-
260
289
  if __name__ == "__main__":
261
290
  global ocr1, ocr2, twopassocr
262
291
  import sys
263
292
 
264
293
  args = sys.argv[1:]
265
-
266
294
  if len(args) == 3:
267
295
  ocr1 = args[0]
268
296
  ocr2 = args[1]
@@ -277,34 +305,32 @@ if __name__ == "__main__":
277
305
  twopassocr = False
278
306
  else:
279
307
  ocr1 = "oneocr"
280
-
281
308
  logger.info(f"Received arguments: ocr1={ocr1}, ocr2={ocr2}, twopassocr={twopassocr}")
282
309
  global ocr_config
283
- ocr_config = get_ocr_config()
284
- rectangles = ocr_config['rectangles']
285
- last_oneocr_results = [""] * len(rectangles) if rectangles else [""]
286
- oneocr_threads = []
287
- run.init_config(False)
288
- if rectangles:
289
- for i, rectangle in enumerate(rectangles):
290
- thread = threading.Thread(target=run_oneocr, args=(ocr_config,i,), daemon=True)
291
- oneocr_threads.append(thread)
292
- thread.start()
310
+ ocr_config: OCRConfig = get_ocr_config()
311
+ if ocr_config and ocr_config.rectangles:
312
+ rectangles = ocr_config.rectangles
313
+ last_oneocr_results = [""] * len(rectangles) if rectangles else [""]
314
+ oneocr_threads = []
315
+ run.init_config(False)
316
+ if rectangles:
317
+ for i, rectangle in enumerate(rectangles):
318
+ thread = threading.Thread(target=run_oneocr, args=(ocr_config, i,), daemon=True)
319
+ oneocr_threads.append(thread)
320
+ thread.start()
321
+ else:
322
+ single_ocr_thread = threading.Thread(target=run_oneocr, args=(ocr_config, 0,), daemon=True)
323
+ oneocr_threads.append(single_ocr_thread)
324
+ single_ocr_thread.start()
325
+ websocket_server_thread = WebsocketServerThread(read=True)
326
+ websocket_server_thread.start()
327
+ try:
328
+ while not done:
329
+ time.sleep(1)
330
+ except KeyboardInterrupt as e:
331
+ pass
332
+ for thread in oneocr_threads:
333
+ thread.join()
334
+ # asyncio.run(websocket_client())
293
335
  else:
294
- single_ocr_thread = threading.Thread(target=run_oneocr, args=(ocr_config, 0,), daemon=True)
295
- oneocr_threads.append(single_ocr_thread)
296
- single_ocr_thread.start()
297
-
298
- websocket_server_thread = WebsocketServerThread(read=True)
299
- websocket_server_thread.start()
300
-
301
- try:
302
- while not done:
303
- time.sleep(1)
304
- except KeyboardInterrupt as e:
305
- pass
306
-
307
- for thread in oneocr_threads:
308
- thread.join()
309
-
310
- # asyncio.run(websocket_client())
336
+ print("Failed to load OCR configuration. Please check the logs.")
@@ -571,7 +571,7 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
571
571
  elif write_to == 'clipboard':
572
572
  pyperclipfix.copy(text)
573
573
  elif write_to == "callback":
574
- txt_callback(text, rectangle, start_time)
574
+ txt_callback(text, rectangle, start_time, img_or_path)
575
575
  elif write_to:
576
576
  with Path(write_to).open('a', encoding='utf-8') as f:
577
577
  f.write(text + '\n')
@@ -1026,6 +1026,7 @@ def run(read_from=None,
1026
1026
  else:
1027
1027
  try:
1028
1028
  coord_left, coord_top, right, bottom = win32gui.GetWindowRect(window_handle)
1029
+
1029
1030
  coord_width = right - coord_left
1030
1031
  coord_height = bottom - coord_top
1031
1032
 
@@ -1045,12 +1046,18 @@ def run(read_from=None,
1045
1046
  on_window_closed(False)
1046
1047
  break
1047
1048
 
1048
- rand = random.randint(1, 10)
1049
+ # rand = random.randint(1, 10)
1049
1050
 
1050
1051
  img = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
1051
1052
 
1053
+ # if rand == 1:
1054
+ # img.show()
1055
+
1052
1056
  if custom_left:
1053
- img = img.crop((custom_left, custom_top, custom_width, custom_height))
1057
+ img = img.crop((custom_left, custom_top, custom_left + custom_width, custom_top + custom_height))
1058
+
1059
+ # if rand == 1:
1060
+ # img.show()
1054
1061
 
1055
1062
  win32gui.DeleteObject(save_bitmap.GetHandle())
1056
1063
  save_dc.DeleteDC()
@@ -1064,8 +1071,8 @@ def run(read_from=None,
1064
1071
  img = img.convert("RGBA")
1065
1072
  draw = ImageDraw.Draw(img)
1066
1073
  for exclusion in screen_capture_exclusions:
1067
- left, top, right, bottom = exclusion
1068
- draw.rectangle((left, top, right, bottom), fill=(0, 0, 0, 0))
1074
+ left, top, width, height = exclusion
1075
+ draw.rectangle((left, top, left + width, top + height), fill=(0, 0, 0, 0))
1069
1076
  # draw.rectangle((left, top, right, bottom), fill=(0, 0, 0))
1070
1077
  # img.save(os.path.join(get_temporary_directory(), 'screencapture.png'), 'png')
1071
1078
  res, _ = process_and_write_results(img, write_to, notifications, last_result, filtering, rectangle=rectangle, start_time=start_time)
@@ -55,12 +55,12 @@ class ScreenSelector:
55
55
  def on_release(event):
56
56
  nonlocal start_x, start_y
57
57
  end_x, end_y = event.x, event.y
58
-
59
- x1 = min(start_x, end_x)
60
- y1 = min(start_y, end_y)
61
- x2 = max(start_x, end_x)
62
- y2 = max(start_y, end_y)
63
-
58
+
59
+ x1 = min(start_x, end_x)
60
+ y1 = min(start_y, end_y)
61
+ x2 = max(start_x, end_x)
62
+ y2 = max(start_y, end_y)
63
+
64
64
  self.on_select(monitor, (x1, y1, x2 - x1, y2 - y1))
65
65
 
66
66
  canvas.bind('<ButtonPress-1>', on_click)
@@ -90,11 +90,19 @@ def get_screen_selection():
90
90
  with Manager() as manager:
91
91
  res = manager.dict()
92
92
  process = Process(target=run_screen_selector, args=(res,))
93
-
94
- process.start()
93
+
94
+ process.start()
95
95
  process.join()
96
96
 
97
97
  if 'monitor' in res and 'coordinates' in res:
98
98
  return res.copy()
99
99
  else:
100
100
  return False
101
+
102
+ if __name__ == "__main__":
103
+ selection = get_screen_selection()
104
+ if selection:
105
+ print(f"Selected monitor: {selection['monitor']}")
106
+ print(f"Selected coordinates: {selection['coordinates']}")
107
+ else:
108
+ print("No selection made or process was interrupted.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GameSentenceMiner
3
- Version: 2.7.7
3
+ Version: 2.7.9
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
@@ -8,7 +8,7 @@ GameSentenceMiner/gametext.py,sha256=AAke4swwmN16da0IpyL5xMhU23nTz_c6z2kMfXPYv-8
8
8
  GameSentenceMiner/gsm.py,sha256=fcczDPDcC0vDLAPZtIY64mU3Di3RZjR40tuLIiwB5lE,24827
9
9
  GameSentenceMiner/model.py,sha256=JdnkT4VoPOXmOpRgFdvERZ09c9wLN6tUJxdrKlGZcqo,5305
10
10
  GameSentenceMiner/notification.py,sha256=FY39ChSRK0Y8TQ6lBGsLnpZUFPtFpSy2tweeXVoV7kc,2809
11
- GameSentenceMiner/obs.py,sha256=Zh3B9m86CCTSw15eVmZLPI8vxqXVAJ93EvaU1ycO33A,9121
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
14
  GameSentenceMiner/utility_gui.py,sha256=H4aOddlsrVR768RwbMzYScCziuOz1JeySUigNrPlaac,7692
@@ -21,23 +21,24 @@ 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
25
  GameSentenceMiner/ocr/ocrconfig.py,sha256=hTROOZ3On2HngXKxwQFZvnr5AxlmlMV0mPxv-F3NbMg,6476
25
- GameSentenceMiner/ocr/owocr_area_selector.py,sha256=6MQPCSAhuyOSEDEgc-w6jVdsELjxstzI2Zhz0JBkG8g,11703
26
- GameSentenceMiner/ocr/owocr_helper.py,sha256=yR_W_tK6w7c_zDj48d1B6-9f61fBD3PPnt_FSSPleLs,11840
26
+ GameSentenceMiner/ocr/owocr_area_selector.py,sha256=fpPE6FOFSSfgc2feur8Czve91Jl1zBG_nwUSRY6lTbg,11934
27
+ GameSentenceMiner/ocr/owocr_helper.py,sha256=_mcjF9TA9GrselOzJQdtXhImj8nlLGih9TIiuXsaEdo,14089
27
28
  GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
28
29
  GameSentenceMiner/owocr/owocr/__main__.py,sha256=r8MI6RAmbkTWqOJ59uvXoDS7CSw5jX5war9ULGWELrA,128
29
30
  GameSentenceMiner/owocr/owocr/config.py,sha256=738QCJHEWpFhMh966plOcXYWwcshSiRsxjjIwldeTtI,7461
30
31
  GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
31
32
  GameSentenceMiner/owocr/owocr/ocr.py,sha256=t0kU2GQyW0gf0NGqaYiOO7SjYgX8mQXLaNKJ8Eup6mg,39704
32
- GameSentenceMiner/owocr/owocr/run.py,sha256=HE5plzuX9297q16b6ivSeGn1wZ4A3-hR-XfoSuM6PQs,47272
33
- GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=f_YIV4-2OEZ21CAEZ_pbqeTb5D28PS82Tig4ZSnkY4Q,3096
33
+ GameSentenceMiner/owocr/owocr/run.py,sha256=0MGrhO6HoLNF1JpYcl-tS6SnXNM5zu9y0oSZDrAcw5k,47499
34
+ GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=fjJ3CSXLti3WboGPpmsa7MWOwIXsfpHC8N4zKahGGY0,3346
34
35
  GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
36
  GameSentenceMiner/vad/silero_trim.py,sha256=ULf3zwS-JMsY82cKF7gZxREHw8L6lgpWF2U1YqgE9Oc,1681
36
37
  GameSentenceMiner/vad/vosk_helper.py,sha256=125X8C9NxFPlWWpoNsbOnEqKx8RCjXN109zNx_QXhyg,6070
37
38
  GameSentenceMiner/vad/whisper_helper.py,sha256=JJ-iltCh813XdjyEw0Wn5DaErf6PDqfH0Efu1Md8cIY,3543
38
- gamesentenceminer-2.7.7.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
39
- gamesentenceminer-2.7.7.dist-info/METADATA,sha256=F81ngxg6OKtxse6IqeUFPIsAODdFwBH5D8vI1cdBcek,5839
40
- gamesentenceminer-2.7.7.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
41
- gamesentenceminer-2.7.7.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
42
- gamesentenceminer-2.7.7.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
43
- gamesentenceminer-2.7.7.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (79.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5