Merge pull request #140 from zas/pep8

PEP8 compliance fixes
This commit is contained in:
Michael Wiencek
2013-07-02 14:17:34 -07:00
47 changed files with 325 additions and 128 deletions

View File

@@ -60,7 +60,7 @@ class AcoustIDClient(QtCore.QObject):
return artist_credit_el
def parse_recording(recording):
if 'title' not in recording.children: # we have no metadata for this recording
if 'title' not in recording.children: # we have no metadata for this recording
return
recording_id = recording.id[0].text
recording_el = recording_list_el.append_child('recording')
@@ -213,4 +213,3 @@ class AcoustIDClient(QtCore.QObject):
if task[0] != file:
new_queue.appendleft(task)
self._queue = new_queue

View File

@@ -86,4 +86,3 @@ class AcoustIDManager(QtCore.QObject):
for submission in fingerprints:
submission.orig_trackid = submission.trackid
self._check_unsubmitted()

View File

@@ -420,7 +420,7 @@ class Album(DataObject, Item):
for track in self.tracks:
for file in track.linked_files:
if not file.is_saved():
count+=1
count += 1
return count
def column(self, column):
@@ -429,7 +429,7 @@ class Album(DataObject, Item):
linked_tracks = 0
for track in self.tracks:
if track.is_linked():
linked_tracks+=1
linked_tracks += 1
text = u'%s\u200E (%d/%d' % (self.metadata['album'], linked_tracks, len(self.tracks))
unmatched = self.get_num_unmatched_files()
if unmatched:

View File

@@ -24,6 +24,7 @@ import re
from picard import log
from picard.util import webbrowser2
class FileLookup(object):
def __init__(self, parent, server, port, localPort):

View File

@@ -339,7 +339,7 @@ class ClusterDict(object):
return word
def getToken(self, index):
token = None;
token = None
try:
word, token = self.ids[index]
except KeyError:
@@ -416,7 +416,7 @@ class ClusterEngine(object):
for i in xrange(self.clusterDict.getSize()):
word, count = self.clusterDict.getWordAndCount(i)
if word and count > 1:
self.clusterBins[self.clusterCount] = [ i ]
self.clusterBins[self.clusterCount] = [i]
self.idClusterIndex[i] = self.clusterCount
self.clusterCount = self.clusterCount + 1
#print "init ",
@@ -475,4 +475,3 @@ class ClusterEngine(object):
def can_refresh(self):
return False

View File

@@ -17,7 +17,9 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import os, sys, re
import os
import sys
import re
# Install gettext "noop" function in case const.py gets imported directly.
import __builtin__

View File

