quickshell: tone down vinyl fallback, gate ring on playback, widen stream match
This commit is contained in:
parent
88ab7944f4
commit
2b73b1e470
1 changed files with 93 additions and 49 deletions
|
|
@ -3853,13 +3853,38 @@ in
|
||||||
// widget. null when no stream matches.
|
// widget. null when no stream matches.
|
||||||
function streamFor(player) {
|
function streamFor(player) {
|
||||||
if (!player) return null;
|
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;
|
let ns = Pipewire.nodes.values;
|
||||||
for (let i = 0; i < ns.length; i++) {
|
for (let i = 0; i < ns.length; i++) {
|
||||||
let n = ns[i];
|
let n = ns[i];
|
||||||
if (!n.isStream || !n.audio) continue;
|
if (!n.isStream || !n.audio) continue;
|
||||||
let an = (n.properties["application.name"] || "").toLowerCase();
|
let p = n.properties;
|
||||||
if (an && (an === id || an.includes(id) || id.includes(an))) return n;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -4137,8 +4162,14 @@ in
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: eqBar
|
id: eqBar
|
||||||
required property int index
|
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:
|
readonly property real lvl:
|
||||||
index < calPopup.levels.length
|
(mediaCard.modelData.isPlaying
|
||||||
|
&& index < calPopup.levels.length)
|
||||||
? calPopup.levels[index] / 100 : 0
|
? calPopup.levels[index] / 100 : 0
|
||||||
width: 3
|
width: 3
|
||||||
radius: 1.5
|
radius: 1.5
|
||||||
|
|
@ -4164,67 +4195,36 @@ in
|
||||||
width: vinyl.discSize
|
width: vinyl.discSize
|
||||||
height: vinyl.discSize
|
height: vinyl.discSize
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: Theme.base02
|
color: Theme.base01
|
||||||
|
|
||||||
// Art-less fallback: an actual record
|
// Art-less fallback: a dark record with
|
||||||
// rather than a flat placeholder, so
|
// faint grooves. Deliberately low
|
||||||
// a source with no MediaSession
|
// contrast — this is a placeholder, it
|
||||||
// artwork (Jellyfin's web player, a
|
// shouldn't out-shout the actual
|
||||||
// plain <video> tag) still reads as
|
// covers it stands in for. Grooves are
|
||||||
// deliberate. Grooves only turn
|
// eccentric by 1px so they visibly
|
||||||
// visibly because they're eccentric
|
// turn; concentric rings look static
|
||||||
// to the disc — concentric rings
|
// while spinning.
|
||||||
// would look static while spinning.
|
|
||||||
Item {
|
Item {
|
||||||
id: blankLabel
|
id: blankLabel
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: albumArt.status !== Image.Ready
|
visible: albumArt.status !== Image.Ready
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: 3
|
model: 4
|
||||||
Rectangle {
|
Rectangle {
|
||||||
required property int index
|
required property int index
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
anchors.horizontalCenterOffset: 1
|
anchors.horizontalCenterOffset: 1
|
||||||
width: vinyl.discSize - 10 - index * 14
|
width: vinyl.discSize - 8 - index * 11
|
||||||
height: width
|
height: width
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
border.color: Theme.base03
|
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 {
|
Image {
|
||||||
|
|
@ -4247,10 +4247,54 @@ in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spindle hole — outside the disc so
|
// Label content sits outside the rotating
|
||||||
// it stays put while the label turns.
|
// 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 {
|
Rectangle {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
visible: albumArt.status === Image.Ready
|
||||||
width: 14; height: 14
|
width: 14; height: 14
|
||||||
radius: 7
|
radius: 7
|
||||||
color: Theme.cardBg
|
color: Theme.cardBg
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue