2
This commit is contained in:
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configDir=$HOME/.config/apdatifier
|
||||
config=$configDir/config.conf
|
||||
test -d $configDir || mkdir -p $configDir
|
||||
test -f $config || touch $config
|
||||
|
||||
source $(dirname "$0")/vars
|
||||
|
||||
for dir in $iconsDir $notifDir; do
|
||||
test -d $dir || mkdir -p $dir
|
||||
done
|
||||
|
||||
for icon in $icon1 $icon2 $icon3 $icon4; do
|
||||
test -f $iconsDir/$icon || cp $appletDir/contents/ui/assets/icons/$icon $iconsDir
|
||||
done
|
||||
|
||||
test -d $notifDir && cat > $notifDir/$notif << EOF
|
||||
[Global]
|
||||
IconName=apdatifier-plasmoid
|
||||
Comment=Apdatifier
|
||||
|
||||
[Event/updates]
|
||||
Name=New updates
|
||||
Comment=Event when updates notification enabled without sound
|
||||
Action=Popup
|
||||
|
||||
[Event/updatesSound]
|
||||
Name=New updates (with sound)
|
||||
Comment=Event when updates notification enabled with sound
|
||||
Action=Popup|Sound
|
||||
Sound=service-login
|
||||
|
||||
[Event/error]
|
||||
Name=Error
|
||||
Comment=Event when error notification enabled without sound
|
||||
Action=Popup
|
||||
|
||||
[Event/errorSound]
|
||||
Name=Error (with sound)
|
||||
Comment=Event when errors notification enabled with sound
|
||||
Action=Popup|Sound
|
||||
Sound=dialog-error-serious
|
||||
|
||||
[Event/news]
|
||||
Name=News
|
||||
Comment=Event when news notification without sound
|
||||
Action=Popup
|
||||
|
||||
[Event/newsSound]
|
||||
Name=News (with sound)
|
||||
Comment=Event when news notification with sound
|
||||
Action=Popup|Sound
|
||||
Sound=dialog-information
|
||||
EOF
|
||||
@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
trap '' SIGINT
|
||||
|
||||
[ "$1" ] && selected="$1" || selected=0
|
||||
|
||||
[[ $aur != true || -z $wrapper ]] && wrapper="pacman"
|
||||
wrapper="${wrapper##*/}"; wrapper_sudo=$wrapper
|
||||
[[ $wrapper = "pacman" ]] && wrapper_sudo="$sudoBin pacman"
|
||||
|
||||
returnMenu() {
|
||||
printReturn
|
||||
showOptions $selected
|
||||
exit
|
||||
}
|
||||
|
||||
options=(
|
||||
"${ICO_MNG_OPT_01}${MNG_OPT_01}"
|
||||
"${ICO_MNG_OPT_02}${MNG_OPT_02}"
|
||||
"${ICO_MNG_OPT_03}${MNG_OPT_03}"
|
||||
"${ICO_MNG_OPT_04}${MNG_OPT_04}"
|
||||
"${ICO_MNG_OPT_05}${MNG_OPT_05}"
|
||||
"${ICO_MNG_OPT_06}${MNG_OPT_06}"
|
||||
"${ICO_MNG_OPT_07}${MNG_OPT_07}"
|
||||
"${ICO_MNG_OPT_08}${MNG_OPT_08}"
|
||||
"${ICO_MNG_OPT_09}${MNG_OPT_09}"
|
||||
"${ICO_MNG_OPT_10}${MNG_OPT_10}"
|
||||
"${ICO_MNG_OPT_11}${MNG_OPT_11}"
|
||||
"${ICO_MNG_OPT_12}${MNG_OPT_12}"
|
||||
)
|
||||
|
||||
showOptions() {
|
||||
while true; do
|
||||
clear; tput civis
|
||||
for i in "${!options[@]}"; do
|
||||
if [[ $i -eq $selected ]]; then
|
||||
echo -e "$(colorize red bold $ICO_SELECT) $(colorize green bold ${options[$i]})"
|
||||
else
|
||||
echo -e " ${options[$i]}"
|
||||
fi
|
||||
done
|
||||
|
||||
read -rsn1 input
|
||||
case $input in
|
||||
A) ((selected--));;
|
||||
B) ((selected++));;
|
||||
"") break;;
|
||||
esac
|
||||
|
||||
if [[ $selected -lt 0 ]]; then
|
||||
selected=$(( ${#options[@]} - 1 ))
|
||||
elif [ $selected -ge ${#options[@]} ]; then
|
||||
selected=0
|
||||
fi
|
||||
done
|
||||
|
||||
clear; tput cnorm
|
||||
|
||||
case $selected in
|
||||
0) fzfPreview Slq;;
|
||||
1) fzfPreview Qq;;
|
||||
2) fzfPreview Qqe;;
|
||||
3) fzfPreview Qqet;;
|
||||
4) fzfPreview Qqtd;;
|
||||
5) uninstallOrphans;;
|
||||
6) downgradePackage;;
|
||||
7) printExec -Scc; $wrapper_sudo -Scc; returnMenu;;
|
||||
8) printExec -Sc; $wrapper_sudo -Sc; returnMenu;;
|
||||
9) rebuildPython;;
|
||||
10) $scriptDir/mirrorlist true $selected;;
|
||||
11) exit;;
|
||||
esac
|
||||
}
|
||||
|
||||
fzfPreview() {
|
||||
dependencies "fzf" true
|
||||
|
||||
case $1 in
|
||||
Slq) fzfExec -$1 -Si -Syu;;
|
||||
*) fzfExec -$1 -Qil -Rsn;;
|
||||
esac
|
||||
}
|
||||
|
||||
fzfExec() {
|
||||
packages=$($wrapper $1 | fzf $fzf_settings --preview "$wrapper $2 {}")
|
||||
if [[ -z "$packages" ]]; then
|
||||
showOptions $selected
|
||||
else
|
||||
packages=$(echo "$packages" | oneLine)
|
||||
printExec $3 "$packages"
|
||||
$wrapper_sudo $3 $packages
|
||||
returnMenu
|
||||
fi
|
||||
}
|
||||
|
||||
uninstallOrphans() {
|
||||
if [[ -n $($wrapper -Qdt) ]]; then
|
||||
printExec -Rsn "$($wrapper -Qqtd | oneLine)"
|
||||
printImportant "$MNG_WARN"; echo
|
||||
$wrapper_sudo -Rsn $($wrapper -Qqtd)
|
||||
else
|
||||
printDone "$MNG_DONE"
|
||||
fi
|
||||
|
||||
returnMenu
|
||||
}
|
||||
|
||||
downgradePackage() {
|
||||
dependencies "fzf" true
|
||||
pacman_cache_dir="$(pacman-conf CacheDir)"
|
||||
wrapper_cache_dir="$HOME/.cache/$wrapper"
|
||||
|
||||
files=$(find "$pacman_cache_dir" "$wrapper_cache_dir" -type f -name "*.pkg.tar.zst" -printf "%f\n" 2>/dev/null | \
|
||||
fzf --exact --multi --layout=reverse)
|
||||
|
||||
if [[ -z "$files" ]]; then
|
||||
showOptions "$selected"
|
||||
return
|
||||
fi
|
||||
|
||||
file_paths=()
|
||||
|
||||
for file in $files; do
|
||||
if [ -f "${pacman_cache_dir}${file}" ]; then
|
||||
file_paths+=("${pacman_cache_dir}${file}")
|
||||
else
|
||||
path=$(find "$wrapper_cache_dir" -type f -name "$file" 2>/dev/null)
|
||||
[[ -n "$path" ]] && file_paths+=("$path")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#file_paths[@]} -gt 0 ]; then
|
||||
cache="${file_paths[*]}"
|
||||
printExec -U "$cache"
|
||||
$wrapper_sudo -U $cache
|
||||
fi
|
||||
|
||||
returnMenu
|
||||
}
|
||||
|
||||
rebuildPython() {
|
||||
[[ $wrapper = "pacman" ]] && { printDone "$MNG_DONE"; returnMenu; }
|
||||
rebuild_dir=$(find /usr/lib -maxdepth 1 -type d -name "python*.*" | oneLine)
|
||||
|
||||
if [ $(echo "$rebuild_dir" | wc -w) -gt 1 ]; then
|
||||
rebuild_dir="${rebuild_dir#* }"
|
||||
rebuild_packages=$( { pacman -Qqo "$rebuild_dir" | pacman -Qqm - | oneLine; } 2>/dev/null)
|
||||
if [[ -z "$rebuild_packages" ]]; then
|
||||
printDone "$MNG_DONE"
|
||||
else
|
||||
printExec "-S --rebuild" "$rebuild_packages"
|
||||
while true; do
|
||||
printQuestion "$MNG_RESUME"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) echo; break;;
|
||||
[Nn]*|"") echo; showOptions;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
pacman -Qqo "$rebuild_dir" | pacman -Qqm - | $wrapper -S --rebuild -
|
||||
fi
|
||||
else
|
||||
printDone "$MNG_DONE"
|
||||
fi
|
||||
|
||||
returnMenu
|
||||
}
|
||||
|
||||
showOptions
|
||||
@ -0,0 +1,53 @@
|
||||
i18n("Upgrade in progress")
|
||||
i18n("Full system upgrade")
|
||||
i18n("Flatpak Upgrade")
|
||||
i18n("Plasma Widgets Upgrade")
|
||||
i18n("Upgrade")
|
||||
i18n("Total execution time:")
|
||||
i18n("Critical package(s) updated, reboot may be required:")
|
||||
i18n("Do you want to reboot now?")
|
||||
i18n("Press Enter to close")
|
||||
i18n("Skipped...")
|
||||
i18n("Fetching the latest filtered mirror list")
|
||||
i18n("Ranking mirrors by their connection and opening speed")
|
||||
i18n("Check your mirrorlist generator settings...")
|
||||
i18n("was updated with the following servers:")
|
||||
i18n("To write to the mirrorlist file, sudo privileges are required")
|
||||
i18n("Your current mirrorlist:")
|
||||
i18n("For some widgets you may need to Log Out or restart plasmashell after upgrade")
|
||||
i18n("Checking widgets for updates")
|
||||
i18n("Fetching data from the API")
|
||||
i18n("Getting the download link")
|
||||
i18n("Downloading package")
|
||||
i18n("Proceed with upgrade?")
|
||||
i18n("Restart plasmashell now?")
|
||||
i18n("No description")
|
||||
i18n("Too many API requests in the last 15 minutes from your IP address, please try again later")
|
||||
i18n("Failed to retrieve data from the API")
|
||||
i18n("Unkwnown error")
|
||||
i18n("File metadata.json not found")
|
||||
i18n("Errors in metadata.json file")
|
||||
i18n("Unsupported file format")
|
||||
i18n("No files for download")
|
||||
i18n("No file tagged with version")
|
||||
i18n("Multiple files are tagged with version")
|
||||
i18n("Failed to download the package")
|
||||
i18n("List all available packages in repositories")
|
||||
i18n("List all installed packages")
|
||||
i18n("List explicitly installed packages")
|
||||
i18n("List explicitly installed packages and isn't a dependency of anything")
|
||||
i18n("List installed dependencies that aren't needed anymore (orphans)")
|
||||
i18n("Uninstall orphan packages")
|
||||
i18n("Install (downgrade) a package from cache")
|
||||
i18n("Remove ALL cached packages")
|
||||
i18n("Remove cached packages that are not currently installed")
|
||||
i18n("Rebuild AUR python packages after python upgrade")
|
||||
i18n("Refresh mirrorlist")
|
||||
i18n("Exit")
|
||||
i18n("Review dependency warnings before removing")
|
||||
i18n("Resume?")
|
||||
i18n("Press Enter to return menu")
|
||||
i18n("Search:")
|
||||
i18n("Executed:")
|
||||
i18n("Nothing to do")
|
||||
i18n("Required installed")
|
||||
@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
[[ "$1" == "true" ]] && MENU=1 || MENU=0
|
||||
selected=$2
|
||||
|
||||
returnMenu() {
|
||||
printReturn
|
||||
$scriptDir/management $selected
|
||||
exit
|
||||
}
|
||||
|
||||
mirrorfile="/etc/pacman.d/mirrorlist"
|
||||
OUTDATED=false
|
||||
[[ ! -f "$mirrorfile" ]] && {
|
||||
NORM_TIME="(no file)"
|
||||
OUTDATED=true
|
||||
} || {
|
||||
FILE_TIME=$(date -r "$mirrorfile" +%s)
|
||||
NORM_TIME=$(date -d @"$FILE_TIME" +"%d %b %H:%M:%S")
|
||||
CURR_TIME=$(date +%s)
|
||||
[[ "$mirrors" == "age" && $MENU -eq 0 ]] && {
|
||||
(( CURR_TIME - FILE_TIME >= mirrorsAge * 86400 )) && OUTDATED=true || exit
|
||||
}
|
||||
}
|
||||
|
||||
[[ "$mirrors" != "force" || $MENU -eq 1 ]] && {
|
||||
printImportant "$MIRROR_TIME" "$NORM_TIME"
|
||||
while true; do
|
||||
printQuestion "$MNG_OPT_11?"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) echo; break;;
|
||||
[Nn]*|"") (( MENU )) && { returnMenu; } || { echo; exit; };;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
dependencies "rankmirrors" true
|
||||
rankmirrors -V &>/dev/null || {
|
||||
countries=$(echo "$dynamicUrl" | grep -oP '(?<=country=)[^&]+')
|
||||
countries=$(echo "$countries" | tr '\n' ' ')
|
||||
countries=$(echo "$countries" | sed 's/ *$//')
|
||||
|
||||
if [ -z "$countries" ]; then
|
||||
printError "$MIRRORS_ERR"
|
||||
else
|
||||
echo "Selected countries: $countries"
|
||||
${sudoBin} rankmirrors -c ${countries}
|
||||
echo
|
||||
fi
|
||||
|
||||
(( MENU )) && returnMenu || exit
|
||||
}
|
||||
|
||||
tempfile=$(mktemp)
|
||||
tput sc; curl -m 60 -s -o $tempfile "$dynamicUrl" 2>/dev/null &
|
||||
spinner $! "$MIRRORS_FETCH"; tput rc; tput ed
|
||||
if [[ -s "$tempfile" && $(head -n 1 "$tempfile" | grep -c "^##") -gt 0 ]]; then
|
||||
printDone "$MIRRORS_FETCH"
|
||||
else
|
||||
printError "$MIRRORS_FETCH"
|
||||
printError "$MIRRORS_ERR"
|
||||
(( MENU )) && returnMenu || exit
|
||||
fi
|
||||
|
||||
sed -i -e "s/^#Server/Server/" -e "/^#/d" "$tempfile"
|
||||
tempfile2=$(mktemp)
|
||||
tput sc; rankmirrors -n "$mirrorCount" "$tempfile" > "$tempfile2" &
|
||||
spinner $! "$MIRRORS_RANK"; tput rc; tput ed
|
||||
if [[ -s "$tempfile2" && $(head -n 1 "$tempfile2" | grep -c "^# S") -gt 0 ]]; then
|
||||
printDone "$MIRRORS_RANK"
|
||||
else
|
||||
printError "$MIRRORS_RANK"
|
||||
(( MENU )) && returnMenu || exit
|
||||
fi
|
||||
|
||||
sed -i '1d' "$tempfile2"
|
||||
sed -i "1s/^/##\n## Arch Linux repository mirrorlist\n## Generated on $(date '+%Y-%m-%d %H:%M:%S')\n##\n\n/" "$tempfile2"
|
||||
|
||||
${sudoBin} -n true 2>/dev/null || { printImportant "$MIRRORS_SUDO"; }
|
||||
cat $tempfile2 | ${sudoBin} tee $mirrorfile > /dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
printDone "$mirrorfile $MIRRORS_UPD"
|
||||
echo -e "$y$(tail -n +6 $mirrorfile | sed 's/Server = //g')$c\n"
|
||||
rm $tempfile; rm $tempfile2
|
||||
(( MENU )) && returnMenu
|
||||
else
|
||||
printError "$MIRRORS_SUDO"
|
||||
(( MENU )) && returnMenu || exit
|
||||
fi
|
||||
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
declare -A termArg=( ["gnome-terminal"]="--" ["ptyxis"]="--" ["terminator"]="-x" )
|
||||
name="${1^}"
|
||||
sess="Apdatifier"
|
||||
term="$(basename $terminal)"
|
||||
termArg="${termArg[$term]:-"-e"}"
|
||||
scr="$(dirname "$0")/$1"
|
||||
arg="${@:2}"
|
||||
|
||||
# Ghostty requires additional quotes
|
||||
[[ $term = ghostty ]] && Q="'"
|
||||
|
||||
_attach="${Q}tmux attach-session -t $sess \; select-window -t $name${Q}"
|
||||
_script="${Q}$scr $arg${Q}"
|
||||
|
||||
[[ $tmuxSession = "true" && -x $(command -v tmux) ]] && _TMUX=1
|
||||
|
||||
run_terminal() {
|
||||
$term $termArg bash -c "$1"
|
||||
}
|
||||
run_yakuake() {
|
||||
qdbusCMD sessions runCommandInTerminal $session "bash -c '$1'"
|
||||
}
|
||||
create_tmux_session() {
|
||||
! tmux has-session -t "$sess" 2>/dev/null && {
|
||||
tmux new-session -d -s "$sess" -n "$name" "bash -c '$scr $arg'"
|
||||
} || {
|
||||
! tmux list-windows -t "$sess" | grep -wq "$name" && {
|
||||
tmux new-window -t "$sess:" -n "$name" "bash -c '$scr $arg'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if [[ $term = "yakuake" ]]; then
|
||||
session=$(qdbusCMD sessions addSession)
|
||||
visible=$(qdbusCMD MainWindow_1 org.qtproject.Qt.QWidget.visible)
|
||||
qdbusCMD tabs org.kde.yakuake.setTabTitle $session $name
|
||||
if (( _TMUX )); then
|
||||
create_tmux_session
|
||||
run_yakuake "$_attach"
|
||||
else
|
||||
run_yakuake "tput sc; clear; $_script"
|
||||
fi
|
||||
[[ $visible = "false" ]] && qdbusCMD window org.kde.yakuake.toggleWindowState
|
||||
elif (( _TMUX )); then
|
||||
create_tmux_session
|
||||
run_terminal "$_attach"
|
||||
else
|
||||
run_terminal "$_script"
|
||||
fi
|
||||
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
trap 'echo -e "\n\n$(colorize red bold $ICO_WARN "CTRL+C. $UPGRADE_SKIPPED")"' SIGINT
|
||||
|
||||
run() {
|
||||
[[ -z $2 ]] && printExec "$1"
|
||||
|
||||
if [ "$idleInhibit" = true ]; then
|
||||
# (( !OSD )) && {
|
||||
# OSD=1
|
||||
# qdbus6 org.kde.plasmashell /org/kde/osdService org.kde.osdService.showText \
|
||||
# system-suspend-inhibited "Idle inhibition is active during upgrade"
|
||||
# }
|
||||
read -ra cmd <<< "$1"
|
||||
systemd-inhibit --what=idle:sleep:shutdown --who="Apdatifier" --why="$UPGRADE_PROGRESS" "${cmd[@]}"
|
||||
else
|
||||
eval ${1}
|
||||
fi
|
||||
}
|
||||
|
||||
bin="$sudoBin pacman"; [ "$aur" = true ] && bin=$wrapper
|
||||
|
||||
fullSystemUpgrade() {
|
||||
startTime=$(date +%s)
|
||||
critical_updated=()
|
||||
critical_installed=()
|
||||
critical_packages=(
|
||||
"amd-ucode"
|
||||
"intel-ucode"
|
||||
"cryptsetup"
|
||||
"linux"
|
||||
"linux-hardened"
|
||||
"linux-lts"
|
||||
"linux-zen"
|
||||
"linux-rt"
|
||||
"linux-rt-lts"
|
||||
"linux-firmware*"
|
||||
"linux-cachyos*"
|
||||
"linux-cacule*"
|
||||
"nvidia"
|
||||
"nvidia-dkms"
|
||||
"nvidia-*xx-dkms"
|
||||
"nvidia-*xx"
|
||||
"nvidia-*lts-dkms"
|
||||
"nvidia*-lts"
|
||||
"mkinitcpio*"
|
||||
"booster*"
|
||||
"mesa"
|
||||
"systemd*"
|
||||
"wayland"
|
||||
"virtualbox-guest-utils"
|
||||
"virtualbox-host-dkms"
|
||||
"virtualbox-host-modules-arch"
|
||||
"egl-wayland"
|
||||
"xf86-video-*"
|
||||
"xorg-server*"
|
||||
"xorg-fonts*"
|
||||
"winesync-dkms"
|
||||
)
|
||||
|
||||
if [ "$arch" = true ]; then
|
||||
printMsg "$UPGRADE_FULL_ARCH"
|
||||
echo
|
||||
|
||||
[ "$mirrors" != "false" ] && $scriptDir/mirrorlist
|
||||
|
||||
if [ -n "$preExec" ]; then
|
||||
run "$preExec"
|
||||
printf '\n\033[32m'; printf '%.0s:' {1..48}; printf '\033[0m\n\n'
|
||||
fi
|
||||
|
||||
if [ "$rebootSystem" = true ]; then
|
||||
for package in "${critical_packages[@]}"; do
|
||||
matches=$(pacman -Qq | grep -E "^${package//\*/.*}$")
|
||||
[ -n "$matches" ] && critical_installed+=($matches)
|
||||
done
|
||||
|
||||
declare -A beforeVersions
|
||||
for pkg in "${critical_installed[@]}"; do
|
||||
beforeVersions[$pkg]=$(pacman -Q $pkg | awk '{print $2}')
|
||||
done
|
||||
fi
|
||||
|
||||
test -f "$configDir/env.sh" && source "$configDir/env.sh"
|
||||
run "$bin -Syu $archFlags"
|
||||
|
||||
for pkg in "${critical_installed[@]}"; do
|
||||
afterVersion=$(pacman -Q $pkg | awk '{print $2}')
|
||||
if [ "${beforeVersions[$pkg]}" != "$afterVersion" ]; then
|
||||
critical_updated+=("$pkg")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$flatpak" = true ]; then
|
||||
printMsg "$UPGRADE_FULL_FLATPAK"
|
||||
echo
|
||||
|
||||
run "flatpak update $flatpakFlags"
|
||||
|
||||
if [ "$flatpakRemoveUnused" = true ]; then
|
||||
echo
|
||||
run "flatpak uninstall --unused $flatpakFlags"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$widgets" = true ]; then
|
||||
printMsg "$UPGRADE_FULL_WIDGETS"
|
||||
echo
|
||||
run "$scriptDir/widgets upgradeAll" noPrint
|
||||
fi
|
||||
|
||||
if [ -n "$postExec" ]; then
|
||||
printf '\n\033[32m'; printf '%.0s:' {1..48}; printf '\033[0m\n\n'
|
||||
run "$postExec"
|
||||
fi
|
||||
|
||||
trap ' ' SIGINT
|
||||
|
||||
endTime=$(date +%s)
|
||||
runTime=$((endTime-startTime))
|
||||
echo
|
||||
printImportant "$UPGRADE_EXECTIME" "$(printf "%02dh:%02dm:%02ds" $((runTime / 3600)) $(( (runTime % 3600) / 60 )) $((runTime % 60)))"
|
||||
|
||||
# if [ "$idleInhibit" = true ]; then
|
||||
# qdbus6 org.kde.plasmashell /org/kde/osdService org.kde.osdService.showText \
|
||||
# system-suspend-uninhibited "Idle inhibition has been disabled"
|
||||
# fi
|
||||
|
||||
if [ ${#critical_updated[@]} -gt 0 ]; then
|
||||
echo
|
||||
printImportant "$UPGRADE_CRITICAL" "${critical_updated[*]}"
|
||||
while true; do
|
||||
printQuestion "$UPGRADE_REBOOT"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) qdbus6 org.kde.Shutdown /Shutdown logoutAndReboot;;
|
||||
[Nn]*|"") break;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
printClose
|
||||
}
|
||||
|
||||
flatpak_package() {
|
||||
printMsg "$UPGRADE_PACKAGE: $2"
|
||||
echo
|
||||
|
||||
printExec "flatpak update" "$1 $flatpakFlags"
|
||||
|
||||
flatpak update $1 $flatpakFlags
|
||||
|
||||
printClose
|
||||
}
|
||||
|
||||
widget_package() {
|
||||
printMsg "$UPGRADE_PACKAGE: $2"
|
||||
echo
|
||||
|
||||
$scriptDir/widgets upgrade $1 $2
|
||||
|
||||
printClose
|
||||
}
|
||||
|
||||
postUpgrade() {
|
||||
local list=""
|
||||
[ "$arch" = true ] && list+=$(pacman -Q | awk '{print "{\"NM\": \"" $1 "\", \"VO\": \"" $2 "\"},"}')
|
||||
[ "$flatpak" = true ] && list+=$(flatpak list --app --columns=name,version,active | awk -F'\t' '{print "{\"NM\": \"" $1 "\", \"VO\": \"" $2 $3 "\"},"}')
|
||||
[ "$widgets" = true ] && list+=$($scriptDir/widgets list)
|
||||
echo "[${list%,}]"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"full") fullSystemUpgrade ;;
|
||||
"flatpak") shift; flatpak_package $1 $2;;
|
||||
"widget") shift; widget_package $1 $2;;
|
||||
"postUpgrade") postUpgrade ;;
|
||||
*) exit;;
|
||||
esac
|
||||
@ -0,0 +1,382 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
source "$(dirname "$0")/vars"
|
||||
|
||||
cleanup() {
|
||||
[ -d $tempDir ] && rm -rf $tempDir
|
||||
}
|
||||
|
||||
makeTempDir() {
|
||||
cleanup
|
||||
mkdir $tempDir
|
||||
}
|
||||
|
||||
colorize() {
|
||||
local red="\033[31m"
|
||||
local green="\033[32m"
|
||||
local blue="\033[34m"
|
||||
local yellow="\033[33m"
|
||||
local white="\033[37m"
|
||||
local clear="\033[0m"
|
||||
local bold="\033[1m"
|
||||
|
||||
local color="$1"; shift
|
||||
local style=""
|
||||
|
||||
if [[ "$1" == "bold" ]]; then
|
||||
style="${!1}"
|
||||
shift
|
||||
fi
|
||||
|
||||
echo -ne "${!color}${style}$*${clear}"
|
||||
}
|
||||
|
||||
printDone() {
|
||||
echo -e "$(colorize green bold "$ICO_DONE $1")"
|
||||
}
|
||||
|
||||
printError() {
|
||||
echo -e "$(colorize red bold "$ICO_ERR $1")"
|
||||
}
|
||||
|
||||
printImportant() {
|
||||
echo -e "$(colorize yellow bold "$ICO_WARN $1") $2"
|
||||
}
|
||||
|
||||
printQuestion() {
|
||||
echo -en "$(colorize yellow bold "$ICO_QUESTION $1") [y/N]: "
|
||||
}
|
||||
|
||||
printExec() {
|
||||
echo -e "$(colorize blue bold "$ICO_EXEC $MNG_EXEC") $(colorize white $wrapper_sudo $1 $2) \n"
|
||||
}
|
||||
|
||||
printMsg() {
|
||||
local text="$1"
|
||||
local padding=$(( (48 - ${#text} - 2) / 2 ))
|
||||
local l=$(printf ":%.0s" {1..48})
|
||||
local s=$(printf ":%.0s" $(seq 1 $padding))
|
||||
local p=${s}$( (( ${#text} % 2 )) && echo ":" )
|
||||
|
||||
echo
|
||||
echo -e "$(colorize green bold $l)"
|
||||
echo -e "$(colorize green bold $s) $(colorize white bold $text) $(colorize green bold $p)"
|
||||
echo -e "$(colorize green bold $l)"
|
||||
}
|
||||
|
||||
printReturn() {
|
||||
tput civis
|
||||
echo
|
||||
echo -e "$(colorize blue bold $ICO_RETURN $MNG_RETURN)"
|
||||
read -r
|
||||
tput cnorm
|
||||
}
|
||||
|
||||
printClose() {
|
||||
tput civis
|
||||
printMsg "$UPGRADE_ENTER"
|
||||
read -r
|
||||
tput cnorm
|
||||
[[ $(basename $terminal) = "yakuake" ]] && qdbusCMD sessions removeSession $(qdbusCMD sessions activeSessionId)
|
||||
}
|
||||
|
||||
printWhile() {
|
||||
tput sc
|
||||
pid=$1
|
||||
spinner $pid "$2"
|
||||
wait $pid
|
||||
exitCode=$?
|
||||
tput rc
|
||||
tput ed
|
||||
|
||||
[[ $exitCode -eq 0 ]] && printDone "$2" || printError "$2"
|
||||
|
||||
if [[ "$2" = "$WIDGETS_FETCH" ]]; then
|
||||
case $exitCode in
|
||||
1) printError "$WIDGETS_ERR_API_FAIL" ;;
|
||||
2) printError "$WIDGETS_ERR_API" ;;
|
||||
3) printError "$WIDGETS_ERR_UNKNOWN" ;;
|
||||
esac
|
||||
elif [[ "$2" = "$WIDGETS_LINK" ]]; then
|
||||
case $exitCode in
|
||||
1) printError "$WIDGETS_ERR_NOFILES. $UPGRADE_SKIPPED" ;;
|
||||
2) printError "$WIDGETS_ERR_NOTAGGED $latestVer. $UPGRADE_SKIPPED" ;;
|
||||
3) printError "$WIDGETS_ERR_TAGGED $latestVer. $UPGRADE_SKIPPED" ;;
|
||||
esac
|
||||
elif [[ "$2" = "$WIDGETS_DOWNLOADING" ]]; then
|
||||
case $exitCode in
|
||||
1) printError "$WIDGETS_ERR_PACKAGE_FAIL" ;;
|
||||
2) printError "$WIDGETS_ERR_EXT" ;;
|
||||
3) printError "$WIDGETS_ERR_NO_JSON" ;;
|
||||
4) printError "$WIDGETS_ERR_JSON" ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
qdbusCMD() {
|
||||
qdbus6 org.kde.yakuake /yakuake/$1 "${@:2}"
|
||||
}
|
||||
|
||||
dependencies() {
|
||||
for cmd in ${1}; do
|
||||
if ! command -v "$cmd" >/dev/null; then
|
||||
printError "${CMD_ERR} ${cmd}"
|
||||
[ $2 ] && returnMenu || exit
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
oneLine() {
|
||||
tr '\n' ' ' | sed 's/ $//'
|
||||
}
|
||||
|
||||
spinner() {
|
||||
local spin="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
||||
while kill -0 $1 2>/dev/null; do
|
||||
i=$(( (i+1) %10 ))
|
||||
printf "\r$(colorize red ${spin:$i:1}) $(colorize blue bold "$2...")"
|
||||
sleep .2
|
||||
done
|
||||
}
|
||||
|
||||
clearVer() {
|
||||
local ver="${1}"
|
||||
ver="${ver//[^0-9.]/.}"
|
||||
while [[ "$ver" == *".."* ]]; do
|
||||
ver="${ver//../.}"
|
||||
done
|
||||
ver="${ver#.}"
|
||||
ver="${ver%.}"
|
||||
echo "${ver}"
|
||||
}
|
||||
|
||||
compareVer() {
|
||||
[[ $1 == $2 ]] && return 0
|
||||
local IFS=.; local i ver1=($1) ver2=($2)
|
||||
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do ver1[i]=0; done
|
||||
for ((i=0; i<${#ver1[@]}; i++)); do
|
||||
[[ -z ${ver2[i]} ]] && ver2[i]=0
|
||||
((10#${ver1[i]} > 10#${ver2[i]})) && return 1
|
||||
((10#${ver1[i]} < 10#${ver2[i]})) && return 2
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
getId() {
|
||||
[[ $1 =~ ^[0-9]+$ ]] && f=2 || f=1
|
||||
grep $1 "$(dirname "$0")/widgets-id" | cut -d' ' -f$f
|
||||
}
|
||||
|
||||
restartPlasmashell() {
|
||||
if [[ "$restartShell" = true ]]; then
|
||||
sleep 1
|
||||
while true; do
|
||||
printQuestion "$WIDGETS_RESTART"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) break;;
|
||||
[Nn]*|"") exit;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
eval ${restartCommand}
|
||||
else
|
||||
printImportant "$WIDGETS_WARN"
|
||||
fi
|
||||
}
|
||||
|
||||
combineFiles() {
|
||||
sleep 1
|
||||
[ -f "$1" ] && rm "$1"
|
||||
for cache in $(find $configDir -name "updates.json_*" | sort); do
|
||||
cat "$cache" >> "$1"; rm "$cache"
|
||||
done
|
||||
}
|
||||
|
||||
updateJson() {
|
||||
cat <<< "$2" > "$1.tmp" && mv "$1.tmp" "$1"
|
||||
}
|
||||
|
||||
rss() {
|
||||
command -v jq >/dev/null || { echo "${CMD_ERR} jq" >&2; return 127; }
|
||||
|
||||
makeTempDir
|
||||
trap cleanup EXIT
|
||||
|
||||
local newsFile="$configDir/news.json"
|
||||
|
||||
if [[ ! -s "$newsFile" ]] \
|
||||
|| ! jq -e '.' "$newsFile" >/dev/null 2>&1 \
|
||||
|| ! jq -e '[.[] | .date | strptime("%d.%m.%Y | %H:%M")] | length == length' "$newsFile" >/dev/null 2>&1; then
|
||||
echo '[]' > "$newsFile"
|
||||
fi
|
||||
|
||||
for url in "$@"; do
|
||||
local rssFile=$(mktemp "$tempDir/XXXXXX.xml")
|
||||
curl -s -o "$rssFile" --connect-timeout 5 --retry 2 --max-time 60 --url "$url" 2>/dev/null
|
||||
xmllint --noout "$rssFile" 2>/dev/null || { echo $url; exit 1; }
|
||||
|
||||
local title=$(xmllint --xpath "string(/rss/channel/title)" $rssFile)
|
||||
local article=$(xmllint --xpath "string(/rss/channel/item[1]/title)" $rssFile)
|
||||
local date=$(date -d "$(xmllint --xpath 'string(/rss/channel/item[1]/pubDate)' $rssFile)" +"%d.%m.%Y | %H:%M")
|
||||
local link=$(xmllint --xpath "string(/rss/channel/item[1]/link)" $rssFile)
|
||||
|
||||
[[ -z "$title" || -z "$article" || -z "$date" || -z "$link" ]] && { echo $url; exit 1; }
|
||||
|
||||
while (( $(jq "[.[] | select(.title == \"$title\")] | length" "$newsFile") > newsKeep )); do
|
||||
index=$(jq --arg title "$title" 'to_entries | reverse | map(select(.value.title == $title)) | .[0].key' $newsFile)
|
||||
updateJson $newsFile "$(jq --argjson index "$index" "del(.[$index])" $newsFile)"
|
||||
done
|
||||
|
||||
if ! jq -e ".[] | select(.link == \"$link\")" $newsFile > /dev/null; then
|
||||
local item=$(jq -n --arg t "$title" --arg a "$article" --arg d "$date" --arg l "$link" '{title: $t, article: $a, date: $d, link: $l, removed: false}')
|
||||
updateJson $newsFile "$(jq "[$item] + ." $newsFile)"
|
||||
fi
|
||||
done
|
||||
|
||||
updateJson $newsFile "$(jq 'sort_by(.date | strptime("%d.%m.%Y | %H:%M") | mktime) | reverse' $newsFile)"
|
||||
|
||||
jq . $newsFile
|
||||
}
|
||||
|
||||
install_devel() {
|
||||
printMsg "Downloading devel"
|
||||
echo
|
||||
|
||||
required="git jq"
|
||||
for cmd in ${required}; do command -v "$cmd" >/dev/null || { printError "Required installed ${cmd}"; printClose; exit; }; done;
|
||||
|
||||
commit=$(curl -s https://api.github.com/repos/exequtic/apdatifier/commits/main | jq -r '.sha' | cut -c1-7)
|
||||
|
||||
if [[ "github commit: $commit" = "$(jq -r '.KPlugin.Description' $appletDir/metadata.json)" ]]; then
|
||||
printDone "Up to date"
|
||||
printClose
|
||||
exit
|
||||
fi
|
||||
|
||||
savedir=$(pwd)
|
||||
clonedir=$(mktemp -d)
|
||||
cd $clonedir
|
||||
|
||||
{ git clone -n --depth=10 --filter=tree:0 -b main https://github.com/exequtic/apdatifier 2>/dev/null
|
||||
} & printWhile $! "Clone main branch"
|
||||
[[ $exitCode -ne 0 ]] && { printClose; exit; }
|
||||
|
||||
cd apdatifier
|
||||
|
||||
{ git sparse-checkout set --no-cone package 2>/dev/null && git checkout 2>/dev/null
|
||||
} & printWhile $! "Checkout package directory"
|
||||
[[ $exitCode -ne 0 ]] && { printClose; exit; }
|
||||
|
||||
if command -v less &>/dev/null; then
|
||||
echo
|
||||
printImportant "Last 10 commits:"
|
||||
git log --oneline
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
echo
|
||||
printMsg "Upgrading Apdatifier"
|
||||
echo
|
||||
cd package || exit 1
|
||||
updateJson metadata.json "$(jq --arg new_value "github commit: $commit" '.KPlugin.Description = $new_value' metadata.json)"
|
||||
[[ $trayEnabledByDefault == "true" ]] && updateJson $metadata "$(jq '.KPlugin.EnabledByDefault = true' $metadata)"
|
||||
while true; do
|
||||
printQuestion "Do you want build translations?"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) cd translate; bash build; cd ..; break;;
|
||||
[Nn]*|"") break;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
echo
|
||||
tar --exclude=./apdatifier.tar -cf apdatifier.tar .
|
||||
kpackagetool6 -t Plasma/Applet -u apdatifier.tar 2>/dev/null
|
||||
echo
|
||||
|
||||
cd $savedir
|
||||
[ ! -d $clonedir ] || rm -rf $clonedir
|
||||
|
||||
restartShell=true
|
||||
restartPlasmashell
|
||||
|
||||
printClose
|
||||
exit
|
||||
}
|
||||
|
||||
install_stable() {
|
||||
if [[ "$(jq -r '.KPlugin.Description' $appletDir/metadata.json)" = "Arch Update Notifier" ]]; then
|
||||
while true; do
|
||||
printImportant "It looks like you already have the stable version."
|
||||
printQuestion "Do you still want to proceed?"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) break;;
|
||||
[Nn]*|"") printClose; exit;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
$scriptDir/upgrade widget "2135796" "apdatifier"
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
printMsg "Uninstall Apdatifier"
|
||||
echo
|
||||
|
||||
while true; do
|
||||
printQuestion "Continue?"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) break;;
|
||||
[Nn]*|"") printClose; exit;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo
|
||||
|
||||
showCommand() {
|
||||
echo -e "$(colorize yellow bold "$ICO_EXEC Command:") $1"
|
||||
}
|
||||
|
||||
printImportant "Remove icons"
|
||||
for icon in $icon1 $icon2 $icon3 $icon4; do
|
||||
showCommand "rm $iconsDir/$icon"
|
||||
test -f "$iconsDir/$icon" && rm "$iconsDir/$icon"
|
||||
done
|
||||
showCommand "rmdir -p --ignore-fail-on-non-empty $iconsDir"
|
||||
test -d "$iconsDir" && rmdir -p --ignore-fail-on-non-empty "$iconsDir"
|
||||
sleep 1; echo
|
||||
|
||||
printImportant "Remove notification"
|
||||
showCommand "rm $notifDir/$notif"
|
||||
test -f "$notifDir/$notif" && rm "$notifDir/$notif"
|
||||
showCommand "rmdir -p --ignore-fail-on-non-empty $notifDir"
|
||||
test -d "$notifDir" && rmdir -p --ignore-fail-on-non-empty "$notifDir"
|
||||
sleep 1; echo
|
||||
|
||||
printImportant "Remove configuration"
|
||||
showCommand "rm -rf $configDir"
|
||||
test -d "$configDir" && rm -rf "$configDir"
|
||||
sleep 1; echo
|
||||
|
||||
printImportant "Uninstall plasmoid"
|
||||
showCommand "kpackagetool6 -t Plasma/Applet -r $applet"
|
||||
kpackagetool6 -t Plasma/Applet -r $applet 2>/dev/null
|
||||
sleep 1; echo
|
||||
|
||||
echo "Bye :("; sleep 1
|
||||
|
||||
printClose
|
||||
exit
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"rss") shift; rss "$@" ;;
|
||||
"combineFiles") shift; combineFiles $1 ;;
|
||||
"installDev") install_devel ;;
|
||||
"installStable") install_stable ;;
|
||||
"uninstall") uninstall ;;
|
||||
esac
|
||||
@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
scriptDir=`cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd`
|
||||
|
||||
applet="com.github.exequtic.apdatifier"
|
||||
configDir="$HOME/.config/apdatifier"
|
||||
localDir="$HOME/.local/share"
|
||||
iconsDir="$localDir/icons/breeze/status/24"
|
||||
notifDir="$localDir/knotifications6"
|
||||
appletDir="$localDir/plasma/plasmoids/$applet"
|
||||
tempDir="/tmp/apdatifier"
|
||||
|
||||
config="$configDir/config.conf"
|
||||
icon1="apdatifier-plasmoid.svg"
|
||||
icon2="apdatifier-packages.svg"
|
||||
icon3="apdatifier-package.svg"
|
||||
icon4="apdatifier-flatpak.svg"
|
||||
notif="apdatifier.notifyrc"
|
||||
|
||||
export TEXTDOMAINDIR="$scriptDir/../../locale"
|
||||
export TEXTDOMAIN="plasma_applet_${applet}"
|
||||
|
||||
source $config
|
||||
|
||||
declare -a var_names=(
|
||||
UPGRADE_PROGRESS
|
||||
UPGRADE_FULL_ARCH
|
||||
UPGRADE_FULL_FLATPAK
|
||||
UPGRADE_FULL_WIDGETS
|
||||
UPGRADE_PACKAGE
|
||||
UPGRADE_EXECTIME
|
||||
UPGRADE_CRITICAL
|
||||
UPGRADE_REBOOT
|
||||
UPGRADE_ENTER
|
||||
UPGRADE_SKIPPED
|
||||
MIRRORS_FETCH
|
||||
MIRRORS_RANK
|
||||
MIRRORS_ERR
|
||||
MIRRORS_UPD
|
||||
MIRRORS_SUDO
|
||||
MIRROR_TIME
|
||||
WIDGETS_WARN
|
||||
WIDGETS_CHECK
|
||||
WIDGETS_FETCH
|
||||
WIDGETS_LINK
|
||||
WIDGETS_DOWNLOADING
|
||||
WIDGETS_PROCEED
|
||||
WIDGETS_RESTART
|
||||
WIDGETS_NODESC
|
||||
WIDGETS_ERR_API
|
||||
WIDGETS_ERR_API_FAIL
|
||||
WIDGETS_ERR_UNKNOWN
|
||||
WIDGETS_ERR_NO_JSON
|
||||
WIDGETS_ERR_JSON
|
||||
WIDGETS_ERR_EXT
|
||||
WIDGETS_ERR_NOFILES
|
||||
WIDGETS_ERR_NOTAGGED
|
||||
WIDGETS_ERR_TAGGED
|
||||
WIDGETS_ERR_PACKAGE_FAIL
|
||||
MNG_OPT_01
|
||||
MNG_OPT_02
|
||||
MNG_OPT_03
|
||||
MNG_OPT_04
|
||||
MNG_OPT_05
|
||||
MNG_OPT_06
|
||||
MNG_OPT_07
|
||||
MNG_OPT_08
|
||||
MNG_OPT_09
|
||||
MNG_OPT_10
|
||||
MNG_OPT_11
|
||||
MNG_OPT_12
|
||||
MNG_WARN
|
||||
MNG_RESUME
|
||||
MNG_RETURN
|
||||
MNG_SEARCH
|
||||
MNG_EXEC
|
||||
MNG_DONE
|
||||
CMD_ERR
|
||||
)
|
||||
|
||||
i=0
|
||||
while IFS= read -r line && [ $i -lt ${#var_names[@]} ]; do
|
||||
i=$((i+1))
|
||||
text=$(echo "$line" | grep -oP '(?<=\().*(?=\))' | sed 's/"//g')
|
||||
eval "${var_names[$((i-1))]}=\"$(gettext "$text")\""
|
||||
done < "$scriptDir/messages"
|
||||
|
||||
if [[ $termFont = "true" ]]; then
|
||||
ICO_MNG_OPT_01=" "
|
||||
ICO_MNG_OPT_02=" "
|
||||
ICO_MNG_OPT_03=" "
|
||||
ICO_MNG_OPT_04=" "
|
||||
ICO_MNG_OPT_05=" "
|
||||
ICO_MNG_OPT_06=" "
|
||||
ICO_MNG_OPT_07=" "
|
||||
ICO_MNG_OPT_08=" "
|
||||
ICO_MNG_OPT_09=" "
|
||||
ICO_MNG_OPT_10=" "
|
||||
ICO_MNG_OPT_11=" "
|
||||
ICO_MNG_OPT_12=" "
|
||||
|
||||
ICO_ERR=""
|
||||
ICO_DONE=""
|
||||
ICO_WARN=""
|
||||
ICO_QUESTION=""
|
||||
ICO_EXEC=""
|
||||
ICO_RETURN=""
|
||||
ICO_SELECT=""
|
||||
else
|
||||
ICO_ERR="✘"
|
||||
ICO_DONE="✔"
|
||||
ICO_WARN="::"
|
||||
ICO_QUESTION="::"
|
||||
ICO_EXEC="::"
|
||||
ICO_RETURN="<<"
|
||||
ICO_SELECT=">"
|
||||
fi
|
||||
|
||||
fzf_settings="--preview-window "right:70%" --height=100% \
|
||||
--layout=reverse --info=right --border=none \
|
||||
--multi --track --exact --margin=0 --padding=0 \
|
||||
--cycle --prompt=$MNG_SEARCH⠀ --marker=•"
|
||||
@ -0,0 +1,346 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2024 Evgeny Kazantsev <exequtic@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
fetch_api_data() {
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?><data>' > "$XML"
|
||||
|
||||
local page=0
|
||||
local pagesize=100
|
||||
|
||||
while true; do
|
||||
pageXML=$(mktemp "$tempDir/XXXXXX.xml")
|
||||
api_url="https://api.kde-look.org/ocs/v1/content/data?categories=705x715x719x720&sort=new&page=$page&pagesize=$pagesize"
|
||||
|
||||
curl -s -o "$pageXML" --connect-timeout 5 --retry 2 --max-time 60 --request GET --url "$api_url"
|
||||
|
||||
! xmllint --noout "$pageXML" 2>/dev/null && return 1
|
||||
|
||||
totalitems=$(xmllint --xpath "string(//ocs/meta/totalitems)" $pageXML)
|
||||
statuscode=$(xmllint --xpath "string(//ocs/meta/statuscode)" $pageXML)
|
||||
|
||||
case $statuscode in
|
||||
100)
|
||||
xmllint --xpath "//content[@details='summary']" $pageXML >> $XML
|
||||
|
||||
items=$(((page + 1) * pagesize))
|
||||
if [[ $totalitems > $items ]]; then
|
||||
((page++))
|
||||
else
|
||||
echo '</data>' >> "$XML"
|
||||
break
|
||||
fi
|
||||
;;
|
||||
|
||||
200) return 2 ;;
|
||||
*) return 3 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
get_packages() {
|
||||
packages=($(find \
|
||||
"$localDir/kwin/effects" \
|
||||
"$localDir/kwin/scripts" \
|
||||
"$localDir/plasma/plasmoids" \
|
||||
"$localDir/plasma/wallpapers" \
|
||||
-mindepth 1 -maxdepth 1 -type d 2>/dev/null))
|
||||
[ ${#packages[@]} -eq 0 ] && exit
|
||||
}
|
||||
|
||||
get_package_type() {
|
||||
case $(xmllint --xpath "string(//id[text()='$contentId']/../typeid)" $XML) in
|
||||
"715") echo "Plasma/Wallpaper";;
|
||||
"719") echo "KWin/Effect";;
|
||||
"720") echo "KWin/Script";;
|
||||
*) echo "Plasma/Applet";;
|
||||
esac
|
||||
}
|
||||
|
||||
get_package_info() {
|
||||
local id=$(basename "$package")
|
||||
|
||||
local metadata="$package/metadata.json"
|
||||
[[ -s "$metadata" ]] || return 1
|
||||
jq . $metadata >/dev/null 2>&1 || return 2
|
||||
|
||||
local originName=$(jq -r '.KPlugin.Name' $metadata)
|
||||
name=$(echo "$originName" | sed 's/ /-/g; s/.*/\L&/')
|
||||
|
||||
local quotedOriginName='"'$(echo "$originName" | sed 's/"/\\"/g')'"'
|
||||
contentId=$(xmllint --xpath "string(//name[text()=$quotedOriginName]/../id)" "$XML")
|
||||
if [[ -z "$contentId" ]]; then
|
||||
knsregistry=("plasmoids" "kwinscripts" "kwineffect" "wallpaperplugin")
|
||||
for kns in "${knsregistry[@]}"; do
|
||||
kns="$HOME/.local/share/knewstuff3/$kns.knsregistry"
|
||||
[[ -s "$kns" ]] && contentId=$(xmllint --xpath "string(//installedfile[contains(text(), '/$id')]/../id)" $kns)
|
||||
[[ -n "$contentId" ]] && break
|
||||
done
|
||||
fi
|
||||
[[ -z "$contentId" ]] && contentId="$(getId "$id" | head -n 1)"
|
||||
[[ -z "$contentId" ]] && return 3
|
||||
|
||||
currentVer=$(clearVer "$(jq -r '.KPlugin.Version' "$metadata")")
|
||||
latestVer=$(clearVer "$(xmllint --xpath "string(//id[text()='$contentId']/../version)" $XML)")
|
||||
[ -z "$currentVer" ] || [ -z "$latestVer" ] && return 4
|
||||
compareVer "$currentVer" "$latestVer"
|
||||
[[ $? != 2 ]] && return 5
|
||||
|
||||
description=$(jq -r '.KPlugin.Description' $metadata | sed 's/"/\\"/g' | tr -d '\n')
|
||||
[ -z "$description" ] || [ "$description" = "null" ] && description="$WIDGETS_NODESC"
|
||||
|
||||
author=$(jq -r '.KPlugin.Authors[].Name' $metadata | paste -sd "," - | sed 's/,/, /g')
|
||||
[ -z "$author" ] || [ "$author" = "null" ] && author="?"
|
||||
|
||||
icon=$(jq -r '.KPlugin.Icon' $metadata)
|
||||
local fallbackIcon="start-here-kde-plasma-symbolic"
|
||||
if [ -z "$icon" ]; then
|
||||
icon=$fallbackIcon
|
||||
else
|
||||
local iconTheme=$(kreadconfig6 --file kdeglobals --group Icons --key Theme)
|
||||
local themeDir1="/usr/share/icons/$iconTheme/"
|
||||
local themeDir2="$HOME/.local/share/icons/$iconTheme/"
|
||||
local themeDir3="$HOME/.icons/$iconTheme/"
|
||||
! find -L "$themeDir1" "$themeDir2" "$themeDir3" \
|
||||
-type f -name "$icon.svg" -print -quit 2>/dev/null | grep -q . \
|
||||
&& icon=$fallbackIcon
|
||||
fi
|
||||
|
||||
url="https://store.kde.org/p/$contentId"
|
||||
repo="kde-store"
|
||||
type=$(get_package_type)
|
||||
|
||||
if ! jq -e ".KPackageStructure == \"$type\"" "$metadata" >/dev/null 2>&1; then
|
||||
updateJson $metadata "$(jq ". + { \"KPackageStructure\": \"$type\" }" $metadata)"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
get_download_link() {
|
||||
local signed=()
|
||||
local files=0
|
||||
|
||||
while read -r download_version; do
|
||||
((files++))
|
||||
if [[ "$latestVer" == "$(clearVer "$(xmllint --xpath "string(//id[text()='$contentId']/../$download_version)" $XML)")" ]]; then
|
||||
signed+=("${download_version#download_version}")
|
||||
fi
|
||||
done < <(xmllint --xpath "//content[id='$contentId']/*" $XML | grep -o 'download_version[0-9]\+' | uniq)
|
||||
|
||||
if [[ $files -eq 1 || ${#signed[@]} -eq 1 ]]; then
|
||||
echo $(xmllint --xpath "string(//id[text()='$contentId']/../downloadlink${signed[0]:-1})" $XML) > $tempDir/link
|
||||
return 0
|
||||
else
|
||||
[[ $files -eq 0 ]] && return 1
|
||||
[[ ${#signed[@]} -eq 0 ]] && return 2
|
||||
(( ${#signed[@]} > 1 )) && return 3
|
||||
fi
|
||||
}
|
||||
|
||||
make_updates_list() {
|
||||
local out=""
|
||||
for package in "${packages[@]}"; do
|
||||
get_package_info
|
||||
[[ $? -ne 0 ]] && continue
|
||||
out+="{\"NM\": \"${name}\","
|
||||
out+="\"RE\": \"${repo}\","
|
||||
out+="\"TP\": \"${type}\","
|
||||
out+="\"CN\": \"${contentId}\","
|
||||
out+="\"IN\": \"${icon}\","
|
||||
out+="\"DE\": \"${description}\","
|
||||
out+="\"AU\": \"${author}\","
|
||||
out+="\"VO\": \"${currentVer}\","
|
||||
out+="\"VN\": \"${latestVer}\","
|
||||
out+="\"LN\": \"${url}\"}",
|
||||
done
|
||||
|
||||
echo -e "[${out%,}]" > $updatesList
|
||||
}
|
||||
|
||||
check_packages_updates() {
|
||||
command -v jq >/dev/null || { echo 127; exit; }
|
||||
|
||||
makeTempDir
|
||||
XML="$tempDir/api.kde-look.xml"
|
||||
updatesList="$tempDir/update.list.json"
|
||||
trap cleanup EXIT
|
||||
|
||||
declare -a packages
|
||||
get_packages
|
||||
|
||||
fetch_api_data
|
||||
case $? in
|
||||
1) echo 1; exit ;;
|
||||
2) echo 2; exit ;;
|
||||
3) echo 3; exit ;;
|
||||
esac
|
||||
|
||||
make_updates_list
|
||||
jq . $updatesList
|
||||
}
|
||||
|
||||
download_package() {
|
||||
mkdir -p $tempDir/$name/unpacked
|
||||
link=$(cat $tempDir/link)
|
||||
package="$tempDir/$name/$(basename "${link}")"
|
||||
|
||||
curl -s -o $package --connect-timeout 5 --retry 2 --max-time 120 --request GET --location --url "$link" 2>/dev/null
|
||||
|
||||
[ ! -s "$package" ] && return 1
|
||||
|
||||
bsdtar -xf "$package" -C "$tempDir/$name/unpacked" || return 2
|
||||
|
||||
metadata=$(find $tempDir/$name/unpacked -name metadata.json)
|
||||
[ -z "metadata" ] && return 3
|
||||
|
||||
jq . $metadata >/dev/null 2>&1 || return 4
|
||||
|
||||
if ! jq -e ".KPackageStructure == \"$type\"" "$metadata" >/dev/null 2>&1; then
|
||||
updateJson $metadata "$(jq ". + { \"KPackageStructure\": \"$type\" }" $metadata)"
|
||||
fi
|
||||
|
||||
updateJson $metadata "$(jq --arg new_value "$latestVer" '.KPlugin.Version = $new_value' $metadata)"
|
||||
|
||||
if [[ $name == "apdatifier" && $trayEnabledByDefault == "true" ]]; then
|
||||
if jq -e '.KPlugin.EnabledByDefault == false' "$metadata" > /dev/null; then
|
||||
updateJson $metadata "$(jq '.KPlugin.EnabledByDefault = true' $metadata)"
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
upgrade_all_packages() {
|
||||
dependencies "jq"
|
||||
|
||||
makeTempDir
|
||||
XML="$tempDir/api.kde-look.xml"
|
||||
updatesList="$tempDir/update.list.json"
|
||||
trap cleanup EXIT
|
||||
|
||||
declare -a packages
|
||||
get_packages
|
||||
|
||||
fetch_api_data & printWhile $! "$WIDGETS_FETCH"
|
||||
[[ $exitCode -ne 0 ]] && exit
|
||||
|
||||
make_updates_list & printWhile $! "$WIDGETS_CHECK"
|
||||
|
||||
if [[ -s "$updatesList" ]] && jq -e '(. | length) > 0' "$updatesList" > /dev/null 2>&1; then
|
||||
echo
|
||||
jq -c 'sort_by(.NM)[]' "$updatesList" | while read -r info; do
|
||||
echo -e "$(colorize white bold $(echo "$info" | jq -r '.NM')) \t \
|
||||
$(colorize yellow bold $(echo "$info" | jq -r '.TP')) \t \
|
||||
$(colorize red bold $(echo "$info" | jq -r '.VO')) \t \
|
||||
$(colorize white "->") \t \
|
||||
$(colorize green bold $(echo "$info" | jq -r '.VN'))"
|
||||
done | column -t
|
||||
echo
|
||||
|
||||
if [[ $widgetConfirmation = true ]]; then
|
||||
while true; do
|
||||
printQuestion "$WIDGETS_PROCEED"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) echo; break;;
|
||||
[Nn]*|"") return 0;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
updated=false
|
||||
while read -r info; do
|
||||
name=$(echo "$info" | jq -r '.NM')
|
||||
contentId=$(echo "$info" | jq -r '.CN')
|
||||
latestVer=$(echo "$info" | jq -r '.VN')
|
||||
type=$(echo "$info" | jq -r '.TP')
|
||||
|
||||
echo "$(colorize blue bold "$ICO_EXEC $name ($latestVer)")"
|
||||
|
||||
get_download_link & printWhile $! "$WIDGETS_LINK"
|
||||
[[ $exitCode -ne 0 ]] && { echo; continue; }
|
||||
|
||||
download_package & printWhile $! "$WIDGETS_DOWNLOADING"
|
||||
[[ $exitCode -ne 0 ]] && { echo; continue; }
|
||||
|
||||
kpackagetool6 -t $type -u $(dirname $(find $tempDir/$name -name "metadata.json")) 2>/dev/null
|
||||
updated=true
|
||||
sleep 1
|
||||
echo
|
||||
done < <(jq -c 'sort_by(.NM)[]' "$updatesList")
|
||||
|
||||
[[ $updated = true ]] && restartPlasmashell
|
||||
else
|
||||
printDone "$MNG_DONE"
|
||||
fi
|
||||
}
|
||||
|
||||
upgrade_package() {
|
||||
[ -n "$1" ] && contentId="$1" || exit
|
||||
[ -n "$2" ] && name="$2" || exit
|
||||
|
||||
dependencies "jq"
|
||||
|
||||
mkdir -p $tempDir/$name/unpacked
|
||||
XML="$tempDir/$name/api.kde-look.xml"
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
{
|
||||
curl -s -o $XML --connect-timeout 5 --retry 2 --max-time 60 --request GET --url "https://api.kde-look.org/ocs/v1/content/data/$contentId" 2>/dev/null
|
||||
if xmllint --noout $XML 2>/dev/null; then
|
||||
statuscode=$(xmllint --xpath "string(//ocs/meta/statuscode)" $XML)
|
||||
case $statuscode in
|
||||
100) return 0 ;;
|
||||
200) return 2 ;;
|
||||
*) return 3 ;;
|
||||
esac
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
} & printWhile $! "$WIDGETS_FETCH"
|
||||
[[ $exitCode -ne 0 ]] && exit
|
||||
|
||||
latestVer=$(clearVer "$(xmllint --xpath "string(//id[text()='$contentId']/../version)" $XML)")
|
||||
type=$(get_package_type)
|
||||
|
||||
get_download_link & printWhile $! "$WIDGETS_LINK"
|
||||
[[ $exitCode -ne 0 ]] && exit
|
||||
|
||||
download_package & printWhile $! "$WIDGETS_DOWNLOADING"
|
||||
[[ $exitCode -ne 0 ]] && exit
|
||||
|
||||
kpackagetool6 -t $type -u $(dirname $(find $tempDir/$name -name "metadata.json")) 2>/dev/null
|
||||
sleep 1
|
||||
echo
|
||||
|
||||
restartPlasmashell
|
||||
}
|
||||
|
||||
list_current_packages() {
|
||||
declare -a packages
|
||||
get_packages
|
||||
local out=""
|
||||
for package in "${packages[@]}"; do
|
||||
local json="$package/metadata.json"; [ -s "$json" ] || continue
|
||||
local name="$(jq -r '.KPlugin.Name' $json)"; [ -z "$name" ] && continue
|
||||
local currentVer=$(clearVer "$(jq -r '.KPlugin.Version' "$json")"); [ -z "$currentVer" ] && continue
|
||||
out+="{\"NM\": \"${name}\","
|
||||
out+="\"VO\": \"${currentVer}\"},\n"
|
||||
done
|
||||
echo -e $out
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"list") list_current_packages;;
|
||||
"check") check_packages_updates;;
|
||||
"upgrade") shift; upgrade_package $1 $2;;
|
||||
"upgradeAll") upgrade_all_packages;;
|
||||
*) exit;;
|
||||
esac
|
||||
@ -0,0 +1,189 @@
|
||||
998890 com.bxabi.bumblebee-indicator
|
||||
998913 org.kde.plasma.awesomewidget
|
||||
1155946 com.dschopf.plasma.qalculate
|
||||
1288430 org.kde.plasma.shutdownorswitch
|
||||
1377704 org.kde.mediabar
|
||||
1384066 org.kde.workraveApplet
|
||||
1396415 com.github.solant.plasmataker
|
||||
1627256 com.github.xyz32.timekeeper
|
||||
1804745 com.github.heqro.day-night-switcher
|
||||
1898708 org.nielsvm.plasma.menupager
|
||||
2079446 dev.vili.sahkoporssi
|
||||
2100417 org.kde.mcwsremote
|
||||
2100418 org.kde.olib.thermalmonitor
|
||||
2107649 org.kde.panel.transparency.toggle
|
||||
2112443 com.github.tilorenz.compact_pager
|
||||
2113872 com.github.korapp.cloudflare-warp
|
||||
2114471 de.davidhi.ddcci-brightness
|
||||
2115883 org.kde.plasma.simplekickoff
|
||||
2117117 com.dv.fokus
|
||||
2118132 com.github.stepan-zubkov.days-to-new-year
|
||||
2118492 com.github.korapp.nordvpn
|
||||
2126775 com.github.tilorenz.timeprogressbar
|
||||
2128047 luisbocanegra.panelspacer.extended
|
||||
2128143 plasmusic-toolbar
|
||||
2128477 luisbocanegra.intel.gpu.monitor
|
||||
2129423 org.kde.windowtitle
|
||||
2130222 luisbocanegra.panel.modes.switcher
|
||||
2130541 org.kde.archupdatechecker
|
||||
2130967 luisbocanegra.panel.colorizer
|
||||
2131364 com.github.korapp.homeassistant
|
||||
2131462 org.kde.plasma.plasm6desktopindicator
|
||||
2132405 com.github.dhruv8sh.year-progress-mod
|
||||
2132554 com.himdek.kde.plasma.overview
|
||||
2132555 com.himdek.kde.plasma.runcommand
|
||||
2134470 a2n.archupdate.plasmoid
|
||||
2135509 com.github.antroids.application-title-bar
|
||||
2135511 org.kde.placesWidget
|
||||
2135552 org.kde.plasma.yesplaymusic-lyrics
|
||||
2135642 com.github.prayag2.minimalistclock
|
||||
2135653 com.github.prayag2.modernclock
|
||||
2135796 com.github.exequtic.apdatifier
|
||||
2135799 com.github.k-donn.plasmoid-wunderground
|
||||
2135898 com.dv.uswitcher
|
||||
2136288 org.kde.Big.Clock
|
||||
2136291 zayron.chaac.weather
|
||||
2136295 Clock.Asitoki.Color
|
||||
2136299 CircleClock
|
||||
2136302 zayron.almanac
|
||||
2136307 Minimal.chaac.weather
|
||||
2136321 com.Petik.clock
|
||||
2136329 weather.bicolor.widget
|
||||
2136505 org.kde.netspeedWidget
|
||||
2136546 com.nemmayan.clock
|
||||
2136631 com.github.scriptinator
|
||||
2136636 com.github.zren.commandoutput
|
||||
2136852 org.kde.latte.separator
|
||||
2136860 com.github.zren.alphablackcontrol
|
||||
2136933 org.kde.plasma.advancedradio
|
||||
2136963 luisbocanegra.kdematerialyou.colors
|
||||
2137016 org.kde.plasma.Beclock
|
||||
2137185 com.github.zren.dailyforecast
|
||||
2137197 com.github.zren.condensedweather
|
||||
2137217 org.kde.plasma.scpmk
|
||||
2137231 com.github.eatsu.spaceraspager
|
||||
2137418 zayron.simple.separator
|
||||
2137431 com.github.zren.simpleweather
|
||||
2137675 com.gitlab.scias.advancedreboot
|
||||
2137726 org.zayronxio.vector.clock
|
||||
2137844 org.kde.plasma.catwalk
|
||||
2138251 org.kpple.kppleMenu
|
||||
2138283 ink.chyk.minimumMediaController
|
||||
2138365 optimus-gpu-switcher
|
||||
2138473 org.kde.plasma.videocard
|
||||
2138476 lenovo-conservation-mode-switcher
|
||||
2138485 com.github.boraerciyas.controlcentre
|
||||
2138746 org.kde.plasma.pminhibition
|
||||
2138853 org.kde.Date.Bubble.P6
|
||||
2138907 org.kde.latte.spacer
|
||||
2139337 split-clock
|
||||
2139541 com.github.configurable_button
|
||||
2139890 Plasma.Control.Hub
|
||||
2140275 com.github.davide-sd.ip_address
|
||||
2140856 d4rkwzd.colorpicker-tray
|
||||
2141133 org.kde.MinimalMusic.P6
|
||||
2142681 Audio.Wave.Widget
|
||||
2142716 com.github.zren.tiledmenu
|
||||
2143899 org.kde.plasma.resources-monitor
|
||||
2144212 AndromedaLauncher
|
||||
2144426 org.previewqt.previewqt.plasmoidpreviewqt
|
||||
2144969 SoloDay.P6
|
||||
2145065 com.github.liujed.rssfeeds
|
||||
2145280 com.github.DenysMb.Kicker-AppsOnly
|
||||
2145723 luisbocanegra.desktop.wallpaper.effects
|
||||
2146553 org.kde.plasma.ginti
|
||||
2147850 zayron.almanac.V2
|
||||
2147871 org.kde.plasma.clearclock
|
||||
2147882 org.kde.windowtitle.Fork
|
||||
2148373 lyrics-on-panel-plasma6
|
||||
2148469 thot.observer.ram
|
||||
2148472 thot.observer.cpu
|
||||
2150544 org.kde.plasma.composeEmail
|
||||
2150610 com.gitlab.scias.plasmavantage
|
||||
2150916 org.kde.paneltransparencybutton
|
||||
2151247 org.kde.plasma.win7showdesktop
|
||||
2151576 ppk.plasma6.hydrobot
|
||||
2151585 com.samirgaire10.Brave-plasma6
|
||||
2151600 com.samirgaire10.chatgpt-plasma6
|
||||
2151622 com.samirgaire10.Deepl-plasma6
|
||||
2151683 com.samirgaire10.google_gemini-plasma6
|
||||
2151687 com.samirgaire10.Google-plasma6
|
||||
2151694 com.samirgaire10.GoogleTranslater-plasma6
|
||||
2151931 org.kde.windowbuttons
|
||||
2151962 com.samirgaire10.perplexityAi-plasma6
|
||||
2152642 com.samirgaire10.blackbox.AI_plasma6
|
||||
2153869 org.kde.plasma.private.BusyTasksPlasmoid
|
||||
2154331 com.softtechok.systemmonitorplasmoid
|
||||
2154894 org.51n7.kMenu
|
||||
2157390 org.latgardi.darwinmenu
|
||||
2158349 Almanac.Asimetric
|
||||
2158358 org.kde.losungen6
|
||||
2159592 com.samirgaire10.Ollama-plasma6
|
||||
2160638 com.softtechok.processmonitor
|
||||
2161461 Weather.IntiSol.kde
|
||||
2162586 org.kde.dwardor.fanspeedmonitor
|
||||
2163165 OnlyText.Date.Kde
|
||||
2163199 com.samirgaire10.DuckDuckGo-plasma6
|
||||
2163340 ChatAI-Plasmoid
|
||||
2163368 Circle.Music.Widget
|
||||
2164459 org.kde.redshiftControl6
|
||||
2164590 org.kde.plasma.mediacontroller.panel
|
||||
2164679 com.github.chrtall.kppleMenu
|
||||
2166656 Aligned.Music.Widget
|
||||
2166934 adhe.runcommand
|
||||
2167594 adhe.dittomenu
|
||||
2167836 com.github.jonmagon.plasma-screendimmer
|
||||
2168131 Date.Urban.kde
|
||||
2168466 Rates.Mercurius.Widget
|
||||
2169597 com.github.danielwjchen.folding-at-home-plasma-widget
|
||||
2170656 com.samirgaire10.Github-plasma6
|
||||
2171750 adhe.menu.11
|
||||
2172470 org.magpie.sonomatic.separator
|
||||
2173278 org.kde.plasma.powerusage
|
||||
2174238 adhe.launchpadPlasma
|
||||
2174265 Chaac.Complete.Weather
|
||||
2175475 Redmi.Clock
|
||||
2179264 org.magpie.nightglow.separator
|
||||
2179317 Start.Next.Menu
|
||||
2179829 com.kevinbburns.grammarly
|
||||
2179833 Small.Weather.Plasma6
|
||||
2180314 Soniq.Widget
|
||||
2180887 DeepinMenu.Classic
|
||||
2182964 adhe.menu.11 #Ignored, not a unique ID
|
||||
2183258 Compact.Menu
|
||||
2183752 luisbocanegra.cursor.eyes
|
||||
2184730 OnlyText.Date.Kde.v2
|
||||
2184747 ChatQT-Plasmoid
|
||||
2185174 Seeua.Weather
|
||||
2185549 org.kde.plasma.pipewiresettings
|
||||
2185626 org.kde.plasma.dockio
|
||||
2185931 asus-battery-health-switcher
|
||||
2185962 Start.11.Simple
|
||||
2186442 Music.Waves
|
||||
2187344 MusicMini
|
||||
2187962 MechanicalClock
|
||||
2188275 com.junongx.kde.currentlyplaying
|
||||
2188581 Lyndo.Kde
|
||||
2189544 Plain.Circle.Weather
|
||||
2189637 Solid.Plain.Music
|
||||
2193670 qinvvv.qtodo.widget
|
||||
2194992 org.kde.osymUpdates
|
||||
2195583 org.ruiny.NowPlaying
|
||||
2196105 KdeControlStation
|
||||
2196149 ff.qtodo.widget
|
||||
2196368 com.github.imoize.ollamacontrol
|
||||
2199942 Bix.Plasma.Player
|
||||
2200890 org.dhruv8sh.kara
|
||||
2201084 Emulator.Spectrum.Audio
|
||||
2202249 com.espaker.simpleradioplayer-plasma6
|
||||
2204865 org.dr42.analogclock
|
||||
2206628 runcommand.fork
|
||||
2211264 com.ekaaty.vinyl-launcher
|
||||
2216429 org.kde.plasma.ipaddress
|
||||
2216432 org.kde.plasma.shamsi-calendar
|
||||
2217171 Concepto.menu.12
|
||||
2217301 hermes.observer.ram.P6
|
||||
2217313 hermes.observer.cpu.P6
|
||||
2224768 org.kde.plasma.dayprogress
|
||||
2225123 org.kde.olib.pinpanel
|
||||
2225378 material.clock
|
||||
@ -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