Allow limited bundling of plugins. After core-upgrade, merge this. Removes obsolete plugins. Merge installer-new AFTER.

This commit is contained in:
Carlin Mangar
2009-10-20 17:42:57 +11:00
parent 3dbc2864c0
commit 5d44f0306b
15 changed files with 802 additions and 97 deletions

View File

@@ -3,14 +3,17 @@
PLUGIN_NAME = u"Add Cluster As Release"
PLUGIN_AUTHOR = u"Lukáš Lalinský"
PLUGIN_DESCRIPTION = ""
PLUGIN_VERSION = "0.1"
PLUGIN_API_VERSIONS = ["0.9.0", "0.10"]
PLUGIN_VERSION = "0.2"
PLUGIN_API_VERSIONS = ["0.9.0", "0.10", "0.11", "0.12"]
# Hacked to popup an ugly dialog (to enable copy+pasting to non-IE browsers) for URLs longer than 2083
# characters to get around a Windows path limitation. Probably shouldn't be used on other platforms.
from PyQt4 import QtCore
from PyQt4 import QtCore, QtGui, Qt
from picard.cluster import Cluster
from picard.util import webbrowser2, format_time
from picard.ui.itemviews import BaseAction, register_cluster_action
import sys
class AddClusterAsRelease(BaseAction):
@@ -45,8 +48,40 @@ class AddClusterAsRelease(BaseAction):
url += "&tr%d_artistedit=1" % i
url += "&tr%d_artistname=%s" % (i, QtCore.QUrl.toPercentEncoding(file.metadata["artist"]))
url += "&tracks=%d" % tracks
webbrowser2.open(url)
# Chad Wilson: This is a windows-specific hack, and probably shouldn't be done on other systems. I'm lazy for now.
# Carlin Mangar: I put in a condition for win32
self.log.info(url)
if (len(url) <= 2048) and sys.platform =="win32":
webbrowser2.open(url)
else:
global w
w = AddClusterViewUrl()
w.addText(url)
w.show()
class AddClusterViewUrl(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.setWindowTitle(_("Add Cluster URL - Please copy and paste"))
self.doc = QtGui.QTextDocument(self)
self.textCursor = QtGui.QTextCursor(self.doc)
font = QtGui.QFont()
font.setFixedPitch(True)
font.setPointSize(8)
font.setWeight(QtGui.QFont.Normal)
font.setFamily("")
self.textFormat = QtGui.QTextCharFormat()
self.textFormat.setFont(font)
self.browser = QtGui.QTextBrowser(self)
self.browser.setDocument(self.doc)
vbox = QtGui.QHBoxLayout(self)
vbox.addWidget(self.browser)
def addText(self, text):
self.textCursor.insertText(text, self.textFormat)
self.textCursor.insertBlock()
#w = AddClusterViewUrl()
register_cluster_action(AddClusterAsRelease())

View File

@@ -0,0 +1,183 @@
"""
A small plugin to download cover art for any releseas that have a
CoverArtLink relation.
Changelog:
[2009-10-11] Added support for Amazon's new api and possibility for configuration
NXisGOD
[2008-04-15] Refactored the code to be similar to the server code (hartzell, phw)
[2008-03-10] Added CDBaby support (phw)
[2007-09-06] Added Jamendo support (phw)
[2007-04-24] Moved parsing code into here
Swapped to QUrl
Moved to a list of urls
[2007-04-23] Moved it to use the bzr picard
Took the hack out
Added Amazon ASIN support
[2007-04-23] Initial plugin, uses a hack that relies on Python being
installed and musicbrainz2 for the query.
"""
PLUGIN_NAME = 'Cover Art Plus'
PLUGIN_AUTHOR = 'Carlin Mangar, Oliver Charles, Philipp Wolfer, Lukas Lalinksy'
PLUGIN_DESCRIPTION = '''Downloads cover artwork for releases that have a
CoverArtLink. Now uses the new Amazon API to get cover art.'''
PLUGIN_VERSION = "0.6"
PLUGIN_API_VERSIONS = ["0.12"]
from PyQt4.QtCore import QUrl
from picard.metadata import register_album_metadata_processor
from picard.util import partial, mimetype
from picard.webservice import *
from picard.tagger import Tagger
import re
import datetime
#
# data transliterated from the perl stuff used to find cover art for the
# musicbrainz server.
# See mb_server/cgi-bin/MusicBrainz/Server/CoverArt.pm
# hartzell --- Tue Apr 15 15:25:58 PDT 2008
coverArtSites = [
# CD-Baby
# tested with http://musicbrainz.org/release/1243cc17-b9f7-48bd-a536-b10d2013c938.html
{
'regexp': 'http://cdbaby.com/cd/(\w)(\w)(\w*)',
'imguri': 'http://cdbaby.name/$1/$2/$1$2$3.jpg',
},
# Jamendo
# tested with http://musicbrainz.org/release/2fe63977-bda9-45da-8184-25a4e7af8da7.html
{
'regexp': 'http:\/\/(?:www.)?jamendo.com\/(?:[a-z]+\/)?album\/([0-9]+)',
'imguri': 'http://www.jamendo.com/get/album/id/album/artworkurl/redirect/$1/?artwork_size=0',
},
]
_AMAZON_IMAGE_HOST = 'images.amazon.com'
_AMAZON_IMAGE_PATH = '/images/P/%s.01.LZZZZZZZ.jpg'
_AMAZON_IMAGE_PATH_SMALL = '/images/P/%s.01.MZZZZZZZ.jpg'
_AMAZON_IMAGE_PATH2 = '/images/P/%s.02.LZZZZZZZ.jpg'
_AMAZON_IMAGE_PATH2_SMALL = '/images/P/%s.02.MZZZZZZZ.jpg'
AMAZON_API_LOCATOR = 'free.apisigning.com'
AMAZON_XML="/onca/xml"
ACCESS_KEY='AKIAJU7ZJ3PGRVJG6LSA'
AMAZON_SIGNATURE=''
_re_fixdate=re.compile(":")
_re_detecturl=re.compile(r"(?<=\<LargeImage><URL>)https?://(\w*:\w*@)?[-\w.]+(:\d+)?(/([\w/_.]*(\?\S+)?)?)?(?=</URL>)")
def _coverart_downloaded(album, metadata, release, try_list, data, http, error):
try:
if error or len(data) < 1000:
if error:
album.log.error(str(http.errorString()))
coverart(album, metadata, release, try_list)
else:
mime = mimetype.get_from_data(data, "image/jpeg")
metadata.add_image(mime, data)
for track in album._new_tracks:
track.metadata.add_image(mime, data)
finally:
album._requests -= 1
album._finalize_loading(None)
def coverart(album, metadata, release, try_list=None):
""" Gets the CDBaby URL from the metadata, and the attempts to
download the album art. """
# try_list will be None for the first call
if try_list is None:
try_list = []
try:
for relation_list in release.relation_list:
if relation_list.target_type == 'Url':
for relation in relation_list.relation:
# Search for cover art on special sites
for site in coverArtSites:
#
# this loop transliterated from the perl stuff used to find cover art for the
# musicbrainz server.
# See mb_server/cgi-bin/MusicBrainz/Server/CoverArt.pm
# hartzell --- Tue Apr 15 15:25:58 PDT 2008
match = re.match(site['regexp'], relation.target)
if match != None:
imgURI = site['imguri']
for i in range(1, len(match.groups())+1 ):
if match.group(i) != None:
imgURI = imgURI.replace('$' + str(i), match.group(i))
_try_list_append_image_url(try_list, QUrl.fromEncoded(str(imgURI)))
# Use the URL of a cover art link directly
if relation.type == 'CoverArtLink':
_try_list_append_image_url(try_list, QUrl.fromEncoded(str(relation.target)))
except AttributeError:
pass
if metadata['asin']:
try_list.append({'host': _AMAZON_IMAGE_HOST, 'port': 80,
'path': _AMAZON_IMAGE_PATH % metadata['asin']
})
try_list.append({'host': _AMAZON_IMAGE_HOST, 'port': 80,
'path': _AMAZON_IMAGE_PATH_SMALL % metadata['asin']
})
try_list.append({'host': _AMAZON_IMAGE_HOST, 'port': 80,
'path': _AMAZON_IMAGE_PATH2 % metadata['asin']
})
try_list.append({'host': _AMAZON_IMAGE_HOST, 'port': 80,
'path': _AMAZON_IMAGE_PATH2_SMALL % metadata['asin']
})
path = '%s?Service=AWSECommerceService&AWSAccessKeyId=%s&ItemId=%s&Operation=ItemLookup&ResponseGroup=Images&Timestamp=%s&Version=%s' \
% ( AMAZON_XML, ACCESS_KEY, metadata['asin'], _re_fixdate.sub("%3A",datetime.datetime.now().isoformat()[:23]+"Z"),datetime.date.today().isoformat())
album._requests += 1
album.tagger.xmlws.get(AMAZON_API_LOCATOR, 80, path,
partial(_url_downloaded, album, metadata, release, try_list))
if len(try_list) > 0:
# We still have some items to try!
album._requests += 1
album.tagger.xmlws.download(
try_list[0]['host'], try_list[0]['port'], try_list[0]['path'],
partial(_coverart_downloaded, album, metadata, release, try_list[1:]),
position=1)
def _parse_image_url(parsed_url):
path = parsed_url.path()
if parsed_url.hasQuery():
path += '?' + location.encodedQuery()
return {
'host': str(parsed_url.host()),
'port': parsed_url.port(80),
'path': str(path)
}
def _try_list_append_image_url(try_list, parsed_url):
try_list.append(_parse_image_url(parsed_url))
def _url_downloaded(album, metadata, release, try_list, data, http, error):
album._requests -= 1
if data:
try:
url = data.ItemLookupResponse[0].Items[0].Item[0].LargeImage[0].URL[0].text
try_list.insert(0, _parse_image_url(QUrl.fromEncoded(str(url))))
except AttributeError:
try:
url = data.ItemLookupResponse[0].Items[0].Item[0].MediumImage[0].URL[0].text
try_list.insert(0, _parse_image_url(QUrl.fromEncoded(str(url))))
except AttributeError:
pass
coverart(album, metadata, release, try_list)
register_album_metadata_processor(coverart)

View File

@@ -1,18 +0,0 @@
PLUGIN_NAME = 'First alphabetic character function'
PLUGIN_AUTHOR = 'Philipp Wolfer'
PLUGIN_DESCRIPTION = 'Provides the tagger script function $firstalphachar(text, nonalpha=#).'
PLUGIN_VERSION = "0.1"
PLUGIN_API_VERSIONS = ["0.9.0", "0.10", "0.11", "0.12"]
from picard.script import register_script_function
def firstalphachar(parser, text, nonalpha="#"):
if len(text) == 0:
return nonalpha
firstchar = text[0]
if firstchar.isalpha():
return firstchar.upper()
else:
return nonalpha
register_script_function(firstalphachar)

View File

@@ -1,12 +0,0 @@
PLUGIN_NAME = 'Initials'
PLUGIN_AUTHOR = 'Lukas Lalinsky'
PLUGIN_DESCRIPTION = 'Provides tagger script function $initials(text).'
PLUGIN_VERSION = "0.1"
PLUGIN_API_VERSIONS = ["0.9.0", "0.10"]
from picard.script import register_script_function
def initials(parser, text):
return "".join(a[:1] for a in text.split(" ") if a[:1].isalpha())
register_script_function(initials)

View File

@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
#
# Built in Player for Picard
# Copyright (C) 2007 Gary van der Merwe
# Copyright (C) 2009 Carlin Mangar
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
PLUGIN_NAME = u"Preview Bar"
PLUGIN_AUTHOR = u"Carlin Mangar, Gary van der Merwe"
PLUGIN_DESCRIPTION = "Adds a cool music preview toolbar to Picard. Note however that you need Picard 0.12 and above to use this and you are limited\
by the codecs you have installed. On linux this plugin depends on Gstreamer natively, \
but there are different backends you can use.;)"
PLUGIN_VERSION = "0.3"
PLUGIN_API_VERSIONS = ["0.12.0"]
from PyQt4 import QtCore
from picard.ui.mainwindow import register_ui_init
from picard.plugins.previewbar.playerbox import PlayerBox
import picard.plugins.previewbar.resources
try:
from PyQt4.phonon import Phonon
except ImportError, e:
Phonon = None
phonon_import_error = e
def create_player_box(mainwindow):
player_box = PlayerBox(mainwindow)
mainwindow.addToolBar(player_box)
player_box.connect(mainwindow, QtCore.SIGNAL("update_selection"),
player_box.updateSelection)
player_box.connect(mainwindow.tagger, QtCore.SIGNAL("file_save"),
player_box.file_save)
player_box.connect(mainwindow.tagger, QtCore.SIGNAL("file_saving_finished"),
player_box.AutoPlay)
player_box.connect(mainwindow.tagger, QtCore.SIGNAL("file_state_changed"),
player_box.file_changed)
register_ui_init(create_player_box)

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

View File

@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
#
# Built in Player for Picard
# Copyright (C) 2007 Gary van der Merwe
# Copyright (C) 2009 Carlin Mangar
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from PyQt4 import QtCore, QtGui
try:
from PyQt4.phonon import Phonon
except ImportError, e:
Phonon = None
phonon_import_error = e
from picard.util import icontheme
from picard.file import File
from picard.track import Track
nano_to_milli = 1000000L
slider_acc = 100 #1/10 of a sec
class PlayerBox(QtGui.QToolBar):
def __init__(self, parent):
QtGui.QToolBar.__init__(self,"&Preview Bar", parent)
self.setObjectName("picard.plugin.player")
if Phonon:
self.auto_play_action = QtGui.QAction(
icontheme.lookup('media-playback-start',icontheme.ICON_SIZE_ALL),
u"Play/Stop", self)
self.auto_play_action.setCheckable(True)
#if self.config.persist["view_cover_art"]:
# self.show_cover_art_action.setChecked(True)
self.connect(self.auto_play_action, QtCore.SIGNAL("triggered()"),
self.onAutoPlayClicked)
self.addAction(self.auto_play_action)
self.volume_slider = Phonon.VolumeSlider(self)
self.seek_slider = Phonon.SeekSlider(self)
self.addWidget(self.seek_slider)
self.addWidget(self.volume_slider)
self.selection = None
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Preferred)
self.volume_slider.setSizePolicy(sizePolicy)
self.volume_slider.setMaximumSize(QtCore.QSize(125, 48))
sizePolicy2 = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.seek_slider.setSizePolicy(sizePolicy2)
self.seek_slider.setMinimumSize(QtCore.QSize(250, 8))
self.media_object = Phonon.MediaObject(self)
self.audio_output = Phonon.AudioOutput(self)
Phonon.createPath(self.media_object, self.audio_output)
self.seek_slider.setMediaObject(self.media_object)
self.volume_slider.setAudioOutput(self.audio_output)
else:
self.addWidget(QtGui.QLabel(("Could not load Phonon. (%s)" %
phonon_import_error), self))
def updateSelection(self, objects):
new_selection = None
if len(objects)>0:
objects = objects[0]
if isinstance(objects, Track):
if len(objects.linked_files) == 1:
new_selection = objects.linked_files[0]
if isinstance(objects, File):
new_selection = objects
if new_selection is not None and not new_selection==self.selection:
self.selection = new_selection
self.AutoPlay()
def file_save(self):
if self.selection:
self.media_object.clear()
def file_changed(self):
if self.selection and self.selection.state==File.REMOVED:
self.media_object.clear()
def AutoPlay(self):
if self.auto_play_action.isChecked():
if isinstance(self.selection, File) and self.selection.state!=File.PENDING:
source = Phonon.MediaSource(self.selection.filename)
self.media_object.setCurrentSource(source)
self.media_object.play()
def onAutoPlayClicked(self):
if self.auto_play_action.isChecked() :
self.AutoPlay()
else:
self.media_object.stop()
self.updateAutoPlayIcon()
def updateAutoPlayIcon(self):
if self.auto_play_action.isChecked():
self.auto_play_action.setIcon(icontheme.lookup('media-playback-stop',
icontheme.ICON_SIZE_ALL))
else:
self.auto_play_action.setIcon(icontheme.lookup('media-playback-start',
icontheme.ICON_SIZE_ALL))

View File

@@ -0,0 +1,235 @@
# -*- coding: utf-8 -*-
# Resource object code
#
# Created: Fri 2. Oct 21:15:21 2009
# by: The Resource Compiler for PyQt (Qt v4.5.2)
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore
qt_resource_data = "\
\x00\x00\x02\x94\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\
\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\
\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\
\xd5\x0c\x06\x11\x24\x21\xce\xd9\xad\x5e\x00\x00\x02\x21\x49\x44\
\x41\x54\x38\xcb\x9d\x93\x4b\x6b\x13\x01\x14\x85\xcf\x3c\x32\xc9\
\x24\x4d\x26\x31\x99\x69\x62\x13\x13\x5a\x49\x30\x23\xba\x4a\xd1\
\xd4\x48\x52\x6c\x8a\x05\xc1\x67\x74\x27\x58\x10\xc1\x42\xb6\xdd\
\xf9\x07\x5c\xf4\x17\x48\x97\x82\x2f\xba\x72\xa3\x88\x20\x2e\x0c\
\x42\x4d\x15\xb1\x58\x6c\xa0\x11\x4a\xfa\xd0\xa4\x33\x4d\x32\x33\
\xb9\x2e\xc4\xa6\x62\x29\xb1\x77\x75\xee\xe6\x83\x73\xef\x39\x40\
\x77\x78\x1c\x60\xb8\x5d\x3a\x02\xc0\x01\xa0\x09\x80\x7a\x05\xb0\
\xbb\x97\xfc\xf8\xd8\xeb\x60\x30\x98\x06\xe0\xeb\x15\xc0\xfc\x11\
\x8a\x2c\x1f\x19\x3e\x95\xfa\xc6\x30\x4c\xc7\xb2\xcc\x2f\xe5\x0f\
\x9f\x6e\xb6\x5a\xad\xcf\xb5\x5a\x4d\xef\xc9\x42\x34\x16\x93\xbc\
\x3e\xa9\xf8\xec\xe9\x1c\xef\x91\x3c\xfe\x6a\x75\x65\xb2\xbf\x5f\
\x19\xe9\x30\xf4\x66\x20\x34\x50\xaf\xd5\x6a\xb4\x2f\x20\xa9\xaa\
\x92\x28\x3a\x8a\xd7\x0b\x37\xd8\x48\x64\x80\x9d\x38\x3f\xc1\xf5\
\xb9\xdd\xe1\x8d\xf5\xf5\xdb\xa2\x53\x4c\x3a\x45\xd7\xbb\x64\xf2\
\x58\x63\x79\xb9\xb2\x37\x60\x38\x95\x92\x38\x8e\x29\x5e\xbd\x72\
\x8d\x05\x01\xae\x3e\x37\xd4\xa4\xca\xa5\x4f\xa7\x05\x97\x53\x4c\
\xfc\xa8\xff\x9c\x34\x2d\x6b\x75\x68\x70\xa8\xb2\xb4\xb4\xd4\xfc\
\x07\x90\xcb\x66\xa5\xb6\xd9\x2e\x5e\xba\x78\x99\xdd\xd2\xeb\x30\
\x8c\x36\x0c\xa3\x0d\xbb\xc3\x8e\x78\x22\xc1\xe5\xf3\xe3\x0e\x22\
\xba\x40\x30\x53\x89\x78\xfc\x7d\xe6\x4c\xa6\x51\x2a\x95\xcc\x9d\
\xdf\x07\x64\x05\x0d\xad\x0e\x00\xd0\x74\x0d\x20\x02\x01\xb0\xf1\
\x36\x78\x3d\x3e\xe8\xa4\x83\xc8\x62\xfc\x87\xfc\x86\xd7\x13\x90\
\x78\x8e\x75\x03\x68\xee\x00\x14\x45\x46\xf5\xfb\x6f\x7f\xba\xbe\
\x05\x9b\xcd\x8e\xc3\xc1\x30\x2c\xd3\xc4\xec\xec\x03\x6b\xf1\xeb\
\xe2\x66\x2c\x16\xbd\xa3\x04\x42\x6f\x01\x5a\xbb\x3b\x35\x65\xfc\
\x95\x3e\x59\x96\xc9\x26\x08\x00\x08\xd1\xf0\x20\x78\x9e\xc7\xe3\
\x27\x8f\xac\x85\x8f\x0b\x46\x3c\x71\xf4\x5e\x66\x24\xfb\xb0\xd3\
\xe9\xac\x16\x0a\x85\x16\xf6\x8a\xaf\x2c\x2b\x10\x04\x1b\x75\x88\
\xf0\xf2\xd5\x0b\x2a\x97\xe7\x2d\xb7\xc7\xf3\x3c\x7b\x76\x74\x5a\
\x10\xec\x95\x5c\x2e\xab\xed\xf5\xc6\x2e\x40\x09\x40\x14\xed\x34\
\x33\x73\xdf\xd2\x74\x6d\x65\x34\x3b\x7a\x2b\x20\x87\xe6\x8f\xab\
\xea\xc6\x7e\x41\xea\xde\x20\x20\x63\x7b\xbb\xb5\x79\x6e\x2c\x35\
\x7d\x42\x3d\x39\x07\x30\x9b\x8a\xa2\x50\xcf\x51\x26\xa2\x30\x00\
\x03\xc0\x1a\xc3\x30\xd6\x7f\xd7\x92\x88\xd8\x83\xd4\xf9\x17\x30\
\x46\xc1\x1f\x41\xe0\x4e\x62\x00\x00\x00\x00\x49\x45\x4e\x44\xae\
\x42\x60\x82\
\x00\x00\x01\xad\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\
\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\
\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\
\xd5\x0c\x06\x11\x28\x2e\xf2\xd3\xff\xc3\x00\x00\x01\x3a\x49\x44\
\x41\x54\x38\xcb\xcd\x92\x5f\x4a\x03\x31\x10\x87\xbf\xec\xa4\xaf\
\x16\x0a\x66\x3c\x80\x58\xf0\x14\x22\x1e\x42\xc4\x4b\x08\xe2\x89\
\x44\xbc\x82\x88\xd7\x10\x54\x04\x45\x29\x8a\x3e\xac\x2c\xb4\xbb\
\xdd\xdd\xc4\x87\xfd\xab\xad\x8a\x4f\x1a\x08\xcc\x64\x32\x5f\xe6\
\x37\x19\xf8\xeb\x65\x7a\xf6\xc6\x2f\x73\x6f\x80\x60\x1b\x6f\x34\
\x1a\xb1\xbd\xb3\x75\x69\x8c\x91\x9f\x32\xcf\xcf\x2e\xc6\x71\x1c\
\x9b\x0f\x00\x75\x8e\xc1\x60\x60\x4e\x8e\x4f\xbf\x4d\xde\xdb\xdf\
\xf5\xaa\x4a\x1c\xc7\x00\x44\x4d\x60\xd5\x29\x22\xd5\xe3\xb7\x77\
\x57\x3c\x4e\xee\x79\x79\x7d\xe6\x2d\x89\x49\xb3\x14\xef\x3d\x00\
\xd6\x5a\x9c\x6a\x0b\x6c\x01\xaa\x1d\x40\xc4\xd6\x5b\x90\x48\x30\
\xc0\x3c\xcf\xea\x98\xa0\xce\x2d\x02\x9c\xba\x16\x10\x45\x02\x06\
\x7c\x08\x14\x65\x41\x9a\xa5\xcc\x66\xd3\x0a\x60\x05\xb7\x0c\xb0\
\xd6\xab\x20\x04\x4f\x59\x96\x14\xc5\x9c\x2c\x4b\x99\xa5\x53\xa6\
\x69\x05\xb0\x22\x68\x4f\x82\xed\x7a\xe0\x98\x3c\x3d\x00\x50\x96\
\x25\xde\x7b\xca\xa2\xe2\xfb\x1a\xd8\xc8\x73\xea\x16\x01\xea\x3a\
\x09\x79\x9e\x13\x08\x18\x20\x10\xf0\xde\xb7\x4d\x14\x2b\xa8\x5b\
\x52\x81\xf6\x7a\x30\x5e\xdf\xfc\xf2\x1b\xa5\x93\x10\x3e\x55\xa0\
\x88\x48\x38\x3c\x3a\xf8\x76\x0e\x24\x8a\x22\xe7\x3a\x40\x3b\xca\
\x49\x92\xd4\xa3\x1c\xaa\xe3\x50\xd9\xc1\x54\x42\x2a\xbf\xb9\x11\
\x18\xae\x0c\xaf\xf9\x17\xeb\x1d\x29\x6c\x75\xb8\xda\x45\x87\xce\
\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x04\x04\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
\x00\x00\x03\xbb\x49\x44\x41\x54\x58\x85\xed\x96\x4f\x68\x5c\x45\
\x1c\xc7\xbf\xbf\x99\xf7\xde\x26\x98\xdd\x4d\xba\x59\xb3\x66\x77\
\x9b\xcd\xd6\x1c\x34\xc1\x43\xd8\x62\xac\xc6\x9e\x02\x6d\x12\xe3\
\x25\x35\xe9\xa1\x10\x8a\x20\x05\x8b\x07\x11\x5b\xa9\x24\xa2\x87\
\x6a\x34\xb9\x15\xa4\x2d\xf6\x14\x91\x1c\x2c\x5a\x10\xa9\x60\x8a\
\x28\x0a\x5e\x5a\x28\x42\xa8\xca\x66\x4b\x92\x6e\xf6\x6d\xdc\x34\
\xc9\xfb\xb7\x33\xe3\x61\x37\x8a\x10\x36\xdb\x98\xc6\x83\xf9\xc2\
\x9c\xe6\xbd\xf9\x7d\xde\x77\x7e\xf3\x9d\x07\xec\x69\x4f\xff\xb1\
\xf8\x16\xf3\xcd\x00\x1a\x00\x78\xe5\xb1\xeb\x00\x91\xa3\x7d\x3d\
\x1f\x19\xba\x66\x2e\x2d\x99\x16\x00\x07\x80\xd8\x4d\x80\x50\x32\
\xd9\x7a\xa1\xbd\xbd\xe3\xa5\xe6\xe8\x63\xad\xcb\xf9\x3f\x66\xd7\
\xd7\xd7\x05\x00\x1b\x80\xda\x15\x80\x03\x8f\x27\x4f\x4f\x4d\x7d\
\xc6\x5b\x5a\x12\x4f\x2c\x2e\xce\x9f\x88\x34\x37\x61\x61\x7e\x71\
\x4e\x08\x21\x51\x72\xe4\xe1\x03\x0c\x0e\x1e\x43\x43\xa8\x9e\x06\
\x06\x06\x74\xc3\xd0\xbb\xa4\x12\xc7\x02\xfe\xba\xf4\xfc\xfc\xc2\
\x0a\x80\x22\x00\xf7\xa1\x02\x0c\x0d\x0d\xc3\x75\x1d\x28\x25\xd1\
\xde\xd1\xc1\x8f\x1c\x39\xea\x5f\xca\xe5\xfa\x43\xfb\xea\x9f\x53\
\x4a\xcc\x9a\xe6\xb2\x8b\x6d\xf6\x47\xd5\x00\x9e\x57\xfa\x48\x29\
\x25\x38\x67\xe8\xee\x3e\xcc\x53\xa9\x83\xb1\x4c\x26\x33\xdc\x1c\
\x6b\x7e\x34\xb7\x94\xbb\x63\xdb\x36\xca\x20\x72\x47\x00\x02\x81\
\x40\x28\x16\x8f\xfe\x03\x60\x43\x52\x0a\xf8\x03\x7e\xf4\xf5\xf5\
\xf3\xfd\xf1\xf8\x53\x39\x33\x7b\x22\xd2\xd4\x64\xe5\xf3\xcb\x77\
\x7d\x3e\x1f\x1c\xc7\xb1\xff\x35\x40\x32\x99\x0c\x05\x83\xfe\xd3\
\x43\xc3\xc7\x51\x14\x45\x30\x22\x10\xb1\xbf\x07\x08\x52\x0a\xc4\
\xa2\x31\xea\x7f\x61\xc0\x00\x54\x37\x41\xbc\x58\x17\x0c\xfe\x5a\
\x5b\x53\xbb\xde\xd8\xd8\x28\x4c\xd3\xac\xd8\x1f\x54\x69\xb2\xa7\
\xa7\xa7\x8d\x98\x9c\xbd\x7a\xf5\x0b\x58\xb6\x05\xa2\x8a\x8f\x83\
\x33\x8e\xfb\xab\xab\x18\xff\xe0\xbc\x37\x97\xce\x7c\x7f\xdf\x5a\
\x7b\x9b\x49\x96\x76\x5d\x77\x69\x66\x66\x66\x53\x47\x58\xa5\x05\
\xa3\xd1\x68\x89\x92\x18\x38\xe7\xe0\x8c\x83\x73\x0e\xc6\x4a\x0e\
\x28\xa5\x20\xa5\x80\x57\xf4\x60\x3b\x36\x56\x56\x0b\x90\xaa\x88\
\xd1\xd1\x77\xf4\xd1\xb1\xb1\xc3\xc1\xba\xba\xef\x02\xf5\x75\xaf\
\x25\x12\x89\xf8\xc8\xc8\x48\xcd\x03\x03\x84\xc3\x61\x00\x80\x52\
\x12\x96\x6d\xc1\x76\x2c\xd8\xb6\x05\xc7\x75\xe0\x79\x0e\x84\x28\
\x42\x48\x09\x28\x05\x46\x04\x8d\x6b\xf0\xf9\x6a\xc0\x19\xc7\xc2\
\xfc\x82\xb4\x2c\x0b\x9c\x58\x3a\x1c\x0e\x63\x6d\x6d\x6d\xd3\x28\
\xd7\x2a\x01\xc4\x62\x31\xdc\xfe\xe5\x16\x88\x18\x34\xae\x81\xb1\
\x8d\x1e\x20\x10\xa8\xb4\x81\x0a\x50\xe5\x50\xd4\x35\x03\x99\xcc\
\x1c\x26\x26\xc7\x5d\xc7\x71\x3f\x7d\xf6\xd0\x33\x13\x7e\xff\xbe\
\x85\x7c\x3e\x6f\x4e\x4f\x4f\x6f\x7a\x32\x2a\x02\x44\xa3\x51\x18\
\xba\x01\x22\xfa\x6b\x0b\x18\x63\x20\xc6\xc0\x88\xb0\xd1\x42\x9c\
\x73\x14\x0a\x05\x4c\x4e\x4e\x78\x77\xe7\x32\x3f\x27\x12\xfb\xcf\
\xb6\xb5\x3d\xf9\x5b\x30\x18\xcc\xf6\xf6\xf6\x56\x4c\xcb\x2d\x1d\
\xd0\x75\x1d\x04\x80\x11\x03\x31\x02\x63\x0c\xac\xdc\x0b\x9c\x73\
\x58\xeb\x16\xae\x5d\xfb\xd2\xbb\x7e\xfd\xeb\x6c\xb8\x31\x7c\xe6\
\xd4\xa9\x57\x7f\x10\x42\xe4\xba\xba\xba\x56\x2a\xad\x5d\xbd\x03\
\x86\x0e\x05\x40\x2a\x09\x92\x04\x09\x09\xc6\x18\xa4\x90\xb8\x75\
\xf3\xa6\xbc\xfc\xc9\x45\xbb\xbe\xbe\xe1\xc3\xb3\x67\xce\x4d\xd5\
\xd6\xd6\xe6\x23\x91\x88\x49\x44\x55\x07\x51\x55\x0e\x28\xa5\x20\
\x44\xb1\xf4\x82\xa6\x23\x7b\xef\x1e\x2e\x5e\xfa\xd8\x55\x0a\x9f\
\xbf\x7c\xf2\x95\xf1\xce\xce\xce\x2c\x80\x2c\x11\x3d\xf0\xe5\x54\
\x11\x00\x00\x0c\xc3\x80\x52\x12\x9a\xa6\x43\x0a\x85\x2b\x57\x2e\
\x7b\xe9\xf4\xef\xb7\x0f\xa6\x9e\x7e\x73\x70\x70\xe8\x0e\x80\x1c\
\x11\x55\x65\xf7\xb6\x00\x74\x5d\x07\xe7\x1a\x7e\xfa\xf1\x46\xf1\
\xdb\x99\x6f\xcc\x78\xbc\xe5\xdc\xfb\xe7\x27\x6e\x00\x28\x94\x8b\
\x57\x6d\xf7\xf6\x00\x0c\x1d\xef\xbe\x37\x66\xfb\x1f\xf1\x5f\x78\
\xe3\xf5\xb7\x2e\x85\x42\xa1\x55\x6c\xd3\xee\x6d\x01\xb4\xb6\xb6\
\x8e\x1d\xea\x7a\xfe\xab\x54\x2a\x95\x03\x60\x12\x51\x61\x27\x0a\
\x6f\xa8\x62\xb8\x2b\xa5\x0e\xa0\x94\x96\x85\x72\xf1\x1d\xfd\x1f\
\xdc\x52\x4a\x29\x5d\x29\xe5\xdb\xd5\xa2\x7b\xfa\xdf\xe9\x4f\x7a\
\x2a\x7c\x95\x17\xe9\x5e\xd9\x00\x00\x00\x00\x49\x45\x4e\x44\xae\
\x42\x60\x82\
\x00\x00\x02\x3b\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\
\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
\x00\x00\x01\xf2\x49\x44\x41\x54\x58\x85\xed\x96\xed\x8a\xd3\x40\
\x14\x86\x9f\xa4\x49\xdb\x93\x04\xb2\xa1\x0d\xb1\x8b\x7f\xbd\x22\
\xbf\xf0\xe6\x44\x14\x05\x41\x6f\xc8\x0b\x58\x64\x17\x84\xed\xe2\
\x07\xdd\xcc\x79\xfd\x91\xb6\xdb\xa2\xdb\x46\x03\xfe\xb1\x2f\x0c\
\x99\x21\x73\xe6\x7d\x32\xe7\xcc\x10\x38\xe9\xa4\x93\xfe\x77\x45\
\x3d\xe6\x9c\x03\xf9\x00\x8f\xaf\xc0\xc5\x7d\x2f\x93\x1e\x0b\x64\
\x4f\x9e\x3f\xfe\x94\x24\xc9\xea\x4f\x9d\xdb\xb6\x1d\x7f\x78\xff\
\xf1\xd1\xa1\x39\x47\x01\xca\xb2\x14\xc0\xdb\xd7\xef\xc6\xee\x8e\
\xbb\xf7\x32\x4f\xd3\x94\x67\x2f\x9e\x52\x96\xa5\xae\xaf\xaf\x87\
\x03\x48\xe2\xf2\xea\x33\xae\x7e\x00\xe7\x0f\x1e\x6e\xe3\x0f\x01\
\xc4\xc7\x16\x3a\x3b\x3b\x13\x40\xf0\x80\xcb\x91\xf7\x6c\x6b\xd0\
\x4d\xfc\x5f\x03\xcc\xe7\x73\x87\x6e\x07\xfe\x44\xbe\x9e\xbf\x89\
\xbf\x4f\x47\x53\x30\x9b\xcd\x24\x1c\x49\xc4\x51\x8c\xef\x22\x6b\
\xbf\x73\x37\xd4\xb6\x56\x66\xb3\xd9\x41\xf2\xa3\x00\x45\x51\xe8\
\xe6\xdb\x12\x24\xe2\x38\x26\xd2\xfe\xc9\xd5\xc6\x76\xc7\xa6\x0d\
\x2d\x5a\x03\x14\x45\x31\x0c\xa0\xae\x6b\xfd\xb8\xf8\x0e\xc0\x68\
\x34\xda\xa6\x42\x12\x21\xb4\x04\x77\x42\x68\x69\x43\xc0\x43\x4b\
\x1b\x9c\x08\xc1\xbc\x21\x4d\x53\xea\xba\x1e\x06\x90\x65\x99\x9b\
\x19\xee\xe2\xb6\x6d\x09\xa1\x45\xc1\x09\x0a\x5b\x10\xe8\x6e\xb4\
\x51\x3c\x22\x8e\x3b\x48\x49\x98\x19\x59\x96\x0d\xab\x81\xc5\x62\
\xa1\xab\x2f\x97\x44\x71\x44\x44\x57\x07\x24\x11\xb1\x46\xdd\xf6\
\xab\x4b\x82\xb4\xe9\x77\xe6\xa2\x03\x58\x2c\x16\xc3\x76\xa0\xaa\
\x2a\x99\x4d\x89\x22\x48\x92\x31\x92\xaf\xbf\x10\xd8\x98\xe9\xce\
\x74\xd3\x47\x60\x36\xa5\xaa\xaa\x61\x00\x65\x59\xba\x99\x11\x11\
\x93\x26\x29\xee\xbe\x63\xe4\xfb\x00\x3b\x0d\xc0\xcc\x98\x4c\x26\
\xc3\x00\xcc\x4c\x66\x46\x14\x41\x1c\x77\x67\x50\x12\x8e\xd3\x5d\
\x23\x5a\xb7\xfd\x54\x6f\x6a\xa0\xaa\xaa\x61\x35\xd0\x34\x8d\xcc\
\x0c\x49\x8c\x27\x93\x6d\xce\x7f\xf7\x84\xf5\xb1\x14\xb8\x1c\x33\
\xa3\x69\x9a\x61\x3b\x90\xe7\xb9\xcc\xa6\xbc\x7a\xf3\xf2\xd8\xd4\
\x5f\x64\x36\x25\xcf\xf3\x83\x00\x47\xff\x07\x24\x15\xc0\xb8\xaf\
\xe9\x72\xb9\xdc\x1b\xaf\x56\xab\xdb\xba\xae\x6f\xfa\xc6\x9f\x74\
\xd2\x49\xff\x5c\x3f\x01\x96\xf9\x3b\x3a\x60\x1d\x6f\x05\x00\x00\
\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
"
qt_resource_name = "\
\x00\x06\
\x07\x03\x7d\xc3\
\x00\x69\
\x00\x6d\x00\x61\x00\x67\x00\x65\x00\x73\
\x00\x05\
\x00\x36\x9b\x62\
\x00\x33\
\x00\x32\x00\x78\x00\x33\x00\x32\
\x00\x05\
\x00\x34\xdb\x46\
\x00\x31\
\x00\x36\x00\x78\x00\x31\x00\x36\
\x00\x18\
\x0f\xa4\x86\x47\
\x00\x6d\
\x00\x65\x00\x64\x00\x69\x00\x61\x00\x2d\x00\x70\x00\x6c\x00\x61\x00\x79\x00\x62\x00\x61\x00\x63\x00\x6b\x00\x2d\x00\x73\x00\x74\
\x00\x61\x00\x72\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x17\
\x09\x10\x6a\x47\
\x00\x6d\
\x00\x65\x00\x64\x00\x69\x00\x61\x00\x2d\x00\x70\x00\x6c\x00\x61\x00\x79\x00\x62\x00\x61\x00\x63\x00\x6b\x00\x2d\x00\x73\x00\x74\
\x00\x6f\x00\x70\x00\x2e\x00\x70\x00\x6e\x00\x67\
"
qt_resource_struct = "\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
\x00\x00\x00\x22\x00\x02\x00\x00\x00\x02\x00\x00\x00\x06\
\x00\x00\x00\x12\x00\x02\x00\x00\x00\x02\x00\x00\x00\x04\
\x00\x00\x00\x68\x00\x00\x00\x00\x00\x01\x00\x00\x08\x51\
\x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x04\x49\
\x00\x00\x00\x68\x00\x00\x00\x00\x00\x01\x00\x00\x02\x98\
\x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
"
def qInitResources():
QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
def qCleanupResources():
QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
qInitResources()

