diff --git a/picard/file.py b/picard/file.py
index d6777f303..3c9917abc 100644
--- a/picard/file.py
+++ b/picard/file.py
@@ -21,13 +21,25 @@
import glob
import os.path
import shutil
+import sys
import traceback
from PyQt4 import QtCore
from picard.metadata import Metadata
from picard.ui.item import Item
+from picard.script import ScriptParser
from picard.similarity import similarity2
-from picard.util import LockableObject, encode_filename, decode_filename, format_time, partial
from picard.util.thread import spawn, proxy_to_main
+from picard.util import (
+ decode_filename,
+ encode_filename,
+ make_short_filename,
+ replace_win32_incompat,
+ replace_non_ascii,
+ sanitize_filename,
+ partial,
+ format_time,
+ LockableObject,
+ )
class File(LockableObject, Item):
@@ -117,6 +129,49 @@ class File(LockableObject, Item):
"""Save the metadata."""
raise NotImplementedError
+ def make_filename(self, settings=None):
+ """Constructs file name based on metadata and file naming formats."""
+
+ if settings is None:
+ settings = self.config.setting
+
+ filename = self.filename
+ metadata = Metadata()
+ metadata.copy(self.metadata)
+
+ if settings["move_files"]:
+ new_dirname = settings["move_files_to"]
+ else:
+ new_dirname = os.path.dirname(filename)
+ old_dirname = new_dirname
+ new_filename, ext = os.path.splitext(os.path.basename(filename))
+
+ if settings["rename_files"]:
+ # replace incompatible characters
+ for name in metadata.keys():
+ value = metadata[name]
+ if isinstance(value, basestring):
+ value = sanitize_filename(value)
+ if settings["windows_compatible_filenames"] or sys.platform == "win32":
+ value = replace_win32_incompat(value)
+ if settings["ascii_filenames"]:
+ value = replace_non_ascii(value)
+ metadata[name] = value
+ # expand the naming format
+ if metadata['compilation'] == '1':
+ format = settings['va_file_naming_format']
+ else:
+ format = settings['file_naming_format']
+ new_filename = ScriptParser().eval(format, metadata)
+ if not settings['move_files']:
+ new_filename = os.path.basename(new_filename)
+ new_filename = make_short_filename(new_dirname, new_filename)
+ # win32 compatibility fixes
+ if settings['windows_compatible_filenames'] or sys.platform == 'win32':
+ new_filename = new_filename.replace('./', '_/').replace('.\\', '_\\')
+
+ return os.path.join(new_dirname, new_filename + ext)
+
def save_images(self):
"""Save the cover images to disk."""
if not "~artwork" in self.metadata:
diff --git a/picard/ui/options/naming.py b/picard/ui/options/naming.py
index 45845f0b2..0277a00d1 100644
--- a/picard/ui/options/naming.py
+++ b/picard/ui/options/naming.py
@@ -21,6 +21,7 @@ import os.path
import sys
from PyQt4 import QtCore, QtGui
from picard.config import BoolOption, TextOption
+from picard.file import File
from picard.script import ScriptParser
from picard.ui.options import OptionsPage, OptionsCheckError, register_options_page
from picard.ui.ui_options_naming import Ui_NamingOptionsPage
@@ -55,6 +56,7 @@ class NamingOptionsPage(OptionsPage):
self.connect(self.ui.va_file_naming_format_default, QtCore.SIGNAL("clicked()"), self.set_va_file_naming_format_default)
self.connect(self.ui.move_files_to_browse, QtCore.SIGNAL("clicked()"), self.move_files_to_browse)
self.connect(self.ui.move_additional_files, QtCore.SIGNAL("clicked()"), self.update_move_additional_files)
+ self.connect(self.ui.test_button, QtCore.SIGNAL("clicked()"), self.test)
def load(self):
if sys.platform == "win32":
@@ -118,5 +120,41 @@ class NamingOptionsPage(OptionsPage):
def update_move_additional_files(self):
self.ui.move_additional_files_pattern.setEnabled(self.ui.move_additional_files.isChecked())
+ def test(self):
+ settings = {
+ 'windows_compatible_filenames': self.ui.windows_compatible_filenames.isChecked(),
+ 'ascii_filenames': self.ui.ascii_filenames.isChecked(),
+ 'rename_files': self.ui.rename_files.isChecked(),
+ 'move_files': self.ui.move_files.isChecked(),
+ 'file_naming_format': unicode(self.ui.file_naming_format.text()),
+ 'va_file_naming_format': unicode(self.ui.va_file_naming_format.text()),
+ 'move_files_to': os.path.normpath(unicode(self.ui.move_files_to.text())),
+ }
+
+ file = File("ticket_to_ride.mp3")
+ file.metadata['album'] = 'Help!'
+ file.metadata['title'] = 'Ticket to Ride'
+ file.metadata['artist'] = 'The Beatles'
+ file.metadata['albumartist'] = 'The Beatles'
+ file.metadata['tracknumber'] = '7'
+ file.metadata['totaltracks'] = '14'
+ file.metadata['date'] = '1965-08-06'
+ file.metadata['~extension'] = 'mp3'
+ filename = file.make_filename(settings=settings)
+ self.ui.example_filename.setText(filename)
+
+ file = File("track05.mp3")
+ file.metadata['album'] = 'Explosive Doowops, Volume 4'
+ file.metadata['title'] = 'Why? Oh Why?'
+ file.metadata['artist'] = 'The Fantasys'
+ file.metadata['albumartist'] = 'Various Artists'
+ file.metadata['tracknumber'] = '5'
+ file.metadata['totaltracks'] = '26'
+ file.metadata['date'] = '1999-02-03'
+ file.metadata['compilation'] = '1'
+ file.metadata['~extension'] = 'mp3'
+ filename = file.make_filename(settings=settings)
+ self.ui.example_va_filename.setText(filename)
+
register_options_page(NamingOptionsPage)
diff --git a/picard/ui/ui_options_naming.py b/picard/ui/ui_options_naming.py
index ece7ad2ff..b43f5cbaf 100644
--- a/picard/ui/ui_options_naming.py
+++ b/picard/ui/ui_options_naming.py
@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'ui/options_naming.ui'
#
-# Created: Sat Mar 3 19:09:31 2007
+# Created: Sun Mar 4 09:56:45 2007
# by: PyQt4 UI code generator 4.1
#
# WARNING! All changes made in this file will be lost!
@@ -13,7 +13,7 @@ from PyQt4 import QtCore, QtGui
class Ui_NamingOptionsPage(object):
def setupUi(self, NamingOptionsPage):
NamingOptionsPage.setObjectName("NamingOptionsPage")
- NamingOptionsPage.resize(QtCore.QSize(QtCore.QRect(0,0,348,397).size()).expandedTo(NamingOptionsPage.minimumSizeHint()))
+ NamingOptionsPage.resize(QtCore.QSize(QtCore.QRect(0,0,396,519).size()).expandedTo(NamingOptionsPage.minimumSizeHint()))
self.vboxlayout = QtGui.QVBoxLayout(NamingOptionsPage)
self.vboxlayout.setMargin(9)
@@ -102,8 +102,52 @@ class Ui_NamingOptionsPage(object):
self.vboxlayout1.addWidget(self.delete_empty_dirs)
self.vboxlayout.addWidget(self.move_files)
- spacerItem = QtGui.QSpacerItem(330,20,QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Expanding)
- self.vboxlayout.addItem(spacerItem)
+ self.groupBox = QtGui.QGroupBox(NamingOptionsPage)
+ self.groupBox.setObjectName("groupBox")
+
+ self.gridlayout1 = QtGui.QGridLayout(self.groupBox)
+ self.gridlayout1.setMargin(9)
+ self.gridlayout1.setSpacing(2)
+ self.gridlayout1.setObjectName("gridlayout1")
+
+ self.label = QtGui.QLabel(self.groupBox)
+ self.label.setObjectName("label")
+ self.gridlayout1.addWidget(self.label,0,0,1,2)
+
+ self.example_va_filename = QtGui.QLabel(self.groupBox)
+
+ font = QtGui.QFont(self.example_va_filename.font())
+ font.setItalic(True)
+ self.example_va_filename.setFont(font)
+ self.example_va_filename.setTextFormat(QtCore.Qt.PlainText)
+ self.example_va_filename.setWordWrap(True)
+ self.example_va_filename.setObjectName("example_va_filename")
+ self.gridlayout1.addWidget(self.example_va_filename,3,0,1,2)
+
+ self.label_5 = QtGui.QLabel(self.groupBox)
+ self.label_5.setObjectName("label_5")
+ self.gridlayout1.addWidget(self.label_5,2,0,1,2)
+
+ self.example_filename = QtGui.QLabel(self.groupBox)
+
+ font = QtGui.QFont(self.example_filename.font())
+ font.setItalic(True)
+ self.example_filename.setFont(font)
+ self.example_filename.setTextFormat(QtCore.Qt.PlainText)
+ self.example_filename.setWordWrap(True)
+ self.example_filename.setObjectName("example_filename")
+ self.gridlayout1.addWidget(self.example_filename,1,0,1,2)
+
+ spacerItem = QtGui.QSpacerItem(301,20,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Minimum)
+ self.gridlayout1.addItem(spacerItem,4,1,1,1)
+
+ self.test_button = QtGui.QPushButton(self.groupBox)
+ self.test_button.setObjectName("test_button")
+ self.gridlayout1.addWidget(self.test_button,4,0,1,1)
+ self.vboxlayout.addWidget(self.groupBox)
+
+ spacerItem1 = QtGui.QSpacerItem(311,20,QtGui.QSizePolicy.Minimum,QtGui.QSizePolicy.Expanding)
+ self.vboxlayout.addItem(spacerItem1)
self.label_4.setBuddy(self.va_file_naming_format)
self.label_3.setBuddy(self.file_naming_format)
self.label_2.setBuddy(self.move_files_to_browse)
@@ -131,4 +175,8 @@ class Ui_NamingOptionsPage(object):
self.move_files_to_browse.setText(_(u"Browse..."))
self.move_additional_files.setText(_(u"Move additional files:"))
self.delete_empty_dirs.setText(_(u"Delete empty directories"))
+ self.groupBox.setTitle(_(u"Example"))
+ self.label.setText(_(u"File name:"))
+ self.label_5.setText(_(u"Multiple artist file name:"))
+ self.test_button.setText(_(u"&Test"))
diff --git a/ui/options_naming.ui b/ui/options_naming.ui
index 0a9140a5a..cdf4aa27d 100644
--- a/ui/options_naming.ui
+++ b/ui/options_naming.ui
@@ -5,8 +5,8 @@
0
0
- 348
- 397
+ 396
+ 519
@@ -153,6 +153,91 @@
+ -
+
+
+ Example
+
+
+
+ 9
+
+
+ 2
+
+
-
+
+
+ File name:
+
+
+
+ -
+
+
+
+ true
+
+
+
+
+
+
+ Qt::PlainText
+
+
+ true
+
+
+
+ -
+
+
+ Multiple artist file name:
+
+
+
+ -
+
+
+
+ true
+
+
+
+
+
+
+ Qt::PlainText
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 301
+ 20
+
+
+
+
+ -
+
+
+ &Test
+
+
+
+
+
+
-
@@ -160,7 +245,7 @@
- 330
+ 311
20