GameSentenceMiner 2.16.0__py3-none-any.whl → 2.16.2__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/gsm.py +0 -1
- GameSentenceMiner/util/downloader/download_tools.py +44 -1
- GameSentenceMiner/web/static/css/shared.css +1 -1
- GameSentenceMiner/web/static/js/shared.js +67 -0
- GameSentenceMiner/web/templates/anki_stats.html +4 -0
- GameSentenceMiner/web/templates/components/navigation.html +4 -1
- GameSentenceMiner/web/templates/database.html +4 -1
- GameSentenceMiner/web/templates/index.html +14 -14
- GameSentenceMiner/web/templates/search.html +4 -1
- GameSentenceMiner/web/templates/stats.html +4 -1
- GameSentenceMiner/web/texthooking_page.py +7 -1
- {gamesentenceminer-2.16.0.dist-info → gamesentenceminer-2.16.2.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.16.0.dist-info → gamesentenceminer-2.16.2.dist-info}/RECORD +17 -17
- {gamesentenceminer-2.16.0.dist-info → gamesentenceminer-2.16.2.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.16.0.dist-info → gamesentenceminer-2.16.2.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.16.0.dist-info → gamesentenceminer-2.16.2.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.16.0.dist-info → gamesentenceminer-2.16.2.dist-info}/top_level.txt +0 -0
GameSentenceMiner/gsm.py
CHANGED
@@ -10,9 +10,37 @@ from GameSentenceMiner.util.downloader.Untitled_json import scenes
|
|
10
10
|
from GameSentenceMiner.util.configuration import get_app_directory, logger
|
11
11
|
from GameSentenceMiner.util.ffmpeg import get_ffmpeg_path, get_ffprobe_path
|
12
12
|
from GameSentenceMiner.obs import get_obs_path
|
13
|
+
import tempfile
|
13
14
|
|
14
15
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
15
16
|
|
17
|
+
def cleanup_temp_files(func):
|
18
|
+
def wrapper(*args, **kwargs):
|
19
|
+
temp_files = []
|
20
|
+
|
21
|
+
# Patch tempfile.NamedTemporaryFile to track created temp files
|
22
|
+
orig_named_tempfile = tempfile.NamedTemporaryFile
|
23
|
+
def tracked_named_tempfile(*a, **kw):
|
24
|
+
tmp = orig_named_tempfile(*a, **kw)
|
25
|
+
temp_files.append(tmp.name)
|
26
|
+
return tmp
|
27
|
+
tempfile.NamedTemporaryFile = tracked_named_tempfile
|
28
|
+
|
29
|
+
try:
|
30
|
+
result = func(*args, **kwargs)
|
31
|
+
finally:
|
32
|
+
# Restore original NamedTemporaryFile
|
33
|
+
tempfile.NamedTemporaryFile = orig_named_tempfile
|
34
|
+
# Remove tracked temp files
|
35
|
+
for f in temp_files:
|
36
|
+
try:
|
37
|
+
if os.path.exists(f):
|
38
|
+
os.remove(f)
|
39
|
+
except Exception:
|
40
|
+
pass
|
41
|
+
return result
|
42
|
+
return wrapper
|
43
|
+
|
16
44
|
def copy_obs_settings(src, dest):
|
17
45
|
|
18
46
|
if os.path.exists(src):
|
@@ -111,6 +139,9 @@ def download_obs_if_needed():
|
|
111
139
|
with open(os.path.join(scene_json_path, 'Untitled.json'), 'w') as scene_file:
|
112
140
|
scene_file.write(scenes)
|
113
141
|
logger.info(f"OBS extracted to {obs_path}.")
|
142
|
+
|
143
|
+
# remove zip
|
144
|
+
os.unlink(obs_installer)
|
114
145
|
else:
|
115
146
|
logger.error(f"Please install OBS manually from {obs_installer}")
|
116
147
|
|
@@ -118,9 +149,14 @@ def download_ffmpeg_if_needed():
|
|
118
149
|
ffmpeg_dir = os.path.join(get_app_directory(), 'ffmpeg')
|
119
150
|
ffmpeg_exe_path = get_ffmpeg_path()
|
120
151
|
ffprobe_exe_path = get_ffprobe_path()
|
121
|
-
|
152
|
+
python_dir = os.path.join(get_app_directory(), 'python')
|
153
|
+
ffmpeg_in_python = os.path.join(python_dir, "ffmpeg.exe")
|
154
|
+
|
122
155
|
if os.path.exists(ffmpeg_dir) and os.path.exists(ffmpeg_exe_path) and os.path.exists(ffprobe_exe_path):
|
123
156
|
logger.debug(f"FFmpeg already installed at {ffmpeg_dir}.")
|
157
|
+
if not os.path.exists(ffmpeg_in_python):
|
158
|
+
shutil.copy2(ffmpeg_exe_path, ffmpeg_in_python)
|
159
|
+
logger.info(f"Copied ffmpeg.exe to Python folder: {ffmpeg_in_python}")
|
124
160
|
return
|
125
161
|
|
126
162
|
if os.path.exists(ffmpeg_dir) and (not os.path.exists(ffmpeg_exe_path) or not os.path.exists(ffprobe_exe_path)):
|
@@ -155,6 +191,13 @@ def download_ffmpeg_if_needed():
|
|
155
191
|
target = open(os.path.join(ffmpeg_dir, filename), "wb")
|
156
192
|
with source, target:
|
157
193
|
shutil.copyfileobj(source, target)
|
194
|
+
|
195
|
+
# Copy ffmpeg.exe to the python folder
|
196
|
+
if os.path.exists(ffmpeg_exe_path):
|
197
|
+
shutil.copy2(ffmpeg_exe_path, ffmpeg_in_python)
|
198
|
+
logger.info(f"Copied ffmpeg.exe to Python folder: {ffmpeg_in_python}")
|
199
|
+
else:
|
200
|
+
logger.warning(f"ffmpeg.exe not found in {ffmpeg_dir}.")
|
158
201
|
logger.info(f"FFmpeg extracted to {ffmpeg_dir}.")
|
159
202
|
|
160
203
|
def download_ocenaudio_if_needed():
|
@@ -491,6 +491,70 @@ function escapeRegex(string) {
|
|
491
491
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
492
492
|
}
|
493
493
|
|
494
|
+
// Screenshot functionality
|
495
|
+
function initializeScreenshotButton() {
|
496
|
+
const screenshotButton = document.getElementById('screenshotToggle');
|
497
|
+
|
498
|
+
if (!screenshotButton) {
|
499
|
+
return; // Screenshot button not available on this page
|
500
|
+
}
|
501
|
+
|
502
|
+
screenshotButton.addEventListener('click', takeScreenshot);
|
503
|
+
}
|
504
|
+
|
505
|
+
async function takeScreenshot() {
|
506
|
+
try {
|
507
|
+
// Check if html2canvas is available
|
508
|
+
if (typeof html2canvas === 'undefined') {
|
509
|
+
console.error('html2canvas library not loaded');
|
510
|
+
return;
|
511
|
+
}
|
512
|
+
|
513
|
+
// Generate timestamp for filename
|
514
|
+
const now = new Date();
|
515
|
+
const timestamp = now.getFullYear() + '-' +
|
516
|
+
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
517
|
+
String(now.getDate()).padStart(2, '0') + '_' +
|
518
|
+
String(now.getHours()).padStart(2, '0') + '-' +
|
519
|
+
String(now.getMinutes()).padStart(2, '0') + '-' +
|
520
|
+
String(now.getSeconds()).padStart(2, '0');
|
521
|
+
|
522
|
+
const filename = `screenshot_${timestamp}.png`;
|
523
|
+
|
524
|
+
// Capture the entire page
|
525
|
+
const canvas = await html2canvas(document.body, {
|
526
|
+
useCORS: true,
|
527
|
+
allowTaint: true,
|
528
|
+
scale: 1,
|
529
|
+
scrollX: 0,
|
530
|
+
scrollY: 0,
|
531
|
+
width: document.body.scrollWidth,
|
532
|
+
height: document.body.scrollHeight
|
533
|
+
});
|
534
|
+
|
535
|
+
// Convert canvas to blob
|
536
|
+
canvas.toBlob(function(blob) {
|
537
|
+
// Create download link
|
538
|
+
const link = document.createElement('a');
|
539
|
+
link.download = filename;
|
540
|
+
link.href = URL.createObjectURL(blob);
|
541
|
+
|
542
|
+
// Trigger download
|
543
|
+
document.body.appendChild(link);
|
544
|
+
link.click();
|
545
|
+
document.body.removeChild(link);
|
546
|
+
|
547
|
+
// Clean up the URL object after a short delay to avoid race condition
|
548
|
+
setTimeout(function() {
|
549
|
+
URL.revokeObjectURL(link.href);
|
550
|
+
}, 100);
|
551
|
+
}, 'image/png');
|
552
|
+
|
553
|
+
} catch (error) {
|
554
|
+
console.error('Screenshot failed:', error);
|
555
|
+
}
|
556
|
+
}
|
557
|
+
|
494
558
|
// Initialize shared functionality when DOM loads
|
495
559
|
document.addEventListener('DOMContentLoaded', function() {
|
496
560
|
// Initialize theme toggle
|
@@ -499,6 +563,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
499
563
|
// Initialize modal handlers
|
500
564
|
initializeModalHandlers();
|
501
565
|
|
566
|
+
// Initialize screenshot button
|
567
|
+
initializeScreenshotButton();
|
568
|
+
|
502
569
|
// Initialize settings manager if settings toggle exists
|
503
570
|
if (document.getElementById('settingsToggle')) {
|
504
571
|
new SettingsManager();
|
@@ -4,6 +4,10 @@
|
|
4
4
|
<meta charset="UTF-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
6
|
<title>Anki vs GSM Kanji Stats</title>
|
7
|
+
|
8
|
+
<!-- Include html2canvas for screenshot functionality -->
|
9
|
+
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
10
|
+
|
7
11
|
<!-- Include Chart.js from a CDN -->
|
8
12
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
9
13
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!-- Navigation Component -->
|
2
2
|
<div class="navigation" style="display: flex; justify-content: center; align-items: center; margin-bottom: 30px; padding: 15px; background: var(--bg-secondary); border-radius: 8px; box-shadow: 0 2px 8px var(--shadow-color); border: 1px solid var(--border-color);">
|
3
3
|
<div style="display: flex; gap: 15px;">
|
4
|
-
<a href="/" class="nav-link">Home</a>
|
4
|
+
<!-- <a href="/" class="nav-link">Home</a> -->
|
5
5
|
<a href="/stats" class="nav-link">Statistics</a>
|
6
6
|
<a href="/search" class="nav-link">Search</a>
|
7
7
|
<a href="/database" class="nav-link">Database Management</a>
|
@@ -10,6 +10,9 @@
|
|
10
10
|
<button class="theme-toggle" id="settingsToggle" title="Settings">
|
11
11
|
<span id="settingsIcon">⚙️</span>
|
12
12
|
</button>
|
13
|
+
<button class="theme-toggle" id="screenshotToggle" title="Take screenshot">
|
14
|
+
<span id="screenshotIcon">📷</span>
|
15
|
+
</button>
|
13
16
|
<button class="theme-toggle" id="themeToggle" title="Toggle dark mode">
|
14
17
|
<span id="themeIcon">🌙</span>
|
15
18
|
</button>
|
@@ -6,6 +6,9 @@
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
7
|
<title>GSM Database Management</title>
|
8
8
|
|
9
|
+
<!-- Include html2canvas for screenshot functionality -->
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
11
|
+
|
9
12
|
<!-- Include shared theme styles -->
|
10
13
|
{% include 'components/theme-styles.html' %}
|
11
14
|
|
@@ -15,7 +18,7 @@
|
|
15
18
|
<body>
|
16
19
|
|
17
20
|
<div class="container">
|
18
|
-
<h1>
|
21
|
+
<h1>GSM - Database Management</h1>
|
19
22
|
|
20
23
|
<!-- Include shared navigation -->
|
21
24
|
{% include 'components/navigation.html' %}
|