Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 95 additions & 5 deletions bin/omarchy-transcode
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Usage:

With no input, pick a picture or video with Walker from ~/Pictures and ~/Videos,
or only from --path when provided. Then pick the output format and resolution.
When input is a directory, transcode matching direct child files only.

Options:
--path path Limit interactive fuzzy file selection to this path
Expand Down Expand Up @@ -43,6 +44,17 @@ media_type() {
esac
}

media_type_for_format() {
case "$1" in
jpg | png) echo picture ;;
mp4 | gif) echo video ;;
*)
echo "Invalid format: $1" >&2
return 1
;;
esac
}

output_path() {
local input="$1"
local format="$2"
Expand Down Expand Up @@ -120,6 +132,67 @@ transcode_video() {
esac
}

transcode_file() {
local input="$1" format="$2" resolution="$3"
local type output

type=$(media_type "$input")
output=$(output_path "$input" "$format" "$resolution")

if [[ $type == "video" ]]; then
transcode_video "$input" "$format" "$resolution" "$output"
else
transcode_picture "$input" "$format" "$resolution" "$output"
fi

printf '%s\n' "$output"
}

transcode_directory() {
local input="$1" format="$2" resolution="$3"
local target_type path type output count=0 skipped=0

target_type=$(media_type_for_format "$format")

shopt -s nullglob
for path in "$input"/*; do
[[ -f $path ]] || continue

if ! type=$(media_type "$path" 2>/dev/null); then
(( skipped += 1 ))
continue
fi

if [[ $type != "$target_type" ]]; then
(( skipped += 1 ))
continue
fi

output=$(output_path "$path" "$format" "$resolution")
if [[ $type == "video" ]]; then
omarchy-notification-send -g  "Transcoding video…" "$(basename -- "$path") to $format ($resolution)"
transcode_video "$path" "$format" "$resolution" "$output"
else
transcode_picture "$path" "$format" "$resolution" "$output"
fi

(( count += 1 ))
printf 'Transcoded %s -> %s\n' "$path" "$output"
done
shopt -u nullglob

if (( count == 0 )); then
echo "No supported $target_type files found in: $input" >&2
return 1
fi

omarchy-notification-send "Transcoded $count item(s) to $resolution $format" "Saved in $(basename -- "$input")"

if (( skipped > 0 )); then
printf 'Skipped %d unsupported or mismatched item(s).\n' "$skipped"
fi
}

copy_to_clipboard() {
local output="$1"
local uri
Expand Down Expand Up @@ -170,7 +243,26 @@ main() {
fi

[[ -n $input ]] || return 1
[[ -f $input ]] || { echo "File not found: $input" >&2; return 1; }
[[ -f $input || -d $input ]] || { echo "File or directory not found: $input" >&2; return 1; }

if [[ -d $input ]]; then
if [[ -z $format ]]; then
format=$(omarchy-menu-select "Select format" jpg png mp4 gif)
fi

type=$(media_type_for_format "$format")

if [[ -z $resolution ]]; then
if [[ $type == "picture" ]]; then
resolution=$(omarchy-menu-select "Select resolution" high medium low)
else
resolution=$(omarchy-menu-select "Select resolution" 4k 1080p 720p)
fi
fi

transcode_directory "$input" "$format" "$resolution"
return
fi

type=$(media_type "$input")

Expand All @@ -190,15 +282,13 @@ main() {
fi
fi

output=$(output_path "$input" "$format" "$resolution")

if [[ $type == "video" ]]; then
omarchy-notification-send -g  "Transcoding video…" "$(basename -- "$input") to $format ($resolution)"
transcode_video "$input" "$format" "$resolution" "$output"
output=$(transcode_file "$input" "$format" "$resolution")
copy_to_clipboard "$output"
omarchy-notification-send -g  "Transcoded to $resolution $format" "Saved and copied to clipboard."
else
transcode_picture "$input" "$format" "$resolution" "$output"
output=$(transcode_file "$input" "$format" "$resolution")
copy_to_clipboard "$output"
omarchy-notification-send -g  "Transcoded to $resolution $format" "Saved and copied to clipboard."
fi
Expand Down
77 changes: 52 additions & 25 deletions default/nautilus-python/extensions/transcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,86 @@
from gi.repository import GObject, Gio, Nautilus


SUPPORTED_MIME_PREFIXES = ("image/", "video/")
SUPPORTED_EXTENSIONS = {
".jpg", ".jpeg", ".png", ".webp", ".gif", ".heic", ".avif",
".mp4", ".mov", ".m4v", ".mkv", ".webm", ".avi",
}
PICTURE_EXTENSIONS = (".jpg", ".jpeg", ".png", ".webp", ".gif", ".heic", ".avif")
VIDEO_EXTENSIONS = (".mp4", ".mov", ".m4v", ".mkv", ".webm", ".avi")


class TranscodeAction(GObject.GObject, Nautilus.MenuProvider):
def _media_type(self, file):
mime = file.get_mime_type() or ""
if mime.startswith("image/"):
return "picture"
if mime.startswith("video/"):
return "video"

location = file.get_location()
if not location:
return None
path = (location.get_path() or "").lower()
if path.endswith(PICTURE_EXTENSIONS):
return "picture"
if path.endswith(VIDEO_EXTENSIONS):
return "video"
return None

def _batch_command(self, binary, media_type, paths):
if media_type == "picture":
format_options = "jpg png"
resolution_options = "high medium low"
else:
format_options = "mp4 gif"
resolution_options = "4k 1080p 720p"

commands = [
f"format=$(omarchy-menu-select 'Select {media_type} format' {format_options}) || exit 1",
f"resolution=$(omarchy-menu-select 'Select {media_type} resolution' {resolution_options}) || exit 1",
]

for path in paths:
commands.append(
f"echo {shlex.quote(f'Transcoding {path}')} && "
f"{shlex.join([binary, path])} \"$format\" \"$resolution\" || true"
)

return "; ".join(commands)
Comment on lines +33 to +52

def _launch_transcode(self, paths):
wrapper = shutil.which("omarchy-launch-floating-terminal-with-presentation")
binary = shutil.which("omarchy-transcode")
if not wrapper or not binary:
return

if len(paths) == 1:
cmd = shlex.join([binary, paths[0]])
cmd = shlex.join([binary, paths[0][1]])
else:
cmd = "; ".join(
f"echo {shlex.quote(f'Transcoding {path}')} && "
f"{shlex.join([binary, path])} || true"
for path in paths
)
picture_paths = [path for media_type, path in paths if media_type == "picture"]
video_paths = [path for media_type, path in paths if media_type == "video"]
commands = []
if picture_paths:
commands.append(self._batch_command(binary, "picture", picture_paths))
if video_paths:
commands.append(self._batch_command(binary, "video", video_paths))
Comment on lines +67 to +69
cmd = "; ".join(commands)

Gio.Subprocess.new([wrapper, cmd], Gio.SubprocessFlags.NONE)

def _is_supported(self, file):
mime = file.get_mime_type() or ""
if mime.startswith(SUPPORTED_MIME_PREFIXES):
return True

location = file.get_location()
if not location:
return False
path = location.get_path() or ""
lower = path.lower()
return any(lower.endswith(ext) for ext in SUPPORTED_EXTENSIONS)

def _selected_paths(self, files):
paths = []
seen = set()

for file in files:
if file.is_directory():
continue
if not self._is_supported(file):
media_type = self._media_type(file)
if not media_type:
continue
location = file.get_location()
if not location:
continue
path = location.get_path()
if path and path not in seen:
seen.add(path)
paths.append(path)
paths.append((media_type, path))
return paths

def _make_item(self, paths):
Expand Down
3 changes: 3 additions & 0 deletions migrations/1778424449.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
echo "Refresh Nautilus Transcode context menu"

source "$OMARCHY_PATH/install/config/nautilus-python.sh"
Loading