View File

@@ -0,0 +1,8 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>images/32x32/media-playback-start.png</file>
<file>images/32x32/media-playback-stop.png</file>
<file>images/16x16/media-playback-start.png</file>
<file>images/16x16/media-playback-stop.png</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
#
# Picard, the next-generation MusicBrainz tagger
# Copyright (C) 2009 Carlin Mangar
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# THIS FILE MAKES USE OF:
# titlecase.py v0.2
# Original Perl version by: John Gruber http://daringfireball.net/ 10 May 2008
# Python version by Stuart Colville http://muffinresearch.co.uk
# License: http://www.opensource.org/licenses/mit-license.php
#
# The MIT License
#
# Copyright (c) 2008 John Gruber, Stuart Colville
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Source Code: https://launchpad.net/titlecase.py/trunk/0.2
PLUGIN_NAME = "Advanced Title Case for Picard"
PLUGIN_AUTHOR = "Carlin Mangar, John Gruber, Stuart Colville"
PLUGIN_VERSION = "0.1"
PLUGIN_API_VERSIONS = ["0.9", "0.10", "0.11", "0.12"]
PLUGIN_DESCRIPTION = "This plugin changes all words in title, album and artist to Title Case according \
to the New York Times Manual of Style. Read Source code"
import sys
import re
import unicodedata
from picard.metadata import (
register_track_metadata_processor,
register_album_metadata_processor,
)
SMALL = 'a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v\.?|via|vs\.?'
PUNCT = "[!\"#$%&'‘()*+,-./:;?@[\\\\\\]_`{|}~]"
SMALL_WORDS = re.compile(r'^(%s)$' % SMALL, re.I)
INLINE_PERIOD = re.compile(r'[a-zA-Z][.][a-zA-Z]')
UC_ELSEWHERE = re.compile(r'%s*?[a-zA-Z]+[A-Z]+?' % PUNCT)
CAPFIRST = re.compile(r"^%s*?([A-Za-z])" % PUNCT)
SMALL_FIRST = re.compile(r'^(%s*)(%s)\b' % (PUNCT, SMALL), re.I)
SMALL_LAST = re.compile(r'\b(%s)%s?$' % (SMALL, PUNCT), re.I)
SUBPHRASE = re.compile(r'([:.;?!][ ])(%s)' % SMALL)
def titlecase(text):
"""
Titlecases input text
This filter changes all words to Title Caps, and attempts to be clever
about *un*capitalizing SMALL words like a/an/the in the input.
The list of "SMALL words" which are not capped comes from
the New York Times Manual of Style, plus 'vs' and 'v'.
"""
words = re.split('\s', text)
line = []
for word in words:
if INLINE_PERIOD.search(word) or UC_ELSEWHERE.match(word):
line.append(word)
continue
if SMALL_WORDS.match(word):
line.append(word.lower())
continue
line.append(CAPFIRST.sub(lambda m: m.group(0).upper(), word))
line = " ".join(line)
line = SMALL_FIRST.sub(lambda m: '%s%s' % (
m.group(1),
m.group(2).capitalize()
), line)
line = SMALL_LAST.sub(lambda m: m.group(0).capitalize(), line)
line = SUBPHRASE.sub(lambda m: '%s%s' % (
m.group(1),
m.group(2).capitalize()
), line)
return line
def nx_title_case(tagger, metadata, release, track=None):
for name, value in metadata.rawitems():
if name in ["title", "album", "artist"]:
metadata[name] = [titlecase(x) for x in value]
register_track_metadata_processor(nx_title_case)
register_album_metadata_processor(nx_title_case)

View File

@@ -1,57 +0,0 @@
# Copyright 2007 Javier Kohen
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation
import unicodedata
def iswbound(char):
"""Returns whether the given character is a word boundary."""
category = unicodedata.category(char)
# If it's a space separator or punctuation
return 'Zs' == category or 'Sk' == category or 'P' == category[0]
def utitle(string):
"""Title-case a string using a less destructive method than str.title."""
new_string = string[0].capitalize()
cap = False
for i in xrange(1, len(string)):
s = string[i]
# Special case apostrophe in the middle of a word.
if u"'" == s and string[i-1].isalpha(): cap = False
elif iswbound(s): cap = True
elif cap and s.isalpha():
cap = False
s = s.capitalize()
else: cap = False
new_string += s
return new_string
def title(string, locale="utf-8"):
"""Title-case a string using a less destructive method than str.title."""
if not string: return u""
# if the string is all uppercase, lowercase it - Erich/Javier
# Lots of Japanese songs use entirely upper-case English titles,
# so I don't like this change... - JoeW
#if string == string.upper(): string = string.lower()
if not isinstance(string, unicode):
string = string.decode(locale)
return utitle(string)
PLUGIN_NAME = "Title Case"
PLUGIN_API_VERSIONS = ["0.9", "0.10", "0.11"]
PLUGIN_DESCRIPTION = "Capitalize First Character In Every Word Of A Title"
from picard.metadata import (
register_track_metadata_processor,
register_album_metadata_processor,
)
def title_case(tagger, metadata, release, track=None):
for name, value in metadata.rawitems():
if name in ["title", "album", "artist"]:
metadata[name] = [title(x) for x in value]
register_track_metadata_processor(title_case)
register_album_metadata_processor(title_case)

View File

@@ -11,8 +11,8 @@ from picard import __version__
from picard.const import UI_LANGUAGES
if sys.version_info < (2, 5):
print "*** You need Python 2.5 or higher to use Picard."
if sys.version_info < (2, 6):
print "*** You need Python 2.6 or higher to use Picard."
args = {}
@@ -341,6 +341,24 @@ class picard_clean_ui(Command):
except OSError:
log.warn("'%s' does not exist -- can't clean it", pyfile)
class picard_clean_pyd(Command):
description = "clean up compiled c files"
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
from PyQt4 import uic
for pydfile in glob.glob("picard/*/*.pyd"):
try:
os.unlink(pydfile)
log.info("removing %s", pydfile)
except OSError:
log.warn("'%s' does not exist -- can't clean it", pyfile)
def cflags_to_include_dirs(cflags):
cflags = cflags.split()
@@ -460,6 +478,7 @@ args2 = {
'build_locales': picard_build_locales,
'build_ui': picard_build_ui,
'clean_ui': picard_clean_ui,
'clean_pyd': picard_clean_pyd,
'config': picard_config,
'install': picard_install,
'install_locales': picard_install_locales,
@@ -486,8 +505,8 @@ try:
generate_file('scripts/picard.py2exe.in', 'scripts/picard', {})
self.distribution.data_files.append(
("", ["discid.dll", "libfftw3-3.dll", "libofa.dll",
]))
# "msvcp71.dll"]))
# ]))
"msvcp90.dll"])) # Compile with VS2008 only or you will also need MSVC 7 runtimes as well.
for locale in self.distribution.locales:
self.distribution.data_files.append(
("locale/" + locale[1] + "/LC_MESSAGES",
@@ -495,14 +514,24 @@ try:
self.distribution.data_files.append(
("imageformats", [find_file_in_path("PyQt4/plugins/imageformats/qgif4.dll"),
find_file_in_path("PyQt4/plugins/imageformats/qjpeg4.dll"),
find_file_in_path("PyQt4/plugins/imageformats/qtiff4.dll")]))
find_file_in_path("PyQt4/plugins/imageformats/qtiff4.dll")]))
self.distribution.data_files.append(
("plugins", ["contrib/plugins/addrelease.py",
"contrib/plugins/discnumber.py",
"contrib/plugins/title_case_nx.py",
"contrib/plugins/featartist.py",
"contrib/plugins/originalreleasedate.py"]))
self.distribution.data_files.append(
("phonon_backend", [find_file_in_path("PyQt4/plugins/phonon_backend/phonon_ds94.dll")]))
py2exe.run(self)
print "*** creating the NSIS setup script ***"
pathname = "installer\picard-setup.nsi"
generate_file(pathname + ".in", pathname,
{'name': 'MusicBrainz Picard',
'version': __version__})
'version': __version__,
'description': 'The next generation MusicBrainz tagger.',
'url': 'http://wiki.musicbrainz.org/PicardTagger',})
print "*** compiling the NSIS setup script ***"
from ctypes import windll
operation = 'compile'
@@ -521,6 +550,7 @@ try:
'includes': ['sip'] + [e.name for e in ext_modules],
'excludes': ['ssl', 'socket', 'bz2'],
'optimize': 2,
'dll_excludes' : ['AVIFIL32.dll','MSACM32.dll','MSVFW32.dll']
},
}
except ImportError: