node-mac-recorder 2.21.49 → 2.21.51
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/index.js +242 -2
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -339,11 +339,246 @@ class MacRecorder extends EventEmitter {
|
|
|
339
339
|
// Seçenekleri güncelle
|
|
340
340
|
this.setOptions(options);
|
|
341
341
|
|
|
342
|
+
// Cache display list so we don't fetch multiple times during preparation
|
|
343
|
+
let cachedDisplays = null;
|
|
344
|
+
const getCachedDisplays = async () => {
|
|
345
|
+
if (cachedDisplays) {
|
|
346
|
+
return cachedDisplays;
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
cachedDisplays = await this.getDisplays();
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.warn("Display bilgisi alınamadı:", error.message);
|
|
352
|
+
cachedDisplays = [];
|
|
353
|
+
}
|
|
354
|
+
return cachedDisplays;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Normalize capture area coordinates:
|
|
359
|
+
* - Pick correct display based on user-provided area/displayId info
|
|
360
|
+
* - Convert global coordinates to display-relative when needed
|
|
361
|
+
* - Clamp to valid bounds to avoid ScreenCaptureKit skipping crops
|
|
362
|
+
*/
|
|
363
|
+
const normalizeCaptureArea = async () => {
|
|
364
|
+
if (!this.options.captureArea) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const displays = await getCachedDisplays();
|
|
369
|
+
if (!Array.isArray(displays) || displays.length === 0) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const rawArea = this.options.captureArea;
|
|
374
|
+
const parsedArea = {
|
|
375
|
+
x: Number(rawArea.x),
|
|
376
|
+
y: Number(rawArea.y),
|
|
377
|
+
width: Number(rawArea.width),
|
|
378
|
+
height: Number(rawArea.height),
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
if (
|
|
382
|
+
!Number.isFinite(parsedArea.x) ||
|
|
383
|
+
!Number.isFinite(parsedArea.y) ||
|
|
384
|
+
!Number.isFinite(parsedArea.width) ||
|
|
385
|
+
!Number.isFinite(parsedArea.height) ||
|
|
386
|
+
parsedArea.width <= 0 ||
|
|
387
|
+
parsedArea.height <= 0
|
|
388
|
+
) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const areaRect = {
|
|
393
|
+
left: parsedArea.x,
|
|
394
|
+
top: parsedArea.y,
|
|
395
|
+
right: parsedArea.x + parsedArea.width,
|
|
396
|
+
bottom: parsedArea.y + parsedArea.height,
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const getDisplayRect = (display) => {
|
|
400
|
+
const dx = Number(display.x) || 0;
|
|
401
|
+
const dy = Number(display.y) || 0;
|
|
402
|
+
const dw = Number(display.width) || 0;
|
|
403
|
+
const dh = Number(display.height) || 0;
|
|
404
|
+
return {
|
|
405
|
+
left: dx,
|
|
406
|
+
top: dy,
|
|
407
|
+
right: dx + dw,
|
|
408
|
+
bottom: dy + dh,
|
|
409
|
+
width: dw,
|
|
410
|
+
height: dh,
|
|
411
|
+
};
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const requestedDisplayId =
|
|
415
|
+
this.options.displayId === null || this.options.displayId === undefined
|
|
416
|
+
? null
|
|
417
|
+
: Number(this.options.displayId);
|
|
418
|
+
|
|
419
|
+
let targetDisplay = null;
|
|
420
|
+
if (requestedDisplayId !== null && Number.isFinite(requestedDisplayId)) {
|
|
421
|
+
targetDisplay =
|
|
422
|
+
displays.find(
|
|
423
|
+
(display) => Number(display.id) === requestedDisplayId
|
|
424
|
+
) || null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (!targetDisplay) {
|
|
428
|
+
targetDisplay =
|
|
429
|
+
displays.find((display) => {
|
|
430
|
+
const rect = getDisplayRect(display);
|
|
431
|
+
return (
|
|
432
|
+
areaRect.left >= rect.left &&
|
|
433
|
+
areaRect.right <= rect.right &&
|
|
434
|
+
areaRect.top >= rect.top &&
|
|
435
|
+
areaRect.bottom <= rect.bottom
|
|
436
|
+
);
|
|
437
|
+
}) || null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!targetDisplay) {
|
|
441
|
+
let bestDisplay = null;
|
|
442
|
+
let bestOverlap = 0;
|
|
443
|
+
displays.forEach((display) => {
|
|
444
|
+
const rect = getDisplayRect(display);
|
|
445
|
+
const overlapWidth =
|
|
446
|
+
Math.min(areaRect.right, rect.right) -
|
|
447
|
+
Math.max(areaRect.left, rect.left);
|
|
448
|
+
const overlapHeight =
|
|
449
|
+
Math.min(areaRect.bottom, rect.bottom) -
|
|
450
|
+
Math.max(areaRect.top, rect.top);
|
|
451
|
+
if (overlapWidth > 0 && overlapHeight > 0) {
|
|
452
|
+
const overlapArea = overlapWidth * overlapHeight;
|
|
453
|
+
if (overlapArea > bestOverlap) {
|
|
454
|
+
bestOverlap = overlapArea;
|
|
455
|
+
bestDisplay = display;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
targetDisplay = bestDisplay;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (!targetDisplay) {
|
|
463
|
+
targetDisplay =
|
|
464
|
+
displays.find((display) => display.isPrimary) || displays[0];
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (!targetDisplay) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const targetRect = getDisplayRect(targetDisplay);
|
|
472
|
+
if (targetRect.width <= 0 || targetRect.height <= 0) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const tolerance = 1; // allow sub-pixel offsets
|
|
477
|
+
const isRelativeToDisplay = () => {
|
|
478
|
+
const endX = parsedArea.x + parsedArea.width;
|
|
479
|
+
const endY = parsedArea.y + parsedArea.height;
|
|
480
|
+
return (
|
|
481
|
+
parsedArea.x >= -tolerance &&
|
|
482
|
+
parsedArea.y >= -tolerance &&
|
|
483
|
+
endX <= targetRect.width + tolerance &&
|
|
484
|
+
endY <= targetRect.height + tolerance
|
|
485
|
+
);
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
let relativeX = parsedArea.x;
|
|
489
|
+
let relativeY = parsedArea.y;
|
|
490
|
+
|
|
491
|
+
if (!isRelativeToDisplay()) {
|
|
492
|
+
relativeX = parsedArea.x - targetRect.left;
|
|
493
|
+
relativeY = parsedArea.y - targetRect.top;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
let relativeWidth = parsedArea.width;
|
|
497
|
+
let relativeHeight = parsedArea.height;
|
|
498
|
+
|
|
499
|
+
// Discard if area sits completely outside the display
|
|
500
|
+
if (
|
|
501
|
+
relativeX >= targetRect.width ||
|
|
502
|
+
relativeY >= targetRect.height ||
|
|
503
|
+
relativeWidth <= 0 ||
|
|
504
|
+
relativeHeight <= 0
|
|
505
|
+
) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (relativeX < 0) {
|
|
510
|
+
relativeWidth += relativeX;
|
|
511
|
+
relativeX = 0;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (relativeY < 0) {
|
|
515
|
+
relativeHeight += relativeY;
|
|
516
|
+
relativeY = 0;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const maxWidth = targetRect.width - relativeX;
|
|
520
|
+
const maxHeight = targetRect.height - relativeY;
|
|
521
|
+
|
|
522
|
+
if (maxWidth <= 0 || maxHeight <= 0) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
relativeWidth = Math.min(relativeWidth, maxWidth);
|
|
527
|
+
relativeHeight = Math.min(relativeHeight, maxHeight);
|
|
528
|
+
|
|
529
|
+
if (relativeWidth <= 0 || relativeHeight <= 0) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const normalizeValue = (value, minValue) =>
|
|
534
|
+
Math.max(minValue, Math.round(value));
|
|
535
|
+
const normalizedArea = {
|
|
536
|
+
x: Math.max(0, Math.round(relativeX)),
|
|
537
|
+
y: Math.max(0, Math.round(relativeY)),
|
|
538
|
+
width: normalizeValue(relativeWidth, 1),
|
|
539
|
+
height: normalizeValue(relativeHeight, 1),
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
const originalRounded = {
|
|
543
|
+
x: Math.round(parsedArea.x),
|
|
544
|
+
y: Math.round(parsedArea.y),
|
|
545
|
+
width: normalizeValue(parsedArea.width, 1),
|
|
546
|
+
height: normalizeValue(parsedArea.height, 1),
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
const displayChanged =
|
|
550
|
+
!Number.isFinite(requestedDisplayId) ||
|
|
551
|
+
Number(targetDisplay.id) !== requestedDisplayId;
|
|
552
|
+
const areaChanged =
|
|
553
|
+
normalizedArea.x !== originalRounded.x ||
|
|
554
|
+
normalizedArea.y !== originalRounded.y ||
|
|
555
|
+
normalizedArea.width !== originalRounded.width ||
|
|
556
|
+
normalizedArea.height !== originalRounded.height;
|
|
557
|
+
|
|
558
|
+
if (displayChanged || areaChanged) {
|
|
559
|
+
console.log(
|
|
560
|
+
`🎯 Capture area normalize: display=${targetDisplay.id} -> (${rawArea.x},${rawArea.y},${rawArea.width}x${rawArea.height}) ➜ (${normalizedArea.x},${normalizedArea.y},${normalizedArea.width}x${normalizedArea.height})`
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
this.options.captureArea = normalizedArea;
|
|
565
|
+
this.options.displayId = Number(targetDisplay.id);
|
|
566
|
+
this.recordingDisplayInfo = {
|
|
567
|
+
displayId: Number(targetDisplay.id),
|
|
568
|
+
x: Number(targetDisplay.x) || 0,
|
|
569
|
+
y: Number(targetDisplay.y) || 0,
|
|
570
|
+
width: Number(targetDisplay.width) || 0,
|
|
571
|
+
height: Number(targetDisplay.height) || 0,
|
|
572
|
+
logicalWidth: Number(targetDisplay.width) || 0,
|
|
573
|
+
logicalHeight: Number(targetDisplay.height) || 0,
|
|
574
|
+
};
|
|
575
|
+
};
|
|
576
|
+
|
|
342
577
|
// WindowId varsa captureArea'yı otomatik ayarla
|
|
343
578
|
if (this.options.windowId && !this.options.captureArea) {
|
|
344
579
|
try {
|
|
345
580
|
const windows = await this.getWindows();
|
|
346
|
-
const displays = await
|
|
581
|
+
const displays = await getCachedDisplays();
|
|
347
582
|
const targetWindow = windows.find(
|
|
348
583
|
(w) => w.id === this.options.windowId
|
|
349
584
|
);
|
|
@@ -442,7 +677,7 @@ class MacRecorder extends EventEmitter {
|
|
|
442
677
|
// Ensure recordingDisplayInfo is always set for cursor tracking
|
|
443
678
|
if (!this.recordingDisplayInfo) {
|
|
444
679
|
try {
|
|
445
|
-
const displays = await
|
|
680
|
+
const displays = await getCachedDisplays();
|
|
446
681
|
let targetDisplay;
|
|
447
682
|
|
|
448
683
|
if (this.options.displayId !== null) {
|
|
@@ -470,6 +705,11 @@ class MacRecorder extends EventEmitter {
|
|
|
470
705
|
}
|
|
471
706
|
}
|
|
472
707
|
|
|
708
|
+
// Normalize capture area AFTER automatic window capture logic
|
|
709
|
+
if (this.options.captureArea) {
|
|
710
|
+
await normalizeCaptureArea();
|
|
711
|
+
}
|
|
712
|
+
|
|
473
713
|
// Çıkış dizinini oluştur
|
|
474
714
|
const outputDir = path.dirname(outputPath);
|
|
475
715
|
if (!fs.existsSync(outputDir)) {
|