@@ -53,31 +53,31 @@ COVERART_SITES = (
AMAZON_SERVER = {
"amazon.jp": {
"server": "ec1.images-amazon.com",
"id" : "09",
"id": "09",
},
"amazon.co.jp": {
"server": "ec1.images-amazon.com",
"id" : "09",
"id": "09",
},
"amazon.co.uk": {
"server": "ec1.images-amazon.com",
"id" : "02",
"id": "02",
},
"amazon.de": {
"server": "ec2.images-amazon.com",
"id" : "03",
"id": "03",
},
"amazon.com": {
"server": "ec1.images-amazon.com",
"id" : "01",
"id": "01",
},
"amazon.ca": {
"server": "ec1.images-amazon.com",
"id" : "01", # .com and .ca are identical
"id": "01", # .com and .ca are identical
},
"amazon.fr": {
"server": "ec1.images-amazon.com",
"id" : "08"
"id": "08"
},
}
@@ -156,9 +156,9 @@ def _caa_append_image_to_trylist(try_list, imagedata):
else:
url = QUrl(imagedata["thumbnails"][thumbsize])
extras = {
'type': imagedata["types"][0].lower(), # FIXME: we pass only 1 type
'type': imagedata["types"][0].lower(), # FIXME: we pass only 1 type
'desc': imagedata["comment"],
'front': imagedata['front'], # front image indicator from CAA
'front': imagedata['front'], # front image indicator from CAA
}
_try_list_append_image_url(try_list, url, extras)

View File

@@ -30,7 +30,7 @@ CAA_TYPES = [
{'name': "track", 'title': N_("Track")},
{'name': "sticker", 'title': N_("Sticker")},
{'name': "other", 'title': N_("Other")},
{'name': "unknown", 'title': N_("Unknown")}, # pseudo type, used for the no type case
{'name': "unknown", 'title': N_("Unknown")}, # pseudo type, used for the no type case
]
CAA_TYPES_SEPARATOR = ' ' #separator to use when joining/splitting list of types
CAA_TYPES_SEPARATOR = ' ' # separator to use when joining/splitting list of types

View File

@@ -19,6 +19,7 @@
from picard.util import LockableObject
class DataObject(LockableObject):
def __init__(self, id):

View File

@@ -135,12 +135,12 @@ def _openLibrary():
except OSError, e:
raise NotImplementedError('Error opening library: ' + str(e))
assert False # not reached
assert False # not reached
def _setPrototypes(libDiscId):
ct = ctypes
libDiscId.discid_new.argtypes = ( )
libDiscId.discid_new.argtypes = ()
libDiscId.discid_new.restype = ct.c_void_p
libDiscId.discid_free.argtypes = (ct.c_void_p, )

View File

@@ -18,16 +18,19 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import sys
from mutagen import _util
from picard.plugin import ExtensionPoint
_formats = ExtensionPoint()
_extensions = {}
def register_format(format):
_formats.register(format.__module__, format)
for ext in format.EXTENSIONS:
_extensions[ext[1:]] = format
def supported_formats():
"""Returns list of supported formats."""
formats = []
@@ -35,6 +38,7 @@ def supported_formats():
formats.append((format.EXTENSIONS, format.NAME))
return formats
def open(filename):
"""Open the specified file and return a File instance with the appropriate format handler, or None."""
i = filename.rfind(".")
@@ -48,8 +52,6 @@ def open(filename):
return format(filename)
from mutagen import _util
def _insert_bytes_no_mmap(fobj, size, offset, BUFFER_SIZE=2**16):
"""Insert size bytes of empty space starting at offset.
@@ -101,6 +103,7 @@ def _insert_bytes_no_mmap(fobj, size, offset, BUFFER_SIZE=2**16):
if locked:
_util.unlock(fobj)
def _delete_bytes_no_mmap(fobj, size, offset, BUFFER_SIZE=2**16):
"""Delete size bytes of empty space starting at offset.

View File

@@ -149,24 +149,28 @@ class APEv2File(File):
cover_filename = 'Cover Art (Front)'
cover_filename += mimetype.get_extension(image["mime"], '.jpg')
tags['Cover Art (Front)'] = mutagen.apev2.APEValue(cover_filename + '\0' + image["data"], mutagen.apev2.BINARY)
break # can't save more than one item with the same name
# (mp3tags does this, but it's against the specs)
break # can't save more than one item with the same name
# (mp3tags does this, but it's against the specs)
tags.save(encode_filename(filename))
class MusepackFile(APEv2File):
"""Musepack file."""
EXTENSIONS = [".mpc", ".mp+"]
NAME = "Musepack"
_File = mutagen.musepack.Musepack
def _info(self, metadata, file):
super(MusepackFile, self)._info(metadata, file)
metadata['~format'] = "Musepack, SV%d" % file.info.version
class WavPackFile(APEv2File):
"""WavPack file."""
EXTENSIONS = [".wv"]
NAME = "WavPack"
_File = mutagen.wavpack.WavPack
def _info(self, metadata, file):
super(WavPackFile, self)._info(metadata, file)
metadata['~format'] = self.NAME
@@ -179,11 +183,13 @@ class WavPackFile(APEv2File):
self._rename(wvc_filename, metadata, config.setting)
return File._save_and_rename(self, old_filename, metadata, config.setting)
class OptimFROGFile(APEv2File):
"""OptimFROG file."""
EXTENSIONS = [".ofr", ".ofs"]
NAME = "OptimFROG"
_File = mutagen.optimfrog.OptimFROG
def _info(self, metadata, file):
super(OptimFROGFile, self)._info(metadata, file)
if file.filename.lower().endswith(".ofs"):
@@ -191,20 +197,24 @@ class OptimFROGFile(APEv2File):
else:
metadata['~format'] = "OptimFROG Lossless Audio"
class MonkeysAudioFile(APEv2File):
"""Monkey's Audio file."""
EXTENSIONS = [".ape"]
NAME = "Monkey's Audio"
_File = mutagen.monkeysaudio.MonkeysAudio
def _info(self, metadata, file):
super(MonkeysAudioFile, self)._info(metadata, file)
metadata['~format'] = self.NAME
class TAKFile(APEv2File):
"""TAK file."""
EXTENSIONS = [".tak"]
NAME = "Tom's lossless Audio Kompressor"
_File = mutagenext.tak.TAK
def _info(self, metadata, file):
super(TAKFile, self)._info(metadata, file)
metadata['~format'] = self.NAME

View File

@@ -25,6 +25,7 @@ from picard.metadata import Metadata, save_this_image_to_tags
from mutagen.asf import ASF, ASFByteArrayAttribute
import struct
def unpack_image(data):
"""
Helper function to unpack image data from a WM/Picture tag.
@@ -51,6 +52,7 @@ def unpack_image(data):
image_data = data[pos:pos+size]
return (mime.decode("utf-16-le"), image_data, type, description.decode("utf-16-le"))
def pack_image(mime, data, type=3, description=""):
"""
Helper function to pack image data for a WM/Picture tag.
@@ -62,6 +64,7 @@ def pack_image(mime, data, type=3, description=""):
tag_data += data
return tag_data
class ASFFile(File):
"""ASF (WMA) metadata reader/writer"""
EXTENSIONS = [".wma"]

View File

@@ -36,8 +36,10 @@ from urlparse import urlparse
def patched_EncodedTextSpec_write(self, frame, value):
try:
enc, term = self._encodings[frame.encoding]
except AttributeError: enc, term = self.encodings[frame.encoding]
except AttributeError:
enc, term = self.encodings[frame.encoding]
return value.encode(enc, 'ignore') + term
id3.EncodedTextSpec.write = patched_EncodedTextSpec_write
@@ -55,6 +57,8 @@ def patched_MultiSpec_write(self, frame, value):
if data.endswith(term):
data = data[:-len(term)]
return data
id3.MultiSpec._write_orig = id3.MultiSpec.write
id3.MultiSpec.write = patched_MultiSpec_write
@@ -63,23 +67,25 @@ id3.TCMP = compatid3.TCMP
id3.TSO2 = compatid3.TSO2
__ID3_IMAGE_TYPE_MAP = {
"other": 0,
"obi": 0,
"tray": 0,
"spine": 0,
"sticker": 0,
"front": 3,
"back": 4,
"booklet": 5,
"medium": 6,
"track": 6,
}
"other": 0,
"obi": 0,
"tray": 0,
"spine": 0,
"sticker": 0,
"front": 3,
"back": 4,
"booklet": 5,
"medium": 6,
"track": 6,
}
__ID3_REVERSE_IMAGE_TYPE_MAP = dict([(v, k) for k, v in __ID3_IMAGE_TYPE_MAP.iteritems()])
__ID3_REVERSE_IMAGE_TYPE_MAP = dict([(v,k) for k, v in __ID3_IMAGE_TYPE_MAP.iteritems()])
def image_type_from_id3_num(id3type):
return __ID3_REVERSE_IMAGE_TYPE_MAP.get(id3type, "other")
def image_type_as_id3_num(texttype):
return __ID3_IMAGE_TYPE_MAP.get(texttype, 0)
@@ -324,9 +330,9 @@ class ID3File(File):
tmcl.people.append([role, value])
elif name.startswith('comment:'):
desc = name.split(':', 1)[1]
if desc.lower()[:4]=="itun":
if desc.lower()[:4] == "itun":
tags.delall('COMM:' + desc)
tags.add(id3.COMM(encoding=0, desc=desc, lang='eng', text=[v+u'\x00' for v in values]))
tags.add(id3.COMM(encoding=0, desc=desc, lang='eng', text=[v + u'\x00' for v in values]))
else:
tags.add(id3.COMM(encoding=encoding, desc=desc, lang='eng', text=values))
elif name.startswith('lyrics:') or name == 'lyrics':
@@ -401,8 +407,10 @@ class ID3File(File):
tags.save(encode_filename(filename), v2=4, v1=v1)
if self._IsMP3 and config.setting["remove_ape_from_mp3"]:
try: mutagen.apev2.delete(encode_filename(filename))
except: pass
try:
mutagen.apev2.delete(encode_filename(filename))
except:
pass
def supports_tag(self, name):
return name in self.__rtranslate or name in self.__rtranslate_freetext\
@@ -417,15 +425,18 @@ class MP3File(ID3File):
NAME = "MPEG-1 Audio"
_File = mutagen.mp3.MP3
_IsMP3 = True
def _info(self, metadata, file):
super(MP3File, self)._info(metadata, file)
metadata['~format'] = 'MPEG-1 Layer %d' % file.info.layer
class TrueAudioFile(ID3File):
"""TTA file."""
EXTENSIONS = [".tta"]
NAME = "The True Audio"
_File = mutagen.trueaudio.TrueAudio
def _info(self, metadata, file):
super(TrueAudioFile, self)._info(metadata, file)
metadata['~format'] = self.NAME

View File

@@ -23,6 +23,7 @@ from picard.file import File
from picard.metadata import Metadata, save_this_image_to_tags
from picard.util import encode_filename
class MP4File(File):
EXTENSIONS = [".m4a", ".m4b", ".m4p", ".mp4"]
NAME = "MPEG-4 Audio"

View File

