mirror of
https://github.com/fergalmoran/picard.git
synced 2026-01-06 00:23:58 +00:00
Merge pull request #187 from zas/picard528
PICARD-528: ignore file paths if hidden or excluded by regex
This commit is contained in:
2
NEWS.txt
2
NEWS.txt
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
48
picard/ui/ui_options_advanced.py
Normal file
48
picard/ui/ui_options_advanced.py
Normal 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:"))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
63
ui/options_advanced.ui
Normal 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>
|
||||
Reference in New Issue
Block a user