├── .gitignore ├── LICENSES └── GPL-3.0-or-later.txt ├── README.md ├── add-subtitles ├── allow-ip ├── alphabetize ├── archive ├── backup ├── battery-notify ├── cleanup ├── clipify ├── copy-playlist ├── count-commits ├── count-lines ├── countries ├── coverart ├── cryptdir ├── decode-base64 ├── delete-swp ├── drop-caches ├── dump-streams ├── embed-script ├── epub-convert ├── epub-fixup ├── extract-audio ├── extract-subs ├── format ├── gen-playlist ├── get-song ├── get-webseries ├── gifify ├── git-archive ├── git-clone-all ├── git-gc ├── git-init ├── git-partial-push ├── globals ├── join-vids ├── kvm-clear ├── kvm-cp ├── kvm-ls ├── kvm-mv ├── langs ├── list-ass-fonts ├── make-watch ├── mpv-play ├── mpv-ssh ├── nm-ovpn-delete ├── nm-ovpn-import ├── nm-wg-delete ├── nm-wg-import ├── ova-to-qcow2 ├── pdf-fixup ├── pdf-split ├── ping-test ├── print-colors ├── print-fontstring ├── quickdate ├── rank-mirrorlist ├── rate-song ├── reencode ├── rename-user ├── render ├── restart-network ├── rmlink ├── rotate ├── scourify ├── search-docs ├── seek-and-destroy ├── set-gdm-theme ├── set-replaygain ├── shrink-vm ├── split-cue ├── strip-audio ├── strip-metadata ├── strip-yaml ├── swap-symlink ├── swapfile ├── switch-theme ├── torrentstat-sync ├── upgrade ├── vidname-cleanup ├── vpn-killswitch ├── wget-mirror ├── whattodo ├── write-entry └── wsl-browser-open /allow-ip: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2019 - 2024 sudorook 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | # This program is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by the Free 9 | # Software Foundation, either version 3 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program. If not, see . 19 | 20 | set -euo pipefail 21 | 22 | ROOT="$(dirname "${0}")" 23 | 24 | source "${ROOT}"/globals 25 | 26 | ! check_command grep ip sed && exit 3 27 | 28 | # Command line script to add a routing table entry for an IP address. Useful 29 | # for accessing network printers when behind a VPN that interferes with access 30 | # to local machines addressed by a local/internal DNS. 31 | 32 | function print_usage { 33 | show_header "Usage: allow-ip -i|--ip -m|--mask " 34 | } 35 | 36 | OPTIONS=hi:m: 37 | LONGOPTIONS=help,ip:,mask: 38 | PARSED=$(getopt -o "${OPTIONS}" --long "${LONGOPTIONS}" -n "${0}" -- "${@}") 39 | eval set -- "${PARSED}" 40 | 41 | while [ ${#} -ge 1 ]; do 42 | case "${1}" in 43 | -i | --ip) 44 | # If IP address given as xxx.xxx.xxx.xxx: 45 | if [[ "${2}" == "$(echo "${2}" | 46 | sed -n "s/^\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)$/\1/p")" ]]; then 47 | IP="${2}" 48 | # If IP address and netmask given as xxx.xxx.xxx.xxx/xx: 49 | elif [[ "${2}" == "$(echo "${2}" | 50 | sed -n "s/^\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\/[0-9]\+\)$/\1/p")" ]]; then 51 | IP="$(echo "${2}" | cut -d"/" -f1)" 52 | MASK="$(echo "${2}" | cut -d"/" -f2)" 53 | fi 54 | shift 2 55 | ;; 56 | -m | --mask) 57 | if ((${2} >= 0 && ${2} <= 32)); then 58 | MASK="${2}" 59 | fi 60 | shift 2 61 | ;; 62 | -h | --help) 63 | print_usage 64 | exit 65 | ;; 66 | --) 67 | shift 68 | break 69 | ;; 70 | *) 71 | show_error "What was that?" 72 | exit 3 73 | ;; 74 | esac 75 | done 76 | 77 | # Exit if no ip address supplied. 78 | if ! [[ -v IP ]]; then 79 | show_error "Give me the IP address." 80 | exit 3 81 | fi 82 | 83 | # If the net mask is not specified, only allow the IP address given. 84 | MASK="${MASK:-32}" 85 | 86 | GATEWAY=$(ip route list | 87 | grep -e "enp[0-9]\+s[0-9a-f]\+" -e "wlp[0-9]\+s[0-9a-f]\+" | 88 | sed -n "s/^default via \([0-9\.]\+\) dev .*/\1/p" | head -1) 89 | 90 | IFACE=$(ip route show | 91 | grep -e "${GATEWAY}" | 92 | sed -n "s/^default via [0-9\.]\+ dev \([a-z0-9]\+\) .*/\1/p") 93 | 94 | sudo ip route add "${IP}/${MASK}" via "${GATEWAY}" dev "${IFACE}" 95 | -------------------------------------------------------------------------------- /alphabetize: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2017 - 2024 sudorook 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | # This program is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by the Free 9 | # Software Foundation, either version 3 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program. If not, see . 19 | 20 | set -Eeuo pipefail 21 | 22 | ROOT="$(dirname "${0}")" 23 | 24 | source "${ROOT}"/globals 25 | 26 | function print_usage { 27 | show_header "Usage: alphabetize" 28 | show_listitem "\ 29 | -b|--backup back up the original file (default: false) 30 | -r|--reverse reverse output (default: false) 31 | -h|--help print (this) error message" 32 | } 33 | 34 | OPTIONS=bhr 35 | LONGOPTIONS=backup,help,reverse 36 | PARSED=$(getopt -o "${OPTIONS}" --long "${LONGOPTIONS}" -n "${0}" -- "${@}") 37 | eval set -- "${PARSED}" 38 | 39 | ISBACKUP=false 40 | REVERSE=false 41 | while [ ${#} -ge 1 ]; do 42 | case "${1}" in 43 | -b | --backup) 44 | ISBACKUP=true 45 | shift 1 46 | ;; 47 | -h | --help) 48 | print_usage 49 | exit 50 | ;; 51 | -r | --reverse) 52 | REVERSE=true 53 | shift 54 | ;; 55 | --) 56 | shift 57 | break 58 | ;; 59 | *) 60 | show_error "Error" 61 | exit 3 62 | ;; 63 | esac 64 | done 65 | 66 | if [ -t 0 ]; then 67 | if ! [ -e "${1}" ]; then 68 | show_error "ERROR: ${1@Q} does not exist." 69 | exit 3 70 | fi 71 | 72 | TMP="$(mktemp)" 73 | trap 'rm -f "${TMP}"; exit' INT TERM ERR EXIT 74 | 75 | if "${REVERSE}"; then 76 | sort -f -u -r "${1}" > "${TMP}" 77 | else 78 | sort -f -u "${1}" > "${TMP}" 79 | fi 80 | 81 | if "${ISBACKUP}"; then 82 | mv "${1}" "${1}.bak" 83 | fi 84 | 85 | mv "${TMP}" "${1}" 86 | else 87 | if "${REVERSE}"; then 88 | cat | sort -f -u -r 89 | else 90 | cat | sort -f -u 91 | fi 92 | fi 93 | -------------------------------------------------------------------------------- /backup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2015 - 2024 sudorook 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | # This program is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by the Free 9 | # Software Foundation, either version 3 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program. If not, see . 19 | 20 | set -euo pipefail 21 | 22 | ROOT="$(dirname "${0}")" 23 | 24 | source "${ROOT}"/globals 25 | 26 | ! check_command grep rsync && exit 3 27 | 28 | # 29 | # Functions 30 | # 31 | 32 | # Check if path for backup1 exists. 33 | function backup_exists() { 34 | if ! [[ -v backup1 ]]; then 35 | show_error "backup1 variable is unset." 36 | exit 1 37 | fi 38 | if ! [ -d "${backup1}" ]; then 39 | show_error "Backup destination ${backup1@Q} does not exist." 40 | exit 1 41 | fi 42 | } 43 | 44 | # Backup home directory to Backups 1 and 2 45 | function sync_home() { 46 | local cmd 47 | local excludedir 48 | 49 | cmd="rsync -Pradog --delete-excluded ${HOME}/ ${backup1}/${USER^}/" 50 | cmd="${cmd} ${vboxdir:+--exclude ${vboxdir#"${HOME}"/}\\*}" 51 | cmd="${cmd} ${kvmdir:+--exclude ${kvmdir#"${HOME}"/}\\*}" 52 | for excludedir in "${EXCLUDEDIRS[@]}"; do 53 | cmd="${cmd} --exclude ${excludedir#"${HOME}"/}\\*" 54 | done 55 | 56 | show_info "Syncing ${HOME@Q} to ${backup1@Q}." 57 | mkdir -p "${backup1}/${USER^}/" 58 | bash -c "${cmd}" 59 | sync 60 | show_success "Done." && echo 61 | 62 | if [[ -v backup2 ]] && [ -d "${backup2}" ]; then 63 | show_info "Syncing ${backup1@Q} to ${backup2@Q}." 64 | sudo rsync -Pradog --delete-excluded \ 65 | "${backup1}/" "${backup2}/" \ 66 | --exclude ".Trash-1000/*" 67 | sync 68 | show_success "Done." && echo 69 | fi 70 | } 71 | 72 | # Backup VMS to Backups 1 and 2 73 | function sync_vms() { 74 | show_info "Syncing VMs in ${HOME@Q} to ${backup1@Q}." 75 | local cmd 76 | if [[ -n "${kvmdir}" ]] && [[ -d "${kvmdir}" ]]; then 77 | mkdir -p "${backup1}/Images/KVM" 78 | cmd="${cmd:+${cmd} && }rsync -Pradog --sparse ${kvmdir}/ ${backup1}/Images/KVM/" 79 | fi 80 | if [[ -n "${vboxdir}" ]] && [[ -d "${vboxdir}" ]]; then 81 | mkdir -p "${backup1}/Images/VirtualBox" 82 | cmd="${cmd:+${cmd} && }rsync -Pradog ${vboxdir}/ ${backup1}/Images/VirtualBox/" 83 | fi 84 | if [ -n "${cmd}" ]; then 85 | sudo bash -c "${cmd}" 86 | fi 87 | sync 88 | show_success "Done." && echo 89 | 90 | if [ -d "${backup2}" ]; then 91 | show_info "Syncing VMs in ${backup1@Q} to ${backup2@Q}." 92 | cmd= 93 | if [[ -d "${backup1}/Images/KVM" ]]; then 94 | mkdir -p "${backup2}/Images/KVM" 95 | cmd="${cmd:+${cmd} && }rsync -Pradog --sparse ${backup1}/Images/KVM/ ${backup2}/Images/KVM/" 96 | fi 97 | if [[ -d "${backup1}/Images/VirtualBox" ]]; then 98 | mkdir -p "${backup2}/Images/VirtualBox" 99 | cmd="${cmd:+${cmd} && }rsync -Pradog ${backup1}/Images/VirtualBox/ ${backup2}/Images/VirtualBox/" 100 | fi 101 | if [ -n "${cmd}" ]; then 102 | sudo bash -c "${cmd}" 103 | fi 104 | sync 105 | show_success "Done." && echo 106 | fi 107 | } 108 | 109 | # 110 | # Main 111 | # 112 | 113 | backup_exists 114 | if grep -q "${HOME}" /etc/mtab; then 115 | show_error "ERROR: Home directories found in /etc/mtab. Unmount and try again." 116 | exit 1 117 | fi 118 | 119 | SYNC_HOME=false 120 | SYNC_VM=false 121 | 122 | EXCLUDEDIRS=() 123 | 124 | OPTIONS=ahv 125 | LONGOPTIONS=all,home,vms 126 | PARSED=$(getopt -o "${OPTIONS}" --long "${LONGOPTIONS}" -n "${0}" -- "${@}") 127 | eval set -- "${PARSED}" 128 | 129 | while [ ${#} -ge 1 ]; do 130 | case ${1} in 131 | -a | --all) 132 | SYNC_HOME=true 133 | SYNC_VM=true 134 | shift 135 | break 136 | ;; 137 | -h | --home) 138 | SYNC_HOME=true 139 | shift 140 | ;; 141 | -v | --vms) 142 | SYNC_VM=true 143 | shift 144 | ;; 145 | --) 146 | shift 147 | break 148 | ;; 149 | *) 150 | show_error "ERROR: Invalid flag ${1@Q}." 151 | exit 3 152 | ;; 153 | esac 154 | done 155 | 156 | ${SYNC_HOME} && sync_home 157 | ${SYNC_VM} && sync_vms 158 | -------------------------------------------------------------------------------- /battery-notify: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2017 - 2024 sudorook 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | # This program is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by the Free 9 | # Software Foundation, either version 3 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program. If not, see . 19 | 20 | set -eu 21 | 22 | # 23 | # Globals 24 | # 25 | 26 | if ! command -v notify-send > /dev/null; then 27 | exit 3 28 | fi 29 | 30 | # Thresholds 31 | LOW=40 32 | FULL=80 33 | CRITICAL=10 34 | 35 | # Icons 36 | LOW_ICON=battery-low 37 | FULL_ICON=battery-full-charging 38 | CRITICAL_ICON=battery-caution 39 | 40 | # Notification flags 41 | LOW_FLAG_SENT=false 42 | FULL_FLAG_SENT=false 43 | CRITICAL_FLAG_SENT=false 44 | 45 | BATTERY_PATH=/sys/class/power_supply/BAT0 46 | DISPLAY=:0 47 | LOCKFILE=/tmp/battery-notify.lock 48 | 49 | # Exit if lockfile already set by running battery-notify instance. 50 | if [ -e "${LOCKFILE}" ] && kill -0 "$(< "${LOCKFILE}")"; then 51 | exit 52 | fi 53 | 54 | trap 'rm -f ${LOCKFILE}; exit' INT TERM ERR EXIT 55 | echo $$ > ${LOCKFILE} 56 | 57 | while true; do 58 | if [ -e ${BATTERY_PATH} ]; then 59 | STATE=$(< ${BATTERY_PATH}/status) 60 | LEVEL=$(< ${BATTERY_PATH}/capacity) 61 | 62 | if [ "${STATE}" == "Discharging" ]; then 63 | # Send flag when battery drops below critical threshold. 64 | if [ "${LEVEL}" -le "${CRITICAL}" ]; then 65 | if ! ${CRITICAL_FLAG_SENT}; then 66 | notify-send -u critical -i ${CRITICAL_ICON} \ 67 | "Battery critical" \ 68 | "Battery is at ${LEVEL}%. Plug in the power supply now." 69 | CRITICAL_FLAG_SENT=true 70 | fi 71 | # If battery level is above critical, check if it is below the low 72 | # threshold and send flag if so. 73 | elif [ "${LEVEL}" -le "${LOW}" ]; then 74 | if ! "${LOW_FLAG_SENT}"; then 75 | notify-send -u critical -i ${LOW_ICON} \ 76 | "Battery low" \ 77 | "Battery is at ${LEVEL}%. Plug in the power supply soon." 78 | LOW_FLAG_SENT=true 79 | fi 80 | fi 81 | 82 | # Unset the full battery flag while discharging. 83 | FULL_FLAG_SENT=false 84 | fi 85 | 86 | if [ "${STATE}" == "Charging" ]; then 87 | # Send flag when battery is full (i.e. above the full battery threshold). 88 | if [ "${LEVEL}" -gt "${FULL}" ]; then 89 | # Check if the full battery message has been sent already. If not, send 90 | # it out. 91 | if ! ${FULL_FLAG_SENT}; then 92 | notify-send -u critical -i ${FULL_ICON} \ 93 | "Battery full" \ 94 | "Battery is at ${LEVEL}%. Unplug the power supply." 95 | FULL_FLAG_SENT=true 96 | fi 97 | fi 98 | 99 | # Unset critical battery flag if charging, and unset the low flag if the 100 | # battery level is above the critical threshold. 101 | CRITICAL_FLAG_SENT=false 102 | if [ "${LEVEL}" -gt "${CRITICAL}" ]; then 103 | LOW_FLAG_SENT=false 104 | fi 105 | fi 106 | fi 107 | sleep 120s 108 | done 109 | -------------------------------------------------------------------------------- /cleanup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2023 - 2024 sudorook 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | # This program is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by the Free 9 | # Software Foundation, either version 3 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program. If not, see . 19 | 20 | set -euo pipefail 21 | 22 | ROOT="$(dirname "${0}")" 23 | 24 | source "${ROOT}"/globals 25 | 26 | ! check_command cmp jq sed && exit 3 27 | 28 | # 29 | # Functions 30 | # 31 | 32 | function delete_directory() { 33 | local dir="${1}" 34 | local dir_owner 35 | if [ -d "${dir}" ]; then 36 | dir_owner="$(stat -c "%U" "${dir}")" 37 | if [[ "${USER}" = "${dir_owner}" ]]; then 38 | if [[ -v 2 ]]; then 39 | show_info "${2}" 40 | fi 41 | rm -rf "${dir}" 42 | sync 43 | else 44 | show_warning "${dir@Q} owned by ${dir_owner@Q}. Skipping." 45 | fi 46 | fi 47 | } 48 | 49 | function delete_file() { 50 | local file="${1}" 51 | local file_owner 52 | if [ -f "${file}" ]; then 53 | file_owner="$(stat -c "%U" "${file}")" 54 | if [[ "${USER}" = "${file_owner}" ]]; then 55 | if [[ -v 2 ]]; then 56 | show_info "${2}" 57 | fi 58 | rm -f "${file}" 59 | sync 60 | else 61 | show_warning "${file@Q} owned by ${file_owner@Q}. Skipping." 62 | fi 63 | fi 64 | } 65 | 66 | function delete_config_section() { 67 | local file="${1}" 68 | local file_owner 69 | local header="${2}" 70 | local tmp 71 | if [ -f "${file}" ]; then 72 | file_owner="$(stat -c "%U" "${file}")" 73 | if [[ "${USER}" = "${file_owner}" ]]; then 74 | tmp="$(mktemp)" 75 | sed "/\[${header}\]/,/^\s*$/{d}" "${file}" | sed '${/^$/d}' > "${tmp}" 76 | if ! cmp -s "${tmp}" "${file}"; then 77 | if [[ -v 3 ]]; then 78 | show_info "${3}" 79 | fi 80 | mv "${tmp}" "${file}" 81 | else 82 | rm "${tmp}" 83 | fi 84 | sync 85 | else 86 | show_warning "${file@Q} owned by ${file_owner@Q}. Skipping." 87 | fi 88 | fi 89 | } 90 | 91 | function delete_json_section() { 92 | local file="${1}" 93 | local file_owner 94 | local path="${2}" 95 | local tmp 96 | if [ -f "${file}" ]; then 97 | file_owner="$(stat -c "%U" "${file}")" 98 | if [[ "${USER}" = "${file_owner}" ]]; then 99 | tmp="$(mktemp)" 100 | # Check if file end with newline. If it does not, then use '-j'. 101 | if [ "$(tail -n 1 "${file}" | wc -l)" -eq 0 ]; then 102 | jq -j "${path} = []" "${file}" > "${tmp}" 103 | else 104 | jq "${path} = []" "${file}" > "${tmp}" 105 | fi 106 | if ! cmp -s "${tmp}" "${file}"; then 107 | if [[ -v 3 ]]; then 108 | show_info "${3}" 109 | fi 110 | mv "${tmp}" "${file}" 111 | else 112 | rm "${tmp}" 113 | fi 114 | sync 115 | else 116 | show_warning "${file@Q} owned by ${file_owner@Q}. Skipping." 117 | fi 118 | fi 119 | } 120 | 121 | # 122 | # Main 123 | # 124 | 125 | # KDE Clipboard 126 | if check_command qdbus 2> /dev/null; then 127 | if qdbus > /dev/null 2>&1; then 128 | show_info "Clearing Klipper history." 129 | qdbus org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory 130 | fi 131 | elif check_command qdbus6 2> /dev/null; then 132 | if qdbus6 > /dev/null 2>&1; then 133 | show_info "Clearing Klipper history." 134 | qdbus6 org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory 135 | fi 136 | fi 137 | 138 | # Nvim state 139 | delete_directory \ 140 | "${HOME}/.local/state/nvim" \ 141 | "Clearing Neovim state." 142 | 143 | # Vim undo 144 | delete_directory \ 145 | "${HOME}/.vim/undo" \ 146 | "Clearing Vim undo history." 147 | 148 | # Recent files, etc. 149 | if check_command sweeper 2> /dev/null; then 150 | if [ -v DESKTOP_SESSION ]; then 151 | show_info "Running sweeper." 152 | sweeper --automatic 2> /dev/null 153 | fi 154 | fi 155 | 156 | # System-wide cleanup 157 | if check_command bleachbit 2> /dev/null; then 158 | if [ -f "${HOME}/.config/bleachbit/bleachbit.ini" ]; then 159 | show_info "Running BleachBit (${USER})." 160 | bleachbit -c --preset 161 | else 162 | show_warning "BleachBit not configured. Skipping." 163 | fi 164 | fi 165 | 166 | # Anki backups 167 | if [ -d "${HOME}/.local/share/Anki2" ]; then 168 | while read -r DIR; do 169 | delete_directory \ 170 | "${DIR}" \ 171 | "Clearing Anki ${DIR@Q}." 172 | done < <(find "${HOME}/.local/share/Anki2" -type d \( -name backups -o -name media.trash \)) 173 | fi 174 | 175 | # Ark history 176 | if [ -f "${HOME}"/.local/share/ark/ark_recentfiles ]; then 177 | TMP='[RecentFiles] 178 | files\size=0' 179 | if ! cmp -s "${HOME}"/.local/share/ark/ark_recentfiles <(echo "${TMP}"); then 180 | show_info "Clearing Ark history." 181 | echo "${TMP}" > "${HOME}"/.local/share/ark/ark_recentfiles 182 | fi 183 | fi 184 | 185 | # CMake caches. 186 | delete_directory \ 187 | "${HOME}/.cmake" \ 188 | "Deleting CMake cache." 189 | 190 | # KFileDialog history 191 | delete_config_section \ 192 | "${HOME}/.config/xdg-desktop-portal-kderc" \ 193 | "KFileDialog Settings" \ 194 | "Clearing KFileDialog history." 195 | 196 | # Okular recent files. 197 | delete_directory \ 198 | "${HOME}/.local/share/okular/docdata" \ 199 | "Deleting Okular document data." 200 | delete_config_section \ 201 | "${HOME}/.config/okularrc" \ 202 | "Recent Files" \ 203 | "Clearing Okular recent file history." 204 | 205 | # Gwenview image viewer history. 206 | delete_directory \ 207 | "${HOME}/.local/share/gwenview/recentfolders" \ 208 | "Deleting Gwenview folder history." 209 | delete_config_section \ 210 | "${HOME}/.config/gwenviewrc" \ 211 | "Recent Files" \ 212 | "Clearing Gwenview recent file history." 213 | 214 | # Calibre 215 | delete_directory \ 216 | "${HOME}/Documents/Library/.caltrash/" \ 217 | "Emptying Calibre trash." 218 | delete_json_section \ 219 | "${HOME}/.config/calibre/viewer-webengine.json" \ 220 | ".session_data.standalone_recently_opened" \ 221 | "Clearing ebook-viewer history." 222 | 223 | # Remove trash directory 224 | delete_directory \ 225 | "${HOME}/.local/share/Trash/" \ 226 | "Deleting trash directory." 227 | 228 | # Trash at Share/ mountpoint 229 | delete_directory \ 230 | "${HOME}/Share/.Trash-1000" \ 231 | "Deleting ~/Share trash directory." 232 | 233 | # Python history 234 | delete_file \ 235 | "${HOME}/.python_history" \ 236 | "Deleting Python REPL history." 237 | 238 | # Node REPL history 239 | delete_file \ 240 | "${HOME}"/.node_repl_history \ 241 | "Deleting node REPL history." 242 | 243 | # mpv watch_later 244 | delete_directory \ 245 | "${HOME}/.local/state/mpv/watch_later" \ 246 | "Clearing mpv history." 247 | 248 | # less history 249 | delete_file \ 250 | "${HOME}/.local/state/lesshst" \ 251 | "Deleting less history." 252 | 253 | # wget history 254 | delete_file \ 255 | "${HOME}/.wget-hsts" \ 256 | "Deleting wget history." 257 | -------------------------------------------------------------------------------- /clipify: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2018 - 2024 sudorook 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | # This program is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by the Free 9 | # Software Foundation, either version 3 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program. If not, see . 19 | 20 | set -Eeuo pipefail 21 | 22 | ROOT="$(dirname "${0}")" 23 | 24 | source "${ROOT}"/globals 25 | 26 | ! check_command bc ffmpeg ffprobe sed && exit 3 27 | 28 | # Global functions. 29 | function print_usage() { 30 | show_header "Usage:" 31 | cat << EOF 32 | -i --input input video (e.g. input.mp4)" 33 | -o --output output video (e.g. output.mp4)" 34 | -s --start starting timestamp (e.g. 01:20.5)" 35 | -l --length length in seconds (e.g. 3.5)" 36 | -e --end ending timestamp (e.g. 01:20.5)" 37 | EOF 38 | } 39 | 40 | function scan_input { 41 | local vstring 42 | local sstring 43 | 44 | fstring="$(ffprobe -probesize 1G -analyzeduration 1G -v error -show_format file:"${IN}")" 45 | vstring="$(ffprobe -probesize 1G -analyzeduration 1G -v error \ 46 | -select_streams v:0 -show_streams file:"${IN}")" 47 | sstring="$(ffprobe -probesize 1G -analyzeduration 1G -v error \ 48 | -select_streams s:0 -show_streams file:"${IN}")" 49 | 50 | if ! [[ -v VCODEC ]]; then 51 | VCODEC="$(echo "${vstring}" | sed -n "s/^codec_name=\(.*\)$/\1/p")" 52 | fi 53 | if ! [[ -v WIDTH ]]; then 54 | WIDTH="$(echo "${vstring}" | sed -n "s/^width=\(.*\)$/\1/p")" 55 | fi 56 | if ! [[ -v HEIGHT ]]; then 57 | HEIGHT="$(echo "${vstring}" | sed -n "s/^height=\(.*\)$/\1/p")" 58 | fi 59 | if ! [[ -v RESOLUTIONS ]]; then 60 | RESOLUTIONS=("native") 61 | fi 62 | if ! [[ -v DURATION ]]; then 63 | DURATION="$(echo "${fstring}" | sed -n "s/^duration=\(.*\)$/\1/p")" 64 | fi 65 | if ! [[ -v DIMENSIONS ]]; then 66 | DIMENSIONS=("${WIDTH}x${HEIGHT}") 67 | fi 68 | if ! [[ -v ASPECTRATIO ]]; then 69 | ASPECTRATIO="$(echo "${vstring}" | sed -n "s/^display_aspect_ratio=\(.*\)$/\1/p")" 70 | fi 71 | if ! [[ -v FPS ]]; then 72 | FPS="$(echo "${vstring}" | sed -n "s/^r_frame_rate=\(.*\)$/\1/p" | bc -l)" 73 | fi 74 | if ! [[ -v SCODEC ]]; then 75 | SCODEC="$(echo "${sstring}" | sed -n "s/^codec_name=\(.*\)$/\1/p")" 76 | fi 77 | } 78 | 79 | function make_clip { 80 | cmd="ffmpeg -probesize 1G -analyzeduration 1G -v info -i file:${IN@Q} -ss ${START}" 81 | if [[ -v LENGTH ]]; then 82 | cmd="${cmd} -t ${LENGTH}" 83 | elif [[ -v END ]]; then 84 | cmd="${cmd} -to ${END}" 85 | fi 86 | cmd="${cmd} -c copy -y file:${OUT@Q}" 87 | eval "${cmd}" 88 | } 89 | 90 | START=0.0 91 | 92 | OPTIONS=i:o:s:e:l:t: 93 | LONGOPTIONS=input:,output:,start:,end:,length:,subtitle: 94 | PARSED=$(getopt -o "${OPTIONS}" --long "${LONGOPTIONS}" -n "${0}" -- "${@}") 95 | eval set -- "${PARSED}" 96 | 97 | while [ ${#} -ge 1 ]; do 98 | case "${1}" in 99 | -i | --input) 100 | IN="${2}" 101 | scan_input 102 | shift 2 103 | ;; 104 | -o | --output) 105 | OUT="${2}" 106 | shift 2 107 | ;; 108 | -s | --start) 109 | START="${2}" 110 | shift 2 111 | ;; 112 | -e | --end) 113 | END="${2}" 114 | shift 2 115 | ;; 116 | -l | --length) 117 | LENGTH="${2}" 118 | shift 2 119 | ;; 120 | --) 121 | shift 122 | break 123 | ;; 124 | *) 125 | show_error "ERROR: invalid flag ${1}." 126 | print_usage 127 | exit 3 128 | ;; 129 | esac 130 | done 131 | 132 | # Check if variables are set before running ffmpeg. 133 | if ! [[ -v IN ]] || ! [[ -v OUT ]]; then 134 | show_error "ERROR: missing input/output files." 135 | print_usage 136 | exit 3 137 | fi 138 | 139 | if ! [[ -v LENGTH ]] && ! [[ -v END ]]; then 140 | LENGTH="$(echo "${DURATION} - ${START}" | bc -l)" 141 | fi 142 | 143 | make_clip 144 | -------------------------------------------------------------------------------- /copy-playlist: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2020 - 2024 sudorook 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | # This program is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by the Free 9 | # Software Foundation, either version 3 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program. If not, see . 19 | 20 | set -euo pipefail 21 | 22 | ROOT="$(dirname "${0}")" 23 | 24 | source "${ROOT}"/globals 25 | 26 | function print_usage() { 27 | show_header "Usage: copy-playlist" 28 | show_listitem " -i|--input " 29 | show_listitem " -d|--dest " 30 | show_listitem " -m|--music " 31 | } 32 | 33 | OPTIONS=hi:d:m: 34 | LONGOPTIONS=help,input:,dest:,music: 35 | PARSED=$(getopt -o "${OPTIONS}" --long "${LONGOPTIONS}" -n "${0}" -- "${@}") 36 | eval set -- "${PARSED}" 37 | 38 | while [ ${#} -ge 1 ]; do 39 | case "${1}" in 40 | -i | --input) 41 | INPUT=${2} 42 | shift 2 43 | ;; 44 | -d | --dest) 45 | DEST=${2} 46 | shift 2 47 | ;; 48 | -m | --music) 49 | MUSIC=${2} 50 | shift 2 51 | ;; 52 | -h | --help) 53 | print_usage 54 | exit 55 | ;; 56 | --) 57 | shift 58 | break 59 | ;; 60 | *) 61 | show_error "ERROR" 62 | print_usage 63 | exit 3 64 | ;; 65 | esac 66 | done 67 | 68 | MUSIC="${MUSIC:-"${musicdir}"}" 69 | 70 | if ! [ -d "${MUSIC}" ]; then 71 | show_error "ERROR: ${MUSIC@Q} does not exist. Exiting." 72 | exit 3 73 | fi 74 | 75 | if [[ -v INPUT ]] && [[ -v DEST ]]; then 76 | mkdir -p "${DEST}" 77 | if ! [ -f "${INPUT}" ]; then 78 | show_error "ERROR: ${INPUT@Q} not a file. Exiting." 79 | exit 3 80 | fi 81 | shopt -s extglob 82 | while read -r ITEM; do 83 | if [ -f "${MUSIC}/${ITEM}" ]; then 84 | cp -uav "${MUSIC}/${ITEM}" "${DEST}/${ITEM##*/+([[:digit:]]) }" 85 | sync 86 | else 87 | show_error "ERROR: file ${ITEM@Q} not found. Exiting." 88 | exit 3 89 | fi 90 | done < "${INPUT}" 91 | shopt -u extglob 92 | else 93 | show_error "ERROR: Improper usage." 94 | print_usage 95 | exit 3 96 | fi 97 | -------------------------------------------------------------------------------- /count-commits: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2017 - 2024 sudorook 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | # 7 | # This program is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by the Free 9 | # Software Foundation, either version 3 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 | # for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program. If not, see . 19 | 20 | set -euo pipefail 21 | 22 | ROOT="$(dirname "${0}")" 23 | 24 | source "${ROOT}"/globals 25 | 26 | ! check_command awk find git && exit 3 27 | 28 | # 29 | # Functions 30 | # 31 | 32 | get_count() { 33 | local dir 34 | local cmd 35 | dir=${1%/*} 36 | 37 | # Git will traverse the entire from HEAD to the first commit where the 38 | # committer or the author date falls outside the range. This can caused 39 | # unexpected behavior for merge / rebase / fixup commits, as the committer 40 | # and author dates will likely not match. 41 | cmd="git -C ${dir@Q} rev-list --count --branches" 42 | if [ -n "${AUTHOR}" ] || [ -n "${EMAIL}" ]; then 43 | cmd="${cmd} --author=^${AUTHOR:-.*}\ \<${EMAIL:-.*}\>$" 44 | fi 45 | if [ -n "${SINCE}" ]; then 46 | cmd="${cmd} --since=$(date -d "${SINCE}" +%s)" 47 | fi 48 | if [ -n "${UNTIL}" ]; then 49 | cmd="${cmd} --until=$(date -d "${UNTIL}" +%s)" 50 | fi 51 | eval "${cmd}" 52 | } 53 | export -f get_count 54 | 55 | print_usage() { 56 | show_header "Usage: count-commits" 57 | echo 58 | show_listitem " -a|--author " 59 | show_listitem " -e|--email " 60 | show_listitem " -s|--since