@@ -25,18 +25,23 @@ from mutagen._util import insert_bytes
from mutagen.id3 import ID3, Frame, Frames, Frames_2_2, TextFrame, TORY, \
TYER, TIME, APIC, IPLS, TDAT, BitPaddedInt, MakeID3v1
class TCMP(TextFrame):
pass
class TSO2(TextFrame):
pass
class XDOR(TextFrame):
pass
class XSOP(TextFrame):
pass
class CompatID3(ID3):
"""
Additional features over mutagen.id3.ID3:
@@ -56,7 +61,7 @@ class CompatID3(ID3):
known_frames["XDOR"] = XDOR
known_frames["XSOP"] = XSOP
kwargs["known_frames"] = known_frames
super(CompatID3, self).__init__(*args, **kwargs)
super(CompatID3, self).__init__(*args, **kwargs)
def save(self, filename=None, v1=1, v2=4):
"""Save changes to a file.
@@ -90,28 +95,37 @@ class CompatID3(ID3):
self.delete(filename)
except EnvironmentError, err:
from errno import ENOENT
if err.errno != ENOENT: raise
if err.errno != ENOENT:
raise
return
framedata = ''.join(framedata)
framesize = len(framedata)
if filename is None: filename = self.filename
try: f = open(filename, 'rb+')
if filename is None:
filename = self.filename
try:
f = open(filename, 'rb+')
except IOError, err:
from errno import ENOENT
if err.errno != ENOENT: raise
f = open(filename, 'ab') # create, then reopen
if err.errno != ENOENT:
raise
f = open(filename, 'ab') # create, then reopen
f = open(filename, 'rb+')
try:
idata = f.read(10)
try: id3, vmaj, vrev, flags, insize = unpack('>3sBBB4s', idata)
except struct.error: id3, insize = '', 0
try:
id3, vmaj, vrev, flags, insize = unpack('>3sBBB4s', idata)
except struct.error:
id3, insize = '', 0
insize = BitPaddedInt(insize)
if id3 != 'ID3': insize = -10
if id3 != 'ID3':
insize = -10
if insize >= framesize: outsize = insize
else: outsize = (framesize + 1023) & ~0x3FF
if insize >= framesize:
outsize = insize
else:
outsize = (framesize + 1023) & ~0x3FF
framedata += '\x00' * (outsize - framesize)
framesize = BitPaddedInt.to_str(outsize, width=4)
@@ -128,13 +142,16 @@ class CompatID3(ID3):
f.seek(-128, 2)
except IOError, err:
from errno import EINVAL
if err.errno != EINVAL: raise
f.seek(0, 2) # ensure read won't get "TAG"
if err.errno != EINVAL:
raise
f.seek(0, 2) # ensure read won't get "TAG"
if f.read(3) == "TAG":
f.seek(-128, 2)
if v1 > 0: f.write(MakeID3v1(self))
else: f.truncate()
if v1 > 0:
f.write(MakeID3v1(self))
else:
f.truncate()
elif v1 == 2:
f.seek(0, 2)
f.write(MakeID3v1(self))
@@ -145,10 +162,13 @@ class CompatID3(ID3):
def __save_frame(self, frame, v2):
flags = 0
if self.PEDANTIC and isinstance(frame, TextFrame):
if len(str(frame)) == 0: return ''
if len(str(frame)) == 0:
return ''
framedata = frame._writeData()
if v2 == 3: bits=8
else: bits=7
if v2 == 3:
bits = 8
else:
bits = 7
datasize = BitPaddedInt.to_str(len(framedata), width=4, bits=bits)
header = pack('>4s4sH', type(frame).__name__, datasize, flags)
return header + framedata
@@ -161,7 +181,8 @@ class CompatID3(ID3):
at some point.
"""
if self.version < (2,3,0): del self.unknown_frames[:]
if self.version < (2, 3, 0):
del self.unknown_frames[:]
# TMCL, TIPL -> TIPL
if "TIPL" in self or "TMCL" in self:
@@ -177,7 +198,7 @@ class CompatID3(ID3):
# TODO:
# * EQU2 -> EQUA
# * RVA2 -> RVAD
# * RVA2 -> RVAD
# TDOR -> TORY
if "TDOR" in self:
@@ -205,7 +226,10 @@ class CompatID3(ID3):
if self.version < (2, 3):
# ID3v2.2 PIC frames are slightly different.
pics = self.getall("APIC")
mimes = { "PNG": "image/png", "JPG": "image/jpeg" }
mimes = {
"PNG": "image/png",
"JPG": "image/jpeg"
}
self.delall("APIC")
for pic in pics:
newpic = APIC(
@@ -222,7 +246,8 @@ class CompatID3(ID3):
# New frames added in v2.4.
for key in ["ASPI", "EQU2", "RVA2", "SEEK", "SIGN", "TDRL", "TDTG",
"TMOO", "TPRO"]:
if key in self: del(self[key])
if key in self:
del(self[key])
for frame in self.values():
# ID3v2.3 doesn't support UTF-8 (and WMP can't read UTF-16 BE)

View File

@@ -20,7 +20,10 @@ __all__ = ["TAK", "Open", "delete"]
from mutagen.apev2 import APEv2File, error, delete
class TAKHeaderError(error): pass
class TAKHeaderError(error):
pass
class TAKInfo(object):
"""TAK stream information.
@@ -37,6 +40,7 @@ class TAKInfo(object):
def pprint(self):
return "Tom's lossless Audio Kompressor"
class TAK(APEv2File):
_Info = TAKInfo
_mimes = ["audio/x-tak"]

View File

@@ -36,6 +36,7 @@ from picard.formats.id3 import image_type_from_id3_num, image_type_as_id3_num
from picard.metadata import Metadata, save_this_image_to_tags
from picard.util import encode_filename, sanitize_date
class VCommentFile(File):
"""Generic VComment-based file."""
_File = None
@@ -67,8 +68,10 @@ class VCommentFile(File):
name += value[start + 2:-1]
value = value[:start]
elif name.startswith('rating'):
try: name, email = name.split(':', 1)
except ValueError: email = ''
try:
name, email = name.split(':', 1)
except ValueError:
email = ''
if email != config.setting['rating_user_email']:
continue
name = '~rating'
@@ -179,60 +182,73 @@ class VCommentFile(File):
except TypeError:
file.save()
class FLACFile(VCommentFile):
"""FLAC file."""
EXTENSIONS = [".flac"]
NAME = "FLAC"
_File = mutagen.flac.FLAC
def _info(self, metadata, file):
super(FLACFile, self)._info(metadata, file)
metadata['~format'] = self.NAME
class OggFLACFile(VCommentFile):
"""FLAC file."""
EXTENSIONS = [".oggflac"]
NAME = "Ogg FLAC"
_File = mutagen.oggflac.OggFLAC
def _info(self, metadata, file):
super(OggFLACFile, self)._info(metadata, file)
metadata['~format'] = self.NAME
class OggSpeexFile(VCommentFile):
"""Ogg Speex file."""
EXTENSIONS = [".spx"]
NAME = "Speex"
_File = mutagen.oggspeex.OggSpeex
def _info(self, metadata, file):
super(OggSpeexFile, self)._info(metadata, file)
metadata['~format'] = self.NAME
class OggTheoraFile(VCommentFile):
"""Ogg Theora file."""
EXTENSIONS = [".oggtheora"]
NAME = "Ogg Theora"
_File = mutagen.oggtheora.OggTheora
def _info(self, metadata, file):
super(OggTheoraFile, self)._info(metadata, file)
metadata['~format'] = self.NAME
class OggVorbisFile(VCommentFile):
"""Ogg Vorbis file."""
EXTENSIONS = [".ogg"]
NAME = "Ogg Vorbis"
_File = mutagen.oggvorbis.OggVorbis
def _info(self, metadata, file):
super(OggVorbisFile, self)._info(metadata, file)
metadata['~format'] = self.NAME
class OggOpusFile(VCommentFile):
"""Ogg Opus file."""
EXTENSIONS = [".opus"]
NAME = "Ogg Opus"
_File = OggOpus
def _info(self, metadata, file):
super(OggOpusFile, self)._info(metadata, file)
metadata['~format'] = self.NAME
def OggAudioFile(filename):
"""Generic Ogg audio file."""
options = [OggFLACFile, OggSpeexFile, OggVorbisFile]

