Heic support

This commit is contained in:
Roman Krček
2025-07-18 09:17:46 +02:00
parent 9bbd02dd67
commit 2e228126be
3 changed files with 50 additions and 37 deletions

7
package-lock.json generated
View File

@@ -17,6 +17,7 @@
"@types/google.accounts": "^0.0.17", "@types/google.accounts": "^0.0.17",
"@types/uuid": "^10.0.0", "@types/uuid": "^10.0.0",
"fontkit": "^2.0.4", "fontkit": "^2.0.4",
"heic2any": "^0.0.4",
"idb": "^8.0.3", "idb": "^8.0.3",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"uuid": "^11.1.0" "uuid": "^11.1.0"
@@ -2255,6 +2256,12 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/heic2any": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/heic2any/-/heic2any-0.0.4.tgz",
"integrity": "sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==",
"license": "MIT"
},
"node_modules/idb": { "node_modules/idb": {
"version": "8.0.3", "version": "8.0.3",
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.3.tgz", "resolved": "https://registry.npmjs.org/idb/-/idb-8.0.3.tgz",

View File

@@ -38,6 +38,7 @@
"@types/google.accounts": "^0.0.17", "@types/google.accounts": "^0.0.17",
"@types/uuid": "^10.0.0", "@types/uuid": "^10.0.0",
"fontkit": "^2.0.4", "fontkit": "^2.0.4",
"heic2any": "^0.0.4",
"idb": "^8.0.3", "idb": "^8.0.3",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"uuid": "^11.1.0" "uuid": "^11.1.0"

View File

@@ -116,18 +116,42 @@
} }
try { try {
let objectUrl: string; let blob: Blob;
if (isGoogleDriveUrl(photo.url)) { if (isGoogleDriveUrl(photo.url)) {
// Download from Google Drive // Download from Google Drive
console.log(`Downloading from Google Drive: ${photo.name}`); console.log(`Downloading from Google Drive: ${photo.name}`);
const blob = await downloadDriveImage(photo.url); blob = await downloadDriveImage(photo.url);
objectUrl = createImageObjectUrl(blob);
} else { } else {
// Use direct URL // For direct URLs, convert to blob
objectUrl = photo.url; const response = await fetch(photo.url);
blob = await response.blob();
} }
// Check for HEIC/HEIF format and convert if necessary
if (
blob.type === 'image/heic' ||
blob.type === 'image/heif' ||
photo.url.toLowerCase().endsWith('.heic')
) {
console.log(`Converting HEIC image for ${photo.name}...`);
try {
const { default: heic2any } = await import('heic2any');
const conversionResult = await heic2any({
blob,
toType: 'image/jpeg',
quality: 0.9
});
blob = Array.isArray(conversionResult) ? conversionResult[0] : conversionResult;
console.log(`Successfully converted HEIC for ${photo.name}`);
} catch (e) {
console.error(`Failed to convert HEIC image for ${photo.name}:`, e);
throw new Error('HEIC conversion failed');
}
}
const objectUrl = createImageObjectUrl(blob);
// Test if image loads properly // Test if image loads properly
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const img = new Image(); const img = new Image();
@@ -144,36 +168,17 @@
console.log(`Photo loaded successfully: ${photo.name}`); console.log(`Photo loaded successfully: ${photo.name}`);
// Save to pictures store // Save to pictures store
if (isGoogleDriveUrl(photo.url)) { pictures.update(pics => ({
// For Google Drive images, save the blob ...pics,
const blob = await downloadDriveImage(photo.url); [photo.url]: {
pictures.update(pics => ({ id: photo.url,
...pics, blob: blob,
[photo.url]: { url: objectUrl,
id: photo.url, downloaded: true,
blob: blob, faceDetected: false,
url: objectUrl, faceCount: 0
downloaded: true, }
faceDetected: false, }));
faceCount: 0
}
}));
} else {
// For direct URLs, convert to blob
const response = await fetch(photo.url);
const blob = await response.blob();
pictures.update(pics => ({
...pics,
[photo.url]: {
id: photo.url,
blob: blob,
url: objectUrl,
downloaded: true,
faceDetected: false,
faceCount: 0
}
}));
}
// Automatically run face detection to generate crop // Automatically run face detection to generate crop
await detectFaceForPhoto(index); await detectFaceForPhoto(index);
@@ -410,7 +415,7 @@
{/if} {/if}
<!-- Photo Grid --> <!-- Photo Grid -->
<div class="bg-white overflow-hidden mb-6"> <div class="bg-white border border-gray-200 rounded-lg overflow-hidden mb-6">
{#if photos.length === 0 && !isProcessing} {#if photos.length === 0 && !isProcessing}
<div class="text-center py-12"> <div class="text-center py-12">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -422,7 +427,7 @@
</p> </p>
</div> </div>
{:else} {:else}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> <div class="p-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{#each photos as photo, index} {#each photos as photo, index}
<PhotoCard <PhotoCard
{photo} {photo}