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
110 lines
4.9 KiB
TOML
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"
|