Эх сурвалжийг харах

Simplify scan bundle metadata to single language and add original photograph display support

unknown 2 сар өмнө
parent
commit
91585c99d2

+ 26 - 2
backend/routers/portfolio.py

@@ -182,7 +182,12 @@ async def get_public_scans():
     WHERE is_public = TRUE
     ORDER BY created_at DESC
     """
-    return db.execute_query(query)
+    scans = db.execute_query(query)
+    scans_dir = os.path.join(config.UPLOAD_DIR, "scans")
+    for scan in scans:
+        photo_path = os.path.join(scans_dir, scan["folder_name"], "photo.webp")
+        scan["has_photo"] = os.path.exists(photo_path)
+    return scans
 
 @router.get("/admin/scans")
 async def admin_get_all_scans(admin: dict = Depends(require_admin)):
@@ -193,7 +198,12 @@ async def admin_get_all_scans(admin: dict = Depends(require_admin)):
     FROM portfolio_scans
     ORDER BY created_at DESC
     """
-    return db.execute_query(query)
+    scans = db.execute_query(query)
+    scans_dir = os.path.join(config.UPLOAD_DIR, "scans")
+    for scan in scans:
+        photo_path = os.path.join(scans_dir, scan["folder_name"], "photo.webp")
+        scan["has_photo"] = os.path.exists(photo_path)
+    return scans
 
 @router.post("/admin/scans/upload-bundle")
 async def admin_upload_scan_bundle(
@@ -257,6 +267,20 @@ async def admin_upload_scan_bundle(
         if not os.path.exists(thumbnail_path):
             raise HTTPException(status_code=400, detail="Invalid bundle: thumbnail.webp is missing")
             
+        # Check and convert original photograph if present
+        for ext in [".webp", ".png", ".jpg", ".jpeg"]:
+            p_path = os.path.join(dest_dir, f"photo{ext}")
+            if os.path.exists(p_path):
+                if ext != ".webp":
+                    from PIL import Image
+                    try:
+                        with Image.open(p_path) as img:
+                            img.save(os.path.join(dest_dir, "photo.webp"), "WEBP", quality=85)
+                        os.remove(p_path)
+                    except Exception as ex:
+                        print(f"Failed to convert photo to WebP: {ex}")
+                break
+            
         # Insert DB record
         query = """
         INSERT INTO portfolio_scans (

+ 30 - 1
src/pages/Scanning.vue

@@ -85,7 +85,35 @@
         <div v-else class="grid grid-cols-1 lg:grid-cols-12 gap-8 items-start">
           <!-- Active scan 3D viewer (Top on mobile, right on desktop) -->
           <div class="lg:col-span-7 lg:order-2 space-y-4">
+            <!-- Toggle 3D vs Real Photo -->
+            <div v-if="activeScan.has_photo" class="flex gap-1.5 p-1 bg-card/60 border border-border/40 rounded-2xl w-fit">
+              <button 
+                @click="showRealPhoto = false"
+                class="px-4 py-1.5 rounded-xl text-xs font-black transition-all"
+                :class="!showRealPhoto ? 'bg-primary text-primary-foreground shadow-lg shadow-primary/20' : 'text-muted-foreground hover:text-white'"
+              >
+                360° View
+              </button>
+              <button 
+                @click="showRealPhoto = true"
+                class="px-4 py-1.5 rounded-xl text-xs font-black transition-all"
+                :class="showRealPhoto ? 'bg-primary text-primary-foreground shadow-lg shadow-primary/20' : 'text-muted-foreground hover:text-white'"
+              >
+                Original Photo
+              </button>
+            </div>
+
+            <!-- Photorealistic Image Container -->
+            <div v-if="showRealPhoto && activeScan.has_photo" class="relative group aspect-square lg:aspect-video rounded-[2.5rem] border border-border/50 bg-black/45 overflow-hidden flex items-center justify-center backdrop-blur-sm">
+              <img 
+                :src="`${RESOURCES_BASE_URL}/uploads/scans/${activeScan.folder_name}/photo.webp`"
+                alt="Original physical object"
+                class="max-w-full max-h-full object-contain"
+              />
+            </div>
+            
             <ThreeSixtyViewer
+              v-else
               :folder-name="activeScan.folder_name"
               :frames-count="activeScan.frames_count"
               :resources-base-url="RESOURCES_BASE_URL"
@@ -105,7 +133,7 @@
             <button
               v-for="scan in scans"
               :key="scan.id"
-              @click="activeScan = scan"
+              @click="activeScan = scan; showRealPhoto = false"
               class="w-full text-left p-4 rounded-2xl border transition-all duration-300 flex gap-4 bg-card/20 hover:bg-card/45"
               :class="activeScan.id === scan.id ? 'border-primary/50 bg-primary/5 shadow-lg shadow-primary/5' : 'border-border/35 hover:border-border/60'"
             >
@@ -191,6 +219,7 @@ const { t, locale } = useI18n();
 
 const scans = ref<any[]>([]);
 const activeScan = ref<any>(null);
+const showRealPhoto = ref(false);
 const loading = ref(true);
 
 const getIcon = (step: string) => {