Merge pull request #187 from zas/picard528

PICARD-528: ignore file paths if hidden or excluded by regex
This commit is contained in:
Laurent Monin
2014-01-06 16:34:06 -08:00
10 changed files with 188 additions and 7 deletions

View File

@@ -16,6 +16,8 @@
* Main window is now emitting a "selection_updated" signal, plugin api version bumps to 1.3.0
* Append system information to user-agent string
* Compilation tag/variable aligned with iTunes, set only for Various Artists type compilations.
* Ignore directories and files while indexing when show_hidden_files option is set to False (PICARD-528)
* Add ignore_regex option which allows one to ignore matching paths, can be set in Options > Advanced (PICARD-528)
Version 1.2 - 2013-03-30
* Picard now requires at least Python 2.6

View File

@@ -68,7 +68,8 @@ from picard.util import (
thread,
mbid_validate,
check_io_encoding,
uniqify
uniqify,
is_hidden_path,
)
from picard.webservice import XmlWebService
@@ -271,9 +272,20 @@ class Tagger(QtGui.QApplication):
def add_files(self, filenames, target=None):
"""Add files to the tagger."""
log.debug("Adding files %r", filenames)
ignoreregex = None
pattern = config.setting['ignore_regex']
if pattern:
ignoreregex = re.compile(pattern)
ignore_hidden = not config.persist["show_hidden_files"]
new_files = []
for filename in filenames:
filename = os.path.normpath(os.path.realpath(filename))
if ignore_hidden and is_hidden_path(filename):
log.debug("File ignored (hidden): %s" % (filename))
continue
if ignoreregex is not None and ignoreregex.search(filename):
log.info("File ignored (matching %s): %s" % (pattern, filename))
continue
if filename not in self.files:
file = open_file(filename)
if file:

View File

@@ -33,6 +33,7 @@ class OptionsPage(QtGui.QWidget):
PARENT = None
SORT_ORDER = 1000
ACTIVE = True
STYLESHEET_ERROR = "QWidget { background-color: #f55; color: white; font-weight:bold }"
def info(self):
raise NotImplementedError

View File

@@ -17,7 +17,12 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from picard.ui.options import OptionsPage, register_options_page
from PyQt4.QtGui import QPalette, QColor
import re
from picard import config
from picard.ui.options import OptionsPage, OptionsCheckError, register_options_page
from picard.ui.ui_options_advanced import Ui_AdvancedOptionsPage
class AdvancedOptionsPage(OptionsPage):
@@ -26,7 +31,39 @@ class AdvancedOptionsPage(OptionsPage):
TITLE = N_("Advanced")
PARENT = None
SORT_ORDER = 90
ACTIVE = False
ACTIVE = True
options = [
config.TextOption("setting", "ignore_regex", ""),
]
def __init__(self, parent=None):
super(AdvancedOptionsPage, self).__init__(parent)
self.ui = Ui_AdvancedOptionsPage()
self.ui.setupUi(self)
self.ui.ignore_regex.textChanged.connect(self.live_checker)
def load(self):
self.ui.ignore_regex.setText(config.setting["ignore_regex"])
def save(self):
config.setting["ignore_regex"] = unicode(self.ui.ignore_regex.text())
def live_checker(self, text):
self.ui.regex_error.setStyleSheet("")
self.ui.regex_error.setText("")
try:
self.check()
except OptionsCheckError as e:
self.ui.regex_error.setStyleSheet(self.STYLESHEET_ERROR)
self.ui.regex_error.setText(e.info)
return
def check(self):
try:
re.compile(unicode(self.ui.ignore_regex.text()))
except re.error as e:
raise OptionsCheckError(_("Regex Error"), str(e))
register_options_page(AdvancedOptionsPage)

View File

@@ -270,8 +270,6 @@ class RenamingOptionsPage(OptionsPage):
file.metadata['musicbrainz_releasetrackid'] = 'eac99807-93d4-3668-9714-fa0c1b487ccf'
return file
STYLESHEET_ERROR = "QWidget { background-color: #f55; color: white; font-weight:bold }"
def move_files_to_browse(self):
path = QtGui.QFileDialog.getExistingDirectory(self, "", self.ui.move_files_to.text())
if path:

View File

