mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
Look for a print's file when the printer says it is on the card
(#2780 regression) A print of a file already on the printer -- a reprint from the touchscreen, from Handy, or a slicer send-to-storage followed by a print -- reports its location as a path rather than as a fresh upload: file:///media/usb0/<name>. Since #2780 landed on 2026-08-14 Bambuddy read anything that was not ftp:// as "the printer kept this internally", skipped the FTPS sweep, and archived the print with a name and timing only. Measured on an H2D: the file was listable and downloadable over FTPS at the moment Bambuddy declared it unreachable. Before that change those prints archived normally, so this is a regression, and it is not confined to the H2 series the change was about -- an X1C reprint from its own screen loses its thumbnail exactly the same way. That module's own rule is to skip only on positive evidence, and a file:// path is not evidence of internal storage. It now reads the path: the printer's model cache under /userdata is a genuine skip, anything else is unknown and sweeps, which is what it did before. Unknown rather than external on purpose -- the empty-slot check still runs ahead of it, so a file:// print on a printer with nothing in the slot reports the missing card instead of sweeping for something that cannot be there. The user-facing copy shipped this morning is corrected in the same change, because it was written before we understood how Bambu Studio actually chooses. Its Print button always uses internal memory; only Send offers Cache or External, and that defaults to Cache too. So the advice now leads with the two routes that take one step -- start the print from Bambuddy, or slice in OrcaSlicer -- and offers Send-with-External and a separate print start as the way to stay in Bambu Studio. The earlier wording named no remedy at all and blamed the printer's firmware for a choice the slicer makes. Banner and diagnostic, thirteen locales, README, bundle rebuilt.
This commit is contained in:
@@ -77,6 +77,71 @@ class TestUrlScheme:
|
||||
"""None is a third answer and must not collapse into False."""
|
||||
assert url_is_external_storage(url) is None
|
||||
|
||||
|
||||
class TestFileScheme:
|
||||
"""A print of a file that was already on the printer.
|
||||
|
||||
``file://`` is what the printer reports for a reprint from its own screen,
|
||||
from Handy, or after a slicer sends to storage and then prints. Measured on
|
||||
an H2D 2026-08-17: ``file:///media/usb0/foobar.gcode.3mf`` while that exact
|
||||
file was listable and downloadable over FTPS. Reading it as internal storage
|
||||
skipped the sweep and produced an archive with no 3MF, for a file sitting
|
||||
right there -- and it did so on every model, not just the H2 series.
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"url",
|
||||
[
|
||||
"file:///media/usb0/foobar.gcode.3mf",
|
||||
"file:///media/sdcard/Benchy.gcode.3mf",
|
||||
"file:///media/usb0/timelapse/video.mp4",
|
||||
],
|
||||
)
|
||||
def test_an_external_mount_is_not_evidence_of_internal_storage(self, url):
|
||||
"""None, not True: the path is good reason to look, and looking is what
|
||||
the caller's default already does."""
|
||||
assert url_is_external_storage(url) is None
|
||||
|
||||
def test_the_model_cache_is_internal(self):
|
||||
"""``/userdata/model/history/<name>`` is where the printer's own file
|
||||
listing puts cached models, and port 990 does not serve it."""
|
||||
assert url_is_external_storage("file:///userdata/model/history/Cube.gcode.3mf") is False
|
||||
|
||||
def test_an_unrecognised_path_sweeps_rather_than_skips(self):
|
||||
"""Skip only on positive evidence -- a path we do not know is not that."""
|
||||
assert url_is_external_storage("file:///somewhere/new/Cube.3mf") is None
|
||||
|
||||
def test_the_sweep_runs_for_a_file_on_the_card(self):
|
||||
"""The regression in one assertion."""
|
||||
state = FakeState(
|
||||
current_project_url="file:///media/usb0/foobar.gcode.3mf",
|
||||
sdcard=True,
|
||||
sdcard_reported=True,
|
||||
)
|
||||
assert print_file_reachable_over_ftp(state).reachable is True
|
||||
|
||||
def test_an_empty_slot_still_wins(self):
|
||||
"""With nothing in the slot the file cannot be on it, whatever the path
|
||||
says -- and the operator gets the reason they can act on."""
|
||||
state = FakeState(
|
||||
current_project_url="file:///media/usb0/foobar.gcode.3mf",
|
||||
sdcard=False,
|
||||
sdcard_reported=True,
|
||||
)
|
||||
verdict = print_file_reachable_over_ftp(state)
|
||||
assert verdict.reachable is False
|
||||
assert verdict.reason == REASON_NO_EXTERNAL_STORAGE
|
||||
|
||||
def test_the_model_cache_still_skips(self):
|
||||
state = FakeState(
|
||||
current_project_url="file:///userdata/model/history/Cube.gcode.3mf",
|
||||
sdcard=True,
|
||||
sdcard_reported=True,
|
||||
)
|
||||
verdict = print_file_reachable_over_ftp(state)
|
||||
assert verdict.reachable is False
|
||||
assert verdict.reason == REASON_INTERNAL_STORAGE
|
||||
|
||||
@pytest.mark.parametrize("url", [12345, [], {}, object()])
|
||||
def test_a_non_string_url_declines_too(self, url):
|
||||
"""The value arrives straight off the wire, so it is whatever the
|
||||
|
||||
Reference in New Issue
Block a user