mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 08:39:17 +01:00
Merge branch 'master' into libmodels-formatting
Conflicts: beetsplug/embedart.py
This commit is contained in:
commit
060c275fd3
7 changed files with 152 additions and 60 deletions
|
|
@ -748,7 +748,6 @@ class Album(LibModel):
|
||||||
'year': types.PaddedInt(4),
|
'year': types.PaddedInt(4),
|
||||||
'month': types.PaddedInt(2),
|
'month': types.PaddedInt(2),
|
||||||
'day': types.PaddedInt(2),
|
'day': types.PaddedInt(2),
|
||||||
'tracktotal': types.PaddedInt(2),
|
|
||||||
'disctotal': types.PaddedInt(2),
|
'disctotal': types.PaddedInt(2),
|
||||||
'comp': types.BOOLEAN,
|
'comp': types.BOOLEAN,
|
||||||
'mb_albumid': types.STRING,
|
'mb_albumid': types.STRING,
|
||||||
|
|
@ -787,7 +786,6 @@ class Album(LibModel):
|
||||||
'year',
|
'year',
|
||||||
'month',
|
'month',
|
||||||
'day',
|
'day',
|
||||||
'tracktotal',
|
|
||||||
'disctotal',
|
'disctotal',
|
||||||
'comp',
|
'comp',
|
||||||
'mb_albumid',
|
'mb_albumid',
|
||||||
|
|
@ -819,6 +817,7 @@ class Album(LibModel):
|
||||||
# the album's directory as `path`.
|
# the album's directory as `path`.
|
||||||
getters = plugins.album_field_getters()
|
getters = plugins.album_field_getters()
|
||||||
getters['path'] = Album.item_dir
|
getters['path'] = Album.item_dir
|
||||||
|
getters['albumtotal'] = Album._albumtotal
|
||||||
return getters
|
return getters
|
||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
|
|
@ -906,6 +905,27 @@ class Album(LibModel):
|
||||||
raise ValueError('empty album')
|
raise ValueError('empty album')
|
||||||
return os.path.dirname(item.path)
|
return os.path.dirname(item.path)
|
||||||
|
|
||||||
|
def _albumtotal(self):
|
||||||
|
"""Return the total number of tracks on all discs on the album
|
||||||
|
"""
|
||||||
|
if self.disctotal == 1 or not beets.config['per_disc_numbering']:
|
||||||
|
return self.items()[0].tracktotal
|
||||||
|
|
||||||
|
counted = []
|
||||||
|
total = 0
|
||||||
|
|
||||||
|
for item in self.items():
|
||||||
|
if item.disc in counted:
|
||||||
|
continue
|
||||||
|
|
||||||
|
total += item.tracktotal
|
||||||
|
counted.append(item.disc)
|
||||||
|
|
||||||
|
if len(counted) == self.disctotal:
|
||||||
|
break
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
def art_destination(self, image, item_dir=None):
|
def art_destination(self, image, item_dir=None):
|
||||||
"""Returns a path to the destination for the album art image
|
"""Returns a path to the destination for the album art image
|
||||||
for the album. `image` is the path of the image that will be
|
for the album. `image` is the path of the image that will be
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import subprocess
|
||||||
import platform
|
import platform
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
from beets import logging
|
|
||||||
from beets.plugins import BeetsPlugin
|
from beets.plugins import BeetsPlugin
|
||||||
from beets import mediafile
|
from beets import mediafile
|
||||||
from beets import ui
|
from beets import ui
|
||||||
|
|
@ -43,13 +42,13 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
|
|
||||||
if self.config['maxwidth'].get(int) and not ArtResizer.shared.local:
|
if self.config['maxwidth'].get(int) and not ArtResizer.shared.local:
|
||||||
self.config['maxwidth'] = 0
|
self.config['maxwidth'] = 0
|
||||||
self._log.warn(u"ImageMagick or PIL not found; "
|
self._log.warning(u"ImageMagick or PIL not found; "
|
||||||
u"'maxwidth' option ignored")
|
u"'maxwidth' option ignored")
|
||||||
if self.config['compare_threshold'].get(int) and not \
|
if self.config['compare_threshold'].get(int) and not \
|
||||||
ArtResizer.shared.can_compare:
|
ArtResizer.shared.can_compare:
|
||||||
self.config['compare_threshold'] = 0
|
self.config['compare_threshold'] = 0
|
||||||
self._log.warn(u"ImageMagick 6.8.7 or higher not installed; "
|
self._log.warning(u"ImageMagick 6.8.7 or higher not installed; "
|
||||||
u"'compare_threshold' option ignored")
|
u"'compare_threshold' option ignored")
|
||||||
|
|
||||||
self.register_listener('album_imported', self.album_imported)
|
self.register_listener('album_imported', self.album_imported)
|
||||||
|
|
||||||
|
|
@ -118,15 +117,10 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
if compare_threshold:
|
if compare_threshold:
|
||||||
if not self.check_art_similarity(item, imagepath,
|
if not self.check_art_similarity(item, imagepath,
|
||||||
compare_threshold):
|
compare_threshold):
|
||||||
self._log.warn(u'Image not similar; skipping.')
|
self._log.info(u'Image not similar; skipping.')
|
||||||
return
|
return
|
||||||
if ifempty:
|
if ifempty and self.get_art(item):
|
||||||
art = self.get_art(item)
|
self._log.info(u'media file already contained art')
|
||||||
if not art:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self._log.debug(u'media file contained art already {0}',
|
|
||||||
displayable_path(imagepath))
|
|
||||||
return
|
return
|
||||||
if maxwidth and not as_album:
|
if maxwidth and not as_album:
|
||||||
imagepath = self.resize_image(imagepath, maxwidth)
|
imagepath = self.resize_image(imagepath, maxwidth)
|
||||||
|
|
@ -135,7 +129,7 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
self._log.debug(u'embedding {0}', displayable_path(imagepath))
|
self._log.debug(u'embedding {0}', displayable_path(imagepath))
|
||||||
item['images'] = [self._mediafile_image(imagepath, maxwidth)]
|
item['images'] = [self._mediafile_image(imagepath, maxwidth)]
|
||||||
except IOError as exc:
|
except IOError as exc:
|
||||||
self._log.error(u'could not read image file: {0}', exc)
|
self._log.warning(u'could not read image file: {0}', exc)
|
||||||
else:
|
else:
|
||||||
# We don't want to store the image in the database.
|
# We don't want to store the image in the database.
|
||||||
item.try_write(itempath)
|
item.try_write(itempath)
|
||||||
|
|
@ -146,19 +140,16 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
"""
|
"""
|
||||||
imagepath = album.artpath
|
imagepath = album.artpath
|
||||||
if not imagepath:
|
if not imagepath:
|
||||||
self._log.info(u'No album art present: {0}', album)
|
self._log.info(u'No album art present for {0}', album)
|
||||||
return
|
return
|
||||||
if not os.path.isfile(syspath(imagepath)):
|
if not os.path.isfile(syspath(imagepath)):
|
||||||
self._log.error(u'Album art not found at {0}',
|
self._log.info(u'Album art not found at {0} for {1}',
|
||||||
displayable_path(imagepath))
|
displayable_path(imagepath), album)
|
||||||
return
|
return
|
||||||
if maxwidth:
|
if maxwidth:
|
||||||
imagepath = self.resize_image(imagepath, maxwidth)
|
imagepath = self.resize_image(imagepath, maxwidth)
|
||||||
|
|
||||||
self._log.log(
|
self._log.info(u'Embedding album art into {0}', album)
|
||||||
logging.DEBUG if quiet else logging.INFO,
|
|
||||||
u'Embedding album art into {0}.', album
|
|
||||||
)
|
|
||||||
|
|
||||||
for item in album.items():
|
for item in album.items():
|
||||||
thresh = self.config['compare_threshold'].get(int)
|
thresh = self.config['compare_threshold'].get(int)
|
||||||
|
|
@ -169,7 +160,7 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
def resize_image(self, imagepath, maxwidth):
|
def resize_image(self, imagepath, maxwidth):
|
||||||
"""Returns path to an image resized to maxwidth.
|
"""Returns path to an image resized to maxwidth.
|
||||||
"""
|
"""
|
||||||
self._log.info(u'Resizing album art to {0} pixels wide', maxwidth)
|
self._log.debug(u'Resizing album art to {0} pixels wide', maxwidth)
|
||||||
imagepath = ArtResizer.shared.resize(maxwidth, syspath(imagepath))
|
imagepath = ArtResizer.shared.resize(maxwidth, syspath(imagepath))
|
||||||
return imagepath
|
return imagepath
|
||||||
|
|
||||||
|
|
@ -217,9 +208,8 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
out_str)
|
out_str)
|
||||||
return
|
return
|
||||||
|
|
||||||
self._log.info(u'compare PHASH score is {0}', phash_diff)
|
self._log.debug(u'compare PHASH score is {0}', phash_diff)
|
||||||
if phash_diff > compare_threshold:
|
return phash_diff <= compare_threshold
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -236,8 +226,8 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
try:
|
try:
|
||||||
mf = mediafile.MediaFile(syspath(item.path))
|
mf = mediafile.MediaFile(syspath(item.path))
|
||||||
except mediafile.UnreadableFileError as exc:
|
except mediafile.UnreadableFileError as exc:
|
||||||
self._log.error(u'Could not extract art from {0}: {1}',
|
self._log.warning(u'Could not extract art from {0}: {1}',
|
||||||
displayable_path(item.path), exc)
|
displayable_path(item.path), exc)
|
||||||
return
|
return
|
||||||
|
|
||||||
return mf.art
|
return mf.art
|
||||||
|
|
@ -245,20 +235,17 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
# 'extractart' command.
|
# 'extractart' command.
|
||||||
|
|
||||||
def extract(self, outpath, item):
|
def extract(self, outpath, item):
|
||||||
if not item:
|
|
||||||
self._log.error(u'No item matches query.')
|
|
||||||
return
|
|
||||||
|
|
||||||
art = self.get_art(item)
|
art = self.get_art(item)
|
||||||
|
|
||||||
if not art:
|
if not art:
|
||||||
self._log.error(u'No album art present in {0}.', item)
|
self._log.info(u'No album art present in {0}, skipping.', item)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Add an extension to the filename.
|
# Add an extension to the filename.
|
||||||
ext = imghdr.what(None, h=art)
|
ext = imghdr.what(None, h=art)
|
||||||
if not ext:
|
if not ext:
|
||||||
self._log.error(u'Unknown image type.')
|
self._log.warning(u'Unknown image type in {0}.',
|
||||||
|
displayable_path(item.path))
|
||||||
return
|
return
|
||||||
outpath += '.' + ext
|
outpath += '.' + ext
|
||||||
|
|
||||||
|
|
@ -270,15 +257,17 @@ class EmbedCoverArtPlugin(BeetsPlugin):
|
||||||
|
|
||||||
# 'clearart' command.
|
# 'clearart' command.
|
||||||
def clear(self, lib, query):
|
def clear(self, lib, query):
|
||||||
self._log.info(u'Clearing album art from items:')
|
id3v23 = config['id3v23'].get(bool)
|
||||||
for item in lib.items(query):
|
|
||||||
self._log.info(u'{0}', item)
|
items = lib.items(query)
|
||||||
|
self._log.info(u'Clearing album art from {0} items', len(items))
|
||||||
|
for item in items:
|
||||||
|
self._log.debug(u'Clearing art for {0}', item)
|
||||||
try:
|
try:
|
||||||
mf = mediafile.MediaFile(syspath(item.path),
|
mf = mediafile.MediaFile(syspath(item.path), id3v23)
|
||||||
config['id3v23'].get(bool))
|
|
||||||
except mediafile.UnreadableFileError as exc:
|
except mediafile.UnreadableFileError as exc:
|
||||||
self._log.error(u'Could not clear art from {0}: {1}',
|
self._log.warning(u'Could not read file {0}: {1}',
|
||||||
displayable_path(item.path), exc)
|
displayable_path(item.path), exc)
|
||||||
continue
|
else:
|
||||||
del mf.art
|
del mf.art
|
||||||
mf.save()
|
mf.save()
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,77 @@ class ITunesStore(ArtSource):
|
||||||
self._log.debug(u'album not found in iTunes Store')
|
self._log.debug(u'album not found in iTunes Store')
|
||||||
|
|
||||||
|
|
||||||
|
class Wikipedia(ArtSource):
|
||||||
|
# Art from Wikipedia (queried through DBpedia)
|
||||||
|
DBPEDIA_URL = 'http://dbpedia.org/sparql'
|
||||||
|
WIKIPEDIA_URL = 'http://en.wikipedia.org/w/api.php'
|
||||||
|
SPARQL_QUERY = '''PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
||||||
|
PREFIX dbpprop: <http://dbpedia.org/property/>
|
||||||
|
PREFIX owl: <http://dbpedia.org/ontology/>
|
||||||
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||||
|
SELECT DISTINCT ?coverFilename WHERE {{
|
||||||
|
?subject dbpprop:name ?name .
|
||||||
|
?subject rdfs:label ?label .
|
||||||
|
{{ ?subject dbpprop:artist ?artist }}
|
||||||
|
UNION
|
||||||
|
{{ ?subject owl:artist ?artist }}
|
||||||
|
{{ ?artist rdfs:label "{artist}"@en }}
|
||||||
|
UNION
|
||||||
|
{{ ?artist dbpprop:name "{artist}"@en }}
|
||||||
|
?subject rdf:type <http://dbpedia.org/ontology/Album> .
|
||||||
|
?subject dbpprop:cover ?coverFilename .
|
||||||
|
FILTER ( regex(?name, "{album}", "i") )
|
||||||
|
}}
|
||||||
|
Limit 1'''
|
||||||
|
|
||||||
|
def get(self, album):
|
||||||
|
if not (album.albumartist and album.album):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find the name of the cover art filename on DBpedia
|
||||||
|
cover_filename = None
|
||||||
|
dbpedia_response = requests.get(
|
||||||
|
self.DBPEDIA_URL,
|
||||||
|
params={
|
||||||
|
'format': 'application/sparql-results+json',
|
||||||
|
'timeout': 2500,
|
||||||
|
'query': self.SPARQL_QUERY.format(artist=album.albumartist,
|
||||||
|
album=album.album)
|
||||||
|
}, headers={'content-type': 'application/json'})
|
||||||
|
try:
|
||||||
|
data = dbpedia_response.json()
|
||||||
|
results = data['results']['bindings']
|
||||||
|
if results:
|
||||||
|
cover_filename = results[0]['coverFilename']['value']
|
||||||
|
else:
|
||||||
|
self._log.debug(u'album not found on dbpedia')
|
||||||
|
except:
|
||||||
|
self._log.debug(u'error scraping dbpedia album page')
|
||||||
|
|
||||||
|
# Ensure we have a filename before attempting to query wikipedia
|
||||||
|
if not cover_filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find the absolute url of the cover art on Wikipedia
|
||||||
|
wikipedia_response = requests.get(self.WIKIPEDIA_URL, params={
|
||||||
|
'format': 'json',
|
||||||
|
'action': 'query',
|
||||||
|
'continue': '',
|
||||||
|
'prop': 'imageinfo',
|
||||||
|
'iiprop': 'url',
|
||||||
|
'titles': ('File:' + cover_filename).encode('utf-8')},
|
||||||
|
headers={'content-type': 'application/json'})
|
||||||
|
try:
|
||||||
|
data = wikipedia_response.json()
|
||||||
|
results = data['query']['pages']
|
||||||
|
for _, result in results.iteritems():
|
||||||
|
image_url = result['imageinfo'][0]['url']
|
||||||
|
yield image_url
|
||||||
|
except:
|
||||||
|
self._log.debug(u'error scraping wikipedia imageinfo')
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class FileSystem(ArtSource):
|
class FileSystem(ArtSource):
|
||||||
"""Art from the filesystem"""
|
"""Art from the filesystem"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -203,7 +274,8 @@ class FileSystem(ArtSource):
|
||||||
|
|
||||||
# Try each source in turn.
|
# Try each source in turn.
|
||||||
|
|
||||||
SOURCES_ALL = [u'coverart', u'itunes', u'amazon', u'albumart', u'google']
|
SOURCES_ALL = [u'coverart', u'itunes', u'amazon', u'albumart', u'google',
|
||||||
|
u'wikipedia']
|
||||||
|
|
||||||
ART_FUNCS = {
|
ART_FUNCS = {
|
||||||
u'coverart': CoverArtArchive,
|
u'coverart': CoverArtArchive,
|
||||||
|
|
@ -211,6 +283,7 @@ ART_FUNCS = {
|
||||||
u'albumart': AlbumArtOrg,
|
u'albumart': AlbumArtOrg,
|
||||||
u'amazon': Amazon,
|
u'amazon': Amazon,
|
||||||
u'google': GoogleImages,
|
u'google': GoogleImages,
|
||||||
|
u'wikipedia': Wikipedia,
|
||||||
}
|
}
|
||||||
|
|
||||||
# PLUGIN LOGIC ###############################################################
|
# PLUGIN LOGIC ###############################################################
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ from beets.ui import decargs, print_obj, Subcommand
|
||||||
def _missing_count(album):
|
def _missing_count(album):
|
||||||
"""Return number of missing items in `album`.
|
"""Return number of missing items in `album`.
|
||||||
"""
|
"""
|
||||||
return (album.tracktotal or 0) - len(album.items())
|
return (album.albumtotal or 0) - len(album.items())
|
||||||
|
|
||||||
|
|
||||||
def _item(track_info, album_info, album_id):
|
def _item(track_info, album_info, album_id):
|
||||||
|
|
@ -139,7 +139,7 @@ class MissingPlugin(BeetsPlugin):
|
||||||
"""
|
"""
|
||||||
item_mbids = map(lambda x: x.mb_trackid, album.items())
|
item_mbids = map(lambda x: x.mb_trackid, album.items())
|
||||||
|
|
||||||
if len([i for i in album.items()]) < album.tracktotal:
|
if len([i for i in album.items()]) < album.albumtotal:
|
||||||
# fetch missing items
|
# fetch missing items
|
||||||
# TODO: Implement caching that without breaking other stuff
|
# TODO: Implement caching that without breaking other stuff
|
||||||
album_info = hooks.album_for_mbid(album.mb_albumid)
|
album_info = hooks.album_for_mbid(album.mb_albumid)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ Features:
|
||||||
* A new :doc:`/plugins/filefilter` lets you write regular expressions to
|
* A new :doc:`/plugins/filefilter` lets you write regular expressions to
|
||||||
automatically avoid importing certain files. Thanks to :user:`mried`.
|
automatically avoid importing certain files. Thanks to :user:`mried`.
|
||||||
:bug:`1186`
|
:bug:`1186`
|
||||||
* Stop on invalid queries instead of ignoring the invalid part.
|
* When there's a parse error in a query (for example, when you type a
|
||||||
|
malformed date in a :ref:`date query <datequery>`), beets now stops with an
|
||||||
|
error instead of silently ignoring the query component.
|
||||||
* A new :ref:`searchlimit` configuration option allows you to specify how many
|
* A new :ref:`searchlimit` configuration option allows you to specify how many
|
||||||
search results you wish to see when looking up releases at MusicBrainz
|
search results you wish to see when looking up releases at MusicBrainz
|
||||||
during import. :bug:`1245`
|
during import. :bug:`1245`
|
||||||
|
|
@ -24,6 +26,22 @@ Features:
|
||||||
by default. :bug:`1246`
|
by default. :bug:`1246`
|
||||||
* :doc:`/plugins/fetchart`: Names of extracted image art is taken from the
|
* :doc:`/plugins/fetchart`: Names of extracted image art is taken from the
|
||||||
``art_filename`` configuration option. :bug:`1258`
|
``art_filename`` configuration option. :bug:`1258`
|
||||||
|
* :doc:`/plugins/fetchart`: There's a new Wikipedia image source that uses
|
||||||
|
DBpedia to find albums. Thanks to Tom Jaspers. :bug:`1194`
|
||||||
|
|
||||||
|
Core changes:
|
||||||
|
|
||||||
|
* The ``tracktotal`` attribute is now a *track-level field* instead of an
|
||||||
|
album-level one. This field stores the total number of tracks on the
|
||||||
|
album, or if the :ref:`per_disc_numbering` config option is set, the total
|
||||||
|
number of tracks on a particular medium (i.e., disc). The field was causing
|
||||||
|
problems with that :ref:`per_disc_numbering` mode: different discs on the
|
||||||
|
same album needed different track totals. The field can now work correctly
|
||||||
|
in either mode.
|
||||||
|
* To replace ``tracktotal`` as an album-level field, there is a new
|
||||||
|
``albumtotal`` computed attribute that provides the total number of tracks
|
||||||
|
on the album. (The :ref:`per_disc_numbering` option has no influence on this
|
||||||
|
field.)
|
||||||
|
|
||||||
Fixes:
|
Fixes:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ file. The available options are:
|
||||||
found in the filesystem.
|
found in the filesystem.
|
||||||
- **sources**: List of sources to search for images. An asterisk `*` expands
|
- **sources**: List of sources to search for images. An asterisk `*` expands
|
||||||
to all available sources.
|
to all available sources.
|
||||||
Default: ``coverart itunes albumart amazon google``, i.e., all sources
|
Default: ``coverart itunes albumart amazon google wikipedia``, i.e.,
|
||||||
|
all sources.
|
||||||
|
|
||||||
Here's an example that makes plugin select only images that contain *front* or
|
Here's an example that makes plugin select only images that contain *front* or
|
||||||
*back* keywords in their filenames and prioritizes the iTunes source over
|
*back* keywords in their filenames and prioritizes the iTunes source over
|
||||||
|
|
@ -97,7 +98,7 @@ Album Art Sources
|
||||||
|
|
||||||
By default, this plugin searches for art in the local filesystem as well as on
|
By default, this plugin searches for art in the local filesystem as well as on
|
||||||
the Cover Art Archive, the iTunes Store, Amazon, AlbumArt.org,
|
the Cover Art Archive, the iTunes Store, Amazon, AlbumArt.org,
|
||||||
and Google Image Search, in that order. You can reorder the sources or remove
|
and Google Image Search, and Wikipedia, in that order. You can reorder the sources or remove
|
||||||
some to speed up the process using the ``sources`` configuration option.
|
some to speed up the process using the ``sources`` configuration option.
|
||||||
|
|
||||||
When looking for local album art, beets checks for image files located in the
|
When looking for local album art, beets checks for image files located in the
|
||||||
|
|
|
||||||
|
|
@ -1068,15 +1068,6 @@ class ImportTimeTest(_common.TestCase):
|
||||||
|
|
||||||
|
|
||||||
class TemplateTest(_common.LibTestCase):
|
class TemplateTest(_common.LibTestCase):
|
||||||
def album_fields_override_item_values(self):
|
|
||||||
self.album = self.lib.add_album([self.i])
|
|
||||||
self.album.albumartist = 'album-level'
|
|
||||||
self.album.store()
|
|
||||||
self.i.albumartist = 'track-level'
|
|
||||||
self.i.store()
|
|
||||||
self.assertEqual(self.i.evaluate_template('$albumartist'),
|
|
||||||
'album-level')
|
|
||||||
|
|
||||||
def test_year_formatted_in_template(self):
|
def test_year_formatted_in_template(self):
|
||||||
self.i.year = 123
|
self.i.year = 123
|
||||||
self.i.store()
|
self.i.store()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue