argus-eye 0.2.1 → 0.4.0
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/lib/compress.d.ts +19 -0
- package/lib/config.d.ts +2 -0
- package/lib/index.mjs +71 -6
- package/package.json +2 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CompressOptions {
|
|
2
|
+
/** 目标体积上限(字节)。 */
|
|
3
|
+
targetBytes: number;
|
|
4
|
+
/** 起始 jpeg 质量。 */
|
|
5
|
+
initialQuality?: number;
|
|
6
|
+
/** 最低 jpeg 质量。 */
|
|
7
|
+
minQuality?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 把图片压到 `targetBytes` 以内的 JPEG。
|
|
11
|
+
*
|
|
12
|
+
* 性能优先(photon WASM 实现):
|
|
13
|
+
* - 输入已经 ≤ target → 直接返回(0 ms)。
|
|
14
|
+
* - 否则估算 quality 编一遍。命中就完事;不行再 resize 一次。
|
|
15
|
+
* - photon 的 resize 偏慢(~180ms / 2560x1440),所以放在最后兜底。
|
|
16
|
+
*
|
|
17
|
+
* 典型路径 ≤ 200ms:decode (~65ms) + 1 次 encode (~110ms) = ~175ms。
|
|
18
|
+
*/
|
|
19
|
+
export declare function compressToBudget(input: Buffer, options: CompressOptions): Buffer;
|
package/lib/config.d.ts
CHANGED
package/lib/index.mjs
CHANGED
|
@@ -71,6 +71,51 @@ async function capture(options = {}) {
|
|
|
71
71
|
}
|
|
72
72
|
__name(capture, "capture");
|
|
73
73
|
|
|
74
|
+
// src/compress.ts
|
|
75
|
+
import {
|
|
76
|
+
PhotonImage,
|
|
77
|
+
resize,
|
|
78
|
+
SamplingFilter
|
|
79
|
+
} from "@cf-wasm/photon/node";
|
|
80
|
+
function compressToBudget(input, options) {
|
|
81
|
+
const target = Math.max(8 * 1024, options.targetBytes);
|
|
82
|
+
if (input.length <= target) return input;
|
|
83
|
+
const initialQuality = options.initialQuality ?? 80;
|
|
84
|
+
const minQuality = options.minQuality ?? 40;
|
|
85
|
+
const img = PhotonImage.new_from_byteslice(new Uint8Array(input));
|
|
86
|
+
try {
|
|
87
|
+
const ratioByteWise = target / input.length;
|
|
88
|
+
const estQ = Math.max(
|
|
89
|
+
minQuality,
|
|
90
|
+
Math.min(initialQuality, Math.round(initialQuality * ratioByteWise))
|
|
91
|
+
);
|
|
92
|
+
let buf = Buffer.from(img.get_bytes_jpeg(estQ));
|
|
93
|
+
if (buf.length <= target) return buf;
|
|
94
|
+
const q2 = Math.max(
|
|
95
|
+
minQuality,
|
|
96
|
+
Math.round(estQ * (target / buf.length) * 0.95)
|
|
97
|
+
);
|
|
98
|
+
if (q2 < estQ) {
|
|
99
|
+
buf = Buffer.from(img.get_bytes_jpeg(q2));
|
|
100
|
+
if (buf.length <= target) return buf;
|
|
101
|
+
}
|
|
102
|
+
const w = img.get_width();
|
|
103
|
+
const h = img.get_height();
|
|
104
|
+
const scale = Math.sqrt(target / buf.length) * 0.9;
|
|
105
|
+
const newW = Math.max(64, Math.round(w * scale));
|
|
106
|
+
const newH = Math.max(64, Math.round(h * scale));
|
|
107
|
+
const small = resize(img, newW, newH, SamplingFilter.Triangle);
|
|
108
|
+
try {
|
|
109
|
+
return Buffer.from(small.get_bytes_jpeg(q2));
|
|
110
|
+
} finally {
|
|
111
|
+
small.free();
|
|
112
|
+
}
|
|
113
|
+
} finally {
|
|
114
|
+
img.free();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
__name(compressToBudget, "compressToBudget");
|
|
118
|
+
|
|
74
119
|
// src/fullscreen.ts
|
|
75
120
|
var DEFAULT_ALLOW_APPS = [
|
|
76
121
|
// browsers
|
|
@@ -516,19 +561,33 @@ var ArgusClient = class {
|
|
|
516
561
|
display: target,
|
|
517
562
|
format: this.config.format
|
|
518
563
|
});
|
|
519
|
-
const
|
|
564
|
+
const compressStart = Date.now();
|
|
565
|
+
let final = result.buffer;
|
|
566
|
+
let mime = result.mime;
|
|
567
|
+
const rawSize = result.buffer.length;
|
|
568
|
+
const budgetBytes = this.config.maxKB * 1024;
|
|
569
|
+
if (budgetBytes > 0 && rawSize > budgetBytes) {
|
|
570
|
+
final = compressToBudget(result.buffer, {
|
|
571
|
+
targetBytes: budgetBytes
|
|
572
|
+
});
|
|
573
|
+
mime = "image/jpeg";
|
|
574
|
+
}
|
|
575
|
+
const compressMs = Date.now() - compressStart;
|
|
576
|
+
const payload = encryptBuffer(final, this.config.token);
|
|
520
577
|
this.send({
|
|
521
578
|
type: "peek_result",
|
|
522
579
|
id: frame.id,
|
|
523
580
|
image: payload,
|
|
524
581
|
enc: ENC_ALGO,
|
|
525
|
-
mime
|
|
582
|
+
mime,
|
|
526
583
|
display: frame.display
|
|
527
584
|
});
|
|
528
585
|
const elapsed = Date.now() - start2;
|
|
529
|
-
const
|
|
586
|
+
const rawKb = (rawSize / 1024).toFixed(1);
|
|
587
|
+
const finalKb = (final.length / 1024).toFixed(1);
|
|
588
|
+
const same = final === result.buffer;
|
|
530
589
|
logger.info(
|
|
531
|
-
`peek #${frame.id} → display ${frame.display ?? "default"} (${
|
|
590
|
+
`peek #${frame.id} → display ${frame.display ?? "default"} (${same ? finalKb : `${rawKb}→${finalKb}`} KB ${same ? mime.replace("image/", "") : "jpg"} encrypted, total=${elapsed}ms compress=${compressMs}ms)`
|
|
532
591
|
);
|
|
533
592
|
} catch (err) {
|
|
534
593
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -617,7 +676,10 @@ var HELP_TEXT = `argus-eye [options]
|
|
|
617
676
|
-n, --name <name> 上报给服务端的名字(默认: os.hostname())
|
|
618
677
|
-d, --display <id> 默认截哪块屏(数字 id,缺省=主屏)
|
|
619
678
|
--list-displays 列出本机显示器后退出
|
|
620
|
-
--format <png|jpg>
|
|
679
|
+
--format <png|jpg> 原始捕获格式(默认 jpg)。
|
|
680
|
+
--max-kb <n> 预压缩目标体积上限(KB,默认 600)。
|
|
681
|
+
客户端会用降质量 + 缩小图片把 buffer 压到这个上限以内
|
|
682
|
+
再加密发出,省带宽。0 = 关闭预压缩,按原始截图发。
|
|
621
683
|
--no-reconnect 禁用断线重连
|
|
622
684
|
--backoff <ms> 重连最大间隔(默认 30000)
|
|
623
685
|
--no-detect-fullscreen
|
|
@@ -646,7 +708,7 @@ function parseArgs(argv) {
|
|
|
646
708
|
},
|
|
647
709
|
string: ["server", "token", "name", "config", "format", "display"],
|
|
648
710
|
array: ["allowApp", "busyApp"],
|
|
649
|
-
number: ["backoff"],
|
|
711
|
+
number: ["backoff", "maxKb"],
|
|
650
712
|
boolean: [
|
|
651
713
|
"help",
|
|
652
714
|
"version",
|
|
@@ -679,6 +741,7 @@ function parseArgs(argv) {
|
|
|
679
741
|
name: asString(parsed.name) ?? env.ARGUS_NAME ?? asString(fileConfig.name) ?? os.hostname(),
|
|
680
742
|
display: asDisplay(parsed.display) ?? asDisplay(fileConfig.display) ?? void 0,
|
|
681
743
|
format: asString(parsed.format) ?? asString(fileConfig.format) ?? "jpg",
|
|
744
|
+
maxKB: asNumber(parsed.maxKb) ?? asNumber(fileConfig.maxKB) ?? asNumber(fileConfig.maxKb) ?? 600,
|
|
682
745
|
reconnect: asBoolean(parsed.reconnect) ?? asBoolean(fileConfig.reconnect) ?? true,
|
|
683
746
|
backoff: asNumber(parsed.backoff) ?? asNumber(fileConfig.backoff) ?? 3e4,
|
|
684
747
|
color: asBoolean(parsed.color) ?? true,
|
|
@@ -695,6 +758,7 @@ function parseArgs(argv) {
|
|
|
695
758
|
name: merged.name ?? os.hostname(),
|
|
696
759
|
display: merged.display,
|
|
697
760
|
format: merged.format ?? "jpg",
|
|
761
|
+
maxKB: merged.maxKB ?? 600,
|
|
698
762
|
reconnect: merged.reconnect ?? true,
|
|
699
763
|
backoff: merged.backoff ?? 3e4,
|
|
700
764
|
color: merged.color ?? true,
|
|
@@ -720,6 +784,7 @@ function parseArgs(argv) {
|
|
|
720
784
|
name: merged.name ?? os.hostname(),
|
|
721
785
|
display: merged.display,
|
|
722
786
|
format: merged.format,
|
|
787
|
+
maxKB: merged.maxKB ?? 600,
|
|
723
788
|
reconnect: merged.reconnect ?? true,
|
|
724
789
|
backoff: merged.backoff ?? 3e4,
|
|
725
790
|
color: merged.color ?? true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "argus-eye",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Argus eye —— koishi-plugin-argus 的截图客户端 CLI。",
|
|
6
6
|
"author": "dingyi222666 <dingyi222666@foxmail.com>",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"lint-fix": "yarn eslint src --ext=ts --fix"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
+
"@cf-wasm/photon": "^0.3.5",
|
|
25
26
|
"kleur": "^4.1.5",
|
|
26
27
|
"screenshot-desktop": "^1.15.0",
|
|
27
28
|
"ws": "^8.18.0",
|