git @ Cat's Eye Technologies tagfarm / master script / tagfarm_nautilus.py
master

Tree @master (Download .tar.gz)

tagfarm_nautilus.py @masterraw · history · blame

# 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)