quickshell: fall back to the page favicon when no artwork
This commit is contained in:
parent
02ebdc36ce
commit
88ab7944f4
1 changed files with 91 additions and 2 deletions
|
|
@ -182,6 +182,50 @@ in
|
|||
${pkgs.curl}/bin/curl -sf --max-time 10 "https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon&daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset&timezone=auto&forecast_days=7"
|
||||
'';
|
||||
|
||||
# Last-resort artwork: when a page publishes no MediaSession artwork
|
||||
# (Jellyfin's web player, a bare <video> tag), fall back to its
|
||||
# favicon. Filenames are often content-hashed, so /favicon.ico can't
|
||||
# be guessed — the page's <link> tags have to be read. Prefers
|
||||
# apple-touch-icon (180px) over favicon.ico (16px). Prints nothing on
|
||||
# any failure, which the QML treats as "no icon".
|
||||
faviconScript = pkgs.writeShellScript "favicon-fetch" ''
|
||||
url="$1"
|
||||
[ -z "$url" ] && exit 0
|
||||
case "$url" in http://*|https://*) ;; *) exit 0 ;; esac
|
||||
|
||||
page=''${url%%#*} # drop SPA fragment (#/video)
|
||||
proto=''${page%%://*}
|
||||
rest=''${page#*://}
|
||||
host=''${rest%%/*}
|
||||
origin="$proto://$host"
|
||||
dir=''${page%/*}/
|
||||
|
||||
html=$(${pkgs.curl}/bin/curl -sfL --max-time 5 "$page" 2>/dev/null)
|
||||
[ -z "$html" ] && html=$(${pkgs.curl}/bin/curl -sfL --max-time 5 "$origin/" 2>/dev/null)
|
||||
[ -z "$html" ] && exit 0
|
||||
|
||||
# Minified HTML is one long line, so pull each <link> out with
|
||||
# grep -o rather than trying to split on newlines.
|
||||
pick() {
|
||||
printf '%s' "$html" \
|
||||
| grep -oiE '<link[^>]*>' \
|
||||
| grep -iE "rel=[\"']?$1" \
|
||||
| grep -oiE "href=[\"'][^\"']+[\"']" \
|
||||
| head -1 | sed -E "s/^href=[\"']//; s/[\"']\$//"
|
||||
}
|
||||
|
||||
href=$(pick "apple-touch-icon")
|
||||
[ -z "$href" ] && href=$(pick "(shortcut +)?icon[\"' ]")
|
||||
[ -z "$href" ] && exit 0
|
||||
|
||||
case "$href" in
|
||||
http://*|https://*) printf '%s' "$href" ;;
|
||||
//*) printf '%s:%s' "$proto" "$href" ;;
|
||||
/*) printf '%s%s' "$origin" "$href" ;;
|
||||
*) printf '%s%s' "$dir" "$href" ;;
|
||||
esac
|
||||
'';
|
||||
|
||||
# cava in raw mode: one newline-terminated frame per tick, bars as
|
||||
# semicolon-separated 0-100 ints — trivially parsed by SplitParser.
|
||||
# Drives the media card's spectrum ring. Only spawned while the
|
||||
|
|
@ -499,6 +543,7 @@ in
|
|||
// Recoloured to the theme accent at draw time (ColorOverlay),
|
||||
// so the stock blue/grey snowflake artwork is fine here.
|
||||
readonly property string nixLogo: "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg"
|
||||
readonly property string faviconFetch: "${faviconScript}"
|
||||
readonly property string cava: "${pkgs.cava}/bin/cava"
|
||||
readonly property string cavaConfig: "${cavaConfig}"
|
||||
}
|
||||
|
|
@ -4022,6 +4067,33 @@ in
|
|||
id: mediaCard
|
||||
required property var modelData
|
||||
property var pwNode: calPopup.streamFor(modelData)
|
||||
|
||||
// xesam:url is the only field that says where
|
||||
// the audio actually comes from; identity just
|
||||
// names the browser.
|
||||
readonly property string pageUrl: {
|
||||
let md = modelData.metadata || ({});
|
||||
return md["xesam:url"] || "";
|
||||
}
|
||||
property string faviconUrl: ""
|
||||
|
||||
function refreshFavicon() {
|
||||
faviconUrl = "";
|
||||
// Rewriting a live command truncates the
|
||||
// read, so skip while one is in flight.
|
||||
if (pageUrl !== "" && !faviconProc.running)
|
||||
faviconProc.running = true;
|
||||
}
|
||||
onPageUrlChanged: refreshFavicon()
|
||||
Component.onCompleted: refreshFavicon()
|
||||
|
||||
Process {
|
||||
id: faviconProc
|
||||
command: [Commands.faviconFetch, mediaCard.pageUrl]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: mediaCard.faviconUrl = text.trim()
|
||||
}
|
||||
}
|
||||
width: parent.width
|
||||
height: mediaCol.height + 16
|
||||
radius: Theme.radiusCard
|
||||
|
|
@ -4125,13 +4197,30 @@ in
|
|||
}
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 34; height: 34
|
||||
radius: 17
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue