mirror of
https://github.com/beetbox/beets.git
synced 2026-01-06 16:02:53 +01:00
Format track duration as H:MM instead of seconds
* Modify library.Item in order to have length formatted as H:MM instead of the raw number of seconds by using a types.Float subclass (DurationType). * Add library.DurationType, with custom format() and parse() methods that handle the conversion. * Add dbcore.query.DurationQuery as a NumericQuery subclass that _convert()s the ranges specified by the user to floats, delegating the rest of the functionality in the parent NumericQuery class. * Add ui.raw_seconds_short() as the reverse of human_seconds_short(). This function uses a regular expression in order to allow any number of minutes, and always required SS to have two digits.
This commit is contained in:
parent
5c8acc9a49
commit
67eb0ed54c
3 changed files with 62 additions and 1 deletions
|
|
@ -653,6 +653,35 @@ class DateQuery(FieldQuery):
|
|||
return clause, subvals
|
||||
|
||||
|
||||
class DurationQuery(NumericQuery):
|
||||
"""NumericQuery that allow human-friendly (M:SS) time interval formats.
|
||||
|
||||
Converts the range(s) to a float value, and delegates on NumericQuery.
|
||||
|
||||
Raises InvalidQueryError when the pattern does not represent an int, float
|
||||
or M:SS time interval.
|
||||
"""
|
||||
def _convert(self, s):
|
||||
"""Convert a M:SS or numeric string to a float.
|
||||
|
||||
Return None if `s` is empty.
|
||||
Raise an InvalidQueryError if the string cannot be converted.
|
||||
"""
|
||||
if not s:
|
||||
return None
|
||||
try:
|
||||
# TODO: tidy up circular import
|
||||
from beets.ui import raw_seconds_short
|
||||
return raw_seconds_short(s)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(s)
|
||||
except ValueError:
|
||||
raise InvalidQueryArgumentTypeError(
|
||||
s,
|
||||
"a M:SS string or a float")
|
||||
|
||||
|
||||
# Sorting.
|
||||
|
||||
class Sort(object):
|
||||
|
|
|
|||
|
|
@ -195,6 +195,25 @@ class MusicalKey(types.String):
|
|||
return self.parse(key)
|
||||
|
||||
|
||||
class DurationType(types.Float):
|
||||
"""Human-friendly (M:SS) representation of a time interval."""
|
||||
query = dbcore.query.DurationQuery
|
||||
|
||||
def format(self, value):
|
||||
return beets.ui.human_seconds_short(value or 0.0)
|
||||
|
||||
def parse(self, string):
|
||||
try:
|
||||
# Try to format back hh:ss to seconds.
|
||||
return beets.ui.raw_seconds_short(value)
|
||||
except ValueError:
|
||||
# Fall back to a plain float..
|
||||
try:
|
||||
return float(string)
|
||||
except ValueError:
|
||||
return self.null
|
||||
|
||||
|
||||
# Library-specific sort types.
|
||||
|
||||
class SmartArtistSort(dbcore.query.Sort):
|
||||
|
|
@ -426,7 +445,7 @@ class Item(LibModel):
|
|||
'original_day': types.PaddedInt(2),
|
||||
'initial_key': MusicalKey(),
|
||||
|
||||
'length': types.FLOAT,
|
||||
'length': DurationType(),
|
||||
'bitrate': types.ScaledInt(1000, u'kbps'),
|
||||
'format': types.STRING,
|
||||
'samplerate': types.ScaledInt(1000, u'kHz'),
|
||||
|
|
|
|||
|
|
@ -416,6 +416,19 @@ def human_seconds_short(interval):
|
|||
return u'%i:%02i' % (interval // 60, interval % 60)
|
||||
|
||||
|
||||
def raw_seconds_short(string):
|
||||
"""Formats a human-readable M:SS string as a float (number of seconds).
|
||||
|
||||
Raises ValueError if the conversion cannot take place due to `string` not
|
||||
being in the right format.
|
||||
"""
|
||||
match = re.match('^(\d+):([0-5]\d)$', string)
|
||||
if not match:
|
||||
raise ValueError('String not in M:SS format')
|
||||
minutes, seconds = map(int, match.groups())
|
||||
return float(minutes*60 + seconds)
|
||||
|
||||
|
||||
# Colorization.
|
||||
|
||||
# ANSI terminal colorization code heavily inspired by pygments:
|
||||
|
|
|
|||
Loading…
Reference in a new issue