Files
qbz/crates/qbz-qt/Cargo.toml
T
vicrodh edbc037b0f feat(qt): Rust share seam, and fix the dead label share link
The port had no clipboard seam at all. share_qt.rs ports the URL builders and
the clipboard from crates/qbz/src/share.rs:1-70. The long-lived
OnceLock<Mutex<Option<Clipboard>>> is copied verbatim with its reason:
arboard ties the offer's lifetime to the LAST live Clipboard object, so a
create-per-copy port passes on KDE, which runs a clipboard manager that
accepts the handoff, and silently loses the text everywhere else (#514).
arboard carries the wayland-data-control feature at the same pin the
reference uses; without it arboard is X11-only and every copy fails on a
Wayland-only session.

The label header's Share was not merely silent - it copied a link that does
not resolve. open.qobuz.com has no /label/ route. The host is per-ENTITY, not
per-app: track and album on open.qobuz.com, artist, playlist and label on
play.qobuz.com. That asymmetry is why the five builders are ported as a set
and why no call site formats its own URL any more: this one did, and got it
wrong. QbzHome::label_share now copies play.qobuz.com/label/{id} and raises
the "Link copied" toast, 1:1 with main.rs:13164-13178, and the QML
Loader/TextEdit clipboard hack it replaced is deleted - its own comment
claimed there was no toast seam, which stopped being true when toast_qt.rs
landed.

Only the URL builders and the clipboard are ported. share.rs:72-184 is the
Song.link/Odesli resolution, which applies to tracks and albums; artists and
labels are link-only, so porting it now would be dead async HTTP.

LabelView also adopts QbzLoadMore here - its two changes share the file.

cc
2026-08-02 16:32:51 -06:00

110 lines
4.9 KiB
TOML

[package]
name = "qbz-qt"
description = "Qt/QML frontend POC for QBZ (cxx-qt bridge over the UI-agnostic backend)"
version.workspace = true
edition.workspace = true
license.workspace = true
[[bin]]
name = "qbz-qt"
path = "src/main.rs"
[dependencies]
cxx = "1"
cxx-qt = "0.7"
cxx-qt-lib = { version = "0.7", features = ["qt_full"] }
tokio = { workspace = true }
log = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
reqwest = { workspace = true }
urlencoding = "2"
open = "5"
dirs = "5"
# Share links (src/share_qt.rs, the port of crates/qbz/src/share.rs:32-70).
# Rust owns the clipboard on this side because the toast does: cxx-qt-lib 0.7
# ships no QClipboard binding, and QML cannot publish QbzShell.toastJson, so
# splitting the copy and its confirmation across the boundary would buy
# nothing. Same pin as the reference (crates/qbz/Cargo.toml:68), comment
# included:
#
# wayland-data-control: Wayland-native clipboard via the data-control
# protocols (GNOME/KDE/sway/niri/Hyprland), falling back to X11 when Wayland
# is absent (#514 — without it arboard is X11-only and every copy fails on
# Wayland-only sessions).
arboard = { version = "3", features = ["wayland-data-control"] }
qbz-core = { path = "../qbz-core" }
qbz-app = { path = "../qbz-app" }
qbz-audio = { path = "../qbz-audio" }
qbz-qobuz = { path = "../qbz-qobuz" }
qbz-models = { path = "../qbz-models" }
qbz-cache = { path = "../qbz-cache" }
qbz-offline-cache = { path = "../qbz-offline-cache" }
qbz-library = { path = "../qbz-library" }
# MyQBZ: Mixtapes & Collections backend (schema + migrations, repo, shuffle,
# enqueue). Headless and frontend-agnostic (ADR-006) — the same crate the
# Slint app uses (crates/qbz/Cargo.toml:96), so the two frontends share
# library.db's mixtape tables rather than each growing their own.
qbz-mixtape = { path = "../qbz-mixtape" }
qbz-source = { path = "../qbz-source" }
# Cast: Chromecast + DLNA discovery, the two device families and the local
# media server (src/cast_qt.rs drives it — nothing is reimplemented).
qbz-cast = { path = "../qbz-cast" }
qbz-plex = { path = "../qbz-plex" }
qbz-lyrics = { path = "../qbz-lyrics" }
# Playlist Importer: the provider scrapers (Spotify / Apple Music / Tidal /
# Deezer), the Qobuz matcher and the playlist creation. Headless and
# frontend-agnostic (ADR-006) — the same crate the Slint app uses
# (crates/qbz/Cargo.toml), so the two frontends share one importer instead of
# each growing its own. src/playlist_import_qt.rs drives it.
qbz-playlist-import = { path = "../qbz-playlist-import" }
qbz-text-utils = { path = "../qbz-text-utils" }
qbz-credentials = { path = "../qbz-credentials" }
qbz-i18n = { path = "../qbz-i18n" }
qbz-log = { path = "../qbz-log" }
qbz-theme = { path = "../qbz-theme" }
qbz-integrations = { path = "../qbz-integrations" }
# Last.fm "similar albums" row on the album page (external_reco_qt.rs).
qbz-external-reco = { path = "../qbz-external-reco" }
# Qobuz Connect (2026-08-01 Qt-port contract): the shared session/renderer
# orchestration and the WS transport, consumed VERBATIM — backend untouchable,
# same edges as the Slint crate (crates/qbz/Cargo.toml:87-88). Driven by
# src/qconnect_transport_qt.rs + src/qconnect_engine_qt.rs (+ the B3/B4 facade
# and sink).
qconnect-app = { path = "../qconnect-app" }
qconnect-transport-ws = { path = "../qconnect-transport-ws" }
# Type-level edge only, sanctioned by contract §1.1: the QConnect renderer
# engine trait names `qbz_player::PlaybackState`, and the engine's feeder
# reaches the protected player seams THROUGH `runtime.core().player()` — the
# same edge the Slint crate has (crates/qbz/Cargo.toml:85). No
# qbz-player/qbz-audio internals are touched (TRACK-RULES §7).
qbz-player = { path = "../qbz-player" }
# QConnect: uuid v4 for controller action ids (the B3 facade / B4 sink — same
# as crates/qbz/Cargo.toml:90); futures-util for the engine's HTTP feeder
# (StreamExt over the response byte stream).
uuid = { version = "1", features = ["v4"] }
futures-util = { workspace = true }
async-trait = "0.1"
rusqlite = { version = "0.31", features = ["bundled"] }
image = { version = "0.25", default-features = false, features = ["jpeg", "png"] }
# Native file picker, used by exactly two call sites — the MyQBZ collection
# cover upload (src/myqbz_cover_qt.rs) and the My-QBZ branding icon picker
# (src/myqbz_prefs_qt.rs).
#
# The feature set is NOT negotiable and is copied verbatim from the reference
# (crates/qbz/Cargo.toml:72): "async-std" is rfd's name for the
# async-io/ashpd backend. NEVER enable rfd's "tokio" feature — zbus 5's
# tokio feature unifies graph-wide and breaks the accesskit/atspi/ksni
# threads. `default-features = false` keeps the GTK3 backend out; the
# xdg-portal path is the Wayland-correct one and needs no extra system libs.
rfd = { version = "0.15", default-features = false, features = ["xdg-portal", "async-std"] }
[build-dependencies]
cxx-qt-build = "0.7"