@frybynite/image-cloud 0.5.0 → 0.6.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 +46 -13
- package/dist/composite-CtUxtN2l.js +96 -0
- package/dist/composite-CtUxtN2l.js.map +1 -0
- package/dist/google-drive-CC-qFSV1.js +260 -0
- package/dist/google-drive-CC-qFSV1.js.map +1 -0
- package/dist/image-cloud-auto-init.js +399 -842
- package/dist/image-cloud-auto-init.js.map +1 -1
- package/dist/image-cloud.js +1356 -1341
- package/dist/image-cloud.js.map +1 -1
- package/dist/image-cloud.umd.js +5 -5
- package/dist/image-cloud.umd.js.map +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/loaders/all.d.ts +1654 -0
- package/dist/loaders/all.js +496 -0
- package/dist/loaders/all.js.map +1 -0
- package/dist/loaders/composite.d.ts +1654 -0
- package/dist/loaders/composite.js +96 -0
- package/dist/loaders/composite.js.map +1 -0
- package/dist/loaders/google-drive.d.ts +1654 -0
- package/dist/loaders/google-drive.js +260 -0
- package/dist/loaders/google-drive.js.map +1 -0
- package/dist/loaders/static.d.ts +1654 -0
- package/dist/loaders/static.js +219 -0
- package/dist/loaders/static.js.map +1 -0
- package/dist/react.d.ts +17 -0
- package/dist/react.js +575 -1018
- package/dist/react.js.map +1 -1
- package/dist/static-ejylHtQ4.js +219 -0
- package/dist/static-ejylHtQ4.js.map +1 -0
- package/dist/vue.d.ts +17 -0
- package/dist/vue.js +507 -950
- package/dist/vue.js.map +1 -1
- package/dist/web-component.d.ts +17 -0
- package/dist/web-component.js +513 -956
- package/dist/web-component.js.map +1 -1
- package/package.json +24 -5
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
const h = class h {
|
|
2
|
+
/**
|
|
3
|
+
* Register a loader implementation with the registry
|
|
4
|
+
* @param name - Loader identifier (e.g., 'static', 'google-drive', 'composite')
|
|
5
|
+
* @param Loader - Loader class constructor to register
|
|
6
|
+
*/
|
|
7
|
+
static registerLoader(e, s) {
|
|
8
|
+
h.registry.set(e, s);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get a registered loader implementation
|
|
12
|
+
* @param name - Loader identifier
|
|
13
|
+
* @returns Loader class constructor
|
|
14
|
+
* @throws Error if loader is not registered
|
|
15
|
+
*/
|
|
16
|
+
static getLoader(e) {
|
|
17
|
+
const s = h.registry.get(e);
|
|
18
|
+
if (!s)
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Loader "${e}" is not registered. Import "@frybynite/image-cloud/loaders/${e}" or "@frybynite/image-cloud/loaders/all".`
|
|
21
|
+
);
|
|
22
|
+
return s;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if a loader is registered
|
|
26
|
+
* @param name - Loader identifier
|
|
27
|
+
* @returns True if the loader is registered, false otherwise
|
|
28
|
+
*/
|
|
29
|
+
static isRegistered(e) {
|
|
30
|
+
return h.registry.has(e);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
h.registry = /* @__PURE__ */ new Map();
|
|
34
|
+
let g = h;
|
|
35
|
+
class f {
|
|
36
|
+
constructor(e) {
|
|
37
|
+
if (this._prepared = !1, this._discoveredUrls = [], this.apiKey = e.apiKey ?? "", this.apiEndpoint = e.apiEndpoint ?? "https://www.googleapis.com/drive/v3/files", this.debugLogging = e.debugLogging ?? !1, this.sources = e.sources ?? [], !this.sources || this.sources.length === 0)
|
|
38
|
+
throw new Error("GoogleDriveLoader requires at least one source to be configured");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Prepare the loader by discovering all images from configured sources
|
|
42
|
+
* @param filter - Filter to apply to discovered images
|
|
43
|
+
*/
|
|
44
|
+
async prepare(e) {
|
|
45
|
+
this._discoveredUrls = [];
|
|
46
|
+
for (const s of this.sources)
|
|
47
|
+
if ("folders" in s)
|
|
48
|
+
for (const r of s.folders) {
|
|
49
|
+
const t = s.recursive !== void 0 ? s.recursive : !0, o = await this.loadFromFolder(r, e, t);
|
|
50
|
+
this._discoveredUrls.push(...o);
|
|
51
|
+
}
|
|
52
|
+
else if ("files" in s) {
|
|
53
|
+
const r = await this.loadFiles(s.files, e);
|
|
54
|
+
this._discoveredUrls.push(...r);
|
|
55
|
+
}
|
|
56
|
+
this._prepared = !0;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the number of discovered images
|
|
60
|
+
* @throws Error if called before prepare()
|
|
61
|
+
*/
|
|
62
|
+
imagesLength() {
|
|
63
|
+
if (!this._prepared)
|
|
64
|
+
throw new Error("GoogleDriveLoader.imagesLength() called before prepare()");
|
|
65
|
+
return this._discoveredUrls.length;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the ordered list of image URLs
|
|
69
|
+
* @throws Error if called before prepare()
|
|
70
|
+
*/
|
|
71
|
+
imageURLs() {
|
|
72
|
+
if (!this._prepared)
|
|
73
|
+
throw new Error("GoogleDriveLoader.imageURLs() called before prepare()");
|
|
74
|
+
return [...this._discoveredUrls];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if the loader has been prepared
|
|
78
|
+
*/
|
|
79
|
+
isPrepared() {
|
|
80
|
+
return this._prepared;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Extract folder ID from various Google Drive URL formats
|
|
84
|
+
* @param folderUrl - Google Drive folder URL
|
|
85
|
+
* @returns Folder ID or null if invalid
|
|
86
|
+
*/
|
|
87
|
+
extractFolderId(e) {
|
|
88
|
+
const s = [
|
|
89
|
+
/\/folders\/([a-zA-Z0-9_-]+)/,
|
|
90
|
+
// Standard format
|
|
91
|
+
/id=([a-zA-Z0-9_-]+)/
|
|
92
|
+
// Alternative format
|
|
93
|
+
];
|
|
94
|
+
for (const r of s) {
|
|
95
|
+
const t = e.match(r);
|
|
96
|
+
if (t && t[1])
|
|
97
|
+
return t[1];
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Load images from a Google Drive folder
|
|
103
|
+
* @param folderUrl - Google Drive folder URL
|
|
104
|
+
* @param filter - Filter to apply to discovered images
|
|
105
|
+
* @param recursive - Whether to include images from subfolders
|
|
106
|
+
* @returns Promise resolving to array of image URLs
|
|
107
|
+
*/
|
|
108
|
+
async loadFromFolder(e, s, r = !0) {
|
|
109
|
+
const t = this.extractFolderId(e);
|
|
110
|
+
if (!t)
|
|
111
|
+
throw new Error("Invalid Google Drive folder URL. Please check the URL format.");
|
|
112
|
+
if (!this.apiKey || this.apiKey === "YOUR_API_KEY_HERE")
|
|
113
|
+
return this.loadImagesDirectly(t, s);
|
|
114
|
+
try {
|
|
115
|
+
return r ? await this.loadImagesRecursively(t, s) : await this.loadImagesFromSingleFolder(t, s);
|
|
116
|
+
} catch (o) {
|
|
117
|
+
return console.error("Error loading from Google Drive API:", o), this.loadImagesDirectly(t, s);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Load images from a single folder (non-recursive)
|
|
122
|
+
* @param folderId - Google Drive folder ID
|
|
123
|
+
* @param filter - Filter to apply to discovered images
|
|
124
|
+
* @returns Promise resolving to array of image URLs
|
|
125
|
+
*/
|
|
126
|
+
async loadImagesFromSingleFolder(e, s) {
|
|
127
|
+
const r = [], t = `'${e}' in parents and trashed=false`, d = `${this.apiEndpoint}?q=${encodeURIComponent(t)}&fields=files(id,name,mimeType,thumbnailLink)&key=${this.apiKey}`, i = await fetch(d);
|
|
128
|
+
if (!i.ok)
|
|
129
|
+
throw new Error(`API request failed: ${i.status} ${i.statusText}`);
|
|
130
|
+
const c = (await i.json()).files.filter(
|
|
131
|
+
(l) => l.mimeType.startsWith("image/") && s.isAllowed(l.name)
|
|
132
|
+
);
|
|
133
|
+
return this.log(`Found ${c.length} images in folder ${e} (non-recursive)`), c.forEach((l) => {
|
|
134
|
+
r.push(`https://lh3.googleusercontent.com/d/${l.id}=s1600`), this.log(`Added file: ${l.name}`);
|
|
135
|
+
}), r;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Load specific files by their URLs or IDs
|
|
139
|
+
* @param fileUrls - Array of Google Drive file URLs or IDs
|
|
140
|
+
* @param filter - Filter to apply to discovered images
|
|
141
|
+
* @returns Promise resolving to array of image URLs
|
|
142
|
+
*/
|
|
143
|
+
async loadFiles(e, s) {
|
|
144
|
+
const r = [];
|
|
145
|
+
for (const t of e) {
|
|
146
|
+
const o = this.extractFileId(t);
|
|
147
|
+
if (!o) {
|
|
148
|
+
this.log(`Skipping invalid file URL: ${t}`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (this.apiKey && this.apiKey !== "YOUR_API_KEY_HERE")
|
|
152
|
+
try {
|
|
153
|
+
const d = `${this.apiEndpoint}/${o}?fields=name,mimeType&key=${this.apiKey}`, i = await fetch(d);
|
|
154
|
+
if (i.ok) {
|
|
155
|
+
const n = await i.json();
|
|
156
|
+
n.mimeType.startsWith("image/") && s.isAllowed(n.name) ? (r.push(`https://lh3.googleusercontent.com/d/${o}=s1600`), this.log(`Added file: ${n.name}`)) : this.log(`Skipping non-image file: ${n.name} (${n.mimeType})`);
|
|
157
|
+
} else
|
|
158
|
+
this.log(`Failed to fetch metadata for file ${o}: ${i.status}`);
|
|
159
|
+
} catch (d) {
|
|
160
|
+
this.log(`Error fetching metadata for file ${o}:`, d);
|
|
161
|
+
}
|
|
162
|
+
else
|
|
163
|
+
r.push(`https://lh3.googleusercontent.com/d/${o}=s1600`);
|
|
164
|
+
}
|
|
165
|
+
return r;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Extract file ID from Google Drive file URL
|
|
169
|
+
* @param fileUrl - Google Drive file URL or file ID
|
|
170
|
+
* @returns File ID or null if invalid
|
|
171
|
+
*/
|
|
172
|
+
extractFileId(e) {
|
|
173
|
+
if (!/[/:.]/.test(e))
|
|
174
|
+
return e;
|
|
175
|
+
const s = [
|
|
176
|
+
/\/file\/d\/([a-zA-Z0-9_-]+)/,
|
|
177
|
+
// Standard file format
|
|
178
|
+
/\/open\?id=([a-zA-Z0-9_-]+)/,
|
|
179
|
+
// Alternative format
|
|
180
|
+
/id=([a-zA-Z0-9_-]+)/
|
|
181
|
+
// Generic id parameter
|
|
182
|
+
];
|
|
183
|
+
for (const r of s) {
|
|
184
|
+
const t = e.match(r);
|
|
185
|
+
if (t && t[1])
|
|
186
|
+
return t[1];
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Recursively load images from a folder and all its subfolders
|
|
192
|
+
* @param folderId - Google Drive folder ID
|
|
193
|
+
* @param filter - Filter to apply to discovered images
|
|
194
|
+
* @returns Promise resolving to array of image URLs
|
|
195
|
+
*/
|
|
196
|
+
async loadImagesRecursively(e, s) {
|
|
197
|
+
const r = [], t = `'${e}' in parents and trashed=false`, d = `${this.apiEndpoint}?q=${encodeURIComponent(t)}&fields=files(id,name,mimeType,thumbnailLink)&key=${this.apiKey}`, i = await fetch(d);
|
|
198
|
+
if (!i.ok)
|
|
199
|
+
throw new Error(`API request failed: ${i.status} ${i.statusText}`);
|
|
200
|
+
const n = await i.json(), c = n.files.filter(
|
|
201
|
+
(a) => a.mimeType.startsWith("image/") && s.isAllowed(a.name)
|
|
202
|
+
), l = n.files.filter(
|
|
203
|
+
(a) => a.mimeType === "application/vnd.google-apps.folder"
|
|
204
|
+
);
|
|
205
|
+
this.log(`Found ${n.files.length} total items in folder ${e}`), n.files.forEach((a) => this.log(` - File: ${a.name} (${a.mimeType})`)), this.log(`- ${c.length} valid files (images only)`), this.log(`- ${l.length} subfolders`), c.forEach((a) => {
|
|
206
|
+
r.push(`https://lh3.googleusercontent.com/d/${a.id}=s1600`), this.log(`Added file: ${a.name}`);
|
|
207
|
+
});
|
|
208
|
+
for (const a of l) {
|
|
209
|
+
this.log(`Loading images from subfolder: ${a.name}`);
|
|
210
|
+
const m = await this.loadImagesRecursively(a.id, s);
|
|
211
|
+
r.push(...m);
|
|
212
|
+
}
|
|
213
|
+
return r;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Direct loading method (no API key required, but less reliable)
|
|
217
|
+
* Uses embedded folder view to scrape image IDs
|
|
218
|
+
* @param folderId - Google Drive folder ID
|
|
219
|
+
* @param filter - Filter to apply (not used in fallback mode)
|
|
220
|
+
* @returns Promise resolving to array of image URLs
|
|
221
|
+
*/
|
|
222
|
+
async loadImagesDirectly(e, s) {
|
|
223
|
+
try {
|
|
224
|
+
const r = `https://drive.google.com/embeddedfolderview?id=${e}`, t = await fetch(r, { mode: "cors" });
|
|
225
|
+
if (!t.ok)
|
|
226
|
+
throw new Error("Cannot access folder directly (CORS or permissions issue)");
|
|
227
|
+
const o = await t.text(), d = /\/file\/d\/([a-zA-Z0-9_-]+)/g, i = [...o.matchAll(d)];
|
|
228
|
+
return [...new Set(i.map((l) => l[1]))].map(
|
|
229
|
+
(l) => `https://drive.google.com/uc?export=view&id=${l}`
|
|
230
|
+
);
|
|
231
|
+
} catch (r) {
|
|
232
|
+
throw console.error("Direct loading failed:", r), new Error(
|
|
233
|
+
`Unable to load images. Please ensure:
|
|
234
|
+
1. The folder is shared publicly (Anyone with the link can view)
|
|
235
|
+
2. The folder contains image files
|
|
236
|
+
3. Consider adding a Google Drive API key in config.js for better reliability`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Manually add image URLs (for testing or when auto-loading fails)
|
|
242
|
+
* @param imageIds - Array of Google Drive file IDs
|
|
243
|
+
* @returns Array of direct image URLs
|
|
244
|
+
*/
|
|
245
|
+
manualImageUrls(e) {
|
|
246
|
+
return e.map((s) => `https://drive.google.com/uc?export=view&id=${s}`);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Debug logging helper
|
|
250
|
+
* @param args - Arguments to log
|
|
251
|
+
*/
|
|
252
|
+
log(...e) {
|
|
253
|
+
this.debugLogging && typeof console < "u" && console.log(...e);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
g.registerLoader("google-drive", f);
|
|
257
|
+
export {
|
|
258
|
+
f as GoogleDriveLoader
|
|
259
|
+
};
|
|
260
|
+
//# sourceMappingURL=google-drive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-drive.js","sources":["../../src/engines/LoaderRegistry.ts","../../src/loaders/GoogleDriveLoader.ts","../../src/loaders/index-google-drive.ts"],"sourcesContent":["/**\n * Loader Registry - Manages registration and lookup of image loader implementations\n *\n * This registry enables dynamic loading of loaders through separate bundles\n * while maintaining a central registry of available loaders. It mirrors the\n * LayoutEngine registry pattern for consistency.\n *\n * Public API:\n * - registerLoader(name, LoaderClass)\n * - getLoader(name)\n * - isRegistered(name)\n */\n\nimport type { ImageLoader, StaticLoaderInnerConfig, GoogleDriveLoaderInnerConfig } from '../config/types';\n\n/**\n * Constructor signature for loader classes\n * Supports both simple loaders and composite loaders with their respective config types\n */\nexport type LoaderConstructor =\n | (new (config: StaticLoaderInnerConfig) => ImageLoader)\n | (new (config: GoogleDriveLoaderInnerConfig) => ImageLoader)\n | (new (config: any) => ImageLoader);\n\nexport class LoaderRegistry {\n private static readonly registry = new Map<string, LoaderConstructor>();\n\n /**\n * Register a loader implementation with the registry\n * @param name - Loader identifier (e.g., 'static', 'google-drive', 'composite')\n * @param Loader - Loader class constructor to register\n */\n static registerLoader(name: string, Loader: LoaderConstructor): void {\n LoaderRegistry.registry.set(name, Loader);\n }\n\n /**\n * Get a registered loader implementation\n * @param name - Loader identifier\n * @returns Loader class constructor\n * @throws Error if loader is not registered\n */\n static getLoader(name: string): LoaderConstructor {\n const Loader = LoaderRegistry.registry.get(name);\n\n if (!Loader) {\n throw new Error(\n `Loader \"${name}\" is not registered. ` +\n `Import \"@frybynite/image-cloud/loaders/${name}\" or \"@frybynite/image-cloud/loaders/all\".`\n );\n }\n\n return Loader;\n }\n\n /**\n * Check if a loader is registered\n * @param name - Loader identifier\n * @returns True if the loader is registered, false otherwise\n */\n static isRegistered(name: string): boolean {\n return LoaderRegistry.registry.has(name);\n }\n}\n","/**\n * GoogleDriveLoader.ts\n * Loads images from a public Google Drive folder\n *\n * Public API:\n * - prepare(filter) - Async discovery of images\n * - imagesLength() - Get count of discovered images\n * - imageURLs() - Get ordered list of image URLs\n * - isPrepared() - Check if loader has been prepared\n *\n * Helper methods (for advanced usage):\n * - extractFolderId(folderUrl)\n * - manualImageUrls(imageIds)\n */\n\nimport type { ImageLoader, IImageFilter, GoogleDriveResponse, GoogleDriveLoaderInnerConfig, GoogleDriveSource } from '../config/types';\n\nexport class GoogleDriveLoader implements ImageLoader {\n private apiKey: string;\n private apiEndpoint: string;\n private debugLogging: boolean;\n private sources: GoogleDriveSource[];\n\n // State for new interface\n private _prepared: boolean = false;\n private _discoveredUrls: string[] = [];\n\n constructor(config: GoogleDriveLoaderInnerConfig) {\n this.apiKey = config.apiKey ?? '';\n this.apiEndpoint = config.apiEndpoint ?? 'https://www.googleapis.com/drive/v3/files';\n this.debugLogging = config.debugLogging ?? false;\n this.sources = config.sources ?? [];\n\n // Validate that we have sources configured\n if (!this.sources || this.sources.length === 0) {\n throw new Error('GoogleDriveLoader requires at least one source to be configured');\n }\n }\n\n /**\n * Prepare the loader by discovering all images from configured sources\n * @param filter - Filter to apply to discovered images\n */\n async prepare(filter: IImageFilter): Promise<void> {\n this._discoveredUrls = [];\n\n for (const source of this.sources) {\n if ('folders' in source) {\n for (const folderUrl of source.folders) {\n const recursive = source.recursive !== undefined ? source.recursive : true;\n const urls = await this.loadFromFolder(folderUrl, filter, recursive);\n this._discoveredUrls.push(...urls);\n }\n } else if ('files' in source) {\n const urls = await this.loadFiles(source.files, filter);\n this._discoveredUrls.push(...urls);\n }\n }\n\n this._prepared = true;\n }\n\n /**\n * Get the number of discovered images\n * @throws Error if called before prepare()\n */\n imagesLength(): number {\n if (!this._prepared) {\n throw new Error('GoogleDriveLoader.imagesLength() called before prepare()');\n }\n return this._discoveredUrls.length;\n }\n\n /**\n * Get the ordered list of image URLs\n * @throws Error if called before prepare()\n */\n imageURLs(): string[] {\n if (!this._prepared) {\n throw new Error('GoogleDriveLoader.imageURLs() called before prepare()');\n }\n return [...this._discoveredUrls];\n }\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean {\n return this._prepared;\n }\n\n /**\n * Extract folder ID from various Google Drive URL formats\n * @param folderUrl - Google Drive folder URL\n * @returns Folder ID or null if invalid\n */\n extractFolderId(folderUrl: string): string | null {\n // Handle various URL formats:\n // https://drive.google.com/drive/folders/FOLDER_ID\n // https://drive.google.com/drive/folders/FOLDER_ID?usp=sharing\n\n const patterns = [\n /\\/folders\\/([a-zA-Z0-9_-]+)/, // Standard format\n /id=([a-zA-Z0-9_-]+)/ // Alternative format\n ];\n\n for (const pattern of patterns) {\n const match = folderUrl.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n /**\n * Load images from a Google Drive folder\n * @param folderUrl - Google Drive folder URL\n * @param filter - Filter to apply to discovered images\n * @param recursive - Whether to include images from subfolders\n * @returns Promise resolving to array of image URLs\n */\n private async loadFromFolder(folderUrl: string, filter: IImageFilter, recursive: boolean = true): Promise<string[]> {\n const folderId = this.extractFolderId(folderUrl);\n\n if (!folderId) {\n throw new Error('Invalid Google Drive folder URL. Please check the URL format.');\n }\n\n // If no API key is configured, use direct link method\n if (!this.apiKey || this.apiKey === 'YOUR_API_KEY_HERE') {\n return this.loadImagesDirectly(folderId, filter);\n }\n\n try {\n if (recursive) {\n return await this.loadImagesRecursively(folderId, filter);\n } else {\n return await this.loadImagesFromSingleFolder(folderId, filter);\n }\n } catch (error) {\n console.error('Error loading from Google Drive API:', error);\n // Fallback to direct link method\n return this.loadImagesDirectly(folderId, filter);\n }\n }\n\n /**\n * Load images from a single folder (non-recursive)\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesFromSingleFolder(folderId: string, filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n // Query for all files in this folder\n const query = `'${folderId}' in parents and trashed=false`;\n const fields = 'files(id,name,mimeType,thumbnailLink)';\n const url = `${this.apiEndpoint}?q=${encodeURIComponent(query)}&fields=${fields}&key=${this.apiKey}`;\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`API request failed: ${response.status} ${response.statusText}`);\n }\n\n const data: GoogleDriveResponse = await response.json();\n\n // Filter for valid image files only using the provided filter\n const validFiles = data.files.filter(file =>\n file.mimeType.startsWith('image/') && filter.isAllowed(file.name)\n );\n\n this.log(`Found ${validFiles.length} images in folder ${folderId} (non-recursive)`);\n\n // Add image URLs\n validFiles.forEach(file => {\n imageUrls.push(`https://lh3.googleusercontent.com/d/${file.id}=s1600`);\n this.log(`Added file: ${file.name}`);\n });\n\n return imageUrls;\n }\n\n /**\n * Load specific files by their URLs or IDs\n * @param fileUrls - Array of Google Drive file URLs or IDs\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadFiles(fileUrls: string[], filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n for (const fileUrl of fileUrls) {\n const fileId = this.extractFileId(fileUrl);\n\n if (!fileId) {\n this.log(`Skipping invalid file URL: ${fileUrl}`);\n continue;\n }\n\n // Validate it's an image file\n if (this.apiKey && this.apiKey !== 'YOUR_API_KEY_HERE') {\n try {\n // Get file metadata to verify it's an image\n const metadataUrl = `${this.apiEndpoint}/${fileId}?fields=name,mimeType&key=${this.apiKey}`;\n const response = await fetch(metadataUrl);\n\n if (response.ok) {\n const metadata = await response.json();\n if (metadata.mimeType.startsWith('image/') && filter.isAllowed(metadata.name)) {\n imageUrls.push(`https://lh3.googleusercontent.com/d/${fileId}=s1600`);\n this.log(`Added file: ${metadata.name}`);\n } else {\n this.log(`Skipping non-image file: ${metadata.name} (${metadata.mimeType})`);\n }\n } else {\n this.log(`Failed to fetch metadata for file ${fileId}: ${response.status}`);\n }\n } catch (error) {\n this.log(`Error fetching metadata for file ${fileId}:`, error);\n }\n } else {\n // Without API key, assume it's valid and add it\n imageUrls.push(`https://lh3.googleusercontent.com/d/${fileId}=s1600`);\n }\n }\n\n return imageUrls;\n }\n\n /**\n * Extract file ID from Google Drive file URL\n * @param fileUrl - Google Drive file URL or file ID\n * @returns File ID or null if invalid\n */\n private extractFileId(fileUrl: string): string | null {\n // Handle various URL formats:\n // https://drive.google.com/file/d/FILE_ID/view\n // https://drive.google.com/open?id=FILE_ID\n // FILE_ID (raw ID)\n\n // If it looks like a raw ID (no slashes or protocol), return it\n if (!/[/:.]/.test(fileUrl)) {\n return fileUrl;\n }\n\n const patterns = [\n /\\/file\\/d\\/([a-zA-Z0-9_-]+)/, // Standard file format\n /\\/open\\?id=([a-zA-Z0-9_-]+)/, // Alternative format\n /id=([a-zA-Z0-9_-]+)/ // Generic id parameter\n ];\n\n for (const pattern of patterns) {\n const match = fileUrl.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n /**\n * Recursively load images from a folder and all its subfolders\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesRecursively(folderId: string, filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n // Query for all files in this folder\n const query = `'${folderId}' in parents and trashed=false`;\n // Request thumbnailLink for PDFs\n const fields = 'files(id,name,mimeType,thumbnailLink)';\n const url = `${this.apiEndpoint}?q=${encodeURIComponent(query)}&fields=${fields}&key=${this.apiKey}`;\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`API request failed: ${response.status} ${response.statusText}`);\n }\n\n const data: GoogleDriveResponse = await response.json();\n\n // Separate images and folders using the provided filter\n const validFiles = data.files.filter(file =>\n file.mimeType.startsWith('image/') && filter.isAllowed(file.name)\n );\n\n const subfolders = data.files.filter(file =>\n file.mimeType === 'application/vnd.google-apps.folder'\n );\n\n this.log(`Found ${data.files.length} total items in folder ${folderId}`);\n // Log details of all files to see what we are missing\n data.files.forEach(f => this.log(` - File: ${f.name} (${f.mimeType})`));\n\n this.log(`- ${validFiles.length} valid files (images only)`);\n this.log(`- ${subfolders.length} subfolders`);\n\n // Add image URLs from this folder\n validFiles.forEach(file => {\n // Use the reliable thumbnail/preview endpoint for both Images and PDFs\n // This works for public folders and handles file format conversion automatically\n // 'sz=w1000' requests a high-quality preview (1000px width)\n // detailed explanation:\n // 1. \"drive.google.com\" is blocked by ad-blockers (net::ERR_BLOCKED_BY_CLIENT)\n // 2. The API's \"thumbnailLink\" is a signed URL that can expire or fail 403.\n // 3. \"lh3.googleusercontent.com/d/{ID}\" is the permanent CDN link structure.\n // It bypasses the domain block AND the signing issues.\n imageUrls.push(`https://lh3.googleusercontent.com/d/${file.id}=s1600`);\n\n this.log(`Added file: ${file.name}`);\n });\n\n // Recursively process subfolders\n for (const folder of subfolders) {\n this.log(`Loading images from subfolder: ${folder.name}`);\n const subfolderImages = await this.loadImagesRecursively(folder.id, filter);\n imageUrls.push(...subfolderImages);\n }\n\n return imageUrls;\n }\n\n /**\n * Direct loading method (no API key required, but less reliable)\n * Uses embedded folder view to scrape image IDs\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply (not used in fallback mode)\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesDirectly(folderId: string, _filter: IImageFilter): Promise<string[]> {\n // For now, we'll return a method that requires the user to manually provide image IDs\n // or we construct URLs based on a known pattern\n\n // This is a fallback - in production, you'd want to use the API\n // For demo purposes, we can try to fetch the folder page and extract image IDs\n\n try {\n // Attempt to fetch folder page (CORS may block this)\n const folderUrl = `https://drive.google.com/embeddedfolderview?id=${folderId}`;\n const response = await fetch(folderUrl, { mode: 'cors' });\n\n if (!response.ok) {\n throw new Error('Cannot access folder directly (CORS or permissions issue)');\n }\n\n const html = await response.text();\n\n // Try to extract image IDs from HTML (this is fragile and may break)\n const imageIdPattern = /\\/file\\/d\\/([a-zA-Z0-9_-]+)/g;\n const matches = [...html.matchAll(imageIdPattern)];\n const imageIds = [...new Set(matches.map(m => m[1]))];\n\n const imageUrls = imageIds.map(id =>\n `https://drive.google.com/uc?export=view&id=${id}`\n );\n\n return imageUrls;\n\n } catch (error) {\n console.error('Direct loading failed:', error);\n throw new Error(\n 'Unable to load images. Please ensure:\\n' +\n '1. The folder is shared publicly (Anyone with the link can view)\\n' +\n '2. The folder contains image files\\n' +\n '3. Consider adding a Google Drive API key in config.js for better reliability'\n );\n }\n }\n\n /**\n * Manually add image URLs (for testing or when auto-loading fails)\n * @param imageIds - Array of Google Drive file IDs\n * @returns Array of direct image URLs\n */\n manualImageUrls(imageIds: string[]): string[] {\n return imageIds.map(id => `https://drive.google.com/uc?export=view&id=${id}`);\n }\n\n /**\n * Debug logging helper\n * @param args - Arguments to log\n */\n private log(...args: unknown[]): void {\n if (this.debugLogging && typeof console !== 'undefined') {\n console.log(...args);\n }\n }\n}\n","import { LoaderRegistry } from '../engines/LoaderRegistry';\nimport { GoogleDriveLoader } from './GoogleDriveLoader';\n\nLoaderRegistry.registerLoader('google-drive', GoogleDriveLoader);\n\nexport { GoogleDriveLoader };\n"],"names":["_LoaderRegistry","name","Loader","LoaderRegistry","GoogleDriveLoader","config","filter","source","folderUrl","recursive","urls","patterns","pattern","match","folderId","error","imageUrls","query","url","response","validFiles","file","fileUrls","fileUrl","fileId","metadataUrl","metadata","data","subfolders","f","folder","subfolderImages","_filter","html","imageIdPattern","matches","m","id","imageIds","args"],"mappings":"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"}
|