Initial commit

This commit is contained in:
2025-11-14 17:35:52 +01:00
commit 3fc38a20d9
106 changed files with 9123 additions and 0 deletions

5
UserScripts/00-Readme Executable file
View File

@@ -0,0 +1,5 @@
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ #
# Place your new scripts here.
# If you need to edit a script from main script (~/.config/hypr/scripts), copy it on this directory, and edit.
# Make sure to update as well the keybinds in ~/.config/hypr/UserConfigs directory if any script is linked to it

14
UserScripts/RainbowBorders.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ##
# for rainbow borders animation
function random_hex() {
random_hex=("0xff$(openssl rand -hex 3)")
echo $random_hex
}
# rainbow colors only for active window
hyprctl keyword general:col.active_border $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) 270deg
# rainbow colors for inactive window (uncomment to take effect)
#hyprctl keyword general:col.inactive_border $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) $(random_hex) 270deg

153
UserScripts/RofiBeats.sh Executable file
View File

@@ -0,0 +1,153 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ##
# For Rofi Beats to play online Music or Locally saved media files
# Variables
mDIR="$HOME/Music/"
iDIR="$HOME/.config/swaync/icons"
rofi_theme="$HOME/.config/rofi/config-rofi-Beats.rasi"
rofi_theme_1="$HOME/.config/rofi/config-rofi-Beats-menu.rasi"
# Online Stations. Edit as required
declare -A online_music=(
["FM - Easy Rock 96.3 📻🎶"]="https://radio-stations-philippines.com/easy-rock"
["FM - Easy Rock - Baguio 91.9 📻🎶"]="https://radio-stations-philippines.com/easy-rock-baguio"
["FM - Love Radio 90.7 📻🎶"]="https://radio-stations-philippines.com/love"
["FM - WRock - CEBU 96.3 📻🎶"]="https://onlineradio.ph/126-96-3-wrock.html"
["FM - Fresh Philippines 📻🎶"]="https://onlineradio.ph/553-fresh-fm.html"
["Radio - Lofi Girl 🎧🎶"]="https://play.streamafrica.net/lofiradio"
["Radio - Chillhop 🎧🎶"]="http://stream.zeno.fm/fyn8eh3h5f8uv"
["Radio - Ibiza Global 🎧🎶"]="https://filtermusic.net/ibiza-global"
["Radio - Metal Music 🎧🎶"]="https://tunein.com/radio/mETaLmuSicRaDio-s119867/"
["YT - Wish 107.5 YT Pinoy HipHop 📻🎶"]="https://youtube.com/playlist?list=PLkrzfEDjeYJnmgMYwCKid4XIFqUKBVWEs&si=vahW_noh4UDJ5d37"
["YT - Youtube Top 100 Songs Global 📹🎶"]="https://youtube.com/playlist?list=PL4fGSI1pDJn6puJdseH2Rt9sMvt9E2M4i&si=5jsyfqcoUXBCSLeu"
["YT - Wish 107.5 YT Wishclusives 📹🎶"]="https://youtube.com/playlist?list=PLkrzfEDjeYJn5B22H9HOWP3Kxxs-DkPSM&si=d_Ld2OKhGvpH48WO"
["YT - Relaxing Piano Music 🎹🎶"]="https://youtu.be/6H7hXzjFoVU?si=nZTPREC9lnK1JJUG"
["YT - Youtube Remix 📹🎶"]="https://youtube.com/playlist?list=PLeqTkIUlrZXlSNn3tcXAa-zbo95j0iN-0"
["YT - Korean Drama OST 📹🎶"]="https://youtube.com/playlist?list=PLUge_o9AIFp4HuA-A3e3ZqENh63LuRRlQ"
["YT - lofi hip hop radio beats 📹🎶"]="https://www.youtube.com/live/jfKfPfyJRdk?si=PnJIA9ErQIAw6-qd"
["YT - Relaxing Piano Jazz Music 🎹🎶"]="https://youtu.be/85UEqRat6E4?si=jXQL1Yp2VP_G6NSn"
)
# Populate local_music array with files from music directory and subdirectories
populate_local_music() {
local_music=()
filenames=()
while IFS= read -r file; do
local_music+=("$file")
filenames+=("$(basename "$file")")
done < <(find -L "$mDIR" -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.wav" -o -iname "*.ogg" -o -iname "*.mp4" \))
}
# Function for displaying notifications
notification() {
notify-send -u normal -i "$iDIR/music.png" "Now Playing:" "$@"
}
# Main function for playing local music
play_local_music() {
populate_local_music
# Prompt the user to select a song
choice=$(printf "%s\n" "${filenames[@]}" | rofi -i -dmenu -config $rofi_theme)
if [ -z "$choice" ]; then
exit 1
fi
# Find the corresponding file path based on user's choice and set that to play the song then continue on the list
for (( i=0; i<"${#filenames[@]}"; ++i )); do
if [ "${filenames[$i]}" = "$choice" ]; then
if music_playing; then
stop_music
fi
notification "$choice"
mpv --playlist-start="$i" --loop-playlist --vid=no "${local_music[@]}"
break
fi
done
}
# Main function for shuffling local music
shuffle_local_music() {
if music_playing; then
stop_music
fi
notification "Shuffle Play local music"
# Play music in $mDIR on shuffle
mpv --shuffle --loop-playlist --vid=no "$mDIR"
}
# Main function for playing online music
play_online_music() {
choice=$(for online in "${!online_music[@]}"; do
echo "$online"
done | sort | rofi -i -dmenu -config "$rofi_theme")
if [ -z "$choice" ]; then
exit 1
fi
link="${online_music[$choice]}"
if music_playing; then
stop_music
fi
notification "$choice"
# Play the selected online music using mpv
mpv --shuffle --vid=no "$link"
}
# Function to check if music is already playing
music_playing() {
pgrep -x "mpv" > /dev/null
}
# Function to stop music and kill mpv processes
stop_music() {
mpv_pids=$(pgrep -x mpv)
if [ -n "$mpv_pids" ]; then
# Get the PID of the mpv process used by mpvpaper (using the unique argument added)
mpvpaper_pid=$(ps aux | grep -- 'unique-wallpaper-process' | grep -v 'grep' | awk '{print $2}')
for pid in $mpv_pids; do
if ! echo "$mpvpaper_pid" | grep -q "$pid"; then
kill -9 $pid || true
fi
done
notify-send -u low -i "$iDIR/music.png" "Music stopped" || true
fi
}
user_choice=$(printf "%s\n" \
"Play from Online Stations" \
"Play from Music directory" \
"Shuffle Play from Music directory" \
"Stop RofiBeats" \
| rofi -dmenu -config $rofi_theme_1)
echo "User choice: $user_choice"
case "$user_choice" in
"Play from Online Stations")
play_online_music
;;
"Play from Music directory")
play_local_music
;;
"Shuffle Play from Music directory")
shuffle_local_music
;;
"Stop RofiBeats")
if music_playing; then
stop_music
fi
;;
*)
;;
esac

