@muhgholy/next-drive 4.7.0 → 4.9.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/README.md +103 -78
- package/dist/{chunk-OWKTTRQC.js → chunk-C5CORNPP.js} +83 -49
- package/dist/chunk-C5CORNPP.js.map +1 -0
- package/dist/{chunk-YR4DEKWI.cjs → chunk-WZWJYN64.cjs} +83 -49
- package/dist/chunk-WZWJYN64.cjs.map +1 -0
- package/dist/server/controllers/drive.d.ts +3 -2
- package/dist/server/controllers/drive.d.ts.map +1 -1
- package/dist/server/express.cjs +11 -11
- package/dist/server/express.js +2 -2
- package/dist/server/index.cjs +13 -13
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/utils.d.ts +17 -5
- package/dist/server/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-OWKTTRQC.js.map +0 -1
- package/dist/chunk-YR4DEKWI.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -590,7 +590,7 @@ Serve optimized images with dynamic compression, resizing, and format conversion
|
|
|
590
590
|
### URL Format
|
|
591
591
|
|
|
592
592
|
```
|
|
593
|
-
/api/drive?action=serve&id={fileId}&quality={preset}&display={context}&size={
|
|
593
|
+
/api/drive?action=serve&id={fileId}&quality={preset}&display={context}&size={scale}&fit={mode}&position={anchor}&format={format}
|
|
594
594
|
```
|
|
595
595
|
|
|
596
596
|
### Parameters
|
|
@@ -598,10 +598,46 @@ Serve optimized images with dynamic compression, resizing, and format conversion
|
|
|
598
598
|
| Parameter | Type | Description |
|
|
599
599
|
|-----------|------|-------------|
|
|
600
600
|
| `quality` | `low` / `medium` / `high` / `1-100` | Compression level |
|
|
601
|
-
| `display` | string |
|
|
602
|
-
| `size` | string |
|
|
601
|
+
| `display` | string | Sets aspect ratio, base dimensions, quality factor, and default fit |
|
|
602
|
+
| `size` | string | Scale factor (xs/sm/md/lg/xl) or standalone dimension preset |
|
|
603
|
+
| `fit` | `cover` / `contain` / `fill` / `inside` / `outside` | How image fits into dimensions |
|
|
604
|
+
| `position` | `center` / `top` / `bottom` / `left` / `right` / `attention` / `entropy` | Crop anchor point (for cover/contain) |
|
|
603
605
|
| `format` | `jpeg` / `webp` / `avif` / `png` | Output format |
|
|
604
606
|
|
|
607
|
+
### Fit Options
|
|
608
|
+
|
|
609
|
+
| Fit | Behavior | Use Case |
|
|
610
|
+
|-----|----------|----------|
|
|
611
|
+
| `cover` | Crop to fill exact dimensions | Thumbnails, avatars, cards |
|
|
612
|
+
| `contain` | Fit within dimensions (may letterbox) | Logos, icons |
|
|
613
|
+
| `fill` | Stretch to exact dimensions (may distort) | Background fills |
|
|
614
|
+
| `inside` | Fit within, no upscaling *(default)* | Article images |
|
|
615
|
+
| `outside` | Cover minimum dimensions | Backgrounds |
|
|
616
|
+
|
|
617
|
+
### Position Options (for cover/contain)
|
|
618
|
+
|
|
619
|
+
| Position | Anchor Point |
|
|
620
|
+
|----------|--------------|
|
|
621
|
+
| `center` | Center *(default)* |
|
|
622
|
+
| `top` | Top center |
|
|
623
|
+
| `bottom` | Bottom center |
|
|
624
|
+
| `left` | Left center |
|
|
625
|
+
| `right` | Right center |
|
|
626
|
+
| `attention` | Focus on most "interesting" area (AI-based) |
|
|
627
|
+
| `entropy` | Focus on highest entropy area |
|
|
628
|
+
|
|
629
|
+
### How Display + Size Work Together
|
|
630
|
+
|
|
631
|
+
When **display** is specified, it defines the aspect ratio, base dimensions, and default fit. The **size** parameter then scales those dimensions:
|
|
632
|
+
|
|
633
|
+
```
|
|
634
|
+
display=article-image + size=sm → 400×225 (16:9, half size, fit=inside)
|
|
635
|
+
display=thumbnail + size=md → 150×150 (1:1, fit=cover)
|
|
636
|
+
display=avatar + fit=contain → 128×128 (override default cover to contain)
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
When **no display** is specified, size uses standalone presets (fixed dimensions).
|
|
640
|
+
|
|
605
641
|
### Quality Presets
|
|
606
642
|
|
|
607
643
|
| Preset | Base Quality | Use Case |
|
|
@@ -613,88 +649,77 @@ Serve optimized images with dynamic compression, resizing, and format conversion
|
|
|
613
649
|
|
|
614
650
|
> Quality is dynamically adjusted based on file size. Larger files get more aggressive compression.
|
|
615
651
|
|
|
616
|
-
### Display Presets (
|
|
617
|
-
|
|
618
|
-
| Display |
|
|
619
|
-
|
|
620
|
-
| `article-header` | 0.9 |
|
|
621
|
-
| `article-image` | 0.85 |
|
|
622
|
-
| `thumbnail` | 0.7 |
|
|
623
|
-
| `avatar` | 0.8 |
|
|
624
|
-
| `logo` | 0.95 |
|
|
625
|
-
| `card` | 0.8 |
|
|
626
|
-
| `gallery` | 0.85 |
|
|
627
|
-
| `og` |
|
|
628
|
-
| `icon` | 0.75 |
|
|
629
|
-
| `cover` | 0.9 |
|
|
630
|
-
| `story` | 0.85 |
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
| `
|
|
641
|
-
|
|
642
|
-
| `
|
|
643
|
-
| `
|
|
644
|
-
| `
|
|
645
|
-
| `
|
|
646
|
-
| `
|
|
647
|
-
| `
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
|
654
|
-
|
|
655
|
-
| `landscape-
|
|
656
|
-
| `landscape
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
|
660
|
-
|
|
661
|
-
| `
|
|
662
|
-
| `
|
|
663
|
-
| `portrait-lg` | 720×1280 |
|
|
664
|
-
|
|
665
|
-
**Wide/Banner:**
|
|
666
|
-
| Size | Dimensions | Ratio |
|
|
667
|
-
|------|------------|-------|
|
|
668
|
-
| `wide` | 1200×630 | OG standard |
|
|
669
|
-
| `banner` | 1200×400 | 3:1 |
|
|
670
|
-
| `banner-sm` | 800×200 | 4:1 |
|
|
671
|
-
|
|
672
|
-
**Other:**
|
|
673
|
-
| Size | Dimensions | Ratio |
|
|
674
|
-
|------|------------|-------|
|
|
675
|
-
| `photo-4x3` | 800×600 | 4:3 |
|
|
676
|
-
| `photo-3x2` | 900×600 | 3:2 |
|
|
677
|
-
| `story` | 1080×1920 | 9:16 |
|
|
678
|
-
| `video` | 1280×720 | 16:9 |
|
|
679
|
-
| `video-sm` | 640×360 | 16:9 |
|
|
680
|
-
| `card-sm` | 300×200 | 3:2 |
|
|
681
|
-
| `card` | 400×300 | 4:3 |
|
|
682
|
-
| `card-lg` | 600×400 | 3:2 |
|
|
652
|
+
### Display Presets (Aspect Ratio + Dimensions + Fit)
|
|
653
|
+
|
|
654
|
+
| Display | Aspect Ratio | Base Size | Quality | Default Fit |
|
|
655
|
+
|---------|--------------|-----------|---------|-------------|
|
|
656
|
+
| `article-header` | 16:9 | 1200×675 | 0.9 | inside |
|
|
657
|
+
| `article-image` | 16:9 | 800×450 | 0.85 | inside |
|
|
658
|
+
| `thumbnail` | 1:1 | 150×150 | 0.7 | cover |
|
|
659
|
+
| `avatar` | 1:1 | 128×128 | 0.8 | cover |
|
|
660
|
+
| `logo` | 2:1 | 200×100 | 0.95 | contain |
|
|
661
|
+
| `card` | 4:3 | 400×300 | 0.8 | cover |
|
|
662
|
+
| `gallery` | 1:1 | 600×600 | 0.85 | cover |
|
|
663
|
+
| `og` | ~1.9:1 | 1200×630 | 0.9 | cover |
|
|
664
|
+
| `icon` | 1:1 | 48×48 | 0.75 | cover |
|
|
665
|
+
| `cover` | 16:9 | 1920×1080 | 0.9 | cover |
|
|
666
|
+
| `story` | 9:16 | 1080×1920 | 0.85 | cover |
|
|
667
|
+
| `video` | 16:9 | 1280×720 | 0.85 | cover |
|
|
668
|
+
| `banner` | 3:1 | 1200×400 | 0.9 | cover |
|
|
669
|
+
| `portrait` | 3:4 | 600×800 | 0.85 | inside |
|
|
670
|
+
| `landscape` | 4:3 | 800×600 | 0.85 | inside |
|
|
671
|
+
|
|
672
|
+
### Size Scale (with Display)
|
|
673
|
+
|
|
674
|
+
When used with a display preset, size scales the dimensions:
|
|
675
|
+
|
|
676
|
+
| Size | Scale | Example with `article-image` (800×450) |
|
|
677
|
+
|------|-------|----------------------------------------|
|
|
678
|
+
| `xs` | 0.25× | 200×113 |
|
|
679
|
+
| `sm` | 0.5× | 400×225 |
|
|
680
|
+
| `md` | 1.0× | 800×450 |
|
|
681
|
+
| `lg` | 1.5× | 1200×675 |
|
|
682
|
+
| `xl` | 2.0× | 1600×900 |
|
|
683
|
+
| `2xl` | 2.5× | 2000×1125 |
|
|
684
|
+
|
|
685
|
+
### Standalone Size Presets (without Display)
|
|
686
|
+
|
|
687
|
+
When no display is specified, use these fixed dimension presets:
|
|
688
|
+
|
|
689
|
+
| Size | Dimensions | Size | Dimensions |
|
|
690
|
+
|------|------------|------|------------|
|
|
691
|
+
| `xs` | 64×64 | `landscape-sm` | 480×270 |
|
|
692
|
+
| `sm` | 128×128 | `landscape` | 800×450 |
|
|
693
|
+
| `md` | 256×256 | `landscape-lg` | 1280×720 |
|
|
694
|
+
| `lg` | 512×512 | `portrait-sm` | 270×480 |
|
|
695
|
+
| `xl` | 1024×1024 | `portrait` | 450×800 |
|
|
696
|
+
| `icon` | 48×48 | `wide` | 1200×630 |
|
|
697
|
+
| `thumb` | 150×150 | `banner` | 1200×400 |
|
|
698
|
+
| `video` | 1280×720 | `card` | 400×300 |
|
|
683
699
|
|
|
684
700
|
### Examples
|
|
685
701
|
|
|
686
702
|
```html
|
|
687
|
-
<!-- Article
|
|
688
|
-
<img src="/api/drive?action=serve&id=123&display=article-
|
|
703
|
+
<!-- Article image, smaller variant (400×225, fit=inside) -->
|
|
704
|
+
<img src="/api/drive?action=serve&id=123&display=article-image&size=sm&format=webp">
|
|
705
|
+
|
|
706
|
+
<!-- Thumbnail with cover fit (crops to fill 150×150 square) -->
|
|
707
|
+
<img src="/api/drive?action=serve&id=123&display=thumbnail&format=webp">
|
|
708
|
+
|
|
709
|
+
<!-- Avatar with top-focused crop (for face photos) -->
|
|
710
|
+
<img src="/api/drive?action=serve&id=123&display=avatar&fit=cover&position=top&format=webp">
|
|
711
|
+
|
|
712
|
+
<!-- Gallery with AI-based attention crop -->
|
|
713
|
+
<img src="/api/drive?action=serve&id=123&display=gallery&fit=cover&position=attention&format=webp">
|
|
689
714
|
|
|
690
|
-
<!--
|
|
691
|
-
<img src="/api/drive?action=serve&id=123&display=
|
|
715
|
+
<!-- Card image, override default cover to contain -->
|
|
716
|
+
<img src="/api/drive?action=serve&id=123&display=card&fit=contain&format=webp">
|
|
692
717
|
|
|
693
|
-
<!--
|
|
694
|
-
<img src="/api/drive?action=serve&id=123&display=
|
|
718
|
+
<!-- Banner with custom position -->
|
|
719
|
+
<img src="/api/drive?action=serve&id=123&display=banner&position=bottom&format=webp">
|
|
695
720
|
|
|
696
|
-
<!--
|
|
697
|
-
<img src="/api/drive?action=serve&id=123&
|
|
721
|
+
<!-- Standalone size, no display -->
|
|
722
|
+
<img src="/api/drive?action=serve&id=123&size=landscape&fit=cover&format=webp">
|
|
698
723
|
|
|
699
724
|
<!-- Just quality, no resize -->
|
|
700
725
|
<img src="/api/drive?action=serve&id=123&quality=medium&format=webp">
|
|
@@ -357,75 +357,77 @@ var extractImageMetadata = async (filePath) => {
|
|
|
357
357
|
}
|
|
358
358
|
};
|
|
359
359
|
var DISPLAY_PRESETS = {
|
|
360
|
-
"article-header": 0.9,
|
|
361
|
-
|
|
362
|
-
"
|
|
363
|
-
|
|
364
|
-
"
|
|
365
|
-
|
|
366
|
-
"
|
|
367
|
-
|
|
368
|
-
"
|
|
369
|
-
|
|
370
|
-
"
|
|
371
|
-
|
|
372
|
-
"
|
|
373
|
-
|
|
374
|
-
"
|
|
375
|
-
// Open Graph/social sharing
|
|
376
|
-
"icon": 0.75,
|
|
377
|
-
// Small icons
|
|
378
|
-
"cover": 0.9,
|
|
379
|
-
// Full-width covers
|
|
380
|
-
"story": 0.85
|
|
381
|
-
// Story/vertical format
|
|
360
|
+
"article-header": { ratio: [16, 9], baseWidth: 1200, qualityFactor: 0.9, defaultFit: "inside" },
|
|
361
|
+
"article-image": { ratio: [16, 9], baseWidth: 800, qualityFactor: 0.85, defaultFit: "inside" },
|
|
362
|
+
"thumbnail": { ratio: [1, 1], baseWidth: 150, qualityFactor: 0.7, defaultFit: "cover" },
|
|
363
|
+
"avatar": { ratio: [1, 1], baseWidth: 128, qualityFactor: 0.8, defaultFit: "cover" },
|
|
364
|
+
"logo": { ratio: [2, 1], baseWidth: 200, qualityFactor: 0.95, defaultFit: "contain" },
|
|
365
|
+
"card": { ratio: [4, 3], baseWidth: 400, qualityFactor: 0.8, defaultFit: "cover" },
|
|
366
|
+
"gallery": { ratio: [1, 1], baseWidth: 600, qualityFactor: 0.85, defaultFit: "cover" },
|
|
367
|
+
"og": { ratio: [1200, 630], baseWidth: 1200, qualityFactor: 0.9, defaultFit: "cover" },
|
|
368
|
+
"icon": { ratio: [1, 1], baseWidth: 48, qualityFactor: 0.75, defaultFit: "cover" },
|
|
369
|
+
"cover": { ratio: [16, 9], baseWidth: 1920, qualityFactor: 0.9, defaultFit: "cover" },
|
|
370
|
+
"story": { ratio: [9, 16], baseWidth: 1080, qualityFactor: 0.85, defaultFit: "cover" },
|
|
371
|
+
"video": { ratio: [16, 9], baseWidth: 1280, qualityFactor: 0.85, defaultFit: "cover" },
|
|
372
|
+
"banner": { ratio: [3, 1], baseWidth: 1200, qualityFactor: 0.9, defaultFit: "cover" },
|
|
373
|
+
"portrait": { ratio: [3, 4], baseWidth: 600, qualityFactor: 0.85, defaultFit: "inside" },
|
|
374
|
+
"landscape": { ratio: [4, 3], baseWidth: 800, qualityFactor: 0.85, defaultFit: "inside" }
|
|
382
375
|
};
|
|
383
|
-
var
|
|
384
|
-
|
|
376
|
+
var VALID_FIT_OPTIONS = ["cover", "contain", "fill", "inside", "outside"];
|
|
377
|
+
var VALID_POSITION_OPTIONS = [
|
|
378
|
+
"center",
|
|
379
|
+
"top",
|
|
380
|
+
"right top",
|
|
381
|
+
"right",
|
|
382
|
+
"right bottom",
|
|
383
|
+
"bottom",
|
|
384
|
+
"left bottom",
|
|
385
|
+
"left",
|
|
386
|
+
"left top",
|
|
387
|
+
"attention",
|
|
388
|
+
"entropy"
|
|
389
|
+
];
|
|
390
|
+
var SIZE_SCALES = {
|
|
391
|
+
"xs": 0.25,
|
|
392
|
+
"sm": 0.5,
|
|
393
|
+
"md": 1,
|
|
394
|
+
"lg": 1.5,
|
|
395
|
+
"xl": 2,
|
|
396
|
+
"2xl": 2.5
|
|
397
|
+
};
|
|
398
|
+
var STANDALONE_SIZES = {
|
|
385
399
|
"xs": { width: 64, height: 64 },
|
|
386
400
|
"sm": { width: 128, height: 128 },
|
|
387
401
|
"md": { width: 256, height: 256 },
|
|
388
402
|
"lg": { width: 512, height: 512 },
|
|
389
403
|
"xl": { width: 1024, height: 1024 },
|
|
390
404
|
"2xl": { width: 1600, height: 1600 },
|
|
391
|
-
// Named squares
|
|
392
405
|
"icon": { width: 48, height: 48 },
|
|
393
406
|
"thumb": { width: 150, height: 150 },
|
|
394
407
|
"square": { width: 600, height: 600 },
|
|
395
408
|
"avatar-sm": { width: 64, height: 64 },
|
|
396
409
|
"avatar-md": { width: 128, height: 128 },
|
|
397
410
|
"avatar-lg": { width: 256, height: 256 },
|
|
398
|
-
// Landscape (16:9)
|
|
399
411
|
"landscape-sm": { width: 480, height: 270 },
|
|
400
412
|
"landscape": { width: 800, height: 450 },
|
|
401
413
|
"landscape-lg": { width: 1280, height: 720 },
|
|
402
414
|
"landscape-xl": { width: 1920, height: 1080 },
|
|
403
|
-
// Portrait (9:16)
|
|
404
415
|
"portrait-sm": { width: 270, height: 480 },
|
|
405
416
|
"portrait": { width: 450, height: 800 },
|
|
406
417
|
"portrait-lg": { width: 720, height: 1280 },
|
|
407
|
-
// Wide/Banner (OG, social)
|
|
408
418
|
"wide": { width: 1200, height: 630 },
|
|
409
|
-
// Open Graph standard
|
|
410
419
|
"banner": { width: 1200, height: 400 },
|
|
411
|
-
// Banner/header
|
|
412
420
|
"banner-sm": { width: 800, height: 200 },
|
|
413
|
-
// Classic photo ratios
|
|
414
421
|
"photo-4x3": { width: 800, height: 600 },
|
|
415
|
-
// 4:3
|
|
416
422
|
"photo-3x2": { width: 900, height: 600 },
|
|
417
|
-
// 3:2
|
|
418
|
-
// Story/vertical (9:16)
|
|
419
423
|
"story": { width: 1080, height: 1920 },
|
|
420
|
-
// Video thumbnails
|
|
421
424
|
"video": { width: 1280, height: 720 },
|
|
422
425
|
"video-sm": { width: 640, height: 360 },
|
|
423
|
-
// Card sizes
|
|
424
426
|
"card-sm": { width: 300, height: 200 },
|
|
425
427
|
"card": { width: 400, height: 300 },
|
|
426
428
|
"card-lg": { width: 600, height: 400 }
|
|
427
429
|
};
|
|
428
|
-
var getImageSettings = (fileSizeInBytes, qualityPreset, display, size) => {
|
|
430
|
+
var getImageSettings = (fileSizeInBytes, qualityPreset, display, size, fit, position) => {
|
|
429
431
|
let baseQuality = 80;
|
|
430
432
|
if (qualityPreset === "low") baseQuality = 30;
|
|
431
433
|
else if (qualityPreset === "medium") baseQuality = 50;
|
|
@@ -434,8 +436,28 @@ var getImageSettings = (fileSizeInBytes, qualityPreset, display, size) => {
|
|
|
434
436
|
const n = parseInt(qualityPreset, 10);
|
|
435
437
|
if (!isNaN(n)) baseQuality = Math.min(100, Math.max(1, n));
|
|
436
438
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
+
let width;
|
|
440
|
+
let height;
|
|
441
|
+
let qualityFactor = 1;
|
|
442
|
+
let defaultFit = "inside";
|
|
443
|
+
const displayPreset = display ? DISPLAY_PRESETS[display] : void 0;
|
|
444
|
+
if (displayPreset) {
|
|
445
|
+
qualityFactor = displayPreset.qualityFactor;
|
|
446
|
+
defaultFit = displayPreset.defaultFit;
|
|
447
|
+
const [ratioW, ratioH] = displayPreset.ratio;
|
|
448
|
+
const scale = size && SIZE_SCALES[size] ? SIZE_SCALES[size] : 1;
|
|
449
|
+
width = Math.round(displayPreset.baseWidth * scale);
|
|
450
|
+
height = Math.round(width * ratioH / ratioW);
|
|
451
|
+
} else if (size) {
|
|
452
|
+
const standalone = STANDALONE_SIZES[size];
|
|
453
|
+
if (standalone) {
|
|
454
|
+
width = standalone.width;
|
|
455
|
+
height = standalone.height;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const resolvedFit = fit && VALID_FIT_OPTIONS.includes(fit) ? fit : defaultFit;
|
|
459
|
+
const resolvedPosition = position && VALID_POSITION_OPTIONS.includes(position) ? position : void 0;
|
|
460
|
+
baseQuality = Math.round(baseQuality * qualityFactor);
|
|
439
461
|
let quality = baseQuality;
|
|
440
462
|
let effort = 4;
|
|
441
463
|
let pngCompression = 6;
|
|
@@ -463,12 +485,12 @@ var getImageSettings = (fileSizeInBytes, qualityPreset, display, size) => {
|
|
|
463
485
|
pngCompression = 7;
|
|
464
486
|
}
|
|
465
487
|
}
|
|
466
|
-
const dimensions = size && SIZE_PRESETS[size] ? SIZE_PRESETS[size] : void 0;
|
|
467
488
|
return {
|
|
468
489
|
quality: Math.max(1, Math.min(100, quality)),
|
|
469
490
|
effort,
|
|
470
491
|
pngCompression,
|
|
471
|
-
...
|
|
492
|
+
...width && height && { width, height, fit: resolvedFit },
|
|
493
|
+
...resolvedPosition && { position: resolvedPosition }
|
|
472
494
|
};
|
|
473
495
|
};
|
|
474
496
|
var objectIdSchema = z.string().refine((val) => isValidObjectId(val), {
|
|
@@ -1547,9 +1569,9 @@ var driveUpload = async (source, key, options) => {
|
|
|
1547
1569
|
}
|
|
1548
1570
|
}
|
|
1549
1571
|
let resolvedParentId = null;
|
|
1550
|
-
if (options.folder
|
|
1572
|
+
if (options.folder && "path" in options.folder) {
|
|
1551
1573
|
resolvedParentId = await resolveFolderByPath(options.folder.path, key, accountId);
|
|
1552
|
-
} else if (options.folder
|
|
1574
|
+
} else if (options.folder && "id" in options.folder && options.folder.id !== "root") {
|
|
1553
1575
|
resolvedParentId = options.folder.id;
|
|
1554
1576
|
} else if (options.parentId && options.parentId !== "root") {
|
|
1555
1577
|
resolvedParentId = options.parentId;
|
|
@@ -1709,23 +1731,30 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1709
1731
|
const quality = req.query.quality;
|
|
1710
1732
|
const display = req.query.display;
|
|
1711
1733
|
const sizePreset = req.query.size;
|
|
1734
|
+
const fit = req.query.fit;
|
|
1735
|
+
const position = req.query.position;
|
|
1712
1736
|
const isImage = mime.startsWith("image/");
|
|
1713
|
-
const shouldTransform = isImage && (format || quality || display || sizePreset);
|
|
1737
|
+
const shouldTransform = isImage && (format || quality || display || sizePreset || fit);
|
|
1714
1738
|
res.setHeader("Content-Disposition", `inline; filename="${safeFilename}"`);
|
|
1715
1739
|
if (config.cors?.enabled) {
|
|
1716
1740
|
res.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
1717
1741
|
}
|
|
1718
1742
|
if (shouldTransform) {
|
|
1719
1743
|
try {
|
|
1720
|
-
const settings = getImageSettings(fileSize, quality, display, sizePreset);
|
|
1744
|
+
const settings = getImageSettings(fileSize, quality, display, sizePreset, fit, position);
|
|
1721
1745
|
let targetFormat = format || mime.split("/")[1];
|
|
1722
1746
|
if (targetFormat === "jpg") targetFormat = "jpeg";
|
|
1747
|
+
if (!["jpeg", "png", "webp", "avif"].includes(targetFormat)) {
|
|
1748
|
+
targetFormat = format || "webp";
|
|
1749
|
+
}
|
|
1723
1750
|
const cacheDir = path.join(config.storage.path, "file", drive._id.toString(), "cache");
|
|
1724
1751
|
const cacheKey = [
|
|
1725
1752
|
"opt",
|
|
1726
1753
|
`q${settings.quality}`,
|
|
1727
1754
|
`e${settings.effort}`,
|
|
1728
1755
|
settings.width ? `${settings.width}x${settings.height}` : "orig",
|
|
1756
|
+
settings.fit || "none",
|
|
1757
|
+
settings.position || "c",
|
|
1729
1758
|
targetFormat
|
|
1730
1759
|
].join("_");
|
|
1731
1760
|
const cachePath = path.join(cacheDir, `${cacheKey}.bin`);
|
|
@@ -1745,7 +1774,8 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1745
1774
|
let pipeline = sharp();
|
|
1746
1775
|
if (settings.width && settings.height) {
|
|
1747
1776
|
pipeline = pipeline.resize(settings.width, settings.height, {
|
|
1748
|
-
fit: "inside",
|
|
1777
|
+
fit: settings.fit || "inside",
|
|
1778
|
+
position: settings.position || "center",
|
|
1749
1779
|
withoutEnlargement: true
|
|
1750
1780
|
});
|
|
1751
1781
|
}
|
|
@@ -1756,13 +1786,17 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1756
1786
|
pipeline = pipeline.png({ compressionLevel: settings.pngCompression, adaptiveFiltering: true });
|
|
1757
1787
|
res.setHeader("Content-Type", "image/png");
|
|
1758
1788
|
} else if (targetFormat === "webp") {
|
|
1759
|
-
|
|
1789
|
+
const webpEffort = Math.min(settings.effort, 6);
|
|
1790
|
+
pipeline = pipeline.webp({ quality: settings.quality, effort: webpEffort });
|
|
1760
1791
|
res.setHeader("Content-Type", "image/webp");
|
|
1761
1792
|
} else if (targetFormat === "avif") {
|
|
1762
1793
|
pipeline = pipeline.avif({ quality: settings.quality, effort: settings.effort });
|
|
1763
1794
|
res.setHeader("Content-Type", "image/avif");
|
|
1764
1795
|
}
|
|
1765
1796
|
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
1797
|
+
pipeline.on("error", (err) => {
|
|
1798
|
+
console.error("[next-drive] Pipeline error:", err);
|
|
1799
|
+
});
|
|
1766
1800
|
stream.pipe(pipeline);
|
|
1767
1801
|
pipeline.clone().toFile(cachePath).catch((e) => console.error("[next-drive] Cache write failed:", e));
|
|
1768
1802
|
pipeline.clone().pipe(res);
|
|
@@ -2306,5 +2340,5 @@ var driveAPIHandler = async (req, res) => {
|
|
|
2306
2340
|
};
|
|
2307
2341
|
|
|
2308
2342
|
export { driveAPIHandler, driveConfiguration, driveDelete, driveFilePath, driveFileSchemaZod, driveGetUrl, driveInfo, driveList, driveReadFile, driveUpload, getDriveConfig, getDriveInformation };
|
|
2309
|
-
//# sourceMappingURL=chunk-
|
|
2310
|
-
//# sourceMappingURL=chunk-
|
|
2343
|
+
//# sourceMappingURL=chunk-C5CORNPP.js.map
|
|
2344
|
+
//# sourceMappingURL=chunk-C5CORNPP.js.map
|