From 8d40e6700d05c6573e7305a3ffe5c9f61d2de702 Mon Sep 17 00:00:00 2001 From: Laurent Monin Date: Tue, 2 Jul 2013 23:01:13 +0200 Subject: [PATCH] pep8 fixes --- picard/acoustid.py | 3 +- picard/acoustidmanager.py | 1 - picard/album.py | 4 +- picard/browser/filelookup.py | 1 + picard/cluster.py | 5 +- picard/const.py | 4 +- picard/coverart.py | 18 +++---- picard/coverartarchive.py | 4 +- picard/dataobj.py | 1 + picard/disc.py | 4 +- picard/formats/__init__.py | 7 ++- picard/formats/apev2.py | 14 ++++- picard/formats/asf.py | 3 ++ picard/formats/id3.py | 45 ++++++++++------ picard/formats/mp4.py | 1 + picard/formats/mutagenext/compatid3.py | 69 +++++++++++++++++-------- picard/formats/mutagenext/tak.py | 6 ++- picard/formats/vorbis.py | 20 +++++++- picard/log.py | 1 + picard/mbxml.py | 4 ++ picard/metadata.py | 3 ++ picard/parsefilename.py | 2 +- picard/script.py | 71 +++++++++++++++++++++++--- picard/similarity.py | 3 ++ picard/tagger.py | 15 ++++-- picard/ui/cdlookup.py | 1 + picard/ui/filebrowser.py | 3 +- picard/ui/infodialog.py | 19 ++++--- picard/ui/infostatus.py | 2 +- picard/ui/item.py | 1 + picard/ui/itemviews.py | 24 +++++---- picard/ui/mainwindow.py | 13 +++-- picard/ui/metadatabox.py | 2 +- picard/ui/options/__init__.py | 4 +- picard/ui/options/plugins.py | 4 +- picard/ui/options/renaming.py | 15 +++--- picard/ui/options/scripting.py | 4 +- picard/ui/ratingwidget.py | 2 +- picard/ui/util.py | 1 - picard/util/__init__.py | 22 ++++++-- picard/util/icontheme.py | 1 + picard/util/mimetype.py | 5 +- picard/util/queue.py | 3 +- picard/util/tags.py | 2 +- picard/webservice.py | 12 +++-- setup.py | 3 +- tagger.py | 1 - 47 files changed, 325 insertions(+), 128 deletions(-) diff --git a/picard/acoustid.py b/picard/acoustid.py index 79aa1ad5a..dd10e9e12 100644 --- a/picard/acoustid.py +++ b/picard/acoustid.py @@ -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 - diff --git a/picard/acoustidmanager.py b/picard/acoustidmanager.py index 3237daa23..9bbf7bdcd 100644 --- a/picard/acoustidmanager.py +++ b/picard/acoustidmanager.py @@ -86,4 +86,3 @@ class AcoustIDManager(QtCore.QObject): for submission in fingerprints: submission.orig_trackid = submission.trackid self._check_unsubmitted() - diff --git a/picard/album.py b/picard/album.py index 3a338e54e..6b749962e 100644 --- a/picard/album.py +++ b/picard/album.py @@ -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: diff --git a/picard/browser/filelookup.py b/picard/browser/filelookup.py index d99c60a65..0bc6b9bbc 100644 --- a/picard/browser/filelookup.py +++ b/picard/browser/filelookup.py @@ -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): diff --git a/picard/cluster.py b/picard/cluster.py index cc1901240..fdac5d64c 100644 --- a/picard/cluster.py +++ b/picard/cluster.py @@ -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 - diff --git a/picard/const.py b/picard/const.py index 399a32188..8803ce276 100644 --- a/picard/const.py +++ b/picard/const.py @@ -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__ diff --git a/picard/coverart.py b/picard/coverart.py index 7df342c81..cae09474a 100644 --- a/picard/coverart.py +++ b/picard/coverart.py @@ -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) diff --git a/picard/coverartarchive.py b/picard/coverartarchive.py index 9e574177d..8ad268614 100644 --- a/picard/coverartarchive.py +++ b/picard/coverartarchive.py @@ -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 diff --git a/picard/dataobj.py b/picard/dataobj.py index 85bc36f34..989e6650b 100644 --- a/picard/dataobj.py +++ b/picard/dataobj.py @@ -19,6 +19,7 @@ from picard.util import LockableObject + class DataObject(LockableObject): def __init__(self, id): diff --git a/picard/disc.py b/picard/disc.py index 4008dc5a3..c0fab2815 100644 --- a/picard/disc.py +++ b/picard/disc.py @@ -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, ) diff --git a/picard/formats/__init__.py b/picard/formats/__init__.py index 6ac9915f4..07dae5af5 100644 --- a/picard/formats/__init__.py +++ b/picard/formats/__init__.py @@ -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. diff --git a/picard/formats/apev2.py b/picard/formats/apev2.py index e89e152dd..d2a476f7f 100644 --- a/picard/formats/apev2.py +++ b/picard/formats/apev2.py @@ -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 diff --git a/picard/formats/asf.py b/picard/formats/asf.py index 494835f69..3fd723a3e 100644 --- a/picard/formats/asf.py +++ b/picard/formats/asf.py @@ -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"] diff --git a/picard/formats/id3.py b/picard/formats/id3.py index 3be35273c..51d0f09e6 100644 --- a/picard/formats/id3.py +++ b/picard/formats/id3.py @@ -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 diff --git a/picard/formats/mp4.py b/picard/formats/mp4.py index b816c4186..736f5040c 100644 --- a/picard/formats/mp4.py +++ b/picard/formats/mp4.py @@ -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" diff --git a/picard/formats/mutagenext/compatid3.py b/picard/formats/mutagenext/compatid3.py index 78782b0ba..d1fdc9bcf 100644 --- a/picard/formats/mutagenext/compatid3.py +++ b/picard/formats/mutagenext/compatid3.py @@ -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) diff --git a/picard/formats/mutagenext/tak.py b/picard/formats/mutagenext/tak.py index b4568cb8f..b7dd94fb0 100644 --- a/picard/formats/mutagenext/tak.py +++ b/picard/formats/mutagenext/tak.py @@ -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"] diff --git a/picard/formats/vorbis.py b/picard/formats/vorbis.py index c1d1df99f..75c7f4e65 100644 --- a/picard/formats/vorbis.py +++ b/picard/formats/vorbis.py @@ -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] diff --git a/picard/log.py b/picard/log.py index a547fcb7b..2dd353cb3 100644 --- a/picard/log.py +++ b/picard/log.py @@ -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) diff --git a/picard/mbxml.py b/picard/mbxml.py index bb08b91c2..46932c12d 100644 --- a/picard/mbxml.py +++ b/picard/mbxml.py @@ -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: diff --git a/picard/metadata.py b/picard/metadata.py index a083972a6..93a3287f6 100644 --- a/picard/metadata.py +++ b/picard/metadata.py @@ -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.""" diff --git a/picard/parsefilename.py b/picard/parsefilename.py index 663b3de58..871857ce1 100644 --- a/picard/parsefilename.py +++ b/picard/parsefilename.py @@ -35,6 +35,7 @@ _patterns = [ re.compile(r"(?:.*(/|\\))?(?P.*)(/|\\)(?P.*)(/|\\)(?P.*)-(?P\d{2})-(?P.*)\.(?:\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 - diff --git a/picard/script.py b/picard/script.py index 3713abdec..31b2825ea 100644 --- a/picard/script.py +++ b/picard/script.py @@ -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) diff --git a/picard/similarity.py b/picard/similarity.py index 5b079f95e..222297775 100644 --- a/picard/similarity.py +++ b/picard/similarity.py @@ -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())) diff --git a/picard/tagger.py b/picard/tagger.py index 90f27d022..a3eabea4d 100644 --- a/picard/tagger.py +++ b/picard/tagger.py @@ -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] ... diff --git a/picard/ui/cdlookup.py b/picard/ui/cdlookup.py index 41b0959bb..cc5bd4810 100644 --- a/picard/ui/cdlookup.py +++ b/picard/ui/cdlookup.py @@ -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): diff --git a/picard/ui/filebrowser.py b/picard/ui/filebrowser.py index 7394b8f13..a820c6d2f 100644 --- a/picard/ui/filebrowser.py +++ b/picard/ui/filebrowser.py @@ -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 diff --git a/picard/ui/infodialog.py b/picard/ui/infodialog.py index eff24d105..e81917cbd 100644 --- a/picard/ui/infodialog.py +++ b/picard/ui/infodialog.py @@ -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) diff --git a/picard/ui/infostatus.py b/picard/ui/infostatus.py index 6bbf79f12..769a5b9db 100644 --- a/picard/ui/infostatus.py +++ b/picard/ui/infostatus.py @@ -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") diff --git a/picard/ui/item.py b/picard/ui/item.py index d88302994..2bc705bf3 100644 --- a/picard/ui/item.py +++ b/picard/ui/item.py @@ -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): diff --git a/picard/ui/itemviews.py b/picard/ui/itemviews.py index c3910487f..d27746be7 100644 --- a/picard/ui/itemviews.py +++ b/picard/ui/itemviews.py @@ -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) diff --git a/picard/ui/mainwindow.py b/picard/ui/mainwindow.py index 19b6428b0..ada47174e 100644 --- a/picard/ui/mainwindow.py +++ b/picard/ui/mainwindow.py @@ -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") diff --git a/picard/ui/metadatabox.py b/picard/ui/metadatabox.py index 0a42afc89..5e22a8b05 100644 --- a/picard/ui/metadatabox.py +++ b/picard/ui/metadatabox.py @@ -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, "")) diff --git a/picard/ui/options/__init__.py b/picard/ui/options/__init__.py index 31aa340b3..da17d3b4b 100644 --- a/picard/ui/options/__init__.py +++ b/picard/ui/options/__init__.py @@ -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) diff --git a/picard/ui/options/plugins.py b/picard/ui/options/plugins.py index b19fd739f..87528170d 100644 --- a/picard/ui/options/plugins.py +++ b/picard/ui/options/plugins.py @@ -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) diff --git a/picard/ui/options/renaming.py b/picard/ui/options/renaming.py index 042f08292..c276fc12d 100644 --- a/picard/ui/options/renaming.py +++ b/picard/ui/options/renaming.py @@ -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 diff --git a/picard/ui/options/scripting.py b/picard/ui/options/scripting.py index a1209cda7..5120dabbd 100644 --- a/picard/ui/options/scripting.py +++ b/picard/ui/options/scripting.py @@ -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 diff --git a/picard/ui/ratingwidget.py b/picard/ui/ratingwidget.py index ce95e54ba..b1d601854 100644 --- a/picard/ui/ratingwidget.py +++ b/picard/ui/ratingwidget.py @@ -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: diff --git a/picard/ui/util.py b/picard/ui/util.py index 31599bf13..29c8d58b2 100644 --- a/picard/ui/util.py +++ b/picard/ui/util.py @@ -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) - diff --git a/picard/util/__init__.py b/picard/util/__init__.py index 6cd980b23..3d6729c1a 100644 --- a/picard/util/__init__.py +++ b/picard/util/__init__.py @@ -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 diff --git a/picard/util/icontheme.py b/picard/util/icontheme.py index 790a206d3..c9ff83b0c 100644 --- a/picard/util/icontheme.py +++ b/picard/util/icontheme.py @@ -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: diff --git a/picard/util/mimetype.py b/picard/util/mimetype.py index e3325067d..a6abe3d52 100644 --- a/picard/util/mimetype.py +++ b/picard/util/mimetype.py @@ -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) \ No newline at end of file + return MIME_TYPE_EXTENSION_MAP.get(mimetype, default) diff --git a/picard/util/queue.py b/picard/util/queue.py index 7203775d1..c6d286307 100644 --- a/picard/util/queue.py +++ b/picard/util/queue.py @@ -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: diff --git a/picard/util/tags.py b/picard/util/tags.py index 2862f5357..46622f417 100644 --- a/picard/util/tags.py +++ b/picard/util/tags.py @@ -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) - diff --git a/picard/webservice.py b/picard/webservice.py index f869ab20a..af1e8c53f 100644 --- a/picard/webservice.py +++ b/picard/webservice.py @@ -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))) diff --git a/setup.py b/setup.py index 5bba12dfc..bb3268fe6 100755 --- a/setup.py +++ b/setup.py @@ -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 diff --git a/tagger.py b/tagger.py index f854d93c7..57c7ebcd7 100755 --- a/tagger.py +++ b/tagger.py @@ -8,4 +8,3 @@ from picard.tagger import main localedir = os.path.join(os.path.dirname(sys.argv[0]), 'locale') main(localedir, True) -