# SPDX-FileCopyrightText: Chris Pressey, the original author of this work, has dedicated it to the public domain.
# For more information, please refer to <https://unlicense.org/>
# SPDX-License-Identifier: Unlicense
#
# tagfarm plugin for Nautilus.
#
# to install:
#
# sudo apt-get install python3-nautilus
# mkdir -p ~/.local/share/nautilus-python/extensions/
# cp script/tagfarm_nautilus.py ~/.local/share/nautilus-python/extensions/
# nautilus -q
#
# to troubleshoot:
#
# nautilus -q ; NAUTILUS_DEBUG=Extension nautilus
#
import os
import subprocess
from urllib.parse import unquote
from gi.repository import Nautilus, GObject
class TagfarmExtension(Nautilus.MenuProvider, GObject.GObject):
def __init__(self):
pass
def get_file_items(self, window, files):
if not files:
return
filenames = [unquote(file.get_uri()[7:]) for file in files]
print("tagfarm extension. Filenames:", filenames)
tags, available_tags = self._get_tags(filenames)
menu_items = []
if available_tags:
menu_items.append(self._create_add_tag_submenu(filenames, available_tags))
if tags:
menu_items.append(self._create_remove_tag_submenu(filenames, tags))
repair_item = Nautilus.MenuItem(
name="NautilusPython::tagfarm_repair",
label="Repair Tag Links",
tip="Repair tag links after files moved"
)
repair_item.connect('activate', self._repair_tags, filenames)
menu_items.append(repair_item)
return menu_items
def _create_remove_tag_submenu(self, filenames, tags):
# For "Remove Tag" submenu: create parent menu item, containing
# a submenu, with one submenu item per tag on at least one file.
menu_item = Nautilus.MenuItem(
name="NautilusPython::tagfarm_remove_tags",
label="Remove Tag",
tip="Remove tag from selected files"
)
submenu = Nautilus.Menu()
menu_item.set_submenu(submenu)
for i, tag in enumerate(tags):
tag_item = Nautilus.MenuItem(
name=f"NautilusPython::tagfarm_remove_tag_{i}",
label=tag,
tip=f"Remove tag '{tag}' from files"
)
tag_item.connect('activate', self._remove_tag, tag, filenames)
submenu.append_item(tag_item)
return menu_item
def _create_add_tag_submenu(self, filenames, tags):
# For "Add Tag" submenu: create parent menu item, containing
# a submenu, with one submenu item per tag *not* on at least one file.
menu_item = Nautilus.MenuItem(
name="NautilusPython::tagfarm_add_tags",
label="Add Tag",
tip="Add tag to selected files"
)
submenu = Nautilus.Menu()
menu_item.set_submenu(submenu)
for i, tag in enumerate(tags):
tag_item = Nautilus.MenuItem(
name=f"NautilusPython::tagfarm_add_tag_{i}",
label=tag,
tip=f"Add tag '{tag}' to files"
)
tag_item.connect('activate', self._add_tag, tag, filenames)
submenu.append_item(tag_item)
return menu_item
def _get_tags(self, filenames):
# Returns a pair of (tags, available_tags), where each of those
# is a sorted list
if not filenames:
return ([], [])
working_dir = os.path.dirname(filenames[0])
filenames = [os.path.basename(filename) for filename in filenames]
command = ["tagfarm", "showtags", "--terse", "--all"] + filenames
print("command:", command)
result = subprocess.run(
command,
capture_output=True,
text=True,
cwd=working_dir,
)
print("result.stdout:", repr(result.stdout))
if result.stdout:
all_tags = set(result.stdout.strip().split())
return (
sorted(set([tag[1:] for tag in all_tags if tag.startswith("*")])),
sorted(set([tag for tag in all_tags if not tag.startswith("*")])),
)
else:
return ([], [])
def _add_tag(self, menu, tag, filenames):
working_dir = os.path.dirname(filenames[0])
base_filenames = [os.path.basename(filename) for filename in filenames]
subprocess.run(["tagfarm", "tag", tag] + base_filenames, cwd=working_dir)
def _remove_tag(self, menu, tag, filenames):
working_dir = os.path.dirname(filenames[0])
base_filenames = [os.path.basename(filename) for filename in filenames]
subprocess.run(["tagfarm", "untag", tag] + base_filenames, cwd=working_dir)
def _repair_tags(self, menu, filenames):
working_dir = os.path.dirname(filenames[0])
subprocess.run(["tagfarm", "repair"], cwd=working_dir)