GameSentenceMiner 2.21.25__tar.gz → 2.21.29__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.
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/ss_picker_qt.py +178 -36
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/goals.js +21 -20
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner.egg-info/PKG-INFO +1 -1
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/PKG-INFO +1 -1
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/pyproject.toml +1 -1
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ai/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ai/ai_prompting.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/anki.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon128.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon256.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon32.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon512.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon64.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/pickaxe.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/discord_rpc.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/gametext.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/gsm.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/live_stats.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/locales/en_us.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/locales/es_es.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/locales/ja_jp.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/locales/zh_cn.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/obs.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/gsm_ocr_config.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/ocrconfig.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/owocr_area_selector.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/owocr_area_selector_qt.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/owocr_helper.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/ss_picker.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/__main__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/config.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/lens_betterproto.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/ocr.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/run.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/tools/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/tools/audio_offset_selector.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/tools/furigana_filter_preview.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/tools/ss_selector.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/tools/window_transparency.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/anki_confirmation.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/anki_confirmation_qt.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/config_gui.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/config_gui_qt.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/furigana_filter_preview.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/furigana_filter_preview_qt.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/qt_main.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/screenshot_selector.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/screenshot_selector_qt.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/audio_player.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/communication/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/communication/electron_ipc.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/communication/send.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/configuration.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/cron/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/cron/daily_rollup.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/cron/jiten_update.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/cron/populate_games.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/cron/run_crons.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/cron/setup_populate_games_cron.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/cron_table.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/db.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/downloader/Untitled_json.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/downloader/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/downloader/download_tools.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/downloader/oneocr_dl.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/electron_config.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/ffmpeg.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/games_table.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/get_overlay_coords.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/gsm_utils.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/hotkey.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/jiten_api_client.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/model.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/notification.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/stats_rollup_table.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/text_log.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/win10toast/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/win10toast/__main__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/vad.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/anki_api_endpoints.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/database_api.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/events.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/goals_api.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/gsm_websocket.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/jiten_database_api.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/rollup_stats.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/service.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/__init__.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/apple-touch-icon.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/dashboard-shared.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/goals.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/kanji-grid.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/loading-skeleton.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/overview.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/popups-shared.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/search.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/shared.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/css/stats.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/favicon-96x96.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/favicon.ico +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/favicon.svg +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/anki_stats.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/components/bar-chart.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database-bulk-operations.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database-game-data.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database-game-operations.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database-helpers.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database-jiten-integration.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database-popups.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database-tabs.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database-text-management.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/database.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/heatmap.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/kanji-grid.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/overview.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/regex-patterns.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/search.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/shared.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/stats.js +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/site.webmanifest +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/style.css +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/web-app-manifest-192x192.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/web-app-manifest-512x512.png +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/stats.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/stats_api.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/anki_stats.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/date-range.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/html-head.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/js-config.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/basic_kanji_book_bkb_v1_v2.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/duolingo_kanji.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/grade.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/hk_primary_learning.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/hkscs2016.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/hsk_levels.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/humanum_frequency_list.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/jis_levels.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/jlpt_level.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/jpdb_kanji_frequency_list.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/jpdbv2_kanji_frequency_list.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/jun_das_modern_chinese_character_frequency_list.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/kanji_in_context_revised_edition.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/kanji_kentei_level.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/mainland_china_elementary_textbook_characters.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/moe_way_quiz.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/official_kanji.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/remembering_the_kanji.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/standard_form_of_national_characters.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/table_of_general_standard_chinese_characters.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/the_kodansha_kanji_learners_course_klc.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/thousand_character_classic.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/wanikani_levels.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/kanji_grid/words_hk_frequency_list.json +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/navigation.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/popups.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/regex-input.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/settings-modal.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/components/theme-styles.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/database.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/goals.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/index.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/overview.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/search.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/stats.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/templates/utility.html +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/texthooking_page.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/wip/__init___.py +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner.egg-info/SOURCES.txt +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner.egg-info/dependency_links.txt +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner.egg-info/entry_points.txt +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner.egg-info/requires.txt +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner.egg-info/top_level.txt +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/LICENSE +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/README.md +0 -0
- {gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/setup.cfg +0 -0
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/ss_picker_qt.py
RENAMED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import io
|
|
2
1
|
import logging
|
|
3
2
|
from PIL import Image
|
|
4
3
|
import mss
|
|
@@ -7,15 +6,71 @@ from PyQt6.QtWidgets import QApplication, QWidget
|
|
|
7
6
|
from PyQt6.QtCore import Qt, QRect
|
|
8
7
|
from PyQt6.QtGui import QPainter, QPen, QColor, QPixmap, QImage
|
|
9
8
|
import sys
|
|
9
|
+
import ctypes
|
|
10
|
+
import ctypes.wintypes
|
|
10
11
|
|
|
11
12
|
# Import Window State Manager
|
|
12
13
|
from GameSentenceMiner.ui import window_state_manager, WindowId
|
|
13
14
|
|
|
14
15
|
logger = logging.getLogger("GSM_OCR")
|
|
15
16
|
|
|
17
|
+
|
|
18
|
+
def get_monitor_dpi_scale():
|
|
19
|
+
"""
|
|
20
|
+
Get DPI scaling information for all monitors.
|
|
21
|
+
Returns a dict mapping monitor index to scale factor.
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
# Get DPI awareness
|
|
25
|
+
user32 = ctypes.windll.user32
|
|
26
|
+
user32.SetProcessDPIAware()
|
|
27
|
+
|
|
28
|
+
# Enumerate all monitors and their DPI
|
|
29
|
+
monitors_dpi = {}
|
|
30
|
+
|
|
31
|
+
def callback(hMonitor, hdcMonitor, lprcMonitor, dwData):
|
|
32
|
+
try:
|
|
33
|
+
# Get DPI for this monitor
|
|
34
|
+
shcore = ctypes.windll.shcore
|
|
35
|
+
dpiX = ctypes.c_uint()
|
|
36
|
+
dpiY = ctypes.c_uint()
|
|
37
|
+
|
|
38
|
+
# MDT_EFFECTIVE_DPI = 0
|
|
39
|
+
shcore.GetDpiForMonitor(hMonitor, 0, ctypes.byref(dpiX), ctypes.byref(dpiY))
|
|
40
|
+
|
|
41
|
+
# Standard DPI is 96, so scale factor is dpi/96
|
|
42
|
+
scale = dpiX.value / 96.0
|
|
43
|
+
|
|
44
|
+
# Store by monitor count
|
|
45
|
+
idx = len(monitors_dpi)
|
|
46
|
+
monitors_dpi[idx] = scale
|
|
47
|
+
logger.debug(f"Monitor {idx}: DPI={dpiX.value}, Scale={scale:.2f}")
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.warning(f"Failed to get DPI for monitor: {e}")
|
|
50
|
+
monitors_dpi[len(monitors_dpi)] = 1.0
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
# Define callback type
|
|
54
|
+
MonitorEnumProc = ctypes.WINFUNCTYPE(
|
|
55
|
+
ctypes.c_bool,
|
|
56
|
+
ctypes.wintypes.HMONITOR,
|
|
57
|
+
ctypes.wintypes.HDC,
|
|
58
|
+
ctypes.POINTER(ctypes.wintypes.RECT),
|
|
59
|
+
ctypes.wintypes.LPARAM
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Enumerate monitors
|
|
63
|
+
user32.EnumDisplayMonitors(None, None, MonitorEnumProc(callback), 0)
|
|
64
|
+
|
|
65
|
+
return monitors_dpi
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.warning(f"Failed to get monitor DPI scaling: {e}. Using 1.0")
|
|
68
|
+
return {}
|
|
69
|
+
|
|
16
70
|
# Global instance
|
|
17
71
|
_screen_cropper_instance = None
|
|
18
72
|
|
|
73
|
+
|
|
19
74
|
class ScreenCropperWidget(QWidget):
|
|
20
75
|
def __init__(self, parent=None):
|
|
21
76
|
super().__init__(parent)
|
|
@@ -32,6 +87,11 @@ class ScreenCropperWidget(QWidget):
|
|
|
32
87
|
self.result = None
|
|
33
88
|
self.pixmap = None
|
|
34
89
|
|
|
90
|
+
# DPI scaling factors
|
|
91
|
+
self.dpi_scale_x = 1.0
|
|
92
|
+
self.dpi_scale_y = 1.0
|
|
93
|
+
self.physical_to_logical_scale = 1.0
|
|
94
|
+
|
|
35
95
|
# Drawing state
|
|
36
96
|
self.start_pos = None
|
|
37
97
|
self.current_pos = None
|
|
@@ -39,9 +99,18 @@ class ScreenCropperWidget(QWidget):
|
|
|
39
99
|
|
|
40
100
|
self.setCursor(Qt.CursorShape.CrossCursor)
|
|
41
101
|
|
|
42
|
-
def prepare_capture(self, captured_image, monitor_geometry, main_monitor,
|
|
102
|
+
def prepare_capture(self, captured_image, monitor_geometry, main_monitor,
|
|
103
|
+
on_complete=None, transparent_mode=False, dpi_scale=1.0):
|
|
43
104
|
"""
|
|
44
105
|
Resets the widget state for a new capture session.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
captured_image: PIL Image captured at physical pixel resolution
|
|
109
|
+
monitor_geometry: Physical pixel coordinates from mss
|
|
110
|
+
main_monitor: Main monitor info from mss
|
|
111
|
+
on_complete: Callback function
|
|
112
|
+
transparent_mode: Whether to use transparent overlay mode
|
|
113
|
+
dpi_scale: DPI scale factor for coordinate conversion
|
|
45
114
|
"""
|
|
46
115
|
self.captured_image = captured_image
|
|
47
116
|
self.monitor_geometry = monitor_geometry
|
|
@@ -49,27 +118,46 @@ class ScreenCropperWidget(QWidget):
|
|
|
49
118
|
self.on_complete = on_complete
|
|
50
119
|
self.transparent_mode = transparent_mode
|
|
51
120
|
self.result = None
|
|
121
|
+
self.physical_to_logical_scale = dpi_scale
|
|
52
122
|
|
|
53
123
|
# Reset drawing state
|
|
54
124
|
self.start_pos = None
|
|
55
125
|
self.current_pos = None
|
|
56
126
|
self.is_drawing = False
|
|
57
127
|
|
|
128
|
+
# Calculate logical coordinates for Qt widget
|
|
129
|
+
# mss gives us physical pixels, Qt uses logical pixels
|
|
130
|
+
logical_left = int(self.monitor_geometry['left'] / dpi_scale)
|
|
131
|
+
logical_top = int(self.monitor_geometry['top'] / dpi_scale)
|
|
132
|
+
logical_width = int(self.monitor_geometry['width'] / dpi_scale)
|
|
133
|
+
logical_height = int(self.monitor_geometry['height'] / dpi_scale)
|
|
134
|
+
|
|
58
135
|
if not self.transparent_mode and self.captured_image:
|
|
59
|
-
#
|
|
136
|
+
# Keep the image at full physical resolution for best quality
|
|
137
|
+
# Qt will handle the scaling automatically via devicePixelRatio
|
|
60
138
|
img_data = self.captured_image.tobytes('raw', 'RGB')
|
|
61
|
-
qimage = QImage(
|
|
62
|
-
|
|
139
|
+
qimage = QImage(
|
|
140
|
+
img_data,
|
|
141
|
+
self.captured_image.width,
|
|
142
|
+
self.captured_image.height,
|
|
143
|
+
self.captured_image.width * 3,
|
|
144
|
+
QImage.Format.Format_RGB888
|
|
145
|
+
)
|
|
63
146
|
self.pixmap = QPixmap.fromImage(qimage)
|
|
147
|
+
# Set device pixel ratio so Qt knows this is a high-DPI image
|
|
148
|
+
self.pixmap.setDevicePixelRatio(dpi_scale)
|
|
149
|
+
logger.debug(f"Pixmap size: {self.pixmap.width()}x{self.pixmap.height()}, DPR: {dpi_scale}")
|
|
64
150
|
else:
|
|
65
151
|
self.pixmap = None
|
|
66
152
|
|
|
67
153
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, self.transparent_mode)
|
|
68
154
|
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
self.
|
|
72
|
-
|
|
155
|
+
# Set widget geometry using logical coordinates
|
|
156
|
+
self.move(logical_left, logical_top)
|
|
157
|
+
self.resize(logical_width, logical_height)
|
|
158
|
+
|
|
159
|
+
logger.info(f"Widget positioned at logical ({logical_left}, {logical_top}) "
|
|
160
|
+
f"size {logical_width}x{logical_height}, DPI scale: {dpi_scale:.2f}")
|
|
73
161
|
|
|
74
162
|
self.show()
|
|
75
163
|
self.activateWindow()
|
|
@@ -104,12 +192,11 @@ class ScreenCropperWidget(QWidget):
|
|
|
104
192
|
else:
|
|
105
193
|
# Original mode with screenshot background
|
|
106
194
|
if self.pixmap:
|
|
107
|
-
|
|
195
|
+
# Scale the drawing to account for device pixel ratio
|
|
196
|
+
# The pixmap has devicePixelRatio set, so it will draw at the correct size
|
|
197
|
+
painter.drawPixmap(0, 0, self.width(), self.height(), self.pixmap)
|
|
108
198
|
|
|
109
|
-
#
|
|
110
|
-
painter.fillRect(self.rect(), QColor(0, 0, 0, 128))
|
|
111
|
-
|
|
112
|
-
# If we're drawing a selection, clear that area and draw border
|
|
199
|
+
# If we're drawing a selection, only overlay outside the selection
|
|
113
200
|
if self.start_pos and self.current_pos:
|
|
114
201
|
x1 = min(self.start_pos.x(), self.current_pos.x())
|
|
115
202
|
y1 = min(self.start_pos.y(), self.current_pos.y())
|
|
@@ -118,19 +205,29 @@ class ScreenCropperWidget(QWidget):
|
|
|
118
205
|
|
|
119
206
|
selection_rect = QRect(x1, y1, x2 - x1, y2 - y1)
|
|
120
207
|
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
painter.fillRect(selection_rect, Qt.GlobalColor.transparent)
|
|
124
|
-
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceOver)
|
|
208
|
+
# Draw overlay everywhere except selection area
|
|
209
|
+
overlay_color = QColor(0, 0, 0, 128)
|
|
125
210
|
|
|
126
|
-
#
|
|
127
|
-
if
|
|
128
|
-
painter.
|
|
211
|
+
# Top rectangle
|
|
212
|
+
if y1 > 0:
|
|
213
|
+
painter.fillRect(0, 0, self.width(), y1, overlay_color)
|
|
214
|
+
# Bottom rectangle
|
|
215
|
+
if y2 < self.height():
|
|
216
|
+
painter.fillRect(0, y2, self.width(), self.height() - y2, overlay_color)
|
|
217
|
+
# Left rectangle
|
|
218
|
+
if x1 > 0:
|
|
219
|
+
painter.fillRect(0, y1, x1, y2 - y1, overlay_color)
|
|
220
|
+
# Right rectangle
|
|
221
|
+
if x2 < self.width():
|
|
222
|
+
painter.fillRect(x2, y1, self.width() - x2, y2 - y1, overlay_color)
|
|
129
223
|
|
|
130
224
|
# Draw red border
|
|
131
225
|
pen = QPen(QColor(255, 0, 0), 3)
|
|
132
226
|
painter.setPen(pen)
|
|
133
227
|
painter.drawRect(selection_rect)
|
|
228
|
+
else:
|
|
229
|
+
# No selection, draw overlay over everything
|
|
230
|
+
painter.fillRect(self.rect(), QColor(0, 0, 0, 128))
|
|
134
231
|
|
|
135
232
|
def mousePressEvent(self, event):
|
|
136
233
|
if event.button() == Qt.MouseButton.LeftButton:
|
|
@@ -157,27 +254,41 @@ class ScreenCropperWidget(QWidget):
|
|
|
157
254
|
if (x2 - x1) > 0 and (y2 - y1) > 0:
|
|
158
255
|
if self.transparent_mode:
|
|
159
256
|
# In transparent mode, take a fresh screenshot of the selected area
|
|
257
|
+
# Convert widget logical coordinates to physical screen coordinates
|
|
160
258
|
try:
|
|
161
259
|
with mss.mss() as sct:
|
|
162
|
-
#
|
|
260
|
+
# Widget coordinates are in logical pixels, need to convert to physical
|
|
261
|
+
physical_x1 = int(x1 * self.physical_to_logical_scale)
|
|
262
|
+
physical_y1 = int(y1 * self.physical_to_logical_scale)
|
|
263
|
+
physical_x2 = int(x2 * self.physical_to_logical_scale)
|
|
264
|
+
physical_y2 = int(y2 * self.physical_to_logical_scale)
|
|
265
|
+
|
|
163
266
|
monitor_region = {
|
|
164
|
-
"left": self.monitor_geometry['left'] +
|
|
165
|
-
"top": self.monitor_geometry['top'] +
|
|
166
|
-
"width":
|
|
167
|
-
"height":
|
|
267
|
+
"left": self.monitor_geometry['left'] + physical_x1,
|
|
268
|
+
"top": self.monitor_geometry['top'] + physical_y1,
|
|
269
|
+
"width": physical_x2 - physical_x1,
|
|
270
|
+
"height": physical_y2 - physical_y1
|
|
168
271
|
}
|
|
169
272
|
sct_grab = sct.grab(monitor_region)
|
|
170
273
|
# Convert to PIL Image
|
|
171
274
|
self.result = Image.frombytes('RGB', sct_grab.size, sct_grab.bgra, 'raw', 'BGRX')
|
|
172
|
-
logger.info(f"Fresh screenshot captured: ({monitor_region['left']}, {monitor_region['top']})
|
|
275
|
+
logger.info(f"Fresh screenshot captured: ({monitor_region['left']}, {monitor_region['top']}) "
|
|
276
|
+
f"size {monitor_region['width']}x{monitor_region['height']} (physical pixels)")
|
|
173
277
|
except Exception as e:
|
|
174
278
|
logger.error(f"Error capturing fresh screenshot: {e}")
|
|
175
279
|
self.result = None
|
|
176
280
|
else:
|
|
177
281
|
# Original mode: crop from the already-captured image
|
|
282
|
+
# Widget coordinates are in logical pixels, need to convert to physical for cropping
|
|
178
283
|
if self.captured_image:
|
|
179
|
-
|
|
180
|
-
|
|
284
|
+
physical_x1 = int(x1 * self.physical_to_logical_scale)
|
|
285
|
+
physical_y1 = int(y1 * self.physical_to_logical_scale)
|
|
286
|
+
physical_x2 = int(x2 * self.physical_to_logical_scale)
|
|
287
|
+
physical_y2 = int(y2 * self.physical_to_logical_scale)
|
|
288
|
+
|
|
289
|
+
self.result = self.captured_image.crop((physical_x1, physical_y1, physical_x2, physical_y2))
|
|
290
|
+
logger.info(f"Selection made: logical ({x1}, {y1}) to ({x2}, {y2}), "
|
|
291
|
+
f"physical ({physical_x1}, {physical_y1}) to ({physical_x2}, {physical_y2})")
|
|
181
292
|
|
|
182
293
|
# Hide instead of close to preserve instance
|
|
183
294
|
self._finish()
|
|
@@ -194,12 +305,19 @@ class ScreenCropperWidget(QWidget):
|
|
|
194
305
|
self._finish()
|
|
195
306
|
elif event.key() == Qt.Key.Key_Return or event.key() == Qt.Key.Key_Enter:
|
|
196
307
|
# Grab main monitor area
|
|
308
|
+
# Convert main monitor coords from physical to logical for cropping
|
|
197
309
|
if self.captured_image:
|
|
310
|
+
# Main monitor coordinates are in physical pixels
|
|
311
|
+
main_left = self.main_monitor['left'] - self.monitor_geometry['left']
|
|
312
|
+
main_top = self.main_monitor['top'] - self.monitor_geometry['top']
|
|
313
|
+
main_right = main_left + self.main_monitor['width']
|
|
314
|
+
main_bottom = main_top + self.main_monitor['height']
|
|
315
|
+
|
|
198
316
|
self.result = self.captured_image.crop((
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
317
|
+
main_left,
|
|
318
|
+
main_top,
|
|
319
|
+
main_right,
|
|
320
|
+
main_bottom
|
|
203
321
|
))
|
|
204
322
|
logger.info("Main monitor area selected")
|
|
205
323
|
self._finish()
|
|
@@ -225,9 +343,13 @@ def show_screen_cropper(on_complete=None, transparent_mode=False):
|
|
|
225
343
|
"""
|
|
226
344
|
Displays a Qt-based screen cropper that allows the user to select a region.
|
|
227
345
|
Reuses the existing widget instance.
|
|
346
|
+
Properly handles Windows DPI scaling.
|
|
228
347
|
"""
|
|
229
348
|
global _screen_cropper_instance
|
|
230
349
|
|
|
350
|
+
# Get DPI scaling information for all monitors
|
|
351
|
+
monitors_dpi = get_monitor_dpi_scale()
|
|
352
|
+
|
|
231
353
|
if not transparent_mode:
|
|
232
354
|
# Original mode: capture screen first
|
|
233
355
|
try:
|
|
@@ -235,19 +357,27 @@ def show_screen_cropper(on_complete=None, transparent_mode=False):
|
|
|
235
357
|
with mss.mss() as sct:
|
|
236
358
|
all_monitors_bbox = sct.monitors[0]
|
|
237
359
|
main_monitor = sct.monitors[1]
|
|
360
|
+
|
|
361
|
+
# Determine DPI scale - use primary monitor's scale
|
|
362
|
+
# mss monitor indices start at 1 for actual monitors
|
|
363
|
+
dpi_scale = monitors_dpi.get(0, 1.0) # Monitor 0 in our enum is monitor 1 in mss
|
|
364
|
+
|
|
238
365
|
monitor_geometry = {
|
|
239
366
|
'left': all_monitors_bbox['left'],
|
|
240
367
|
'top': all_monitors_bbox['top'],
|
|
241
368
|
'width': all_monitors_bbox['width'],
|
|
242
369
|
'height': all_monitors_bbox['height']
|
|
243
370
|
}
|
|
371
|
+
|
|
372
|
+
# mss captures at physical pixel resolution
|
|
244
373
|
sct_grab = sct.grab(all_monitors_bbox)
|
|
245
374
|
|
|
246
375
|
# Convert directly from raw bytes to PIL Image (faster than to_png)
|
|
247
376
|
# MSS returns BGRA format, convert to RGB
|
|
248
377
|
captured_image = Image.frombytes('RGB', sct_grab.size, sct_grab.bgra, 'raw', 'BGRX')
|
|
249
378
|
|
|
250
|
-
logger.info(f"Screen captured: {monitor_geometry['width']}x{monitor_geometry['height']}"
|
|
379
|
+
logger.info(f"Screen captured: {monitor_geometry['width']}x{monitor_geometry['height']} "
|
|
380
|
+
f"(physical pixels), DPI scale: {dpi_scale:.2f}")
|
|
251
381
|
except Exception as e:
|
|
252
382
|
logger.error(f"Error capturing screen: {e}")
|
|
253
383
|
if on_complete:
|
|
@@ -259,6 +389,10 @@ def show_screen_cropper(on_complete=None, transparent_mode=False):
|
|
|
259
389
|
with mss.mss() as sct:
|
|
260
390
|
all_monitors_bbox = sct.monitors[0]
|
|
261
391
|
main_monitor = sct.monitors[1]
|
|
392
|
+
|
|
393
|
+
# Determine DPI scale
|
|
394
|
+
dpi_scale = monitors_dpi.get(0, 1.0)
|
|
395
|
+
|
|
262
396
|
monitor_geometry = {
|
|
263
397
|
'left': all_monitors_bbox['left'],
|
|
264
398
|
'top': all_monitors_bbox['top'],
|
|
@@ -266,7 +400,8 @@ def show_screen_cropper(on_complete=None, transparent_mode=False):
|
|
|
266
400
|
'height': all_monitors_bbox['height']
|
|
267
401
|
}
|
|
268
402
|
captured_image = None
|
|
269
|
-
logger.info(f"Transparent mode: monitor geometry {monitor_geometry['width']}x{monitor_geometry['height']}"
|
|
403
|
+
logger.info(f"Transparent mode: monitor geometry {monitor_geometry['width']}x{monitor_geometry['height']} "
|
|
404
|
+
f"(physical pixels), DPI scale: {dpi_scale:.2f}")
|
|
270
405
|
except Exception as e:
|
|
271
406
|
logger.error(f"Error getting monitor geometry: {e}")
|
|
272
407
|
if on_complete:
|
|
@@ -283,7 +418,14 @@ def show_screen_cropper(on_complete=None, transparent_mode=False):
|
|
|
283
418
|
_screen_cropper_instance = ScreenCropperWidget()
|
|
284
419
|
|
|
285
420
|
# Prepare the widget with the new screenshot data
|
|
286
|
-
_screen_cropper_instance.prepare_capture(
|
|
421
|
+
_screen_cropper_instance.prepare_capture(
|
|
422
|
+
captured_image,
|
|
423
|
+
monitor_geometry,
|
|
424
|
+
main_monitor,
|
|
425
|
+
on_complete,
|
|
426
|
+
transparent_mode,
|
|
427
|
+
dpi_scale
|
|
428
|
+
)
|
|
287
429
|
|
|
288
430
|
# Keep the widget alive by entering event loop until it's hidden
|
|
289
431
|
if on_complete:
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/web/static/js/goals.js
RENAMED
|
@@ -188,6 +188,13 @@ const GoalsUtils = {
|
|
|
188
188
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
189
189
|
},
|
|
190
190
|
|
|
191
|
+
// Parse YYYY-MM-DD date string as local date (not UTC)
|
|
192
|
+
parseLocalDate(dateStr) {
|
|
193
|
+
if (!dateStr) return null;
|
|
194
|
+
const [year, month, day] = dateStr.split('-').map(Number);
|
|
195
|
+
return new Date(year, month - 1, day); // month is 0-indexed
|
|
196
|
+
},
|
|
197
|
+
|
|
191
198
|
// Get goals from database (uses cache if available)
|
|
192
199
|
async getGoalsWithFallback() {
|
|
193
200
|
const data = GoalsDataManager.getCached() || await GoalsDataManager.fetchCurrent();
|
|
@@ -395,7 +402,7 @@ const CustomGoalCheckboxManager = {
|
|
|
395
402
|
|
|
396
403
|
// Start from the most recent completion date
|
|
397
404
|
let streak = 1;
|
|
398
|
-
let prevDate =
|
|
405
|
+
let prevDate = GoalsUtils.parseLocalDate(sortedDates[0]);
|
|
399
406
|
|
|
400
407
|
// Count consecutive days backwards from the most recent date
|
|
401
408
|
for (let i = 1; i < sortedDates.length; i++) {
|
|
@@ -477,9 +484,7 @@ const CustomGoalsManager = {
|
|
|
477
484
|
|
|
478
485
|
// Get active goals (within current date or future)
|
|
479
486
|
async getActive() {
|
|
480
|
-
const
|
|
481
|
-
today.setHours(0, 0, 0, 0);
|
|
482
|
-
const todayStr = today.toISOString().split('T')[0];
|
|
487
|
+
const todayStr = GoalsUtils.getTodayDateString();
|
|
483
488
|
|
|
484
489
|
const allGoals = await this.getAll();
|
|
485
490
|
return allGoals.filter(goal => {
|
|
@@ -491,9 +496,7 @@ const CustomGoalsManager = {
|
|
|
491
496
|
|
|
492
497
|
// Get goals that are currently in progress (today is within date range)
|
|
493
498
|
async getInProgress() {
|
|
494
|
-
const
|
|
495
|
-
today.setHours(0, 0, 0, 0);
|
|
496
|
-
const todayStr = today.toISOString().split('T')[0];
|
|
499
|
+
const todayStr = GoalsUtils.getTodayDateString();
|
|
497
500
|
|
|
498
501
|
const allGoals = await this.getAll();
|
|
499
502
|
return allGoals.filter(goal => {
|
|
@@ -729,15 +732,15 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
729
732
|
|
|
730
733
|
const progressBarClass = `completion-${Math.floor(percentage / 25) * 25}`;
|
|
731
734
|
|
|
732
|
-
// Format dates for display
|
|
733
|
-
const startDate =
|
|
734
|
-
const endDate =
|
|
735
|
+
// Format dates for display - parse YYYY-MM-DD as local date
|
|
736
|
+
const startDate = GoalsUtils.parseLocalDate(goal.startDate);
|
|
737
|
+
const endDate = GoalsUtils.parseLocalDate(goal.endDate);
|
|
735
738
|
let formattedStartDate = 'N/A';
|
|
736
739
|
|
|
737
|
-
if (startDate >= new Date(1980, 0, 1)) {
|
|
740
|
+
if (startDate && startDate >= new Date(1980, 0, 1)) {
|
|
738
741
|
formattedStartDate = startDate.toLocaleDateString(navigator.language, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
739
742
|
}
|
|
740
|
-
const formattedEndDate = endDate.toLocaleDateString(navigator.language, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
743
|
+
const formattedEndDate = endDate ? endDate.toLocaleDateString(navigator.language, { month: 'short', day: 'numeric', year: 'numeric' }) : 'N/A';
|
|
741
744
|
|
|
742
745
|
return `
|
|
743
746
|
<div class="goal-progress-item custom-goal-item" data-goal-id="${goal.id}">
|
|
@@ -1148,9 +1151,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
1148
1151
|
formattedProjection = projectionData.projection.toLocaleString();
|
|
1149
1152
|
}
|
|
1150
1153
|
|
|
1151
|
-
// Format target date
|
|
1152
|
-
const targetDate =
|
|
1153
|
-
const formattedTargetDate = targetDate.toLocaleDateString(navigator.language);
|
|
1154
|
+
// Format target date - parse YYYY-MM-DD as local date
|
|
1155
|
+
const targetDate = GoalsUtils.parseLocalDate(projectionData.end_date);
|
|
1156
|
+
const formattedTargetDate = targetDate ? targetDate.toLocaleDateString(navigator.language) : 'N/A';
|
|
1154
1157
|
|
|
1155
1158
|
// Calculate projected completion date
|
|
1156
1159
|
const remaining = Math.max(0, projectionData.target - projectionData.current);
|
|
@@ -1231,9 +1234,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
1231
1234
|
|
|
1232
1235
|
// Filter for only the 5 core metrics and goals that have started
|
|
1233
1236
|
const coreMetrics = ['hours', 'characters', 'games', 'cards', 'mature_cards'];
|
|
1234
|
-
const
|
|
1235
|
-
today.setHours(0, 0, 0, 0);
|
|
1236
|
-
const todayStr = today.toISOString().split('T')[0];
|
|
1237
|
+
const todayStr = GoalsUtils.getTodayDateString();
|
|
1237
1238
|
|
|
1238
1239
|
const customGoalsWithProjections = customGoals.filter(goal => {
|
|
1239
1240
|
// Must be a core metric
|
|
@@ -1387,8 +1388,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
|
|
1387
1388
|
// Helper to calculate days between two dates (inclusive)
|
|
1388
1389
|
function getDaysBetween(start, end) {
|
|
1389
1390
|
if (!start || !end) return null;
|
|
1390
|
-
const startDate =
|
|
1391
|
-
const endDate =
|
|
1391
|
+
const startDate = GoalsUtils.parseLocalDate(start);
|
|
1392
|
+
const endDate = GoalsUtils.parseLocalDate(end);
|
|
1392
1393
|
if (isNaN(startDate) || isNaN(endDate)) return null;
|
|
1393
1394
|
// Add 1 to include both start and end dates
|
|
1394
1395
|
return Math.floor((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GameSentenceMiner
|
|
3
|
-
Version: 2.21.
|
|
3
|
+
Version: 2.21.29
|
|
4
4
|
Summary: A tool for mining sentences from games. Update: Dependencies, replay buffer based line searching, and bug fixes.
|
|
5
5
|
Author-email: Beangate <bpwhelan95@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GameSentenceMiner
|
|
3
|
-
Version: 2.21.
|
|
3
|
+
Version: 2.21.29
|
|
4
4
|
Summary: A tool for mining sentences from games. Update: Dependencies, replay buffer based line searching, and bug fixes.
|
|
5
5
|
Author-email: Beangate <bpwhelan95@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "GameSentenceMiner"
|
|
10
|
-
version = "2.21.
|
|
10
|
+
version = "2.21.29"
|
|
11
11
|
description = "A tool for mining sentences from games. Update: Dependencies, replay buffer based line searching, and bug fixes."
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.10, <3.14"
|
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ai/ai_prompting.py
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon128.png
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon256.png
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/icon512.png
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/assets/pickaxe.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/locales/en_us.json
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/locales/es_es.json
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/locales/ja_jp.json
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/locales/zh_cn.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/gsm_ocr_config.py
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/owocr_area_selector.py
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ocr/owocr_helper.py
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/__init__.py
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/__main__.py
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/config.py
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/ocr.py
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/owocr/owocr/run.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/tools/ss_selector.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/anki_confirmation.py
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/anki_confirmation_qt.py
RENAMED
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/config_gui_qt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/ui/screenshot_selector.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/audio_player.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/communication/send.py
RENAMED
|
File without changes
|
{gamesentenceminer-2.21.25 → gamesentenceminer-2.21.29}/GameSentenceMiner/util/configuration.py
RENAMED
|
File without changes
|