diff --git a/src/MediaManager.html b/src/MediaManager.html
index 6c534a9..96d7905 100644
--- a/src/MediaManager.html
+++ b/src/MediaManager.html
@@ -153,6 +153,7 @@
}
.media-overlay .icon-btn {
+ padding: 4px;
pointer-events: auto;
}
@@ -160,6 +161,32 @@
opacity: 1;
}
+ /* Processing State */
+ .media-item.processing-card {
+ background-color: #334155 !important;
+ /* Slate-700 */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .media-item.processing-card .media-content {
+ display: none !important;
+ }
+
+ .processing-icon {
+ font-size: 40px;
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 5;
+ transition: transform 0.6s ease-in-out;
+ }
+
+ /* .flipping removed, handled by JS inline style */
+
.type-badge {
position: absolute;
top: 6px;
@@ -364,7 +391,7 @@
...
-
+
@@ -464,7 +491,8 @@
×
-
+
@@ -542,64 +570,64 @@
/**
* State Management & Error Handling
*/
- window.onerror = function (msg, url, line) {
- alert("Script Error: " + msg + "\nLine: " + line);
- };
+ window.onerror = function (msg, url, line) {
+ alert("Script Error: " + msg + "\nLine: " + line);
+ };
- // --- ES5 Refactor: MediaState ---
- function MediaState() {
- this.sku = null;
+ // --- ES5 Refactor: MediaState ---
+ function MediaState() {
+ this.sku = null;
this.token = null;
this.items = [];
this.initialState = [];
}
- MediaState.prototype.setSku = function (info) {
- this.sku = info ? info.sku : null;
- this.title = info ? info.title : "";
- this.items = [];
- this.initialState = [];
- ui.updateSku(this.sku, this.title);
+ MediaState.prototype.setSku = function (info) {
+ this.sku = info ? info.sku : null;
+ this.title = info ? info.title : "";
+ this.items = [];
+ this.initialState = [];
+ ui.updateSku(this.sku, this.title);
};
- MediaState.prototype.setItems = function (items) {
- this.items = items || [];
+ MediaState.prototype.setItems = function (items) {
+ this.items = items || [];
this.initialState = JSON.parse(JSON.stringify(this.items));
- ui.render(this.items);
- this.checkDirty();
+ ui.render(this.items);
+ this.checkDirty();
};
- MediaState.prototype.addItem = function (item) {
- this.items.push(item);
- ui.render(this.items);
- this.checkDirty();
+ MediaState.prototype.addItem = function (item) {
+ this.items.push(item);
+ ui.render(this.items);
+ this.checkDirty();
};
- MediaState.prototype.deleteItem = function (index) {
- var item = this.items[index];
+ MediaState.prototype.deleteItem = function (index) {
+ var item = this.items[index];
if (item.source === 'new') {
- this.items.splice(index, 1);
- } else {
- item._deleted = !item._deleted;
- }
- ui.render(this.items);
- this.checkDirty();
+ this.items.splice(index, 1);
+ } else {
+ item._deleted = !item._deleted;
+ }
+ ui.render(this.items);
+ this.checkDirty();
};
- MediaState.prototype.reorderItems = function (newIndices) {
- // Handled by Sortable
- };
-
- MediaState.prototype.checkDirty = function () {
- var plan = this.calculateDiff();
- var isDirty = plan.hasChanges;
- ui.toggleSave(isDirty);
- return plan;
+ MediaState.prototype.reorderItems = function (newIndices) {
+ // Handled by Sortable
};
- MediaState.prototype.calculateDiff = function () {
- var currentIds = new Set(this.items.map(function (i) { return i.id; }));
- var initialIds = new Set(this.initialState.map(function (i) { return i.id; }));
+ MediaState.prototype.checkDirty = function () {
+ var plan = this.calculateDiff();
+ var isDirty = plan.hasChanges;
+ ui.toggleSave(isDirty);
+ return plan;
+ };
+
+ MediaState.prototype.calculateDiff = function () {
+ var currentIds = new Set(this.items.map(function (i) { return i.id; }));
+ var initialIds = new Set(this.initialState.map(function (i) { return i.id; }));
var actions = [];
@@ -638,108 +666,108 @@
// If lengths differ despite logic, assume change or weird state
}
- if (orderChanged) {
- actions.push({ type: 'reorder', name: 'Reorder Gallery' });
- }
+ if (orderChanged) {
+ actions.push({ type: 'reorder', name: 'Reorder Gallery' });
+ }
var uniqueActions = actions.filter(function (v, i, a) {
return a.findIndex(function (t) { return t.type === v.type && t.name === v.name; }) === i;
});
- return {
- hasChanges: uniqueActions.length > 0,
- actions: uniqueActions
- };
+ return {
+ hasChanges: uniqueActions.length > 0,
+ actions: uniqueActions
+ };
};
- MediaState.prototype.hasNewItems = function () {
- return this.items.some(function (i) {
- return !i._deleted && (i.status === 'drive_only' || i.source === 'new');
- });
- };
+ MediaState.prototype.hasNewItems = function () {
+ return this.items.some(function (i) {
+ return !i._deleted && (i.status === 'drive_only' || i.source === 'new');
+ });
+ };
- var state = new MediaState();
- window.state = state;
+ var state = new MediaState();
+ window.state = state;
- // --- ES5 Refactor: UI ---
- function UI() {
- this.grid = document.getElementById('media-grid');
- this.saveBtn = document.getElementById('save-btn');
- this.toggleLogBtn = document.getElementById('toggle-log-btn');
- this.logContainer = document.getElementById('status-log-container');
- this.linksContainer = document.getElementById('quick-links');
- this.sortable = null;
- this.driveUrl = null;
- this.shopifyUrl = null;
- }
+ // --- ES5 Refactor: UI ---
+ function UI() {
+ this.grid = document.getElementById('media-grid');
+ this.saveBtn = document.getElementById('save-btn');
+ this.toggleLogBtn = document.getElementById('toggle-log-btn');
+ this.logContainer = document.getElementById('status-log-container');
+ this.linksContainer = document.getElementById('quick-links');
+ this.sortable = null;
+ this.driveUrl = null;
+ this.shopifyUrl = null;
+ }
- UI.prototype.setDriveLink = function (url) { this.driveUrl = url; this.renderLinks(); };
- UI.prototype.setShopifyLink = function (url) { this.shopifyUrl = url; this.renderLinks(); };
+ UI.prototype.setDriveLink = function (url) { this.driveUrl = url; this.renderLinks(); };
+ UI.prototype.setShopifyLink = function (url) { this.shopifyUrl = url; this.renderLinks(); };
- UI.prototype.renderLinks = function () {
- this.linksContainer.innerHTML = '';
+ UI.prototype.renderLinks = function () {
+ this.linksContainer.innerHTML = '';
if (this.driveUrl) this.linksContainer.innerHTML += 'Drive ↗ ';
if (this.shopifyUrl) this.linksContainer.innerHTML += 'Shopify ↗ ';
};
- UI.prototype.toggleLog = function (forceState) {
- var isVisible = typeof forceState === 'boolean' ? !forceState : this.logContainer.style.display !== 'none';
- this.logContainer.style.display = isVisible ? 'none' : 'block';
- this.toggleLogBtn.innerText = isVisible ? "View Log" : "Hide Log";
+ UI.prototype.toggleLog = function (forceState) {
+ var isVisible = typeof forceState === 'boolean' ? !forceState : this.logContainer.style.display !== 'none';
+ this.logContainer.style.display = isVisible ? 'none' : 'block';
+ this.toggleLogBtn.innerText = isVisible ? "View Log" : "Hide Log";
};
- UI.prototype.updateSku = function (sku, title) {
- document.getElementById('current-sku').innerText = sku || '...';
- document.getElementById('current-title').innerText = title || '';
- document.getElementById('loading-ui').style.display = 'none';
- document.getElementById('main-ui').style.display = 'block';
+ UI.prototype.updateSku = function (sku, title) {
+ document.getElementById('current-sku').innerText = sku || '...';
+ document.getElementById('current-title').innerText = title || '';
+ document.getElementById('loading-ui').style.display = 'none';
+ document.getElementById('main-ui').style.display = 'block';
};
- UI.prototype.toggleSave = function (enable) {
- this.saveBtn.disabled = !enable;
- this.saveBtn.innerText = enable ? "Save Changes" : "No Changes";
+ UI.prototype.toggleSave = function (enable) {
+ this.saveBtn.disabled = !enable;
+ this.saveBtn.innerText = enable ? "Save Changes" : "No Changes";
};
- UI.prototype.showPhotoSession = function (url) {
- var uiEl = document.getElementById('photos-session-ui');
- var link = document.getElementById('photos-session-link');
- var status = document.getElementById('photos-session-status');
+ UI.prototype.showPhotoSession = function (url) {
+ var uiEl = document.getElementById('photos-session-ui');
+ var link = document.getElementById('photos-session-link');
+ var status = document.getElementById('photos-session-status');
- uiEl.style.display = 'block';
- link.href = url;
- // We also open it automatically in a popup
- const width = 1200;
- const height = 800;
- const left = (screen.width - width) / 2;
- const top = (screen.height - height) / 2;
+ uiEl.style.display = 'block';
+ link.href = url;
+ // We also open it automatically in a popup
+ const width = 1200;
+ const height = 800;
+ const left = (screen.width - width) / 2;
+ const top = (screen.height - height) / 2;
- // Attempt popup
- const popup = window.open(url, 'googlePhotos', `width=${width},height=${height},top=${top},left=${left}`);
+ // Attempt popup
+ const popup = window.open(url, 'googlePhotos', `width=${width},height=${height},top=${top},left=${left}`);
- if (popup) {
- link.innerText = "Re-open Popup ↗";
- link.onclick = function (e) {
- e.preventDefault();
- window.open(url, 'googlePhotos', `width=${width},height=${height},top=${top},left=${left}`);
- }
- } else {
- link.innerText = "Open Google Photos ↗";
- link.onclick = null; // Default href behavior
- }
+ if (popup) {
+ link.innerText = "Re-open Popup ↗";
+ link.onclick = function (e) {
+ e.preventDefault();
+ window.open(url, 'googlePhotos', `width=${width},height=${height},top=${top},left=${left}`);
+ }
+ } else {
+ link.innerText = "Open Google Photos ↗";
+ link.onclick = null; // Default href behavior
+ }
- status.innerText = "Waiting for selection in popup...";
- };
+ status.innerText = "Waiting for selection in popup...";
+ };
- UI.prototype.closePhotoSession = function () {
- document.getElementById('photos-session-ui').style.display = 'none';
- };
+ UI.prototype.closePhotoSession = function () {
+ document.getElementById('photos-session-ui').style.display = 'none';
+ };
- UI.prototype.updatePhotoStatus = function (msg) {
- document.getElementById('photos-session-status').innerText = msg;
- };
+ UI.prototype.updatePhotoStatus = function (msg) {
+ document.getElementById('photos-session-status').innerText = msg;
+ };
- UI.prototype.render = function (items) {
- this.grid.innerHTML = '';
+ UI.prototype.render = function (items) {
+ this.grid.innerHTML = '';
var _this = this; // Capture 'this' for callbacks
var activeCount = items.filter(function (i) { return !i._deleted; }).length;
document.getElementById('item-count').innerText = '(' + activeCount + ')';
@@ -771,28 +799,33 @@
});
};
- UI.prototype.setLoadingState = function (isLoading) {
- if (isLoading) {
- this.grid.innerHTML = '' +
- '
' +
- '
Connecting to systems...
';
- }
+ UI.prototype.setLoadingState = function (isLoading) {
+ if (isLoading) {
+ this.grid.innerHTML = '' +
+ '
' +
+ '
Connecting to systems...
';
+ }
};
- UI.prototype.logStatus = function (step, message, type) {
- if (!type) type = 'info';
- var container = this.logContainer;
- var icon = type === 'success' ? '✅' : type === 'error' ? '❌' : '⏳';
- var el = document.createElement('div');
- el.innerHTML = '' + icon + ' ' + message;
- if (type === 'error') el.style.color = 'var(--error)';
- container.appendChild(el);
+ UI.prototype.logStatus = function (step, message, type) {
+ if (!type) type = 'info';
+ var container = this.logContainer;
+ var icon = type === 'success' ? '✅' : type === 'error' ? '❌' : '⏳';
+ var el = document.createElement('div');
+ el.innerHTML = '' + icon + ' ' + message;
+ if (type === 'error') el.style.color = 'var(--error)';
+ container.appendChild(el);
};
- UI.prototype.createCard = function (item, index) {
- var div = document.createElement('div');
- div.className = 'media-item ' + (item._deleted ? 'deleted-item' : '');
- div.dataset.id = item.id;
+ UI.prototype.createCard = function (item, index) {
+ var div = document.createElement('div');
+ div.className = 'media-item ' + (item._deleted ? 'deleted-item' : '');
+ div.dataset.id = item.id;
+
+ // Processing Class
+ if (item.isProcessing) {
+ div.className += ' processing-card';
+ }
div.onmouseenter = function () {
var v = div.querySelector('video');
@@ -815,10 +848,16 @@
var isVideo = (item.mimeType && item.mimeType.startsWith('video/')) || (item.filename && item.filename.match(/\.(mp4|mov|webm)$/i));
if (isVideo) console.log("[MediaManager] Video Detected: " + item.filename);
- var videoBadgeIcon = isVideo ? '🎞️
' : '';
+ var videoBadgeIcon = isVideo ? '🎞️
' : '';
+ // Processing Badge REMOVED (Handled by center icon now)
- // content URL logic (Only relevant for Shopify where we have a direct public link)
- var contentUrl = item.contentUrl || "";
+ var centerIcon = '';
+ if (item.isProcessing) {
+ centerIcon = '⏳
';
+ }
+
+ // content URL logic (Only relevant for Shopify where we have a direct public link)
+ var contentUrl = item.contentUrl || "";
var actionBtn = item._deleted
? '↩️ '
@@ -827,25 +866,26 @@
div.innerHTML =
badge +
videoBadgeIcon +
+ centerIcon +
'' +
'👁️ ' +
actionBtn +
'
';
- // Create Media Element
- // RULE: Only create for Shopify-hosted videos (public).
- // Drive videos use static thumbnail + Iframe Preview.
- var mediaEl;
- // Allow Shopify-only OR Synced items with valid contentUrl (Shopify Video URL) to use tag
- if (isVideo && (item.source === 'shopify_only' || item.source === 'synced') && contentUrl) {
- mediaEl = document.createElement('video');
- mediaEl.src = contentUrl;
- mediaEl.poster = item.thumbnail || "";
- mediaEl.muted = true;
- mediaEl.loop = true;
- mediaEl.style.objectFit = 'cover';
+ // Create Media Element
+ // RULE: Only create for Shopify-hosted videos (public).
+ // Drive videos use static thumbnail + Iframe Preview.
+ var mediaEl;
+ // Allow Shopify-only OR Synced items with valid contentUrl (Shopify Video URL) to use tag
+ if (isVideo && (item.source === 'shopify_only' || item.source === 'synced') && contentUrl) {
+ mediaEl = document.createElement('video');
+ mediaEl.src = contentUrl;
+ mediaEl.poster = item.thumbnail || "";
+ mediaEl.muted = true;
+ mediaEl.loop = true;
+ mediaEl.style.objectFit = 'cover';
} else {
- // Static Image for Drive videos or regular images
+ // Static Image for Drive videos or regular images
mediaEl = document.createElement('img');
mediaEl.src = item.thumbnail || "";
mediaEl.loading = "lazy";
@@ -858,9 +898,9 @@
return div;
};
- UI.prototype.openPreview = function (id) {
- var item = state.items.find(function (i) { return i.id === id; });
- if (!item) return;
+ UI.prototype.openPreview = function (id) {
+ var item = state.items.find(function (i) { return i.id === id; });
+ if (!item) return;
var modal = document.getElementById('preview-modal');
var img = document.getElementById('preview-image');
@@ -900,16 +940,16 @@
modal.style.display = 'flex';
};
- UI.prototype.closeModal = function (e) {
- if (e && e.target !== document.getElementById('preview-modal') && e.target !== document.querySelector('.modal-close')) return;
- document.getElementById('preview-modal').style.display = 'none';
- document.getElementById('preview-video').pause();
+ UI.prototype.closeModal = function (e) {
+ if (e && e.target !== document.getElementById('preview-modal') && e.target !== document.querySelector('.modal-close')) return;
+ document.getElementById('preview-modal').style.display = 'none';
+ document.getElementById('preview-video').pause();
document.getElementById('preview-iframe').src = 'about:blank';
};
- UI.prototype.showDetails = function () {
- var plan = state.calculateDiff();
- var container = document.getElementById('details-content');
+ UI.prototype.showDetails = function () {
+ var plan = state.calculateDiff();
+ var container = document.getElementById('details-content');
if (plan.actions.length === 0) {
container.innerHTML = 'No pending changes.
';
@@ -935,96 +975,96 @@
document.getElementById('details-modal').style.display = 'flex';
};
- UI.prototype.closeDetails = function (e) {
- if (e && e.target !== document.getElementById('details-modal') && !e.target.matches('.modal-close, .btn-secondary, .close-btn')) return;
- document.getElementById('details-modal').style.display = 'none';
+ UI.prototype.closeDetails = function (e) {
+ if (e && e.target !== document.getElementById('details-modal') && !e.target.matches('.modal-close, .btn-secondary, .close-btn')) return;
+ document.getElementById('details-modal').style.display = 'none';
};
- var ui = new UI();
- window.ui = ui;
+ var ui = new UI();
+ window.ui = ui;
- /**
- * Data Controller
- */
- var controller = {
- init() {
- // Start polling for SKU selection
- setInterval(() => this.checkSku(), 2000);
- this.checkSku();
- },
+ /**
+ * Data Controller
+ */
+ var controller = {
+ init() {
+ // Start polling for SKU selection
+ setInterval(() => this.checkSku(), 2000);
+ this.checkSku();
+ },
- checkSku() {
- google.script.run
- .withSuccessHandler(info => {
- // Info is now { sku, title } or null
- const sku = info ? info.sku : null;
+ checkSku() {
+ google.script.run
+ .withSuccessHandler(info => {
+ // Info is now { sku, title } or null
+ const sku = info ? info.sku : null;
- if (sku && sku !== state.sku) {
- state.setSku(info); // Pass whole object
+ if (sku && sku !== state.sku) {
+ state.setSku(info); // Pass whole object
+ this.loadMedia();
+ } else if (!sku && !state.sku) {
+ // If we don't have a SKU and haven't shown error yet
+ if (document.getElementById('error-ui').style.display !== 'flex') {
this.loadMedia();
- } else if (!sku && !state.sku) {
- // If we don't have a SKU and haven't shown error yet
- if (document.getElementById('error-ui').style.display !== 'flex') {
- this.loadMedia();
- }
}
- })
- .getSelectedProductInfo();
- },
+ }
+ })
+ .getSelectedProductInfo();
+ },
- loadMedia(preserveLogs = false) {
- // Resolve SKU/Title - prefer state, fallback to DOM
- let sku = state.sku;
- let title = state.title;
+ loadMedia(preserveLogs = false) {
+ // Resolve SKU/Title - prefer state, fallback to DOM
+ let sku = state.sku;
+ let title = state.title;
- if (!sku) {
- const domSku = document.getElementById('current-sku').innerText;
- if (domSku && domSku !== '...') sku = domSku;
- }
+ if (!sku) {
+ const domSku = document.getElementById('current-sku').innerText;
+ if (domSku && domSku !== '...') sku = domSku;
+ }
- // CHECK FOR MISSING SKU
- if (!sku || sku === '...') {
- console.warn("No SKU found. Showing error.");
- document.getElementById('loading-ui').style.display = 'none';
- document.getElementById('main-ui').style.display = 'none';
- document.getElementById('error-ui').style.display = 'flex';
- return;
- }
-
- if (!title) {
- const domTitle = document.getElementById('current-title').innerText;
- if (domTitle && domTitle !== 'Loading...') title = domTitle;
- }
-
- // Show Main UI immediately so logs are visible
+ // CHECK FOR MISSING SKU
+ if (!sku || sku === '...') {
+ console.warn("No SKU found. Showing error.");
document.getElementById('loading-ui').style.display = 'none';
- document.getElementById('main-ui').style.display = 'block';
+ document.getElementById('main-ui').style.display = 'none';
+ document.getElementById('error-ui').style.display = 'flex';
+ return;
+ }
- // Set Inline Loading State
- ui.setLoadingState(true);
+ if (!title) {
+ const domTitle = document.getElementById('current-title').innerText;
+ if (domTitle && domTitle !== 'Loading...') title = domTitle;
+ }
- // Reset State (this calls ui.updateSku)
- state.setSku({ sku, title });
+ // Show Main UI immediately so logs are visible
+ document.getElementById('loading-ui').style.display = 'none';
+ document.getElementById('main-ui').style.display = 'block';
- if (!preserveLogs) {
- document.getElementById('status-log-container').innerHTML = '';
- }
- ui.toggleLogBtn.style.display = 'inline-block';
- ui.toggleLog(true); // Force Show Log to see progress
+ // Set Inline Loading State
+ ui.setLoadingState(true);
- // 1. Run Diagnostics
- // 1. Run Diagnostics
- ui.logStatus('init', 'Initializing access...', 'info');
+ // Reset State (this calls ui.updateSku)
+ state.setSku({ sku, title });
- google.script.run
- .withSuccessHandler(function (diagnostics) {
- // Drive Status
- if (diagnostics.drive.status === 'ok') {
- ui.logStatus('drive', `Drive Folder: ok (${diagnostics.drive.fileCount} files) Open Folder ↗ `, 'success');
- ui.setDriveLink(diagnostics.drive.folderUrl);
- } else {
- ui.logStatus('drive', `Drive Check Failed: ${diagnostics.drive.error}`, 'error');
- }
+ if (!preserveLogs) {
+ document.getElementById('status-log-container').innerHTML = '';
+ }
+ ui.toggleLogBtn.style.display = 'inline-block';
+ ui.toggleLog(true); // Force Show Log to see progress
+
+ // 1. Run Diagnostics
+ // 1. Run Diagnostics
+ ui.logStatus('init', 'Initializing access...', 'info');
+
+ google.script.run
+ .withSuccessHandler(function (diagnostics) {
+ // Drive Status
+ if (diagnostics.drive.status === 'ok') {
+ ui.logStatus('drive', `Drive Folder: ok (${diagnostics.drive.fileCount} files) Open Folder ↗ `, 'success');
+ ui.setDriveLink(diagnostics.drive.folderUrl);
+ } else {
+ ui.logStatus('drive', `Drive Check Failed: ${diagnostics.drive.error}`, 'error');
+ }
// Capture Token
if (diagnostics.token) state.token = diagnostics.token;
@@ -1069,25 +1109,25 @@
.getMediaForSku(sku);
})
- .withFailureHandler(function (err) {
- ui.logStatus('fatal', `Diagnostics failed: ${err.message}`, 'error');
- })
- .getMediaDiagnostics(sku, "");
- },
+ .withFailureHandler(function (err) {
+ ui.logStatus('fatal', `Diagnostics failed: ${err.message}`, 'error');
+ })
+ .getMediaDiagnostics(sku, "");
+ },
- saveChanges() {
- ui.toggleSave(false);
- ui.saveBtn.innerText = "Saving...";
+ saveChanges() {
+ ui.toggleSave(false);
+ ui.saveBtn.innerText = "Saving...";
- ui.saveBtn.innerText = "Saving...";
+ ui.saveBtn.innerText = "Saving...";
- // Filter out deleted items so they are actually removed
- const activeItems = state.items.filter(i => !i._deleted);
+ // Filter out deleted items so they are actually removed
+ const activeItems = state.items.filter(i => !i._deleted);
- // Send final state array to backend
- google.script.run
- .withSuccessHandler((logs) => {
- ui.saveBtn.innerText = "Saved!";
+ // Send final state array to backend
+ google.script.run
+ .withSuccessHandler((logs) => {
+ ui.saveBtn.innerText = "Saved!";
// Verify logs is an array (backward compatibility check)
if (Array.isArray(logs)) {
@@ -1102,127 +1142,127 @@
// Reload to get fresh IDs/State, preserving the save logs
setTimeout(() => this.loadMedia(true), 1500);
})
- .withFailureHandler(e => {
- alert(`Save Failed: ${e.message}`);
- ui.toggleSave(true);
- })
- .saveMediaChanges(state.sku, activeItems);
- },
+ .withFailureHandler(e => {
+ alert(`Save Failed: ${e.message}`);
+ ui.toggleSave(true);
+ })
+ .saveMediaChanges(state.sku, activeItems);
+ },
- handleFiles(fileList) {
- Array.from(fileList).forEach(file => {
- const reader = new FileReader();
- reader.onload = (e) => {
- const data = e.target.result.split(',')[1]; // Base64
+ handleFiles(fileList) {
+ Array.from(fileList).forEach(file => {
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const data = e.target.result.split(',')[1]; // Base64
- google.script.run
- .withSuccessHandler(() => {
- this.loadMedia();
- })
- .saveFileToDrive(state.sku, file.name, file.type, data);
- };
- reader.readAsDataURL(file);
- });
- },
-
- // --- Picker ---
- // --- Picker ---
- openPicker() {
- if (!pickerApiLoaded) return alert("API Loading...");
- google.script.run.withSuccessHandler(c => createPicker(c)).getPickerConfig();
- },
-
- importFromPicker(fileId, mime, name, url) {
google.script.run
- .withSuccessHandler(() => this.loadMedia())
- .importFromPicker(state.sku, fileId, mime, name, url);
- },
-
- // --- Photos (Popup Flow) ---
- startPhotoSession() {
- ui.updatePhotoStatus("Starting session...");
- google.script.run
- .withSuccessHandler(session => {
- ui.showPhotoSession(session.pickerUri);
- this.pollPhotoSession(session.id);
+ .withSuccessHandler(() => {
+ this.loadMedia();
})
- .createPhotoSession();
- },
+ .saveFileToDrive(state.sku, file.name, file.type, data);
+ };
+ reader.readAsDataURL(file);
+ });
+ },
- pollPhotoSession(sessionId) {
- let processing = false;
- const check = () => {
- if (processing) return;
- google.script.run
- .withSuccessHandler(res => {
- if (res.status === 'complete') {
- processing = true;
- ui.updatePhotoStatus("Importing photos...");
- controller.processPhotoItems(res.mediaItems);
- } else if (res.status === 'error') {
- ui.updatePhotoStatus("Error: " + res.message);
- } else {
- setTimeout(check, 2000);
- }
- })
- .checkPhotoSession(sessionId);
- };
- check();
- },
+ // --- Picker ---
+ // --- Picker ---
+ openPicker() {
+ if (!pickerApiLoaded) return alert("API Loading...");
+ google.script.run.withSuccessHandler(c => createPicker(c)).getPickerConfig();
+ },
- processPhotoItems(items) {
- let done = 0;
- items.forEach(item => {
- console.log("[MediaManager] Processing Item:", JSON.stringify(item));
+ importFromPicker(fileId, mime, name, url) {
+ google.script.run
+ .withSuccessHandler(() => this.loadMedia())
+ .importFromPicker(state.sku, fileId, mime, name, url);
+ },
- // The API returns nested 'mediaFile' object for actual file details
- const mediaFile = item.mediaFile || item;
+ // --- Photos (Popup Flow) ---
+ startPhotoSession() {
+ ui.updatePhotoStatus("Starting session...");
+ google.script.run
+ .withSuccessHandler(session => {
+ ui.showPhotoSession(session.pickerUri);
+ this.pollPhotoSession(session.id);
+ })
+ .createPhotoSession();
+ },
- const url = mediaFile.baseUrl || item.baseUrl;
- const filename = mediaFile.filename || item.filename;
- let mimeType = mediaFile.mimeType || item.mimeType;
-
- console.log(`[MediaManager] Extracted: URL=${url ? 'Yes' : 'No'}, Mime=${mimeType}, Name=${filename}`);
-
- // Force video mimeType if metadata indicates video (Critical for backend =dv param)
- if (item.mediaMetadata && item.mediaMetadata.video) {
- console.log("[MediaManager] Metadata indicates VIDEO. Forcing video/mp4.");
- mimeType = 'video/mp4';
+ pollPhotoSession(sessionId) {
+ let processing = false;
+ const check = () => {
+ if (processing) return;
+ google.script.run
+ .withSuccessHandler(res => {
+ if (res.status === 'complete') {
+ processing = true;
+ ui.updatePhotoStatus("Importing photos...");
+ controller.processPhotoItems(res.mediaItems);
+ } else if (res.status === 'error') {
+ ui.updatePhotoStatus("Error: " + res.message);
+ } else {
+ setTimeout(check, 2000);
}
+ })
+ .checkPhotoSession(sessionId);
+ };
+ check();
+ },
- google.script.run
- .withSuccessHandler(() => {
- done++;
- if (done === items.length) {
- ui.updatePhotoStatus("Done!");
- controller.loadMedia();
- setTimeout(() => ui.closePhotoSession(), 2000);
- }
- })
- .importFromPicker(state.sku, null, mimeType, filename, url);
- });
- },
+ processPhotoItems(items) {
+ let done = 0;
+ items.forEach(item => {
+ console.log("[MediaManager] Processing Item:", JSON.stringify(item));
- // --- Legacy Photos Session (Removed in favor of Embedded Picker) ---
- // startPhotoSession() { ... }
+ // The API returns nested 'mediaFile' object for actual file details
+ const mediaFile = item.mediaFile || item;
- // --- Compatibility / Matching Logic ---
+ const url = mediaFile.baseUrl || item.baseUrl;
+ const filename = mediaFile.filename || item.filename;
+ let mimeType = mediaFile.mimeType || item.mimeType;
- matches: [],
- currentMatchIndex: 0,
- hasRunMatching: false,
+ console.log(`[MediaManager] Extracted: URL=${url ? 'Yes' : 'No'}, Mime=${mimeType}, Name=${filename}`);
- checkMatches(items) {
- // Filter candidates
- var driveOnly = items.filter(function (i) { return i.status === 'drive_only'; });
- var shopifyOnly = items.filter(function (i) { return i.source === 'shopify_only'; }); // source check is safer for shopify items
+ // Force video mimeType if metadata indicates video (Critical for backend =dv param)
+ if (item.mediaMetadata && item.mediaMetadata.video) {
+ console.log("[MediaManager] Metadata indicates VIDEO. Forcing video/mp4.");
+ mimeType = 'video/mp4';
+ }
- var newMatches = [];
+ google.script.run
+ .withSuccessHandler(() => {
+ done++;
+ if (done === items.length) {
+ ui.updatePhotoStatus("Done!");
+ controller.loadMedia();
+ setTimeout(() => ui.closePhotoSession(), 2000);
+ }
+ })
+ .importFromPicker(state.sku, null, mimeType, filename, url);
+ });
+ },
- driveOnly.forEach(function (d) {
- // Find match by filename
- // Note: Backend might return "Orphaned Media" if extraction failed, ignore those.
- if (!d.filename || d.filename === 'Orphaned Media') return;
+ // --- Legacy Photos Session (Removed in favor of Embedded Picker) ---
+ // startPhotoSession() { ... }
+
+ // --- Compatibility / Matching Logic ---
+
+ matches: [],
+ currentMatchIndex: 0,
+ hasRunMatching: false,
+
+ checkMatches(items) {
+ // Filter candidates
+ var driveOnly = items.filter(function (i) { return i.status === 'drive_only'; });
+ var shopifyOnly = items.filter(function (i) { return i.source === 'shopify_only'; }); // source check is safer for shopify items
+
+ var newMatches = [];
+
+ driveOnly.forEach(function (d) {
+ // Find match by filename
+ // Note: Backend might return "Orphaned Media" if extraction failed, ignore those.
+ if (!d.filename || d.filename === 'Orphaned Media') return;
var match = shopifyOnly.find(function (s) {
return s.filename === d.filename; // Exact match
@@ -1233,63 +1273,63 @@
}
});
- if (newMatches.length > 0) {
- this.matches = newMatches;
- this.currentMatchIndex = 0;
- ui.logStatus('info', 'Found ' + newMatches.length + ' potential matches. Starting matching wizard...', 'info');
- this.startMatching();
- } else {
- // No matches, show UI
- this.showGallery();
- }
- },
+ if (newMatches.length > 0) {
+ this.matches = newMatches;
+ this.currentMatchIndex = 0;
+ ui.logStatus('info', 'Found ' + newMatches.length + ' potential matches. Starting matching wizard...', 'info');
+ this.startMatching();
+ } else {
+ // No matches, show UI
+ this.showGallery();
+ }
+ },
- startMatching() {
- document.getElementById('loading-ui').style.display = 'none';
- document.getElementById('main-ui').style.display = 'none';
- document.getElementById('matching-modal').style.display = 'flex';
- this.renderMatch();
- },
+ startMatching() {
+ document.getElementById('loading-ui').style.display = 'none';
+ document.getElementById('main-ui').style.display = 'none';
+ document.getElementById('matching-modal').style.display = 'flex';
+ this.renderMatch();
+ },
- renderMatch() {
- var match = this.matches[this.currentMatchIndex];
+ renderMatch() {
+ var match = this.matches[this.currentMatchIndex];
- // Reset Buttons
- var btnConfirm = document.getElementById('btn-match-confirm');
- var btnSkip = document.getElementById('btn-match-skip');
- if (btnConfirm) {
- btnConfirm.disabled = false;
- btnConfirm.innerText = "Yes, Link";
- }
- if (btnSkip) {
- btnSkip.disabled = false;
- btnSkip.innerText = "No, Skip";
- }
+ // Reset Buttons
+ var btnConfirm = document.getElementById('btn-match-confirm');
+ var btnSkip = document.getElementById('btn-match-skip');
+ if (btnConfirm) {
+ btnConfirm.disabled = false;
+ btnConfirm.innerText = "Yes, Link";
+ }
+ if (btnSkip) {
+ btnSkip.disabled = false;
+ btnSkip.innerText = "No, Skip";
+ }
- var dImg = document.getElementById('match-drive-img');
- var sImg = document.getElementById('match-shopify-img');
+ var dImg = document.getElementById('match-drive-img');
+ var sImg = document.getElementById('match-shopify-img');
- // Reset visual state safely
- dImg.style.transition = 'none';
- dImg.style.opacity = '0';
- sImg.style.transition = 'none';
- sImg.style.opacity = '0';
+ // Reset visual state safely
+ dImg.style.transition = 'none';
+ dImg.style.opacity = '0';
+ sImg.style.transition = 'none';
+ sImg.style.opacity = '0';
- // Clear source to blank pixel to ensure old image is gone
- var blank = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
- dImg.src = blank;
- sImg.src = blank;
+ // Clear source to blank pixel to ensure old image is gone
+ var blank = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
+ dImg.src = blank;
+ sImg.src = blank;
- document.getElementById('match-drive-name').innerText = match.drive.filename;
- document.getElementById('match-shopify-name').innerText = match.shopify.filename;
+ document.getElementById('match-drive-name').innerText = match.drive.filename;
+ document.getElementById('match-shopify-name').innerText = match.shopify.filename;
- document.getElementById('match-index').innerText = this.currentMatchIndex + 1;
- document.getElementById('match-total').innerText = this.matches.length;
+ document.getElementById('match-index').innerText = this.currentMatchIndex + 1;
+ document.getElementById('match-total').innerText = this.matches.length;
- // Load new images
- setTimeout(function () {
- dImg.style.transition = 'opacity 0.3s ease';
- sImg.style.transition = 'opacity 0.3s ease';
+ // Load new images
+ setTimeout(function () {
+ dImg.style.transition = 'opacity 0.3s ease';
+ sImg.style.transition = 'opacity 0.3s ease';
dImg.onload = function () { dImg.style.opacity = '1'; };
sImg.onload = function () { sImg.style.opacity = '1'; };
@@ -1297,141 +1337,240 @@
dImg.src = match.drive.thumbnail;
sImg.src = match.shopify.thumbnail;
}, 50);
- },
+ },
- confirmLink() {
- var match = this.matches[this.currentMatchIndex];
- var _this = this;
+ confirmLink() {
+ var match = this.matches[this.currentMatchIndex];
+ var _this = this;
- document.getElementById('btn-match-confirm').disabled = true;
- document.getElementById('btn-match-confirm').innerText = "Linking...";
- document.getElementById('btn-match-skip').disabled = true;
+ document.getElementById('btn-match-confirm').disabled = true;
+ document.getElementById('btn-match-confirm').innerText = "Linking...";
+ document.getElementById('btn-match-skip').disabled = true;
- // ui.logStatus('link', 'Linking ' + match.drive.filename + '...', 'info');
+ // ui.logStatus('link', 'Linking ' + match.drive.filename + '...', 'info');
- google.script.run
- .withSuccessHandler(function () {
- // ui.logStatus('link', 'Linked ' + match.drive.filename, 'success');
- _this.nextMatch();
- })
- .withFailureHandler(function (e) {
- alert("Failed to link: " + e.message);
- document.getElementById('btn-match-confirm').disabled = false;
- document.getElementById('btn-match-confirm').innerText = "Yes, Link";
- document.getElementById('btn-match-skip').disabled = false;
- })
- .linkDriveFileToShopifyMedia(state.sku, match.drive.id, match.shopify.id);
- },
+ google.script.run
+ .withSuccessHandler(function () {
+ // ui.logStatus('link', 'Linked ' + match.drive.filename, 'success');
+ _this.nextMatch();
+ })
+ .withFailureHandler(function (e) {
+ alert("Failed to link: " + e.message);
+ document.getElementById('btn-match-confirm').disabled = false;
+ document.getElementById('btn-match-confirm').innerText = "Yes, Link";
+ document.getElementById('btn-match-skip').disabled = false;
+ })
+ .linkDriveFileToShopifyMedia(state.sku, match.drive.id, match.shopify.id);
+ },
- skipLink() {
- document.getElementById('btn-match-skip').innerText = "Skipping...";
- document.getElementById('btn-match-skip').disabled = true;
- document.getElementById('btn-match-confirm').disabled = true;
- setTimeout(() => this.nextMatch(), 200);
- },
+ skipLink() {
+ document.getElementById('btn-match-skip').innerText = "Skipping...";
+ document.getElementById('btn-match-skip').disabled = true;
+ document.getElementById('btn-match-confirm').disabled = true;
+ setTimeout(() => this.nextMatch(), 200);
+ },
- nextMatch() {
- this.currentMatchIndex++;
- if (this.currentMatchIndex < this.matches.length) {
- this.renderMatch();
- } else {
- // Done
- document.getElementById('matching-modal').style.display = 'none';
- ui.logStatus('info', 'Matching complete. Reloading...', 'info');
- document.getElementById('loading-ui').style.display = 'block';
- // Reload to get fresh state. Since hasRunMatching is true, it shouldn't trigger again.
- this.loadMedia(true);
+ nextMatch() {
+ this.currentMatchIndex++;
+ if (this.currentMatchIndex < this.matches.length) {
+ this.renderMatch();
+ } else {
+ // Done
+ document.getElementById('matching-modal').style.display = 'none';
+ ui.logStatus('info', 'Matching complete. Reloading...', 'info');
+ document.getElementById('loading-ui').style.display = 'block';
+ // Reload to get fresh state. Since hasRunMatching is true, it shouldn't trigger again.
+ this.loadMedia(true);
+ }
+ },
+
+ // Sand Animation (Global Loop)
+ sandInterval: null,
+ startSandAnimation() {
+ if (this.sandInterval) return;
+ this.sandInterval = setInterval(() => {
+ const icons = document.querySelectorAll('.processing-icon');
+ icons.forEach(icon => {
+ // Only swap if NOT currently flipping to avoid visual glitch
+ if (!icon.classList.contains('flipping')) {
+ icon.innerText = icon.innerText === '⏳' ? '⌛' : '⏳';
}
- },
+ });
+ }, 1000);
+ },
- showGallery() {
- document.getElementById('loading-ui').style.display = 'none';
- document.getElementById('main-ui').style.display = 'block';
- ui.logStatus('done', 'Finished loading.', 'success');
- setTimeout(function () { ui.toggleLog(false); }, 1000);
+
+ showGallery() {
+ document.getElementById('loading-ui').style.display = 'none';
+ document.getElementById('main-ui').style.display = 'block';
+ ui.logStatus('done', 'Finished loading.', 'success');
+ setTimeout(function () { ui.toggleLog(false); }, 1000);
+
+ // Start Polling for Processing Items
+ this.pollProcessingItems();
+ },
+
+ pollInterval: null,
+ pollProcessingItems() {
+ if (this.pollInterval) clearInterval(this.pollInterval);
+
+ const hasProcessing = state.items.some(function (i) { return i.isProcessing; });
+ if (!hasProcessing) return;
+
+ console.log("[MediaManager] Items are processing. Starting poll...");
+
+ // Ensure sand animation is running
+ this.startSandAnimation();
+
+ var _this = this;
+ this.pollInterval = setInterval(function () {
+ var processingItems = state.items.filter(function (i) { return i.isProcessing; });
+ if (processingItems.length === 0) {
+ clearInterval(_this.pollInterval);
+ return;
}
- };
- // --- Google Picker API ---
- let pickerApiLoaded = false;
- window.onApiLoad = function () { gapi.load('picker', () => { pickerApiLoaded = true; }); };
+ // Visual Trigger: Rotate 180deg CW (Cumulative)
+ const icons = document.querySelectorAll('.processing-icon');
+ icons.forEach(el => {
+ let currentRot = parseInt(el.dataset.rotation || '0');
+ currentRot += 180;
+ el.style.transform = 'rotate(' + currentRot + 'deg)';
+ el.dataset.rotation = currentRot;
+ });
+ // No timeout needed, we stay at new rotation
- function createPicker(config) {
- const view = new google.picker.DocsView(google.picker.ViewId.DOCS)
- .setMimeTypes("image/png,image/jpeg,image/jpg,video/mp4")
- .setIncludeFolders(true)
- .setSelectFolderEnabled(false);
+ // Poll backend silently
+ google.script.run
+ .withSuccessHandler(function (items) {
+ // Update items relative to current state
+ // We only want to update the 'isProcessing' status and thumbnail of existing items
+ // to avoid jarring re-renders or losing unsaved reordering.
+ let changed = false;
- const builder = new google.picker.PickerBuilder();
+ items.forEach(function (newItem) {
+ // Find existing
+ var idx = state.items.findIndex(function (cur) { return cur.id === newItem.id || (newItem.source === 'drive_only' && cur.driveId === newItem.id); });
+ // Note: backend 'id' is driveId for drive items.
- builder.addView(view)
- .setOAuthToken(config.token)
- .setDeveloperKey(config.apiKey)
- .setOrigin(window.location.protocol + '//' + window.location.host)
- .setCallback(data => {
- if (data.action == google.picker.Action.PICKED) {
- const doc = data.docs[0];
+ if (idx !== -1) {
+ var item = state.items[idx];
+ if (item.isProcessing) {
+ // Check if it's done now
+ // The backend logic for 'isProcessing' in getUnifiedMediaState checks if getThumbnail fails.
+ // If it succeeds now, isProcessing will be false (undefined/false).
+ // Update our local item
+ // CAUTION: The normalized structure in loadMedia sets defaults.
+ // We need to match that.
+
+ const stillProcessing = newItem.isProcessing === true;
+
+ if (!stillProcessing) {
+ console.log("[MediaManager] Processing complete for " + item.filename);
+ item.isProcessing = false;
+ item.thumbnail = newItem.thumbnail;
+ changed = true;
+ }
+ }
+ }
+ });
+
+ if (changed) {
+ ui.render(state.items);
+
+ // If none left, stop
+ if (!state.items.some(function (i) { return i.isProcessing; })) {
+ clearInterval(_this.pollInterval);
+ console.log("[MediaManager] All processing complete. Stopping poll.");
+ }
+ }
+ })
+ .getMediaForSku(state.sku);
+
+ }, 15000); // 15 seconds
+ }
+ };
+
+ // --- Google Picker API ---
+ let pickerApiLoaded = false;
+ window.onApiLoad = function () { gapi.load('picker', () => { pickerApiLoaded = true; }); };
+
+ function createPicker(config) {
+ const view = new google.picker.DocsView(google.picker.ViewId.DOCS)
+ .setMimeTypes("image/png,image/jpeg,image/jpg,video/mp4")
+ .setIncludeFolders(true)
+ .setSelectFolderEnabled(false);
+
+ const builder = new google.picker.PickerBuilder();
+
+ builder.addView(view)
+ .setOAuthToken(config.token)
+ .setDeveloperKey(config.apiKey)
+ .setOrigin(window.location.protocol + '//' + window.location.host)
+ .setCallback(data => {
+ if (data.action == google.picker.Action.PICKED) {
+ const doc = data.docs[0];
const isDrive = doc.serviceId === 'docs';
// Drive File (Always, since we removed Photos view)
controller.importFromPicker(doc.id, doc.mimeType, doc.name, null);
}
})
- .build()
- .setVisible(true);
+ .build()
+ .setVisible(true);
}
- // Init
- try {
- if (!window.state || !window.ui || !window.controller) {
- throw new Error("Core components failed to initialize. Check console for SyntaxError.");
- }
- controller.init();
- window.controller = controller; // Re-assert global access
- } catch (e) {
- alert("Init Failed: " + e.message);
- console.error(e);
- }
+ // Init
+ try {
+ if (!window.state || !window.ui || !window.controller) {
+ throw new Error("Core components failed to initialize. Check console for SyntaxError.");
+ }
+ controller.init();
+ window.controller = controller; // Re-assert global access
+ } catch (e) {
+ alert("Init Failed: " + e.message);
+ console.error(e);
+ }
- // Drag & Drop Handlers (Global)
- const dropOverlay = document.getElementById('drop-overlay');
- let dragCounter = 0;
+ // Drag & Drop Handlers (Global)
+ const dropOverlay = document.getElementById('drop-overlay');
+ let dragCounter = 0;
- // Check if the drag involves files
- function isFileDrag(e) {
- return e.dataTransfer.types && Array.from(e.dataTransfer.types).includes('Files');
- }
+ // Check if the drag involves files
+ function isFileDrag(e) {
+ return e.dataTransfer.types && Array.from(e.dataTransfer.types).includes('Files');
+ }
- document.addEventListener('dragenter', (e) => {
- if (!isFileDrag(e)) return;
- e.preventDefault();
- dragCounter++;
- dropOverlay.style.display = 'flex';
- });
+ document.addEventListener('dragenter', (e) => {
+ if (!isFileDrag(e)) return;
+ e.preventDefault();
+ dragCounter++;
+ dropOverlay.style.display = 'flex';
+ });
- document.addEventListener('dragleave', (e) => {
- if (!isFileDrag(e)) return;
- e.preventDefault();
- dragCounter--;
- if (dragCounter === 0) {
- dropOverlay.style.display = 'none';
- }
- });
+ document.addEventListener('dragleave', (e) => {
+ if (!isFileDrag(e)) return;
+ e.preventDefault();
+ dragCounter--;
+ if (dragCounter === 0) {
+ dropOverlay.style.display = 'none';
+ }
+ });
- document.addEventListener('dragover', (e) => {
- if (!isFileDrag(e)) return;
- e.preventDefault();
- });
+ document.addEventListener('dragover', (e) => {
+ if (!isFileDrag(e)) return;
+ e.preventDefault();
+ });
- document.addEventListener('drop', (e) => {
- if (!isFileDrag(e)) return;
- e.preventDefault();
- dragCounter = 0;
- dropOverlay.style.display = 'none';
- if (e.dataTransfer && e.dataTransfer.files.length > 0) {
- controller.handleFiles(e.dataTransfer.files);
- }
- });
+ document.addEventListener('drop', (e) => {
+ if (!isFileDrag(e)) return;
+ e.preventDefault();
+ dragCounter = 0;
+ dropOverlay.style.display = 'none';
+ if (e.dataTransfer && e.dataTransfer.files.length > 0) {
+ controller.handleFiles(e.dataTransfer.files);
+ }
+ });
diff --git a/src/services/MediaService.ts b/src/services/MediaService.ts
index e028b51..cf8e611 100644
--- a/src/services/MediaService.ts
+++ b/src/services/MediaService.ts
@@ -111,6 +111,8 @@ export class MediaService {
// Match Logic (Strict ID Match Only)
driveFileStats.forEach(d => {
let match = null
+ let isProcessing = false
+ let thumbnail = "";
// 1. ID Match
if (d.shopifyId) {
@@ -120,22 +122,37 @@ export class MediaService {
// NO Filename Fallback matching per new design "Strict Linkage"
+ if (match && match.preview && match.preview.image && match.preview.image.originalSrc) {
+ thumbnail = match.preview.image.originalSrc;
+ } else {
+ try {
+ // Try to get Drive thumbnail
+ thumbnail = `data:image/jpeg;base64,${Utilities.base64Encode(d.file.getThumbnail().getBytes())}`;
+ } catch (e) {
+ console.warn(`Failed to get thumbnail for ${d.file.getName()} (likely processing): ${e}`);
+ // Return a generic placeholder (Gray 1x1 pixel) + Flag as processing
+ // thumbnail = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
+ // Better placeholder: https://ssl.gstatic.com/docs/doclist/images/icon_10_movie_list.png (Video Icon) or just gray
+ thumbnail = "https://ssl.gstatic.com/docs/doclist/images/icon_128_video_blue.png"; // Official Video Icon
+ isProcessing = true;
+ }
+ }
+
unifiedState.push({
id: d.file.getId(), // Use Drive ID as primary key
driveId: d.file.getId(),
shopifyId: match ? match.id : null,
filename: d.file.getName(),
source: match ? 'synced' : 'drive_only',
- thumbnail: (match && match.preview && match.preview.image && match.preview.image.originalSrc)
- ? match.preview.image.originalSrc
- : `data:image/jpeg;base64,${Utilities.base64Encode(d.file.getThumbnail().getBytes())}`,
+ thumbnail: thumbnail,
status: 'active',
galleryOrder: d.galleryOrder,
mimeType: d.file.getMimeType(),
// Prefer Shopify Video URL for playback/hover if available, otherwise Drive Download URL
contentUrl: (match && match.sources)
? (match.sources.find((s: any) => s.mimeType === 'video/mp4')?.url || match.sources[0]?.url)
- : `https://drive.google.com/uc?export=download&id=${d.file.getId()}`
+ : `https://drive.google.com/uc?export=download&id=${d.file.getId()}`,
+ isProcessing: isProcessing
})
// console.log(`[MediaService] File ${d.file.getName()} (${d.file.getId()}): Mime=${d.file.getMimeType()}, ContentUrl=https://drive.google.com/uc?export=download&id=${d.file.getId()}`)
})