diff --git a/src/MediaManager.html b/src/MediaManager.html
index 3d9af71..cd11c51 100644
--- a/src/MediaManager.html
+++ b/src/MediaManager.html
@@ -1364,7 +1364,7 @@
})
.withFailureHandler(e => ui.logStatus('error', `Ticket failed: ${e.message}`, 'error'))
- .getUploadUrl(state.sku, filename, mimeType);
+ .getUploadUrl(state.sku, filename, mimeType, fetchUrl);
})
.withFailureHandler(e => {
ui.logStatus('error', `Cannot transfer ${filename}: ${e.message}`, 'error');
diff --git a/src/mediaHandlers.ts b/src/mediaHandlers.ts
index ca7c34d..b015337 100644
--- a/src/mediaHandlers.ts
+++ b/src/mediaHandlers.ts
@@ -128,7 +128,7 @@ export function linkDriveFileToShopifyMedia(sku: string, driveId: string, shopif
}
// NEW: Resumable Upload Ticket
-export function getUploadUrl(sku: string, filename: string, mimeType: string) {
+export function getUploadUrl(sku: string, filename: string, mimeType: string, sourceUrl?: string) {
const config = new Config()
const driveService = new GASDriveService()
@@ -136,7 +136,7 @@ export function getUploadUrl(sku: string, filename: string, mimeType: string) {
const folder = driveService.getOrCreateFolder(sku, config.productPhotosFolderId)
// Generate Ticket
- return driveService.getResumableUploadUrl(filename, mimeType, folder.getId())
+ return driveService.getResumableUploadUrl(filename, mimeType, folder.getId(), sourceUrl)
}
// Deprecated (but kept for fallback/legacy small files if needed)
diff --git a/src/services/GASDriveService.ts b/src/services/GASDriveService.ts
index cf93081..303b5b9 100644
--- a/src/services/GASDriveService.ts
+++ b/src/services/GASDriveService.ts
@@ -100,16 +100,59 @@ export class GASDriveService implements IDriveService {
}
}
- getResumableUploadUrl(filename: string, mimeType: string, folderId: string): string {
+ getResumableUploadUrl(filename: string, mimeType: string, folderId: string, sourceUrl?: string): string {
const token = ScriptApp.getOAuthToken();
// Metadata for the file to be created
- const metadata = {
+ const metadata: any = {
name: filename,
mimeType: mimeType,
parents: [folderId]
};
+ // feature: video-thumbnails-for-processing
+ // If this is a video from Google Photos, fetch a thumbnail and set as contentHint.
+ if (sourceUrl && sourceUrl.includes('googleusercontent.com') && mimeType.startsWith('video/')) {
+ try {
+ console.log(`[GASDriveService] Fetching thumbnail for ${filename}...`);
+ // =w800-h600 gives a decent sized jpeg thumbnail
+ // Use same auth token as we use for the video fetch
+ let thumbUrl = sourceUrl;
+ if (!thumbUrl.includes('=')) {
+ thumbUrl += '=w320-h320';
+ } else {
+ thumbUrl = thumbUrl.replace(/=.*$/, '') + '=w320-h320';
+ }
+
+ const thumbResp = UrlFetchApp.fetch(thumbUrl, {
+ headers: { Authorization: `Bearer ${token}` },
+ muteHttpExceptions: true
+ });
+
+ if (thumbResp.getResponseCode() === 200) {
+ const thumbBlob = thumbResp.getBlob();
+ const base64Thumb = Utilities.base64EncodeWebSafe(thumbBlob.getBytes());
+
+ // Drive API Limit check (2MB)
+ if (base64Thumb.length < 2 * 1024 * 1024) {
+ metadata.contentHints = {
+ thumbnail: {
+ image: base64Thumb,
+ mimeType: 'image/jpeg'
+ }
+ };
+ console.log(`[GASDriveService] Custom thumbnail injected (${base64Thumb.length} chars).`);
+ } else {
+ console.warn(`[GASDriveService] Thumbnail too large (${base64Thumb.length} chars). Skipping injection.`);
+ }
+ } else {
+ console.warn(`[GASDriveService] Thumbnail fetch failed: ${thumbResp.getResponseCode()}`);
+ }
+ } catch (e) {
+ console.warn(`[GASDriveService] Thumbnail generation failed: ${e.message}`);
+ }
+ }
+
const params = {
method: 'post' as const,
contentType: 'application/json',