Add yt-dlp "Download Video" Chromium extension
Download the current page's video with yt-dlp via Alt+Shift+D or a click on the toolbar icon. A native-messaging host runs the download, shows live progress on the Quickshell OSD, and posts a clickable "Download complete" toast that opens the file in mpv. - Extension: pinned key for a stable id, green download-video icon, keyboard command + toolbar action (reads the active tab URL). - Native host (omarchy-chromium-ytdlp-host): verifies the URL with yt-dlp --simulate (else "No video found"), streams progress to the OSD (time-throttled to ~4/s), saves to ~/Videos, opens mpv on click. - Installer (omarchy-install-chromium-ytdlp) writes the native-messaging manifest into installed Chromium/Chrome/Brave/Edge profiles; wired into browser install and chromium refresh, with a migration for existing users. - omarchy-osd: add -d/--duration so the OSD can persist during a download. - Add yt-dlp to base packages, load the extension via --load-extension, and document the Alt+Shift+D binding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
1d6467c91c
commit
e5290b0a12
Executable
+136
@@ -0,0 +1,136 @@
|
||||
#!/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
|
||||
|
||||
# Clicking the toast opens the video in mpv (-a blocks until clicked or timed out).
|
||||
(
|
||||
if [[ -n $(omarchy-notification-send -g "Download complete" "$title" -t 10000 --image "${preview:-$filepath}" -a) ]]; then
|
||||
mpv "$filepath"
|
||||
fi
|
||||
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
|
||||
@@ -17,6 +17,7 @@ announce_browser_installed() {
|
||||
copy_chromium_flags() {
|
||||
mkdir -p ~/.config
|
||||
cp -f "$OMARCHY_PATH/config/chromium-flags.conf" "$1"
|
||||
omarchy-install-chromium-ytdlp
|
||||
}
|
||||
|
||||
setup_firefox_preferences() {
|
||||
@@ -66,7 +67,8 @@ brave-origin)
|
||||
setup_policy_directory /etc/brave/policies/managed
|
||||
mkdir -p ~/.config
|
||||
# FIXME: Use normal chromium flags when Brave Origin wrapper has been fixed
|
||||
echo "--load-extension=~/.local/share/omarchy/default/chromium/extensions/copy-url" > ~/.config/brave-origin-beta-flags.conf
|
||||
echo "--load-extension=~/.local/share/omarchy/default/chromium/extensions/copy-url,~/.local/share/omarchy/default/chromium/extensions/yt-dlp" > ~/.config/brave-origin-beta-flags.conf
|
||||
omarchy-install-chromium-ytdlp
|
||||
omarchy-theme-set-browser
|
||||
announce_browser_installed "Brave Origin"
|
||||
;;
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Install the native messaging host for the yt-dlp Chromium extension
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
HOST_NAME="com.omarchy.ytdlp"
|
||||
HOST_PATH="$OMARCHY_PATH/bin/omarchy-chromium-ytdlp-host"
|
||||
TEMPLATE="$OMARCHY_PATH/default/chromium/native-messaging-hosts/$HOST_NAME.json"
|
||||
|
||||
# Chromium-based browser profile roots that use the NativeMessagingHosts layout.
|
||||
browser_dirs=(
|
||||
"$HOME/.config/chromium"
|
||||
"$HOME/.config/google-chrome"
|
||||
"$HOME/.config/google-chrome-beta"
|
||||
"$HOME/.config/google-chrome-unstable"
|
||||
"$HOME/.config/BraveSoftware/Brave-Browser"
|
||||
"$HOME/.config/BraveSoftware/Brave-Browser-Beta"
|
||||
"$HOME/.config/BraveSoftware/Brave-Browser-Nightly"
|
||||
"$HOME/.config/microsoft-edge"
|
||||
"$HOME/.config/microsoft-edge-dev"
|
||||
)
|
||||
|
||||
manifest=$(sed "s|__HOST_PATH__|$HOST_PATH|g" "$TEMPLATE")
|
||||
|
||||
for dir in "${browser_dirs[@]}"; do
|
||||
mkdir -p "$dir/NativeMessagingHosts"
|
||||
printf '%s\n' "$manifest" >"$dir/NativeMessagingHosts/$HOST_NAME.json"
|
||||
done
|
||||
@@ -379,6 +379,7 @@ dynamic_bindings() {
|
||||
# Hardcoded bindings, like the copy-url extension and such
|
||||
static_bindings() {
|
||||
echo "SHIFT ALT,L,Copy URL from Web App,sendshortcut,SHIFT ALT,L,"
|
||||
echo "SHIFT ALT,D,Download Video from Web App,sendshortcut,SHIFT ALT,D,"
|
||||
}
|
||||
|
||||
# Parse and format keybindings
|
||||
|
||||
+5
-2
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Show the Omarchy Quickshell on-screen display
|
||||
# omarchy:args=[-i|--icon <icon>] [-m|--message <text>] [-p|--progress <0-100>]
|
||||
# omarchy:args=[-i|--icon <icon>] [-m|--message <text>] [-p|--progress <0-100>] [-d|--duration <ms>]
|
||||
# omarchy:examples=omarchy osd -i brightness -p 50 | omarchy osd -m "Hello"
|
||||
|
||||
set -euo pipefail
|
||||
@@ -11,12 +11,14 @@ message=""
|
||||
progress=""
|
||||
progress_text=""
|
||||
max="100"
|
||||
duration=""
|
||||
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
-i|--icon) icon="${2:-}"; shift 2 ;;
|
||||
-m|--message) message="${2:-}"; shift 2 ;;
|
||||
-p|--progress) progress="${2:-}"; shift 2 ;;
|
||||
-d|--duration) duration="${2:-}"; shift 2 ;;
|
||||
-h|--help) omarchy osd --help; exit 0 ;;
|
||||
*) echo "Unknown OSD option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
@@ -32,6 +34,7 @@ payload=$(jq -cn \
|
||||
--arg value "$progress" \
|
||||
--arg progressText "$progress_text" \
|
||||
--arg max "$max" \
|
||||
'{icon:$icon,message:$message,value:$value,progressText:$progressText,max:$max}')
|
||||
--arg duration "$duration" \
|
||||
'{icon:$icon,message:$message,value:$value,progressText:$progressText,max:$max,duration:$duration}')
|
||||
|
||||
omarchy-shell -q osd show "$payload"
|
||||
|
||||
@@ -15,6 +15,9 @@ fi
|
||||
# Refresh the Chromium configuration
|
||||
omarchy-refresh-config chromium-flags.conf
|
||||
|
||||
# Install/refresh the native messaging host used by the yt-dlp extension
|
||||
omarchy-install-chromium-ytdlp
|
||||
|
||||
# Re-install Google accounts if previously configured
|
||||
if [[ $INSTALL_GOOGLE_ACCOUNTS == "true" ]]; then
|
||||
omarchy-install-chromium-google-account
|
||||
|
||||
Reference in New Issue
Block a user