@muhgholy/next-drive 4.7.0 → 4.8.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 +76 -78
- package/dist/{chunk-OWKTTRQC.js → chunk-AYHO6FSR.js} +55 -45
- package/dist/chunk-AYHO6FSR.js.map +1 -0
- package/dist/{chunk-YR4DEKWI.cjs → chunk-TAG5UMWA.cjs} +55 -45
- package/dist/chunk-TAG5UMWA.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 +2 -2
- 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}&format={format}
|
|
594
594
|
```
|
|
595
595
|
|
|
596
596
|
### Parameters
|
|
@@ -598,10 +598,22 @@ 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, and quality factor |
|
|
602
|
+
| `size` | string | Scale factor (xs/sm/md/lg/xl) or standalone dimension preset |
|
|
603
603
|
| `format` | `jpeg` / `webp` / `avif` / `png` | Output format |
|
|
604
604
|
|
|
605
|
+
### How Display + Size Work Together
|
|
606
|
+
|
|
607
|
+
When **display** is specified, it defines the aspect ratio and base dimensions. The **size** parameter then scales those dimensions:
|
|
608
|
+
|
|
609
|
+
```
|
|
610
|
+
display=article-image + size=sm → 400×225 (16:9, half size)
|
|
611
|
+
display=article-image + size=md → 800×450 (16:9, default)
|
|
612
|
+
display=article-image + size=lg → 1200×675 (16:9, 1.5x)
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
When **no display** is specified, size uses standalone presets (fixed dimensions).
|
|
616
|
+
|
|
605
617
|
### Quality Presets
|
|
606
618
|
|
|
607
619
|
| Preset | Base Quality | Use Case |
|
|
@@ -613,88 +625,74 @@ Serve optimized images with dynamic compression, resizing, and format conversion
|
|
|
613
625
|
|
|
614
626
|
> Quality is dynamically adjusted based on file size. Larger files get more aggressive compression.
|
|
615
627
|
|
|
616
|
-
### Display Presets (
|
|
617
|
-
|
|
618
|
-
| Display |
|
|
619
|
-
|
|
620
|
-
| `article-header` |
|
|
621
|
-
| `article-image` |
|
|
622
|
-
| `thumbnail` |
|
|
623
|
-
| `avatar` |
|
|
624
|
-
| `logo` | 0.95 |
|
|
625
|
-
| `card` |
|
|
626
|
-
| `gallery` | 0.85 |
|
|
627
|
-
| `og` |
|
|
628
|
-
| `icon` |
|
|
629
|
-
| `cover` |
|
|
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 |
|
|
628
|
+
### Display Presets (Aspect Ratio + Dimensions)
|
|
629
|
+
|
|
630
|
+
| Display | Aspect Ratio | Base Size | Quality Factor |
|
|
631
|
+
|---------|--------------|-----------|----------------|
|
|
632
|
+
| `article-header` | 16:9 | 1200×675 | 0.9 |
|
|
633
|
+
| `article-image` | 16:9 | 800×450 | 0.85 |
|
|
634
|
+
| `thumbnail` | 1:1 | 150×150 | 0.7 |
|
|
635
|
+
| `avatar` | 1:1 | 128×128 | 0.8 |
|
|
636
|
+
| `logo` | 2:1 | 200×100 | 0.95 |
|
|
637
|
+
| `card` | 4:3 | 400×300 | 0.8 |
|
|
638
|
+
| `gallery` | 1:1 | 600×600 | 0.85 |
|
|
639
|
+
| `og` | ~1.9:1 | 1200×630 | 0.9 |
|
|
640
|
+
| `icon` | 1:1 | 48×48 | 0.75 |
|
|
641
|
+
| `cover` | 16:9 | 1920×1080 | 0.9 |
|
|
642
|
+
| `story` | 9:16 | 1080×1920 | 0.85 |
|
|
643
|
+
| `video` | 16:9 | 1280×720 | 0.85 |
|
|
644
|
+
| `banner` | 3:1 | 1200×400 | 0.9 |
|
|
645
|
+
| `portrait` | 3:4 | 600×800 | 0.85 |
|
|
646
|
+
| `landscape` | 4:3 | 800×600 | 0.85 |
|
|
647
|
+
|
|
648
|
+
### Size Scale (with Display)
|
|
649
|
+
|
|
650
|
+
When used with a display preset, size scales the dimensions:
|
|
651
|
+
|
|
652
|
+
| Size | Scale | Example with `article-image` (800×450) |
|
|
653
|
+
|------|-------|----------------------------------------|
|
|
654
|
+
| `xs` | 0.25× | 200×113 |
|
|
655
|
+
| `sm` | 0.5× | 400×225 |
|
|
656
|
+
| `md` | 1.0× | 800×450 |
|
|
657
|
+
| `lg` | 1.5× | 1200×675 |
|
|
658
|
+
| `xl` | 2.0× | 1600×900 |
|
|
659
|
+
| `2xl` | 2.5× | 2000×1125 |
|
|
660
|
+
|
|
661
|
+
### Standalone Size Presets (without Display)
|
|
662
|
+
|
|
663
|
+
When no display is specified, use these fixed dimension presets:
|
|
664
|
+
|
|
665
|
+
| Size | Dimensions | Size | Dimensions |
|
|
666
|
+
|------|------------|------|------------|
|
|
667
|
+
| `xs` | 64×64 | `landscape-sm` | 480×270 |
|
|
668
|
+
| `sm` | 128×128 | `landscape` | 800×450 |
|
|
669
|
+
| `md` | 256×256 | `landscape-lg` | 1280×720 |
|
|
670
|
+
| `lg` | 512×512 | `portrait-sm` | 270×480 |
|
|
671
|
+
| `xl` | 1024×1024 | `portrait` | 450×800 |
|
|
672
|
+
| `icon` | 48×48 | `wide` | 1200×630 |
|
|
673
|
+
| `thumb` | 150×150 | `banner` | 1200×400 |
|
|
674
|
+
| `video` | 1280×720 | `card` | 400×300 |
|
|
683
675
|
|
|
684
676
|
### Examples
|
|
685
677
|
|
|
686
678
|
```html
|
|
687
|
-
<!-- Article
|
|
688
|
-
<img src="/api/drive?action=serve&id=123&display=article-
|
|
679
|
+
<!-- Article image, smaller variant (400×225) -->
|
|
680
|
+
<img src="/api/drive?action=serve&id=123&display=article-image&size=sm&format=webp">
|
|
681
|
+
|
|
682
|
+
<!-- Article image, default size (800×450) -->
|
|
683
|
+
<img src="/api/drive?action=serve&id=123&display=article-image&format=webp">
|
|
684
|
+
|
|
685
|
+
<!-- Article image, larger variant (1200×675) -->
|
|
686
|
+
<img src="/api/drive?action=serve&id=123&display=article-image&size=lg&format=webp">
|
|
689
687
|
|
|
690
|
-
<!-- Thumbnail
|
|
691
|
-
<img src="/api/drive?action=serve&id=123&display=thumbnail&
|
|
688
|
+
<!-- Thumbnail (150×150 square) -->
|
|
689
|
+
<img src="/api/drive?action=serve&id=123&display=thumbnail&format=webp">
|
|
692
690
|
|
|
693
|
-
<!-- Avatar -->
|
|
694
|
-
<img src="/api/drive?action=serve&id=123&display=avatar&size=
|
|
691
|
+
<!-- Avatar, smaller (64×64) -->
|
|
692
|
+
<img src="/api/drive?action=serve&id=123&display=avatar&size=sm&format=webp">
|
|
695
693
|
|
|
696
|
-
<!--
|
|
697
|
-
<img src="/api/drive?action=serve&id=123&
|
|
694
|
+
<!-- Standalone size, no display -->
|
|
695
|
+
<img src="/api/drive?action=serve&id=123&size=landscape&format=webp">
|
|
698
696
|
|
|
699
697
|
<!-- Just quality, no resize -->
|
|
700
698
|
<img src="/api/drive?action=serve&id=123&quality=medium&format=webp">
|
|
@@ -357,70 +357,58 @@ 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 },
|
|
361
|
+
"article-image": { ratio: [16, 9], baseWidth: 800, qualityFactor: 0.85 },
|
|
362
|
+
"thumbnail": { ratio: [1, 1], baseWidth: 150, qualityFactor: 0.7 },
|
|
363
|
+
"avatar": { ratio: [1, 1], baseWidth: 128, qualityFactor: 0.8 },
|
|
364
|
+
"logo": { ratio: [2, 1], baseWidth: 200, qualityFactor: 0.95 },
|
|
365
|
+
"card": { ratio: [4, 3], baseWidth: 400, qualityFactor: 0.8 },
|
|
366
|
+
"gallery": { ratio: [1, 1], baseWidth: 600, qualityFactor: 0.85 },
|
|
367
|
+
"og": { ratio: [1200, 630], baseWidth: 1200, qualityFactor: 0.9 },
|
|
368
|
+
"icon": { ratio: [1, 1], baseWidth: 48, qualityFactor: 0.75 },
|
|
369
|
+
"cover": { ratio: [16, 9], baseWidth: 1920, qualityFactor: 0.9 },
|
|
370
|
+
"story": { ratio: [9, 16], baseWidth: 1080, qualityFactor: 0.85 },
|
|
371
|
+
"video": { ratio: [16, 9], baseWidth: 1280, qualityFactor: 0.85 },
|
|
372
|
+
"banner": { ratio: [3, 1], baseWidth: 1200, qualityFactor: 0.9 },
|
|
373
|
+
"portrait": { ratio: [3, 4], baseWidth: 600, qualityFactor: 0.85 },
|
|
374
|
+
"landscape": { ratio: [4, 3], baseWidth: 800, qualityFactor: 0.85 }
|
|
382
375
|
};
|
|
383
|
-
var
|
|
384
|
-
|
|
376
|
+
var SIZE_SCALES = {
|
|
377
|
+
"xs": 0.25,
|
|
378
|
+
"sm": 0.5,
|
|
379
|
+
"md": 1,
|
|
380
|
+
"lg": 1.5,
|
|
381
|
+
"xl": 2,
|
|
382
|
+
"2xl": 2.5
|
|
383
|
+
};
|
|
384
|
+
var STANDALONE_SIZES = {
|
|
385
385
|
"xs": { width: 64, height: 64 },
|
|
386
386
|
"sm": { width: 128, height: 128 },
|
|
387
387
|
"md": { width: 256, height: 256 },
|
|
388
388
|
"lg": { width: 512, height: 512 },
|
|
389
389
|
"xl": { width: 1024, height: 1024 },
|
|
390
390
|
"2xl": { width: 1600, height: 1600 },
|
|
391
|
-
// Named squares
|
|
392
391
|
"icon": { width: 48, height: 48 },
|
|
393
392
|
"thumb": { width: 150, height: 150 },
|
|
394
393
|
"square": { width: 600, height: 600 },
|
|
395
394
|
"avatar-sm": { width: 64, height: 64 },
|
|
396
395
|
"avatar-md": { width: 128, height: 128 },
|
|
397
396
|
"avatar-lg": { width: 256, height: 256 },
|
|
398
|
-
// Landscape (16:9)
|
|
399
397
|
"landscape-sm": { width: 480, height: 270 },
|
|
400
398
|
"landscape": { width: 800, height: 450 },
|
|
401
399
|
"landscape-lg": { width: 1280, height: 720 },
|
|
402
400
|
"landscape-xl": { width: 1920, height: 1080 },
|
|
403
|
-
// Portrait (9:16)
|
|
404
401
|
"portrait-sm": { width: 270, height: 480 },
|
|
405
402
|
"portrait": { width: 450, height: 800 },
|
|
406
403
|
"portrait-lg": { width: 720, height: 1280 },
|
|
407
|
-
// Wide/Banner (OG, social)
|
|
408
404
|
"wide": { width: 1200, height: 630 },
|
|
409
|
-
// Open Graph standard
|
|
410
405
|
"banner": { width: 1200, height: 400 },
|
|
411
|
-
// Banner/header
|
|
412
406
|
"banner-sm": { width: 800, height: 200 },
|
|
413
|
-
// Classic photo ratios
|
|
414
407
|
"photo-4x3": { width: 800, height: 600 },
|
|
415
|
-
// 4:3
|
|
416
408
|
"photo-3x2": { width: 900, height: 600 },
|
|
417
|
-
// 3:2
|
|
418
|
-
// Story/vertical (9:16)
|
|
419
409
|
"story": { width: 1080, height: 1920 },
|
|
420
|
-
// Video thumbnails
|
|
421
410
|
"video": { width: 1280, height: 720 },
|
|
422
411
|
"video-sm": { width: 640, height: 360 },
|
|
423
|
-
// Card sizes
|
|
424
412
|
"card-sm": { width: 300, height: 200 },
|
|
425
413
|
"card": { width: 400, height: 300 },
|
|
426
414
|
"card-lg": { width: 600, height: 400 }
|
|
@@ -434,8 +422,24 @@ var getImageSettings = (fileSizeInBytes, qualityPreset, display, size) => {
|
|
|
434
422
|
const n = parseInt(qualityPreset, 10);
|
|
435
423
|
if (!isNaN(n)) baseQuality = Math.min(100, Math.max(1, n));
|
|
436
424
|
}
|
|
437
|
-
|
|
438
|
-
|
|
425
|
+
let width;
|
|
426
|
+
let height;
|
|
427
|
+
let qualityFactor = 1;
|
|
428
|
+
const displayPreset = display ? DISPLAY_PRESETS[display] : void 0;
|
|
429
|
+
if (displayPreset) {
|
|
430
|
+
qualityFactor = displayPreset.qualityFactor;
|
|
431
|
+
const [ratioW, ratioH] = displayPreset.ratio;
|
|
432
|
+
const scale = size && SIZE_SCALES[size] ? SIZE_SCALES[size] : 1;
|
|
433
|
+
width = Math.round(displayPreset.baseWidth * scale);
|
|
434
|
+
height = Math.round(width * ratioH / ratioW);
|
|
435
|
+
} else if (size) {
|
|
436
|
+
const standalone = STANDALONE_SIZES[size];
|
|
437
|
+
if (standalone) {
|
|
438
|
+
width = standalone.width;
|
|
439
|
+
height = standalone.height;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
baseQuality = Math.round(baseQuality * qualityFactor);
|
|
439
443
|
let quality = baseQuality;
|
|
440
444
|
let effort = 4;
|
|
441
445
|
let pngCompression = 6;
|
|
@@ -463,12 +467,11 @@ var getImageSettings = (fileSizeInBytes, qualityPreset, display, size) => {
|
|
|
463
467
|
pngCompression = 7;
|
|
464
468
|
}
|
|
465
469
|
}
|
|
466
|
-
const dimensions = size && SIZE_PRESETS[size] ? SIZE_PRESETS[size] : void 0;
|
|
467
470
|
return {
|
|
468
471
|
quality: Math.max(1, Math.min(100, quality)),
|
|
469
472
|
effort,
|
|
470
473
|
pngCompression,
|
|
471
|
-
...
|
|
474
|
+
...width && height && { width, height }
|
|
472
475
|
};
|
|
473
476
|
};
|
|
474
477
|
var objectIdSchema = z.string().refine((val) => isValidObjectId(val), {
|
|
@@ -1547,9 +1550,9 @@ var driveUpload = async (source, key, options) => {
|
|
|
1547
1550
|
}
|
|
1548
1551
|
}
|
|
1549
1552
|
let resolvedParentId = null;
|
|
1550
|
-
if (options.folder
|
|
1553
|
+
if (options.folder && "path" in options.folder) {
|
|
1551
1554
|
resolvedParentId = await resolveFolderByPath(options.folder.path, key, accountId);
|
|
1552
|
-
} else if (options.folder
|
|
1555
|
+
} else if (options.folder && "id" in options.folder && options.folder.id !== "root") {
|
|
1553
1556
|
resolvedParentId = options.folder.id;
|
|
1554
1557
|
} else if (options.parentId && options.parentId !== "root") {
|
|
1555
1558
|
resolvedParentId = options.parentId;
|
|
@@ -1720,6 +1723,9 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1720
1723
|
const settings = getImageSettings(fileSize, quality, display, sizePreset);
|
|
1721
1724
|
let targetFormat = format || mime.split("/")[1];
|
|
1722
1725
|
if (targetFormat === "jpg") targetFormat = "jpeg";
|
|
1726
|
+
if (!["jpeg", "png", "webp", "avif"].includes(targetFormat)) {
|
|
1727
|
+
targetFormat = format || "webp";
|
|
1728
|
+
}
|
|
1723
1729
|
const cacheDir = path.join(config.storage.path, "file", drive._id.toString(), "cache");
|
|
1724
1730
|
const cacheKey = [
|
|
1725
1731
|
"opt",
|
|
@@ -1756,13 +1762,17 @@ var driveAPIHandler = async (req, res) => {
|
|
|
1756
1762
|
pipeline = pipeline.png({ compressionLevel: settings.pngCompression, adaptiveFiltering: true });
|
|
1757
1763
|
res.setHeader("Content-Type", "image/png");
|
|
1758
1764
|
} else if (targetFormat === "webp") {
|
|
1759
|
-
|
|
1765
|
+
const webpEffort = Math.min(settings.effort, 6);
|
|
1766
|
+
pipeline = pipeline.webp({ quality: settings.quality, effort: webpEffort });
|
|
1760
1767
|
res.setHeader("Content-Type", "image/webp");
|
|
1761
1768
|
} else if (targetFormat === "avif") {
|
|
1762
1769
|
pipeline = pipeline.avif({ quality: settings.quality, effort: settings.effort });
|
|
1763
1770
|
res.setHeader("Content-Type", "image/avif");
|
|
1764
1771
|
}
|
|
1765
1772
|
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
1773
|
+
pipeline.on("error", (err) => {
|
|
1774
|
+
console.error("[next-drive] Pipeline error:", err);
|
|
1775
|
+
});
|
|
1766
1776
|
stream.pipe(pipeline);
|
|
1767
1777
|
pipeline.clone().toFile(cachePath).catch((e) => console.error("[next-drive] Cache write failed:", e));
|
|
1768
1778
|
pipeline.clone().pipe(res);
|
|
@@ -2306,5 +2316,5 @@ var driveAPIHandler = async (req, res) => {
|
|
|
2306
2316
|
};
|
|
2307
2317
|
|
|
2308
2318
|
export { driveAPIHandler, driveConfiguration, driveDelete, driveFilePath, driveFileSchemaZod, driveGetUrl, driveInfo, driveList, driveReadFile, driveUpload, getDriveConfig, getDriveInformation };
|
|
2309
|
-
//# sourceMappingURL=chunk-
|
|
2310
|
-
//# sourceMappingURL=chunk-
|
|
2319
|
+
//# sourceMappingURL=chunk-AYHO6FSR.js.map
|
|
2320
|
+
//# sourceMappingURL=chunk-AYHO6FSR.js.map
|