diff --git a/bin/omarchy b/bin/omarchy index bca7a306..77b75fd7 100755 --- a/bin/omarchy +++ b/bin/omarchy @@ -63,6 +63,7 @@ GROUP_DESCRIPTIONS[swayosd]="SwayOSD status display helpers" GROUP_DESCRIPTIONS[system]="Reboot, shutdown, logout, and lock" GROUP_DESCRIPTIONS[theme]="Theme management" GROUP_DESCRIPTIONS[toggle]="Toggle Omarchy features" +GROUP_DESCRIPTIONS[transcode]="Image and video transcoding" GROUP_DESCRIPTIONS[tui]="Terminal UI launchers" GROUP_DESCRIPTIONS[tz]="Timezone selection" GROUP_DESCRIPTIONS[update]="Omarchy and system updates" diff --git a/bin/omarchy-menu b/bin/omarchy-menu index 4134265b..1ffa081c 100755 --- a/bin/omarchy-menu +++ b/bin/omarchy-menu @@ -93,8 +93,9 @@ show_learn_menu() { } show_trigger_menu() { - case $(menu "Trigger" " Capture\n Share\n󰔎 Toggle\n Hardware") in + case $(menu "Trigger" " Capture\n󰧸 Transcode\n Share\n󰔎 Toggle\n Hardware") in *Capture*) show_capture_menu ;; + *Transcode*) show_transcode_menu ;; *Share*) show_share_menu ;; *Toggle*) show_toggle_menu ;; *Hardware*) show_hardware_menu ;; @@ -166,6 +167,14 @@ show_share_menu() { esac } +show_transcode_menu() { + case $(menu "Transcode" " Picture\n Video") in + *Picture*) present_terminal "omarchy-transcode --path '$HOME/Pictures'" ;; + *Video*) present_terminal "omarchy-transcode --path '$HOME/Videos'" ;; + *) back_to show_trigger_menu ;; + esac +} + show_toggle_menu() { local options="󱄄 Screensaver\n󰔎 Nightlight\n󱫖 Idle Lock\n󰂛 Notifications\n󰍜 Top Bar\n󱂬 Workspace Layout\n Window Gaps\n 1-Window Ratio\n󰍹 Monitor Scaling\n Direct Boot\n󰟵 Passwordless Sudo" @@ -672,6 +681,7 @@ go_to_menu() { *toggle*) show_toggle_menu ;; *hardware*) show_hardware_menu ;; *share*) show_share_menu ;; + *transcode*) show_transcode_menu ;; *background*) show_background_menu ;; *capture*) show_capture_menu ;; *style*) show_style_menu ;; diff --git a/bin/omarchy-transcode b/bin/omarchy-transcode new file mode 100755 index 00000000..0431710e --- /dev/null +++ b/bin/omarchy-transcode @@ -0,0 +1,231 @@ +#!/bin/bash + +# omarchy:group=transcode +# omarchy:name= +# omarchy:summary=Transcode pictures and videos for sharing +# omarchy:args=[--path path] [input] [format] [resolution] +# omarchy:examples=omarchy transcode|omarchy transcode --path ~/Downloads|omarchy transcode ~/Videos/demo.mov mp4 1080p|omarchy transcode ~/Pictures/wallpaper.heic jpg medium + +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: + omarchy transcode [--path path] [input] [format] [resolution] + +With no input, fuzzy-pick a picture or video from ~/Pictures and ~/Videos, +or only from --path when provided. Then pick the output format and resolution. + +Options: + --path path Limit interactive fuzzy file selection to this path + +Formats: + Pictures: jpg, png + Videos: mp4, gif + +Resolutions: + Pictures: high, medium, low + Videos: 4k, 1080p, 720p +EOF +} + +pick_file() { + local search_path="$1" + local paths=() + + if [[ -n $search_path ]]; then + [[ -e $search_path ]] || { echo "Path not found: $search_path" >&2; return 1; } + paths=("$search_path") + else + paths=("$HOME/Pictures" "$HOME/Videos") + fi + + find "${paths[@]}" -type f \ + \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' -o -iname '*.gif' -o -iname '*.heic' -o -iname '*.avif' \ + -o -iname '*.mp4' -o -iname '*.mov' -o -iname '*.m4v' -o -iname '*.mkv' -o -iname '*.webm' -o -iname '*.avi' \) \ + 2>/dev/null | fzf --prompt="Transcode file> " +} + +pick_option() { + local prompt="$1" + shift + + gum choose --header "$prompt" "$@" +} + +media_type() { + local mime + mime=$(file -b --mime-type "$1") + + case "$mime" in + image/*) echo picture ;; + video/*) echo video ;; + *) + echo "Unsupported file type: $mime" >&2 + return 1 + ;; + esac +} + +output_path() { + local input="$1" + local format="$2" + local resolution="$3" + local dir base stem + + dir=$(dirname -- "$input") + base=$(basename -- "$input") + stem="${base%.*}" + + printf '%s/%s-%s.%s' "$dir" "$stem" "$resolution" "$format" +} + +transcode_picture() { + local input="$1" format="$2" resolution="$3" output="$4" + local resize + + case "$resolution" in + high) resize='3160x>' ;; + medium) resize='2160x>' ;; + low) resize='1080x>' ;; + *) + echo "Invalid picture resolution: $resolution" >&2 + return 1 + ;; + esac + + case "$format" in + jpg) + magick "$input" -resize "$resize" -quality 85 -strip "$output" + ;; + png) + magick "$input" -resize "$resize" -strip -define png:compression-filter=5 \ + -define png:compression-level=9 \ + -define png:compression-strategy=1 \ + -define png:exclude-chunk=all \ + "$output" + ;; + *) + echo "Invalid picture format: $format" >&2 + return 1 + ;; + esac +} + +transcode_video() { + local input="$1" format="$2" resolution="$3" output="$4" + local scale + + case "$resolution" in + 4k) scale='scale=-2:2160' ;; + 1080p) scale='scale=-2:1080' ;; + 720p) scale='scale=-2:720' ;; + *) + echo "Invalid video resolution: $resolution" >&2 + return 1 + ;; + esac + + case "$format" in + mp4) + if [[ $resolution == "4k" ]]; then + ffmpeg -i "$input" -vf "$scale" -c:v libx265 -preset slow -crf 24 -c:a aac -b:a 192k "$output" + else + ffmpeg -i "$input" -vf "$scale" -c:v libx264 -preset fast -crf 23 -c:a copy "$output" + fi + ;; + gif) + ffmpeg -i "$input" -vf "fps=10,$scale:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" "$output" + ;; + *) + echo "Invalid video format: $format" >&2 + return 1 + ;; + esac +} + +copy_to_clipboard() { + local output="$1" + local uri + + uri="file://$(realpath -- "$output")" + printf '%s\n' "$uri" | wl-copy --type text/uri-list +} + +main() { + local input="" format="" resolution="" search_path="" + local type output positional=() + + while (( $# > 0 )); do + case "$1" in + --path) + shift + [[ $# -gt 0 ]] || { echo "Missing value for --path" >&2; return 2; } + search_path="$1" + ;; + --help | -h) + usage + return 0 + ;; + --) + shift + positional+=("$@") + break + ;; + --*) + echo "Unknown option: $1" >&2 + usage >&2 + return 2 + ;; + *) + positional+=("$1") + ;; + esac + shift + done + + input="${positional[0]:-}" + format="${positional[1]:-}" + resolution="${positional[2]:-}" + + if [[ -z $input ]]; then + input=$(pick_file "$search_path") + fi + + [[ -n $input ]] || return 1 + [[ -f $input ]] || { echo "File not found: $input" >&2; return 1; } + + type=$(media_type "$input") + + if [[ -z $format ]]; then + if [[ $type == "picture" ]]; then + format=$(pick_option "Select format..." jpg png) + else + format=$(pick_option "Select format..." mp4 gif) + fi + fi + + if [[ -z $resolution ]]; then + if [[ $type == "picture" ]]; then + resolution=$(pick_option "Select resolution..." high medium low) + else + resolution=$(pick_option "Select resolution..." 4k 1080p 720p) + fi + fi + + output=$(output_path "$input" "$format" "$resolution") + + if [[ $type == "picture" ]]; then + transcode_picture "$input" "$format" "$resolution" "$output" + else + transcode_video "$input" "$format" "$resolution" "$output" + echo "" + fi + + copy_to_clipboard "$output" + echo "Your $type has been transcoded into $format as $resolution. It's on your clipboard." + echo "" + echo "It's also in $output" +} + +main "$@" diff --git a/default/bash/fns/transcoding b/default/bash/fns/transcoding index 81ad0016..017ddb66 100644 --- a/default/bash/fns/transcoding +++ b/default/bash/fns/transcoding @@ -1,58 +1,33 @@ -# Transcode a video to a good-balance 1080p that's great for sharing online +# Transcoding helpers have moved to omarchy-transcode. + transcode-video-1080p() { - ffmpeg -i "$1" -vf scale=1920:1080 -c:v libx264 -preset fast -crf 23 -c:a copy "${1%.*}-1080p.mp4" + omarchy-transcode "$1" mp4 1080p } -# Transcode a video to a good-balance 4K that's great for sharing online transcode-video-4K() { - ffmpeg -i "$1" -c:v libx265 -preset slow -crf 24 -c:a aac -b:a 192k "${1%.*}-optimized.mp4" + omarchy-transcode "$1" mp4 4k } -# Transcode a video to an animated GIF using a palette for accurate colors transcode-video-gif() { - ffmpeg -i "$1" -vf "fps=10,scale=800:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" "${1%.*}.gif" + omarchy-transcode "$1" gif 1080p } -# Transcode any image to JPG image that's great for shrinking wallpapers img2jpg() { - img="$1" - shift - - magick "$img" "$@" -quality 85 -strip "${img%.*}-converted.jpg" + omarchy-transcode "$1" jpg high } -# Transcode any image to a small JPG (max 1080px wide) img2jpg-small() { - img="$1" - shift - - magick "$img" "$@" -resize 1080x\> -quality 85 -strip "${img%.*}-small.jpg" + omarchy-transcode "$1" jpg low } -# Transcode any image to a 4K JPG (max 2160px wide) img2jpg-medium() { - img="$1" - shift - - magick "$img" "$@" -resize 2160x\> -quality 85 -strip "${img%.*}-medium.jpg" + omarchy-transcode "$1" jpg medium } -# Transcode any image to a 6K JPG (max 3160px wide) img2jpg-large() { - img="$1" - shift - - magick "$img" "$@" -resize 3160x\> -quality 85 -strip "${img%.*}-large.jpg" + omarchy-transcode "$1" jpg high } -# Transcode any image to compressed-but-lossless PNG img2png() { - img="$1" - shift - - magick "$img" "$@" -strip -define png:compression-filter=5 \ - -define png:compression-level=9 \ - -define png:compression-strategy=1 \ - -define png:exclude-chunk=all \ - "${img%.*}-optimized.png" + omarchy-transcode "$1" png high } diff --git a/default/hypr/bindings/utilities.conf b/default/hypr/bindings/utilities.conf index 2b871c92..32dc0170 100644 --- a/default/hypr/bindings/utilities.conf +++ b/default/hypr/bindings/utilities.conf @@ -43,6 +43,9 @@ bindd = SUPER CTRL, PRINT, Extract text (OCR) from screenshot, exec, omarchy-cap # File sharing bindd = SUPER CTRL, S, Share, exec, omarchy-menu share +# Transcoding +bindd = SUPER CTRL, R, Transcode, exec, omarchy-menu transcode + # Waybar-less information bindd = SUPER CTRL ALT, T, Show time, exec, notify-send -u low " $(date +"%A %H:%M · %d %B %Y · Week %V")" bindd = SUPER CTRL ALT, B, Show battery remaining, exec, notify-send -u low "$(omarchy-battery-status)"