feat: Use Shopify thumbnail and playback URL for synced media
- Update \MediaService.ts\ to populate \ humbnail\ and \contentUrl\ from Shopify media when a Drive file is synced. - Enable \synced\ videos to use the Shopify video URL for playback/hover. - Update \MediaManager.html\ to allow \synced\ items to render as \<video>\ tags if they have a valid \contentUrl\. - Add regression tests in \MediaService.test.ts\ for thumbnail and video sync behavior.
This commit is contained in:
@ -795,8 +795,9 @@
|
|||||||
// Create Media Element
|
// Create Media Element
|
||||||
// RULE: Only create <video> for Shopify-hosted videos (public).
|
// RULE: Only create <video> for Shopify-hosted videos (public).
|
||||||
// Drive videos use static thumbnail + Iframe Preview.
|
// Drive videos use static thumbnail + Iframe Preview.
|
||||||
var mediaEl;
|
var mediaEl;
|
||||||
if (isVideo && item.source === 'shopify_only' && contentUrl) {
|
// Allow Shopify-only OR Synced items with valid contentUrl (Shopify Video URL) to use <video> tag
|
||||||
|
if (isVideo && (item.source === 'shopify_only' || item.source === 'synced') && contentUrl) {
|
||||||
mediaEl = document.createElement('video');
|
mediaEl = document.createElement('video');
|
||||||
mediaEl.src = contentUrl;
|
mediaEl.src = contentUrl;
|
||||||
mediaEl.poster = item.thumbnail || "";
|
mediaEl.poster = item.thumbnail || "";
|
||||||
|
|||||||
@ -205,4 +205,56 @@ describe("MediaService Robust Sync", () => {
|
|||||||
})
|
})
|
||||||
]))
|
]))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("Thumbnail: Uses Shopify thumbnail when synced", () => {
|
||||||
|
const folder = driveService.getOrCreateFolder("SKU_THUMB", "root")
|
||||||
|
|
||||||
|
// Drive File
|
||||||
|
const blob1 = { getName: () => "img1.jpg", getBytes: () => [], getThumbnail: () => ({ getBytes: () => [1, 2, 3] }) } as any
|
||||||
|
const f1 = driveService.saveFile(blob1, folder.getId())
|
||||||
|
driveService.updateFileProperties(f1.getId(), { shopify_media_id: "gid://shopify/Media/123" })
|
||||||
|
|
||||||
|
// Shopify Media with distinct thumbnail
|
||||||
|
shopifyService.getProductMedia = jest.fn().mockReturnValue([
|
||||||
|
{
|
||||||
|
id: "gid://shopify/Media/123",
|
||||||
|
filename: "img1.jpg",
|
||||||
|
preview: { image: { originalSrc: "https://shopify.com/thumb.jpg" } }
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const state = mediaService.getUnifiedMediaState("SKU_THUMB", "pid")
|
||||||
|
|
||||||
|
const item = state.find(s => s.id === f1.getId())
|
||||||
|
expect(item.source).toBe("synced")
|
||||||
|
expect(item.thumbnail).toBe("https://shopify.com/thumb.jpg")
|
||||||
|
// Verify it didn't use the base64 drive thumbnail
|
||||||
|
expect(item.thumbnail).not.toContain("base64")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("Video Sync: Uses Shopify contentUrl for synced videos", () => {
|
||||||
|
const folder = driveService.getOrCreateFolder("SKU_VID_SYNC", "root")
|
||||||
|
|
||||||
|
// Drive File (Video)
|
||||||
|
const blob = { getName: () => "vid.mp4", getBytes: () => [], getMimeType: () => "video/mp4", getThumbnail: () => ({ getBytes: () => [] }) } as any
|
||||||
|
const f = driveService.saveFile(blob, folder.getId())
|
||||||
|
driveService.updateFileProperties(f.getId(), { shopify_media_id: "gid://shopify/Media/Vid1" })
|
||||||
|
|
||||||
|
// Shopify Media (Video)
|
||||||
|
shopifyService.getProductMedia = jest.fn().mockReturnValue([
|
||||||
|
{
|
||||||
|
id: "gid://shopify/Media/Vid1",
|
||||||
|
filename: "vid.mp4",
|
||||||
|
mediaContentType: "VIDEO",
|
||||||
|
sources: [{ url: "https://shopify.com/video.mp4", mimeType: "video/mp4" }],
|
||||||
|
preview: { image: { originalSrc: "https://shopify.com/vid_thumb.jpg" } }
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const state = mediaService.getUnifiedMediaState("SKU_VID_SYNC", "pid")
|
||||||
|
const item = state.find(s => s.id === f.getId())
|
||||||
|
|
||||||
|
expect(item.contentUrl).toBe("https://shopify.com/video.mp4")
|
||||||
|
expect(item.thumbnail).toBe("https://shopify.com/vid_thumb.jpg")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -126,12 +126,16 @@ export class MediaService {
|
|||||||
shopifyId: match ? match.id : null,
|
shopifyId: match ? match.id : null,
|
||||||
filename: d.file.getName(),
|
filename: d.file.getName(),
|
||||||
source: match ? 'synced' : 'drive_only',
|
source: match ? 'synced' : 'drive_only',
|
||||||
thumbnail: `data:image/jpeg;base64,${Utilities.base64Encode(d.file.getThumbnail().getBytes())}`,
|
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())}`,
|
||||||
status: 'active',
|
status: 'active',
|
||||||
galleryOrder: d.galleryOrder,
|
galleryOrder: d.galleryOrder,
|
||||||
mimeType: d.file.getMimeType(),
|
mimeType: d.file.getMimeType(),
|
||||||
// Use manual download URL construction which is often more reliable for authenticated sessions than getDownloadUrl()
|
// Prefer Shopify Video URL for playback/hover if available, otherwise Drive Download URL
|
||||||
contentUrl: `https://drive.google.com/uc?export=download&id=${d.file.getId()}`
|
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()}`
|
||||||
})
|
})
|
||||||
// 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()}`)
|
// 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()}`)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user