2
This commit is contained in:
@ -0,0 +1,669 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
const scriptDir = "$HOME/.local/share/plasma/plasmoids/com.github.exequtic.apdatifier/contents/tools/sh/"
|
||||
const configDir = "$HOME/.config/apdatifier/"
|
||||
const configFile = configDir + "config.conf"
|
||||
const cacheFile = configDir + "updates.json"
|
||||
const rulesFile = configDir + "rules.json"
|
||||
const newsFile = configDir + "news.json"
|
||||
|
||||
function execute(command, callback, stoppable) {
|
||||
const component = Qt.createComponent("../ui/components/Shell.qml")
|
||||
if (component.status === Component.Ready) {
|
||||
const componentObject = component.createObject(root)
|
||||
if (componentObject) {
|
||||
if (stoppable) check = componentObject
|
||||
componentObject.exec(command, callback)
|
||||
} else {
|
||||
Error(1, "Failed to create executable DataSource object")
|
||||
}
|
||||
} else {
|
||||
Error(1, "Executable DataSource component not ready")
|
||||
}
|
||||
}
|
||||
|
||||
const readFile = (file) => `[ -f "${file}" ] && cat "${file}"`
|
||||
const writeFile = (data, redir, file) => `echo '${data}' ${redir} "${file}"`
|
||||
|
||||
const bash = (script, ...args) => scriptDir + script + ' ' + args.join(' ')
|
||||
const runInTerminal = (script, ...args) => execute('kstart ' + bash('terminal', script, ...args))
|
||||
|
||||
const debug = true
|
||||
function log(message) {
|
||||
if (debug) console.log("[" + new Date().getTime().toString() + "] "+ "APDATIFIER: " + message)
|
||||
}
|
||||
|
||||
function Error(code, err) {
|
||||
if (err) {
|
||||
cfg.notifyErrors && notify.send("error", i18n("Exit code: ") + code, err.trim())
|
||||
sts.errMsg = err.trim().substring(0, 150) + "..."
|
||||
setStatusBar(code)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function init() {
|
||||
execute(bash('init'), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
loadConfig()
|
||||
})
|
||||
|
||||
function loadConfig() {
|
||||
execute(readFile(configFile), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (out) {
|
||||
const config = out.trim().split("\n")
|
||||
const convert = value => {
|
||||
if (!isNaN(parseFloat(value))) return parseFloat(value)
|
||||
if (value === "true" || value === "false") return value === 'true'
|
||||
return value
|
||||
}
|
||||
config.forEach(line => {
|
||||
const match = line.match(/(\w+)="([^"]*)"/)
|
||||
if (match) plasmoid.configuration[match[1]] = convert(match[2])
|
||||
})
|
||||
}
|
||||
|
||||
loadCache()
|
||||
})
|
||||
}
|
||||
|
||||
function loadCache() {
|
||||
execute(readFile(cacheFile), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (out && validJSON(out, cacheFile)) cache = keys(JSON.parse(out.trim()))
|
||||
loadRules()
|
||||
})
|
||||
}
|
||||
|
||||
function loadRules() {
|
||||
execute(readFile(rulesFile), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (out && validJSON(out, rulesFile)) plasmoid.configuration.rules = out
|
||||
loadNews()
|
||||
})
|
||||
}
|
||||
|
||||
function loadNews() {
|
||||
execute(readFile(newsFile), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (out && validJSON(out, newsFile)) JSON.parse(out.trim()).forEach(item => newsModel.append(item))
|
||||
onStartup()
|
||||
})
|
||||
}
|
||||
|
||||
function onStartup() {
|
||||
checkDependencies()
|
||||
refreshListModel()
|
||||
updateActiveNews()
|
||||
upgradingState(true)
|
||||
}
|
||||
}
|
||||
|
||||
function saveConfig() {
|
||||
if (saveTimer.running) return
|
||||
let config = ""
|
||||
Object.keys(cfg).forEach(key => {
|
||||
if (key.endsWith("Default")) {
|
||||
let name = key.slice(0, -7)
|
||||
config += `${name}="${cfg[name]}"\n`
|
||||
}
|
||||
})
|
||||
execute(writeFile(config, ">", configFile))
|
||||
}
|
||||
|
||||
function checkDependencies() {
|
||||
const pkgs = "pacman checkupdates flatpak paru yay jq tmux alacritty foot ghostty gnome-terminal kitty konsole lxterminal ptyxis terminator tilix wezterm xterm yakuake"
|
||||
const checkPkg = (pkgs) => `for pkg in ${pkgs}; do command -v $pkg || echo; done`
|
||||
const populate = (data) => data.map(item => ({ "name": item.split("/").pop(), "value": item }))
|
||||
|
||||
execute(checkPkg(pkgs), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
|
||||
const output = out.split("\n")
|
||||
|
||||
const [pacman, checkupdates, flatpak, paru, yay, jq, tmux ] = output.map(Boolean)
|
||||
cfg.packages = { pacman, checkupdates, flatpak, paru, yay, jq, tmux }
|
||||
if (!cfg.wrapper) cfg.wrapper = paru ? "paru" : yay ? "yay" : ""
|
||||
|
||||
const terminals = populate(output.slice(7).filter(Boolean))
|
||||
cfg.terminals = terminals.length > 0 ? terminals : null
|
||||
if (!cfg.terminal) cfg.terminal = cfg.terminals.length > 0 ? cfg.terminals[0].value : ""
|
||||
|
||||
if (!pacman) plasmoid.configuration.arch = false
|
||||
if (!pacman || (!yay && !paru)) plasmoid.configuration.aur = false
|
||||
if (!flatpak) plasmoid.configuration.flatpak = false
|
||||
if (!checkupdates) plasmoid.configuration.mirrors = "false"
|
||||
if (!tmux) plasmoid.configuration.tmuxSession = false
|
||||
if (!jq) {
|
||||
plasmoid.configuration.widgets = false
|
||||
plasmoid.configuration.newsArch = false
|
||||
plasmoid.configuration.newsKDE = false
|
||||
plasmoid.configuration.newsTWIK = false
|
||||
plasmoid.configuration.newsTWIKA = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function upgradePackage(name, appID, contentID) {
|
||||
if (sts.upgrading) return
|
||||
enableUpgrading(true)
|
||||
|
||||
if (appID) {
|
||||
runInTerminal("upgrade", "flatpak", appID, name)
|
||||
} else if (contentID) {
|
||||
runInTerminal("upgrade", "widget", contentID, name)
|
||||
}
|
||||
}
|
||||
|
||||
function management() {
|
||||
runInTerminal("management")
|
||||
}
|
||||
|
||||
|
||||
function enableUpgrading(state) {
|
||||
sts.busy = sts.upgrading = state
|
||||
if (state) {
|
||||
if (upgradeTimer.running) return
|
||||
upgradeTimer.start()
|
||||
searchTimer.stop()
|
||||
sts.statusMsg = i18n("Upgrade in progress") + "..."
|
||||
sts.statusIco = cfg.ownIconsUI ? "toolbar_upgrade" : "akonadiconsole"
|
||||
} else {
|
||||
upgradeTimer.stop()
|
||||
setStatusBar()
|
||||
}
|
||||
}
|
||||
|
||||
function upgradingState(startup) {
|
||||
execute(`ps aux | grep "[a]pdatifier/contents/tools/sh/upgrade"`, (cmd, out, err, code) => {
|
||||
if (out || err) {
|
||||
enableUpgrading(true)
|
||||
} else if (startup) {
|
||||
if (!cfg.interval) return
|
||||
cfg.checkOnStartup ? searchTimer.triggered() : searchTimer.start()
|
||||
} else {
|
||||
enableUpgrading(false)
|
||||
execute(bash('upgrade', "postUpgrade"), (cmd, out, err, code) => postUpgrade(out))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function postUpgrade(out) {
|
||||
const newList = cache.filter(cached => {
|
||||
const current = JSON.parse(out).find(current => current.NM.replace(/ /g, "-").toLowerCase() === cached.NM)
|
||||
return current && current.VO === cached.VO + cached.AC
|
||||
})
|
||||
if (JSON.stringify(cache) !== JSON.stringify(newList)) {
|
||||
cache = newList
|
||||
refreshListModel()
|
||||
saveCache(cache)
|
||||
}
|
||||
}
|
||||
|
||||
function upgradeSystem() {
|
||||
if (sts.upgrading && !cfg.tmuxSession) return
|
||||
enableUpgrading(true)
|
||||
runInTerminal("upgrade", "full")
|
||||
}
|
||||
|
||||
|
||||
function checkUpdates() {
|
||||
if (sts.upgrading) return
|
||||
if (sts.busy) {
|
||||
check.cleanup()
|
||||
setStatusBar()
|
||||
return
|
||||
}
|
||||
|
||||
searchTimer.stop()
|
||||
sts.busy = true
|
||||
sts.errMsg = ""
|
||||
|
||||
const dbPath = pkg.checkupdates ? " --dbpath /tmp/apdatifier-db" : ""
|
||||
const pkgsync = "pacman -Sl" + dbPath
|
||||
const pkginfo = "pacman -Qi" + dbPath
|
||||
const pkgfiles = "pacman -Ql" + dbPath
|
||||
|
||||
const checkupDB = "export CHECKUPDATES_DB=/tmp/apdatifier-db; checkupdates"
|
||||
const checkupAUR = `${cfg.wrapper} -Qua` + dbPath
|
||||
|
||||
let arch = [], flatpak = [], widgets = []
|
||||
|
||||
const archCmd =
|
||||
!pkg.pacman || !cfg.arch ? false
|
||||
: pkg.checkupdates
|
||||
? cfg.aur ? `${checkupDB}; ${checkupAUR}` : `${checkupDB}`
|
||||
: cfg.aur ? `${cfg.wrapper} -Qu` : "pacman -Qu"
|
||||
|
||||
const feeds = [
|
||||
cfg.newsArch && "'https://archlinux.org/feeds/news/'",
|
||||
cfg.newsKDE && "'https://kde.org/index.xml'",
|
||||
cfg.newsTWIK && "'https://blogs.kde.org/categories/this-week-in-plasma/index.xml'",
|
||||
cfg.newsTWIKA && "'https://blogs.kde.org/categories/this-week-in-kde-apps/index.xml'"
|
||||
].filter(Boolean).join(' ')
|
||||
|
||||
feeds ? checkNews() :
|
||||
archCmd ? checkArch() :
|
||||
cfg.flatpak ? checkFlatpak() :
|
||||
cfg.widgets ? checkWidgets() :
|
||||
merge()
|
||||
|
||||
function checkNews() {
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_news" : "news-subscribe"
|
||||
sts.statusMsg = i18n("Checking latest news...")
|
||||
|
||||
execute(bash('utils', 'rss', feeds), (cmd, out, err, code) => {
|
||||
if (code) {
|
||||
cfg.notifyErrors && notify.send("error", i18n("Cannot fetch news "), out)
|
||||
} else {
|
||||
if (out) updateNews(out)
|
||||
}
|
||||
|
||||
archCmd ? checkArch() : cfg.flatpak ? checkFlatpak() : cfg.widgets ? checkWidgets() : merge()
|
||||
}, true )
|
||||
}
|
||||
|
||||
function checkArch() {
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_package" : "apdatifier-package"
|
||||
sts.statusMsg = i18n("Checking system updates...")
|
||||
|
||||
execute(archCmd, (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
out ? allArch(out.split("\n")) : cfg.flatpak ? checkFlatpak() : cfg.widgets ? checkWidgets() : merge()
|
||||
}, true )
|
||||
}
|
||||
|
||||
function allArch(upd) {
|
||||
execute(pkgsync, (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
descArch(upd, out.split("\n").filter(line => /\[.*\]/.test(line)))
|
||||
}, true )
|
||||
}
|
||||
|
||||
function descArch(upd, all) {
|
||||
const pkgs = upd.map(l => l.split(" ")[0]).join(' ')
|
||||
execute(`${pkginfo} ${pkgs}`, (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
iconsArch(upd, all, out, pkgs)
|
||||
}, true )
|
||||
}
|
||||
|
||||
function iconsArch(upd, all, desc, pkgs) {
|
||||
const getIcons = `\
|
||||
while read -r pkg file; do
|
||||
[[ "$processed" == *"$pkg"* ]] && continue
|
||||
icon=$(awk -F= '/^Icon=/ {print $2; exit}' "$file" 2>/dev/null || true) && [ -n "$icon" ] || continue
|
||||
processed="$processed $pkg"
|
||||
echo "$pkg $icon"
|
||||
done < <(${pkgfiles} ${pkgs} | grep '/usr/share/applications/.*\.desktop$')`
|
||||
|
||||
execute(getIcons, (cmd, out, err, code) => {
|
||||
const icons = (out && !err) ? out.split('\n').map(l => ({ NM: l.split(' ')[0], IN: l.split(' ')[1] })) : []
|
||||
arch = makeArchList(upd, all, desc, icons)
|
||||
cfg.flatpak ? checkFlatpak() : cfg.widgets ? checkWidgets() : merge()
|
||||
}, true )
|
||||
}
|
||||
|
||||
function checkFlatpak() {
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_flatpak" : "apdatifier-flatpak"
|
||||
sts.statusMsg = i18n("Checking flatpak updates...")
|
||||
execute("flatpak update --appstream >/dev/null 2>&1; flatpak remote-ls --app --updates --show-details", (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
out ? descFlatpak(out.trim()) : cfg.widgets ? checkWidgets() : merge()
|
||||
}, true )
|
||||
}
|
||||
|
||||
function descFlatpak(upd) {
|
||||
execute("flatpak list --app --columns=application,version,active", (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
flatpak = out ? makeFlatpakList(upd, out.trim()) : []
|
||||
cfg.widgets ? checkWidgets() : merge()
|
||||
}, true )
|
||||
}
|
||||
|
||||
function checkWidgets() {
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_widgets" : "start-here-kde-plasma-symbolic"
|
||||
sts.statusMsg = i18n("Checking widgets updates...")
|
||||
|
||||
execute(bash('widgets', 'check'), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
out = out.trim()
|
||||
|
||||
const errorTexts = {
|
||||
"127": i18n("Unable check widgets: ") + i18n("Required installed") + " jq",
|
||||
"1": i18n("Unable check widgets: ") + i18n("Failed to retrieve data from the API"),
|
||||
"2": i18n("Unable check widgets: ") + i18n("Too many API requests in the last 15 minutes from your IP address, please try again later"),
|
||||
"3": i18n("Unable check widgets: ") + i18n("Unkwnown error")
|
||||
}
|
||||
|
||||
if (out in errorTexts) {
|
||||
Error(out, errorTexts[out])
|
||||
return
|
||||
}
|
||||
|
||||
widgets = JSON.parse(out)
|
||||
merge()
|
||||
}, true )
|
||||
}
|
||||
|
||||
function merge() {
|
||||
finalize(keys(arch.concat(flatpak, widgets)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateNews(out) {
|
||||
const news = JSON.parse(out.trim())
|
||||
|
||||
if (cfg.notifyNews) {
|
||||
const currentNews = Array.from(Array(newsModel.count), (_, i) => newsModel.get(i))
|
||||
news.forEach(item => {
|
||||
if (!currentNews.some(currentItem => currentItem.link === item.link)) {
|
||||
notify.send("news", item.title, item.article, item.link)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
newsModel.clear()
|
||||
news.forEach(item => newsModel.append(item))
|
||||
updateActiveNews()
|
||||
}
|
||||
function updateActiveNews() {
|
||||
const activeItems = Array.from({ length: newsModel.count }, (_, i) => newsModel.get(i)).filter(item => !item.removed)
|
||||
activeNewsModel.clear()
|
||||
activeItems.forEach(item => activeNewsModel.append(item))
|
||||
}
|
||||
function removeNewsItem(index) {
|
||||
for (let i = 0; i < newsModel.count; i++) {
|
||||
if (newsModel.get(i).link === activeNewsModel.get(index).link) {
|
||||
newsModel.setProperty(i, "removed", true)
|
||||
activeNewsModel.remove(index)
|
||||
break
|
||||
}
|
||||
}
|
||||
let array = Array.from(Array(newsModel.count), (_, i) => newsModel.get(i))
|
||||
execute(writeFile(toFileFormat(array), '>', newsFile))
|
||||
}
|
||||
function restoreNewsList() {
|
||||
let array = []
|
||||
for (let i = 0; i < newsModel.count; i++) {
|
||||
newsModel.setProperty(i, "removed", false)
|
||||
array.push(newsModel.get(i))
|
||||
}
|
||||
execute(writeFile(toFileFormat(array), '>', newsFile))
|
||||
updateActiveNews()
|
||||
}
|
||||
|
||||
|
||||
function makeArchList(updates, all, description, icons) {
|
||||
if (!updates || !all || !description) return []
|
||||
description = description.replace(/^Installed From\s*:.+\n?/gm, '')
|
||||
const packagesData = description.split("\n\n")
|
||||
const skip = new Set([1, 3, 5, 9, 11, 15, 16, 19, 20])
|
||||
const empty = new Set([6, 7, 8, 10, 12, 13])
|
||||
const keyNames = {
|
||||
0: "NM", 2: "DE", 4: "LN", 6: "GR", 7: "PR", 8: "DP",
|
||||
10: "RQ", 12: "CF", 13: "RP", 14: "IS", 17: "DT", 18: "RN"
|
||||
}
|
||||
|
||||
let extendedList = packagesData.map(packageData => {
|
||||
packageData = packageData.split('\n').filter(line => line.includes(" : "))
|
||||
let packageObj = {}
|
||||
packageData.forEach((line, index) => {
|
||||
if (skip.has(index)) return
|
||||
const [, value] = line.split(/\s* : \s*/)
|
||||
if (empty.has(index) && value.charAt(0) === value.charAt(0).toUpperCase()) return
|
||||
if (keyNames[index]) packageObj[keyNames[index]] = value.trim()
|
||||
})
|
||||
|
||||
if (Object.keys(packageObj).length > 0) {
|
||||
updates.forEach(str => {
|
||||
const [name, verold, , vernew] = str.split(" ")
|
||||
if (packageObj.NM === name) {
|
||||
const verNew = (vernew === "latest-commit") ? i18n("latest commit") : vernew
|
||||
Object.assign(packageObj, { VO: verold, VN: verNew })
|
||||
}
|
||||
})
|
||||
|
||||
const foundRepo = all.find(str => packageObj.NM === str.split(" ")[1])
|
||||
packageObj.RE = foundRepo ? foundRepo.split(" ")[0] : (packageObj.NM.endsWith("-git") || packageObj.VN === i18n("latest commit") ? "devel" : "aur")
|
||||
|
||||
const foundIcon = icons.find(item => item.NM === packageObj.NM)
|
||||
if (foundIcon) packageObj.IN = foundIcon.IN
|
||||
}
|
||||
|
||||
return packageObj
|
||||
})
|
||||
|
||||
extendedList.pop()
|
||||
return extendedList
|
||||
}
|
||||
|
||||
|
||||
function makeFlatpakList(updates, description) {
|
||||
if (!updates || !description) return []
|
||||
const list = description.split("\n").reduce((obj, line) => {
|
||||
const [ID, VO, AC] = line.split("\t").map(entry => entry.trim())
|
||||
obj[ID] = { VO, AC }
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
return updates.split("\n").map(line => {
|
||||
const [NM, DE, ID, VN, BR, , RE, , CM, RT, IS, DS] = line.split("\t").map(entry => entry.trim())
|
||||
const { VO, AC } = list[ID]
|
||||
return {
|
||||
NM: NM.replace(/ /g, "-").toLowerCase(),
|
||||
DE, LN: "https://flathub.org/apps/" + ID,
|
||||
ID, BR, RE, AC, CM, RT, IS, DS, VO,
|
||||
VN: VO === VN ? i18n("latest commit") : VN
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function sortList(list, byName) {
|
||||
if (!list) return
|
||||
|
||||
return list.sort((a, b) => {
|
||||
const name = a.NM.localeCompare(b.NM)
|
||||
const repo = a.RE.localeCompare(b.RE)
|
||||
if (byName || !cfg.sorting) return name
|
||||
|
||||
if (a.IM !== b.IM) return a.IM ? -1 : 1
|
||||
|
||||
const develA = a.RE.includes("devel")
|
||||
const develB = b.RE.includes("devel")
|
||||
if (develA !== develB) return develA ? -1 : 1
|
||||
|
||||
const aurA = a.RE.includes("aur")
|
||||
const aurB = b.RE.includes("aur")
|
||||
if (aurA !== aurB) return aurA ? -1 : 1
|
||||
|
||||
return repo || name
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function refreshListModel(list) {
|
||||
list = sortList(applyRules(list || cache)) || []
|
||||
sts.count = list.length || 0
|
||||
setStatusBar()
|
||||
|
||||
if (!list) return
|
||||
|
||||
listModel.clear()
|
||||
list.forEach(item => listModel.append(item))
|
||||
}
|
||||
|
||||
|
||||
function finalize(list) {
|
||||
cfg.timestamp = new Date().getTime().toString()
|
||||
|
||||
if (!list) {
|
||||
listModel.clear()
|
||||
execute(writeFile("[]", '>', cacheFile))
|
||||
cache = []
|
||||
sts.count = 0
|
||||
setStatusBar()
|
||||
return
|
||||
}
|
||||
|
||||
refreshListModel(list)
|
||||
|
||||
if (cfg.notifyUpdates) {
|
||||
const cached = new Map(cache.map(el => [el.NM, el.VN]))
|
||||
const newList = applyRules(list).filter(el => !cached.has(el.NM) || (cfg.notifyEveryBump && cached.get(el.NM) !== el.VN))
|
||||
|
||||
if (newList.length > 0) {
|
||||
const title = i18np("+%1 new update", "+%1 new updates", newList.length)
|
||||
const body = newList.map(pkg => `${pkg.NM} → ${pkg.VN}`).join("\n")
|
||||
notify.send("updates", title, body)
|
||||
}
|
||||
}
|
||||
|
||||
cache = list
|
||||
saveCache(cache)
|
||||
}
|
||||
|
||||
function saveCache(list) {
|
||||
if (JSON.stringify(list).length > 130000) {
|
||||
let start = 0
|
||||
const chunkSize = 200
|
||||
const json = JSON.stringify(keys(sortList(JSON.parse(JSON.stringify(list)), true))).replace(/},/g, "},\n").replace(/'/g, "")
|
||||
const lines = json.split("\n")
|
||||
while (start < lines.length) {
|
||||
const chunk = lines.slice(start, start + chunkSize).join("\n")
|
||||
const redir = start === 0 ? ">" : ">>"
|
||||
execute(writeFile(chunk, redir, `${cacheFile}_${Math.ceil(start / chunkSize)}`))
|
||||
start += chunkSize
|
||||
}
|
||||
execute(bash('utils', 'combineFiles', cacheFile))
|
||||
} else {
|
||||
const json = toFileFormat(keys(sortList(JSON.parse(JSON.stringify(list)), true)))
|
||||
execute(writeFile(json, '>', cacheFile))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setStatusBar(code) {
|
||||
sts.statusIco = sts.err ? "0" : sts.count > 0 ? "1" : "2"
|
||||
sts.statusMsg = sts.err ? "Exit code: " + code : sts.count > 0 ? sts.count + " " + i18np("update is pending", "updates are pending", sts.count) : ""
|
||||
sts.busy = false
|
||||
!cfg.interval ? searchTimer.stop() : searchTimer.restart()
|
||||
}
|
||||
|
||||
|
||||
function getLastCheckTime() {
|
||||
if (!cfg.timestamp) return ""
|
||||
|
||||
const diff = new Date().getTime() - parseInt(cfg.timestamp)
|
||||
const sec = Math.round((diff / 1000) % 60)
|
||||
const min = Math.floor((diff / (1000 * 60)) % 60)
|
||||
const hrs = Math.floor(diff / (1000 * 60 * 60))
|
||||
|
||||
const lastcheck = i18n("Last check:")
|
||||
const second = i18np("%1 second", "%1 seconds", sec)
|
||||
const minute = i18np("%1 minute", "%1 minutes", min)
|
||||
const hour = i18np("%1 hour", "%1 hours", hrs)
|
||||
const ago = i18n("ago")
|
||||
|
||||
if (hrs === 0 && min === 0) return `${lastcheck} ${second} ${ago}`
|
||||
if (hrs === 0) return `${lastcheck} ${minute} ${second} ${ago}`
|
||||
if (min === 0) return `${lastcheck} ${hour} ${ago}`
|
||||
return `${lastcheck} ${hour} ${minute} ${ago}`
|
||||
}
|
||||
|
||||
|
||||
function setIndex(value, arr) {
|
||||
let index = 0
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]["value"] == value) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
const defaultIcon = "apdatifier-plasmoid"
|
||||
function setIcon(icon) {
|
||||
return icon === "" ? defaultIcon : icon
|
||||
}
|
||||
|
||||
|
||||
function applyRules(list) {
|
||||
const rules = !cfg.rules ? [] : JSON.parse(cfg.rules)
|
||||
|
||||
list.forEach(el => {
|
||||
el.IC = el.IN ? el.IN : el.ID ? el.ID : "apdatifier-package"
|
||||
el.EX = false
|
||||
el.IM = false
|
||||
})
|
||||
|
||||
function applyRule(el, rule) {
|
||||
const types = {
|
||||
'all' : () => true,
|
||||
'repo' : () => el.RE === rule.value,
|
||||
'group' : () => el.GR.includes(rule.value),
|
||||
'match' : () => el.NM.includes(rule.value),
|
||||
'name' : () => el.NM === rule.value
|
||||
}
|
||||
|
||||
if (types[rule.type]()) {
|
||||
el.IC = rule.icon
|
||||
el.EX = rule.excluded
|
||||
el.IM = rule.important
|
||||
}
|
||||
}
|
||||
|
||||
rules.forEach(rule => list.forEach(el => applyRule(el, rule)))
|
||||
return list.filter(el => !el.EX)
|
||||
}
|
||||
|
||||
|
||||
function keys(list) {
|
||||
const keysList = ["GR", "PR", "DP", "RQ", "CF", "RP", "IS", "DT", "RN", "ID", "BR", "AC", "CM", "RT", "DS", "CN", "AU"]
|
||||
|
||||
list.forEach(el => {
|
||||
keysList.forEach(key => {
|
||||
if (!el.hasOwnProperty(key)) el[key] = ""
|
||||
else if (el[key] === "") delete el[key]
|
||||
})
|
||||
|
||||
if (el.hasOwnProperty("IC")) delete el["IC"]
|
||||
if (el.hasOwnProperty("EX")) delete el["EX"]
|
||||
if (el.hasOwnProperty("IM")) delete el["IM"]
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
function switchInterval() {
|
||||
cfg.interval = !cfg.interval
|
||||
}
|
||||
|
||||
function toFileFormat(obj) {
|
||||
const jsonStringWithSpace = JSON.stringify(obj, null, 2)
|
||||
const writebleJsonStrings = jsonStringWithSpace.replace(/'/g, "")
|
||||
return writebleJsonStrings
|
||||
}
|
||||
|
||||
function validJSON(string, file) {
|
||||
try {
|
||||
const json = JSON.parse(string)
|
||||
if (json && typeof json === "object") return json
|
||||
}
|
||||
catch (e) {
|
||||
file ? Error(1, `JSON data at ${file} is corrupted or broken and cannot be processed`)
|
||||
: Error(1, "JSON data is broken and cannot be processed")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user