View File

@@ -58,6 +58,7 @@ def add_receiver(receiver):
_log_debug_messages = False
def debug(message, *args, **kwargs):
if _log_debug_messages:
thread.proxy_to_main(_message, "D:", message, args, kwargs)

View File

@@ -54,6 +54,8 @@ def _decamelcase(text):
_REPLACE_MAP = {}
_EXTRA_ATTRS = ['guest', 'additional', 'minor']
_BLANK_SPECIAL_RELTYPES = {'vocal': 'vocals'}
def _parse_attributes(attrs, reltype):
attrs = [_decamelcase(_REPLACE_MAP.get(a, a)) for a in attrs]
prefix = ' '.join([a for a in attrs if a in _EXTRA_ATTRS])
@@ -243,6 +245,7 @@ def recording_to_metadata(node, track):
m['~rating'] = nodes[0].text
m['~length'] = format_time(m.length)
def work_to_metadata(work, m):
m.add("musicbrainz_workid", work.attribs['id'])
if 'language' in work.children:
@@ -250,6 +253,7 @@ def work_to_metadata(work, m):
if 'relation_list' in work.children:
_relations_to_metadata(work.relation_list, m)
def medium_to_metadata(node, m):
for name, nodes in node.children.iteritems():
if not nodes:

View File

@@ -26,6 +26,7 @@ from picard.mbxml import artist_credit_from_node
MULTI_VALUED_JOINER = '; '
def is_front_image(image):
# CAA has a flag for "front" image, use it in priority
caa_front = image.get('front', None)
@@ -34,6 +35,7 @@ def is_front_image(image):
return (image['type'] == 'front')
return caa_front
def save_this_image_to_tags(image):
if not config.setting["save_only_front_images_to_tags"]:
return True
@@ -41,6 +43,7 @@ def save_this_image_to_tags(image):
return True
return False
class Metadata(dict):
"""List of metadata items with dict-like access."""

View File

@@ -35,6 +35,7 @@ _patterns = [
re.compile(r"(?:.*(/|\\))?(?P<albumartist>.*)(/|\\)(?P<album>.*)(/|\\)(?P<artist>.*)-(?P<tracknum>\d{2})-(?P<title>.*)\.(?:\w{2,5})$"),
]
def parseFileName(filename, metadata):
for pattern in _patterns:
match = pattern.match(filename)
@@ -79,4 +80,3 @@ if __name__ == "__main__":
ok += 1
print "OK"
print len(testCases), ok

View File

@@ -26,11 +26,26 @@ from picard.metadata import MULTI_VALUED_JOINER
from picard.plugin import ExtensionPoint
from inspect import getargspec
class ScriptError(Exception): pass
class ParseError(ScriptError): pass
class EndOfFile(ParseError): pass
class SyntaxError(ParseError): pass
class UnknownFunction(ScriptError): pass
class ScriptError(Exception):
pass
class ParseError(ScriptError):
pass
class EndOfFile(ParseError):
pass
class SyntaxError(ParseError):
pass
class UnknownFunction(ScriptError):
pass
class ScriptText(unicode):
@@ -262,7 +277,7 @@ def register_script_function(function, name=None, eval_args=True,
function will not be verified."""
argspec = getargspec(function)
argcount = (len(argspec[0]) - 1,) # -1 for the parser
argcount = (len(argspec[0]) - 1,) # -1 for the parser
if argspec[3] is not None:
argcount = range(argcount[0] - len(argspec[3]), argcount[0] + 1)
@@ -274,6 +289,7 @@ def register_script_function(function, name=None, eval_args=True,
argcount if argcount and check_argcount else False)
)
def func_if(parser, _if, _then, _else=None):
"""If ``if`` is not empty, it returns ``then``, otherwise it returns ``else``."""
if _if.eval(parser):
@@ -282,6 +298,7 @@ def func_if(parser, _if, _then, _else=None):
return _else.eval(parser)
return ''
def func_if2(parser, *args):
"""Returns first non empty argument."""
for arg in args:
@@ -290,48 +307,60 @@ def func_if2(parser, *args):
return arg
return ''
def func_noop(parser, *args):
"""Does nothing :)"""
return ''
def func_left(parser, text, length):
"""Returns first ``num`` characters from ``text``."""
return text[:int(length)]
def func_right(parser, text, length):
"""Returns last ``num`` characters from ``text``."""
return text[-int(length):]
def func_lower(parser, text):
"""Returns ``text`` in lower case."""
return text.lower()
def func_upper(parser, text):
"""Returns ``text`` in upper case."""
return text.upper()
def func_pad(parser, text, length, char):
return char * (int(length) - len(text)) + text
def func_strip(parser, text):
return re.sub("\s+", " ", text).strip()
def func_replace(parser, text, old, new):
return text.replace(old, new)
def func_in(parser, text, needle):
if needle in text:
return "1"
else:
return ""
def func_inmulti(parser, text, value, separator=MULTI_VALUED_JOINER):
"""Splits ``text`` by ``separator``, and returns true if the resulting list contains ``value``."""
return func_in(parser, text.split(separator) if separator else [text], value)
def func_rreplace(parser, text, old, new):
return re.sub(old, new, text)
def func_rsearch(parser, text, pattern):
match = re.search(pattern, text)
if match:
@@ -341,6 +370,7 @@ def func_rsearch(parser, text, pattern):
return match.group(0)
return u""
def func_num(parser, text, length):
format = "%%0%dd" % int(length)
try:
@@ -349,6 +379,7 @@ def func_num(parser, text, length):
value = 0
return format % value
def func_unset(parser, name):
"""Unsets the variable ``name``."""
if name.startswith("_"):
@@ -359,6 +390,7 @@ def func_unset(parser, name):
pass
return ""
def func_set(parser, name, value):
"""Sets the variable ``name`` to ``value``."""
if value:
@@ -369,16 +401,19 @@ def func_set(parser, name, value):
func_unset(parser, name)
return ""
def func_setmulti(parser, name, value, separator=MULTI_VALUED_JOINER):
"""Sets the variable ``name`` to ``value`` as a list; splitting by the passed string, or "; " otherwise."""
return func_set(parser, name, value.split(separator) if value and separator else value)
def func_get(parser, name):
"""Returns the variable ``name`` (equivalent to ``%name%``)."""
if name.startswith("_"):
name = "~" + name[1:]
return parser.context.get(name, u"")
def func_copy(parser, new, old):
"""Copies content of variable ``old`` to variable ``new``."""
if new.startswith("_"):
@@ -388,6 +423,7 @@ def func_copy(parser, new, old):
parser.context[new] = parser.context.getall(old)[:]
return ""
def func_copymerge(parser, new, old):
"""Copies content of variable ``old`` and appends it into variable ``new``, removing duplicates. This is normally
used to merge a multi-valued variable into another, existing multi-valued variable."""
@@ -400,6 +436,7 @@ def func_copymerge(parser, new, old):
parser.context[new] = newvals + list(set(oldvals) - set(newvals))
return ""
def func_trim(parser, text, char=None):
"""Trims all leading and trailing whitespaces from ``text``. The optional
second parameter specifies the character to trim."""
@@ -408,6 +445,7 @@ def func_trim(parser, text, char=None):
else:
return text.strip()
def func_add(parser, x, y):
"""Add ``y`` to ``x``."""
try:
@@ -415,6 +453,7 @@ def func_add(parser, x, y):
except ValueError:
return ""
def func_sub(parser, x, y):
"""Substracts ``y`` from ``x``."""
try:
@@ -422,6 +461,7 @@ def func_sub(parser, x, y):
except ValueError:
return ""
def func_div(parser, x, y):
"""Divides ``x`` by ``y``."""
try:
@@ -429,6 +469,7 @@ def func_div(parser, x, y):
except ValueError:
return ""
def func_mod(parser, x, y):
"""Returns the remainder of ``x`` divided by ``y``."""
try:
@@ -436,6 +477,7 @@ def func_mod(parser, x, y):
except ValueError:
return ""
def func_mul(parser, x, y):
"""Multiplies ``x`` by ``y``."""
try:
@@ -443,6 +485,7 @@ def func_mul(parser, x, y):
except ValueError:
return ""
def func_or(parser, x, y):
"""Returns true, if either ``x`` or ``y`` not empty."""
if x or y:
@@ -450,6 +493,7 @@ def func_or(parser, x, y):
else:
return ""
def func_and(parser, x, y):
"""Returns true, if both ``x`` and ``y`` are not empty."""
if x and y:
@@ -457,6 +501,7 @@ def func_and(parser, x, y):
else:
return ""
def func_not(parser, x):
"""Returns true, if ``x`` is empty."""
if not x:
@@ -464,6 +509,7 @@ def func_not(parser, x):
else:
return ""
def func_eq(parser, x, y):
"""Returns true, if ``x`` equals ``y``."""
if x == y:
@@ -471,6 +517,7 @@ def func_eq(parser, x, y):
else:
return ""
def func_ne(parser, x, y):
"""Returns true, if ``x`` not equals ``y``."""
if x != y:
@@ -478,6 +525,7 @@ def func_ne(parser, x, y):
else:
return ""
def func_lt(parser, x, y):
"""Returns true, if ``x`` is lower than ``y``."""
try:
@@ -487,6 +535,7 @@ def func_lt(parser, x, y):
pass
return ""
def func_lte(parser, x, y):
"""Returns true, if ``x`` is lower than or equals ``y``."""
try:
@@ -496,6 +545,7 @@ def func_lte(parser, x, y):
pass
return ""
def func_gt(parser, x, y):
"""Returns true, if ``x`` is greater than ``y``."""
try:
@@ -505,6 +555,7 @@ def func_gt(parser, x, y):
pass
return ""
def func_gte(parser, x, y):
"""Returns true, if ``x`` is greater than or equals ``y``."""
try:
@@ -514,9 +565,11 @@ def func_gte(parser, x, y):
pass
return ""
def func_len(parser, text):
return str(len(text))
def func_performer(parser, pattern="", join=", "):
values = []
for name, value in parser.context.items():
@@ -524,12 +577,14 @@ def func_performer(parser, pattern="", join=", "):
values.append(value)
return join.join(values)
def func_matchedtracks(parser, arg):
if parser.file:
if parser.file.parent:
return str(parser.file.parent.album.get_num_matched_tracks())
return "0"
def func_firstalphachar(parser, text, nonalpha="#"):
if len(text) == 0:
return nonalpha
@@ -539,9 +594,11 @@ def func_firstalphachar(parser, text, nonalpha="#"):
else:
return nonalpha
def func_initials(parser, text):
return "".join(a[:1] for a in text.split(" ") if a[:1].isalpha())
def func_firstwords(parser, text, length):
try:
length = int(length)
@@ -554,6 +611,7 @@ def func_firstwords(parser, text, length):
return text[:length]
return text[:length].rsplit(' ', 1)[0]
def func_truncate(parser, text, length):
try:
length = int(length)
@@ -561,6 +619,7 @@ def func_truncate(parser, text, length):
length = None
return text[:length].rstrip()
register_script_function(func_if, "if", eval_args=False)
register_script_function(func_if2, "if2", eval_args=False, check_argcount=False)
register_script_function(func_noop, "noop", eval_args=False, check_argcount=False)

View File

@@ -33,6 +33,7 @@ _replace_words = {
"disc 8": "CD8",
}
def normalize(orig_string):
"""Strips non-alphanumeric characters from a string unless doing so would make it blank."""
string = strip_non_alnum(orig_string.lower())
@@ -40,6 +41,7 @@ def normalize(orig_string):
string = orig_string
return string
def similarity(a1, b1):
"""Calculates similarity of single words as a function of their edit distance."""
a2 = normalize(a1)
@@ -52,6 +54,7 @@ def similarity(a1, b1):
_split_words_re = re.compile('\W+', re.UNICODE)
def similarity2(a, b):
"""Calculates similarity of a multi-word strings."""
alist = filter(bool, _split_words_re.split(a.lower()))

View File

@@ -29,10 +29,15 @@ from collections import deque
from functools import partial
from itertools import chain
# A "fix" for http://python.org/sf/1438480
def _patched_shutil_copystat(src, dst):
try: _orig_shutil_copystat(src, dst)
except OSError: pass
try:
_orig_shutil_copystat(src, dst)
except OSError:
pass
_orig_shutil_copystat = shutil.copystat
shutil.copystat = _patched_shutil_copystat
@@ -66,6 +71,7 @@ from picard.util import (
)
from picard.webservice import XmlWebService
class Tagger(QtGui.QApplication):
tagger_stats_changed = QtCore.pyqtSignal()
@@ -163,7 +169,7 @@ class Tagger(QtGui.QApplication):
def remove_va_file_naming_format(merge=True):
if merge:
config.setting["file_naming_format"] = \
"$if($eq(%compilation%,1),\n$noop(Various Artist albums)\n"+\
"$if($eq(%compilation%,1),\n$noop(Various Artist albums)\n" + \
"%s,\n$noop(Single Artist Albums)\n%s)" %\
(config.setting["va_file_naming_format"].toString(),
config.setting["file_naming_format"])
@@ -414,7 +420,7 @@ class Tagger(QtGui.QApplication):
def remove_files(self, files, from_parent=True):
"""Remove files from the tagger."""
for file in files:
if self.files.has_key(file.filename):
if file.filename in self.files:
file.clear_lookup_task()
self._acoustid.stop_analyze(file)
del self.files[file.filename]
@@ -553,6 +559,7 @@ class Tagger(QtGui.QApplication):
def instance(cls):
return cls.__instance
def help():
print """Usage: %s [OPTIONS] [FILE] [FILE] ...

View File

@@ -21,6 +21,7 @@ from PyQt4 import QtCore, QtGui
from picard.ui.ui_cdlookup import Ui_Dialog
from picard.mbxml import artist_credit_from_node, label_info_from_node
class CDLookupDialog(QtGui.QDialog):
def __init__(self, releases, disc, parent=None):

View File

@@ -18,7 +18,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
import os, sys
import os
import sys
from PyQt4 import QtCore, QtGui
from picard import config
from picard.formats import supported_formats

View File

@@ -54,10 +54,10 @@ class InfoDialog(QtGui.QDialog):
pixmap.loadFromData(data)
icon = QtGui.QIcon(pixmap)
item.setIcon(icon)
s = "%s (%s)\n%d x %d" % (bytes2human.decimal(size),
bytes2human.binary(size),
pixmap.width(),
pixmap.height())
s = "%s (%s)\n%d x %d" % (bytes2human.decimal(size),
bytes2human.binary(size),
pixmap.width(),
pixmap.height())
item.setText(s)
self.ui.artwork_list.addItem(item)
@@ -81,7 +81,7 @@ class FileInfoDialog(InfoDialog):
info.append((_('Format:'), file.orig_metadata['~format']))
try:
size = os.path.getsize(encode_filename(file.filename))
sizestr = "%s (%s)" % (bytes2human.decimal(size), bytes2human.binary(size))
sizestr = "%s (%s)" % (bytes2human.decimal(size), bytes2human.binary(size))
info.append((_('Size:'), sizestr))
except:
pass
@@ -95,9 +95,12 @@ class FileInfoDialog(InfoDialog):
info.append((_('Bits per sample:'), str(file.orig_metadata['~bits_per_sample'])))
if '~channels' in file.orig_metadata:
ch = file.orig_metadata['~channels']
if ch == 1: ch = _('Mono')
elif ch == 2: ch = _('Stereo')
else: ch = str(ch)
if ch == 1:
ch = _('Mono')
elif ch == 2:
ch = _('Stereo')
else:
ch = str(ch)
info.append((_('Channels:'), ch))
text = '<br/>'.join(map(lambda i: '<b>%s</b><br/>%s' % i, info))
self.ui.info.setText(text)

View File

@@ -46,7 +46,7 @@ class InfoStatus(QtGui.QWidget, Ui_InfoStatus):
self.icon_cd = icontheme.lookup('media-optical')
self.icon_file = QtGui.QIcon(":/images/file.png")
self.icon_file_pending = QtGui.QIcon(":/images/file-pending.png")
self.icon_download = QtGui.QIcon(":/images/16x16/action-go-down-16.png")
self.icon_download = QtGui.QIcon(":/images/16x16/action-go-down-16.png")
def _init_tooltips(self):
t1 = _("Files")

View File

@@ -17,6 +17,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Item(object):
def can_save(self):

View File

