mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-01 20:28:16 +02:00
* Add Transcode entry to Nautilus context menu Right-click on image or video files in Nautilus to send them to omarchy-transcode in a presentation terminal, mirroring the existing LocalSend integration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Use shlex.quote for shell-safe command construction in transcode.py Quote both the resolved binary path and file paths via shlex.quote instead of a custom escaper. Addresses Copilot review on #5635. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Style changes --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: David Heinemeier Hansson <david@hey.com>
95 lines
2.7 KiB
Python
95 lines
2.7 KiB
Python
import shlex
|
|
import shutil
|
|
|
|
from gi import require_version
|
|
|
|
require_version("Nautilus", "4.1")
|
|
|
|
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",
|
|
}
|
|
|
|
|
|
class TranscodeAction(GObject.GObject, Nautilus.MenuProvider):
|
|
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]])
|
|
else:
|
|
cmd = "; ".join(
|
|
f"echo {shlex.quote(f'Transcoding {path}')} && "
|
|
f"{shlex.join([binary, path])} || true"
|
|
for path in paths
|
|
)
|
|
|
|
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):
|
|
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)
|
|
return paths
|
|
|
|
def _make_item(self, paths):
|
|
label = "Transcode" if len(paths) == 1 else f"Transcode {len(paths)} items"
|
|
item = Nautilus.MenuItem(
|
|
name="OmarchyTranscodeNautilus::transcode",
|
|
label=label,
|
|
icon="media-playback-start",
|
|
)
|
|
item.connect("activate", self._on_activate, paths)
|
|
return item
|
|
|
|
def _on_activate(self, _menu, paths):
|
|
self._launch_transcode(paths)
|
|
|
|
def _tools_available(self):
|
|
return bool(
|
|
shutil.which("omarchy-launch-floating-terminal-with-presentation")
|
|
and shutil.which("omarchy-transcode")
|
|
)
|
|
|
|
def get_file_items(self, *args):
|
|
files = args[0] if len(args) == 1 else args[1]
|
|
if not self._tools_available():
|
|
return []
|
|
|
|
paths = self._selected_paths(files)
|
|
if not paths:
|
|
return []
|
|
|
|
return [self._make_item(paths)]
|