ScanStudio 0.4.0-beta.1
Pre-releaseScanStudio 0.4.0-beta.1 is the first prerelease of the 0.4 line. The headline: multi-batch roll capture now runs on one transport reservation from feed to eject, which fixes the mid-roll failure several of you hit past frame 30.
Support levels are unchanged from 0.3.0-beta.3:
- macOS Apple Silicon: Beta. The Nikon LS-5000 real-film workflow has been validated, including the new multi-batch session shape on a full 36-exposure roll.
- macOS Intel: Preview. Built and package-tested natively in CI; real-scanner validation is still pending.
- Windows x64: Preview. The native installer is built, installed, and checked in CI. The portable archive requires Microsoft Edge WebView2 to already be installed, so clean systems should use setup.exe. Scanner access requires WSL2 with Ubuntu 24.04 and usbipd-win. Real-scanner validation is pending.
- Linux x64: Preview. The AppImage and portable archive are built and package-verified on Ubuntu 22.04. The host must provide compatible SANE/libusb runtime packages and scanner permissions. Real-scanner validation is pending.
Everything that changed since 0.3.0-beta.3
One reservation from feed to eject (#30). Every scan_many() call used to open its own transport reservation. The second batch mid-roll replayed the SET_WINDOW preamble about 30 frames in, and the scanner answered sense 052600 -- the "ScanStudio could not complete that action (INTERNAL)" report. Now preview() keeps its reservation open and every scan_many()/scan() that follows resumes it, indefinitely, until eject_after=True, Roll.eject(), Roll.release(), or Roll.close() ends the session. This is the vendor's own traced session shape. Validated on real film: 34/34 frames across two batches on a single reservation, frame 31+ clean, single release, traced eject.
Behavior change: batches hold the reservation. scan_many() without eject_after no longer releases after the batch. If you scripted against the old per-batch release (for example to force a refeed between batches), call roll.release() explicitly.
Behavior change: eject goes through the held reservation. Roll.eject() replays the traced software eject sequence instead of delegating to the SANE path. A suspected transport wedge during that sequence raises FeederParked (power-cycle required) instead of silently falling back.
New padding dialect accepted (post-power-on scans). The LS-5000 stamps a previously unseen padding counter dialect (f6b1-prefixed) on the first fine scan after a power-on. The capture validator refused it -- correctly, fail-closed, with the full stream retained -- and it is now accepted as the sixth known stable form. If your very first scan after switching the scanner on ever failed with "packed-stream decode refused", this was it.
Streaming decoder enforces one dialect per stream. The offline decoder always refused streams whose padding dialect changed mid-stream; the streaming decoder validated each record independently and would have accepted them. Both paths now refuse identically. Never observed on real hardware; closed as a hardening fix.
Integration test for the exact live session shape. A new test drives the real capture worker (only the USB transport faked) through one feed-to-eject, two-batch reservation -- the shape that found four resume-path bugs on real film before it existed. The vendored suite is 1543 tests.
Fixed a flaky concurrent-close handshake test (#40).
Versioning going forward. Feature releases bump the minor version and start a new beta line (this release). Bug-fix rounds bump the prerelease number (0.4.0-beta.2, ...). A field-proven line cuts the plain stable so the updater's stable channel means something.
The macOS builds are ad-hoc signed and not notarized. The Windows installer is unsigned and may show a SmartScreen warning. The Linux packages are unsigned. Review SHA256SUMS before installing when downloading outside the GitHub Releases page.