@@ -54,21 +54,27 @@ _clusterlist_actions = ExtensionPoint()
_track_actions = ExtensionPoint()
_file_actions = ExtensionPoint()
def register_album_action(action):
_album_actions.register(action.__module__, action)
def register_cluster_action(action):
_cluster_actions.register(action.__module__, action)
def register_clusterlist_action(action):
_clusterlist_actions.register(action.__module__, action)
def register_track_action(action):
_track_actions.register(action.__module__, action)
def register_file_action(action):
_file_actions.register(action.__module__, action)
def get_match_color(similarity, basecolor):
c1 = (basecolor.red(), basecolor.green(), basecolor.blue())
c2 = (223, 125, 125)
@@ -556,7 +562,7 @@ class TreeItem(QtGui.QTreeWidgetItem):
obj.item = self
if sortable:
self.__lt__ = self._lt
self.setTextAlignment(1, QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
self.setTextAlignment(1, QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
def _lt(self, other):
column = self.treeWidget().sortColumn()
@@ -609,7 +615,7 @@ class AlbumItem(TreeItem):
if update_tracks:
oldnum = self.childCount() - 1
newnum = len(album.tracks)
if oldnum > newnum: # remove old items
if oldnum > newnum: # remove old items
for i in xrange(oldnum - newnum):
self.takeChild(newnum - 1)
oldnum = newnum
@@ -620,14 +626,14 @@ class AlbumItem(TreeItem):
item.obj = track
track.item = item
item.update(update_album=False)
if newnum > oldnum: # add new items
if newnum > oldnum: # add new items
items = []
for i in xrange(newnum - 1, oldnum - 1, -1): # insertChildren is backwards
for i in xrange(newnum - 1, oldnum - 1, -1): # insertChildren is backwards
item = TrackItem(album.tracks[i], False)
item.setHidden(False) # Workaround to make sure the parent state gets updated
item.setHidden(False) # Workaround to make sure the parent state gets updated
items.append(item)
self.insertChildren(oldnum, items)
for item in items: # Update after insertChildren so that setExpanded works
for item in items: # Update after insertChildren so that setExpanded works
item.update(update_album=False)
self.setIcon(0, AlbumItem.icon_cd_saved if album.is_complete() else AlbumItem.icon_cd)
for i, column in enumerate(MainPanel.columns):
@@ -653,17 +659,17 @@ class TrackItem(TreeItem):
icon = TrackItem.icon_note
oldnum = self.childCount()
newnum = track.num_linked_files
if oldnum > newnum: # remove old items
if oldnum > newnum: # remove old items
for i in xrange(oldnum - newnum):
self.takeChild(newnum - 1).obj.item = None
oldnum = newnum
for i in xrange(oldnum): # update existing items
for i in xrange(oldnum): # update existing items
item = self.child(i)
file = track.linked_files[i]
item.obj = file
file.item = item
item.update()
if newnum > oldnum: # add new items
if newnum > oldnum: # add new items
items = []
for i in xrange(newnum - 1, oldnum - 1, -1):
item = FileItem(track.linked_files[i], False)

View File

@@ -40,10 +40,14 @@ from picard.util import icontheme, webbrowser2, find_existing_path, throttle
from picard.util.cdrom import get_cdrom_drives
from picard.plugin import ExtensionPoint
ui_init = ExtensionPoint()
def register_ui_init (function):
def register_ui_init(function):
ui_init.register(function.__module__, function)
class MainWindow(QtGui.QMainWindow):
options = [
@@ -192,7 +196,9 @@ class MainWindow(QtGui.QMainWindow):
pos = config.persist["window_position"]
size = config.persist["window_size"]
self._desktopgeo = self.tagger.desktop().screenGeometry()
if pos.x() > 0 and pos.y() > 0 and pos.x()+size.width() < self._desktopgeo.width() and pos.y()+size.height() < self._desktopgeo.height():
if (pos.x() > 0 and pos.y() > 0
and pos.x() + size.width() < self._desktopgeo.width()
and pos.y() + size.height() < self._desktopgeo.height()):
self.move(pos)
if size.width() <= 0 or size.height() <= 0:
size = QtCore.QSize(780, 560)
@@ -619,7 +625,7 @@ class MainWindow(QtGui.QMainWindow):
# Use a custom file selection dialog to allow the selection of multiple directories
file_dialog = QtGui.QFileDialog(self, "", current_directory)
file_dialog.setFileMode(QtGui.QFileDialog.DirectoryOnly)
if sys.platform == "darwin": # The native dialog doesn't allow selecting >1 directory
if sys.platform == "darwin": # The native dialog doesn't allow selecting >1 directory
file_dialog.setOption(QtGui.QFileDialog.DontUseNativeDialog)
tree_view = file_dialog.findChild(QtGui.QTreeView)
tree_view.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
@@ -671,7 +677,6 @@ removed in this version of Picard. Your file naming scheme has automatically
been merged with that of single artist albums."""),
QtGui.QMessageBox.Ok)
def open_bug_report(self):
webbrowser2.open("http://musicbrainz.org/doc/Picard_Troubleshooting")

View File

@@ -174,7 +174,7 @@ class MetadataBox(QtGui.QTableWidget):
self.objects = set()
self.selection_mutex = QtCore.QMutex()
self.selection_dirty = False
self.editing = None # the QTableWidgetItem being edited
self.editing = None # the QTableWidgetItem being edited
self.clipboard = [""]
self.add_tag_action = QtGui.QAction(_(u"Add New Tag..."), parent)
self.add_tag_action.triggered.connect(partial(self.edit_tag, ""))

View File

@@ -27,6 +27,7 @@ class OptionsCheckError(Exception):
self.title = title
self.info = info
class OptionsPage(QtGui.QWidget):
PARENT = None
@@ -44,7 +45,7 @@ class OptionsPage(QtGui.QWidget):
def save(self):
pass
def display_error(self, error):
dialog = QtGui.QMessageBox(QtGui.QMessageBox.Warning, error.title, error.info, QtGui.QMessageBox.Ok, self)
dialog.exec_()
@@ -52,5 +53,6 @@ class OptionsPage(QtGui.QWidget):
_pages = ExtensionPoint()
def register_options_page(page_class):
_pages.register(page_class.__module__, page_class)

View File

@@ -53,9 +53,9 @@ class PluginsOptionsPage(OptionsPage):
self.ui.plugins.dropEvent = self.dropEvent
self.ui.plugins.dragEnterEvent = self.dragEnterEvent
if sys.platform == "win32":
self.loader="file:///%s"
self.loader = "file:///%s"
else:
self.loader="file://%s"
self.loader = "file://%s"
self.ui.install_plugin.clicked.connect(self.open_plugins)
self.ui.folder_open.clicked.connect(self.open_plugin_dir)
self.ui.plugin_download.clicked.connect(self.open_plugin_site)

View File

@@ -124,7 +124,7 @@ class RenamingOptionsPage(OptionsPage):
'ascii_filenames': self.ui.ascii_filenames.isChecked(),
'rename_files': self.ui.rename_files.isChecked(),
'move_files': self.ui.move_files.isChecked(),
'use_va_format': False, # TODO remove
'use_va_format': False, # TODO remove
'file_naming_format': unicode(self.ui.file_naming_format.toPlainText()),
'move_files_to': os.path.normpath(unicode(self.ui.move_files_to.text()))
}
@@ -137,9 +137,12 @@ class RenamingOptionsPage(OptionsPage):
if not settings["move_files"]:
return os.path.basename(filename)
return filename
except SyntaxError, e: return ""
except TypeError, e: return ""
except UnknownFunction, e: return ""
except SyntaxError:
return ""
except TypeError:
return ""
except UnknownFunction:
return ""
def update_examples(self):
# TODO: Here should be more examples etc.
@@ -259,12 +262,12 @@ class RenamingOptionsPage(OptionsPage):
self.ui.move_files_to.setText(path)
def test(self):
self.ui.renaming_error.setStyleSheet("");
self.ui.renaming_error.setStyleSheet("")
self.ui.renaming_error.setText("")
try:
self.check_format()
except OptionsCheckError, e:
self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR);
self.ui.renaming_error.setStyleSheet(self.STYLESHEET_ERROR)
self.ui.renaming_error.setText(e.info)
return

View File

@@ -80,12 +80,12 @@ class ScriptingOptionsPage(OptionsPage):
self.ui.tagger_script.textChanged.connect(self.live_checker)
def live_checker(self):
self.ui.script_error.setStyleSheet("");
self.ui.script_error.setStyleSheet("")
self.ui.script_error.setText("")
try:
self.check()
except OptionsCheckError, e:
self.ui.script_error.setStyleSheet(self.STYLESHEET_ERROR);
self.ui.script_error.setStyleSheet(self.STYLESHEET_ERROR)
self.ui.script_error.setText(e.info)
return

View File

@@ -85,7 +85,7 @@ class RatingWidget(QtGui.QWidget):
self.tagger.xmlws.submit_ratings(ratings, None)
def paintEvent(self, event=None):
painter = QtGui.QPainter(self)
painter = QtGui.QPainter(self)
offset = self._offset
for i in range(1, self._maximum + 1):
if i <= self._rating or i <= self._highlight:

View File

@@ -42,4 +42,3 @@ class StandardButton(QtGui.QPushButton):
icon = self.tagger.style().standardIcon(getattr(QtGui.QStyle, iconname))
args = [icon, label]
QtGui.QPushButton.__init__(self, *args)

View File

@@ -24,7 +24,7 @@ import sys
import unicodedata
from time import time
from PyQt4 import QtCore
from encodings import rot_13;
from encodings import rot_13
from string import Template
from functools import partial
@@ -76,6 +76,7 @@ class LockableObject(QtCore.QObject):
_io_encoding = sys.getfilesystemencoding()
#The following was adapted from k3b's source code:
#// On a glibc system the system locale defaults to ANSI_X3.4-1968
#// It is very unlikely that one would set the locale to ANSI_X3.4-1968
@@ -96,6 +97,7 @@ Translation: Picard will have problems with non-english characters
in filenames until you change your charset.
""")
def encode_filename(filename):
"""Encode unicode strings to filesystem encoding."""
if isinstance(filename, unicode):
@@ -106,6 +108,7 @@ def encode_filename(filename):
else:
return filename
def decode_filename(filename):
"""Decode strings from filesystem encoding to unicode."""
if isinstance(filename, unicode):
@@ -113,9 +116,11 @@ def decode_filename(filename):
else:
return filename.decode(_io_encoding)
def pathcmp(a, b):
return os.path.normcase(a) == os.path.normcase(b)
def format_time(ms):
"""Formats time in milliseconds to a string representation."""
ms = float(ms)
@@ -124,6 +129,7 @@ def format_time(ms):
else:
return "%d:%02d" % (round(ms / 1000.0) / 60, round(ms / 1000.0) % 60)
def sanitize_date(datestr):
"""Sanitize date format.
@@ -141,6 +147,7 @@ def sanitize_date(datestr):
date.append(num)
return ("", "%04d", "%04d-%02d", "%04d-%02d-%02d")[len(date)] % tuple(date)
_unaccent_dict = {u'Æ': u'AE', u'æ': u'ae', u'Œ': u'OE', u'œ': u'oe', u'ß': 'ss'}
_re_latin_letter = re.compile(r"^(LATIN [A-Z]+ LETTER [A-Z]+) WITH")
def unaccent(string):
@@ -160,26 +167,31 @@ def unaccent(string):
result.append(char)
return "".join(result)
_re_non_ascii = re.compile(r'[^\x00-\x7F]', re.UNICODE)
def replace_non_ascii(string, repl="_"):
"""Replace non-ASCII characters from ``string`` by ``repl``."""
return _re_non_ascii.sub(repl, asciipunct(string))
_re_win32_incompat = re.compile(r'["*:<>?|]', re.UNICODE)
def replace_win32_incompat(string, repl=u"_"):
"""Replace win32 filename incompatible characters from ``string`` by
``repl``."""
return _re_win32_incompat.sub(repl, string)
_re_non_alphanum = re.compile(r'\W+', re.UNICODE)
def strip_non_alnum(string):
"""Remove all non-alphanumeric characters from ``string``."""
return _re_non_alphanum.sub(u" ", string).strip()
_re_slashes = re.compile(r'[\\/]', re.UNICODE)
def sanitize_filename(string, repl="_"):
return _re_slashes.sub(repl, string)
def make_short_filename(prefix, filename, max_path_length=240, max_length=200,
mid_length=32, min_length=2):
"""
@@ -220,7 +232,7 @@ def make_short_filename(prefix, filename, max_path_length=240, max_length=200,
break
if left > 0:
raise IOError, "File name is too long."
raise IOError("File name is too long.")
return os.path.join(*[a.strip() for a in reversed(parts)])
@@ -307,7 +319,11 @@ def load_release_type_scores(setting):
scores = {}
values = setting.split()
for i in range(0, len(values), 2):
scores[values[i]] = float(values[i+1]) if i+1 < len(values) else 0.0
try:
score = float(values[i + 1])
except IndexError:
score = 0.0
scores[values[i]] = score
return scores

View File

@@ -41,6 +41,7 @@ ICON_SIZE_MENU = ('16x16',)
ICON_SIZE_TOOLBAR = ('22x22',)
ICON_SIZE_ALL = ('22x22', '16x16')
def lookup(name, size=ICON_SIZE_ALL):
icon = QtGui.QIcon()
if _current_theme:

View File

@@ -28,6 +28,7 @@ MIME_TYPE_EXTENSION_MAP = {
EXTENSION_MIME_TYPE_MAP = dict([(b, a) for a, b in MIME_TYPE_EXTENSION_MAP.items()])
def get_from_data(data, filename=None, default=None):
"""Tries to determine the mime type from the given data."""
if data.startswith('\xff\xd8\xff'):
@@ -43,11 +44,13 @@ def get_from_data(data, filename=None, default=None):
else:
return default
def get_from_filename(filename, default=None):
"""Tries to determine the mime type from the given filename."""
name, ext = os.path.splitext(os.path.basename(filename))
return EXTENSION_MIME_TYPE_MAP.get(ext, default)
def get_extension(mimetype, default=None):
"""Returns the file extension for a given mime type."""
return MIME_TYPE_EXTENSION_MAP.get(mimetype, default)
return MIME_TYPE_EXTENSION_MAP.get(mimetype, default)

View File

@@ -3,6 +3,7 @@
from collections import deque
from PyQt4 import QtCore
class Queue:
"""Create a queue object with a given maximum size.
@@ -50,7 +51,7 @@ class Queue:
finally:
self.mutex.unlock()
def remove(self,item):
def remove(self, item):
"""Remove an item from the queue."""
self.mutex.lock()
try:

View File

@@ -86,6 +86,7 @@ TAG_NAMES = {
'~rating': N_('Rating'),
}
def display_tag_name(name):
if ':' in name:
name, desc = name.split(':', 1)
@@ -101,4 +102,3 @@ def display_tag_name(name):
return '%s []' % (_(new_name),)
else:
return _(new_name)

View File

@@ -74,11 +74,12 @@ class XmlNode(object):
try:
return self.attribs[name]
except KeyError:
raise AttributeError, name
raise AttributeError(name)
_node_name_re = re.compile('[^a-zA-Z0-9]')
def _node_name(n):
return _node_name_re.sub('_', unicode(n))
@@ -338,7 +339,8 @@ class XmlWebService(QtCore.QObject):
host = config.setting["server_host"]
port = config.setting["server_port"]
path = "/ws/2/%s/%s?inc=%s" % (entitytype, entityid, "+".join(inc))
if params: path += "&" + "&".join(params)
if params:
path += "&" + "&".join(params)
return self.get(host, port, path, handler, priority=priority, important=important, mblogin=mblogin)
def get_release_by_id(self, releaseid, handler, inc=[], priority=True, important=False, mblogin=False):
@@ -361,8 +363,10 @@ class XmlWebService(QtCore.QObject):
filters.append((name, value))
else:
value = _escape_lucene_query(value).strip().lower()
if value: query.append('%s:(%s)' % (name, value))
if query: filters.append(('query', ' '.join(query)))
if value:
query.append('%s:(%s)' % (name, value))
if query:
filters.append(('query', ' '.join(query)))
params = []
for name, value in filters:
value = str(QUrl.toPercentEncoding(QtCore.QString(value)))

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import glob, re
import glob
import os
import re
import sys
from StringIO import StringIO
from ConfigParser import RawConfigParser

View File

@@ -8,4 +8,3 @@ from picard.tagger import main
localedir = os.path.join(os.path.dirname(sys.argv[0]), 'locale')
main(localedir, True)