diff --git a/CHANGELOG.md b/CHANGELOG.md index a1e14f237..1a1dc541b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to Bambuddy will be documented in this file. ## [0.2.4b1] - Unreleased ### Fixed +- **P2S Firmware Check Shows Stale "Latest" Version** ([#1030](https://github.com/maziggy/bambuddy/issues/1030)) — On P2S (and X2D) the Firmware Info modal reported `01.01.01.00` as the newest available release even though `01.02.00.00` had shipped on the Bambu Lab wiki weeks earlier, so the "update available" badge never appeared. Two silent regex mismatches in the wiki scraper caused `_fetch_all_versions_from_wiki()` to return an empty list: (1) the section-heading anchor parser required a dash between the version bytes and the release date (`id="h-01020000-20260409"`), but P2S and X2D publish anchors without the dash (`id="h-0102000020260409"`); (2) the text-based fallback only accepted ASCII parens around the date, while P2S, X2D, A1 and A1-mini headings render dates in full-width `(YYYYMMDD)` (U+FF08/U+FF09). When both paths failed, the code silently fell back to the Bambu Lab download page, which still lagged at `01.01.01.00`. The anchor regex now accepts an optional dash and the fallback accepts both paren styles; added regression tests for the no-dash anchor and full-width paren shapes. Thanks to @Minebuddy for reporting. - **Library File Print-Usage Tracking** ([#1008](https://github.com/maziggy/bambuddy/issues/1008)) — `LibraryFile.print_count` and `last_printed_at` are now updated on every successful queued print completion. Previously both fields were defined on the model and displayed in the File Manager, but nothing ever wrote to them — every file in every library showed as never printed. Now counts increment cumulatively and `last_printed_at` stamps the completion timestamp (UTC). Failed, cancelled and user-aborted prints are intentionally excluded, so the fields represent "successful usage" rather than "attempted usage." This unblocks sorting the File Manager by last-printed date and is a prerequisite for the scheduled-purge feature requested in #1008. Thanks to @cadtoolbox for the report. ### Improved diff --git a/backend/app/services/firmware_check.py b/backend/app/services/firmware_check.py index beef47af8..4ad78cd5e 100644 --- a/backend/app/services/firmware_check.py +++ b/backend/app/services/firmware_check.py @@ -161,8 +161,11 @@ class FirmwareCheckService: Fetch all firmware versions from the wiki release history page. Only extracts versions that appear in section-heading anchors - (e.g. `id="h-01030000-20260303"`) — this excludes version-like - numbers mentioned incidentally in release-note text. + (e.g. `id="h-01030000-20260303"` or `id="h-0102000020260409"`) — + this excludes version-like numbers mentioned incidentally in + release-note text. The dash separator between version and date is + optional: H2D/X1/H2C/H2S still use it, but P2S and X2D publish + anchors without the dash. Returns list of (version, release_date_YYYYMMDD | None) tuples, newest first. """ @@ -176,8 +179,9 @@ class FirmwareCheckService: if response.status_code != 200: return [] - # Primary: heading anchor ids like id="h-01030000-20260303" - anchor_matches = re.findall(r'id="h-(\d{2})(\d{2})(\d{2})(\d{2})-(\d{8})"', response.text) + # Primary: heading anchor ids like id="h-01030000-20260303" (dash) + # or id="h-0102000020260409" (no dash, P2S/X2D-style). + anchor_matches = re.findall(r'id="h-(\d{2})(\d{2})(\d{2})(\d{2})-?(\d{8})"', response.text) seen: set[str] = set() versions: list[tuple[str, str | None]] = [] for a, b, c, d, date in anchor_matches: @@ -190,8 +194,13 @@ class FirmwareCheckService: if versions: return versions - # Fallback: heading text with "XX.XX.XX.XX (YYYYMMDD)" - text_matches = re.findall(r"(\d{2}\.\d{2}\.\d{2}\.\d{2})\s*\((\d{8})\)", response.text) + # Fallback: heading text with "XX.XX.XX.XX (YYYYMMDD)" — + # accept both ASCII "()" and full-width "()" (U+FF08/U+FF09) + # which some pages (A1, A1-mini, P2S) use. + text_matches = re.findall( + r"(\d{2}\.\d{2}\.\d{2}\.\d{2})\s*[(\uff08](\d{8})[)\uff09]", + response.text, + ) for v, date in text_matches: if v in seen: continue diff --git a/backend/tests/unit/test_firmware_versions.py b/backend/tests/unit/test_firmware_versions.py index 31eecba10..bb0012581 100644 --- a/backend/tests/unit/test_firmware_versions.py +++ b/backend/tests/unit/test_firmware_versions.py @@ -50,6 +50,53 @@ async def test_wiki_extraction_returns_empty_for_unknown_api_key(): assert await svc._fetch_all_versions_from_wiki("no-such-key") == [] +# P2S and X2D wiki pages publish anchor ids without a dash between the +# version bytes and the date (e.g. h-0102000020260409). Regression for #1030 +# where the anchor regex required a dash and silently returned no versions, +# causing the UI to fall back to the stale download-page "Latest" value. +P2S_NODASH_ANCHOR_SAMPLE = """ +