mirror of
https://github.com/beetbox/beets.git
synced 2026-02-15 03:41:45 +01:00
new dbcore.types module
Now Type is proper, full-on class so we don't have to fit everything in a lambda.
This commit is contained in:
parent
720587e28e
commit
15510b6187
4 changed files with 181 additions and 119 deletions
|
|
@ -15,5 +15,6 @@
|
|||
"""DBCore is an abstract database package that forms the basis for beets'
|
||||
Library.
|
||||
"""
|
||||
from .db import Type, Model, Database
|
||||
from .db import Model, Database
|
||||
from .query import Query, FieldQuery, MatchQuery, AndQuery
|
||||
from .types import Type
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
"""
|
||||
import time
|
||||
import os
|
||||
from collections import defaultdict, namedtuple
|
||||
from collections import defaultdict
|
||||
import threading
|
||||
import sqlite3
|
||||
import contextlib
|
||||
|
|
@ -27,10 +27,7 @@ from .query import MatchQuery
|
|||
|
||||
|
||||
|
||||
# Abstract base for model classes and their field types.
|
||||
|
||||
|
||||
Type = namedtuple('Type', 'sql query format')
|
||||
# Abstract base for model classes.
|
||||
|
||||
|
||||
class Model(object):
|
||||
|
|
|
|||
83
beets/dbcore/types.py
Normal file
83
beets/dbcore/types.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# This file is part of beets.
|
||||
# Copyright 2014, Adrian Sampson.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""Representation of type information for DBCore model fields.
|
||||
"""
|
||||
from . import query
|
||||
|
||||
|
||||
class Type(object):
|
||||
"""An object encapsulating the type of a model field. Includes
|
||||
information about how to store the value in the database, query,
|
||||
format, and parse a given field.
|
||||
"""
|
||||
def __init__(self, sql, query, format_func=None):
|
||||
"""Create a type. `sql` is the SQLite column type for the value.
|
||||
`query` is the `Query` subclass to be used when querying the
|
||||
field. `format_func` is a function that transforms values of
|
||||
this type to a human-readable Unicode string. If `format_func`
|
||||
is not provided, the subclass must override `format` to provide
|
||||
the functionality.
|
||||
"""
|
||||
self.sql = sql
|
||||
self.query = query
|
||||
self.format_func = format_func
|
||||
|
||||
def format(self, value):
|
||||
"""Given a value of this type, produce a Unicode string
|
||||
representing the value. This is used in template evaluation.
|
||||
"""
|
||||
return self.format_func(value)
|
||||
|
||||
|
||||
# Common singleton types.
|
||||
|
||||
ID_TYPE = Type('INTEGER PRIMARY KEY', query.NumericQuery, unicode)
|
||||
INT_TYPE = Type('INTEGER', query.NumericQuery,
|
||||
lambda n: unicode(n or 0))
|
||||
FLOAT_TYPE = Type('REAL', query.NumericQuery,
|
||||
lambda n: u'{0:.1f}'.format(n or 0.0))
|
||||
STRING_TYPE = Type('TEXT', query.SubstringQuery,
|
||||
lambda s: s or u'')
|
||||
BOOL_TYPE = Type('INTEGER', query.BooleanQuery,
|
||||
lambda b: unicode(bool(b)))
|
||||
|
||||
|
||||
|
||||
# Parameterized types.
|
||||
|
||||
|
||||
class PaddedInt(Type):
|
||||
"""An integer field that is formatted with a given number of digits,
|
||||
padded with zeroes.
|
||||
"""
|
||||
def __init__(self, digits):
|
||||
self.digits = digits
|
||||
super(PaddedInt, self).__init__('INTEGER', query.NumericQuery)
|
||||
|
||||
def format(self, value):
|
||||
return u'{0:0{1}d}'.format(value or 0, self.digits)
|
||||
|
||||
|
||||
class ScaledInt(Type):
|
||||
"""An integer whose formatting operation scales the number by a
|
||||
constant and adds a suffix. Good for units with large magnitudes.
|
||||
"""
|
||||
def __init__(self, unit, suffix=u''):
|
||||
self.unit = unit
|
||||
self.suffix = suffix
|
||||
super(ScaledInt, self).__init__('INTEGER', query.NumericQuery)
|
||||
|
||||
def format(self, value):
|
||||
return u'{0}{1}'.format((value or 0) // self.unit, self.suffix)
|
||||
207
beets/library.py
207
beets/library.py
|
|
@ -29,9 +29,8 @@ from beets import util
|
|||
from beets.util import bytestring_path, syspath, normpath, samefile
|
||||
from beets.util.functemplate import Template
|
||||
from beets import dbcore
|
||||
from beets.dbcore import Type
|
||||
from beets.dbcore import types
|
||||
import beets
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
|
||||
|
|
@ -78,31 +77,13 @@ class SingletonQuery(dbcore.Query):
|
|||
# Model field lists.
|
||||
|
||||
# Common types used in field definitions.
|
||||
def _str_or_empty(s):
|
||||
if not s:
|
||||
return u''
|
||||
return unicode(s)
|
||||
ID_TYPE = Type('INTEGER PRIMARY KEY', dbcore.query.NumericQuery, _str_or_empty)
|
||||
INT_TYPE = Type('INTEGER', dbcore.query.NumericQuery, _str_or_empty)
|
||||
FLOAT_TYPE = Type('REAL', dbcore.query.NumericQuery,
|
||||
lambda n: u'{0:.1f}'.format(n or 0.0))
|
||||
DATE_TYPE = Type(
|
||||
DATE_TYPE = types.Type(
|
||||
'REAL',
|
||||
dbcore.query.NumericQuery,
|
||||
lambda n: time.strftime(beets.config['time_format'].get(unicode),
|
||||
time.localtime(n or 0))
|
||||
)
|
||||
STRING_TYPE = Type('TEXT', dbcore.query.SubstringQuery, _str_or_empty)
|
||||
BOOL_TYPE = Type('INTEGER', dbcore.query.BooleanQuery, _str_or_empty)
|
||||
PATH_TYPE = Type('BLOB', PathQuery, util.displayable_path)
|
||||
|
||||
def _padded_int(digits):
|
||||
return Type('INTEGER', dbcore.query.NumericQuery,
|
||||
lambda n: u'{0:0{1}d}'.format(n or 0, digits))
|
||||
|
||||
def _scaled_int(suffix=u'', unit=1000):
|
||||
return Type('INTEGER', dbcore.query.NumericQuery,
|
||||
lambda n: u'{0}{1}'.format((n or 0) // unit, suffix))
|
||||
PATH_TYPE = types.Type('BLOB', PathQuery, util.displayable_path)
|
||||
|
||||
# Fields in the "items" database table; all the metadata available for
|
||||
# items in the library. These are used directly in SQL; they are
|
||||
|
|
@ -113,67 +94,67 @@ def _scaled_int(suffix=u'', unit=1000):
|
|||
# - Is the field writable?
|
||||
# - Does the field reflect an attribute of a MediaFile?
|
||||
ITEM_FIELDS = [
|
||||
('id', ID_TYPE, False, False),
|
||||
('path', PATH_TYPE, False, False),
|
||||
('album_id', INT_TYPE, False, False),
|
||||
('id', types.ID_TYPE, False, False),
|
||||
('path', PATH_TYPE, False, False),
|
||||
('album_id', types.INT_TYPE, False, False),
|
||||
|
||||
('title', STRING_TYPE, True, True),
|
||||
('artist', STRING_TYPE, True, True),
|
||||
('artist_sort', STRING_TYPE, True, True),
|
||||
('artist_credit', STRING_TYPE, True, True),
|
||||
('album', STRING_TYPE, True, True),
|
||||
('albumartist', STRING_TYPE, True, True),
|
||||
('albumartist_sort', STRING_TYPE, True, True),
|
||||
('albumartist_credit', STRING_TYPE, True, True),
|
||||
('genre', STRING_TYPE, True, True),
|
||||
('composer', STRING_TYPE, True, True),
|
||||
('grouping', STRING_TYPE, True, True),
|
||||
('year', _padded_int(4), True, True),
|
||||
('month', _padded_int(2), True, True),
|
||||
('day', _padded_int(2), True, True),
|
||||
('track', _padded_int(2), True, True),
|
||||
('tracktotal', _padded_int(2), True, True),
|
||||
('disc', _padded_int(2), True, True),
|
||||
('disctotal', _padded_int(2), True, True),
|
||||
('lyrics', STRING_TYPE, True, True),
|
||||
('comments', STRING_TYPE, True, True),
|
||||
('bpm', INT_TYPE, True, True),
|
||||
('comp', BOOL_TYPE, True, True),
|
||||
('mb_trackid', STRING_TYPE, True, True),
|
||||
('mb_albumid', STRING_TYPE, True, True),
|
||||
('mb_artistid', STRING_TYPE, True, True),
|
||||
('mb_albumartistid', STRING_TYPE, True, True),
|
||||
('albumtype', STRING_TYPE, True, True),
|
||||
('label', STRING_TYPE, True, True),
|
||||
('acoustid_fingerprint', STRING_TYPE, True, True),
|
||||
('acoustid_id', STRING_TYPE, True, True),
|
||||
('mb_releasegroupid', STRING_TYPE, True, True),
|
||||
('asin', STRING_TYPE, True, True),
|
||||
('catalognum', STRING_TYPE, True, True),
|
||||
('script', STRING_TYPE, True, True),
|
||||
('language', STRING_TYPE, True, True),
|
||||
('country', STRING_TYPE, True, True),
|
||||
('albumstatus', STRING_TYPE, True, True),
|
||||
('media', STRING_TYPE, True, True),
|
||||
('albumdisambig', STRING_TYPE, True, True),
|
||||
('disctitle', STRING_TYPE, True, True),
|
||||
('encoder', STRING_TYPE, True, True),
|
||||
('rg_track_gain', FLOAT_TYPE, True, True),
|
||||
('rg_track_peak', FLOAT_TYPE, True, True),
|
||||
('rg_album_gain', FLOAT_TYPE, True, True),
|
||||
('rg_album_peak', FLOAT_TYPE, True, True),
|
||||
('original_year', _padded_int(4), True, True),
|
||||
('original_month', _padded_int(2), True, True),
|
||||
('original_day', _padded_int(2), True, True),
|
||||
('title', types.STRING_TYPE, True, True),
|
||||
('artist', types.STRING_TYPE, True, True),
|
||||
('artist_sort', types.STRING_TYPE, True, True),
|
||||
('artist_credit', types.STRING_TYPE, True, True),
|
||||
('album', types.STRING_TYPE, True, True),
|
||||
('albumartist', types.STRING_TYPE, True, True),
|
||||
('albumartist_sort', types.STRING_TYPE, True, True),
|
||||
('albumartist_credit', types.STRING_TYPE, True, True),
|
||||
('genre', types.STRING_TYPE, True, True),
|
||||
('composer', types.STRING_TYPE, True, True),
|
||||
('grouping', types.STRING_TYPE, True, True),
|
||||
('year', types.PaddedInt(4), True, True),
|
||||
('month', types.PaddedInt(2), True, True),
|
||||
('day', types.PaddedInt(2), True, True),
|
||||
('track', types.PaddedInt(2), True, True),
|
||||
('tracktotal', types.PaddedInt(2), True, True),
|
||||
('disc', types.PaddedInt(2), True, True),
|
||||
('disctotal', types.PaddedInt(2), True, True),
|
||||
('lyrics', types.STRING_TYPE, True, True),
|
||||
('comments', types.STRING_TYPE, True, True),
|
||||
('bpm', types.INT_TYPE, True, True),
|
||||
('comp', types.BOOL_TYPE, True, True),
|
||||
('mb_trackid', types.STRING_TYPE, True, True),
|
||||
('mb_albumid', types.STRING_TYPE, True, True),
|
||||
('mb_artistid', types.STRING_TYPE, True, True),
|
||||
('mb_albumartistid', types.STRING_TYPE, True, True),
|
||||
('albumtype', types.STRING_TYPE, True, True),
|
||||
('label', types.STRING_TYPE, True, True),
|
||||
('acoustid_fingerprint', types.STRING_TYPE, True, True),
|
||||
('acoustid_id', types.STRING_TYPE, True, True),
|
||||
('mb_releasegroupid', types.STRING_TYPE, True, True),
|
||||
('asin', types.STRING_TYPE, True, True),
|
||||
('catalognum', types.STRING_TYPE, True, True),
|
||||
('script', types.STRING_TYPE, True, True),
|
||||
('language', types.STRING_TYPE, True, True),
|
||||
('country', types.STRING_TYPE, True, True),
|
||||
('albumstatus', types.STRING_TYPE, True, True),
|
||||
('media', types.STRING_TYPE, True, True),
|
||||
('albumdisambig', types.STRING_TYPE, True, True),
|
||||
('disctitle', types.STRING_TYPE, True, True),
|
||||
('encoder', types.STRING_TYPE, True, True),
|
||||
('rg_track_gain', types.FLOAT_TYPE, True, True),
|
||||
('rg_track_peak', types.FLOAT_TYPE, True, True),
|
||||
('rg_album_gain', types.FLOAT_TYPE, True, True),
|
||||
('rg_album_peak', types.FLOAT_TYPE, True, True),
|
||||
('original_year', types.PaddedInt(4), True, True),
|
||||
('original_month', types.PaddedInt(2), True, True),
|
||||
('original_day', types.PaddedInt(2), True, True),
|
||||
|
||||
('length', FLOAT_TYPE, False, True),
|
||||
('bitrate', _scaled_int(u'kbps'), False, True),
|
||||
('format', STRING_TYPE, False, True),
|
||||
('samplerate', _scaled_int(u'kHz'), False, True),
|
||||
('bitdepth', INT_TYPE, False, True),
|
||||
('channels', INT_TYPE, False, True),
|
||||
('mtime', DATE_TYPE, False, False),
|
||||
('added', DATE_TYPE, False, False),
|
||||
('length', types.FLOAT_TYPE, False, True),
|
||||
('bitrate', types.ScaledInt(1000, u'kbps'), False, True),
|
||||
('format', types.STRING_TYPE, False, True),
|
||||
('samplerate', types.ScaledInt(1000, u'kHz'), False, True),
|
||||
('bitdepth', types.INT_TYPE, False, True),
|
||||
('channels', types.INT_TYPE, False, True),
|
||||
('mtime', DATE_TYPE, False, False),
|
||||
('added', DATE_TYPE, False, False),
|
||||
]
|
||||
ITEM_KEYS_WRITABLE = [f[0] for f in ITEM_FIELDS if f[3] and f[2]]
|
||||
ITEM_KEYS_META = [f[0] for f in ITEM_FIELDS if f[3]]
|
||||
|
|
@ -183,39 +164,39 @@ ITEM_KEYS = [f[0] for f in ITEM_FIELDS]
|
|||
# The third entry in each tuple indicates whether the field reflects an
|
||||
# identically-named field in the items table.
|
||||
ALBUM_FIELDS = [
|
||||
('id', ID_TYPE, False),
|
||||
('artpath', PATH_TYPE, False),
|
||||
('added', DATE_TYPE, True),
|
||||
('id', types.ID_TYPE, False),
|
||||
('artpath', PATH_TYPE, False),
|
||||
('added', DATE_TYPE, True),
|
||||
|
||||
('albumartist', STRING_TYPE, True),
|
||||
('albumartist_sort', STRING_TYPE, True),
|
||||
('albumartist_credit', STRING_TYPE, True),
|
||||
('album', STRING_TYPE, True),
|
||||
('genre', STRING_TYPE, True),
|
||||
('year', _padded_int(4), True),
|
||||
('month', _padded_int(2), True),
|
||||
('day', _padded_int(2), True),
|
||||
('tracktotal', _padded_int(2), True),
|
||||
('disctotal', _padded_int(2), True),
|
||||
('comp', BOOL_TYPE, True),
|
||||
('mb_albumid', STRING_TYPE, True),
|
||||
('mb_albumartistid', STRING_TYPE, True),
|
||||
('albumtype', STRING_TYPE, True),
|
||||
('label', STRING_TYPE, True),
|
||||
('mb_releasegroupid', STRING_TYPE, True),
|
||||
('asin', STRING_TYPE, True),
|
||||
('catalognum', STRING_TYPE, True),
|
||||
('script', STRING_TYPE, True),
|
||||
('language', STRING_TYPE, True),
|
||||
('country', STRING_TYPE, True),
|
||||
('albumstatus', STRING_TYPE, True),
|
||||
('media', STRING_TYPE, True),
|
||||
('albumdisambig', STRING_TYPE, True),
|
||||
('rg_album_gain', FLOAT_TYPE, True),
|
||||
('rg_album_peak', FLOAT_TYPE, True),
|
||||
('original_year', _padded_int(4), True),
|
||||
('original_month', _padded_int(2), True),
|
||||
('original_day', _padded_int(2), True),
|
||||
('albumartist', types.STRING_TYPE, True),
|
||||
('albumartist_sort', types.STRING_TYPE, True),
|
||||
('albumartist_credit', types.STRING_TYPE, True),
|
||||
('album', types.STRING_TYPE, True),
|
||||
('genre', types.STRING_TYPE, True),
|
||||
('year', types.PaddedInt(4), True),
|
||||
('month', types.PaddedInt(2), True),
|
||||
('day', types.PaddedInt(2), True),
|
||||
('tracktotal', types.PaddedInt(2), True),
|
||||
('disctotal', types.PaddedInt(2), True),
|
||||
('comp', types.BOOL_TYPE, True),
|
||||
('mb_albumid', types.STRING_TYPE, True),
|
||||
('mb_albumartistid', types.STRING_TYPE, True),
|
||||
('albumtype', types.STRING_TYPE, True),
|
||||
('label', types.STRING_TYPE, True),
|
||||
('mb_releasegroupid', types.STRING_TYPE, True),
|
||||
('asin', types.STRING_TYPE, True),
|
||||
('catalognum', types.STRING_TYPE, True),
|
||||
('script', types.STRING_TYPE, True),
|
||||
('language', types.STRING_TYPE, True),
|
||||
('country', types.STRING_TYPE, True),
|
||||
('albumstatus', types.STRING_TYPE, True),
|
||||
('media', types.STRING_TYPE, True),
|
||||
('albumdisambig', types.STRING_TYPE, True),
|
||||
('rg_album_gain', types.FLOAT_TYPE, True),
|
||||
('rg_album_peak', types.FLOAT_TYPE, True),
|
||||
('original_year', types.PaddedInt(4), True),
|
||||
('original_month', types.PaddedInt(2), True),
|
||||
('original_day', types.PaddedInt(2), True),
|
||||
]
|
||||
ALBUM_KEYS = [f[0] for f in ALBUM_FIELDS]
|
||||
ALBUM_KEYS_ITEM = [f[0] for f in ALBUM_FIELDS if f[2]]
|
||||
|
|
|
|||
Loading…
Reference in a new issue