quickshell: tone down vinyl fallback, gate ring on playback, widen stream match

This commit is contained in:
rope 2026-07-30 16:00:29 +01:00
parent 88ab7944f4
commit 2b73b1e470

View file

@ -3853,13 +3853,38 @@ in
// widget. null when no stream matches.
function streamFor(player) {
if (!player) return null;
let id = (player.identity || "").toLowerCase();
// Apps are inconsistent about which property carries their
// name Spotify doesn't fill application.name the way
// browsers do so try every property that plausibly
// holds it, against both MPRIS names. Candidates shorter
// than 3 chars are skipped: substring matching on those
// matches almost anything.
let want = [];
let add = s => {
s = (s || "").toLowerCase();
if (s.length >= 3) want.push(s);
};
add(player.identity);
add(player.desktopEntry);
let ns = Pipewire.nodes.values;
for (let i = 0; i < ns.length; i++) {
let n = ns[i];
if (!n.isStream || !n.audio) continue;
let an = (n.properties["application.name"] || "").toLowerCase();
if (an && (an === id || an.includes(id) || id.includes(an))) return n;
let p = n.properties;
let cands = [
p["application.name"],
p["node.name"],
p["application.process.binary"]
];
for (let c = 0; c < cands.length; c++) {
let an = (cands[c] || "").toLowerCase();
if (an.length < 3) continue;
for (let w = 0; w < want.length; w++) {
if (an === want[w] || an.includes(want[w]) || want[w].includes(an))
return n;
}
}
}
return null;
}
@ -4137,8 +4162,14 @@ in
Rectangle {
id: eqBar
required property int index
// cava hears the whole output mix,
// not this stream, so a paused
// card must flatten its own ring
// otherwise every card dances to
// whatever else is playing.
readonly property real lvl:
index < calPopup.levels.length
(mediaCard.modelData.isPlaying
&& index < calPopup.levels.length)
? calPopup.levels[index] / 100 : 0
width: 3
radius: 1.5
@ -4164,67 +4195,36 @@ in
width: vinyl.discSize
height: vinyl.discSize
radius: width / 2
color: Theme.base02
color: Theme.base01
// Art-less fallback: an actual record
// rather than a flat placeholder, so
// a source with no MediaSession
// artwork (Jellyfin's web player, a
// plain <video> tag) still reads as
// deliberate. Grooves only turn
// visibly because they're eccentric
// to the disc concentric rings
// would look static while spinning.
// Art-less fallback: a dark record with
// faint grooves. Deliberately low
// contrast this is a placeholder, it
// shouldn't out-shout the actual
// covers it stands in for. Grooves are
// eccentric by 1px so they visibly
// turn; concentric rings look static
// while spinning.
Item {
id: blankLabel
anchors.fill: parent
visible: albumArt.status !== Image.Ready
Repeater {
model: 3
model: 4
Rectangle {
required property int index
anchors.centerIn: parent
anchors.horizontalCenterOffset: 1
width: vinyl.discSize - 10 - index * 14
width: vinyl.discSize - 8 - index * 11
height: width
radius: width / 2
color: "transparent"
border.width: 1
border.color: Theme.base03
opacity: 0.5
opacity: 0.18
}
}
Rectangle {
anchors.centerIn: parent
width: 40; height: 40
radius: 20
color: Theme.base0C
opacity: 0.25
}
// The site's own favicon, if it
// has one. Kept small and centred
// rather than filling the disc
// most are 16-32px and look awful
// upscaled to 86.
Image {
id: faviconImg
anchors.centerIn: parent
width: 28; height: 28
fillMode: Image.PreserveAspectFit
asynchronous: true
smooth: true
mipmap: true
visible: status === Image.Ready
source: mediaCard.faviconUrl
}
SIcon {
anchors.centerIn: parent
visible: faviconImg.status !== Image.Ready
text: sourceBadge.glyph !== "" ? sourceBadge.glyph : "music"
color: Theme.base05
font.pixelSize: 18
}
}
Image {
@ -4247,10 +4247,54 @@ in
}
}
// Spindle hole outside the disc so
// it stays put while the label turns.
// Label content sits outside the rotating
// disc so the favicon stays upright
// only the grooves turn. Doubles as the
// record's centre label, so no spindle
// hole is drawn over it.
Item {
anchors.centerIn: parent
width: 38; height: 38
visible: albumArt.status !== Image.Ready
Rectangle {
anchors.fill: parent
radius: width / 2
color: Theme.base02
}
// Circular crop: most favicons are a
// square plate on their own dark
// background, which fights the disc.
ClippingRectangle {
anchors.centerIn: parent
width: 24; height: 24
radius: width / 2
color: "transparent"
Image {
id: faviconImg
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
asynchronous: true
smooth: true
mipmap: true
visible: status === Image.Ready
source: mediaCard.faviconUrl
}
}
SIcon {
anchors.centerIn: parent
visible: faviconImg.status !== Image.Ready
text: sourceBadge.glyph !== "" ? sourceBadge.glyph : "music"
color: Theme.base04
font.pixelSize: 15
}
}
// Spindle hole, only over real artwork
// the blank label already owns the centre.
Rectangle {
anchors.centerIn: parent
visible: albumArt.status === Image.Ready
width: 14; height: 14
radius: 7
color: Theme.cardBg