scrypted-detection-trainer 0.1.1 → 0.1.3
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.
- package/dist/main.nodejs.js +1 -1
- package/dist/main.nodejs.js.map +1 -1
- package/dist/plugin.zip +0 -0
- package/out/main.nodejs.js +27 -12
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/main.ts +27 -12
package/out/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/main.ts
CHANGED
|
@@ -116,15 +116,16 @@ class DetectionTrainer extends ScryptedDeviceBase implements Settings, HttpReque
|
|
|
116
116
|
{
|
|
117
117
|
key: 'info',
|
|
118
118
|
title: 'Detection Trainer',
|
|
119
|
-
description: `${this.captures.size} captures stored (${[...this.captures.values()].filter(c => !c.reviewed).length} pending review, ${[...this.captures.values()].filter(c => c.reviewed && c.label !== 'discard').length} labeled)
|
|
119
|
+
description: `${this.captures.size} captures stored (${[...this.captures.values()].filter(c => !c.reviewed).length} pending review, ${[...this.captures.values()].filter(c => c.reviewed && c.label !== 'discard').length} labeled).`,
|
|
120
120
|
readonly: true,
|
|
121
121
|
value: '',
|
|
122
122
|
},
|
|
123
123
|
{
|
|
124
|
-
key: '
|
|
125
|
-
title: '
|
|
124
|
+
key: 'ui_link',
|
|
125
|
+
title: 'Review UI',
|
|
126
126
|
description: 'Open the detection review and labeling interface.',
|
|
127
|
-
|
|
127
|
+
readonly: true,
|
|
128
|
+
value: await sdk.endpointManager.getLocalEndpoint('scrypted-detection-trainer', { public: true }).catch(() => '/endpoint/scrypted-detection-trainer/public/'),
|
|
128
129
|
},
|
|
129
130
|
];
|
|
130
131
|
|
|
@@ -144,7 +145,7 @@ class DetectionTrainer extends ScryptedDeviceBase implements Settings, HttpReque
|
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
async putSetting(key: string, value: string) {
|
|
147
|
-
if (key === 'open_ui') return;
|
|
148
|
+
if (key === 'open_ui' || key === 'ui_link' || key === 'info') return;
|
|
148
149
|
this.storage.setItem(key, value);
|
|
149
150
|
if (key.startsWith('rate:')) {
|
|
150
151
|
// Re-register listeners when rates change
|
|
@@ -371,6 +372,7 @@ class DetectionTrainer extends ScryptedDeviceBase implements Settings, HttpReque
|
|
|
371
372
|
<meta charset="UTF-8">
|
|
372
373
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
373
374
|
<title>Detection Trainer</title>
|
|
375
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
|
374
376
|
<style>
|
|
375
377
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
376
378
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0f0f0f; color: #e8e8e8; min-height: 100vh; }
|
|
@@ -497,10 +499,14 @@ class DetectionTrainer extends ScryptedDeviceBase implements Settings, HttpReque
|
|
|
497
499
|
<div class="toast" id="toast"></div>
|
|
498
500
|
|
|
499
501
|
<script>
|
|
500
|
-
const BASE = location.pathname.replace(
|
|
502
|
+
const BASE = location.pathname.replace(/\/$/, '');
|
|
501
503
|
let pending = [];
|
|
502
504
|
let labeledCount = 0;
|
|
503
505
|
|
|
506
|
+
function imgError(img) {
|
|
507
|
+
img.parentElement.innerHTML = '<div style="padding:20px;color:#555;font-size:12px;text-align:center">No image</div>';
|
|
508
|
+
}
|
|
509
|
+
|
|
504
510
|
function showTab(name) {
|
|
505
511
|
document.querySelectorAll('.tab').forEach((t, i) => {
|
|
506
512
|
const names = ['review', 'stats', 'export'];
|
|
@@ -542,7 +548,7 @@ async function loadPending() {
|
|
|
542
548
|
return \`
|
|
543
549
|
<div class="detection" id="det-\${r.id}">
|
|
544
550
|
<div class="detection-img">
|
|
545
|
-
<img src="\${BASE}/img/\${r.id}" alt="\${r.detectedClass}" loading="lazy" onerror="this
|
|
551
|
+
<img src="\${BASE}/img/\${r.id}" alt="\${r.detectedClass}" loading="lazy" onerror="imgError(this)">
|
|
546
552
|
<div class="detection-class">\${r.detectedClass} \${score}%</div>
|
|
547
553
|
</div>
|
|
548
554
|
<div class="detection-info">
|
|
@@ -613,7 +619,7 @@ async function exportDataset() {
|
|
|
613
619
|
const btn = document.getElementById('export-btn');
|
|
614
620
|
const status = document.getElementById('export-status');
|
|
615
621
|
btn.disabled = true;
|
|
616
|
-
status.textContent = '
|
|
622
|
+
status.textContent = 'Fetching data…';
|
|
617
623
|
|
|
618
624
|
try {
|
|
619
625
|
const res = await fetch(BASE + '/api/export');
|
|
@@ -621,13 +627,22 @@ async function exportDataset() {
|
|
|
621
627
|
const data = await res.json();
|
|
622
628
|
if (data.error) { status.textContent = data.error; btn.disabled = false; return; }
|
|
623
629
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
const
|
|
630
|
+
status.textContent = 'Building zip…';
|
|
631
|
+
|
|
632
|
+
const zip = new JSZip();
|
|
633
|
+
for (const f of data.files) {
|
|
634
|
+
if (f.encoding === 'base64') {
|
|
635
|
+
zip.file(f.filename, f.content, { base64: true });
|
|
636
|
+
} else {
|
|
637
|
+
zip.file(f.filename, f.content);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const blob = await zip.generateAsync({ type: 'blob', compression: 'DEFLATE' });
|
|
627
642
|
const url = URL.createObjectURL(blob);
|
|
628
643
|
const a = document.createElement('a');
|
|
629
644
|
a.href = url;
|
|
630
|
-
a.download = 'scrypted_dataset_' + new Date().toISOString().slice(0,10) + '.
|
|
645
|
+
a.download = 'scrypted_dataset_' + new Date().toISOString().slice(0,10) + '.zip';
|
|
631
646
|
a.click();
|
|
632
647
|
URL.revokeObjectURL(url);
|
|
633
648
|
status.textContent = \`Downloaded \${data.count} samples.\`;
|