30
UserScripts/RofiCalc.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */
# /* Calculator (using qalculate) and rofi */
# /* Submitted by: https://github.com/JosephArmas */
rofi_theme="$HOME/.config/rofi/config-calc.rasi"
# Kill Rofi if already running before execution
if pgrep -x "rofi" >/dev/null; then
pkill rofi
fi
# main function
while true; do
result=$(
rofi -i -dmenu \
-config $rofi_theme \
-mesg "$result = $calc_result"
)
if [ $? -ne 0 ]; then
exit
fi
if [ -n "$result" ]; then
calc_result=$(qalc -t "$result")
echo "$calc_result" | wl-copy
fi
done

View File

@@ -0,0 +1,90 @@
#!/bin/bash
# USAGE / ІНСТРУКЦІЯ:
# 1) Run from terminal:
# ./dispatch.sh <application_command> <target_workspace_number>
# Example:
# ./dispatch.sh discord 2
#
# 2) Call from Hyprland config (in hyprland.conf file):
# exec-once = /path/to/dispatch.sh <application_command> <target_workspace_number>
#
# Logs are saved in dispatch.log file next to the script.
# If the window doesn't appear or is dispatched incorrectly — info will be there.
#
# Notes:
# - Script waits about ~9 seconds (30 iterations of 0.3 sec) for window to appear.
# - Uses hyprctl and jq, so these tools must be installed.
#
# USAGE / ІНСТРУКЦІЯ:
# 1) Запуск з терміналу:
# ./dispatch.sh <application_command> <target_workspace_number>
# Наприклад:
# ./dispatch.sh discord 2
#
# 2) Виклик з конфігурації Hyprland (у файлі hyprland.conf):
# exec-once = /path/to/dispatch.sh <application_command> <target_workspace_number>
#
# Логи зберігаються у файлі dispatch.log поруч зі скриптом.
# Якщо вікно не з'явилось або неправильно диспатчилось — інформація там.
#
# Примітки:
# - Скрипт чекає до ~9 секунд (30 ітерацій по 0.3 сек) поки вікно з'явиться.
# - Використовує hyprctl і jq, тому ці інструменти мають бути встановлені.
LOGFILE="$(dirname "$0")/dispatch.log"
# Log file path located next to the script.
# Файл логів розташований поруч зі скриптом.
APP=$1
# The application command or window class to launch or match.
# Команда для запуску аплікації або клас вікна для пошуку.
TARGET_WORKSPACE=$2
# The target workspace number where the window should be moved.
# Цільовий номер воркспейсу, куди потрібно перемістити вікно.
# Check if required arguments are provided.
# Перевірка наявності необхідних параметрів.
if [[ -z "$APP" || -z "$TARGET_WORKSPACE" ]]; then
echo "Usage: $0 <application_command> <target_workspace_number>" >> "$LOGFILE" 2>&1
exit 1
fi
echo "Starting dispatch of '$APP' to workspace $TARGET_WORKSPACE at $(date)" >> "$LOGFILE"
# Starting the dispatch process and logging the event.
# Початок процесу диспатчу, запис у лог.
# Avoid early workspace focus issues by switching workspace first.
# Уникаємо проблем з раннім фокусом, спочатку переключаємо воркспейс.
hyprctl dispatch workspace "$TARGET_WORKSPACE" >> "$LOGFILE" 2>&1
sleep 0.4
# Launch the application in the background and disown it.
# Запускаємо аплікацію у фоновому режимі та відв’язуємо від терміналу.
$APP & disown
pid=$!
echo "Launched '$APP' with PID $pid" >> "$LOGFILE"
# Log the launched process ID.
# Лог процесу запуску з PID.
# Wait for the application window to appear (matching window class).
# Чекаємо появи вікна аплікації (за класом вікна).
for i in {1..30}; do
win=$(hyprctl clients -j | jq -r --arg APP "$APP" '
.[] | select(.class | test($APP;"i")) | .address' 2>>"$LOGFILE")
if [[ -n "$win" ]]; then
echo "Found window $win for app '$APP', moving to workspace $TARGET_WORKSPACE" >> "$LOGFILE"
# Move the window to the target workspace.
# Переміщаємо вікно на цільовий воркспейс.
hyprctl dispatch movetoworkspace "$TARGET_WORKSPACE,address:$win" >> "$LOGFILE" 2>&1
exit 0
fi
sleep 0.3
done
echo "ERROR: Window for '$APP' was NOT found or dispatched properly to workspace $TARGET_WORKSPACE at $(date)" >> "$LOGFILE"
# Log error if window was not found or dispatched correctly.
# Запис помилки, якщо вікно не знайдено або неправильно диспатчено.
exit 1

View File

@@ -0,0 +1,42 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ##
# source https://wiki.archlinux.org/title/Hyprland#Using_a_script_to_change_wallpaper_every_X_minutes
# This script will randomly go through the files of a directory, setting it
# up as the wallpaper at regular intervals
#
# NOTE: this script uses bash (not POSIX shell) for the RANDOM variable
wallust_refresh=$HOME/.config/hypr/scripts/RefreshNoWaybar.sh
focused_monitor=$(hyprctl monitors | awk '/^Monitor/{name=$2} /focused: yes/{print name}')
if [[ $# -lt 1 ]] || [[ ! -d $1 ]]; then
echo "Usage:
$0 <dir containing images>"
exit 1
fi
# Edit below to control the images transition
export SWWW_TRANSITION_FPS=60
export SWWW_TRANSITION_TYPE=simple
# This controls (in seconds) when to switch to the next image
INTERVAL=1800
while true; do
find "$1" \
| while read -r img; do
echo "$((RANDOM % 1000)):$img"
done \
| sort -n | cut -d':' -f2- \
| while read -r img; do
swww img -o $focused_monitor "$img"
# Regenerate colors from the exact image path to avoid cache races
$HOME/.config/hypr/scripts/WallustSwww.sh "$img"
# Refresh UI components that depend on wallust output
$wallust_refresh
sleep $INTERVAL
done
done

148
UserScripts/WallpaperEffects.sh Executable file
View File

@@ -0,0 +1,148 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ #
# Wallpaper Effects using ImageMagick (SUPER SHIFT W)
# Variables
terminal=kitty
wallpaper_current="$HOME/.config/hypr/wallpaper_effects/.wallpaper_current"
wallpaper_output="$HOME/.config/hypr/wallpaper_effects/.wallpaper_modified"
SCRIPTSDIR="$HOME/.config/hypr/scripts"
focused_monitor=$(hyprctl monitors -j | jq -r '.[] | select(.focused) | .name')
rofi_theme="$HOME/.config/rofi/config-wallpaper-effect.rasi"
# Directory for swaync
iDIR="$HOME/.config/swaync/images"
iDIRi="$HOME/.config/swaync/icons"
# swww transition config
FPS=60
TYPE="wipe"
DURATION=2
BEZIER=".43,1.19,1,.4"
SWWW_PARAMS="--transition-fps $FPS --transition-type $TYPE --transition-duration $DURATION --transition-bezier $BEZIER"
# Define ImageMagick effects
declare -A effects=(
["No Effects"]="no-effects"
["Black & White"]="magick $wallpaper_current -colorspace gray -sigmoidal-contrast 10,40% $wallpaper_output"
["Blurred"]="magick $wallpaper_current -blur 0x10 $wallpaper_output"
["Charcoal"]="magick $wallpaper_current -charcoal 0x5 $wallpaper_output"
["Edge Detect"]="magick $wallpaper_current -edge 1 $wallpaper_output"
["Emboss"]="magick $wallpaper_current -emboss 0x5 $wallpaper_output"
["Frame Raised"]="magick $wallpaper_current +raise 150 $wallpaper_output"
["Frame Sunk"]="magick $wallpaper_current -raise 150 $wallpaper_output"
["Negate"]="magick $wallpaper_current -negate $wallpaper_output"
["Oil Paint"]="magick $wallpaper_current -paint 4 $wallpaper_output"
["Posterize"]="magick $wallpaper_current -posterize 4 $wallpaper_output"
["Polaroid"]="magick $wallpaper_current -polaroid 0 $wallpaper_output"
["Sepia Tone"]="magick $wallpaper_current -sepia-tone 65% $wallpaper_output"
["Solarize"]="magick $wallpaper_current -solarize 80% $wallpaper_output"
["Sharpen"]="magick $wallpaper_current -sharpen 0x5 $wallpaper_output"
["Vignette"]="magick $wallpaper_current -vignette 0x3 $wallpaper_output"
["Vignette-black"]="magick $wallpaper_current -background black -vignette 0x3 $wallpaper_output"
["Zoomed"]="magick $wallpaper_current -gravity Center -extent 1:1 $wallpaper_output"
)
# Function to apply no effects
no-effects() {
swww img -o "$focused_monitor" "$wallpaper_current" $SWWW_PARAMS &&
wait $!
wallust run "$wallpaper_current" -s &&
wait $!
# Refresh rofi, waybar, wallust palettes
sleep 2
"$SCRIPTSDIR/Refresh.sh"
notify-send -u low -i "$iDIR/ja.png" "No wallpaper" "effects applied"
# copying wallpaper for rofi menu
cp "$wallpaper_current" "$wallpaper_output"
}
# Function to run rofi menu
main() {
# Populate rofi menu options
options=("No Effects")
for effect in "${!effects[@]}"; do
[[ "$effect" != "No Effects" ]] && options+=("$effect")
done
choice=$(printf "%s\n" "${options[@]}" | LC_COLLATE=C sort | rofi -dmenu -i -config $rofi_theme)
# Process user choice
if [[ -n "$choice" ]]; then
if [[ "$choice" == "No Effects" ]]; then
no-effects
elif [[ "${effects[$choice]+exists}" ]]; then
# Apply selected effect
notify-send -u normal -i "$iDIR/ja.png" "Applying:" "$choice effects"
eval "${effects[$choice]}"
# intial kill process
for pid in swaybg mpvpaper; do
killall -SIGUSR1 "$pid"
done
sleep 1
swww img -o "$focused_monitor" "$wallpaper_output" $SWWW_PARAMS &
sleep 2
wallust run "$wallpaper_output" -s &
sleep 1
# Refresh rofi, waybar, wallust palettes
"${SCRIPTSDIR}/Refresh.sh"
notify-send -u low -i "$iDIR/ja.png" "$choice" "effects applied"
else
echo "Effect '$choice' not recognized."
fi
fi
}
# Check if rofi is already running and kill it
if pidof rofi > /dev/null; then
pkill rofi
fi
main
sleep 1
if [[ -n "$choice" ]]; then
# Resolve SDDM themes directory (standard and NixOS path)
sddm_themes_dir=""
if [ -d "/usr/share/sddm/themes" ]; then
sddm_themes_dir="/usr/share/sddm/themes"
elif [ -d "/run/current-system/sw/share/sddm/themes" ]; then
sddm_themes_dir="/run/current-system/sw/share/sddm/themes"
fi
if [ -n "$sddm_themes_dir" ]; then
sddm_simple="$sddm_themes_dir/simple_sddm_2"
# Only prompt if theme exists and its Backgrounds directory is writable
if [ -d "$sddm_simple" ] && [ -w "$sddm_simple/Backgrounds" ]; then
# Check if yad is running to avoid multiple yad notification
if pidof yad > /dev/null; then
killall yad
fi
if yad --info --text="Set current wallpaper as SDDM background?\n\nNOTE: This only applies to SIMPLE SDDM v2 Theme" \
--text-align=left \
--title="SDDM Background" \
--timeout=5 \
--timeout-indicator=right \
--button="yad-yes:0" \
--button="yad-no:1" \
; then
# Check if terminal exists
if ! command -v "$terminal" &>/dev/null; then
notify-send -i "$iDIR/ja.png" "Missing $terminal" "Install $terminal to enable setting of wallpaper background"
exit 1
fi
exec "$SCRIPTSDIR/sddm_wallpaper.sh" --effects
fi
fi
fi
fi

30
UserScripts/WallpaperRandom.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ##
# Script for Random Wallpaper ( CTRL ALT W)
wallDIR="$HOME/Pictures/wallpapers"
SCRIPTSDIR="$HOME/.config/hypr/scripts"
focused_monitor=$(hyprctl monitors -j | jq -r '.[] | select(.focused) | .name')
PICS=($(find -L ${wallDIR} -type f \( -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" -o -name "*.pnm" -o -name "*.tga" -o -name "*.tiff" -o -name "*.webp" -o -name "*.bmp" -o -name "*.farbfeld" -o -name "*.gif" \)))
RANDOMPICS=${PICS[ $RANDOM % ${#PICS[@]} ]}
# Transition config
FPS=30
TYPE="random"
DURATION=1
BEZIER=".43,1.19,1,.4"
SWWW_PARAMS="--transition-fps $FPS --transition-type $TYPE --transition-duration $DURATION --transition-bezier $BEZIER"
swww query || swww-daemon --format xrgb && swww img -o $focused_monitor ${RANDOMPICS} $SWWW_PARAMS
wait $!
"$SCRIPTSDIR/WallustSwww.sh" &&
wait $!
sleep 2
"$SCRIPTSDIR/Refresh.sh"

248
UserScripts/WallpaperSelect.sh Executable file
View File

@@ -0,0 +1,248 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */
# This script for selecting wallpapers (SUPER W)
# WALLPAPERS PATH
terminal=kitty
wallDIR="$HOME/Pictures/wallpapers"
SCRIPTSDIR="$HOME/.config/hypr/scripts"
wallpaper_current="$HOME/.config/hypr/wallpaper_effects/.wallpaper_current"
# Directory for swaync
iDIR="$HOME/.config/swaync/images"
iDIRi="$HOME/.config/swaync/icons"
# swww transition config
FPS=60
TYPE="any"
DURATION=2
BEZIER=".43,1.19,1,.4"
SWWW_PARAMS="--transition-fps $FPS --transition-type $TYPE --transition-duration $DURATION --transition-bezier $BEZIER"
# Check if package bc exists
if ! command -v bc &>/dev/null; then
notify-send -i "$iDIR/error.png" "bc missing" "Install package bc first"
exit 1
fi
# Variables
rofi_theme="$HOME/.config/rofi/config-wallpaper.rasi"
focused_monitor=$(hyprctl monitors -j | jq -r '.[] | select(.focused) | .name')
# Ensure focused_monitor is detected
if [[ -z "$focused_monitor" ]]; then
notify-send -i "$iDIR/error.png" "E-R-R-O-R" "Could not detect focused monitor"
exit 1
fi
# Monitor details
scale_factor=$(hyprctl monitors -j | jq -r --arg mon "$focused_monitor" '.[] | select(.name == $mon) | .scale')
monitor_height=$(hyprctl monitors -j | jq -r --arg mon "$focused_monitor" '.[] | select(.name == $mon) | .height')
icon_size=$(echo "scale=1; ($monitor_height * 3) / ($scale_factor * 150)" | bc)
adjusted_icon_size=$(echo "$icon_size" | awk '{if ($1 < 15) $1 = 20; if ($1 > 25) $1 = 25; print $1}')
rofi_override="element-icon{size:${adjusted_icon_size}%;}"
# Kill existing wallpaper daemons for video
kill_wallpaper_for_video() {
swww kill 2>/dev/null
pkill mpvpaper 2>/dev/null
pkill swaybg 2>/dev/null
pkill hyprpaper 2>/dev/null
}
# Kill existing wallpaper daemons for image
kill_wallpaper_for_image() {
pkill mpvpaper 2>/dev/null
pkill swaybg 2>/dev/null
pkill hyprpaper 2>/dev/null
}
# Retrieve wallpapers (both images & videos)
mapfile -d '' PICS < <(find -L "${wallDIR}" -type f \( \
-iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" -o \
-iname "*.bmp" -o -iname "*.tiff" -o -iname "*.webp" -o \
-iname "*.mp4" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.webm" \) -print0)
RANDOM_PIC="${PICS[$((RANDOM % ${#PICS[@]}))]}"
RANDOM_PIC_NAME=". random"
# Rofi command
rofi_command="rofi -i -show -dmenu -config $rofi_theme -theme-str $rofi_override"
# Sorting Wallpapers
menu() {
IFS=$'\n' sorted_options=($(sort <<<"${PICS[*]}"))
printf "%s\x00icon\x1f%s\n" "$RANDOM_PIC_NAME" "$RANDOM_PIC"
for pic_path in "${sorted_options[@]}"; do
pic_name=$(basename "$pic_path")
if [[ "$pic_name" =~ \.gif$ ]]; then
cache_gif_image="$HOME/.cache/gif_preview/${pic_name}.png"
if [[ ! -f "$cache_gif_image" ]]; then
mkdir -p "$HOME/.cache/gif_preview"
magick "$pic_path[0]" -resize 1920x1080 "$cache_gif_image"
fi
printf "%s\x00icon\x1f%s\n" "$pic_name" "$cache_gif_image"
elif [[ "$pic_name" =~ \.(mp4|mkv|mov|webm|MP4|MKV|MOV|WEBM)$ ]]; then
cache_preview_image="$HOME/.cache/video_preview/${pic_name}.png"
if [[ ! -f "$cache_preview_image" ]]; then
mkdir -p "$HOME/.cache/video_preview"
ffmpeg -v error -y -i "$pic_path" -ss 00:00:01.000 -vframes 1 "$cache_preview_image"
fi
printf "%s\x00icon\x1f%s\n" "$pic_name" "$cache_preview_image"
else
printf "%s\x00icon\x1f%s\n" "$(echo "$pic_name" | cut -d. -f1)" "$pic_path"
fi
done
}
# Offer SDDM Simple Wallpaper Option (only for non-video wallpapers)
set_sddm_wallpaper() {
sleep 1
# Resolve SDDM themes directory (standard and NixOS path)
local sddm_themes_dir=""
if [ -d "/usr/share/sddm/themes" ]; then
sddm_themes_dir="/usr/share/sddm/themes"
elif [ -d "/run/current-system/sw/share/sddm/themes" ]; then
sddm_themes_dir="/run/current-system/sw/share/sddm/themes"
fi
[ -z "$sddm_themes_dir" ] && return 0
local sddm_simple="$sddm_themes_dir/simple_sddm_2"
# Only prompt if theme exists and its Backgrounds directory is writable
if [ -d "$sddm_simple" ] && [ -w "$sddm_simple/Backgrounds" ]; then
# Check if yad is running to avoid multiple notifications
if pidof yad >/dev/null; then
killall yad
fi
if yad --info --text="Set current wallpaper as SDDM background?\n\nNOTE: This only applies to SIMPLE SDDM v2 Theme" \
--text-align=left \
--title="SDDM Background" \
--timeout=5 \
--timeout-indicator=right \
--button="yes:0" \
--button="no:1"; then
# Check if terminal exists
if ! command -v "$terminal" &>/dev/null; then
notify-send -i "$iDIR/error.png" "Missing $terminal" "Install $terminal to enable setting of wallpaper background"
exit 1
fi
exec "$SCRIPTSDIR/sddm_wallpaper.sh" --normal
fi
fi
}
modify_startup_config() {
local selected_file="$1"
local startup_config="$HOME/.config/hypr/UserConfigs/Startup_Apps.conf"
# Check if it's a live wallpaper (video)
if [[ "$selected_file" =~ \.(mp4|mkv|mov|webm)$ ]]; then
# For video wallpapers:
sed -i '/^\s*exec-once\s*=\s*swww-daemon\s*--format\s*xrgb\s*$/s/^/\#/' "$startup_config"
sed -i '/^\s*#\s*exec-once\s*=\s*mpvpaper\s*.*$/s/^#\s*//;' "$startup_config"
# Update the livewallpaper variable with the selected video path (using $HOME)
selected_file="${selected_file/#$HOME/\$HOME}" # Replace /home/user with $HOME
sed -i "s|^\$livewallpaper=.*|\$livewallpaper=\"$selected_file\"|" "$startup_config"
echo "Configured for live wallpaper (video)."
else
# For image wallpapers:
sed -i '/^\s*#\s*exec-once\s*=\s*swww-daemon\s*--format\s*xrgb\s*$/s/^\s*#\s*//;' "$startup_config"
sed -i '/^\s*exec-once\s*=\s*mpvpaper\s*.*$/s/^/\#/' "$startup_config"
echo "Configured for static wallpaper (image)."
fi
}
# Apply Image Wallpaper
apply_image_wallpaper() {
local image_path="$1"
kill_wallpaper_for_image
if ! pgrep -x "swww-daemon" >/dev/null; then
echo "Starting swww-daemon..."
swww-daemon --format xrgb &
fi
swww img -o "$focused_monitor" "$image_path" $SWWW_PARAMS
# Run additional scripts (pass the image path to avoid cache race conditions)
"$SCRIPTSDIR/WallustSwww.sh" "$image_path"
sleep 2
"$SCRIPTSDIR/Refresh.sh"
sleep 1
set_sddm_wallpaper
}
apply_video_wallpaper() {
local video_path="$1"
# Check if mpvpaper is installed
if ! command -v mpvpaper &>/dev/null; then
notify-send -i "$iDIR/error.png" "E-R-R-O-R" "mpvpaper not found"
return 1
fi
kill_wallpaper_for_video
# Apply video wallpaper using mpvpaper
mpvpaper '*' -o "load-scripts=no no-audio --loop" "$video_path" &
}
# Main function
main() {
choice=$(menu | $rofi_command)
choice=$(echo "$choice" | xargs)
RANDOM_PIC_NAME=$(echo "$RANDOM_PIC_NAME" | xargs)
if [[ -z "$choice" ]]; then
echo "No choice selected. Exiting."
exit 0
fi
# Handle random selection correctly
if [[ "$choice" == "$RANDOM_PIC_NAME" ]]; then
choice=$(basename "$RANDOM_PIC")
fi
choice_basename=$(basename "$choice" | sed 's/\(.*\)\.[^.]*$/\1/')
# Search for the selected file in the wallpapers directory, including subdirectories
selected_file=$(find "$wallDIR" -iname "$choice_basename.*" -print -quit)
if [[ -z "$selected_file" ]]; then
echo "File not found. Selected choice: $choice"
exit 1
fi
# Modify the Startup_Apps.conf file based on wallpaper type
modify_startup_config "$selected_file"
# **CHECK FIRST** if it's a video or an image **before calling any function**
if [[ "$selected_file" =~ \.(mp4|mkv|mov|webm|MP4|MKV|MOV|WEBM)$ ]]; then
apply_video_wallpaper "$selected_file"
else
apply_image_wallpaper "$selected_file"
fi
}
# Check if rofi is already running
if pidof rofi >/dev/null; then
pkill rofi
fi
main

544
UserScripts/Weather.py Executable file
View File

@@ -0,0 +1,544 @@
#!/usr/bin/env python3
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ #
# Rewritten to use Open-Meteo APIs (worldwide, no API key) for robust weather data.
# Outputs Waybar-compatible JSON and a simple text cache.
import json
import os
import sys
import time
import html
from typing import Any, Dict, List, Optional, Tuple
import requests
# =============== Configuration ===============
# You can configure behavior via environment variables OR the constants below.
# Examples (zsh):
# # One-off run
# # WEATHER_UNITS can be "metric" or "imperial"
# WEATHER_UNITS=imperial WEATHER_PLACE="Concord, NH" python3 /home/dwilliams/Projects/Weather.py
#
# # Persist in current shell session
# export WEATHER_UNITS=imperial
# export WEATHER_LAT=43.2229
# export WEATHER_LON=-71.332
# export WEATHER_PLACE="Concord, NH"
# export WEATHER_TOOLTIP_MARKUP=1 # 1 to enable Pango markup, 0 to disable
# export WEATHER_LOC_ICON="📍" # or "*" for ASCII-only
#
CACHE_DIR = os.path.expanduser("~/.cache")
API_CACHE_PATH = os.path.join(CACHE_DIR, "open_meteo_cache.json")
SIMPLE_TEXT_CACHE_PATH = os.path.join(CACHE_DIR, ".weather_cache")
CACHE_TTL_SECONDS = int(os.getenv("WEATHER_CACHE_TTL", "600")) # default 10 minutes
# Units: metric or imperial (default metric)
UNITS = os.getenv("WEATHER_UNITS", "metric").strip().lower() # metric|imperial
# Optional manual coordinates
ENV_LAT = os.getenv("WEATHER_LAT")
ENV_LON = os.getenv("WEATHER_LON")
# Optional manual place override for tooltip
ENV_PLACE = os.getenv("WEATHER_PLACE")
# Manual place name set inside this file. If set (non-empty), this takes top priority.
# Example: MANUAL_PLACE = "Concord, NH, US"
MANUAL_PLACE: Optional[str] = None
# Location icon in tooltip (default to a standard emoji to avoid missing glyphs)
LOC_ICON = os.getenv("WEATHER_LOC_ICON", "📍")
# Enable/disable Pango markup in tooltip (1/0, true/false)
TOOLTIP_MARKUP = os.getenv("WEATHER_TOOLTIP_MARKUP", "1").lower() not in ("0", "false", "no")
# Optional debug logging to stderr (set WEATHER_DEBUG=1 to enable)
DEBUG = os.getenv("WEATHER_DEBUG", "0").lower() not in ("0", "false", "no")
# HTTP settings
UA = (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/128.0 Safari/537.36"
)
TIMEOUT = 8
SESSION = requests.Session()
SESSION.headers.update({"User-Agent": UA})
# =============== Icon and status mapping ===============
# Reuse prior icon set for continuity
WEATHER_ICONS = {
"sunnyDay": "󰖙",
"clearNight": "󰖔",
"cloudyFoggyDay": "",
"cloudyFoggyNight": "",
"rainyDay": "",
"rainyNight": "",
"snowyIcyDay": "",
"snowyIcyNight": "",
"severe": "",
"default": "",
}
WMO_STATUS = {
0: "Clear sky",
1: "Mainly clear",
2: "Partly cloudy",
3: "Overcast",
45: "Fog",
48: "Depositing rime fog",
51: "Light drizzle",
53: "Moderate drizzle",
55: "Dense drizzle",
56: "Freezing drizzle",
57: "Freezing drizzle",
61: "Light rain",
63: "Moderate rain",
65: "Heavy rain",
66: "Freezing rain",
67: "Freezing rain",
71: "Slight snow",
73: "Moderate snow",
75: "Heavy snow",
77: "Snow grains",
80: "Rain showers",
81: "Rain showers",
82: "Violent rain showers",
85: "Snow showers",
86: "Heavy snow showers",
95: "Thunderstorm",
96: "Thunderstorm w/ hail",
99: "Thunderstorm w/ hail",
}
def wmo_to_icon(code: int, is_day: int) -> str:
day = bool(is_day)
if code == 0:
return WEATHER_ICONS["sunnyDay" if day else "clearNight"]
if code in (1, 2, 3, 45, 48):
return WEATHER_ICONS["cloudyFoggyDay" if day else "cloudyFoggyNight"]
if code in (51, 53, 55, 61, 63, 65, 80, 81, 82):
return WEATHER_ICONS["rainyDay" if day else "rainyNight"]
if code in (56, 57, 66, 67, 71, 73, 75, 77, 85, 86):
return WEATHER_ICONS["snowyIcyDay" if day else "snowyIcyNight"]
if code in (95, 96, 99):
return WEATHER_ICONS["severe"]
return WEATHER_ICONS["default"]
def wmo_to_status(code: int) -> str:
return WMO_STATUS.get(code, "Unknown")
# =============== Utilities ===============
def esc(s: Optional[str]) -> str:
return html.escape(s, quote=False) if s else ""
def log_debug(msg: str) -> None:
if DEBUG:
print(msg, file=sys.stderr)
def ensure_cache_dir() -> None:
try:
os.makedirs(CACHE_DIR, exist_ok=True)
except Exception as e:
print(f"Error creating cache dir: {e}", file=sys.stderr)
def read_api_cache() -> Optional[Dict[str, Any]]:
try:
if not os.path.exists(API_CACHE_PATH):
return None
with open(API_CACHE_PATH, "r", encoding="utf-8") as f:
data = json.load(f)
if (time.time() - data.get("timestamp", 0)) <= CACHE_TTL_SECONDS:
return data
return None
except Exception as e:
print(f"Error reading cache: {e}", file=sys.stderr)
return None
def write_api_cache(payload: Dict[str, Any]) -> None:
try:
ensure_cache_dir()
payload["timestamp"] = time.time()
with open(API_CACHE_PATH, "w", encoding="utf-8") as f:
json.dump(payload, f)
except Exception as e:
print(f"Error writing API cache: {e}", file=sys.stderr)
def write_simple_text_cache(text: str) -> None:
try:
ensure_cache_dir()
with open(SIMPLE_TEXT_CACHE_PATH, "w", encoding="utf-8") as f:
f.write(text)
except Exception as e:
print(f"Error writing simple cache: {e}", file=sys.stderr)
def get_coords() -> Tuple[float, float]:
# 1) Explicit env
if ENV_LAT and ENV_LON:
try:
return float(ENV_LAT), float(ENV_LON)
except ValueError:
print("Invalid WEATHER_LAT/WEATHER_LON; falling back to IP geolocation", file=sys.stderr)
# 2) Try cached coordinates from last successful forecast
try:
cached = read_api_cache()
if cached and isinstance(cached, dict):
fc = cached.get("forecast") or {}
lat = fc.get("latitude")
lon = fc.get("longitude")
if isinstance(lat, (int, float)) and isinstance(lon, (int, float)):
return float(lat), float(lon)
except Exception as e:
print(f"Reading cached coords failed: {e}", file=sys.stderr)
# 3) IP-based geolocation with multiple providers (prefer ipwho.is, ipapi.co; ipinfo.io as fallback)
# ipwho.is
try:
resp = SESSION.get("https://ipwho.is/", timeout=TIMEOUT)
resp.raise_for_status()
data = resp.json()
if data.get("success"):
lat = data.get("latitude")
lon = data.get("longitude")
if isinstance(lat, (int, float)) and isinstance(lon, (int, float)):
return float(lat), float(lon)
except Exception as e:
print(f"ipwho.is failed: {e}", file=sys.stderr)
# ipapi.co
try:
resp = SESSION.get("https://ipapi.co/json", timeout=TIMEOUT)
resp.raise_for_status()
data = resp.json()
lat = data.get("latitude")
lon = data.get("longitude")
if isinstance(lat, (int, float)) and isinstance(lon, (int, float)):
return float(lat), float(lon)
except Exception as e:
print(f"ipapi.co failed: {e}", file=sys.stderr)
# ipinfo.io (fallback)
try:
resp = SESSION.get("https://ipinfo.io/json", timeout=TIMEOUT)
resp.raise_for_status()
data = resp.json()
loc = data.get("loc")
if loc and "," in loc:
lat_s, lon_s = loc.split(",", 1)
return float(lat_s), float(lon_s)
except Exception as e:
print(f"ipinfo.io failed: {e}", file=sys.stderr)
# 4) Last resort
print("IP geolocation failed: no providers succeeded", file=sys.stderr)
return 0.0, 0.0
def units_params(units: str) -> Dict[str, str]:
if units == "imperial":
return {
"temperature_unit": "fahrenheit",
"wind_speed_unit": "mph",
"precipitation_unit": "inch",
}
# default metric
return {
"temperature_unit": "celsius",
"wind_speed_unit": "kmh",
"precipitation_unit": "mm",
}
def format_visibility(meters: Optional[float]) -> str:
if meters is None:
return ""
try:
if UNITS == "imperial":
miles = meters / 1609.344
return f"{miles:.1f} mi"
else:
km = meters / 1000.0
return f"{km:.1f} km"
except Exception:
return ""
# =============== API Fetching ===============
def fetch_open_meteo(lat: float, lon: float) -> Dict[str, Any]:
base = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": lat,
"longitude": lon,
"current": "temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_direction_10m,weather_code,visibility,precipitation,pressure_msl,is_day",
"hourly": "precipitation_probability",
"daily": "temperature_2m_max,temperature_2m_min",
"timezone": "auto",
}
params.update(units_params(UNITS))
resp = SESSION.get(base, params=params, timeout=TIMEOUT)
resp.raise_for_status()
return resp.json()
def fetch_aqi(lat: float, lon: float) -> Optional[Dict[str, Any]]:
try:
base = "https://air-quality-api.open-meteo.com/v1/air-quality"
params = {
"latitude": lat,
"longitude": lon,
"current": "european_aqi",
"timezone": "auto",
}
resp = SESSION.get(base, params=params, timeout=TIMEOUT)
resp.raise_for_status()
return resp.json()
except Exception as e:
print(f"AQI fetch failed: {e}", file=sys.stderr)
return None
def fetch_place(lat: float, lon: float) -> Optional[str]:
"""Reverse geocode lat/lon to an approximate place. Tries Nominatim first, then Open-Meteo."""
lang = os.getenv("WEATHER_LANG", "en")
# 1) Nominatim (OpenStreetMap)
try:
base = "https://nominatim.openstreetmap.org/reverse"
params = {
"lat": lat,
"lon": lon,
"format": "jsonv2",
"accept-language": lang,
}
headers = {"User-Agent": UA + " Weather.py/1.0"}
resp = SESSION.get(base, params=params, headers=headers, timeout=TIMEOUT)
resp.raise_for_status()
data = resp.json()
address = data.get("address", {})
name = data.get("name") or address.get("city") or address.get("town") or address.get("village") or address.get("hamlet")
admin1 = address.get("state")
country = address.get("country")
parts = [part for part in [name, admin1, country] if part]
if parts:
return ", ".join(parts)
except Exception as e:
log_debug(f"Reverse geocoding (Nominatim) failed: {e}")
# 2) Open-Meteo reverse (fallback)
try:
base = "https://geocoding-api.open-meteo.com/v1/reverse"
params = {
"latitude": lat,
"longitude": lon,
"language": lang,
"format": "json",
}
resp = SESSION.get(base, params=params, timeout=TIMEOUT)
resp.raise_for_status()
data = resp.json()
results = data.get("results") or []
if results:
p = results[0]
name = p.get("name")
admin1 = p.get("admin1")
country = p.get("country")
parts = [part for part in [name, admin1, country] if part]
if parts:
return ", ".join(parts)
except Exception as e:
log_debug(f"Reverse geocoding (Open-Meteo) failed: {e}")
return None
# =============== Build Output ===============
def safe_get(dct: Dict[str, Any], *keys, default=None):
cur: Any = dct
for k in keys:
if isinstance(cur, dict):
if k not in cur:
return default
cur = cur[k]
elif isinstance(cur, list):
try:
cur = cur[k] # type: ignore[index]
except Exception:
return default
else:
return default
return cur
def build_hourly_precip(forecast: Dict[str, Any]) -> str:
try:
times: List[str] = safe_get(forecast, "hourly", "time", default=[]) or []
probs: List[Optional[float]] = safe_get(
forecast, "hourly", "precipitation_probability", default=[]
) or []
cur_time: Optional[str] = safe_get(forecast, "current", "time")
idx = times.index(cur_time) if cur_time in times else 0
window = probs[idx : idx + 6]
if not window:
return ""
parts = [f"{int(p)}%" if p is not None else "-" for p in window]
return " (next 6h) " + " ".join(parts)
except Exception:
return ""
def build_output(lat: float, lon: float, forecast: Dict[str, Any], aqi: Optional[Dict[str, Any]], place: Optional[str] = None) -> Tuple[Dict[str, Any], str]:
cur = forecast.get("current", {})
cur_units = forecast.get("current_units", {})
daily = forecast.get("daily", {})
daily_units = forecast.get("daily_units", {})
temp_val = cur.get("temperature_2m")
temp_unit = cur_units.get("temperature_2m", "")
temp_str = f"{int(round(temp_val))}{temp_unit}" if isinstance(temp_val, (int, float)) else "N/A"
feels_val = cur.get("apparent_temperature")
feels_unit = cur_units.get("apparent_temperature", "")
feels_str = f"Feels like {int(round(feels_val))}{feels_unit}" if isinstance(feels_val, (int, float)) else ""
is_day = int(cur.get("is_day", 1) or 1)
code = int(cur.get("weather_code", -1) or -1)
icon = wmo_to_icon(code, is_day)
status = wmo_to_status(code)
# min/max today (index 0)
tmin_val = safe_get(daily, "temperature_2m_min", 0)
tmax_val = safe_get(daily, "temperature_2m_max", 0)
dtemp_unit = daily_units.get("temperature_2m_min", temp_unit)
tmin_str = f"{int(round(tmin_val))}{dtemp_unit}" if isinstance(tmin_val, (int, float)) else ""
tmax_str = f"{int(round(tmax_val))}{dtemp_unit}" if isinstance(tmax_val, (int, float)) else ""
min_max = f"{tmin_str}\t\t{tmax_str}" if tmin_str and tmax_str else ""
wind_val = cur.get("wind_speed_10m")
wind_unit = cur_units.get("wind_speed_10m", "")
wind_text = f"{int(round(wind_val))}{wind_unit}" if isinstance(wind_val, (int, float)) else ""
hum_val = cur.get("relative_humidity_2m")
humidity_text = f"{int(hum_val)}%" if isinstance(hum_val, (int, float)) else ""
vis_val = cur.get("visibility")
visibility_text = f"{format_visibility(vis_val)}" if isinstance(vis_val, (int, float)) else ""
aqi_val = safe_get(aqi or {}, "current", "european_aqi")
aqi_text = f"AQI {int(aqi_val)}" if isinstance(aqi_val, (int, float)) else "AQI N/A"
hourly_precip = build_hourly_precip(forecast)
prediction = f"\n\n{hourly_precip}" if hourly_precip else ""
# Build place string (priority: MANUAL_PLACE > ENV_PLACE > reverse geocode > lat,lon)
place_str = (MANUAL_PLACE or ENV_PLACE or place or f"{lat:.3f}, {lon:.3f}")
location_text = f"{LOC_ICON} {place_str}"
# Build tooltip (markup or plain)
if TOOLTIP_MARKUP:
# Escape dynamic text to avoid breaking Pango markup
tooltip_text = str.format(
"\t\t{}\t\t\n{}\n{}\n{}\n{}\n\n{}\n{}\n{}{}",
f'<span size="xx-large">{esc(temp_str)}</span>',
f"<big> {icon}</big>",
f"<b>{esc(status)}</b>",
esc(location_text),
f"<small>{esc(feels_str)}</small>" if feels_str else "",
f"<b>{esc(min_max)}</b>" if min_max else "",
f"{esc(wind_text)}\t{esc(humidity_text)}",
f"{esc(visibility_text)}\t{esc(aqi_text)}",
f"<i> {esc(prediction)}</i>" if prediction else "",
)
else:
lines = [
f"{icon} {temp_str}",
status,
location_text,
]
if feels_str:
lines.append(feels_str)
if min_max:
lines.append(min_max)
lines.append(f"{wind_text} {humidity_text}".strip())
lines.append(f"{visibility_text} {aqi_text}".strip())
if prediction:
lines.append(hourly_precip)
tooltip_text = "\n".join([ln for ln in lines if ln])
out_data = {
"text": f"{icon} {temp_str}",
"alt": status,
"tooltip": tooltip_text,
"class": f"wmo-{code} {'day' if is_day else 'night'}",
}
simple_weather = (
f"{icon} {status}\n"
+ f"{temp_str} ({feels_str})\n"
+ (f"{wind_text} \n" if wind_text else "")
+ (f"{humidity_text} \n" if humidity_text else "")
+ f"{visibility_text} {aqi_text}\n"
)
return out_data, simple_weather
def main() -> None:
lat, lon = get_coords()
# Try cache first
cached = read_api_cache()
if cached and isinstance(cached, dict):
forecast = cached.get("forecast")
aqi = cached.get("aqi")
cached_place = cached.get("place") if isinstance(cached.get("place"), str) else None
place_effective = MANUAL_PLACE or ENV_PLACE or cached_place
try:
out, simple = build_output(lat, lon, forecast, aqi, place_effective)
print(json.dumps(out, ensure_ascii=False))
write_simple_text_cache(simple)
return
except Exception as e:
print(f"Cached data build failed, refetching: {e}", file=sys.stderr)
# Fetch fresh
try:
forecast = fetch_open_meteo(lat, lon)
aqi = fetch_aqi(lat, lon)
# Use manual/env place if provided; otherwise reverse geocode
place_effective = MANUAL_PLACE or ENV_PLACE or fetch_place(lat, lon)
write_api_cache({"forecast": forecast, "aqi": aqi, "place": place_effective})
out, simple = build_output(lat, lon, forecast, aqi, place_effective)
print(json.dumps(out, ensure_ascii=False))
write_simple_text_cache(simple)
except Exception as e:
print(f"Open-Meteo fetch failed: {e}", file=sys.stderr)
# Last resort: try stale cache without TTL
try:
if os.path.exists(API_CACHE_PATH):
with open(API_CACHE_PATH, "r", encoding="utf-8") as f:
stale = json.load(f)
out, simple = build_output(lat, lon, stale.get("forecast", {}), stale.get("aqi"), stale.get("place") if isinstance(stale.get("place"), str) else None)
print(json.dumps(out, ensure_ascii=False))
write_simple_text_cache(simple)
return
except Exception as e2:
print(f"Failed to use stale cache: {e2}", file=sys.stderr)
# Fallback minimal output
fallback = {
"text": f"{WEATHER_ICONS['default']} N/A",
"alt": "Unavailable",
"tooltip": "Weather unavailable",
"class": "unavailable",
}
print(json.dumps(fallback, ensure_ascii=False))
if __name__ == "__main__":
main()

87
UserScripts/Weather.sh Executable file
View File

@@ -0,0 +1,87 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ##
# weather info from wttr. https://github.com/chubin/wttr.in
# Remember to add city
city=
cachedir="$HOME/.cache/rbn"
cachefile=${0##*/}-$1
if [ ! -d $cachedir ]; then
mkdir -p $cachedir
fi
if [ ! -f $cachedir/$cachefile ]; then
touch $cachedir/$cachefile
fi
# Save current IFS
SAVEIFS=$IFS
# Change IFS to new line.
IFS=$'\n'
cacheage=$(($(date +%s) - $(stat -c '%Y' "$cachedir/$cachefile")))
if [ $cacheage -gt 1740 ] || [ ! -s $cachedir/$cachefile ]; then
data=($(curl -s https://en.wttr.in/"$city"$1\?0qnT 2>&1))
echo ${data[0]} | cut -f1 -d, > $cachedir/$cachefile
echo ${data[1]} | sed -E 's/^.{15}//' >> $cachedir/$cachefile
echo ${data[2]} | sed -E 's/^.{15}//' >> $cachedir/$cachefile
fi
weather=($(cat $cachedir/$cachefile))
# Restore IFSClear
IFS=$SAVEIFS
temperature=$(echo ${weather[2]} | sed -E 's/([[:digit:]]+)\.\./\1 to /g')
#echo ${weather[1]##*,}
# https://fontawesome.com/icons?s=solid&c=weather
case $(echo ${weather[1]##*,} | tr '[:upper:]' '[:lower:]') in
"clear" | "sunny")
condition=""
;;
"partly cloudy")
condition="󰖕"
;;
"cloudy")
condition=""
;;
"overcast")
condition=""
;;
"fog" | "freezing fog")
condition=""
;;
"patchy rain possible" | "patchy light drizzle" | "light drizzle" | "patchy light rain" | "light rain" | "light rain shower" | "mist" | "rain")
condition="󰼳"
;;
"moderate rain at times" | "moderate rain" | "heavy rain at times" | "heavy rain" | "moderate or heavy rain shower" | "torrential rain shower" | "rain shower")
condition=""
;;
"patchy snow possible" | "patchy sleet possible" | "patchy freezing drizzle possible" | "freezing drizzle" | "heavy freezing drizzle" | "light freezing rain" | "moderate or heavy freezing rain" | "light sleet" | "ice pellets" | "light sleet showers" | "moderate or heavy sleet showers")
condition="󰼴"
;;
"blowing snow" | "moderate or heavy sleet" | "patchy light snow" | "light snow" | "light snow showers")
condition="󰙿"
;;
"blizzard" | "patchy moderate snow" | "moderate snow" | "patchy heavy snow" | "heavy snow" | "moderate or heavy snow with thunder" | "moderate or heavy snow showers")
condition=""
;;
"thundery outbreaks possible" | "patchy light rain with thunder" | "moderate or heavy rain with thunder" | "patchy light snow with thunder")
condition=""
;;
*)
condition=""
echo -e "{\"text\":\""$condition"\", \"alt\":\""${weather[0]}"\", \"tooltip\":\""${weather[0]}: $temperature ${weather[1]}"\"}"
;;
esac
#echo $temp $condition
echo -e "{\"text\":\""$temperature $condition"\", \"alt\":\""${weather[0]}"\", \"tooltip\":\""${weather[0]}: $temperature ${weather[1]}"\"}"
cached_weather="$temperature \n$condition ${weather[1]}"
echo -e $cached_weather > "$HOME/.cache/.weather_cache"

69
UserScripts/ZshChangeTheme.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/bash
# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ ##
# Script for Oh my ZSH theme ( CTRL SHIFT O)
# preview of theme can be view here: https://github.com/ohmyzsh/ohmyzsh/wiki/Themes
# after choosing theme, TTY need to be closed and re-open
# Variables
iDIR="$HOME/.config/swaync/images"
rofi_theme="$HOME/.config/rofi/config-zsh-theme.rasi"
if [ -n "$(grep -i nixos < /etc/os-release)" ]; then
notify-send -i "$iDIR/note.png" "NOT Supported" "Sorry NixOS does not support this KooL feature"
exit 1
fi
themes_dir="$HOME/.oh-my-zsh/themes"
file_extension=".zsh-theme"
themes_array=($(find -L "$themes_dir" -type f -name "*$file_extension" -exec basename {} \; | sed -e "s/$file_extension//"))
# Add "Random" option to the beginning of the array
themes_array=("Random" "${themes_array[@]}")
rofi_command="rofi -i -dmenu -config $rofi_theme"
menu() {
for theme in "${themes_array[@]}"; do
echo "$theme"
done
}
main() {
choice=$(menu | ${rofi_command})
# if nothing selected, script won't change anything
if [ -z "$choice" ]; then
exit 0
fi
zsh_path="$HOME/.zshrc"
var_name="ZSH_THEME"
if [[ "$choice" == "Random" ]]; then
# Pick a random theme from the original themes_array (excluding "Random")
random_theme=${themes_array[$((RANDOM % (${#themes_array[@]} - 1) + 1))]}
theme_to_set="$random_theme"
notify-send -i "$iDIR/ja.png" "Random theme:" "selected: $random_theme"
else
# Set theme to the selected choice
theme_to_set="$choice"
notify-send -i "$iDIR/ja.png" "Theme selected:" "$choice"
fi
if [ -f "$zsh_path" ]; then
sed -i "s/^$var_name=.*/$var_name=\"$theme_to_set\"/" "$zsh_path"
notify-send -i "$iDIR/ja.png" "OMZ theme" "applied. restart your terminal"
else
notify-send -i "$iDIR/error.png" "E-R-R-O-R" "~.zshrc file not found!"
fi
}
# Check if rofi is already running
if pidof rofi > /dev/null; then
pkill rofi
fi
main