-
-
Notifications
You must be signed in to change notification settings - Fork 80
Expand file tree
/
Copy pathcloudinary-helpers.ts
More file actions
216 lines (184 loc) · 6.41 KB
/
Copy pathcloudinary-helpers.ts
File metadata and controls
216 lines (184 loc) · 6.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/**
* Cloudinary Helper Functions
* Utility functions to generate optimized Cloudinary URLs from public IDs
*/
export interface CloudinaryTransformOptions {
width?: number;
height?: number;
crop?: "fill" | "fit" | "scale" | "limit" | "pad" | "crop" | "thumb";
quality?: "auto" | "auto:best" | "auto:good" | "auto:eco" | "auto:low" | number;
format?: "auto" | "webp" | "jpg" | "png" | "avif";
gravity?: "auto" | "center" | "face" | "faces" | "north" | "south" | "east" | "west";
dpr?: "auto" | number;
flags?: string[];
}
const CLOUDINARY_CLOUD_NAME = process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME || "vetswhocode";
/**
* Generate an optimized Cloudinary URL from a public ID
* @param publicId - The Cloudinary public ID (e.g., "blog_header_nmrhob" or "v1714768089/blog_header_nmrhob")
* @param options - Transformation options
* @returns Full Cloudinary URL with transformations
*/
export function getCloudinaryUrl(
publicId: string,
options: CloudinaryTransformOptions = {}
): string {
if (!publicId) {
return "";
}
// Default transformations - keep it simple to match original URLs
const defaultOptions: CloudinaryTransformOptions = {
format: "auto",
quality: "auto",
gravity: "auto",
};
const mergedOptions = { ...defaultOptions, ...options };
// Build transformation string
const transformations: string[] = [];
if (mergedOptions.width) {
transformations.push(`w_${mergedOptions.width}`);
}
if (mergedOptions.height) {
transformations.push(`h_${mergedOptions.height}`);
}
if (mergedOptions.crop) {
transformations.push(`c_${mergedOptions.crop}`);
}
if (mergedOptions.quality) {
transformations.push(`q_${mergedOptions.quality}`);
}
if (mergedOptions.format) {
transformations.push(`f_${mergedOptions.format}`);
}
if (mergedOptions.gravity) {
transformations.push(`g_${mergedOptions.gravity}`);
}
if (mergedOptions.dpr) {
transformations.push(`dpr_${mergedOptions.dpr}`);
}
if (mergedOptions.flags && mergedOptions.flags.length > 0) {
mergedOptions.flags.forEach((flag) => {
transformations.push(`fl_${flag}`);
});
}
const transformString = transformations.join(",");
// Clean up the public ID (remove leading slashes)
const cleanPublicId = publicId.trim();
// Build the URL
return `https://res.cloudinary.com/${CLOUDINARY_CLOUD_NAME}/image/upload/${transformString}/${cleanPublicId}`;
}
/**
* Generate a blog header image URL with standard transformations
* @param imageSource - The Cloudinary public ID or full URL
* @returns Optimized blog header URL
*/
export function getBlogHeaderUrl(imageSource: string): string {
if (!imageSource) {
return "";
}
// If it's already a full URL, return it as-is
if (imageSource.startsWith("http://") || imageSource.startsWith("https://")) {
return imageSource;
}
// Otherwise, treat it as a public ID and generate the URL with blog header optimizations
return getCloudinaryUrl(imageSource, {
width: 1600,
height: 840,
crop: "fill",
quality: "auto:good",
format: "auto",
dpr: "auto",
});
}
/**
* Generate an Open Graph optimized image URL for social media sharing
* This function is specifically designed for og:image meta tags
* Dimensions: 1200x630 (optimal for Twitter, Facebook, LinkedIn)
* @param imageSource - The Cloudinary public ID or full URL
* @returns Optimized Open Graph image URL
*/
export function getBlogOpenGraphUrl(imageSource: string): string {
if (!imageSource) {
return "";
}
// If it's already a full URL, return it as-is
if (imageSource.startsWith("http://") || imageSource.startsWith("https://")) {
return imageSource;
}
// Generate URL with Open Graph specific optimizations
// Note: gravity is NOT included to avoid 400 errors (requires paid Cloudinary addon)
return getCloudinaryUrl(imageSource, {
width: 1200,
height: 630,
crop: "fill",
quality: "auto",
format: "auto",
});
}
/**
* Generate a blog thumbnail image URL with standard transformations
* @param publicId - The Cloudinary public ID
* @returns Optimized blog thumbnail URL
*/
export function getBlogThumbnailUrl(publicId: string): string {
return getCloudinaryUrl(publicId, {
width: 400,
height: 300,
crop: "fill",
quality: "auto",
format: "auto",
gravity: "auto",
});
}
/**
* Extract public ID from a Cloudinary URL
* @param url - Full Cloudinary URL
* @returns Public ID or null if not a valid Cloudinary URL
*/
export function extractPublicIdFromUrl(url: string): string | null {
if (!url || !url.includes("cloudinary.com")) {
return null;
}
try {
// Match pattern: /upload/[transformations]/[version]/[public_id].[extension]
// or: /upload/[version]/[public_id].[extension]
const match = url.match(/\/upload\/(?:.*?\/)?(v\d+\/)?(.+?)(?:\.[^.]+)?$/);
if (match) {
// Combine version and public_id if version exists
const version = match[1] || "";
const publicId = match[2];
return version + publicId;
}
return null;
} catch (error) {
console.error("Error extracting public ID from URL:", error);
return null;
}
}
/**
* Check if a URL is a Cloudinary URL
* @param url - URL to check
* @returns True if URL is from Cloudinary
*/
export function isCloudinaryUrl(url: string): boolean {
return url.includes("cloudinary.com") || url.includes("res.cloudinary.com");
}
/**
* Get image URL - accepts either a public ID or a full URL
* If it's already a URL, return it as-is
* If it's a public ID, generate the Cloudinary URL
* @param imageSource - Either a public ID or full URL
* @param options - Transformation options (only used if imageSource is a public ID)
* @returns Image URL
*/
export function getImageUrl(imageSource: string, options?: CloudinaryTransformOptions): string {
if (!imageSource) {
return "";
}
// If it's already a full URL, return it
if (imageSource.startsWith("http://") || imageSource.startsWith("https://")) {
return imageSource;
}
// Otherwise, treat it as a public ID and generate the URL
return getCloudinaryUrl(imageSource, options);
}