mirror of
https://github.com/beetbox/beets.git
synced 2025-12-25 18:13:17 +01:00
Merge pull request #264 from duailibe/master
Adding support for the "Date added field"
This commit is contained in:
commit
cb8195fe2b
4 changed files with 56 additions and 9 deletions
|
|
@ -24,6 +24,7 @@ import unicodedata
|
|||
import threading
|
||||
import contextlib
|
||||
import traceback
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from unidecode import unidecode
|
||||
from beets.mediafile import MediaFile
|
||||
|
|
@ -36,6 +37,10 @@ import beets
|
|||
|
||||
MAX_FILENAME_LENGTH = 200
|
||||
|
||||
# This is the default format when printing the import time
|
||||
# of an object. This needs to be a format accepted by time.strftime()
|
||||
ITIME_FORMAT = '%Y-%m-%d %H:%M:%S'
|
||||
|
||||
# Fields in the "items" database table; all the metadata available for
|
||||
# items in the library. These are used directly in SQL; they are
|
||||
# vulnerable to injection if accessible to the user.
|
||||
|
|
@ -105,6 +110,7 @@ ITEM_FIELDS = [
|
|||
('bitdepth', 'int', False, True),
|
||||
('channels', 'int', False, True),
|
||||
('mtime', 'int', False, False),
|
||||
('itime', 'datetime', 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]]
|
||||
|
|
@ -142,10 +148,12 @@ ALBUM_FIELDS = [
|
|||
('media', 'text', True),
|
||||
('albumdisambig', 'text', True),
|
||||
('rg_album_gain', 'real', True),
|
||||
('rg_album_peak', 'real', True),
|
||||
('rg_album_peak', 'real', True),®
|
||||
('original_year', 'int', True),
|
||||
('original_month', 'int', True),
|
||||
('original_day', 'int', True),
|
||||
|
||||
('itime', 'datetime', False),
|
||||
]
|
||||
ALBUM_KEYS = [f[0] for f in ALBUM_FIELDS]
|
||||
ALBUM_KEYS_ITEM = [f[0] for f in ALBUM_FIELDS if f[2]]
|
||||
|
|
@ -394,6 +402,9 @@ class Item(object):
|
|||
if not sanitize:
|
||||
mapping['path'] = displayable_path(self.path)
|
||||
|
||||
# Convert the import time to human readable
|
||||
mapping['itime'] = time.strftime(ITIME_FORMAT, time.localtime(getattr(self, 'itime')))
|
||||
|
||||
# Use the album artist if the track artist is not set and
|
||||
# vice-versa.
|
||||
if not mapping['artist']:
|
||||
|
|
@ -1289,6 +1300,7 @@ class Library(BaseLibrary):
|
|||
# Item manipulation.
|
||||
|
||||
def add(self, item, copy=False):
|
||||
item.itime = time.time()
|
||||
item.library = self
|
||||
if copy:
|
||||
self.move(item, copy=True)
|
||||
|
|
@ -1498,15 +1510,21 @@ class Library(BaseLibrary):
|
|||
from its items. The items are added to the database if they
|
||||
don't yet have an ID. Returns an Album object.
|
||||
"""
|
||||
album_keys = ALBUM_KEYS_ITEM + ['itime']
|
||||
|
||||
# Set the metadata from the first item.
|
||||
item_values = dict(
|
||||
album_values = dict(
|
||||
(key, getattr(items[0], key)) for key in ALBUM_KEYS_ITEM)
|
||||
|
||||
# Manually set the date when the album was added,
|
||||
# because the items don't yet have these
|
||||
album_values['itime'] = time.time()
|
||||
|
||||
with self.transaction() as tx:
|
||||
sql = 'INSERT INTO albums (%s) VALUES (%s)' % \
|
||||
(', '.join(ALBUM_KEYS_ITEM),
|
||||
', '.join(['?'] * len(ALBUM_KEYS_ITEM)))
|
||||
subvals = [item_values[key] for key in ALBUM_KEYS_ITEM]
|
||||
(', '.join(album_keys),
|
||||
', '.join(['?'] * len(album_keys)))
|
||||
subvals = [album_values[key] for key in album_keys]
|
||||
album_id = tx.mutate(sql, subvals)
|
||||
|
||||
# Add the items to the library.
|
||||
|
|
@ -1520,8 +1538,8 @@ class Library(BaseLibrary):
|
|||
# Construct the new Album object.
|
||||
record = {}
|
||||
for key in ALBUM_KEYS:
|
||||
if key in ALBUM_KEYS_ITEM:
|
||||
record[key] = item_values[key]
|
||||
if key in album_keys:
|
||||
record[key] = album_values[key]
|
||||
else:
|
||||
# Non-item fields default to None.
|
||||
record[key] = None
|
||||
|
|
@ -1730,6 +1748,9 @@ class Album(BaseAlbum):
|
|||
mapping['artpath'] = displayable_path(mapping['artpath'])
|
||||
mapping['path'] = displayable_path(self.item_dir())
|
||||
|
||||
# Convert the import time to human readable format
|
||||
mapping['itime'] = time.strftime(ITIME_FORMAT, time.localtime(mapping['itime']))
|
||||
|
||||
# Get template functions.
|
||||
funcs = DefaultTemplateFunctions().functions()
|
||||
funcs.update(plugins.template_funcs())
|
||||
|
|
@ -1818,6 +1839,12 @@ class DefaultTemplateFunctions(object):
|
|||
"""
|
||||
return unidecode(s)
|
||||
|
||||
@staticmethod
|
||||
def tmpl_format(s, format):
|
||||
"""Format the import time to any format according to time.strfime()
|
||||
"""
|
||||
return time.strftime(format, time.strptime(s, ITIME_FORMAT))
|
||||
|
||||
def tmpl_aunique(self, keys=None, disam=None):
|
||||
"""Generate a string that is guaranteed to be unique among all
|
||||
albums in the library who share the same set of keys. A fields
|
||||
|
|
|
|||
|
|
@ -993,6 +993,7 @@ CONSTRUCTOR_MAPPING = {
|
|||
'int': int,
|
||||
'bool': util.str2bool,
|
||||
'real': float,
|
||||
'datetime': lambda v: int(time.mktime(time.strptime(v, library.ITIME_FORMAT))),
|
||||
}
|
||||
|
||||
# Convert a string (from user input) to the correct Python type
|
||||
|
|
@ -1013,7 +1014,7 @@ def _convert_type(key, value, album=False):
|
|||
def modify_items(lib, mods, query, write, move, album, confirm):
|
||||
"""Modifies matching items according to key=value assignments."""
|
||||
# Parse key=value specifications into a dictionary.
|
||||
allowed_keys = library.ALBUM_KEYS if album else library.ITEM_KEYS_WRITABLE
|
||||
allowed_keys = library.ALBUM_KEYS if album else library.ITEM_KEYS_WRITABLE + ['itime']
|
||||
fsets = {}
|
||||
for mod in mods:
|
||||
key, value = mod.split('=', 1)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ track's artists.
|
|||
|
||||
These functions are built in to beets:
|
||||
|
||||
* ``%lower{text}``: Convert ``text`` to lowercase.
|
||||
* ``%lower{text}``: Convert ``text`` to lowercase.
|
||||
* ``%upper{text}``: Convert ``text`` to UPPERCASE.
|
||||
* ``%title{text}``: Convert ``text`` to Title Case.
|
||||
* ``%left{text,n}``: Return the first ``n`` characters of ``text``.
|
||||
|
|
@ -70,8 +70,12 @@ These functions are built in to beets:
|
|||
`unidecode module`_.
|
||||
* ``%aunique{identifiers,disambiguators}``: Provides a unique string to
|
||||
disambiguate similar albums in the database. See :ref:`aunique`, below.
|
||||
* ``%format{date_time,format}``: Return the date and time in any format accepted
|
||||
by the `time.strfime() method`_. Should probably be used together with the
|
||||
``itime`` field (import time).
|
||||
|
||||
.. _unidecode module: http://pypi.python.org/pypi/Unidecode
|
||||
.. _time.strftime() method: http://docs.python.org/2/library/time.html#time.strftime
|
||||
|
||||
Plugins can extend beets with more template functions (see
|
||||
:ref:`writing-plugins`).
|
||||
|
|
|
|||
|
|
@ -967,6 +967,21 @@ class MtimeTest(unittest.TestCase):
|
|||
self.i.read()
|
||||
self.assertGreaterEqual(self.i.mtime, self._mtime())
|
||||
|
||||
class ImportTimeTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
|
||||
def test_itime_for_album(self):
|
||||
self.track = item()
|
||||
self.album = self.lib.add_album((self.track,))
|
||||
self.assertGreater(self.album.itime, 0)
|
||||
self.assertGreater(self.track.itime, 0)
|
||||
|
||||
def test_atime_for_singleton(self):
|
||||
self.singleton = item()
|
||||
self.lib.add(self.singleton)
|
||||
self.assertGreater(self.singleton.itime, 0)
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue