Browse Source

Fix missing image thumbnails in external folder scan (#124)

  External folder scan generated thumbnails for 3mf/stl/gcode files but
  skipped image files. Added IMAGE_EXTENSIONS check with
  create_image_thumbnail() to the scan loop.
maziggy 2 months ago
parent
commit
95c703282d

+ 1 - 1
CHANGELOG.md

@@ -8,7 +8,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Missing Spool Assignment Notification** ([#763](https://github.com/maziggy/bambuddy/issues/763)) — When a print starts and the AMS mapping references tray slots without assigned spools, Bambuddy now shows a warning toast in the frontend and can send push notifications via any configured notification provider. The notification includes the printer name, missing slot labels (e.g. A2, Ext-L), and expected material profile. A new "Missing Spool Assignment" toggle is available under Print Events in notification provider settings (off by default). Fully integrated with i18n (all 7 locales). Contributed by @Keybored02.
 - **Missing Spool Assignment Notification** ([#763](https://github.com/maziggy/bambuddy/issues/763)) — When a print starts and the AMS mapping references tray slots without assigned spools, Bambuddy now shows a warning toast in the frontend and can send push notifications via any configured notification provider. The notification includes the printer name, missing slot labels (e.g. A2, Ext-L), and expected material profile. A new "Missing Spool Assignment" toggle is available under Print Events in notification provider settings (off by default). Fully integrated with i18n (all 7 locales). Contributed by @Keybored02.
 - **Mid-Print Spool Reassignment Tracking** ([#763](https://github.com/maziggy/bambuddy/issues/763)) — Usage tracking now correctly handles spool changes during a print. If a spool assignment is changed after a print starts, the system uses the live assignment for filament deduction; otherwise it falls back to the snapshot taken at print start. This ensures accurate filament tracking even when swapping spools mid-print. Contributed by @Keybored02.
 - **Mid-Print Spool Reassignment Tracking** ([#763](https://github.com/maziggy/bambuddy/issues/763)) — Usage tracking now correctly handles spool changes during a print. If a spool assignment is changed after a print starts, the system uses the live assignment for filament deduction; otherwise it falls back to the snapshot taken at print start. This ensures accurate filament tracking even when swapping spools mid-print. Contributed by @Keybored02.
 - **Auto-Link Untagged Inventory Spools on AMS Insert** ([#538](https://github.com/maziggy/bambuddy/issues/538)) — When a Bambu Lab spool is inserted into the AMS and no existing tag match is found, the system now checks if there is an untagged inventory spool with the same material, subtype, and color. If found, the RFID tag is automatically linked to that existing spool instead of creating a duplicate entry. Uses FIFO ordering (oldest spool first) so spools are consumed in purchase order. Matching is case-insensitive. Requested by @wreuel.
 - **Auto-Link Untagged Inventory Spools on AMS Insert** ([#538](https://github.com/maziggy/bambuddy/issues/538)) — When a Bambu Lab spool is inserted into the AMS and no existing tag match is found, the system now checks if there is an untagged inventory spool with the same material, subtype, and color. If found, the RFID tag is automatically linked to that existing spool instead of creating a duplicate entry. Uses FIFO ordering (oldest spool first) so spools are consumed in purchase order. Matching is case-insensitive. Requested by @wreuel.
-- **External Folder Mounting for File Manager** ([#124](https://github.com/maziggy/bambuddy/issues/124)) — Host directories (NAS shares, USB drives, network storage) can now be mounted into the File Manager without copying files. Click "Link External" to point at a Docker bind-mounted path. Files are indexed into the database on scan but accessed directly from their original location — nothing is copied. Supports read-only mode (default, blocks uploads/moves/deletes), hidden file filtering, and automatic thumbnail extraction for 3MF, STL, and gcode files. External folders show a distinct icon and info bar with a rescan button. Deleting an external folder only removes the database index, never the actual files. Requested by @S1N4X.
+- **External Folder Mounting for File Manager** ([#124](https://github.com/maziggy/bambuddy/issues/124)) — Host directories (NAS shares, USB drives, network storage) can now be mounted into the File Manager without copying files. Click "Link External" to point at a Docker bind-mounted path. Files are indexed into the database on scan but accessed directly from their original location — nothing is copied. Supports read-only mode (default, blocks uploads/moves/deletes), hidden file filtering, and automatic thumbnail extraction for 3MF, STL, gcode, and image files. External folders show a distinct icon and info bar with a rescan button. Deleting an external folder only removes the database index, never the actual files. Requested by @S1N4X.
 
 
 ### Fixed
 ### Fixed
 - **SpoolBuddy Update Check Always Shows "Up to Date"** — The SpoolBuddy daemon update check compared the device's firmware version against GitHub releases instead of the running Bambuddy backend version. This meant the check could incorrectly report "up to date" even when the daemon was behind. Fixed by comparing directly against `APP_VERSION` from the backend config.
 - **SpoolBuddy Update Check Always Shows "Up to Date"** — The SpoolBuddy daemon update check compared the device's firmware version against GitHub releases instead of the running Bambuddy backend version. This meant the check could incorrectly report "up to date" even when the daemon was behind. Fixed by comparing directly against `APP_VERSION` from the backend config.

+ 6 - 0
backend/app/api/routes/library.py

@@ -871,6 +871,12 @@ async def scan_external_folder(
                     thumb_full.write_bytes(thumb_data)
                     thumb_full.write_bytes(thumb_data)
                     thumbnail_path = to_relative_path(thumb_full)
                     thumbnail_path = to_relative_path(thumb_full)
 
 
+            # Create thumbnail for image files
+            if ext.lower() in IMAGE_EXTENSIONS and thumbnail_path is None:
+                thumbnail_path_str = create_image_thumbnail(filepath, get_library_thumbnails_dir())
+                if thumbnail_path_str:
+                    thumbnail_path = to_relative_path(Path(thumbnail_path_str))
+
             db_file = LibraryFile(
             db_file = LibraryFile(
                 folder_id=folder_id,
                 folder_id=folder_id,
                 is_external=True,
                 is_external=True,

+ 46 - 4
backend/tests/unit/services/test_spool_tag_matcher.py

@@ -224,20 +224,62 @@ async def test_get_spool_by_tag_first_char_variance_same_length(db_session):
 
 
 
 
 @pytest.mark.asyncio
 @pytest.mark.asyncio
-@pytest.mark.skip(reason="Pending reliable short-UID LIKE coverage across fixtures")
 async def test_get_spool_by_tag_first_char_variance_short_uid(db_session):
 async def test_get_spool_by_tag_first_char_variance_short_uid(db_session):
     """Match spool when 8-char scanned tag differs only in first character.
     """Match spool when 8-char scanned tag differs only in first character.
 
 
     Handles short UID (8 char) from 4-byte readers with first-char variance.
     Handles short UID (8 char) from 4-byte readers with first-char variance.
+    The stored tag is longer (16 char), but the first 8 chars of the stored tag
+    should match the scanned 8-char UID with first-char tolerance.
     """
     """
-    pass
+    spool = Spool(
+        material="PLA",
+        tag_uid="A4501234CCDDEE88",  # 16-char stored tag
+        label_weight=1000,
+        core_weight=250,
+    )
+    spool.k_profiles = []
+    spool.assignments = []
+    db_session.add(spool)
+    await db_session.commit()
+
+    # Scan with 8-char short UID whose first char differs but remaining 7 match
+    # the first 8 chars of the stored tag: stored[:8] = "A4501234",
+    # scanned = "B4501234" → first-char variance on short UID
+    found = await get_spool_by_tag(db_session, "B4501234", "")
+    assert found is not None
+    assert found.id == spool.id
 
 
 
 
 @pytest.mark.asyncio
 @pytest.mark.asyncio
-@pytest.mark.skip(reason="Pending reliable exact-vs-variance short-UID fixture setup")
 async def test_get_spool_by_tag_short_uid_exact_match_preferred(db_session):
 async def test_get_spool_by_tag_short_uid_exact_match_preferred(db_session):
     """Prefer exact match over first-char variance match."""
     """Prefer exact match over first-char variance match."""
-    pass
+    # Spool with exact 8-char UID match
+    spool_exact = Spool(
+        material="PLA",
+        tag_uid="B4501234",
+        label_weight=1000,
+        core_weight=250,
+    )
+    spool_exact.k_profiles = []
+    spool_exact.assignments = []
+    db_session.add(spool_exact)
+
+    # Spool that would match via first-char variance
+    spool_variance = Spool(
+        material="PETG",
+        tag_uid="A4501234",
+        label_weight=1000,
+        core_weight=250,
+    )
+    spool_variance.k_profiles = []
+    spool_variance.assignments = []
+    db_session.add(spool_variance)
+    await db_session.commit()
+
+    # Exact match should win over variance match
+    found = await get_spool_by_tag(db_session, "B4501234", "")
+    assert found is not None
+    assert found.id == spool_exact.id
 
 
 
 
 @pytest.mark.asyncio
 @pytest.mark.asyncio