mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-06-05 11:40:00 +00:00
WallpaperPanel: add wallhaven support
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
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: "date_added" // 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.i("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.e("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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user