✨ feat(scripts, screen-capture): add shell script for screen recording & image capturing with various options and actions

This commit is contained in:
sankalp committed 2025-06-19 13:53:44 +05:45
1 parent 18c296f863
commit e257ec2a2b
3 files changed
+648 -159

No files matched your search

+1 -2
View File
@@ -12,7 +12,6 @@ from modules.dock.main import Dock
from modules.switcher import ApplicationSwitcher from modules.switcher import ApplicationSwitcher
from modules.osd import OSD from modules.osd import OSD
from modules.launcher import Launcher from modules.launcher import Launcher
from services.screencapture import ScreenCapture
gi.require_version("GLib", "2.0") gi.require_version("GLib", "2.0")
@@ -49,7 +48,7 @@ if __name__ == "__main__":
switcher = ApplicationSwitcher() switcher = ApplicationSwitcher()
osd = OSD() osd = OSD()
launcher = Launcher() launcher = Launcher()
sc = ScreenCapture()
# Set corners visibility based on config # Set corners visibility based on config
corners_visible = config.get("corners_visible", True) corners_visible = config.get("corners_visible", True)
corners.set_visible(corners_visible) corners.set_visible(corners_visible)
+647
View File
@@ -0,0 +1,647 @@
#!/bin/env bash
# Script name
SCRIPT_NAME=$(basename "$0")
# Function to display usage
usage() {
cat <<EOF
Usage: $SCRIPT_NAME <command> [options]
Commands:
screenshot <target> Take a screenshot
Targets:
selection - Screenshot selected area
eDP-1 - Screenshot eDP-1 display
HDMI-A-1 - Screenshot HDMI-A-1 display
both - Screenshot both displays
record <target> Start/stop recording (with audio)
Targets:
selection - Record selected area
eDP-1 - Record eDP-1 display
HDMI-A-1 - Record HDMI-A-1 display
stop - Stop current recording
record-noaudio <target> Start/stop recording (no audio)
Targets:
selection - Record selected area without audio
eDP-1 - Record eDP-1 display without audio
HDMI-A-1 - Record HDMI-A-1 display without audio
stop - Stop current recording
record-hq <target> Start/stop high-quality recording (for YouTube)
Targets:
selection - Record selected area in high quality
eDP-1 - Record eDP-1 display in high quality
HDMI-A-1 - Record HDMI-A-1 display in high quality
stop - Stop current recording
record-gif <target> Start/stop GIF recording
Targets:
selection - Record selected area as GIF
eDP-1 - Record eDP-1 display as GIF
HDMI-A-1 - Record HDMI-A-1 display as GIF
stop - Stop current recording
status Check if recording is active (exit 0 if recording, 1 if not)
convert <format> Convert recordings
Formats:
webm - Convert MKV files to WebM
iphone - Convert MKV files for iPhone
youtube - Convert MKV files for YouTube (high quality)
gif - Convert MKV/MP4 files to GIF
Examples:
$SCRIPT_NAME screenshot selection
$SCRIPT_NAME record eDP-1
$SCRIPT_NAME record-noaudio selection
$SCRIPT_NAME record-hq eDP-1
$SCRIPT_NAME record-gif selection
$SCRIPT_NAME record stop
$SCRIPT_NAME convert gif
EOF
exit 1
}
# Check if no arguments provided
if [ $# -eq 0 ]; then
usage
fi
# Function to send screenshot notification with action buttons
send_screenshot_notification() {
local full_path="$1"
local save_dir=$(dirname "$full_path")
ACTION=$(notify-send -a "Modus" -i "$full_path" "Screenshot saved" "in $full_path" \
-A "view=View" -A "edit=Edit" -A "open=Open Folder")
case "$ACTION" in
view) xdg-open "$full_path" ;;
edit) swappy -f "$full_path" ;;
open) xdg-open "$save_dir" ;;
esac
}
# Function to send recording notification with action buttons
send_recording_notification() {
local full_path="$1"
local save_dir=$(dirname "$full_path")
ACTION=$(notify-send -a "Modus" -i "camera-video-symbolic" "Recording saved" "in $full_path" \
-A "view=View" -A "open=Open Folder")
case "$ACTION" in
view) xdg-open "$full_path" ;;
open) xdg-open "$save_dir" ;;
esac
}
wf-recorder_check() {
if pgrep -x "wf-recorder" >/dev/null; then
pkill -INT -x wf-recorder
# Get the recording file path and send notification with actions
if [ -f /tmp/recording.txt ]; then
recording_file=$(cat /tmp/recording.txt)
wl-copy <"$recording_file"
send_recording_notification "$recording_file"
else
notify-send "Recording stopped" "wf-recorder process terminated"
fi
exit 0
fi
}
# Function to record with standard settings
record_video() {
local output_file="$1"
shift
wf-recorder "$@" -f "$output_file" -c libvpx-vp9 --pixel-format yuv420p -F "eq=brightness=0.12:contrast=1.1"
}
# Function to record without audio
record_video_noaudio() {
local output_file="$1"
shift
wf-recorder "$@" -f "$output_file" -c libvpx-vp9 --pixel-format yuv420p -F "eq=brightness=0.12:contrast=1.1" --no-audio
}
record_high_quality() {
local output_file="$1"
shift
# High quality settings for YouTube uploads
# - h264_vaapi for hardware encoding (if available) or libx264 for software
# - yuv420p pixel format for maximum compatibility
# - High bitrate (8000k) for quality
# - GOP size of 30 for better seeking
# - Preset 'slow' for better compression efficiency
# - CRF 18 for high quality (lower = better quality, 0-51 scale)
# - Audio at 192k bitrate
# - 60 FPS for smooth motion
# - No color filters to maintain original colors
# Check if VAAPI hardware encoding is available
if vainfo &>/dev/null && wf-recorder --help | grep -q "h264_vaapi"; then
# Use hardware encoding for better performance
wf-recorder "$@" -f "$output_file" \
-c h264_vaapi \
-p "preset=slow" \
-p "crf=18" \
-r 60 \
-b 8000000 \
-B 192000 \
--pixel-format yuv420p \
-g 30
else
# Fallback to software encoding
wf-recorder "$@" -f "$output_file" \
-c libx264 \
-p "preset=slow" \
-p "crf=18" \
-r 60 \
-b 8000000 \
-B 192000 \
--pixel-format yuv420p \
-g 30
fi
}
record_gif() {
local output_file="$1"
shift
# Record temporary video first (MKV format for better quality)
local temp_video="/tmp/gif_recording_$(date +%s).mkv"
echo "$temp_video" >/tmp/gif_temp_video.txt
# GIF-optimized recording settings:
# - Lower framerate (15 fps) for smaller file size
# - No audio recording
# - Standard codec for compatibility
wf-recorder "$@" -f "$temp_video" \
-c libvpx-vp9 \
-r 15 \
--pixel-format yuv420p \
--no-audio
# After recording stops, convert to GIF
if [ -f "$temp_video" ]; then
notify-send "Converting to GIF" "Processing recording..."
# Create high-quality GIF with optimized palette
# Using ffmpeg with palette generation for better colors
ffmpeg -i "$temp_video" \
-vf "fps=15,scale=iw:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=128:stats_mode=diff[p];[s1][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle" \
-loop 0 \
"$output_file" 2>/tmp/gif_conversion.log
if [ $? -eq 0 ]; then
# Clean up temp file
rm -f "$temp_video"
rm -f /tmp/gif_temp_video.txt
# Copy to clipboard and send notification with actions
wl-copy <"$output_file"
send_recording_notification "$output_file"
else
error=$(cat /tmp/gif_conversion.log | tail -n 5)
notify-send "GIF Conversion Failed" "Error: $error"
rm -f "$temp_video"
rm -f /tmp/gif_temp_video.txt
fi
fi
}
# Parse command
COMMAND="$1"
TARGET="$2"
# Set up file paths
IMG="${HOME}/Pictures/Screenshots/$(date +%Y-%m-%d_%H-%m-%s).png"
VID="${HOME}/Videos/Recordings/$(date +%Y-%m-%d_%H-%m-%s).mkv"
case "$COMMAND" in
"screenshot")
case "$TARGET" in
"selection")
grim -g "$(slurp)" "$IMG"
wl-copy <"$IMG"
send_screenshot_notification "$IMG"
;;
"eDP-1")
grim -c -o eDP-1 "$IMG"
wl-copy <"$IMG"
send_screenshot_notification "$IMG"
;;
"HDMI-A-1")
grim -c -o HDMI-A-1 "$IMG"
wl-copy <"$IMG"
send_screenshot_notification "$IMG"
;;
"both")
grim -c -o eDP-1 "${IMG//.png/-eDP-1.png}"
grim -c -o HDMI-A-1 "${IMG//.png/-HDMI-A-1.png}"
montage "${IMG//.png/-eDP-1.png}" "${IMG//.png/-HDMI-A-1.png}" -tile 2x1 -geometry +0+0 "$IMG"
wl-copy <"$IMG"
rm "${IMG//.png/-eDP-1.png}" "${IMG//.png/-HDMI-A-1.png}"
send_screenshot_notification "$IMG"
;;
*)
echo "Error: Invalid screenshot target '$TARGET'"
usage
;;
esac
;;
"record")
case "$TARGET" in
"stop")
wf-recorder_check
;;
"selection")
wf-recorder_check
echo "$VID" >/tmp/recording.txt
record_video "$VID" -g "$(slurp)"
;;
"eDP-1")
wf-recorder_check
echo "$VID" >/tmp/recording.txt
record_video "$VID" -a -o eDP-1
;;
"HDMI-A-1")
wf-recorder_check
echo "$VID" >/tmp/recording.txt
record_video "$VID" -a -o HDMI-A-1
;;
*)
echo "Error: Invalid record target '$TARGET'"
usage
;;
esac
;;
"record-noaudio")
case "$TARGET" in
"stop")
wf-recorder_check
;;
"selection")
wf-recorder_check
echo "$VID" >/tmp/recording.txt
record_video_noaudio "$VID" -g "$(slurp)"
;;
"eDP-1")
wf-recorder_check
echo "$VID" >/tmp/recording.txt
record_video_noaudio "$VID" -o eDP-1
;;
"HDMI-A-1")
wf-recorder_check
echo "$VID" >/tmp/recording.txt
record_video_noaudio "$VID" -o HDMI-A-1
;;
*)
echo "Error: Invalid record-noaudio target '$TARGET'"
usage
;;
esac
;;
"record-hq")
# Change file extension to mp4 for high quality recordings
VID_HQ="${HOME}/Videos/Recordings/$(date +%Y-%m-%d_%H-%m-%s)-hq.mp4"
case "$TARGET" in
"stop")
wf-recorder_check
;;
"selection")
wf-recorder_check
echo "$VID_HQ" >/tmp/recording.txt
notify-send "High Quality Recording" "Starting YouTube-quality recording..."
record_high_quality "$VID_HQ" -g "$(slurp)"
;;
"eDP-1")
wf-recorder_check
echo "$VID_HQ" >/tmp/recording.txt
notify-send "High Quality Recording" "Starting YouTube-quality recording on eDP-1..."
record_high_quality "$VID_HQ" -a -o eDP-1
;;
"HDMI-A-1")
wf-recorder_check
echo "$VID_HQ" >/tmp/recording.txt
notify-send "High Quality Recording" "Starting YouTube-quality recording on HDMI-A-1..."
record_high_quality "$VID_HQ" -a -o HDMI-A-1
;;
*)
echo "Error: Invalid record-hq target '$TARGET'"
usage
;;
esac
;;
"record-gif")
# GIF files go to a specific location
GIF="${HOME}/Videos/Recordings/$(date +%Y-%m-%d_%H-%m-%s).gif"
case "$TARGET" in
"stop")
wf-recorder_check
;;
"selection")
wf-recorder_check
echo "$GIF" >/tmp/recording.txt
notify-send "GIF Recording" "Starting GIF recording (15 FPS)..."
record_gif "$GIF" -g "$(slurp)"
;;
"eDP-1")
wf-recorder_check
echo "$GIF" >/tmp/recording.txt
notify-send "GIF Recording" "Starting GIF recording on eDP-1..."
record_gif "$GIF" -o eDP-1
;;
"HDMI-A-1")
wf-recorder_check
echo "$GIF" >/tmp/recording.txt
notify-send "GIF Recording" "Starting GIF recording on HDMI-A-1..."
record_gif "$GIF" -o HDMI-A-1
;;
*)
echo "Error: Invalid record-gif target '$TARGET'"
usage
;;
esac
;;
"status")
# Check if wf-recorder is running
if pgrep -x "wf-recorder" >/dev/null; then
echo "true"
exit 0
else
echo "false"
exit 0
fi
;;
"convert")
case "$TARGET" in
"webm")
# Check if ffmpeg is installed
if ! command -v ffmpeg >/dev/null 2>&1; then
notify-send "Error" "ffmpeg is not installed. Please install it to use this feature."
exit 1
fi
RECORDING_DIR="${HOME}/Videos/Recordings"
CONVERTED=0
TOTAL=0
for mkv_file in "${RECORDING_DIR}"/*.mkv; do
if [ -f "$mkv_file" ]; then
TOTAL=$((TOTAL + 1))
webm_file="${mkv_file%.mkv}.webm"
# Check if webm version doesn't already exist
if [ ! -f "$webm_file" ]; then
# Simpler ffmpeg command with basic settings
ffmpeg -y -i "$mkv_file" -c:v libvpx -b:v 1M -c:a libvorbis "$webm_file" 2>/tmp/ffmpeg_error.log
if [ $? -eq 0 ]; then
CONVERTED=$((CONVERTED + 1))
notify-send "Converted to WebM" "$(basename "$mkv_file")"
else
error=$(cat /tmp/ffmpeg_error.log | tail -n 5)
notify-send "Conversion Failed" "Error: $error"
fi
fi
fi
done
if [ $TOTAL -eq 0 ]; then
notify-send "WebM Conversion" "No MKV files found in Recordings folder"
else
notify-send "WebM Conversion Complete" "Converted $CONVERTED out of $TOTAL MKV files"
fi
;;
"iphone")
# Check if ffmpeg is installed
if ! command -v ffmpeg >/dev/null 2>&1; then
notify-send "Error" "ffmpeg is not installed. Please install it to use this feature."
exit 1
fi
RECORDING_DIR="${HOME}/Videos/Recordings"
CONVERTED=0
SKIPPED_IPHONE=0
SKIPPED_EXISTING=0
TOTAL_FILES=0
for mkv_file in "${RECORDING_DIR}"/*.mkv; do
if [ -f "$mkv_file" ]; then
TOTAL_FILES=$((TOTAL_FILES + 1))
base_filename=$(basename "$mkv_file")
# Skip files with "iphone" in the filename
if [[ $base_filename == *"iphone"* ]]; then
SKIPPED_IPHONE=$((SKIPPED_IPHONE + 1))
continue
fi
iphone_file="${mkv_file%.mkv}-iphone.mp4"
# Check if iPhone version doesn't already exist
if [ ! -f "$iphone_file" ]; then
# Simpler ffmpeg command for iPhone compatibility
ffmpeg -y -i "$mkv_file" -vcodec h264 -acodec aac "$iphone_file" 2>/tmp/ffmpeg_error.log
if [ $? -eq 0 ]; then
CONVERTED=$((CONVERTED + 1))
notify-send "Converted for iPhone" "$(basename "$mkv_file")"
else
error=$(cat /tmp/ffmpeg_error.log | tail -n 5)
notify-send "Conversion Failed" "Error: $error"
fi
else
SKIPPED_EXISTING=$((SKIPPED_EXISTING + 1))
fi
fi
done
if [ $TOTAL_FILES -eq 0 ]; then
notify-send "iPhone Conversion" "No MKV files found in Recordings folder"
else
notify-send "iPhone Conversion Complete" "Converted: $CONVERTED files
Skipped (already iPhone): $SKIPPED_IPHONE files
Skipped (has iPhone version): $SKIPPED_EXISTING files
Total files checked: $TOTAL_FILES"
fi
;;
"youtube")
# Check if ffmpeg is installed
if ! command -v ffmpeg >/dev/null 2>&1; then
notify-send "Error" "ffmpeg is not installed. Please install it to use this feature."
exit 1
fi
RECORDING_DIR="${HOME}/Videos/Recordings"
CONVERTED=0
SKIPPED_YOUTUBE=0
SKIPPED_EXISTING=0
TOTAL_FILES=0
# Process both MKV and MP4 files
for video_file in "${RECORDING_DIR}"/*.{mkv,mp4}; do
if [ -f "$video_file" ]; then
TOTAL_FILES=$((TOTAL_FILES + 1))
base_filename=$(basename "$video_file")
# Skip files already marked as YouTube uploads
if [[ $base_filename == *"youtube"* ]]; then
SKIPPED_YOUTUBE=$((SKIPPED_YOUTUBE + 1))
continue
fi
# Create YouTube optimized filename
youtube_file="${video_file%.*}-youtube.mp4"
# Check if YouTube version doesn't already exist
if [ ! -f "$youtube_file" ]; then
notify-send "Converting for YouTube" "Processing: $(basename "$video_file")"
# YouTube recommended settings:
# - H.264 codec with High profile
# - 1080p or source resolution
# - 60fps or source framerate
# - High bitrate for quality (8-12 Mbps for 1080p60)
# - AAC audio at 384kbps
# - yuv420p pixel format for compatibility
# - Keyframe interval of 2 seconds (GOP)
# - No filters to preserve original colors
ffmpeg -y -i "$video_file" \
-c:v libx264 \
-profile:v high \
-preset slow \
-crf 18 \
-pix_fmt yuv420p \
-c:a aac \
-b:a 384k \
-movflags +faststart \
"$youtube_file" 2>/tmp/ffmpeg_error.log
if [ $? -eq 0 ]; then
CONVERTED=$((CONVERTED + 1))
file_size=$(du -h "$youtube_file" | cut -f1)
notify-send "YouTube Conversion Success" "$(basename "$video_file") → $(basename "$youtube_file") ($file_size)"
else
error=$(cat /tmp/ffmpeg_error.log | tail -n 5)
notify-send "YouTube Conversion Failed" "Error converting $(basename "$video_file"): $error"
fi
else
SKIPPED_EXISTING=$((SKIPPED_EXISTING + 1))
fi
fi
done
if [ $TOTAL_FILES -eq 0 ]; then
notify-send "YouTube Conversion" "No video files found in Recordings folder"
else
notify-send "YouTube Conversion Complete" "Converted: $CONVERTED files
Skipped (already YouTube): $SKIPPED_YOUTUBE files
Skipped (has YouTube version): $SKIPPED_EXISTING files
Total files checked: $TOTAL_FILES"
fi
;;
"gif")
# Check if ffmpeg is installed
if ! command -v ffmpeg >/dev/null 2>&1; then
notify-send "Error" "ffmpeg is not installed. Please install it to use this feature."
exit 1
fi
RECORDING_DIR="${HOME}/Videos/Recordings"
CONVERTED=0
SKIPPED_GIF=0
SKIPPED_EXISTING=0
TOTAL_FILES=0
# Process both MKV and MP4 files
for video_file in "${RECORDING_DIR}"/*.{mkv,mp4}; do
if [ -f "$video_file" ]; then
TOTAL_FILES=$((TOTAL_FILES + 1))
base_filename=$(basename "$video_file")
# Skip files already GIFs
if [[ $base_filename == *.gif ]]; then
SKIPPED_GIF=$((SKIPPED_GIF + 1))
continue
fi
# Create GIF filename
gif_file="${video_file%.*}.gif"
# Check if GIF version doesn't already exist
if [ ! -f "$gif_file" ]; then
notify-send "Converting to GIF" "Processing: $(basename "$video_file")"
# Get video dimensions for scaling
width=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=s=x:p=0 "$video_file")
# Scale down if wider than 800px to keep file size reasonable
if [ "$width" -gt 800 ]; then
scale_filter="scale=800:-1:flags=lanczos,"
else
scale_filter=""
fi
# Create high-quality GIF with optimized palette
ffmpeg -i "$video_file" \
-vf "${scale_filter}fps=15,split[s0][s1];[s0]palettegen=max_colors=256:stats_mode=diff[p];[s1][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle" \
-loop 0 \
"$gif_file" 2>/tmp/ffmpeg_error.log
if [ $? -eq 0 ]; then
CONVERTED=$((CONVERTED + 1))
send_recording_notification "$gif_file"
else
error=$(cat /tmp/ffmpeg_error.log | tail -n 5)
notify-send "GIF Conversion Failed" "Error converting $(basename "$video_file"): $error"
fi
else
SKIPPED_EXISTING=$((SKIPPED_EXISTING + 1))
fi
fi
done
if [ $TOTAL_FILES -eq 0 ]; then
notify-send "GIF Conversion" "No video files found in Recordings folder"
else
notify-send "GIF Conversion Complete" "Converted: $CONVERTED files
Skipped (already GIF): $SKIPPED_GIF files
Skipped (has GIF version): $SKIPPED_EXISTING files
Total files checked: $TOTAL_FILES"
fi
;;
*)
echo "Error: Invalid convert format '$TARGET'"
usage
;;
esac
;;
*)
echo "Error: Invalid command '$COMMAND'"
usage
;;
esac
-157
View File
@@ -1,157 +0,0 @@
import datetime
import os
from fabric.core.service import Property, Service, Signal
from fabric.utils import exec_shell_command, exec_shell_command_async
from gi.repository import Gio, GLib
from loguru import logger
class ScreenCapture(Service):
@Signal
def recording(self, value: bool) -> None: ...
def __init__(self, **kwargs):
self.screenshot_path = GLib.get_home_dir() + "/Pictures/Screenshots/"
self.screenrecord_path = GLib.get_home_dir() + "/Videos/Screencasting/"
os.makedirs(self.screenshot_path, exist_ok=True)
os.makedirs(self.screenrecord_path, exist_ok=True)
super().__init__(**kwargs)
def screenshot(self, fullscreen=False, save_copy=True):
time = datetime.datetime.today().strftime("%Y-%m-%d_%H-%M-%S")
file_path = self.screenshot_path + str(time) + ".png"
# hyprshot: --mode [window|output|region], --stdout, --clipboard, --save-path
if save_copy:
command = [
"hyprshot",
"--mode",
"output" if fullscreen else "region",
"--clipboard",
"--save-path",
file_path,
]
else:
command = [
"hyprshot",
"--mode",
"output" if fullscreen else "region",
"--clipboard"
]
try:
exec_shell_command_async(
" ".join(command),
lambda *_: self.send_screenshot_notification(
file_path=file_path if save_copy else None,
),
)
except Exception as e:
logger.error(f"[SCREENSHOT] Failed to run command: {command}")
def screencast_start(self, fullscreen=False, audio=False):
if self.is_recording:
logger.error(
"[SCREENRECORD] Another instance of wf-recorder is already running"
)
return
time = datetime.datetime.today().strftime("%Y-%m-%d_%H-%M-%S")
file_path = self.screenrecord_path + str(time) + ".mp4"
self._current_screencast_path = file_path
area = "" if fullscreen else f"-g '{exec_shell_command('slurp')}'"
audio_flag = "--audio --no-mic" if audio else ""
command = f"wf-recorder --file={file_path} --pixel-format yuv420p {audio_flag} {area}"
exec_shell_command_async(command)
self.emit("recording", True)
def screencast_stop(self):
exec_shell_command_async("killall -INT wf-recorder")
self.emit("recording", False)
self.send_screencast_notification(self._current_screencast_path)
def send_screencast_notification(self, file_path):
# TODO: Generate a thumbnail
# exec_shell_command_async(f"totem-video-thumbnailer {file_path} {FABRIC_CACHE}")
cmd = ["notify-send"]
cmd.extend(
[
"-A",
"files=Show in Files",
"-A",
"view=View",
"-i",
"camera-video-symbolic",
"-a",
"Fabric Screenshot Utility",
"Screencast Saved",
f"Saved Screencast at {file_path}",
]
)
proc: Gio.Subprocess = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.STDOUT_PIPE)
def do_callback(process: Gio.Subprocess, task: Gio.Task):
try:
_, stdout, stderr = process.communicate_utf8_finish(task)
except Exception:
logger.error(
f"[SCREENCAST] Failed read notification action with error {stderr}"
)
return
match stdout.strip("\n"):
case "files":
exec_shell_command_async(f"xdg-open {self.screenrecord_path}")
case "view":
exec_shell_command_async(f"xdg-open {file_path}")
proc.communicate_utf8_async(None, None, do_callback)
def send_screenshot_notification(self, file_path=None):
cmd = ["notify-send"]
cmd.extend(
[
"-A",
"files=Show in Files",
"-A",
"view=View",
"-A",
"edit=Edit",
"-i",
"camera-photo-symbolic",
"-a",
"Fabric Screenshot Utility",
"-h",
f"STRING:image-path:{file_path}",
"Screenshot Saved",
f"Saved Screenshot at {file_path}",
]
if file_path
else ["Screenshot Sent to Clipboard"]
)
proc: Gio.Subprocess = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.STDOUT_PIPE)
def do_callback(process: Gio.Subprocess, task: Gio.Task):
try:
_, stdout, stderr = process.communicate_utf8_finish(task)
except Exception:
logger.error(
f"[SCREENSHOT] Failed read notification action with error {stderr}"
)
return
match stdout.strip("\n"):
case "files":
exec_shell_command_async(f"xdg-open {self.screenshot_path}")
case "view":
exec_shell_command_async(f"xdg-open {file_path}")
case "edit":
exec_shell_command_async(f"swappy -f {file_path}")
proc.communicate_utf8_async(None, None, do_callback)
@Property(bool, "readable", default_value=False)
def is_recording(self):
return False if len(exec_shell_command("pidof wf-recorder")) == 0 else True