mirror of
https://github.com/fergalmoran/picard.git
synced 2026-02-25 09:03:59 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
|
||||
from PyQt4 import QtCore
|
||||
import os.path
|
||||
import re
|
||||
from picard import log
|
||||
from picard.util import webbrowser2
|
||||
|
||||
class FileLookup(object):
|
||||
@@ -33,7 +35,9 @@ class FileLookup(object):
|
||||
return str(QtCore.QUrl.toPercentEncoding(text))
|
||||
|
||||
def launch(self, url):
|
||||
log.debug("webbrowser2: %s" % url)
|
||||
webbrowser2.open(url)
|
||||
return True
|
||||
|
||||
def discLookup(self, url):
|
||||
return self.launch("%s&tport=%d" % (url, self.localPort))
|
||||
@@ -56,11 +60,34 @@ class FileLookup(object):
|
||||
def artistLookup(self, artist_id):
|
||||
return self._lookup('artist', artist_id)
|
||||
|
||||
def mbidLookup(self, string, type_):
|
||||
"""Parses string for known entity type and mbid, open browser for it
|
||||
If entity type is 'release', it will load corresponding release if
|
||||
possible.
|
||||
"""
|
||||
uuid = '[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}'
|
||||
entity_type = '(?:release-group|release|recording|work|artist|label|url|area|track)'
|
||||
regex = r"\b(%s)?\W*(%s)" % (entity_type, uuid)
|
||||
m = re.search(regex, string, re.IGNORECASE)
|
||||
if m is None:
|
||||
return False
|
||||
if m.group(1) is None:
|
||||
entity = type_
|
||||
else:
|
||||
entity = m.group(1).lower()
|
||||
mbid = m.group(2).lower()
|
||||
if entity == 'release':
|
||||
QtCore.QObject.tagger.load_album(mbid)
|
||||
return True
|
||||
return self._lookup(entity, mbid)
|
||||
|
||||
def _search(self, type_, query, adv=False):
|
||||
if self.mbidLookup(query, type_):
|
||||
return True
|
||||
url = "http://%s:%d/search/textsearch?limit=25&type=%s&query=%s&tport=%d" % (
|
||||
self._encode(self.server),
|
||||
self.port,
|
||||
type_,
|
||||
type_,
|
||||
self._encode(query),
|
||||
self.localPort)
|
||||
if adv:
|
||||
|
||||
65
picard/i18n.py
Normal file
65
picard/i18n.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import gettext
|
||||
import locale
|
||||
import os.path
|
||||
import sys
|
||||
import __builtin__
|
||||
|
||||
__builtin__.__dict__['N_'] = lambda a: a
|
||||
|
||||
|
||||
def setup_gettext(localedir, ui_language=None, logdebug=None):
|
||||
"""Setup locales, load translations, install gettext functions."""
|
||||
current_locale = ''
|
||||
if ui_language:
|
||||
os.environ['LANGUAGE'] = ''
|
||||
os.environ['LANG'] = ui_language
|
||||
try:
|
||||
current_locale = locale.normalize(ui_language)
|
||||
locale.setlocale(locale.LC_ALL, current_locale)
|
||||
except:
|
||||
pass
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, os.environ["LANG"])
|
||||
except KeyError:
|
||||
os.environ["LANG"] = locale.getdefaultlocale()[0]
|
||||
try:
|
||||
current_locale = locale.setlocale(locale.LC_ALL, "")
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
elif not ui_language:
|
||||
if sys.platform == "darwin":
|
||||
try:
|
||||
import Foundation
|
||||
defaults = Foundation.NSUserDefaults.standardUserDefaults()
|
||||
os.environ["LANG"] = \
|
||||
defaults.objectForKey_("AppleLanguages")[0]
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
current_locale = locale.setlocale(locale.LC_ALL, "")
|
||||
except:
|
||||
pass
|
||||
if logdebug:
|
||||
logdebug("Using locale %r", current_locale)
|
||||
try:
|
||||
if logdebug:
|
||||
logdebug("Loading gettext translation, localedir=%r", localedir)
|
||||
trans = gettext.translation("picard", localedir)
|
||||
trans.install(True)
|
||||
_ungettext = trans.ungettext
|
||||
except IOError:
|
||||
__builtin__.__dict__['_'] = lambda a: a
|
||||
|
||||
def _ungettext(a, b, c):
|
||||
if c == 1:
|
||||
return a
|
||||
else:
|
||||
return b
|
||||
__builtin__.__dict__['ungettext'] = _ungettext
|
||||
if logdebug:
|
||||
logdebug("_ = %r", _)
|
||||
logdebug("N_ = %r", N_)
|
||||
logdebug("ungettext = %r", ungettext)
|
||||
@@ -55,7 +55,8 @@ def _decamelcase(text):
|
||||
|
||||
_REPLACE_MAP = {}
|
||||
_EXTRA_ATTRS = ['guest', 'additional', 'minor']
|
||||
def _parse_attributes(attrs):
|
||||
_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])
|
||||
attrs = [a for a in attrs if a not in _EXTRA_ATTRS]
|
||||
@@ -64,7 +65,7 @@ def _parse_attributes(attrs):
|
||||
elif len(attrs) == 1:
|
||||
attrs = attrs[0]
|
||||
else:
|
||||
attrs = ''
|
||||
attrs = _BLANK_SPECIAL_RELTYPES.get(reltype, '')
|
||||
return ' '.join([prefix, attrs]).strip().lower()
|
||||
|
||||
|
||||
@@ -79,7 +80,7 @@ def _relations_to_metadata(relation_lists, m):
|
||||
if 'attribute_list' in relation.children:
|
||||
attribs = [a.text for a in relation.attribute_list[0].attribute]
|
||||
if reltype in ('vocal', 'instrument', 'performer'):
|
||||
name = 'performer:' + _parse_attributes(attribs)
|
||||
name = 'performer:' + _parse_attributes(attribs, reltype)
|
||||
elif reltype == 'mix-DJ' and len(attribs) > 0:
|
||||
if not hasattr(m, "_djmix_ars"):
|
||||
m._djmix_ars = {}
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
import gettext
|
||||
import locale
|
||||
import getopt
|
||||
import os.path
|
||||
import shutil
|
||||
@@ -30,18 +28,6 @@ import sys
|
||||
from collections import deque
|
||||
from itertools import chain
|
||||
|
||||
# Install gettext "noop" function.
|
||||
import __builtin__
|
||||
__builtin__.__dict__['N_'] = lambda a: a
|
||||
|
||||
# Py2exe 0.6.6 has broken fake_getline which doesn't work with Python 2.5
|
||||
if hasattr(sys, "frozen"):
|
||||
import linecache
|
||||
def fake_getline(filename, lineno, module_globals = None):
|
||||
return ''
|
||||
linecache.getline = fake_getline
|
||||
del linecache, fake_getline
|
||||
|
||||
# A "fix" for http://python.org/sf/1438480
|
||||
def _patched_shutil_copystat(src, dst):
|
||||
try: _orig_shutil_copystat(src, dst)
|
||||
@@ -51,6 +37,7 @@ shutil.copystat = _patched_shutil_copystat
|
||||
|
||||
import picard.resources
|
||||
import picard.plugins
|
||||
from picard.i18n import setup_gettext
|
||||
|
||||
from picard import version_string, log, acoustid, config
|
||||
from picard.album import Album, NatAlbum
|
||||
@@ -140,7 +127,7 @@ class Tagger(QtGui.QApplication):
|
||||
|
||||
check_io_encoding()
|
||||
|
||||
self.setup_gettext(localedir)
|
||||
setup_gettext(localedir, config.setting["ui_language"], log.debug)
|
||||
|
||||
self.xmlws = XmlWebService()
|
||||
|
||||
@@ -198,47 +185,6 @@ class Tagger(QtGui.QApplication):
|
||||
# default format, disabled
|
||||
remove_va_file_naming_format(merge=False)
|
||||
|
||||
def setup_gettext(self, localedir):
|
||||
"""Setup locales, load translations, install gettext functions."""
|
||||
ui_language = config.setting["ui_language"]
|
||||
if ui_language:
|
||||
os.environ['LANGUAGE'] = ''
|
||||
os.environ['LANG'] = ui_language
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, os.environ["LANG"])
|
||||
except KeyError:
|
||||
os.environ["LANG"] = locale.getdefaultlocale()[0]
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
if sys.platform == "darwin" and not ui_language:
|
||||
try:
|
||||
import Foundation
|
||||
defaults = Foundation.NSUserDefaults.standardUserDefaults()
|
||||
os.environ["LANG"] = defaults.objectForKey_("AppleLanguages")[0]
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
log.debug("Loading gettext translation, localedir=%r", localedir)
|
||||
self.translation = gettext.translation("picard", localedir)
|
||||
self.translation.install(True)
|
||||
ungettext = self.translation.ungettext
|
||||
except IOError:
|
||||
__builtin__.__dict__['_'] = lambda a: a
|
||||
def ungettext(a, b, c):
|
||||
if c == 1: return a
|
||||
else: return b
|
||||
__builtin__.__dict__['ungettext'] = ungettext
|
||||
|
||||
def move_files_to_album(self, files, albumid=None, album=None):
|
||||
"""Move `files` to tracks on album `albumid`."""
|
||||
if album is None:
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import os.path
|
||||
from PyQt4 import QtGui
|
||||
from picard.util import format_time, encode_filename
|
||||
from picard.util import format_time, encode_filename, bytes2human
|
||||
from picard.ui.ui_infodialog import Ui_InfoDialog
|
||||
|
||||
|
||||
@@ -48,11 +48,17 @@ class InfoDialog(QtGui.QDialog):
|
||||
|
||||
for image in images:
|
||||
data = image["data"]
|
||||
size = len(data)
|
||||
item = QtGui.QListWidgetItem()
|
||||
pixmap = QtGui.QPixmap()
|
||||
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())
|
||||
item.setText(s)
|
||||
self.ui.artwork_list.addItem(item)
|
||||
|
||||
def tab_hide(self, widget):
|
||||
@@ -75,13 +81,8 @@ class FileInfoDialog(InfoDialog):
|
||||
info.append((_('Format:'), file.orig_metadata['~format']))
|
||||
try:
|
||||
size = os.path.getsize(encode_filename(file.filename))
|
||||
if size < 1024:
|
||||
size = '%d B' % size
|
||||
elif size < 1024 * 1024:
|
||||
size = '%0.1f kB' % (size / 1024.0)
|
||||
else:
|
||||
size = '%0.1f MB' % (size / 1024.0 / 1024.0)
|
||||
info.append((_('Size:'), size))
|
||||
sizestr = "%s (%s)" % (bytes2human.decimal(size), bytes2human.binary(size))
|
||||
info.append((_('Size:'), sizestr))
|
||||
except:
|
||||
pass
|
||||
if file.orig_metadata.length:
|
||||
|
||||
125
picard/util/bytes2human.py
Normal file
125
picard/util/bytes2human.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
# Copyright (C) 2013 Laurent Monin
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import locale
|
||||
import picard.i18n
|
||||
|
||||
|
||||
"""
|
||||
Helper class to convert bytes to human-readable form
|
||||
It supports i18n through gettext, decimal and binary units.
|
||||
|
||||
>>> n = 1572864
|
||||
>>> [binary(n), decimal(n)]
|
||||
['1.5 MiB', '1.6 MB']
|
||||
"""
|
||||
|
||||
#used to force gettextization
|
||||
_BYTES_STRINGS_I18N = (
|
||||
N_('%s B'),
|
||||
N_('%s kB'),
|
||||
N_('%s KiB'),
|
||||
N_('%s MB'),
|
||||
N_('%s MiB'),
|
||||
N_('%s GB'),
|
||||
N_('%s GiB'),
|
||||
N_('%s TB'),
|
||||
N_('%s TiB'),
|
||||
N_('%s PB'),
|
||||
N_('%s PiB'),
|
||||
)
|
||||
|
||||
|
||||
def decimal(number, prec=1):
|
||||
"""
|
||||
Convert bytes to short human-readable string, decimal mode
|
||||
|
||||
>>> [decimal(n) for n in [1000, 1024, 15500]]
|
||||
['1 kB', '1 kB', '15.5 kB']
|
||||
"""
|
||||
return short_string(int(number), 1000)
|
||||
|
||||
|
||||
def binary(number, prec=1):
|
||||
"""
|
||||
Convert bytes to short human-readable string, binary mode
|
||||
>>> [binary(n) for n in [1000, 1024, 15500]]
|
||||
['1000 B', '1 KiB', '15.1 KiB']
|
||||
"""
|
||||
return short_string(int(number), 1024, prec)
|
||||
|
||||
|
||||
def short_string(number, multiple, prec=1):
|
||||
"""
|
||||
Returns short human-readable string for `number` bytes
|
||||
>>> [short_string(n, 1024, 2) for n in [1000, 1100, 15500]]
|
||||
['1000 B', '1.07 KiB', '15.14 KiB']
|
||||
>>> [short_string(n, 1000, 1) for n in [10000, 11000, 1550000]]
|
||||
['10 kB', '11 kB', '1.6 MB']
|
||||
"""
|
||||
num, unit = calc_unit(number, multiple)
|
||||
n = int(num)
|
||||
nr = round(num, prec)
|
||||
if n == nr or unit == 'B':
|
||||
fmt = '%d'
|
||||
num = n
|
||||
else:
|
||||
fmt = '%%0.%df' % prec
|
||||
num = nr
|
||||
fmtnum = locale.format(fmt, num)
|
||||
return _("%s " + unit) % fmtnum
|
||||
|
||||
|
||||
def calc_unit(number, multiple=1000):
|
||||
"""
|
||||
Calculate rounded number of multiple * bytes, finding best unit
|
||||
|
||||
>>> calc_unit(12456, 1024)
|
||||
(12.1640625, 'KiB')
|
||||
>>> calc_unit(-12456, 1000)
|
||||
(-12.456, 'kB')
|
||||
>>> calc_unit(0, 1001)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: multiple parameter has to be 1000 or 1024
|
||||
"""
|
||||
if number < 0:
|
||||
sign = -1
|
||||
number = -number
|
||||
else:
|
||||
sign = 1
|
||||
n = float(number)
|
||||
if multiple == 1000:
|
||||
k, b = 'k', 'B'
|
||||
elif multiple == 1024:
|
||||
k, b = 'K', 'iB'
|
||||
else:
|
||||
raise ValueError('multiple parameter has to be 1000 or 1024')
|
||||
|
||||
suffixes = ["B"] + [i + b for i in k + "MGTP"]
|
||||
for suffix in suffixes:
|
||||
if n < multiple or suffix == suffixes[-1]:
|
||||
return (sign*n, suffix)
|
||||
else:
|
||||
n /= multiple
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
@@ -51,10 +51,13 @@ class Queue:
|
||||
self.mutex.unlock()
|
||||
|
||||
def remove(self,item):
|
||||
"""Remove an item into the queue."""
|
||||
"""Remove an item from the queue."""
|
||||
self.mutex.lock()
|
||||
try:
|
||||
self._remove(item)
|
||||
self.queue.remove(item)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.not_full.wakeOne()
|
||||
finally:
|
||||
self.mutex.unlock()
|
||||
@@ -91,21 +94,6 @@ class Queue:
|
||||
def _put(self, item):
|
||||
self.queue.append(item)
|
||||
|
||||
# Remove an item from the queue
|
||||
def _remove(self, item):
|
||||
if item in self.queue:
|
||||
try:
|
||||
# remove is only availible in python 2.5
|
||||
self.queue.remove(item)
|
||||
except AttributeError:
|
||||
# remove items this way in older versions of python.
|
||||
for i in range(0, len(self.queue)):
|
||||
if self.queue[i] == item:
|
||||
self.queue.rotate(-i)
|
||||
self.queue.popleft()
|
||||
self.queue.rotate(i)
|
||||
break
|
||||
|
||||
# Get an item from the queue
|
||||
def _get(self):
|
||||
return self.queue.popleft()
|
||||
|
||||
74
test/data/b2h_test_C.dat
Normal file
74
test/data/b2h_test_C.dat
Normal file
@@ -0,0 +1,74 @@
|
||||
0;0 B;0 B;0 B
|
||||
1;1 B;1 B;1 B
|
||||
100;100 B;100 B;100 B
|
||||
102;102 B;102 B;102 B
|
||||
500;500 B;500 B;500 B
|
||||
512;512 B;512 B;512 B
|
||||
990;990 B;990 B;990 B
|
||||
999;999 B;999 B;999 B
|
||||
1000;1 kB;1000 B;1000 B
|
||||
1013;1 kB;1013 B;1013 B
|
||||
1023;1 kB;1023 B;1023 B
|
||||
1024;1 kB;1 KiB;1 KiB
|
||||
1500;1.5 kB;1.5 KiB;1.46 KiB
|
||||
1536;1.5 kB;1.5 KiB;1.50 KiB
|
||||
100000;100 kB;97.7 KiB;97.66 KiB
|
||||
104857;104.9 kB;102.4 KiB;102.40 KiB
|
||||
500000;500 kB;488.3 KiB;488.28 KiB
|
||||
524288;524.3 kB;512 KiB;512 KiB
|
||||
990000;990 kB;966.8 KiB;966.80 KiB
|
||||
999900;999.9 kB;976.5 KiB;976.46 KiB
|
||||
1000000;1 MB;976.6 KiB;976.56 KiB
|
||||
1038090;1 MB;1013.8 KiB;1013.76 KiB
|
||||
1048471;1 MB;1023.9 KiB;1023.90 KiB
|
||||
1048576;1 MB;1 MiB;1 MiB
|
||||
1500000;1.5 MB;1.4 MiB;1.43 MiB
|
||||
1572864;1.6 MB;1.5 MiB;1.50 MiB
|
||||
100000000;100 MB;95.4 MiB;95.37 MiB
|
||||
107374182;107.4 MB;102.4 MiB;102.40 MiB
|
||||
500000000;500 MB;476.8 MiB;476.84 MiB
|
||||
536870912;536.9 MB;512 MiB;512 MiB
|
||||
990000000;990 MB;944.1 MiB;944.14 MiB
|
||||
999900000;999.9 MB;953.6 MiB;953.58 MiB
|
||||
1000000000;1 GB;953.7 MiB;953.67 MiB
|
||||
1063004405;1.1 GB;1013.8 MiB;1013.76 MiB
|
||||
1073634449;1.1 GB;1023.9 MiB;1023.90 MiB
|
||||
1073741824;1.1 GB;1 GiB;1 GiB
|
||||
1500000000;1.5 GB;1.4 GiB;1.40 GiB
|
||||
1610612736;1.6 GB;1.5 GiB;1.50 GiB
|
||||
100000000000;100 GB;93.1 GiB;93.13 GiB
|
||||
109951162777;110.0 GB;102.4 GiB;102.40 GiB
|
||||
500000000000;500 GB;465.7 GiB;465.66 GiB
|
||||
549755813888;549.8 GB;512 GiB;512 GiB
|
||||
990000000000;990 GB;922 GiB;922.01 GiB
|
||||
999900000000;999.9 GB;931.2 GiB;931.23 GiB
|
||||
1000000000000;1 TB;931.3 GiB;931.32 GiB
|
||||
1088516511498;1.1 TB;1013.8 GiB;1013.76 GiB
|
||||
1099401676613;1.1 TB;1023.9 GiB;1023.90 GiB
|
||||
1099511627776;1.1 TB;1 TiB;1 TiB
|
||||
1500000000000;1.5 TB;1.4 TiB;1.36 TiB
|
||||
1649267441664;1.6 TB;1.5 TiB;1.50 TiB
|
||||
100000000000000;100 TB;90.9 TiB;90.95 TiB
|
||||
112589990684262;112.6 TB;102.4 TiB;102.40 TiB
|
||||
500000000000000;500 TB;454.7 TiB;454.75 TiB
|
||||
562949953421312;562.9 TB;512 TiB;512 TiB
|
||||
990000000000000;990 TB;900.4 TiB;900.40 TiB
|
||||
999900000000000;999.9 TB;909.4 TiB;909.40 TiB
|
||||
1000000000000000;1 PB;909.5 TiB;909.49 TiB
|
||||
1114640907774197;1.1 PB;1013.8 TiB;1013.76 TiB
|
||||
1125787316851939;1.1 PB;1023.9 TiB;1023.90 TiB
|
||||
1125899906842624;1.1 PB;1 PiB;1 PiB
|
||||
1500000000000000;1.5 PB;1.3 PiB;1.33 PiB
|
||||
1688849860263936;1.7 PB;1.5 PiB;1.50 PiB
|
||||
100000000000000000;100 PB;88.8 PiB;88.82 PiB
|
||||
115292150460684704;115.3 PB;102.4 PiB;102.40 PiB
|
||||
500000000000000000;500 PB;444.1 PiB;444.09 PiB
|
||||
576460752303423488;576.5 PB;512 PiB;512 PiB
|
||||
990000000000000000;990 PB;879.3 PiB;879.30 PiB
|
||||
999900000000000000;999.9 PB;888.1 PiB;888.09 PiB
|
||||
1000000000000000000;1000 PB;888.2 PiB;888.18 PiB
|
||||
1141392289560778496;1141.4 PB;1013.8 PiB;1013.76 PiB
|
||||
1152806212456386304;1152.8 PB;1023.9 PiB;1023.90 PiB
|
||||
1152921504606846976;1152.9 PB;1024 PiB;1024 PiB
|
||||
1500000000000000000;1500 PB;1332.3 PiB;1332.27 PiB
|
||||
1729382256910270464;1729.4 PB;1536 PiB;1536 PiB
|
||||
74
test/data/b2h_test_fr_FR.UTF-8.dat
Normal file
74
test/data/b2h_test_fr_FR.UTF-8.dat
Normal file
@@ -0,0 +1,74 @@
|
||||
0;0 o;0 o;0 o
|
||||
1;1 o;1 o;1 o
|
||||
100;100 o;100 o;100 o
|
||||
102;102 o;102 o;102 o
|
||||
500;500 o;500 o;500 o
|
||||
512;512 o;512 o;512 o
|
||||
990;990 o;990 o;990 o
|
||||
999;999 o;999 o;999 o
|
||||
1000;1 ko;1000 o;1000 o
|
||||
1013;1 ko;1013 o;1013 o
|
||||
1023;1 ko;1023 o;1023 o
|
||||
1024;1 ko;1 Kio;1 Kio
|
||||
1500;1,5 ko;1,5 Kio;1,46 Kio
|
||||
1536;1,5 ko;1,5 Kio;1,50 Kio
|
||||
100000;100 ko;97,7 Kio;97,66 Kio
|
||||
104857;104,9 ko;102,4 Kio;102,40 Kio
|
||||
500000;500 ko;488,3 Kio;488,28 Kio
|
||||
524288;524,3 ko;512 Kio;512 Kio
|
||||
990000;990 ko;966,8 Kio;966,80 Kio
|
||||
999900;999,9 ko;976,5 Kio;976,46 Kio
|
||||
1000000;1 Mo;976,6 Kio;976,56 Kio
|
||||
1038090;1 Mo;1013,8 Kio;1013,76 Kio
|
||||
1048471;1 Mo;1023,9 Kio;1023,90 Kio
|
||||
1048576;1 Mo;1 Mio;1 Mio
|
||||
1500000;1,5 Mo;1,4 Mio;1,43 Mio
|
||||
1572864;1,6 Mo;1,5 Mio;1,50 Mio
|
||||
100000000;100 Mo;95,4 Mio;95,37 Mio
|
||||
107374182;107,4 Mo;102,4 Mio;102,40 Mio
|
||||
500000000;500 Mo;476,8 Mio;476,84 Mio
|
||||
536870912;536,9 Mo;512 Mio;512 Mio
|
||||
990000000;990 Mo;944,1 Mio;944,14 Mio
|
||||
999900000;999,9 Mo;953,6 Mio;953,58 Mio
|
||||
1000000000;1 Go;953,7 Mio;953,67 Mio
|
||||
1063004405;1,1 Go;1013,8 Mio;1013,76 Mio
|
||||
1073634449;1,1 Go;1023,9 Mio;1023,90 Mio
|
||||
1073741824;1,1 Go;1 Gio;1 Gio
|
||||
1500000000;1,5 Go;1,4 Gio;1,40 Gio
|
||||
1610612736;1,6 Go;1,5 Gio;1,50 Gio
|
||||
100000000000;100 Go;93,1 Gio;93,13 Gio
|
||||
109951162777;110,0 Go;102,4 Gio;102,40 Gio
|
||||
500000000000;500 Go;465,7 Gio;465,66 Gio
|
||||
549755813888;549,8 Go;512 Gio;512 Gio
|
||||
990000000000;990 Go;922 Gio;922,01 Gio
|
||||
999900000000;999,9 Go;931,2 Gio;931,23 Gio
|
||||
1000000000000;1 To;931,3 Gio;931,32 Gio
|
||||
1088516511498;1,1 To;1013,8 Gio;1013,76 Gio
|
||||
1099401676613;1,1 To;1023,9 Gio;1023,90 Gio
|
||||
1099511627776;1,1 To;1 Tio;1 Tio
|
||||
1500000000000;1,5 To;1,4 Tio;1,36 Tio
|
||||
1649267441664;1,6 To;1,5 Tio;1,50 Tio
|
||||
100000000000000;100 To;90,9 Tio;90,95 Tio
|
||||
112589990684262;112,6 To;102,4 Tio;102,40 Tio
|
||||
500000000000000;500 To;454,7 Tio;454,75 Tio
|
||||
562949953421312;562,9 To;512 Tio;512 Tio
|
||||
990000000000000;990 To;900,4 Tio;900,40 Tio
|
||||
999900000000000;999,9 To;909,4 Tio;909,40 Tio
|
||||
1000000000000000;1 Po;909,5 Tio;909,49 Tio
|
||||
1114640907774197;1,1 Po;1013,8 Tio;1013,76 Tio
|
||||
1125787316851939;1,1 Po;1023,9 Tio;1023,90 Tio
|
||||
1125899906842624;1,1 Po;1 Pio;1 Pio
|
||||
1500000000000000;1,5 Po;1,3 Pio;1,33 Pio
|
||||
1688849860263936;1,7 Po;1,5 Pio;1,50 Pio
|
||||
100000000000000000;100 Po;88,8 Pio;88,82 Pio
|
||||
115292150460684704;115,3 Po;102,4 Pio;102,40 Pio
|
||||
500000000000000000;500 Po;444,1 Pio;444,09 Pio
|
||||
576460752303423488;576,5 Po;512 Pio;512 Pio
|
||||
990000000000000000;990 Po;879,3 Pio;879,30 Pio
|
||||
999900000000000000;999,9 Po;888,1 Pio;888,09 Pio
|
||||
1000000000000000000;1000 Po;888,2 Pio;888,18 Pio
|
||||
1141392289560778496;1141,4 Po;1013,8 Pio;1013,76 Pio
|
||||
1152806212456386304;1152,8 Po;1023,9 Pio;1023,90 Pio
|
||||
1152921504606846976;1152,9 Po;1024 Pio;1024 Pio
|
||||
1500000000000000000;1500 Po;1332,3 Pio;1332,27 Pio
|
||||
1729382256910270464;1729,4 Po;1536 Pio;1536 Pio
|
||||
69
test/po/fr.po
Normal file
69
test/po/fr.po
Normal file
@@ -0,0 +1,69 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: MusicBrainz\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2013-01-20 13:23+0100\n"
|
||||
"PO-Revision-Date: 2013-01-20 13:26+0100\n"
|
||||
"Last-Translator: Laurent Monin <i18n@norz.org>\n"
|
||||
"Language-Team: French (http://www.transifex.com/projects/p/musicbrainz/language/fr/)\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 0.9.6\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: picard/util/bytes2human.py:47
|
||||
#, python-format
|
||||
msgid "%s B"
|
||||
msgstr "%s o"
|
||||
|
||||
#: picard/util/bytes2human.py:48
|
||||
#, python-format
|
||||
msgid "%s kB"
|
||||
msgstr "%s ko"
|
||||
|
||||
#: picard/util/bytes2human.py:49
|
||||
#, python-format
|
||||
msgid "%s KiB"
|
||||
msgstr "%s Kio"
|
||||
|
||||
#: picard/util/bytes2human.py:50
|
||||
#, python-format
|
||||
msgid "%s MB"
|
||||
msgstr "%s Mo"
|
||||
|
||||
#: picard/util/bytes2human.py:51
|
||||
#, python-format
|
||||
msgid "%s MiB"
|
||||
msgstr "%s Mio"
|
||||
|
||||
#: picard/util/bytes2human.py:52
|
||||
#, python-format
|
||||
msgid "%s GB"
|
||||
msgstr "%s Go"
|
||||
|
||||
#: picard/util/bytes2human.py:53
|
||||
#, python-format
|
||||
msgid "%s GiB"
|
||||
msgstr "%s Gio"
|
||||
|
||||
#: picard/util/bytes2human.py:54
|
||||
#, python-format
|
||||
msgid "%s TB"
|
||||
msgstr "%s To"
|
||||
|
||||
#: picard/util/bytes2human.py:55
|
||||
#, python-format
|
||||
msgid "%s TiB"
|
||||
msgstr "%s Tio"
|
||||
|
||||
#: picard/util/bytes2human.py:56
|
||||
#, python-format
|
||||
msgid "%s PB"
|
||||
msgstr "%s Po"
|
||||
|
||||
#: picard/util/bytes2human.py:57
|
||||
#, python-format
|
||||
msgid "%s PiB"
|
||||
msgstr "%s Pio"
|
||||
99
test/test_bytes2human.py
Normal file
99
test/test_bytes2human.py
Normal file
@@ -0,0 +1,99 @@
|
||||
import locale
|
||||
import os.path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from picard.i18n import setup_gettext
|
||||
from picard.util import bytes2human
|
||||
|
||||
class Testbytes2human(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# we are using temporary locales for tests
|
||||
self.tmp_path = tempfile.mkdtemp().decode("utf-8")
|
||||
if sys.hexversion >= 0x020700F0:
|
||||
self.addCleanup(shutil.rmtree, self.tmp_path)
|
||||
self.localedir = os.path.join(self.tmp_path, 'locale')
|
||||
|
||||
test_locales = [('picard', 'fr', 'test/po/fr.po')]
|
||||
for domain, locale, po in test_locales:
|
||||
path = os.path.join(self.localedir, locale, 'LC_MESSAGES')
|
||||
os.makedirs(path)
|
||||
mo = os.path.join(path, '%s.mo' % domain)
|
||||
assert(subprocess.call(['msgfmt', '-o', mo, po]) == 0)
|
||||
|
||||
def tearDown(self):
|
||||
if sys.hexversion < 0x020700F0:
|
||||
shutil.rmtree(self.tmp_path)
|
||||
|
||||
def test_00(self):
|
||||
# testing with default C locale, english
|
||||
lang = 'C'
|
||||
setup_gettext(self.localedir, lang)
|
||||
self.run_test(lang)
|
||||
|
||||
self.assertEqual(bytes2human.binary(45682), '44.6 KiB')
|
||||
self.assertEqual(bytes2human.binary(-45682), '-44.6 KiB')
|
||||
self.assertEqual(bytes2human.decimal(45682), '45.7 kB')
|
||||
self.assertEqual(bytes2human.decimal(9223372036854775807), '9223.4 PB')
|
||||
self.assertEqual(bytes2human.decimal(123.6), '123 B')
|
||||
self.assertRaises(ValueError, bytes2human.decimal, 'xxx')
|
||||
self.assertRaises(ValueError, bytes2human.decimal, '123.6')
|
||||
self.assertRaises(ValueError, bytes2human.binary, 'yyy')
|
||||
self.assertRaises(ValueError, bytes2human.binary, '456yyy')
|
||||
try:
|
||||
bytes2human.decimal(u'123')
|
||||
except Exception as e:
|
||||
self.fail('Unexpected exception: %s' % e)
|
||||
|
||||
def test_05(self):
|
||||
# testing with french locale and translation
|
||||
# 1.5 MiB -> 1,5 Mio
|
||||
lang = 'fr_FR.UTF-8'
|
||||
setup_gettext(self.localedir, lang)
|
||||
self.run_test(lang)
|
||||
|
||||
def run_test(self, lang = 'C', create_test_data=False):
|
||||
"""
|
||||
Compare data generated with sample files
|
||||
Setting create_test_data to True will generated sample files
|
||||
from code execution (developper-only, check carefully)
|
||||
"""
|
||||
filename = os.path.join('test', 'data', 'b2h_test_%s.dat' % lang)
|
||||
testlist = self._create_testlist()
|
||||
if create_test_data:
|
||||
self._save_expected_to(filename, testlist)
|
||||
expected = self._read_expected_from(filename)
|
||||
#self.maxDiff = None
|
||||
self.assertEqual(testlist, expected)
|
||||
if create_test_data:
|
||||
# be sure it is disabled
|
||||
self.fail('!!! UNSET create_test_data mode !!! (%s)' % filename)
|
||||
|
||||
def _create_testlist(self):
|
||||
values = [0, 1]
|
||||
for n in [1000, 1024]:
|
||||
p = 1
|
||||
for e in range(0,6):
|
||||
p *= n
|
||||
for x in [0.1, 0.5, 0.99, 0.9999, 1, 1.5]:
|
||||
values.append(int(p*x))
|
||||
l = []
|
||||
for x in sorted(values):
|
||||
l.append(";".join([str(x), bytes2human.decimal(x),
|
||||
bytes2human.binary(x),
|
||||
bytes2human.short_string(x, 1024, 2)]))
|
||||
return l
|
||||
|
||||
def _save_expected_to(self, path, a_list):
|
||||
with open(path, 'wb') as f:
|
||||
f.writelines([l + "\n" for l in a_list])
|
||||
f.close()
|
||||
|
||||
def _read_expected_from(self, path):
|
||||
with open(path, 'rb') as f:
|
||||
lines = [l.rstrip("\n") for l in f.readlines()]
|
||||
f.close()
|
||||
return lines
|
||||
Reference in New Issue
Block a user