mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2025-05-21 21:05:50 -07:00
add scripts
This commit is contained in:
parent
5cff20c839
commit
773f468b37
171
projects/scripts/change-wallpaper.go
Normal file
171
projects/scripts/change-wallpaper.go
Normal file
@ -0,0 +1,171 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
wallhavenAPI = "https://wallhaven.cc/api/v1"
|
||||
wallpaperDir = "Pictures/wallpapers/wallhaven"
|
||||
)
|
||||
|
||||
var topics = []string{
|
||||
"132262 - Mobuseka",
|
||||
"konosuba",
|
||||
"bunny girl senpai",
|
||||
"oshi no ko",
|
||||
"kill la kill",
|
||||
"lofi",
|
||||
"eminence in shadow",
|
||||
}
|
||||
|
||||
type WallhavenResponse struct {
|
||||
Data []struct {
|
||||
Path string `json:"path"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Initialize random source
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// Check if a file path was provided as argument
|
||||
if len(os.Args) > 1 {
|
||||
imgPath := os.Args[1]
|
||||
if _, err := os.Stat(imgPath); err == nil {
|
||||
changeWallpaper(imgPath, "")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Create wallpaper directory if it doesn't exist
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error getting home directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
wallpaperPath := filepath.Join(homeDir, wallpaperDir)
|
||||
if err := os.MkdirAll(wallpaperPath, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating wallpaper directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Download and set new wallpaper
|
||||
newWallpaper, topic := downloadRandomWallpaper(wallpaperPath, r)
|
||||
if newWallpaper != "" {
|
||||
changeWallpaper(newWallpaper, topic)
|
||||
} else {
|
||||
notify("Failed to download new wallpaper", "critical")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func downloadRandomWallpaper(wallpaperPath string, r *rand.Rand) (string, string) {
|
||||
// Select random topic
|
||||
topic := topics[r.Intn(len(topics))]
|
||||
var query string
|
||||
var displayName string
|
||||
|
||||
// Check if the topic is a tag ID with name
|
||||
if tagRegex := regexp.MustCompile(`^(\d+)\s*-\s*(.+)$`); tagRegex.MatchString(topic) {
|
||||
matches := tagRegex.FindStringSubmatch(topic)
|
||||
query = fmt.Sprintf("id:%s", matches[1])
|
||||
displayName = strings.TrimSpace(matches[2])
|
||||
} else {
|
||||
query = url.QueryEscape(topic)
|
||||
displayName = topic
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Searching for wallpapers related to: %s\n", displayName)
|
||||
|
||||
// Get wallpapers from Wallhaven API
|
||||
resp, err := http.Get(fmt.Sprintf("%s/search?q=%s&purity=100&categories=110&sorting=random", wallhavenAPI, query))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error fetching from Wallhaven: %v\n", err)
|
||||
return "", ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Parse response
|
||||
var wallhavenResp WallhavenResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&wallhavenResp); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing response: %v\n", err)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
if len(wallhavenResp.Data) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "No wallpapers found for topic: %s\n", displayName)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Select a random image from the results
|
||||
randomIndex := r.Intn(len(wallhavenResp.Data))
|
||||
wallpaperURL := wallhavenResp.Data[randomIndex].Path
|
||||
filename := filepath.Base(wallpaperURL)
|
||||
filepath := filepath.Join(wallpaperPath, filename)
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Downloading: %s\n", filename)
|
||||
|
||||
resp, err = http.Get(wallpaperURL)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error downloading wallpaper: %v\n", err)
|
||||
return "", ""
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
file, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating file: %v\n", err)
|
||||
return "", ""
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err := io.Copy(file, resp.Body); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error saving wallpaper: %v\n", err)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
return filepath, displayName
|
||||
}
|
||||
|
||||
func changeWallpaper(wallpaperPath, topic string) {
|
||||
// Save current wallpaper path
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
wallpaperFile := filepath.Join(homeDir, ".wallpaper")
|
||||
if err := os.WriteFile(wallpaperFile, []byte(wallpaperPath), 0644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error saving wallpaper path: %v\n", err)
|
||||
}
|
||||
|
||||
// Change wallpaper using hyprctl
|
||||
cmd := exec.Command("hyprctl", "hyprpaper", "reload", ","+wallpaperPath)
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error changing wallpaper: %v\n", err)
|
||||
}
|
||||
|
||||
// Send notification
|
||||
filename := filepath.Base(wallpaperPath)
|
||||
message := fmt.Sprintf("Wallpaper changed to %s", filename)
|
||||
if topic != "" {
|
||||
message += fmt.Sprintf(" (%s)", topic)
|
||||
}
|
||||
notify(message, "normal")
|
||||
}
|
||||
|
||||
func notify(message, urgency string) {
|
||||
cmd := exec.Command("notify-send", "-i", "hyprpaper", "-u", urgency, "change-wallpaper.go", message)
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error sending notification: %v\n", err)
|
||||
}
|
||||
}
|
90
projects/scripts/change-wallpaper.sh
Executable file
90
projects/scripts/change-wallpaper.sh
Executable file
@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Wallhaven API configuration
|
||||
WALLHAVEN_API="https://wallhaven.cc/api/v1"
|
||||
WALLPAPER_DIR="$HOME/Pictures/wallpapers"
|
||||
TOPICS=(
|
||||
"konosuba"
|
||||
"bunny girl senpai"
|
||||
"oshi no ko"
|
||||
"kill la kill"
|
||||
"lofi"
|
||||
"eminence in shadow"
|
||||
"132262 - Mobuseka"
|
||||
)
|
||||
|
||||
# Create wallpaper directory if it doesn't exist
|
||||
mkdir -p "$WALLPAPER_DIR"
|
||||
|
||||
# Function to download a random wallpaper from Wallhaven
|
||||
download_random_wallpaper() {
|
||||
# Select random topic
|
||||
local random_topic="${TOPICS[$RANDOM % ${#TOPICS[@]}]}"
|
||||
local query
|
||||
local display_name
|
||||
|
||||
# Check if the topic is a tag ID with name
|
||||
if [[ "$random_topic" =~ ^([0-9]+)[[:space:]]*-[[:space:]]*(.+)$ ]]; then
|
||||
query="id:${BASH_REMATCH[1]}"
|
||||
display_name="${BASH_REMATCH[2]}"
|
||||
else
|
||||
query=$(echo "$random_topic" | sed 's/ /+/g')
|
||||
display_name="$random_topic"
|
||||
fi
|
||||
|
||||
echo "Searching for wallpapers related to: $display_name" >&2
|
||||
|
||||
# Get wallpapers from Wallhaven API
|
||||
local response=$(curl -s "$WALLHAVEN_API/search?q=$query&purity=100&categories=110&sorting=random")
|
||||
|
||||
# Get all image URLs and select a random one
|
||||
local urls=($(echo "$response" | jq -r '.data[].path'))
|
||||
if [ ${#urls[@]} -eq 0 ]; then
|
||||
echo "No wallpapers found for topic: $display_name" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local random_index=$((RANDOM % ${#urls[@]}))
|
||||
local url="${urls[$random_index]}"
|
||||
|
||||
if [ -n "$url" ] && [ "$url" != "null" ]; then
|
||||
local filename=$(basename "$url")
|
||||
echo "Downloading: $filename" >&2
|
||||
curl -s "$url" -o "$WALLPAPER_DIR/$filename"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "$WALLPAPER_DIR/$filename"
|
||||
echo "$display_name" > "$WALLPAPER_DIR/.last_topic"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "No wallpapers found for topic: $display_name" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# Handle direct image file input
|
||||
if [[ -f "$1" ]]; then
|
||||
echo "Changing wallpaper to $1"
|
||||
echo "$1" > "$HOME/.wallpaper"
|
||||
hyprctl hyprpaper reload ,"$1"
|
||||
notify-send -i hyprpaper -u normal "change-wallpaper.sh" "Wallpaper changed to ${1##*/}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Download a new random wallpaper
|
||||
new_wallpaper=$(download_random_wallpaper)
|
||||
|
||||
if [ -n "$new_wallpaper" ] && [ -f "$new_wallpaper" ]; then
|
||||
echo "Changing wallpaper to $new_wallpaper"
|
||||
echo "$new_wallpaper" > "$HOME/.wallpaper"
|
||||
|
||||
# Get the topic used for this wallpaper
|
||||
topic=$(cat "$WALLPAPER_DIR/.last_topic")
|
||||
|
||||
# Apply the selected wallpaper
|
||||
hyprctl hyprpaper reload ,"$new_wallpaper"
|
||||
notify-send -i hyprpaper -u normal "change-wallpaper.sh" "Wallpaper changed to ${new_wallpaper##*/wallpapers/} ($topic)"
|
||||
else
|
||||
notify-send -i hyprpaper -u critical "change-wallpaper.sh" "Failed to download new wallpaper"
|
||||
exit 1
|
||||
fi
|
14
projects/scripts/dragon.sh
Executable file
14
projects/scripts/dragon.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
HOME="/home/$(whoami)"
|
||||
clip="$(wl-paste)"
|
||||
if [[ -z "$clip" ]]; then
|
||||
notify-send "Dragon" "Clipboard is empty"
|
||||
exit 1
|
||||
fi
|
||||
DIR="$(hyprctl activeworkspace | grep -i lastwindowtitle | sed 's/\slastwindowtitle: //')"
|
||||
DIR="${DIR//\~/$HOME}"
|
||||
PTH="$DIR/$(basename "$clip")"
|
||||
if [[ -e "$PTH" ]]; then
|
||||
dragon-drop "$PTH"
|
||||
fi
|
29
projects/scripts/mpv-add.sh
Executable file
29
projects/scripts/mpv-add.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
URL="${1:-$(wl-paste -p)}"
|
||||
MPV_SOCKET=/tmp/mpvsocket
|
||||
ICON_PATH="$HOME/.local/share/icons/Magna-Glassy-Dark-Icons/apps/48/mpv.svg"
|
||||
TITLE="mpv-add.sh"
|
||||
|
||||
if [[ -z "$URL" ]]; then
|
||||
notify-send -i "$ICON_PATH" "$TITLE" "No URL provided"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ -f "$URL" ]] && ! yt-dlp --simulate "$URL"; then
|
||||
notify-send -i "$ICON_PATH" "$TITLE" "Invalid URL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! pgrep -x mpv &> /dev/null; then
|
||||
mpv "$URL" &> /dev/null &
|
||||
notify-send -i "$ICON_PATH" "$TITLE" "Playing $URL"
|
||||
else
|
||||
if echo "{ \"command\": [\"script-message\", \"add_to_queue\", \"$URL\" ] }" | socat - "$MPV_SOCKET" &> /dev/null; then
|
||||
notify-send -i "$ICON_PATH" "$TITLE" "Added $URL to queue"
|
||||
else
|
||||
notify-send -i "$ICON_PATH" "$TITLE" "Failed to add $URL to queue"
|
||||
fi
|
||||
fi
|
15
projects/scripts/ocr.sh
Executable file
15
projects/scripts/ocr.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# RES="$(slurp | grim -g - - | gazou | sed '1d;$d')"
|
||||
# # Truncate RES for display if it's longer than 100 characters
|
||||
# DISPLAY_RES="${RES:0:100}"
|
||||
# if [ ${#RES} -gt 100 ]; then
|
||||
# DISPLAY_RES="${DISPLAY_RES}..."
|
||||
# fi
|
||||
# notify-send "GAZOU" "Text: $DISPLAY_RES"
|
||||
# echo "$RES" | wl-copy
|
||||
|
||||
slurp | grim -g - /tmp/ocr.png || exit 1
|
||||
transformers_ocr recognize --image-path /tmp/ocr.png || exit 1
|
||||
notify-send "tramsformers_ocr" "Text: $DISPLAY_RES"
|
93
projects/scripts/record-audio.sh
Normal file
93
projects/scripts/record-audio.sh
Normal file
@ -0,0 +1,93 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Version 1.2
|
||||
# shoutout to https://gist.github.com/Cephian/f849e326e3522be9a4386b60b85f2f23 for the original script,
|
||||
# https://github.com/xythh/ added the ankiConnect functionality
|
||||
# toggle record computer audio (run once to start, run again to stop)
|
||||
# dependencies: ffmpeg, pulseaudio, curl
|
||||
|
||||
# where recording gets saved, gets deleted after being imported to anki
|
||||
DIRECTORY="$HOME/.cache/"
|
||||
FORMAT="mp3" # ogg or mp3
|
||||
# cut file since it glitches a bit at the end sometimes
|
||||
CUT_DURATION="0.1"
|
||||
#port used by ankiconnect
|
||||
ankiConnectPort="8765"
|
||||
# gets the newest created card, so make sure to create the card first with yomichan
|
||||
newestNoteId=$(curl -s localhost:$ankiConnectPort -X POST -d '{"action": "findNotes", "version": 6, "params": { "query": "is:new"}}' | jq '.result[-1]')
|
||||
#Audio field name
|
||||
audioFieldName="SentenceAudio"
|
||||
|
||||
#if there is no newest note, you either have a complete empty anki or ankiconnect isn't running
|
||||
if [ "$newestNoteId" = "" ]; then
|
||||
notify-send "anki connect not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if pgrep -f "parec"; then
|
||||
pkill -f "parec"
|
||||
else
|
||||
time=$(date +%s)
|
||||
name="$DIRECTORY/$time"
|
||||
wav_file="$name.wav"
|
||||
out_file="$name.$FORMAT"
|
||||
|
||||
if ! [ -d "$DIRECTORY" ]; then
|
||||
mkdir "$DIRECTORY"
|
||||
fi
|
||||
notify-send -t 1000 "Audio recording started"
|
||||
#timeout 1m arecord -t wav -f cd "$wav_file"
|
||||
|
||||
# just grabs last running source... may not always work if your pulseaudio setup is complicated
|
||||
if ! timeout 1m parec -d"$(pactl list sinks | grep -B1 'State: RUNNING' | sed -nE 's/Sink #(.*)/\1/p' | tail -n 1)" --file-format=wav "$wav_file"; then
|
||||
|
||||
notify-send "Error recording " "most likely no audio playing"
|
||||
rm "$wav_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
input_duration=$(ffprobe -v error -select_streams a:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 "$wav_file")
|
||||
output_duration=$(echo "$input_duration"-"$CUT_DURATION" | bc)
|
||||
|
||||
# encode file and delete OG
|
||||
if [ $FORMAT = "ogg" ]; then
|
||||
ffmpeg -i "$wav_file" -vn -codec:a libvorbis -b:a 64k -t "$output_duration" "$out_file"
|
||||
elif [ $FORMAT = "mp3" ]; then
|
||||
ffmpeg -i "$wav_file" -vn -codec:a libmp3lame -qscale:a 1 -t "$output_duration" "$out_file"
|
||||
else
|
||||
notify-send "Record Error" "Unknown format $FORMAT"
|
||||
fi
|
||||
rm "$wav_file"
|
||||
|
||||
# Update newest note with recorded audio
|
||||
curl -s localhost:$ankiConnectPort -X POST -d '{
|
||||
|
||||
"action": "updateNoteFields",
|
||||
"version": 6,
|
||||
"params": {
|
||||
"note": {
|
||||
"id": '"$newestNoteId"',
|
||||
"fields": {
|
||||
"'$audioFieldName'": ""
|
||||
},
|
||||
"audio": [{
|
||||
"path": "'"$out_file"'",
|
||||
"filename": "'"$time"'.'$FORMAT'",
|
||||
"fields": [
|
||||
"'$audioFieldName'"
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}'
|
||||
# opens changed note, comment if you don't want it.
|
||||
curl -s localhost:$ankiConnectPort -X POST -d '{
|
||||
"action": "guiBrowse",
|
||||
"version": 6,
|
||||
"params": {
|
||||
"query": "nid:'"$newestNoteId"'"
|
||||
}
|
||||
}'
|
||||
notify-send -t 1000 "Audio recording copied"
|
||||
rm "$out_file"
|
||||
fi
|
75
projects/scripts/screenshot-anki.sh
Normal file
75
projects/scripts/screenshot-anki.sh
Normal file
@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Version 1.2
|
||||
# click and drag to screenshot dragged portion
|
||||
# click on specific window to screenshot window area
|
||||
# dependencies: imagemagick, xclip,curl maybe xdotool (see comment below)
|
||||
# shoutout to https://gist.github.com/Cephian/f849e326e3522be9a4386b60b85f2f23 for the original script,
|
||||
# https://github.com/xythh/ added the ankiConnect functionality
|
||||
# if anki is running the image is added to your latest note as a jpg, if anki is not running it's added to your clipboard as a png
|
||||
time=$(date +%s)
|
||||
tmp_file="$HOME/.cache/$time"
|
||||
ankiConnectPort="8765"
|
||||
pictureField="Picture"
|
||||
quality="90"
|
||||
|
||||
# This gets your notes marked as new and returns the newest one.
|
||||
newestNoteId=$(curl -s localhost:$ankiConnectPort -X POST -d '{"action": "findNotes", "version": 6, "params": { "query": "is:new"}}' | jq '.result[-1]')
|
||||
|
||||
# you can remove these two lines if you don't have software which
|
||||
# makes your mouse disappear when you use the keyboard (e.g. xbanish, unclutter)
|
||||
# https://github.com/ImageMagick/ImageMagick/issues/1745#issuecomment-777747494
|
||||
xdotool mousemove_relative 1 1
|
||||
xdotool mousemove_relative -- -1 -1
|
||||
|
||||
# if anki connect is running it will return your latest note id, and the following code will run, if anki connect is not running nothing is return.
|
||||
if [ "$newestNoteId" != "" ]; then
|
||||
if ! import -quality $quality "$tmp_file.jpg"; then
|
||||
# most likley reason this returns a error, is for fullscreen applications that take full control which does not allowing imagemagick to select the area, use windowed fullscreen or if running wine use a virtual desktop to avoid this.
|
||||
notify-send "Error screenshoting " "most likely unable to find selection"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
curl -s localhost:$ankiConnectPort -X POST -d '{
|
||||
"action": "updateNoteFields",
|
||||
"version": 6,
|
||||
"params": {
|
||||
"note": {
|
||||
"id": '"$newestNoteId"',
|
||||
"fields": {
|
||||
"'$pictureField'": ""
|
||||
},
|
||||
"picture": [{
|
||||
"path": "'"$tmp_file"'.jpg",
|
||||
"filename": "paste-'"$time"'.jpg",
|
||||
"fields": [
|
||||
"'$pictureField'"
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}'
|
||||
|
||||
#remove if you don't want anki to show you the card you just edited
|
||||
curl -s localhost:$ankiConnectPort -X POST -d '{
|
||||
"action": "guiBrowse",
|
||||
"version": 6,
|
||||
"params": {
|
||||
"query": "nid:'"$newestNoteId"'"
|
||||
}
|
||||
}'
|
||||
|
||||
#you can comment this if you do not use notifcations.
|
||||
notify-send "Screenshot Taken" "Added to note"
|
||||
rm "$tmp_file.jpg"
|
||||
else
|
||||
if ! import -quality $quality "$tmp_file.png"; then
|
||||
notify-send "Error screenshoting " "most likely unable to find selection"
|
||||
exit 1
|
||||
fi
|
||||
# we use pngs when copying to clipboard because they have greater support when pasting.
|
||||
xclip -selection clipboard -target image/png -i "$tmp_file.png"
|
||||
rm "$tmp_file.png"
|
||||
#you can comment this if you do not use notifcations.
|
||||
notify-send "Screenshot Taken" "Copied to clipboard"
|
||||
fi
|
109
projects/scripts/screenshot.sh
Executable file
109
projects/scripts/screenshot.sh
Executable file
@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# GUI Screenshot Tool for Wayland Using Zenity, Grim, Slurp, and Rofi
|
||||
|
||||
SCRIPT_NAME=$(basename "$0")
|
||||
TMP_DIR=/tmp
|
||||
DEFAULT_FILENAME=screenshot.png
|
||||
TMP_SCREENSHOT="$TMP_DIR/$DEFAULT_FILENAME"
|
||||
HYPRLAND_REGEX='.at[0],(.at[1]) .size[0]x(.size[1])'
|
||||
REQUIREMENTS=(grim slurp rofi zenity wl-copy)
|
||||
USE_NOTIFICATIONS=1
|
||||
CHOICES=(
|
||||
"1. Select a region and save - slurp | grim -g - \"$TMP_SCREENSHOT\""
|
||||
"2. Select a region and copy to clipboard - slurp | grim -g - - | wl-copy"
|
||||
"3. Whole screen - grim \"$TMP_SCREENSHOT\""
|
||||
"4. Current window - hyprctl -j activewindow | jq -r \"${HYPRLAND_REGEX}\" | grim -g - \"$TMP_SCREENSHOT\""
|
||||
"5. Edit - slurp | grim -g - - | swappy -f -"
|
||||
"6. Quit - exit 0"
|
||||
)
|
||||
|
||||
notify() {
|
||||
local body="$1"
|
||||
local title="$2"
|
||||
if [[ -z "$body" ]]; then
|
||||
echo "notify: No message provided"
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$title" ]]; then
|
||||
title="$SCRIPT_NAME"
|
||||
fi
|
||||
|
||||
if ((USE_NOTIFICATIONS)); then
|
||||
notify-send "$title" "$body"
|
||||
else
|
||||
printf "%s\n%s\n" "$title" "$body"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
check_deps() {
|
||||
for cmd in "${REQUIREMENTS[@]}"; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
echo "Error: $cmd is not installed. Please install it first."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
CHOICE="$(rofi -dmenu -i -p "Enter option or select from the list" \
|
||||
-mesg "Select a Screenshot Option" \
|
||||
-a 0 -no-custom -location 0 \
|
||||
-yoffset 30 -xoffset 30 \
|
||||
-theme-str 'listview {columns: 2; lines: 3;} window {width: 45%;}' \
|
||||
-window-title "$SCRIPT_NAME" \
|
||||
-format 'i' \
|
||||
<<< "$(printf "%s\n" "${CHOICES[@]%% - *}")")"
|
||||
|
||||
if [[ -z "$CHOICE" ]]; then
|
||||
notify "No option selected." ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep 0.2 # give time for the rofi window to close
|
||||
CMD="${CHOICES[$CHOICE]#* -}"
|
||||
if [[ -z "$CMD" ]]; then
|
||||
notify "No option selected." ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# For option 2 (copy to clipboard), handle differently
|
||||
if [[ "$CHOICE" == "1" ]]; then
|
||||
if eval "$CMD"; then
|
||||
notify "Screenshot copied to clipboard"
|
||||
exit 0
|
||||
else
|
||||
notify "An error occurred while taking the screenshot."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! eval "$CMD"; then
|
||||
notify "An error occurred while taking the screenshot."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
notify "screenshot.sh" "Screenshot saved temporarily.\nChoose where to save it permanently"
|
||||
|
||||
FILE=$(zenity --file-selection --title="Save Screenshot" --filename="$DEFAULT_FILENAME" --save 2> /dev/null)
|
||||
case "$?" in
|
||||
0)
|
||||
if mv "$TMP_SCREENSHOT" "$FILE"; then
|
||||
notify "Screenshot saved to $FILE"
|
||||
else
|
||||
notify "Failed to save screenshot to $FILE"
|
||||
fi
|
||||
;;
|
||||
1)
|
||||
rm -f "$TMP_SCREENSHOT"
|
||||
notify "Screenshot discarded"
|
||||
;;
|
||||
-1)
|
||||
notify "An unexpected error has occurred."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_deps
|
||||
main
|
2
projects/scripts/teppei/.gitignore
vendored
Normal file
2
projects/scripts/teppei/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
env
|
||||
.git
|
10
projects/scripts/teppei/open.sh
Executable file
10
projects/scripts/teppei/open.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
EP="$1"
|
||||
DIR="/truenas/sudacode/japanese/nihongo-con-teppei/Nihongo-Con-Teppei-E$EP.mp3"
|
||||
export FONTCONFIG_FILE="$HOME/.config/mpv/mpv-fonts.conf"
|
||||
if mpv --profile=builtin-pseudo-gui --vid=1 --external-file=pod/cover.jpg "$DIR"; then
|
||||
echo "Finished playing Nihongo Con Teppei E$EP"
|
||||
else
|
||||
echo "Failed to play Nihongo Con Teppei E$EP"
|
||||
fi
|
92
projects/scripts/teppei/teppei.py
Executable file
92
projects/scripts/teppei/teppei.py
Executable file
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python
|
||||
import logging
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from requests import get
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
AUDIO_BASE_URL = (
|
||||
"https://www.heypera.com/listen/nihongo-con-teppei-for-beginners/{}/next"
|
||||
)
|
||||
SUB_BASE_URL = "https://storage.googleapis.com/pera-transcripts/nihongo-con-teppei-for-beginners/transcripts/{}.vtt"
|
||||
|
||||
|
||||
def get_audio_url(episode_num: int):
|
||||
chrome_options = Options()
|
||||
chrome_options.add_argument("--headless")
|
||||
chrome_options.add_argument("--disable-gpu")
|
||||
chrome_options.add_argument("--no-sandbox")
|
||||
|
||||
driver = webdriver.Chrome(options=chrome_options)
|
||||
|
||||
try:
|
||||
driver.get(AUDIO_BASE_URL.format(episode_num))
|
||||
|
||||
# Wait for the audio element to be present
|
||||
audio_element = WebDriverWait(driver, 10).until(
|
||||
EC.presence_of_element_located((By.TAG_NAME, "audio"))
|
||||
)
|
||||
audio_url = audio_element.get_attribute("src")
|
||||
if audio_url:
|
||||
logger.info(f"Audio URL: {audio_url}")
|
||||
else:
|
||||
logger.error("No audio URL found")
|
||||
return audio_url
|
||||
except Exception as e:
|
||||
logger.error(f"Error: {e}")
|
||||
raise e
|
||||
finally:
|
||||
driver.quit()
|
||||
|
||||
|
||||
def get_sub_url(episode_num: int):
|
||||
return SUB_BASE_URL.format(episode_num)
|
||||
|
||||
|
||||
def download_file(url: str, filename: str):
|
||||
response = get(url, timeout=10)
|
||||
if response.status_code != 200:
|
||||
logger.error(f"Failed to download {filename}")
|
||||
return
|
||||
with open(filename, "wb") as file:
|
||||
file.write(response.content)
|
||||
logger.info(f"Downloaded {filename}")
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = ArgumentParser(description="Get the audio URL for a given episode number")
|
||||
parser.add_argument(
|
||||
"episode_num", type=int, help="The episode number to get the audio URL for"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--download", action="store_true", help="Download the audio file"
|
||||
)
|
||||
parser.add_argument("-o", "--output", help="Output directory name")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
if args.episode_num < 1:
|
||||
logger.error("Episode number must be greater than 0")
|
||||
episode = args.episode_num
|
||||
audio = get_audio_url(episode)
|
||||
sub = get_sub_url(episode)
|
||||
if args.download:
|
||||
if args.output:
|
||||
download_file(audio, f"{args.output}/Nihongo-Con-Teppei-E{episode:0>2}.mp3")
|
||||
download_file(sub, f"{args.output}/Nihongo-Con-Teppei-E{episode:0>2}.vtt")
|
||||
else:
|
||||
download_file(audio, f"Nihongo-Con-Teppei-E{episode:0>2}.mp3")
|
||||
download_file(sub, f"Nihongo-Con-Teppei-E{episode:0>2}.vtt")
|
||||
else:
|
||||
print(f"Audio URL: {audio}")
|
||||
print(f"Subtitle URL: {sub}")
|
9
projects/scripts/waybar/escape-pango.sh
Executable file
9
projects/scripts/waybar/escape-pango.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
CMD="$1"
|
||||
BEFORE_SPACE="${CMD%% *}"
|
||||
if command -v "$BEFORE_SPACE" &> /dev/null; then
|
||||
eval "$CMD" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/'\''/\'/g'
|
||||
else
|
||||
echo "$CMD" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/'\''/\'/g'
|
||||
fi
|
238
projects/scripts/waybar/mediaplayer.py
Executable file
238
projects/scripts/waybar/mediaplayer.py
Executable file
@ -0,0 +1,238 @@
|
||||
#!/usr/bin/env python3
|
||||
import gi
|
||||
|
||||
gi.require_version("Playerctl", "2.0")
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from typing import List
|
||||
|
||||
import gi
|
||||
from gi.repository import GLib, Playerctl
|
||||
from gi.repository.Playerctl import Player
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
is_plain = False
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
logger.info("Received signal to stop, exiting")
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
# loop.quit()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class PlayerManager:
|
||||
def __init__(self, selected_player=None, excluded_player=[]):
|
||||
self.manager = Playerctl.PlayerManager()
|
||||
self.loop = GLib.MainLoop()
|
||||
self.manager.connect(
|
||||
"name-appeared", lambda *args: self.on_player_appeared(*args)
|
||||
)
|
||||
self.manager.connect(
|
||||
"player-vanished", lambda *args: self.on_player_vanished(*args)
|
||||
)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
self.selected_player = selected_player
|
||||
self.excluded_player = excluded_player.split(",") if excluded_player else []
|
||||
|
||||
self.init_players()
|
||||
|
||||
def init_players(self):
|
||||
for player in self.manager.props.player_names:
|
||||
if player.name in self.excluded_player:
|
||||
continue
|
||||
if self.selected_player is not None and self.selected_player != player.name:
|
||||
logger.debug(f"{player.name} is not the filtered player, skipping it")
|
||||
continue
|
||||
self.init_player(player)
|
||||
|
||||
def run(self):
|
||||
logger.info("Starting main loop")
|
||||
self.loop.run()
|
||||
|
||||
def init_player(self, player):
|
||||
logger.info(f"Initialize new player: {player.name}")
|
||||
player = Playerctl.Player.new_from_name(player)
|
||||
player.connect("playback-status", self.on_playback_status_changed, None)
|
||||
player.connect("metadata", self.on_metadata_changed, None)
|
||||
self.manager.manage_player(player)
|
||||
self.on_metadata_changed(player, player.props.metadata)
|
||||
|
||||
def get_players(self) -> List[Player]:
|
||||
return self.manager.props.players
|
||||
|
||||
def write_output(self, text, player):
|
||||
logger.debug(f"Writing output: {text}")
|
||||
|
||||
if not is_plain:
|
||||
output = {
|
||||
"text": text,
|
||||
"class": "custom-" + player.props.player_name,
|
||||
"alt": player.props.player_name,
|
||||
}
|
||||
|
||||
sys.stdout.write(json.dumps(output))
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
sys.stdout.write(text + "\n")
|
||||
# sys.stdout.write(text)
|
||||
sys.stdout.flush()
|
||||
|
||||
def clear_output(self):
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
def on_playback_status_changed(self, player, status, _=None):
|
||||
logger.debug(
|
||||
f"Playback status changed for player {player.props.player_name}: {status}"
|
||||
)
|
||||
self.on_metadata_changed(player, player.props.metadata)
|
||||
|
||||
def get_first_playing_player(self):
|
||||
players = self.get_players()
|
||||
logger.debug(f"Getting first playing player from {len(players)} players")
|
||||
if len(players) > 0:
|
||||
# if any are playing, show the first one that is playing
|
||||
# reverse order, so that the most recently added ones are preferred
|
||||
for player in players[::-1]:
|
||||
if player.props.status == "Playing":
|
||||
return player
|
||||
# if none are playing, show the first one
|
||||
return players[0]
|
||||
else:
|
||||
logger.debug("No players found")
|
||||
return None
|
||||
|
||||
def show_most_important_player(self):
|
||||
logger.debug("Showing most important player")
|
||||
# show the currently playing player
|
||||
# or else show the first paused player
|
||||
# or else show nothing
|
||||
current_player = self.get_first_playing_player()
|
||||
if current_player is not None:
|
||||
self.on_metadata_changed(current_player, current_player.props.metadata)
|
||||
else:
|
||||
self.clear_output()
|
||||
|
||||
def on_metadata_changed(self, player, metadata, _=None):
|
||||
logger.debug(f"Metadata changed for player {player.props.player_name}")
|
||||
player_name = player.props.player_name
|
||||
artist = player.get_artist()
|
||||
title = player.get_title()
|
||||
if title is not None:
|
||||
title = title.replace("&", "&")
|
||||
else:
|
||||
title = "No title"
|
||||
|
||||
track_info = ""
|
||||
if (
|
||||
player_name == "spotify"
|
||||
and "mpris:trackid" in metadata.keys()
|
||||
and ":ad:" in player.props.metadata["mpris:trackid"]
|
||||
):
|
||||
track_info = "Advertisement"
|
||||
elif artist is not None and title is not None:
|
||||
track_info = f"{artist} - {title}"
|
||||
else:
|
||||
track_info = title
|
||||
|
||||
if track_info:
|
||||
if player.props.status == "Playing":
|
||||
track_info = "playing " + track_info
|
||||
else:
|
||||
track_info = "paused " + track_info
|
||||
# only print output if no other player is playing
|
||||
current_playing = self.get_first_playing_player()
|
||||
if (
|
||||
current_playing is None
|
||||
or current_playing.props.player_name == player.props.player_name
|
||||
):
|
||||
self.write_output(track_info, player)
|
||||
else:
|
||||
logger.debug(
|
||||
f"Other player {current_playing.props.player_name} is playing, skipping"
|
||||
)
|
||||
|
||||
def on_player_appeared(self, _, player):
|
||||
logger.info(f"Player has appeared: {player.name}")
|
||||
if player.name in self.excluded_player:
|
||||
logger.debug(
|
||||
"New player appeared, but it's in exclude player list, skipping"
|
||||
)
|
||||
return
|
||||
if player is not None and (
|
||||
self.selected_player is None or player.name == self.selected_player
|
||||
):
|
||||
self.init_player(player)
|
||||
else:
|
||||
logger.debug(
|
||||
"New player appeared, but it's not the selected player, skipping"
|
||||
)
|
||||
|
||||
def on_player_vanished(self, _, player):
|
||||
logger.info(f"Player {player.props.player_name} has vanished")
|
||||
self.show_most_important_player()
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
# Increase verbosity with every occurrence of -v
|
||||
parser.add_argument("-v", "--verbose", action="count", default=0)
|
||||
|
||||
parser.add_argument("-x", "--exclude", "- Comma-separated list of excluded player")
|
||||
parser.add_argument(
|
||||
"-p", "--plain", action="store_true", help="Plain text output", default=False
|
||||
)
|
||||
|
||||
# Define for which player we"re listening
|
||||
parser.add_argument("--player")
|
||||
|
||||
parser.add_argument("--enable-logging", action="store_true")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
global is_plain
|
||||
arguments = parse_arguments()
|
||||
|
||||
# Initialize logging
|
||||
if arguments.enable_logging:
|
||||
logfile = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), "media-player.log"
|
||||
)
|
||||
logging.basicConfig(
|
||||
filename=logfile,
|
||||
level=logging.DEBUG,
|
||||
format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s",
|
||||
)
|
||||
|
||||
# Logging is set by default to WARN and higher.
|
||||
# With every occurrence of -v it's lowered by one
|
||||
logger.setLevel(max((3 - arguments.verbose) * 10, 0))
|
||||
|
||||
logger.info("Creating player manager")
|
||||
if arguments.player:
|
||||
logger.info(f"Filtering for player: {arguments.player}")
|
||||
if arguments.exclude:
|
||||
logger.info(f"Exclude player {arguments.exclude}")
|
||||
if arguments.plain:
|
||||
logger.info("Plain text output")
|
||||
is_plain = arguments.plain
|
||||
|
||||
player = PlayerManager(arguments.player, arguments.exclude)
|
||||
player.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
main()
|
41
projects/scripts/waybar/pipewire.sh
Executable file
41
projects/scripts/waybar/pipewire.sh
Executable file
@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# https://blog.dhampir.no/content/sleeping-without-a-subprocess-in-bash-and-how-to-sleep-forever
|
||||
snore() {
|
||||
local IFS
|
||||
[[ -n "${_snore_fd:-}" ]] || exec {_snore_fd}<> <(:)
|
||||
read -r ${1:+-t "$1"} -u $_snore_fd || :
|
||||
}
|
||||
|
||||
DELAY=0.2
|
||||
|
||||
while snore $DELAY; do
|
||||
WP_OUTPUT=$(wpctl get-volume @DEFAULT_AUDIO_SINK@)
|
||||
|
||||
if [[ $WP_OUTPUT =~ ^Volume:[[:blank:]]([0-9]+)\.([0-9]{2})([[:blank:]].MUTED.)?$ ]]; then
|
||||
if [[ -n ${BASH_REMATCH[3]} ]]; then
|
||||
printf "MUTE\n"
|
||||
else
|
||||
VOLUME=$((10#${BASH_REMATCH[1]}${BASH_REMATCH[2]}))
|
||||
ICON=(
|
||||
""
|
||||
""
|
||||
""
|
||||
)
|
||||
|
||||
if [[ $VOLUME -gt 50 ]]; then
|
||||
printf "%s" "${ICON[0]} "
|
||||
elif [[ $VOLUME -gt 25 ]]; then
|
||||
printf "%s" "${ICON[1]} "
|
||||
elif [[ $VOLUME -ge 0 ]]; then
|
||||
printf "%s" "${ICON[2]} "
|
||||
fi
|
||||
|
||||
printf "$VOLUME%%\n"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
25
projects/scripts/waybar/playerctl.sh
Executable file
25
projects/scripts/waybar/playerctl.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
PLAYER="$1"
|
||||
|
||||
if [ -z "$PLAYER" ]; then
|
||||
echo "Usage: $0 <player>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STATUS="$(playerctl -p "$PLAYER" status)"
|
||||
|
||||
if [ -z "$STATUS" ] || [ "$STATUS" = "Stopped" ]; then
|
||||
exit 0
|
||||
elif [ "$STATUS" = "Paused" ]; then
|
||||
STATUS=" "
|
||||
elif [ "$STATUS" = "Playing" ]; then
|
||||
STATUS=" "
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TITLE="$(playerctl -p "$PLAYER" metadata title)"
|
||||
ARTIST="$(playerctl -p "$PLAYER" metadata artist)"
|
||||
|
||||
printf "%s\n" "$STATUS$TITLE - $ARTIST"
|
19
projects/scripts/waybar/reload-waybar
Executable file
19
projects/scripts/waybar/reload-waybar
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
if pgrep -af "waybar -c /home/sudacode/.config/waybar/catppuccin-macchiato/config-battery.jsonc -s /home/sudacode/.config/waybar/catppuccin-macchiato/style.css" ||
|
||||
pgrep -af "waybar -c /home/sudacode/.config/waybar/catppuccin-macchiato/config.jsonc -s /home/sudacode/.config/waybar/catppuccin-macchiato/style.css"; then
|
||||
killall waybar
|
||||
fi
|
||||
|
||||
BASE_DIR="$HOME/.config/waybar/catppuccin-macchiato"
|
||||
NODE_NAME="$(hyprctl systeminfo | grep -i "node name" | sed 's/Node name: //')"
|
||||
|
||||
if [[ "$NODE_NAME" = "sc-arch" ]]; then
|
||||
CONFIG="$BASE_DIR/config.jsonc"
|
||||
else
|
||||
CONFIG="$BASE_DIR/config-laptop.jsonc"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
waybar -c "$CONFIG" -s "$BASE_DIR/style.css" &>/dev/null &
|
11
projects/scripts/waybar/scroll-firefox.sh
Executable file
11
projects/scripts/waybar/scroll-firefox.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
zscroll -p ' | ' --delay 0.2 \
|
||||
--length 30 \
|
||||
--match-command "$HOME/.config/waybar/scripts/playerctl.sh firefox" \
|
||||
--match-text ' ' "" \
|
||||
--match-text ' ' "--scroll 0" \
|
||||
--match-text "^volume:" "--before-text '' --scroll 0 --after-text ''" \
|
||||
--update-interval 1 \
|
||||
--update-check true "$HOME/.config/waybar/scripts/playerctl.sh firefox" &
|
||||
wait
|
11
projects/scripts/waybar/scroll-mpd.sh
Executable file
11
projects/scripts/waybar/scroll-mpd.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
zscroll -p ' | ' --delay 0.2 \
|
||||
--length 30 \
|
||||
--match-command "$HOME/.config/waybar/scripts/escape-pango.sh 'mpc status'" \
|
||||
--match-text "playing" "--before-text ' '" \
|
||||
--match-text "paused" "--before-text ' ' --scroll 0" \
|
||||
--match-text "^volume:" "--before-text '' --scroll 0 --after-text ''" \
|
||||
--update-interval 1 \
|
||||
--update-check true "$HOME/.config/waybar/scripts/escape-pango.sh 'mpc current'" &
|
||||
wait
|
11
projects/scripts/waybar/scroll-mpv.sh
Executable file
11
projects/scripts/waybar/scroll-mpv.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
zscroll -p ' | ' --delay 0.2 \
|
||||
--length 30 \
|
||||
--match-command "$HOME/.config/waybar/scripts/playerctl.sh mpv" \
|
||||
--match-text ' ' "" \
|
||||
--match-text ' ' "--scroll 0" \
|
||||
--match-text "^volume:" "--before-text '' --scroll 0 --after-text ''" \
|
||||
--update-interval 1 \
|
||||
--update-check true "$HOME/.config/waybar/scripts/playerctl.sh mpv" &
|
||||
wait
|
16
projects/scripts/waybar/wttr.sh
Executable file
16
projects/scripts/waybar/wttr.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
for _ in {1..5}; do
|
||||
text="$(curl -s "https://wttr.in/$1?format=1" | sed -E "s/\s+/ /g")"
|
||||
if [[ "$?" = 0 ]]; then
|
||||
# text="$(echo "$text" | awk '{print $2}')"
|
||||
tooltip=$(curl -s "https://wttr.in/$1?format=4")
|
||||
if [ "$?" = 0 ]; then
|
||||
tooltip=$(echo "$tooltip" | sed -E "s/\s+/ /g")
|
||||
echo "{\"text\":\"$text\", \"tooltip\":\"$tooltip\"}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo "{\"text\":\"error\", \"tooltip\":\"error\"}"
|
Loading…
x
Reference in New Issue
Block a user