GameSentenceMiner 2.17.0__tar.gz → 2.17.2__tar.gz
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.
Potentially problematic release.
This version of GameSentenceMiner might be problematic. Click here for more details.
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/anki.py +31 -3
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/config_gui.py +26 -2
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/gametext.py +4 -3
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/gsm.py +19 -23
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/obs.py +17 -7
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/ocr/owocr_helper.py +11 -8
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/owocr/owocr/run.py +11 -5
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/configuration.py +7 -5
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/db.py +176 -8
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/downloader/download_tools.py +57 -24
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/ffmpeg.py +5 -2
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/get_overlay_coords.py +3 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/gsm_utils.py +0 -54
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/vad.py +5 -2
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/database_api.py +12 -1
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/gsm_websocket.py +1 -1
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/css/shared.css +20 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/css/stats.css +496 -1
- gamesentenceminer-2.17.2/GameSentenceMiner/web/static/js/anki_stats.js +168 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/js/shared.js +2 -49
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/js/stats.js +274 -39
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/templates/anki_stats.html +36 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/templates/index.html +1 -1
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/templates/stats.html +35 -15
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/texthooking_page.py +31 -8
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner.egg-info/PKG-INFO +1 -1
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/PKG-INFO +1 -1
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/pyproject.toml +4 -4
- gamesentenceminer-2.17.0/GameSentenceMiner/web/static/js/anki_stats.js +0 -84
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/ai/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/ai/ai_prompting.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/assets/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/assets/icon.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/assets/icon128.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/assets/icon256.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/assets/icon32.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/assets/icon512.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/assets/icon64.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/assets/pickaxe.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/locales/en_us.json +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/locales/ja_jp.json +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/locales/zh_cn.json +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/ocr/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/ocr/gsm_ocr_config.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/ocr/ocrconfig.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/ocr/owocr_area_selector.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/ocr/ss_picker.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/owocr/owocr/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/owocr/owocr/__main__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/owocr/owocr/config.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/owocr/owocr/ocr.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/tools/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/tools/audio_offset_selector.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/tools/furigana_filter_preview.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/tools/ss_selector.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/tools/window_transparency.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/communication/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/communication/send.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/communication/websocket.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/downloader/Untitled_json.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/downloader/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/downloader/oneocr_dl.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/electron_config.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/model.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/notification.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/text_log.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/win10toast/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/win10toast/__main__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/events.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/service.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/__init__.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/css/kanji-grid.css +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/css/search.css +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/favicon-96x96.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/favicon.ico +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/favicon.svg +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/js/database.js +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/js/kanji-grid.js +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/js/search.js +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/site.webmanifest +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/style.css +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/stats.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/templates/components/navigation.html +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/templates/components/theme-styles.html +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/templates/database.html +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/templates/search.html +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/web/templates/utility.html +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/wip/__init___.py +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner.egg-info/SOURCES.txt +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner.egg-info/dependency_links.txt +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner.egg-info/entry_points.txt +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner.egg-info/requires.txt +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner.egg-info/top_level.txt +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/LICENSE +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/README.md +0 -0
- {gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/setup.cfg +0 -0
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import copy
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import threading
|
|
2
6
|
from pathlib import Path
|
|
3
7
|
import queue
|
|
4
8
|
import time
|
|
@@ -15,8 +19,8 @@ from GameSentenceMiner.util.db import GameLinesTable
|
|
|
15
19
|
from GameSentenceMiner.util.gsm_utils import make_unique, sanitize_filename, wait_for_stable_file, remove_html_and_cloze_tags, combine_dialogue, \
|
|
16
20
|
run_new_thread, open_audio_in_external
|
|
17
21
|
from GameSentenceMiner.util import ffmpeg, notification
|
|
18
|
-
from GameSentenceMiner.util.configuration import
|
|
19
|
-
|
|
22
|
+
from GameSentenceMiner.util.configuration import get_config, AnkiUpdateResult, logger, anki_results, gsm_status, \
|
|
23
|
+
gsm_state
|
|
20
24
|
from GameSentenceMiner.util.model import AnkiCard
|
|
21
25
|
from GameSentenceMiner.util.text_log import get_all_lines, get_text_event, get_mined_line, lines_match
|
|
22
26
|
from GameSentenceMiner.obs import get_current_game
|
|
@@ -554,16 +558,40 @@ def start_monitoring_anki():
|
|
|
554
558
|
|
|
555
559
|
# --- Anki Stats Kanji Extraction Utilities ---
|
|
556
560
|
|
|
557
|
-
def
|
|
561
|
+
def get_anki_earliest_date():
|
|
562
|
+
"""
|
|
563
|
+
Fetches the earliest Anki card ID.
|
|
564
|
+
"""
|
|
565
|
+
try:
|
|
566
|
+
note_ids = invoke("findCards", query="")
|
|
567
|
+
if not note_ids:
|
|
568
|
+
return 0
|
|
569
|
+
|
|
570
|
+
# Return the first card ID as the "earliest"
|
|
571
|
+
# return note_ids[0]
|
|
572
|
+
return min(note_ids)
|
|
573
|
+
|
|
574
|
+
except Exception as e:
|
|
575
|
+
logger.error(f"Failed to fetch kanji from Anki: {e}")
|
|
576
|
+
return 0
|
|
577
|
+
|
|
578
|
+
def get_all_anki_first_field_kanji(start_timestamp = None, end_timestamp = None):
|
|
558
579
|
"""
|
|
559
580
|
Fetch all notes from Anki and extract unique kanji from the first field of each note.
|
|
560
581
|
Returns a set of kanji characters.
|
|
582
|
+
Optional filtering by start_timestamp and end_timestamp on note IDs.
|
|
561
583
|
"""
|
|
562
584
|
from GameSentenceMiner.web.stats import is_kanji
|
|
563
585
|
try:
|
|
564
586
|
note_ids = invoke("findNotes", query="")
|
|
565
587
|
if not note_ids:
|
|
566
588
|
return set()
|
|
589
|
+
|
|
590
|
+
# Filter note IDs by start and end timestamps if provided
|
|
591
|
+
if (start_timestamp and end_timestamp):
|
|
592
|
+
note_ids = [nid for nid in note_ids if int(start_timestamp) <= nid <= int(end_timestamp)]
|
|
593
|
+
if not note_ids:
|
|
594
|
+
return set()
|
|
567
595
|
kanji_set = set()
|
|
568
596
|
batch_size = 1000
|
|
569
597
|
for i in range(0, len(note_ids), batch_size):
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import copy
|
|
3
3
|
import json
|
|
4
|
+
import os
|
|
4
5
|
import subprocess
|
|
6
|
+
import sys
|
|
5
7
|
import time
|
|
6
8
|
import tkinter as tk
|
|
7
9
|
from tkinter import filedialog, messagebox, simpledialog, scrolledtext, font
|
|
@@ -12,7 +14,11 @@ import ttkbootstrap as ttk
|
|
|
12
14
|
from GameSentenceMiner import obs
|
|
13
15
|
from GameSentenceMiner.util import configuration
|
|
14
16
|
from GameSentenceMiner.util.communication.send import send_restart_signal
|
|
15
|
-
from GameSentenceMiner.util.configuration import
|
|
17
|
+
from GameSentenceMiner.util.configuration import Config, Locale, logger, CommonLanguages, ProfileConfig, General, Paths, \
|
|
18
|
+
Anki, Features, Screenshot, Audio, OBS, Hotkeys, VAD, Overlay, Ai, Advanced, OverlayEngine, get_app_directory, \
|
|
19
|
+
get_config, is_beangate, AVAILABLE_LANGUAGES, WHSIPER_LARGE, WHISPER_TINY, WHISPER_BASE, WHISPER_SMALL, \
|
|
20
|
+
WHISPER_MEDIUM, WHISPER_TURBO, SILERO, WHISPER, OFF, gsm_state, DEFAULT_CONFIG, get_latest_version, \
|
|
21
|
+
get_current_version, AI_GEMINI, AI_GROQ, AI_OPENAI, save_full_config, get_default_anki_media_collection_path
|
|
16
22
|
from GameSentenceMiner.util.db import AIModelsTable
|
|
17
23
|
from GameSentenceMiner.util.downloader.download_tools import download_ocenaudio_if_needed
|
|
18
24
|
|
|
@@ -378,6 +384,7 @@ class ConfigApp:
|
|
|
378
384
|
self.ocr_websocket_port_value = tk.StringVar(value=str(self.settings.advanced.ocr_websocket_port))
|
|
379
385
|
self.texthooker_communication_websocket_port_value = tk.StringVar(value=str(self.settings.advanced.texthooker_communication_websocket_port))
|
|
380
386
|
self.plaintext_websocket_export_port_value = tk.StringVar(value=str(self.settings.advanced.plaintext_websocket_port))
|
|
387
|
+
self.localhost_bind_address_value = tk.StringVar(value=self.settings.advanced.localhost_bind_address)
|
|
381
388
|
|
|
382
389
|
# AI Settings
|
|
383
390
|
self.ai_enabled_value = tk.BooleanVar(value=self.settings.ai.enabled)
|
|
@@ -401,6 +408,7 @@ class ConfigApp:
|
|
|
401
408
|
self.overlay_engine_value = tk.StringVar(value=self.settings.overlay.engine)
|
|
402
409
|
self.periodic_value = tk.BooleanVar(value=self.settings.overlay.periodic)
|
|
403
410
|
self.periodic_interval_value = tk.StringVar(value=str(self.settings.overlay.periodic_interval))
|
|
411
|
+
self.scan_delay_value = tk.StringVar(value=str(self.settings.overlay.scan_delay))
|
|
404
412
|
|
|
405
413
|
# Master Config Settings
|
|
406
414
|
self.switch_to_default_if_not_found_value = tk.BooleanVar(value=self.master_config.switch_to_default_if_not_found)
|
|
@@ -615,6 +623,7 @@ class ConfigApp:
|
|
|
615
623
|
ocr_websocket_port=int(self.ocr_websocket_port_value.get()),
|
|
616
624
|
texthooker_communication_websocket_port=int(self.texthooker_communication_websocket_port_value.get()),
|
|
617
625
|
plaintext_websocket_port=int(self.plaintext_websocket_export_port_value.get()),
|
|
626
|
+
localhost_bind_address=self.localhost_bind_address_value.get(),
|
|
618
627
|
),
|
|
619
628
|
ai=Ai(
|
|
620
629
|
enabled=self.ai_enabled_value.get(),
|
|
@@ -637,6 +646,7 @@ class ConfigApp:
|
|
|
637
646
|
websocket_port=int(self.overlay_websocket_port_value.get()),
|
|
638
647
|
monitor_to_capture=self.overlay_monitor.current() if self.monitors else 0,
|
|
639
648
|
engine=OverlayEngine(self.overlay_engine_value.get()).value if self.overlay_engine_value.get() else OverlayEngine.LENS.value,
|
|
649
|
+
scan_delay=float(self.scan_delay_value.get()),
|
|
640
650
|
periodic=self.periodic_value.get(),
|
|
641
651
|
periodic_interval=self.periodic_interval_value.get(),
|
|
642
652
|
)
|
|
@@ -1992,6 +2002,12 @@ class ConfigApp:
|
|
|
1992
2002
|
ttk.Entry(advanced_frame, textvariable=self.polling_rate_value).grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
|
1993
2003
|
self.current_row += 1
|
|
1994
2004
|
|
|
2005
|
+
localhost_bind_address_i18n = advanced_i18n.get('localhost_bind_address', {})
|
|
2006
|
+
HoverInfoLabelWidget(advanced_frame, text=localhost_bind_address_i18n.get('label', 'LocalHost Bind Address:'),
|
|
2007
|
+
tooltip=localhost_bind_address_i18n.get('tooltip', 'Set this to 0.0.0.0 if you want to connect from another device in your LAN, otherwise leave as is.'), row=self.current_row, column=0)
|
|
2008
|
+
ttk.Entry(advanced_frame, textvariable=self.localhost_bind_address_value).grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
|
2009
|
+
self.current_row += 1
|
|
2010
|
+
|
|
1995
2011
|
current_ver_i18n = advanced_i18n.get('current_version', {})
|
|
1996
2012
|
HoverInfoLabelWidget(advanced_frame, text=current_ver_i18n.get('label', 'Current Version:'), bootstyle="secondary",
|
|
1997
2013
|
tooltip=current_ver_i18n.get('tooltip', '...'), row=self.current_row, column=0)
|
|
@@ -2279,7 +2295,15 @@ class ConfigApp:
|
|
|
2279
2295
|
textvariable=self.overlay_engine_value)
|
|
2280
2296
|
self.overlay_engine.grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
|
2281
2297
|
self.current_row += 1
|
|
2282
|
-
|
|
2298
|
+
|
|
2299
|
+
# Scan Delay
|
|
2300
|
+
scan_delay_i18n = overlay_i18n.get('scan_delay', {})
|
|
2301
|
+
HoverInfoLabelWidget(overlay_frame, text=scan_delay_i18n.get('label', 'Scan Delay:'),
|
|
2302
|
+
tooltip=scan_delay_i18n.get('tooltip', 'Delay between GSM Receiving Text, and Scanning for Overlay. Increase this value if your game\'s text appears slowly.'),
|
|
2303
|
+
row=self.current_row, column=0)
|
|
2304
|
+
ttk.Entry(overlay_frame, textvariable=self.scan_delay_value).grid(row=self.current_row, column=1, sticky='EW', pady=2)
|
|
2305
|
+
self.current_row += 1
|
|
2306
|
+
|
|
2283
2307
|
# Periodic Settings
|
|
2284
2308
|
periodic_i18n = overlay_i18n.get('periodic', {})
|
|
2285
2309
|
HoverInfoLabelWidget(overlay_frame, text=periodic_i18n.get('label', 'Periodic:'),
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import json
|
|
2
3
|
import re
|
|
4
|
+
from datetime import datetime
|
|
3
5
|
|
|
4
6
|
import pyperclip
|
|
5
7
|
import requests
|
|
@@ -7,12 +9,11 @@ import websockets
|
|
|
7
9
|
from websockets import InvalidStatus
|
|
8
10
|
from rapidfuzz import fuzz
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
from GameSentenceMiner.util.configuration import get_config, gsm_status, logger, gsm_state
|
|
11
13
|
from GameSentenceMiner.util.db import GameLinesTable
|
|
12
14
|
from GameSentenceMiner.util.gsm_utils import do_text_replacements, TEXT_REPLACEMENTS_FILE, run_new_thread
|
|
13
|
-
from GameSentenceMiner.util.configuration import *
|
|
14
|
-
from GameSentenceMiner.util.text_log import *
|
|
15
15
|
from GameSentenceMiner import obs
|
|
16
|
+
from GameSentenceMiner.util.text_log import add_line, get_text_log
|
|
16
17
|
from GameSentenceMiner.web.texthooking_page import add_event_to_texthooker, overlay_server_thread
|
|
17
18
|
|
|
18
19
|
from GameSentenceMiner.util.get_overlay_coords import OverlayProcessor
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
import tempfile
|
|
2
|
-
import time
|
|
3
|
-
import asyncio
|
|
4
|
-
import subprocess
|
|
5
|
-
import sys
|
|
6
|
-
|
|
7
|
-
import os
|
|
8
|
-
import warnings
|
|
9
|
-
|
|
10
|
-
import requests
|
|
11
|
-
|
|
12
|
-
from GameSentenceMiner.util.get_overlay_coords import OverlayThread
|
|
13
|
-
from GameSentenceMiner.util.gsm_utils import remove_html_and_cloze_tags
|
|
14
|
-
|
|
15
|
-
os.environ.pop('TCL_LIBRARY', None)
|
|
16
|
-
|
|
17
|
-
|
|
18
1
|
def handle_error_in_initialization(e):
|
|
19
2
|
"""Handle errors that occur during initialization."""
|
|
20
3
|
logger.exception(e, exc_info=True)
|
|
@@ -29,9 +12,20 @@ def handle_error_in_initialization(e):
|
|
|
29
12
|
|
|
30
13
|
|
|
31
14
|
try:
|
|
15
|
+
import asyncio
|
|
16
|
+
import os
|
|
17
|
+
import shutil
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
import tempfile
|
|
21
|
+
import threading
|
|
22
|
+
import time
|
|
23
|
+
import warnings
|
|
24
|
+
import requests
|
|
32
25
|
import os.path
|
|
33
26
|
import signal
|
|
34
27
|
from subprocess import Popen
|
|
28
|
+
os.environ.pop('TCL_LIBRARY', None)
|
|
35
29
|
|
|
36
30
|
import keyboard
|
|
37
31
|
import ttkbootstrap as ttk
|
|
@@ -42,7 +36,12 @@ try:
|
|
|
42
36
|
import psutil
|
|
43
37
|
|
|
44
38
|
start_time = time.time()
|
|
45
|
-
from GameSentenceMiner.util.configuration import
|
|
39
|
+
from GameSentenceMiner.util.configuration import logger, gsm_state, get_config, anki_results, AnkiUpdateResult, \
|
|
40
|
+
get_temporary_directory, get_log_path, get_master_config, switch_profile_and_save, get_app_directory, gsm_status, \
|
|
41
|
+
is_windows, is_linux
|
|
42
|
+
from GameSentenceMiner.util.get_overlay_coords import OverlayThread
|
|
43
|
+
from GameSentenceMiner.util.gsm_utils import remove_html_and_cloze_tags
|
|
44
|
+
|
|
46
45
|
logger.debug(f"[Import] configuration: {time.time() - start_time:.3f}s")
|
|
47
46
|
|
|
48
47
|
start_time = time.time()
|
|
@@ -113,10 +112,6 @@ try:
|
|
|
113
112
|
logger.debug(
|
|
114
113
|
f"[Import] util.text_log (GameLine, get_text_event, get_mined_line, get_all_lines, game_log): {time.time() - start_time:.3f}s")
|
|
115
114
|
|
|
116
|
-
start_time = time.time()
|
|
117
|
-
from GameSentenceMiner.util import *
|
|
118
|
-
logger.debug(f"[Import] util *: {time.time() - start_time:.3f}s")
|
|
119
|
-
|
|
120
115
|
start_time = time.time()
|
|
121
116
|
from GameSentenceMiner.web import texthooking_page
|
|
122
117
|
logger.debug(
|
|
@@ -675,6 +670,7 @@ def initialize_async():
|
|
|
675
670
|
|
|
676
671
|
|
|
677
672
|
def handle_websocket_message(message: Message):
|
|
673
|
+
logger.info(f"WebSocket Message Received: {message.to_json()}")
|
|
678
674
|
try:
|
|
679
675
|
match FunctionName(message.function):
|
|
680
676
|
case FunctionName.QUIT:
|
|
@@ -683,7 +679,7 @@ def handle_websocket_message(message: Message):
|
|
|
683
679
|
case FunctionName.QUIT_OBS:
|
|
684
680
|
close_obs()
|
|
685
681
|
case FunctionName.START_OBS:
|
|
686
|
-
obs.start_obs()
|
|
682
|
+
obs.start_obs(force_restart=not gsm_status.obs_connected)
|
|
687
683
|
case FunctionName.OPEN_SETTINGS:
|
|
688
684
|
open_settings()
|
|
689
685
|
case FunctionName.OPEN_TEXTHOOKER:
|
|
@@ -165,28 +165,36 @@ class OBSConnectionManager(threading.Thread):
|
|
|
165
165
|
self.counter = 0
|
|
166
166
|
return
|
|
167
167
|
start_replay_buffer()
|
|
168
|
-
|
|
168
|
+
|
|
169
169
|
def get_obs_path():
|
|
170
170
|
return os.path.join(configuration.get_app_directory(), 'obs-studio/bin/64bit/obs64.exe')
|
|
171
171
|
|
|
172
172
|
def is_process_running(pid):
|
|
173
173
|
try:
|
|
174
174
|
process = psutil.Process(pid)
|
|
175
|
-
return 'obs' in process.exe()
|
|
175
|
+
return 'obs' in process.exe().lower()
|
|
176
176
|
except (psutil.NoSuchProcess, psutil.AccessDenied, OSError):
|
|
177
177
|
if os.path.exists(OBS_PID_FILE):
|
|
178
178
|
os.remove(OBS_PID_FILE)
|
|
179
179
|
return False
|
|
180
180
|
|
|
181
|
-
def start_obs():
|
|
181
|
+
def start_obs(force_restart=False):
|
|
182
182
|
global obs_process_pid
|
|
183
183
|
if os.path.exists(OBS_PID_FILE):
|
|
184
184
|
with open(OBS_PID_FILE, "r") as f:
|
|
185
185
|
try:
|
|
186
186
|
obs_process_pid = int(f.read().strip())
|
|
187
187
|
if is_process_running(obs_process_pid):
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
if force_restart:
|
|
189
|
+
try:
|
|
190
|
+
process = psutil.Process(obs_process_pid)
|
|
191
|
+
process.terminate()
|
|
192
|
+
process.wait(timeout=10)
|
|
193
|
+
print("OBS process terminated for restart.")
|
|
194
|
+
except Exception as e:
|
|
195
|
+
print(f"Error terminating OBS process: {e}")
|
|
196
|
+
else:
|
|
197
|
+
return obs_process_pid
|
|
190
198
|
except ValueError:
|
|
191
199
|
print("Invalid PID found in file. Launching new OBS instance.")
|
|
192
200
|
except OSError:
|
|
@@ -693,5 +701,7 @@ def create_scene():
|
|
|
693
701
|
if __name__ == '__main__':
|
|
694
702
|
logging.basicConfig(level=logging.INFO)
|
|
695
703
|
connect_to_obs_sync()
|
|
696
|
-
|
|
697
|
-
|
|
704
|
+
img = get_screenshot_PIL(source_name='Display Capture 2', compression=100, img_format='jpg', width=2560, height=1440)
|
|
705
|
+
img.show()
|
|
706
|
+
# # set_fit_to_screen_for_scene_items(get_current_scene())
|
|
707
|
+
# create_scene()
|
|
@@ -19,13 +19,15 @@ from PIL import Image
|
|
|
19
19
|
from rapidfuzz import fuzz
|
|
20
20
|
|
|
21
21
|
from GameSentenceMiner import obs
|
|
22
|
-
from GameSentenceMiner.util.electron_config import *
|
|
23
22
|
from GameSentenceMiner.ocr.ss_picker import ScreenCropper
|
|
24
23
|
from GameSentenceMiner.owocr.owocr.run import TextFiltering
|
|
25
24
|
from GameSentenceMiner.util.configuration import get_config, get_app_directory, get_temporary_directory
|
|
26
25
|
from GameSentenceMiner.ocr.gsm_ocr_config import OCRConfig, has_config_changed, set_dpi_awareness, get_window, get_ocr_config_path
|
|
27
|
-
from GameSentenceMiner.owocr.owocr import
|
|
28
|
-
from GameSentenceMiner.util.
|
|
26
|
+
from GameSentenceMiner.owocr.owocr import run
|
|
27
|
+
from GameSentenceMiner.util.electron_config import get_ocr_ocr2, get_ocr_send_to_clipboard, get_ocr_scan_rate, \
|
|
28
|
+
has_ocr_config_changed, reload_electron_config, get_ocr_two_pass_ocr, get_ocr_optimize_second_scan, \
|
|
29
|
+
get_ocr_language, get_ocr_manual_ocr_hotkey
|
|
30
|
+
from GameSentenceMiner.util.gsm_utils import sanitize_filename
|
|
29
31
|
import threading
|
|
30
32
|
import time
|
|
31
33
|
|
|
@@ -173,7 +175,7 @@ class WebsocketServerThread(threading.Thread):
|
|
|
173
175
|
self._stop_event = stop_event = asyncio.Event()
|
|
174
176
|
self._event.set()
|
|
175
177
|
self.server = start_server = websockets.serve(self.server_handler,
|
|
176
|
-
|
|
178
|
+
get_config().advanced.localhost_bind_address,
|
|
177
179
|
get_config().advanced.ocr_websocket_port,
|
|
178
180
|
max_size=1000000000)
|
|
179
181
|
async with start_server:
|
|
@@ -420,7 +422,7 @@ def get_ocr2_image(crop_coords, og_image: Image.Image, ocr2_engine=None):
|
|
|
420
422
|
Logic is unchanged, but code is refactored for clarity and maintainability.
|
|
421
423
|
"""
|
|
422
424
|
def return_original_image():
|
|
423
|
-
logger.
|
|
425
|
+
logger.debug("Returning original image for OCR2 (no cropping or optimization).")
|
|
424
426
|
if not crop_coords or not get_ocr_optimize_second_scan():
|
|
425
427
|
return og_image
|
|
426
428
|
x1, y1, x2, y2 = crop_coords
|
|
@@ -546,9 +548,10 @@ def add_ss_hotkey(ss_hotkey="ctrl+shift+g"):
|
|
|
546
548
|
ocr_config = get_ocr_config()
|
|
547
549
|
img = obs.get_screenshot_PIL(compression=80, img_format="jpg")
|
|
548
550
|
ocr_config.scale_to_custom_size(img.width, img.height)
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
551
|
+
for rectangle in [rectangle for rectangle in ocr_config.rectangles if rectangle.is_secondary]:
|
|
552
|
+
new_img = run.apply_ocr_config_to_image(img, ocr_config, is_secondary=True, rectangles=[rectangle])
|
|
553
|
+
do_second_ocr("", datetime.now(), new_img, TextFiltering(lang=get_ocr_language()), ignore_furigana_filter=True, ignore_previous_result=True)
|
|
554
|
+
|
|
552
555
|
filtering = TextFiltering(lang=get_ocr_language())
|
|
553
556
|
cropper = ScreenCropper()
|
|
554
557
|
def capture():
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
1
4
|
from GameSentenceMiner.ocr.gsm_ocr_config import set_dpi_awareness, get_scene_ocr_config
|
|
2
|
-
from GameSentenceMiner.util.electron_config import * # noqa: F403
|
|
3
5
|
from GameSentenceMiner.util.gsm_utils import do_text_replacements, OCR_REPLACEMENTS_FILE
|
|
6
|
+
from GameSentenceMiner.util.electron_config import get_ocr_language, get_ocr_requires_open_window, \
|
|
7
|
+
has_ocr_config_changed, reload_electron_config, get_ocr_scan_rate, get_ocr_two_pass_ocr, get_ocr_keep_newline, \
|
|
8
|
+
get_ocr_ocr1, get_furigana_filter_sensitivity
|
|
4
9
|
|
|
5
10
|
try:
|
|
6
11
|
import win32gui
|
|
@@ -52,7 +57,7 @@ import psutil
|
|
|
52
57
|
from .ocr import * # noqa: F403
|
|
53
58
|
from .config import Config
|
|
54
59
|
from .screen_coordinate_picker import get_screen_selection
|
|
55
|
-
from GameSentenceMiner.util.configuration import get_temporary_directory
|
|
60
|
+
from GameSentenceMiner.util.configuration import get_config, get_temporary_directory
|
|
56
61
|
|
|
57
62
|
from skimage.metrics import structural_similarity as ssim
|
|
58
63
|
from typing import Union
|
|
@@ -286,7 +291,7 @@ class WebsocketServerThread(threading.Thread):
|
|
|
286
291
|
self._stop_event = stop_event = asyncio.Event()
|
|
287
292
|
self._event.set()
|
|
288
293
|
self.server = start_server = websockets.serve(
|
|
289
|
-
self.server_handler,
|
|
294
|
+
self.server_handler, get_config().advanced.localhost_bind_address, config.get_general('websocket_port'), max_size=1000000000)
|
|
290
295
|
async with start_server:
|
|
291
296
|
await stop_event.wait()
|
|
292
297
|
asyncio.run(main())
|
|
@@ -1135,14 +1140,15 @@ def scale_down_width_height(width, height):
|
|
|
1135
1140
|
return width, height
|
|
1136
1141
|
|
|
1137
1142
|
|
|
1138
|
-
def apply_ocr_config_to_image(img, ocr_config, is_secondary=False):
|
|
1143
|
+
def apply_ocr_config_to_image(img, ocr_config, is_secondary=False, rectangles=None):
|
|
1139
1144
|
for rectangle in ocr_config.rectangles:
|
|
1140
1145
|
if rectangle.is_excluded:
|
|
1141
1146
|
left, top, width, height = rectangle.coordinates
|
|
1142
1147
|
draw = ImageDraw.Draw(img)
|
|
1143
1148
|
draw.rectangle((left, top, left + width, top + height), fill=(0, 0, 0, 0))
|
|
1144
1149
|
|
|
1145
|
-
|
|
1150
|
+
if not rectangles:
|
|
1151
|
+
rectangles = [r for r in ocr_config.rectangles if not r.is_excluded and r.is_secondary == is_secondary]
|
|
1146
1152
|
|
|
1147
1153
|
# Sort top to bottom
|
|
1148
1154
|
if rectangles:
|
{gamesentenceminer-2.17.0 → gamesentenceminer-2.17.2}/GameSentenceMiner/util/configuration.py
RENAMED
|
@@ -589,9 +589,10 @@ class Advanced:
|
|
|
589
589
|
multi_line_sentence_storage_field: str = ''
|
|
590
590
|
ocr_websocket_port: int = 9002
|
|
591
591
|
texthooker_communication_websocket_port: int = 55001
|
|
592
|
-
afk_timer_seconds: int = 120
|
|
593
|
-
session_gap_seconds: int = 3600
|
|
594
|
-
streak_requirement_hours: float = 0.01 #
|
|
592
|
+
afk_timer_seconds: int = 120 # LEGACY, not used anymore
|
|
593
|
+
session_gap_seconds: int = 3600 # LEGACY, not used anymore
|
|
594
|
+
streak_requirement_hours: float = 0.01 # LEGACY, not used anymore
|
|
595
|
+
localhost_bind_address: str = '127.0.0.1' # Default 127.0.0.1 for security, set to 0.0.0.0 to allow external connections
|
|
595
596
|
|
|
596
597
|
def __post_init__(self):
|
|
597
598
|
if self.plaintext_websocket_port == -1:
|
|
@@ -646,6 +647,7 @@ class Overlay:
|
|
|
646
647
|
monitor_to_capture: int = 0
|
|
647
648
|
periodic: bool = False
|
|
648
649
|
periodic_interval: float = 1.0
|
|
650
|
+
scan_delay: float = 0.25
|
|
649
651
|
|
|
650
652
|
def __post_init__(self):
|
|
651
653
|
if self.monitor_to_capture == -1:
|
|
@@ -1248,5 +1250,5 @@ is_dev = is_running_from_source()
|
|
|
1248
1250
|
|
|
1249
1251
|
is_beangate = os.path.exists("C:/Users/Beangate")
|
|
1250
1252
|
|
|
1251
|
-
logger.debug(f"Running in development mode: {is_dev}")
|
|
1252
|
-
logger.debug(f"Running on Beangate's PC: {is_beangate}")
|
|
1253
|
+
# logger.debug(f"Running in development mode: {is_dev}")
|
|
1254
|
+
# logger.debug(f"Running on Beangate's PC: {is_beangate}")
|