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"
|
${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
|
# cava in raw mode: one newline-terminated frame per tick, bars as
|
||||||
# semicolon-separated 0-100 ints — trivially parsed by SplitParser.
|
# semicolon-separated 0-100 ints — trivially parsed by SplitParser.
|
||||||
# Drives the media card's spectrum ring. Only spawned while the
|
# 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),
|
// Recoloured to the theme accent at draw time (ColorOverlay),
|
||||||
// so the stock blue/grey snowflake artwork is fine here.
|
// 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 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 cava: "${pkgs.cava}/bin/cava"
|
||||||
readonly property string cavaConfig: "${cavaConfig}"
|
readonly property string cavaConfig: "${cavaConfig}"
|
||||||
}
|
}
|
||||||
|
|
@ -4022,6 +4067,33 @@ in
|
||||||
id: mediaCard
|
id: mediaCard
|
||||||
required property var modelData
|
required property var modelData
|
||||||
property var pwNode: calPopup.streamFor(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
|
width: parent.width
|
||||||
height: mediaCol.height + 16
|
height: mediaCol.height + 16
|
||||||
radius: Theme.radiusCard
|
radius: Theme.radiusCard
|
||||||
|
|
@ -4125,13 +4197,30 @@ in
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: 34; height: 34
|
width: 40; height: 40
|
||||||
radius: 17
|
radius: 20
|
||||||
color: Theme.base0C
|
color: Theme.base0C
|
||||||
opacity: 0.25
|
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 {
|
SIcon {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
visible: faviconImg.status !== Image.Ready
|
||||||
text: sourceBadge.glyph !== "" ? sourceBadge.glyph : "music"
|
text: sourceBadge.glyph !== "" ? sourceBadge.glyph : "music"
|
||||||
color: Theme.base05
|
color: Theme.base05
|
||||||
font.pixelSize: 18
|
font.pixelSize: 18
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue