Right-click a file to send it to your phone, or a video to make it smaller. macOS has both behind the Share sheet and Quick Actions; Windows has "Send to"; a stock Linux file manager has neither, and the usual answer for the second one is a web uploader or an ffmpeg incantation looked up again every time. Neither adds machinery. Sending reuses panama-kdeconnect, the same helper the Home & Phone page and quick settings already drive, so there is one way to talk to a phone rather than two. The entry appears only when a phone is actually reachable: an item that is present and fails is worse than one that is absent, because the absence explains itself. The transcoder's two rules are both about not losing work. It never writes to its input, and it never writes over an earlier output -- a second run produces -2 rather than eating the first result. Verified against a real encode: 1920x1080 became 854x480, with an even width because H.264 rejects an odd one at the very end of a long encode, which is the worst possible moment to find out. Menus decide by mime type rather than extension, act on one file at a time, and refuse anything that is not a local path. nautilus-python turned out to be declared already; it now says it carries Panama's own extensions too.
78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
# 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
|