GameSentenceMiner 2.14.9__py3-none-any.whl → 2.14.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.
- GameSentenceMiner/ai/__init__.py +0 -0
- GameSentenceMiner/ai/ai_prompting.py +473 -0
- GameSentenceMiner/ocr/__init__.py +0 -0
- GameSentenceMiner/ocr/gsm_ocr_config.py +174 -0
- GameSentenceMiner/ocr/ocrconfig.py +129 -0
- GameSentenceMiner/ocr/owocr_area_selector.py +629 -0
- GameSentenceMiner/ocr/owocr_helper.py +638 -0
- GameSentenceMiner/ocr/ss_picker.py +140 -0
- GameSentenceMiner/owocr/owocr/__init__.py +1 -0
- GameSentenceMiner/owocr/owocr/__main__.py +9 -0
- GameSentenceMiner/owocr/owocr/config.py +148 -0
- GameSentenceMiner/owocr/owocr/lens_betterproto.py +1238 -0
- GameSentenceMiner/owocr/owocr/ocr.py +1690 -0
- GameSentenceMiner/owocr/owocr/run.py +1818 -0
- GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +109 -0
- GameSentenceMiner/tools/__init__.py +0 -0
- GameSentenceMiner/tools/audio_offset_selector.py +215 -0
- GameSentenceMiner/tools/ss_selector.py +135 -0
- GameSentenceMiner/tools/window_transparency.py +214 -0
- GameSentenceMiner/util/__init__.py +0 -0
- GameSentenceMiner/util/communication/__init__.py +22 -0
- GameSentenceMiner/util/communication/send.py +7 -0
- GameSentenceMiner/util/communication/websocket.py +94 -0
- GameSentenceMiner/util/configuration.py +1199 -0
- GameSentenceMiner/util/db.py +408 -0
- GameSentenceMiner/util/downloader/Untitled_json.py +472 -0
- GameSentenceMiner/util/downloader/__init__.py +0 -0
- GameSentenceMiner/util/downloader/download_tools.py +194 -0
- GameSentenceMiner/util/downloader/oneocr_dl.py +250 -0
- GameSentenceMiner/util/electron_config.py +259 -0
- GameSentenceMiner/util/ffmpeg.py +571 -0
- GameSentenceMiner/util/get_overlay_coords.py +366 -0
- GameSentenceMiner/util/gsm_utils.py +323 -0
- GameSentenceMiner/util/model.py +206 -0
- GameSentenceMiner/util/notification.py +157 -0
- GameSentenceMiner/util/text_log.py +214 -0
- GameSentenceMiner/util/win10toast/__init__.py +154 -0
- GameSentenceMiner/util/win10toast/__main__.py +22 -0
- GameSentenceMiner/web/__init__.py +0 -0
- GameSentenceMiner/web/service.py +132 -0
- GameSentenceMiner/web/static/__init__.py +0 -0
- GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
- GameSentenceMiner/web/static/favicon-96x96.png +0 -0
- GameSentenceMiner/web/static/favicon.ico +0 -0
- GameSentenceMiner/web/static/favicon.svg +3 -0
- GameSentenceMiner/web/static/site.webmanifest +21 -0
- GameSentenceMiner/web/static/style.css +292 -0
- GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
- GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
- GameSentenceMiner/web/templates/__init__.py +0 -0
- GameSentenceMiner/web/templates/index.html +50 -0
- GameSentenceMiner/web/templates/text_replacements.html +238 -0
- GameSentenceMiner/web/templates/utility.html +483 -0
- GameSentenceMiner/web/texthooking_page.py +584 -0
- GameSentenceMiner/wip/__init___.py +0 -0
- {gamesentenceminer-2.14.9.dist-info → gamesentenceminer-2.14.10.dist-info}/METADATA +1 -1
- gamesentenceminer-2.14.10.dist-info/RECORD +79 -0
- gamesentenceminer-2.14.9.dist-info/RECORD +0 -24
- {gamesentenceminer-2.14.9.dist-info → gamesentenceminer-2.14.10.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.14.9.dist-info → gamesentenceminer-2.14.10.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.14.9.dist-info → gamesentenceminer-2.14.10.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.14.9.dist-info → gamesentenceminer-2.14.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,250 @@
|
|
1
|
+
import os
|
2
|
+
import time
|
3
|
+
import zipfile
|
4
|
+
import shutil
|
5
|
+
from os.path import expanduser
|
6
|
+
|
7
|
+
import requests
|
8
|
+
import re
|
9
|
+
import tempfile
|
10
|
+
|
11
|
+
# Placeholder functions/constants for removed proprietary ones
|
12
|
+
# In a real application, you would replace these with appropriate logic
|
13
|
+
# or standard library equivalents.
|
14
|
+
|
15
|
+
def checkdir(d):
|
16
|
+
"""Checks if a directory exists and contains the expected files."""
|
17
|
+
flist = ["oneocr.dll", "oneocr.onemodel", "onnxruntime.dll"]
|
18
|
+
return os.path.isdir(d) and all((os.path.isfile(os.path.join(d, _)) for _ in flist))
|
19
|
+
|
20
|
+
def selectdir():
|
21
|
+
"""Attempts to find the SnippingTool directory, prioritizing cache."""
|
22
|
+
cachedir = "cache/SnippingTool"
|
23
|
+
packageFamilyName = "Microsoft.ScreenSketch_8wekyb3d8bbwe"
|
24
|
+
|
25
|
+
if checkdir(cachedir):
|
26
|
+
return cachedir
|
27
|
+
# This part needs NativeUtils.GetPackagePathByPackageFamily, which is proprietary.
|
28
|
+
# We'll skip this part for simplification as requested.
|
29
|
+
# path = NativeUtils.GetPackagePathByPackageFamily(packageFamilyName)
|
30
|
+
# if not path:
|
31
|
+
# return None
|
32
|
+
# path = os.path.join(path, "SnippingTool")
|
33
|
+
# if not checkdir(path):
|
34
|
+
# return None
|
35
|
+
# return path
|
36
|
+
return None # Return None if not found in cache
|
37
|
+
|
38
|
+
def getproxy():
|
39
|
+
"""Placeholder for proxy retrieval."""
|
40
|
+
# Replace with actual proxy retrieval logic or return None
|
41
|
+
return None
|
42
|
+
|
43
|
+
def stringfyerror(e):
|
44
|
+
"""Placeholder for error stringification."""
|
45
|
+
return str(e)
|
46
|
+
|
47
|
+
def dynamiclink(path):
|
48
|
+
"""Placeholder for dynamic link resolution."""
|
49
|
+
# This would likely map a resource path to a local file path.
|
50
|
+
# For simplification, we'll just use the provided path string.
|
51
|
+
return path # Assuming path is a URL here based on usage
|
52
|
+
|
53
|
+
# Simplified download logic extracted from the question class
|
54
|
+
class Downloader:
|
55
|
+
def __init__(self):
|
56
|
+
self.oneocr_dir = expanduser("~/.config/oneocr")
|
57
|
+
self.packageFamilyName = "Microsoft.ScreenSketch_8wekyb3d8bbwe"
|
58
|
+
self.flist = ["oneocr.dll", "oneocr.onemodel", "onnxruntime.dll"]
|
59
|
+
|
60
|
+
def download_and_extract(self):
|
61
|
+
"""
|
62
|
+
Main function to attempt download and extraction.
|
63
|
+
Tries official source first, then a fallback URL.
|
64
|
+
"""
|
65
|
+
if checkdir(self.oneocr_dir):
|
66
|
+
print("Files already exist in cache.")
|
67
|
+
return True
|
68
|
+
|
69
|
+
try:
|
70
|
+
print("Attempting to download from official source...")
|
71
|
+
# raise Exception("")
|
72
|
+
self.downloadofficial()
|
73
|
+
print("Download and extraction from official source successful.")
|
74
|
+
return True
|
75
|
+
except Exception as e:
|
76
|
+
print(f"Download from official source failed: {stringfyerror(e)}")
|
77
|
+
print("Attempting to download from fallback URL...")
|
78
|
+
try:
|
79
|
+
fallback_url = "https://gsm.beangate.us/oneocr.zip"
|
80
|
+
self.downloadx(fallback_url)
|
81
|
+
print("Download and extraction from fallback URL successful.")
|
82
|
+
return True
|
83
|
+
except Exception as e_fallback:
|
84
|
+
print(f"Download from fallback URL failed: {stringfyerror(e_fallback)}")
|
85
|
+
print("All download attempts failed.")
|
86
|
+
return False
|
87
|
+
|
88
|
+
|
89
|
+
def downloadofficial(self):
|
90
|
+
"""Downloads the latest SnippingTool MSIX bundle from a store API."""
|
91
|
+
headers = {
|
92
|
+
"accept": "*/*",
|
93
|
+
# Changed accept-language to prioritize US English
|
94
|
+
"accept-language": "en-US,en;q=0.9",
|
95
|
+
"cache-control": "no-cache",
|
96
|
+
"origin": "https://store.rg-adguard.net",
|
97
|
+
"pragma": "no-cache",
|
98
|
+
"priority": "u=1, i",
|
99
|
+
"referer": "https://store.rg-adguard.net/",
|
100
|
+
"sec-ch-ua": '"Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"',
|
101
|
+
"sec-ch-ua-mobile": "?0",
|
102
|
+
"sec-ch-ua-platform": '"Windows"',
|
103
|
+
"sec-fetch-dest": "empty",
|
104
|
+
"sec-fetch-mode": "cors",
|
105
|
+
"sec-fetch-site": "same-origin",
|
106
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
|
107
|
+
}
|
108
|
+
|
109
|
+
data = dict(type="PackageFamilyName", url=self.packageFamilyName)
|
110
|
+
|
111
|
+
response = requests.post(
|
112
|
+
"https://store.rg-adguard.net/api/GetFiles",
|
113
|
+
headers=headers,
|
114
|
+
data=data,
|
115
|
+
proxies=getproxy(),
|
116
|
+
)
|
117
|
+
response.raise_for_status() # Raise an exception for bad status codes
|
118
|
+
|
119
|
+
saves = []
|
120
|
+
for link, package in re.findall('<a href="(.*?)".*?>(.*?)</a>', response.text):
|
121
|
+
if not package.startswith("Microsoft.ScreenSketch"):
|
122
|
+
continue
|
123
|
+
if not package.endswith(".msixbundle"):
|
124
|
+
continue
|
125
|
+
version = re.search(r"\d+\.\d+\.\d+\.\d+", package)
|
126
|
+
if not version:
|
127
|
+
continue
|
128
|
+
version = tuple(int(_) for _ in version.group().split("."))
|
129
|
+
saves.append((version, link, package))
|
130
|
+
|
131
|
+
if not saves:
|
132
|
+
raise Exception("Could not find suitable download link from official source.")
|
133
|
+
|
134
|
+
saves.sort(key=lambda _: _[0])
|
135
|
+
url = saves[-1][1]
|
136
|
+
package_name = saves[-1][2]
|
137
|
+
|
138
|
+
print(f"Downloading {package_name} from {url}")
|
139
|
+
req = requests.get(url, stream=True, proxies=getproxy())
|
140
|
+
req.raise_for_status()
|
141
|
+
|
142
|
+
total_size_in_bytes = int(req.headers.get('content-length', 0))
|
143
|
+
block_size = 1024 * 32 # 32 Kibibytes
|
144
|
+
temp_msixbundle_path = os.path.join(tempfile.gettempdir(), package_name)
|
145
|
+
|
146
|
+
with open(temp_msixbundle_path, "wb") as ff:
|
147
|
+
downloaded_size = 0
|
148
|
+
for chunk in req.iter_content(chunk_size=block_size):
|
149
|
+
ff.write(chunk)
|
150
|
+
downloaded_size += len(chunk)
|
151
|
+
# Basic progress reporting (can be removed)
|
152
|
+
if total_size_in_bytes:
|
153
|
+
progress = (downloaded_size / total_size_in_bytes) * 100
|
154
|
+
print(f"Downloaded {downloaded_size}/{total_size_in_bytes} bytes ({progress:.2f}%)", end='\r')
|
155
|
+
print("\nDownload complete. Extracting...")
|
156
|
+
|
157
|
+
namemsix = None
|
158
|
+
with zipfile.ZipFile(temp_msixbundle_path) as ff:
|
159
|
+
for name in ff.namelist():
|
160
|
+
if name.startswith("SnippingTool") and name.endswith("_x64.msix"):
|
161
|
+
namemsix = name
|
162
|
+
break
|
163
|
+
if not namemsix:
|
164
|
+
raise Exception("Could not find MSIX file within MSIXBUNDLE.")
|
165
|
+
temp_msix_path = os.path.join(tempfile.gettempdir(), namemsix)
|
166
|
+
ff.extract(namemsix, tempfile.gettempdir())
|
167
|
+
|
168
|
+
print(f"Extracted {namemsix}. Extracting components...")
|
169
|
+
if os.path.exists(self.oneocr_dir):
|
170
|
+
shutil.rmtree(self.oneocr_dir)
|
171
|
+
os.makedirs(self.oneocr_dir, exist_ok=True)
|
172
|
+
|
173
|
+
with zipfile.ZipFile(temp_msix_path) as ff:
|
174
|
+
collect = []
|
175
|
+
for name in ff.namelist():
|
176
|
+
# Extract only the files within the "SnippingTool/" directory
|
177
|
+
if name.startswith("SnippingTool/") and any(name.endswith(f) for f in self.flist):
|
178
|
+
# Construct target path relative to cachedir
|
179
|
+
target_path = os.path.join(self.oneocr_dir, os.path.relpath(name, "SnippingTool/"))
|
180
|
+
# Ensure parent directories exist
|
181
|
+
os.makedirs(os.path.dirname(target_path), exist_ok=True)
|
182
|
+
# Extract the file
|
183
|
+
with ff.open(name) as source, open(target_path, "wb") as target:
|
184
|
+
shutil.copyfileobj(source, target)
|
185
|
+
collect.append(name)
|
186
|
+
if not collect:
|
187
|
+
raise Exception("Could not find required files within MSIX.")
|
188
|
+
|
189
|
+
|
190
|
+
if not checkdir(self.oneocr_dir):
|
191
|
+
raise Exception("Extraction failed: Required files not found in cache directory.")
|
192
|
+
|
193
|
+
# Clean up temporary files
|
194
|
+
os.remove(temp_msixbundle_path)
|
195
|
+
os.remove(temp_msix_path)
|
196
|
+
|
197
|
+
|
198
|
+
def downloadx(self, url: str):
|
199
|
+
"""Downloads a zip file from a URL and extracts it."""
|
200
|
+
print(f"Downloading from fallback URL")
|
201
|
+
# Added accept-language to the fallback download as well for consistency
|
202
|
+
headers = {
|
203
|
+
"accept-language": "en-US,en;q=0.9",
|
204
|
+
# Add other relevant headers if necessary for the fallback URL
|
205
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
|
206
|
+
"accept": "*/*",
|
207
|
+
}
|
208
|
+
req = requests.get(url, verify=False, proxies=getproxy(), stream=True, headers=headers)
|
209
|
+
req.raise_for_status()
|
210
|
+
|
211
|
+
total_size_in_bytes = int(req.headers.get('content-length', 0))
|
212
|
+
block_size = 1024 * 32 # 32 Kibibytes
|
213
|
+
temp_zip_path = os.path.join(tempfile.gettempdir(), url.split("/")[-1])
|
214
|
+
|
215
|
+
with open(temp_zip_path, "wb") as ff:
|
216
|
+
downloaded_size = 0
|
217
|
+
for chunk in req.iter_content(chunk_size=block_size):
|
218
|
+
ff.write(chunk)
|
219
|
+
downloaded_size += len(chunk)
|
220
|
+
# Basic progress reporting (can be removed)
|
221
|
+
if total_size_in_bytes:
|
222
|
+
progress = (downloaded_size / total_size_in_bytes) * 100
|
223
|
+
print(f"Downloaded {downloaded_size}/{total_size_in_bytes} bytes ({progress:.2f}%)", end='\r')
|
224
|
+
print("\nDownload complete. Extracting...")
|
225
|
+
|
226
|
+
if os.path.exists(self.oneocr_dir):
|
227
|
+
shutil.rmtree(self.oneocr_dir)
|
228
|
+
os.makedirs(self.oneocr_dir, exist_ok=True)
|
229
|
+
|
230
|
+
with zipfile.ZipFile(temp_zip_path) as zipf:
|
231
|
+
zipf.extractall(self.oneocr_dir)
|
232
|
+
|
233
|
+
if not checkdir(self.oneocr_dir):
|
234
|
+
raise Exception("Extraction failed: Required files not found in cache directory.")
|
235
|
+
|
236
|
+
# Clean up temporary files
|
237
|
+
os.remove(temp_zip_path)
|
238
|
+
|
239
|
+
# Example usage:
|
240
|
+
if __name__ == "__main__":
|
241
|
+
downloader = Downloader()
|
242
|
+
downloader.download_and_extract()
|
243
|
+
# if downloader.download_and_extract():
|
244
|
+
# print("SnippingTool files are ready.")
|
245
|
+
# print("Press Ctrl+C or X on window to exit.")
|
246
|
+
# # input()
|
247
|
+
# else:
|
248
|
+
# # print("Failed to download and extract SnippingTool files. You may need to follow instructions at https://github.com/AuroraWright/oneocr")
|
249
|
+
# print("Press Ctrl+C or X on window to exit.")
|
250
|
+
# input()
|
@@ -0,0 +1,259 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from dataclasses import dataclass, field
|
4
|
+
from typing import List, Optional
|
5
|
+
from dataclasses_json import dataclass_json
|
6
|
+
|
7
|
+
from GameSentenceMiner.util.configuration import get_app_directory, logger
|
8
|
+
|
9
|
+
|
10
|
+
# @dataclass_json
|
11
|
+
# @dataclass
|
12
|
+
# class SteamGame:
|
13
|
+
# id: str = ''
|
14
|
+
# name: str = ''
|
15
|
+
# processName: str = ''
|
16
|
+
# script: str = ''
|
17
|
+
|
18
|
+
@dataclass_json
|
19
|
+
@dataclass
|
20
|
+
class YuzuConfig:
|
21
|
+
emuPath: str = "C:\\Emulation\\Emulators\\yuzu-windows-msvc\\yuzu.exe"
|
22
|
+
romsPath: str = "C:\\Emulation\\Yuzu\\Games"
|
23
|
+
launchGameOnStart: str = ""
|
24
|
+
lastGameLaunched: str = ""
|
25
|
+
|
26
|
+
@dataclass_json
|
27
|
+
@dataclass
|
28
|
+
class VNConfig:
|
29
|
+
vns: List[str] = field(default_factory=list)
|
30
|
+
textractorPath: str = ""
|
31
|
+
launchVNOnStart: str = ""
|
32
|
+
lastVNLaunched: str = ""
|
33
|
+
|
34
|
+
# @dataclass_json
|
35
|
+
# @dataclass
|
36
|
+
# class SteamConfig:
|
37
|
+
# steamPath: str = ""
|
38
|
+
# steamGames: List[SteamGame] = field(default_factory=list)
|
39
|
+
# launchSteamOnStart: int = 0
|
40
|
+
# lastGameLaunched: int = 0
|
41
|
+
|
42
|
+
@dataclass_json
|
43
|
+
@dataclass
|
44
|
+
class OCRConfig:
|
45
|
+
twoPassOCR: bool = True
|
46
|
+
optimize_second_scan: bool = True
|
47
|
+
ocr1: str = "oneOCR"
|
48
|
+
ocr2: str = "glens"
|
49
|
+
window_name: str = ""
|
50
|
+
language: str = "ja"
|
51
|
+
ocr_screenshots: bool = False
|
52
|
+
furigana_filter_sensitivity: int = 0
|
53
|
+
manualOcrHotkey: str = "Ctrl+Shift+G"
|
54
|
+
areaSelectOcrHotkey: str = "Ctrl+Shift+O"
|
55
|
+
sendToClipboard: bool = True
|
56
|
+
scanRate: float = 0.5
|
57
|
+
requiresOpenWindow: bool = False
|
58
|
+
useWindowForConfig: bool = False
|
59
|
+
lastWindowSelected: str = ""
|
60
|
+
keep_newline: bool = False
|
61
|
+
useObsAsOCRSource: bool = True
|
62
|
+
|
63
|
+
def has_changed(self, other: 'OCRConfig') -> bool:
|
64
|
+
return self.to_dict() != other.to_dict()
|
65
|
+
|
66
|
+
@dataclass_json
|
67
|
+
@dataclass
|
68
|
+
class StoreConfig:
|
69
|
+
yuzu: YuzuConfig = field(default_factory=YuzuConfig)
|
70
|
+
agentScriptsPath: str = "E:\\Japanese Stuff\\agent-v0.1.4-win32-x64\\data\\scripts"
|
71
|
+
textractorPath: str = "E:\\Japanese Stuff\\Textractor\\Textractor.exe"
|
72
|
+
startConsoleMinimized: bool = False
|
73
|
+
autoUpdateElectron: bool = True
|
74
|
+
autoUpdateGSMApp: bool = False
|
75
|
+
pythonPath: str = ""
|
76
|
+
VN: VNConfig = field(default_factory=VNConfig)
|
77
|
+
# steam: SteamConfig = field(default_factory=SteamConfig)
|
78
|
+
agentPath: str = ""
|
79
|
+
OCR: OCRConfig = field(default_factory=OCRConfig)
|
80
|
+
|
81
|
+
class Store:
|
82
|
+
def __init__(self, config_path=os.path.join(get_app_directory(), "electron", "config.json"), defaults: Optional[StoreConfig] = None):
|
83
|
+
self.data: StoreConfig = StoreConfig()
|
84
|
+
self.config_path = config_path
|
85
|
+
self.defaults = defaults if defaults is not None else StoreConfig()
|
86
|
+
self._load_config()
|
87
|
+
|
88
|
+
def _load_config(self):
|
89
|
+
if os.path.exists(self.config_path):
|
90
|
+
while True:
|
91
|
+
try:
|
92
|
+
with open(self.config_path, 'r', encoding='utf-8') as f:
|
93
|
+
data = json.load(f)
|
94
|
+
self.data = StoreConfig.from_dict(data)
|
95
|
+
break
|
96
|
+
except (json.JSONDecodeError, IOError) as e:
|
97
|
+
logger.debug(f"File being written to: {e}. Retrying...")
|
98
|
+
else:
|
99
|
+
self.data = self.defaults
|
100
|
+
self._save_config()
|
101
|
+
|
102
|
+
def _save_config(self):
|
103
|
+
with open(self.config_path, 'w', encoding='utf-8') as f:
|
104
|
+
json.dump(self.data.to_dict(), f, indent=4)
|
105
|
+
|
106
|
+
def reload_config(self):
|
107
|
+
self._load_config()
|
108
|
+
|
109
|
+
def get(self, key, default=None):
|
110
|
+
keys = key.split('.')
|
111
|
+
value = self.data
|
112
|
+
for k in keys:
|
113
|
+
if hasattr(value, '__dataclass_fields__') and k in value.__dataclass_fields__:
|
114
|
+
value = getattr(value, k)
|
115
|
+
else:
|
116
|
+
return default
|
117
|
+
return value
|
118
|
+
|
119
|
+
def set(self, key, value):
|
120
|
+
keys = key.split('.')
|
121
|
+
current = self.data
|
122
|
+
for i, k in enumerate(keys):
|
123
|
+
if i == len(keys) - 1:
|
124
|
+
setattr(current, k, value)
|
125
|
+
else:
|
126
|
+
if not hasattr(current, '__dataclass_fields__') or k not in current.__dataclass_fields__:
|
127
|
+
return # Key doesn't exist in the dataclass structure
|
128
|
+
if not hasattr(getattr(current, k), '__dataclass_fields__'):
|
129
|
+
setattr(current, k, object()) # Create a new object if it's not a dataclass instance yet
|
130
|
+
current = getattr(current, k)
|
131
|
+
self._save_config()
|
132
|
+
|
133
|
+
def delete(self, key):
|
134
|
+
keys = key.split('.')
|
135
|
+
if not keys:
|
136
|
+
return False
|
137
|
+
current = self.data
|
138
|
+
for i, k in enumerate(keys[:-1]):
|
139
|
+
if not hasattr(current, '__dataclass_fields__') or k not in current.__dataclass_fields__:
|
140
|
+
return False
|
141
|
+
current = getattr(current, k)
|
142
|
+
if hasattr(current, keys[-1]):
|
143
|
+
delattr(current, keys[-1])
|
144
|
+
self._save_config()
|
145
|
+
return True
|
146
|
+
return False
|
147
|
+
|
148
|
+
def print_store(self):
|
149
|
+
"""Prints the entire contents of the store in a readable JSON format."""
|
150
|
+
print(json.dumps(self.data.to_dict(), indent=4))
|
151
|
+
|
152
|
+
# Initialize the store
|
153
|
+
electron_store = Store(config_path=os.path.join(get_app_directory(), "electron", "config.json"), defaults=StoreConfig())
|
154
|
+
|
155
|
+
|
156
|
+
# def has_section_changed(section_class: type) -> bool:
|
157
|
+
# global electron_store
|
158
|
+
# # Get the attribute name from the class (e.g. OCRConfig -> OCR)
|
159
|
+
# section_name = None
|
160
|
+
# for attr, value in StoreConfig.__dataclass_fields__.items():
|
161
|
+
# if value.type == section_class:
|
162
|
+
# section_name = attr
|
163
|
+
# break
|
164
|
+
# if not section_name:
|
165
|
+
# return False
|
166
|
+
# if not os.path.exists(electron_store.config_path):
|
167
|
+
# return False
|
168
|
+
# with open(electron_store.config_path, 'r', encoding='utf-8') as f:
|
169
|
+
# data = json.load(f)
|
170
|
+
# current = StoreConfig.from_dict(data)
|
171
|
+
# current_section = getattr(current, section_name)
|
172
|
+
# old_section = getattr(electron_store, section_name)
|
173
|
+
# if hasattr(current_section, 'to_dict') and hasattr(old_section, 'to_dict'):
|
174
|
+
# return current_section.to_dict() != old_section.to_dict()
|
175
|
+
# electron_store = Store(config_path=electron_store.config_path)
|
176
|
+
# return True
|
177
|
+
|
178
|
+
# Helper Methods
|
179
|
+
def get_electron_store() -> Store:
|
180
|
+
global electron_store
|
181
|
+
return electron_store
|
182
|
+
|
183
|
+
def get_ocr_two_pass_ocr():
|
184
|
+
return electron_store.data.OCR.twoPassOCR
|
185
|
+
|
186
|
+
def get_ocr_optimize_second_scan():
|
187
|
+
return electron_store.data.OCR.optimize_second_scan
|
188
|
+
|
189
|
+
def get_ocr_ocr1():
|
190
|
+
return electron_store.data.OCR.ocr1
|
191
|
+
|
192
|
+
def get_ocr_ocr2():
|
193
|
+
return electron_store.data.OCR.ocr2
|
194
|
+
|
195
|
+
def get_ocr_window_name():
|
196
|
+
return electron_store.data.OCR.window_name or ""
|
197
|
+
|
198
|
+
def get_ocr_language():
|
199
|
+
return electron_store.data.OCR.language or "ja"
|
200
|
+
|
201
|
+
def get_ocr_ocr_screenshots():
|
202
|
+
return electron_store.data.OCR.ocr_screenshots
|
203
|
+
|
204
|
+
def get_ocr_furigana_filter_sensitivity():
|
205
|
+
return electron_store.data.OCR.furigana_filter_sensitivity
|
206
|
+
|
207
|
+
def get_ocr_manual_ocr_hotkey():
|
208
|
+
return electron_store.data.OCR.manualOcrHotkey
|
209
|
+
|
210
|
+
def get_ocr_area_select_ocr_hotkey():
|
211
|
+
return electron_store.data.OCR.areaSelectOcrHotkey
|
212
|
+
|
213
|
+
def get_ocr_send_to_clipboard():
|
214
|
+
return electron_store.data.OCR.sendToClipboard
|
215
|
+
|
216
|
+
def get_ocr_scan_rate():
|
217
|
+
return electron_store.data.OCR.scanRate
|
218
|
+
|
219
|
+
def get_ocr_requires_open_window():
|
220
|
+
return electron_store.data.OCR.requiresOpenWindow
|
221
|
+
|
222
|
+
def get_ocr_use_window_for_config():
|
223
|
+
return electron_store.data.OCR.useWindowForConfig
|
224
|
+
|
225
|
+
def get_ocr_last_window_selected():
|
226
|
+
return electron_store.data.OCR.lastWindowSelected
|
227
|
+
|
228
|
+
def get_ocr_keep_newline():
|
229
|
+
return electron_store.data.OCR.keep_newline
|
230
|
+
|
231
|
+
def get_ocr_use_obs_as_source():
|
232
|
+
return electron_store.data.OCR.useObsAsOCRSource
|
233
|
+
|
234
|
+
def get_furigana_filter_sensitivity() -> int:
|
235
|
+
return electron_store.data.OCR.furigana_filter_sensitivity
|
236
|
+
|
237
|
+
def has_ocr_config_changed() -> bool:
|
238
|
+
global electron_store
|
239
|
+
if not os.path.exists(electron_store.config_path):
|
240
|
+
return False, {}
|
241
|
+
with open(electron_store.config_path, 'r', encoding='utf-8') as f:
|
242
|
+
data = json.load(f)
|
243
|
+
current = StoreConfig.from_dict(data)
|
244
|
+
current_section = current.OCR
|
245
|
+
old_section = electron_store.data.OCR
|
246
|
+
if not (hasattr(current_section, 'to_dict') and hasattr(old_section, 'to_dict')):
|
247
|
+
return False, {}
|
248
|
+
current_dict = current_section.to_dict()
|
249
|
+
old_dict = old_section.to_dict()
|
250
|
+
if current_dict != old_dict:
|
251
|
+
changes = {k: (old_dict[k], current_dict[k]) for k in current_dict if old_dict.get(k) != current_dict.get(k)}
|
252
|
+
# logger.info(f"OCR Config changes detected: {changes}")
|
253
|
+
return True, changes
|
254
|
+
return False, {}
|
255
|
+
|
256
|
+
def reload_electron_config():
|
257
|
+
global electron_store
|
258
|
+
electron_store.reload_config()
|
259
|
+
return electron_store
|