mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2025-12-06 06:36:15 +00:00
260 lines
7.8 KiB
QML
260 lines
7.8 KiB
QML
pragma Singleton
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import qs.Commons
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
// State
|
|
property bool fetching: false
|
|
property bool initialSearchScheduled: false
|
|
property var currentResults: []
|
|
property var currentMeta: ({})
|
|
property string lastError: ""
|
|
property string currentQuery: ""
|
|
property int currentPage: 1
|
|
property int lastPage: 1
|
|
|
|
// Search parameters
|
|
property string categories: "111" // general,anime,people (all enabled by default)
|
|
property string purity: "100" // sfw
|
|
property string sorting: "relevance" // date_added, relevance, random, views, favorites, toplist
|
|
property string order: "desc" // desc, asc
|
|
property string topRange: "1M" // 1d, 3d, 1w, 1M, 3M, 6M, 1y
|
|
property string seed: "" // For random sorting
|
|
property string minResolution: "" // e.g., "1920x1080"
|
|
property string resolutions: "" // e.g., "1920x1080,1920x1200"
|
|
property string ratios: "" // e.g., "16x9,16x10"
|
|
property string colors: "" // Color hex codes
|
|
|
|
// Signals
|
|
signal searchCompleted(var results, var meta)
|
|
signal searchFailed(string error)
|
|
signal wallpaperDownloaded(string wallpaperId, string localPath)
|
|
|
|
// Base API URL
|
|
readonly property string apiBaseUrl: "https://wallhaven.cc/api/v1"
|
|
|
|
// -------------------------------------------------
|
|
function search(query, page) {
|
|
if (fetching) {
|
|
return;
|
|
}
|
|
|
|
// Reset initial search flag once we start a search
|
|
if (initialSearchScheduled) {
|
|
initialSearchScheduled = false;
|
|
}
|
|
|
|
fetching = true;
|
|
lastError = "";
|
|
currentQuery = query || "";
|
|
currentPage = page || 1;
|
|
|
|
var url = apiBaseUrl + "/search";
|
|
var params = [];
|
|
|
|
if (currentQuery) {
|
|
params.push("q=" + encodeURIComponent(currentQuery));
|
|
}
|
|
|
|
params.push("categories=" + categories);
|
|
params.push("purity=" + purity);
|
|
params.push("sorting=" + sorting);
|
|
params.push("order=" + order);
|
|
|
|
if (sorting === "toplist") {
|
|
params.push("topRange=" + topRange);
|
|
}
|
|
|
|
if (sorting === "random" && seed) {
|
|
params.push("seed=" + seed);
|
|
}
|
|
|
|
if (minResolution) {
|
|
params.push("atleast=" + minResolution);
|
|
}
|
|
|
|
if (resolutions) {
|
|
params.push("resolutions=" + resolutions);
|
|
}
|
|
|
|
if (ratios) {
|
|
params.push("ratios=" + ratios);
|
|
}
|
|
|
|
if (colors) {
|
|
params.push("colors=" + colors);
|
|
}
|
|
|
|
params.push("page=" + currentPage);
|
|
|
|
url += "?" + params.join("&");
|
|
|
|
Logger.d("Wallhaven", "Searching:", url);
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.onreadystatechange = function () {
|
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
fetching = false;
|
|
if (xhr.status === 200) {
|
|
try {
|
|
var response = JSON.parse(xhr.responseText);
|
|
if (response.data && Array.isArray(response.data)) {
|
|
currentResults = response.data;
|
|
currentMeta = response.meta || {};
|
|
lastPage = currentMeta.last_page || 1;
|
|
|
|
// Store seed for random sorting
|
|
if (currentMeta.seed) {
|
|
seed = currentMeta.seed;
|
|
}
|
|
|
|
Logger.d("Wallhaven", "Search completed:", currentResults.length, "results, page", currentPage, "of", lastPage);
|
|
searchCompleted(currentResults, currentMeta);
|
|
} else {
|
|
var errorMsg = "Invalid API response";
|
|
lastError = errorMsg;
|
|
Logger.e("Wallhaven", errorMsg);
|
|
searchFailed(errorMsg);
|
|
}
|
|
} catch (e) {
|
|
var errorMsg = "Failed to parse API response: " + e.toString();
|
|
lastError = errorMsg;
|
|
Logger.e("Wallhaven", errorMsg);
|
|
searchFailed(errorMsg);
|
|
}
|
|
} else if (xhr.status === 429) {
|
|
var errorMsg = "Rate limit exceeded (45 requests/minute)";
|
|
lastError = errorMsg;
|
|
Logger.w("Wallhaven", errorMsg);
|
|
searchFailed(errorMsg);
|
|
} else {
|
|
var errorMsg = "API error: " + xhr.status;
|
|
lastError = errorMsg;
|
|
Logger.e("Wallhaven", "Search failed:", errorMsg);
|
|
searchFailed(errorMsg);
|
|
}
|
|
}
|
|
};
|
|
|
|
xhr.open("GET", url);
|
|
xhr.send();
|
|
}
|
|
|
|
// -------------------------------------------------
|
|
function getWallpaperUrl(wallpaper) {
|
|
// Use the 'path' field which contains the full resolution image URL
|
|
if (wallpaper.path) {
|
|
return wallpaper.path;
|
|
}
|
|
// Fallback to constructing URL from ID
|
|
if (wallpaper.id) {
|
|
var idPrefix = wallpaper.id.substring(0, 2);
|
|
return "https://w.wallhaven.cc/full/" + idPrefix + "/wallhaven-" + wallpaper.id + ".jpg";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// -------------------------------------------------
|
|
function getThumbnailUrl(wallpaper, size) {
|
|
// size: "small", "large", "original"
|
|
if (wallpaper.thumbs && wallpaper.thumbs[size]) {
|
|
return wallpaper.thumbs[size];
|
|
}
|
|
// Fallback
|
|
if (wallpaper.id) {
|
|
var idPrefix = wallpaper.id.substring(0, 2);
|
|
var sizeMap = {
|
|
"small": "small",
|
|
"large": "lg",
|
|
"original": "orig"
|
|
};
|
|
var sizePath = sizeMap[size] || "lg";
|
|
return "https://th.wallhaven.cc/" + sizePath + "/" + idPrefix + "/" + wallpaper.id + ".jpg";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// -------------------------------------------------
|
|
function downloadWallpaper(wallpaper, callback) {
|
|
var url = getWallpaperUrl(wallpaper);
|
|
if (!url) {
|
|
Logger.e("Wallhaven", "No URL available for wallpaper", wallpaper.id);
|
|
if (callback)
|
|
callback(false, "");
|
|
return;
|
|
}
|
|
|
|
var wallpaperId = wallpaper.id;
|
|
|
|
// Get the user's wallpaper directory
|
|
var wallpaperDir = Settings.preprocessPath(Settings.data.wallpaper.directory);
|
|
if (!wallpaperDir || wallpaperDir === "") {
|
|
wallpaperDir = Settings.defaultWallpapersDirectory;
|
|
}
|
|
|
|
// Ensure directory ends with /
|
|
if (!wallpaperDir.endsWith("/")) {
|
|
wallpaperDir += "/";
|
|
}
|
|
|
|
var localPath = wallpaperDir + "wallhaven_" + wallpaperId + ".jpg";
|
|
|
|
Logger.d("Wallhaven", "Downloading wallpaper", wallpaperId, "to", localPath);
|
|
|
|
// Use curl or wget to download the file, ensuring directory exists first
|
|
var downloadProcess = Qt.createQmlObject(`
|
|
import QtQuick
|
|
import Quickshell.Io
|
|
Process {
|
|
id: downloadProcess
|
|
command: ["sh", "-c", "mkdir -p '` + wallpaperDir + `' && (curl -L -s -o '` + localPath + `' '` + url + `' || wget -q -O '` + localPath + `' '` + url + `')"]
|
|
}
|
|
`, root, "DownloadProcess_" + wallpaperId);
|
|
|
|
downloadProcess.exited.connect(function (exitCode) {
|
|
if (exitCode === 0) {
|
|
Logger.i("Wallhaven", "Wallpaper downloaded:", localPath);
|
|
wallpaperDownloaded(wallpaperId, localPath);
|
|
if (callback)
|
|
callback(true, localPath);
|
|
} else {
|
|
Logger.e("Wallhaven", "Failed to download wallpaper, exit code:", exitCode);
|
|
if (callback)
|
|
callback(false, "");
|
|
}
|
|
downloadProcess.destroy();
|
|
});
|
|
|
|
downloadProcess.running = true;
|
|
}
|
|
|
|
// -------------------------------------------------
|
|
function reset() {
|
|
currentResults = [];
|
|
currentMeta = {};
|
|
currentQuery = "";
|
|
currentPage = 1;
|
|
lastPage = 1;
|
|
seed = "";
|
|
lastError = "";
|
|
}
|
|
|
|
// -------------------------------------------------
|
|
function nextPage() {
|
|
if (currentPage < lastPage && !fetching) {
|
|
search(currentQuery, currentPage + 1);
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------
|
|
function previousPage() {
|
|
if (currentPage > 1 && !fetching) {
|
|
search(currentQuery, currentPage - 1);
|
|
}
|
|
}
|
|
}
|