# Right-click a video to shrink it, or a picture to convert it. # # macOS puts both behind Quick Actions in Finder. On Linux the usual answer is # a web uploader or an ffmpeg incantation somebody looks up again every time. # # The menu only appears for files this can actually handle, decided by mime # type rather than by extension, so a .mov named .txt does not get an entry it # would fail on. The work itself is bin/panama-transcode, which writes beside # the input and never over it. import os import subprocess from urllib.parse import unquote, urlparse from gi.repository import GObject, Nautilus HELPER = os.path.expanduser("~/.local/share/Panama/bin/panama-transcode") VIDEO_SIZES = [("1080p", "1080p"), ("720p", "720p"), ("480p", "480p")] IMAGE_FORMATS = [("jpg", "JPEG"), ("png", "PNG"), ("webp", "WebP")] def _local_path(file_info): uri = file_info.get_uri() parsed = urlparse(uri) if parsed.scheme != "file": return None path = unquote(parsed.path) return path if os.path.isfile(path) else None class PanamaTranscodeExtension(GObject.GObject, Nautilus.MenuProvider): def get_file_items(self, files): # One at a time: a submenu that starts nine encodes from one click is # a way to make a machine unusable by accident. if len(files) != 1: return [] info = files[0] path = _local_path(info) if path is None or not os.access(HELPER, os.X_OK): return [] mime = info.get_mime_type() or "" if mime.startswith("video/"): return [self._submenu(path, "video", "Convert video", VIDEO_SIZES)] if mime.startswith("image/"): return [self._submenu(path, "image", "Convert image", IMAGE_FORMATS)] return [] def _submenu(self, path, kind, label, options): top = Nautilus.MenuItem(name=f"Panama::Transcode{kind}", label=label) menu = Nautilus.Menu() top.set_submenu(menu) for value, shown in options: item = Nautilus.MenuItem( name=f"Panama::Transcode{kind}{value}", label=shown, tip=f"Write a {shown} copy beside this file", ) item.connect("activate", self._run, kind, path, value) menu.append_item(item) return top def _run(self, _menu, kind, path, value): # Detached on purpose: a long encode must not block the file manager, # and panama-transcode reports its own progress through notifications. try: subprocess.Popen( [HELPER, kind, path, value], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True, ) except OSError: pass