what's changed
the desktop app moves from qt 5 to qt 6. [qt 5 reached end-of-standard-support in may 2025, and only its commercial LTS continues](https://endoflife.date/qt), so this keeps sunnify on a framework that still gets security fixes. no visual or behavioral changes are intended - it's the same gradient card, verified pixel-by-pixel against the qt 5 build on all three OSes.
changed
- ported to PyQt6 6.11, following riverbank's documented pyqt5 → pyqt6 differences and qt's own porting guide: every enum is now its fully-scoped form (
Qt.AlignmentFlag,Qt.MouseButton,QEasingCurve.Type,QFont.Weight, …),exec_()isexec(), andQMouseEvent.globalPos()isglobalPosition().toPoint(). qt 6's high-dpi scaling is always on, so the manual attributes are gone while thePassThroughrounding policy from the #64 high-dpi fix stays explicit. - the windows-wheel landmine is gone for good.
pyqt5-qt5shipped no windows wheels after 5.15.2, which forced a hand-maintained per-platform pin (and caused a real release-build failure); pyqt6-qt6 ships current wheels on all three platforms, so the pin, its dependabot ignore, and the guardrail note are all deleted. Template.pywas ported by hand (imports, scoped enums, integer font weights → qt 6's opentypeQFont.Weightscale); it is deliberately not regenerated from the staleTemplate.ui.
fixed
- closing the app no longer logs itself as a crash. the close button called
sys.exit()inside a qt slot, which raisedSystemExitinto qt's exception hook and wrote aCRITICAL uncaught exceptionon every normal close, with no clean session-end - the logs read as if sunnify kept crashing. it now leaves the loop the documented way,QApplication.quit(), and an intentionalSystemExitis treated as a clean exit. - the "Show Preview" panel no longer crashes on qt 6. its slide animation referenced
QEasingCurve.InOutQuad, which isQEasingCurve.Type.InOutQuadunder scoped enums, and would have aborted on the first click. new interaction tests now drive the animation, the frameless-window drag, and the close path so this class of regression can't hide behind a green suite again.
notes
- verified before shipping: 213 tests green on python 3.10-3.13 across windows, macos, and linux; every dialog, button, animation, and the drag handlers exercised headless with zero enum failures; close and ctrl+c both confirmed to exit cleanly; the ui rendered at 1x/1.5x/2x on all three OSes and compared against the qt 5 baselines; and the real shipped binary was launched and confirmed to boot clean on its bundled python 3.13. binaries built with yt-dlp 2026.6.9.
verifying this release
sha256sum -c checksums.txt --ignore-missing
gh attestation verify Sunnify-Linux --repo sunnypatell/sunnify-spotify-downloaderinstall via homebrew (macos)
brew tap sunnypatell/sunnify https://github.com/sunnypatell/sunnify-spotify-downloader
brew install --cask sunnifyfull changelog: v2.0.15...v2.1.0
⭐ if sunnify saved you time, star the repo - stars are how people find it.
attestations from this build
direct links to each attestation generated by the workflow run that produced these binaries (sigstore-signed, public-good rekor log entry, SLSA build L3 via the release-build.yml reusable workflow):
- build provenance: Sunnify-Windows.exe
- build provenance: Sunnify-Linux
- build provenance: Sunnify-macOS.zip
- sbom attestation for the binaries
- build provenance for the source tarball
the *.sigstore.json assets attached above are the same attestations as offline-verifiable bundles. these get auto-refreshed on every release re-build, so the links above always correspond to the binaries currently attached to this release.