* Keep clicking a notification working after a shell restart Notification actions lived only in the sending process: `-a` appended `-A default=default`, so notify-send blocked on a D-Bus ActionInvoked signal and the caller ran the command when it arrived. Nothing about that reached disk, so a restored popup had no action to run and its sender stayed blocked forever. Replace `-a` with `--exec <command>`, carried as an `omarchy-exec` hint into the snapshot's `exec` role. It travels through the popup files and history, and the shell runs it on click, so restored toasts behave exactly like live ones and the sender exits immediately. That drops the scaffolding whose only job was keeping a blocked sender alive: the first-run invitations lose their `--show` re-entry and two transient units each, omarchy-migrate-notify loses its transient service, and the screenshot, recording, download, and taildrop toasts lose their wrapper subshells. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Keep a failed toast from failing the work it announces Moving these sends out of their backgrounded subshells put a fallible command on the foreground path, where the `&` used to swallow its exit status. A notification outage — including the shell restart this branch targets — now propagates: - taildrop's receiver dies under `set -e` mid-delivery - omarchy-capture-screenshot reports failure for a screenshot it already saved - a completed download exits before scheduling its thumbnail cleanup, leaking the mktemp file Announcing is best-effort in all three: the work is already done by the time the toast goes out. Also drop the first-run sleep that spaced out the welcome and Wi-Fi toasts. It compensated for the background notify-send processes this branch removes; each send now returns only once the server has taken the toast, so sending in order is enough to stack them newest-on-top. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Stop tying the preview cleanup to the toast's expiry The shell loads a notification thumbnail into memory when the toast appears and never re-reads the file, so the preview only has to outlive that load. Deriving the cleanup delay from the expiry was false precision, and it turned -t into a variable for no reason: -t is already the helper's expiry setting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
143 lines
4.7 KiB
Bash
Executable File
143 lines
4.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Native messaging host: download the URL sent by the yt-dlp Chromium extension
|
|
# omarchy:hidden=true
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_PATH="${BASH_SOURCE[0]}"
|
|
|
|
# The browser launches us without Omarchy's environment, so locate the repo from
|
|
# our own path when OMARCHY_PATH isn't already set. Export it — omarchy-shell (used
|
|
# by omarchy-osd) needs it to find the running shell, and silently no-ops without it.
|
|
export OMARCHY_PATH="${OMARCHY_PATH:-$(cd -- "$(dirname -- "$SCRIPT_PATH")/.." && pwd)}"
|
|
|
|
# Make sure the Omarchy bin and yt-dlp are reachable when launched by the browser.
|
|
export PATH="$OMARCHY_PATH/bin:/usr/local/bin:/usr/bin:$PATH"
|
|
|
|
DOWNLOAD_DIR="${OMARCHY_YTDLP_DIR:-$HOME/Videos}"
|
|
|
|
parse_url() {
|
|
jq -r '.url // empty' 2>/dev/null <<<"$1" || true
|
|
}
|
|
|
|
valid_url() {
|
|
[[ $1 =~ ^https?:// ]]
|
|
}
|
|
|
|
# Drive the Quickshell OSD — a single overlay that updates in place (like the
|
|
# volume/brightness bar), so download progress never stacks like notifications.
|
|
osd_progress() {
|
|
omarchy-osd -i -p "$1" -d 8000 >/dev/null 2>&1 || true
|
|
}
|
|
|
|
osd_close() {
|
|
omarchy-shell -q osd close >/dev/null 2>&1 || true
|
|
}
|
|
|
|
download_url() {
|
|
local url="$1"
|
|
|
|
mkdir -p "$DOWNLOAD_DIR"
|
|
|
|
# Don't show anything until yt-dlp confirms there's actually a video to grab.
|
|
if ! yt-dlp --no-playlist --simulate --quiet --no-warnings "$url" >/dev/null 2>&1; then
|
|
omarchy-notification-send -u critical -g "No video found for download" "$url"
|
|
exit 0
|
|
fi
|
|
|
|
osd_progress 0
|
|
|
|
# Stream the download: OMARCHY_PROG carries the percent (drives the OSD),
|
|
# OMARCHY_FILE (printed only after a successful move) carries title + path.
|
|
local line rest pct intpct last="" er nowms lastms=0 title="" filepath=""
|
|
while IFS= read -r line; do
|
|
case $line in
|
|
OMARCHY_PROG*)
|
|
pct=${line#OMARCHY_PROG$'\t'}
|
|
intpct=${pct%%.*}
|
|
intpct=${intpct//[^0-9]/}
|
|
[[ -n $intpct && $intpct != "$last" ]] || continue # skip no-op repeats
|
|
# Throttle to ~4 redraws/sec so fast downloads don't spawn a flurry of processes.
|
|
er=$EPOCHREALTIME
|
|
nowms=$((${er%[.,]*} * 1000 + 10#${er##*[.,]} / 1000))
|
|
((nowms - lastms >= 250)) || continue
|
|
last=$intpct
|
|
lastms=$nowms
|
|
osd_progress "$intpct"
|
|
;;
|
|
OMARCHY_FILE*)
|
|
rest=${line#OMARCHY_FILE$'\t'}
|
|
title=${rest%%$'\t'*}
|
|
filepath=${rest#*$'\t'}
|
|
;;
|
|
esac
|
|
done < <(PYTHONUNBUFFERED=1 yt-dlp --no-playlist --restrict-filenames --no-simulate \
|
|
--quiet --no-warnings --progress --newline \
|
|
--progress-template $'download:OMARCHY_PROG\t%(progress._percent_str)s' \
|
|
--paths "$DOWNLOAD_DIR" -o '%(title)s [%(id)s].%(ext)s' \
|
|
--print $'after_move:OMARCHY_FILE\t%(title)s\t%(filepath)s' \
|
|
"$url" 2>&1)
|
|
|
|
osd_close
|
|
|
|
# after_move only prints on a successful download+move, so a captured path == success.
|
|
if [[ -n $filepath ]]; then
|
|
((${#title} > 50)) && title="${title:0:50}…" # keep the toast compact
|
|
|
|
# Square, center-cropped thumbnail so the notification preview isn't stretched.
|
|
local preview
|
|
preview="$(mktemp --suffix=.jpg)"
|
|
ffmpeg -y -i "$filepath" -ss 00:00:00.1 -vframes 1 \
|
|
-vf "crop='min(iw,ih)':'min(iw,ih)',scale=256:256" -q:v 2 \
|
|
"$preview" -loglevel quiet 2>/dev/null || true
|
|
|
|
# Best-effort: the download already succeeded, and under `set -e` a failed
|
|
# toast would exit before the thumbnail cleanup below is ever scheduled.
|
|
omarchy-notification-send -g "Download complete" "$title" \
|
|
-t 10000 --image "${preview:-$filepath}" \
|
|
--exec "$(printf 'mpv %q' "$filepath")" || true
|
|
|
|
# The shell loads the thumbnail into memory when the toast appears and never
|
|
# re-reads the file, so the preview only has to outlive that load, not the
|
|
# toast.
|
|
(
|
|
sleep 2
|
|
rm -f "$preview"
|
|
) &
|
|
else
|
|
omarchy-notification-send -u critical -g "Download failed" "$url"
|
|
fi
|
|
|
|
exit 0
|
|
}
|
|
|
|
main() {
|
|
local length payload url
|
|
|
|
# Detached worker: this is what actually runs yt-dlp and fires notifications.
|
|
if [[ "${1:-}" == "--download" ]]; then
|
|
download_url "$2"
|
|
fi
|
|
|
|
# Native messaging frame: 4-byte little-endian length prefix, then UTF-8 JSON.
|
|
length=$(head -c4 | od -An -v -tu4 --endian=little | tr -d ' ')
|
|
[[ -n ${length:-} ]] && ((length > 0)) || exit 0
|
|
|
|
payload=$(head -c "$length")
|
|
|
|
# Ack with an empty message so the extension's sendNativeMessage callback resolves cleanly.
|
|
printf '\x02\x00\x00\x00{}'
|
|
|
|
url=$(parse_url "$payload")
|
|
[[ -n $url ]] || exit 0
|
|
valid_url "$url" || exit 0
|
|
|
|
# Detach the download so this host exits promptly and frees the browser's port.
|
|
setsid -f "$SCRIPT_PATH" --download "$url" </dev/null >/dev/null 2>&1
|
|
}
|
|
|
|
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
|
|
main "$@"
|
|
fi
|