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.
@@ -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
@@ -4,6 +4,8 @@ export interface ResolvedConfig {
4
4
  name: string;
5
5
  display?: number | string;
6
6
  format: 'png' | 'jpg';
7
+ /** 客户端预压缩目标体积(KB)。默认 500。0 = 不预压缩。 */
8
+ maxKB: number;
7
9
  reconnect: boolean;
8
10
  backoff: number;
9
11
  color: boolean;
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 payload = encryptBuffer(result.buffer, this.config.token);
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: result.mime,
582
+ mime,
526
583
  display: frame.display
527
584
  });
528
585
  const elapsed = Date.now() - start2;
529
- const kb = (result.buffer.length / 1024).toFixed(1);
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"} (${kb} KB ${this.config.format} encrypted, ${elapsed}ms)`
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> 传输格式(默认 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.2.1",
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",