Commits

wistful committed a1a9d5d

added first release

Comments (0)

Files changed (4)

+Плагины к nautilus:
+
+
+Release: 2012/02/12
+version: 1.0
+
+Author: wistful <wst[dot]public[dot]mail[dot]gmail[dot]com>
+Licence: GNU GPLv3+ (www.gnu.org)
+
+
+Содержимое:
+
+path_encoder.py -  Рекурсивное исправление кодировки в имени файла или каталога
+                   Зависимости: chardet (http://chardet.feedparser.org/)
+
+nautilus_opener.py - Добавление пунктов меню для открытие файла/папки в зависимости от его имени или имени прямых потомков
+                     настройки хранятся в ~/nautilus-opener.conf
+                     пример файла настроек: nautilus-opener.conf
+                     шаблон для файла/каталога и его потомков задается с помощью реглярных выражений
+                     лог-файл хранит ошибки за текущую сессию: ~/nautilus-opener.log
+
+
+Установка:
+    Скопируйте содержимое в каталог $HOME/.local/share/nautilus-python/extensions

nautilus-opener.conf

+[settings]
+rules = pycharm, gitg, gedit, dvd, baobab
+
+[pycharm]
+comment = Open folder with .idea subfolder with PyCharm
+child = (\.idea)+
+exec_path = /home/wistful/programming/pycharm/bin/pycharm.sh
+emblem = pycharm
+label = Open in PyCharm
+help = Open PyCharm Project
+
+[gitg]
+comment = Open folder with .git subfolder with gitg
+child = (\.git)+
+exec_path = /usr/bin/gitg
+label = Open with gitg
+help = Open git repo
+
+[gedit]
+item = (.txt|.log|.desktop)+
+exec_path = /usr/bin/gedit
+label = Open with gedit
+help = open file with gedit
+
+[dvd]
+comment = Open folder with VIDEO_TS subfolder with vlc player
+child = (VIDEO_TS)+
+exec_path = /usr/bin/vlc
+emblem = dvd
+label = Open DVD in vlc player
+help = open folder in the vlc player
+
+[baobab]
+item = .+
+comment = Disk Usage Analyzer
+exec_path = /usr/bin/baobab
+label = Disk Usage Analyzer
+help = Disk Usage Analyzer

nautilus-opener.py

+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#    Copyleft 2011-3011 wistful (wst[dot]public[dot]mail[dot]gmail[dot]com)
+#
+#    This is a free software; you can redistribute it and/or
+#    modify it under the terms of the GNU Lesser General Public
+#    License as published by the Free Software Foundation; either
+#    version 2.1 of the License, or (at your option) any later version.
+#
+#    This library is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#    Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public
+#    License along with this library; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from ConfigParser import ConfigParser
+from subprocess import Popen
+import traceback
+import datetime
+import os
+import urllib
+import re
+
+
+try:
+    # for old versions of python-nautilus
+    import nautilus
+    import pygtk
+    pygtk.require('2.0')
+
+    class GObject(object):
+        class GObject(object):
+            pass
+except ImportError, ex:
+    # for new versions of python-nautilus
+    from gi.repository import Nautilus as nautilus
+    from gi.repository import GObject
+
+
+SETTINGS_PATH = os.path.expanduser('~/nautilus-opener.conf')
+LOG_PATH = os.path.expanduser('~/nautilus-opener.log')
+
+
+def logger(message):
+    """ write messages to log file """
+    fd = open(LOG_PATH, 'a+')
+    fd.write(message)
+    fd.write("\n")
+
+
+def error_wrapper(fn):
+    """
+    catch Exceptions and write it to log file
+    """
+    name = fn.__name__
+
+    def new_fn(*args, **kwargs):
+        try:
+            result = fn(*args, **kwargs)
+            return result
+        except Exception, err:
+            logger("{date} Error in the {name}:\n{traceback}\n\n".format(
+                    date=datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
+                    name=name, traceback=traceback.format_exc()))
+            raise
+    return new_fn
+
+
+@error_wrapper
+def read_config(config_path):
+    config = ConfigParser()
+    config.read([config_path])
+    rules = [item.strip() for item in config._sections['settings']['rules'].split(',')]
+    result = {}
+    for rule in rules:
+        if config.has_section(rule):
+            result[rule] = dict((item[0], item[1]) for item in config.items(rule))
+    return result
+
+
+def get_paths(nautilus_path_list):
+    for item in nautilus_path_list:
+        yield urllib.unquote(item.get_uri()[7:])
+
+
+class NautilusOpener(GObject.GObject, nautilus.MenuProvider, nautilus.InfoProvider):
+
+    def __init__(self):
+        self.rules = read_config(SETTINGS_PATH)
+        if os.path.exists(LOG_PATH):
+            os.remove(LOG_PATH)
+
+    def run(self, menu, param):
+        item, rule_name = param
+        exec_path = self.rules[rule_name].get('exec_path', None)
+        if exec_path:
+            return Popen([exec_path, item]).pid
+
+    @error_wrapper
+    def get_file_items(self, window, files):
+        if len(files) > 1 or not self.rules:
+            return
+
+        # get items which match to mask ("item" option)
+        item_path_list = [(item, rule_name) for rule_name, rule in self.rules.items()
+                                                for item in get_paths(files)
+                                                    if rule.get('item', None) and
+                                                       re.search(rule['item'], os.path.split(item)[1])]
+
+        # get childes which match to mask ("child" option)
+        child_path_list = [(item, rule_name) for rule_name, rule in self.rules.items()
+                                                for item in get_paths(files)
+                                                    if os.path.isdir(item)
+                                                        for child in os.listdir(item)
+                                                           if rule.get('child', None) and
+                                                                re.search(rule['child'], child)]
+        item_path_list.extend(child_path_list)
+
+        menus = []
+        for item, rule_name in item_path_list:
+            label = self.rules[rule_name].get('label')
+            help_label = self.rules[rule_name].get('help')
+            if not label:
+                continue
+            menus.append(nautilus.MenuItem(name='NautilusOpener::' + rule_name.replace(' ', '_') + '_opener',
+                                           label=label,
+                                           tip=help_label))
+            menus[-1].connect('activate', self.run, (item, rule_name))
+
+        return menus
+
+    @error_wrapper
+    def update_file_info(self, file_item):
+        # get emblems which match to mask ("item" option)
+        item_path_list = [rule['emblem'] for rule_name, rule in self.rules.items()
+                                                for item in get_paths([file_item])
+                                                    if rule.get('item', None) and rule.get('emblem', None) and
+                                                       re.search(rule['item'], os.path.split(item)[1])]
+
+        # get emblems which match to mask ("child" option)
+        child_path_list = [rule['emblem'] for rule_name, rule in self.rules.items()
+                                                for item in get_paths([file_item])
+                                                    if os.path.isdir(item)
+                                                        for child in os.listdir(item)
+                                                            if rule.get('child', None) and rule.get('emblem', None) and
+                                                                re.search(rule['child'], child)]
+        item_path_list.extend(child_path_list)
+
+        if item_path_list:
+            # if emblem not exist -> nothing happens
+            file_item.add_emblem(item_path_list[-1])
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#    Copyleft 2011-3011 wistful (wst[dot]public[dot]mail[dot]gmail[dot]com)
+#
+#    This is a free software; you can redistribute it and/or
+#    modify it under the terms of the GNU Lesser General Public
+#    License as published by the Free Software Foundation; either
+#    version 2.1 of the License, or (at your option) any later version.
+#
+#    This library is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#    Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public
+#    License along with this library; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import os
+import chardet
+import urllib
+try:
+    # for old versions of python-nautilus
+    import nautilus
+    import gtk
+    import pygtk
+    pygtk.require('2.0')
+
+    class GObject(object):
+        class GObject(object):
+            pass
+
+except ImportError, ex:
+    # for new versions of python-nautilus
+    from gi.repository import Nautilus as nautilus
+    from gi.repository import GObject
+    from gi.repository import Gtk as gtk
+
+
+NAUTILUS_MENU_LABEL = u'Преобразовать в utf-8'
+NAUTILUS_MENU_HELP = u'Преобразовать РЕКУРСИВНО в utf-8'
+
+good_encodings = ['utf-8', 'ascii']  # правильные кодировки
+result_encoding = 'utf-8'  # итоговая кодировка
+errors = []  # список необработанных путей
+
+
+def error_dialog(message, dialog_title="Ошибка..."):
+    """вывод сообщения об ошибке"""
+    dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL,
+                                type=gtk.MESSAGE_ERROR,
+                                buttons=gtk.BUTTONS_OK,
+                                message_format=message)
+    dialog.set_title(dialog_title)
+    dialog.run()
+    dialog.destroy()
+
+
+def encoder_item(item):
+    """определение правильной кодировки item -> текст в правильной кодировке"""
+    encoding = chardet.detect(item)['encoding']
+    if encoding == result_encoding:
+        return item
+    elif encoding in good_encodings:
+        return item.encode(result_encoding)
+    else:
+        return item.decode(encoding).encode(result_encoding)
+
+
+def path_encoder(root, recursive=False):
+    """проверка и исправление кодировки частей пути"""
+    encoding_path = root
+    path, item = os.path.split(root)  # отделение последней части пути
+    try:
+        # новый путь
+        encoding_path = os.path.join(path, encoder_item(item))
+    except Exception:
+        errors.append(root)
+        encoding_path = root
+
+    if encoding_path != root:
+        try:
+            os.renames(root, encoding_path)
+        except Exception:
+            errors.append(root)
+            return
+
+    if os.path.isdir(encoding_path) and recursive:
+        for item in os.listdir(encoding_path):
+            path_encoder(os.path.join(encoding_path, item), recursive)
+
+
+class PathEncoderMenuProvider(GObject.GObject, nautilus.MenuProvider):
+
+    def __init__(self):
+        pass
+
+    def run(self, menu, source_path_list):
+        errors = []
+        [path_encoder(path, recursive=True) for path in source_path_list]
+        if errors:
+            error_message = "Не удалось преобразовать:\n"
+            for item in errors:
+                error_message = error_message + item + '\n'
+            error_dialog(error_message)
+
+    def get_file_items(self, window, files):
+        """получение списка путей для выделенных элементов"""
+        source_path_list = [urllib.unquote(item.get_uri()[7:]) \
+                            for item in files]
+        if not source_path_list:
+            return
+        menuitem = nautilus.MenuItem(name='NautilusPython::path_encoder',
+                                     label=NAUTILUS_MENU_LABEL,
+                                     tip=NAUTILUS_MENU_HELP)
+        menuitem.connect('activate', self.run, source_path_list)
+        return menuitem,