@@ -70,8 +70,6 @@ class ScriptingOptionsPage(OptionsPage):
config.TextOption("setting", "tagger_script", ""),
]
STYLESHEET_ERROR = "QWidget { background-color: #f55; color: white; font-weight:bold }"
def __init__(self, parent=None):
super(ScriptingOptionsPage, self).__init__(parent)
self.ui = Ui_ScriptingOptionsPage()

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/options_advanced.ui'
#
# Created: Wed Dec 25 02:35:20 2013
# by: PyQt4 UI code generator 4.9.3
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_AdvancedOptionsPage(object):
def setupUi(self, AdvancedOptionsPage):
AdvancedOptionsPage.setObjectName(_fromUtf8("AdvancedOptionsPage"))
AdvancedOptionsPage.resize(338, 435)
self.vboxlayout = QtGui.QVBoxLayout(AdvancedOptionsPage)
self.vboxlayout.setObjectName(_fromUtf8("vboxlayout"))
self.groupBox = QtGui.QGroupBox(AdvancedOptionsPage)
self.groupBox.setObjectName(_fromUtf8("groupBox"))
self.gridlayout = QtGui.QGridLayout(self.groupBox)
self.gridlayout.setSpacing(2)
self.gridlayout.setObjectName(_fromUtf8("gridlayout"))
self.label_ignore_regex = QtGui.QLabel(self.groupBox)
self.label_ignore_regex.setObjectName(_fromUtf8("label_ignore_regex"))
self.gridlayout.addWidget(self.label_ignore_regex, 1, 0, 1, 1)
self.ignore_regex = QtGui.QLineEdit(self.groupBox)
self.ignore_regex.setObjectName(_fromUtf8("ignore_regex"))
self.gridlayout.addWidget(self.ignore_regex, 2, 0, 1, 1)
self.regex_error = QtGui.QLabel(self.groupBox)
self.regex_error.setText(_fromUtf8(""))
self.regex_error.setObjectName(_fromUtf8("regex_error"))
self.gridlayout.addWidget(self.regex_error, 3, 0, 1, 1)
self.vboxlayout.addWidget(self.groupBox)
spacerItem = QtGui.QSpacerItem(181, 21, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.vboxlayout.addItem(spacerItem)
self.retranslateUi(AdvancedOptionsPage)
QtCore.QMetaObject.connectSlotsByName(AdvancedOptionsPage)
def retranslateUi(self, AdvancedOptionsPage):
self.groupBox.setTitle(_("Advanced options"))
self.label_ignore_regex.setText(_("Ignore file paths matching the following regular expression:"))

View File

@@ -365,3 +365,9 @@ if sys.platform == 'win32':
return ap1 == ap2
else:
os_path_samefile = os.path.samefile
def is_hidden_path(path):
"""Returns true if at least one element of the path starts with a dot"""
path = os.path.normpath(path) # we need to ignore /./ and /a/../ cases
return any(s.startswith('.') for s in path.split(os.sep))

View File

@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import os.path
import unittest
from picard import util
@@ -119,3 +120,18 @@ class SaveReleaseTypeScoresTest(unittest.TestCase):
self.assertTrue("Single 0.50" in saved_scores)
self.assertTrue("Other 0.00" in saved_scores)
self.assertEqual(6, len(saved_scores.split()))
class HiddenPathTest(unittest.TestCase):
def test(self):
self.assertEqual(util.is_hidden_path('/a/.b/c.mp3'), True)
self.assertEqual(util.is_hidden_path('/a/b/c.mp3'), False)
self.assertEqual(util.is_hidden_path('/a/.b/.c.mp3'), True)
self.assertEqual(util.is_hidden_path('/a/b/.c.mp3'), True)
self.assertEqual(util.is_hidden_path('c.mp3'), False)
self.assertEqual(util.is_hidden_path('.c.mp3'), True)
self.assertEqual(util.is_hidden_path('/a/./c.mp3'), False)
self.assertEqual(util.is_hidden_path('/a/./.c.mp3'), True)
self.assertEqual(util.is_hidden_path('/a/../c.mp3'), False)
self.assertEqual(util.is_hidden_path('/a/../.c.mp3'), True)

63
ui/options_advanced.ui Normal file
View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AdvancedOptionsPage</class>
<widget class="QWidget" name="AdvancedOptionsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>338</width>
<height>435</height>
</rect>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Advanced options</string>
</property>
<layout class="QGridLayout">
<property name="spacing">
<number>2</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_ignore_regex">
<property name="text">
<string>Ignore file paths matching the following regular expression:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLineEdit" name="ignore_regex"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="regex_error" >
<property name="text" >
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>181</width>
<height>21</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>ignore_regex</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>