1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-01 19:18:28 -04:00
Files
DankMaterialShell/quickshell/Services/TrackArtService.qml
T

215 lines
8.6 KiB
QML

pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import Quickshell.Services.Mpris
import qs.Common
Singleton {
id: root
property string _lastArtUrl: ""
property string resolvedArtUrl: ""
property alias _bgArtSource: root.resolvedArtUrl
property bool loading: false
// sha1s of placeholder art to reject (Chrome's own logo, shown before real cover).
readonly property var _artHashDenylist: ["764a730860c5b8a7bbee690ee5a443672ae37dc8"]
function djb2Hash(str) {
if (!str) return "";
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) + hash) + str.charCodeAt(i);
hash = hash & 0x7FFFFFFF;
}
return hash.toString(16).padStart(8, '0');
}
function getArtworkUrl(player) {
if (!player) return "";
// 1. If native trackArtUrl is present and valid
let artUrl = player.trackArtUrl || "";
if (artUrl !== "") {
return artUrl;
}
// 2. Fallback to raw metadata mpris:artUrl if present
if (player.metadata && player.metadata["mpris:artUrl"]) {
artUrl = player.metadata["mpris:artUrl"].toString();
if (artUrl !== "") return artUrl;
}
// 3. Fallback for YouTube from xesam:url
if (player.metadata && player.metadata["xesam:url"]) {
const url = player.metadata["xesam:url"].toString();
if (url.includes("youtube.com") || url.includes("youtu.be")) {
const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
const match = url.match(regExp);
if (match && match[2].length === 11) {
return "https://img.youtube.com/vi/" + match[2] + "/hqdefault.jpg";
}
}
}
return "";
}
function _commit(u, artKey, srcUrl) {
resolvedArtUrl = u;
_committedArtKey = u !== "" ? artKey : "";
_committedSrcUrl = u !== "" ? srcUrl : "";
}
function loadArtwork(url, artKey, requestSerial) {
if (!url || url === "") {
// Keep stale art; only blank once the empty url debounce settles.
_lastArtUrl = "";
loading = false;
_clearDebounce.restart();
return;
}
_clearDebounce.stop();
if (url === _lastArtUrl)
return;
_lastArtUrl = url;
if (url.startsWith("http://") || url.startsWith("https://")) {
loading = true;
const targetUrl = url;
const hash = djb2Hash(url);
const cacheDir = Paths.strip(Paths.imagecache);
const filePath = cacheDir + "/remote_" + hash;
const localFileUrl = "file://" + filePath;
// 1. First, check if the file already exists locally
Proc.runCommand(null, ["test", "-f", filePath], (output, exitCode) => {
if (_lastArtUrl !== targetUrl || _requestSerial !== requestSerial)
return;
if (exitCode === 0) {
_commit(localFileUrl, artKey, targetUrl);
loading = false;
} else {
const dlCmd = "mkdir -p \"$(dirname \"$1\")\" && curl -f -s -L -o \"$1\" \"$2\" && mv \"$1\" \"$3\" || { rm -f \"$1\"; exit 1; }";
// 2. Check if this is a YouTube URL to do high quality 16:9 fallback
if (targetUrl.includes("img.youtube.com/vi/")) {
const videoId = targetUrl.split("/vi/")[1].split("/")[0];
const maxresUrl = "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg";
const mqUrl = "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg";
const tmpPath = filePath + ".tmp";
Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, maxresUrl, filePath], (maxOutput, maxExitCode) => {
if (_lastArtUrl !== targetUrl || _requestSerial !== requestSerial)
return;
if (maxExitCode === 0) {
_commit(localFileUrl, artKey, targetUrl);
loading = false;
} else {
Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, mqUrl, filePath], (mqOutput, mqExitCode) => {
if (_lastArtUrl !== targetUrl || _requestSerial !== requestSerial)
return;
_commit(mqExitCode === 0 ? localFileUrl : targetUrl, artKey, targetUrl);
loading = false;
}, 50, 15000);
}
}, 50, 15000);
} else {
// Standard curl download for other remote URLs (e.g. SoundCloud)
const tmpPath = filePath + ".tmp";
Proc.runCommand(null, ["sh", "-c", dlCmd, "sh", tmpPath, targetUrl, filePath], (dlOutput, dlExitCode) => {
if (_lastArtUrl !== targetUrl || _requestSerial !== requestSerial)
return;
_commit(dlExitCode === 0 ? localFileUrl : targetUrl, artKey, targetUrl);
loading = false;
}, 50, 15000);
}
}
}, 50, 5000);
return;
}
loading = true;
const localUrl = url;
const filePath = url.startsWith("file://") ? url.substring(7) : url;
const cacheDir = Paths.strip(Paths.imagecache);
// Cover lands after metadata, so poll; commit a content-addressed copy so identical bytes keep an identical url
const script = "f=\"$1\"; d=\"$2\"; i=0; while [ ! -f \"$f\" ] && [ \"$i\" -lt 20 ]; do sleep 0.15; i=$((i + 1)); done; [ -f \"$f\" ] || exit 1; s=$(sha1sum \"$f\" | cut -c1-40); if [ ! -f \"$d/art_$s\" ]; then mkdir -p \"$d\" && cp \"$f\" \"$d/art_$s.tmp\" && mv \"$d/art_$s.tmp\" \"$d/art_$s\" || exit 1; fi; echo \"$s\"";
Proc.runCommand(null, ["sh", "-c", script, "sh", filePath, cacheDir], (output, exitCode) => {
if (_lastArtUrl !== localUrl || _requestSerial !== requestSerial)
return;
loading = false;
if (exitCode !== 0)
return;
const sha = (output || "").trim();
if (_artHashDenylist.indexOf(sha) !== -1)
return;
_commit("file://" + cacheDir + "/art_" + sha, artKey, localUrl);
}, 50, 5000);
}
Timer {
id: _clearDebounce
interval: 800
onTriggered: {
if (root._lastArtUrl === "")
root._commit("", "", "");
}
}
property MprisPlayer activePlayer: MprisController.activePlayer
property string _committedArtKey: ""
property string _pendingArtKey: ""
property string _committedSrcUrl: ""
property int _requestSerial: 0
onActivePlayerChanged: _updateArtUrl()
Connections {
target: root.activePlayer
ignoreUnknownSignals: true
function onTrackTitleChanged() { root._updateArtUrl(); }
function onTrackArtUrlChanged() { root._updateArtUrl(); }
function onMetadataChanged() { root._updateArtUrl(); }
}
function _trackKey() {
const p = activePlayer;
if (!p)
return "";
// Scope artwork ownership to the MPRIS object and track.
const playerId = p.uniqueId || p.identity || "";
const tid = p.metadata && p.metadata["mpris:trackid"] ? p.metadata["mpris:trackid"].toString() : "";
return playerId + " " + tid + " " + (p.trackTitle || "") + " " + (p.trackArtist || "");
}
function artReadyFor(player) {
const url = getArtworkUrl(player);
return url !== "" && url === _lastArtUrl && !loading && resolvedArtUrl !== "";
}
function _updateArtUrl() {
const key = _trackKey();
if (key !== _pendingArtKey) {
_requestSerial++;
loading = false;
}
_pendingArtKey = key;
const url = getArtworkUrl(activePlayer);
// Ignore Chrome's same-track thumbnail size updates.
if (key !== "" && key === _committedArtKey)
return;
if (key !== "" && url !== "" && url === _committedSrcUrl) {
// Chrome can publish track metadata before its new artwork URL.
return;
}
loadArtwork(url, key, _requestSerial);
}
}