feat: Add custom video thumbnails for Drive uploads
- Implemented custom thumbnail injection in GASDriveService.getResumableUploadUrl. - Fetches thumbnails from Google Photos using w320 size to avoid API limits. - Added strict < 2MB size check for thumbnails. - Updated mediaHandlers and MediaManager to pass sourceUrl to the backend. - This allows Drive to display a visual cue immediately for video files still processing.
This commit is contained in:
@ -1364,7 +1364,7 @@
|
|||||||
|
|
||||||
})
|
})
|
||||||
.withFailureHandler(e => ui.logStatus('error', `Ticket failed: ${e.message}`, 'error'))
|
.withFailureHandler(e => ui.logStatus('error', `Ticket failed: ${e.message}`, 'error'))
|
||||||
.getUploadUrl(state.sku, filename, mimeType);
|
.getUploadUrl(state.sku, filename, mimeType, fetchUrl);
|
||||||
})
|
})
|
||||||
.withFailureHandler(e => {
|
.withFailureHandler(e => {
|
||||||
ui.logStatus('error', `Cannot transfer ${filename}: ${e.message}`, 'error');
|
ui.logStatus('error', `Cannot transfer ${filename}: ${e.message}`, 'error');
|
||||||
|
|||||||
@ -128,7 +128,7 @@ export function linkDriveFileToShopifyMedia(sku: string, driveId: string, shopif
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Resumable Upload Ticket
|
// 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 config = new Config()
|
||||||
const driveService = new GASDriveService()
|
const driveService = new GASDriveService()
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ export function getUploadUrl(sku: string, filename: string, mimeType: string) {
|
|||||||
const folder = driveService.getOrCreateFolder(sku, config.productPhotosFolderId)
|
const folder = driveService.getOrCreateFolder(sku, config.productPhotosFolderId)
|
||||||
|
|
||||||
// Generate Ticket
|
// 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)
|
// Deprecated (but kept for fallback/legacy small files if needed)
|
||||||
|
|||||||
@ -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();
|
const token = ScriptApp.getOAuthToken();
|
||||||
|
|
||||||
// Metadata for the file to be created
|
// Metadata for the file to be created
|
||||||
const metadata = {
|
const metadata: any = {
|
||||||
name: filename,
|
name: filename,
|
||||||
mimeType: mimeType,
|
mimeType: mimeType,
|
||||||
parents: [folderId]
|
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 = {
|
const params = {
|
||||||
method: 'post' as const,
|
method: 'post' as const,
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
|
|||||||
Reference in New Issue
Block a user