Skip to content

Commit dbfe7f8

Browse files
committed
Rework local cover art handling
Local cover art as a provider doesn't make sense - local cover art is a property of a file, same as tag cover art. Move the handling of local cover art to the File class. TODO: Need to resolve how to configure load ordering of tag cover art vs local cover art. Also, there needs to be a config ui written. In the current state, the local_cover_regex setting is re-used, and a new load_local_cover_art boolean is used to enable the feature. This will probably fix many of the causes of PICARD-1001.
1 parent d5f131d commit dbfe7f8

File tree

7 files changed

+62
-209
lines changed

7 files changed

+62
-209
lines changed

picard/const/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@
189189
DEFAULT_NUMBERED_SCRIPT_NAME = N_("My script %d")
190190
DEFAULT_SCRIPT_NAME = N_("My script")
191191
DEFAULT_COVER_IMAGE_FILENAME = "cover"
192+
DEFAULT_LOCAL_COVER_ART_REGEX = r'^(?:cover|folder|albumart)(.*)\.(?:jpe?g|png|gif|tiff?|webp)$'
192193
DEFAULT_NUMBERED_PROFILE_NAME = N_("My profile %d")
193194
DEFAULT_PROFILE_NAME = N_("My profile")
194195

picard/coverart/image.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,6 @@ def __init__(self, filepath, types=None, comment='',
480480
super().__init__(url=url, types=types, comment=comment)
481481
self.support_types = support_types
482482
self.support_multi_types = support_multi_types
483+
path = self.url.toLocalFile()
484+
with open(path, 'rb') as file:
485+
self.set_data(file.read())

picard/coverart/providers/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
from picard.coverart.providers.caa_release_group import (
3636
CoverArtProviderCaaReleaseGroup,
3737
)
38-
from picard.coverart.providers.local import CoverArtProviderLocal
3938
from picard.coverart.providers.provider import ( # noqa: F401 # pylint: disable=unused-import
4039
CoverArtProvider,
4140
ProviderOptions,
@@ -92,7 +91,6 @@ def label(p):
9291

9392

9493
__providers = [
95-
CoverArtProviderLocal,
9694
CoverArtProviderCaa,
9795
CoverArtProviderUrlRelationships,
9896
CoverArtProviderCaaReleaseGroup,

picard/coverart/providers/local.py

Lines changed: 0 additions & 123 deletions
This file was deleted.

picard/file.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@
6363
IS_MACOS,
6464
IS_WIN,
6565
)
66+
from picard.coverart.image import (
67+
CoverArtImageError,
68+
LocalFileCoverArtImage
69+
)
70+
from picard.coverart.utils import CAA_TYPES
6671
from picard.metadata import (
6772
Metadata,
6873
SimMatchTrack,
@@ -240,6 +245,11 @@ def _loading_finished(self, callback, result=None, error=None):
240245
postprocessors = []
241246
if config.setting["guess_tracknumber_and_title"]:
242247
postprocessors.append(self._guess_tracknumber_and_title)
248+
# If no cover art was loaded from file tags, try loading from a local file
249+
# TODO: should this be a preference? should we load from both sources?
250+
log.debug("load local cover art: %r", config.setting["load_local_cover_art"])
251+
if config.setting["load_local_cover_art"] and len(result.images) == 0:
252+
self._load_local_cover_art(result, config)
243253
self._copy_loaded_metadata(result, postprocessors)
244254
# use cached fingerprint from file metadata
245255
if not config.setting["ignore_existing_acoustid_fingerprints"]:
@@ -250,6 +260,48 @@ def _loading_finished(self, callback, result=None, error=None):
250260
self.update()
251261
callback(self)
252262

263+
_types_split_re = re.compile('[^a-z0-9]', re.IGNORECASE)
264+
_known_types = set([t['name'] for t in CAA_TYPES])
265+
_default_types = ['front']
266+
267+
def get_types(self, string):
268+
found = set([x.lower() for x in self._types_split_re.split(string) if x])
269+
return list(found.intersection(self._known_types))
270+
271+
def _load_local_cover_art(self, metadata, config):
272+
log.debug("Attempting to load cover art from local files")
273+
match_re = re.compile(config.setting['local_cover_regex'], re.IGNORECASE)
274+
current_dir = os.path.dirname(self.filename)
275+
for root, dirs, files in os.walk(current_dir):
276+
for filename in files:
277+
m = match_re.search(filename)
278+
if not m:
279+
continue
280+
filepath = os.path.join(current_dir, root, filename)
281+
if not os.path.exists(filepath):
282+
continue
283+
try:
284+
type_from_filename = self.get_types(m.group(1))
285+
except IndexError:
286+
type_from_filename = []
287+
try:
288+
coverartimage = LocalFileCoverArtImage(
289+
filepath,
290+
types=type_from_filename or self._default_types,
291+
support_types=True,
292+
support_multi_types=True
293+
)
294+
295+
log.debug("Loaded local cover art image: %r", coverartimage)
296+
except OSError as exc:
297+
(errnum, errmsg) = exc.args
298+
log.error("Failed to read %r: %s (%d)" %
299+
(filepath, errmsg, errnum))
300+
except (CoverArtImageError) as e:
301+
log.error('Cannot load image from %r: %s' % (filename, e))
302+
else:
303+
metadata.images.append(coverartimage)
304+
253305
def _copy_loaded_metadata(self, metadata, postprocessors=None):
254306
metadata['~length'] = format_time(metadata.length)
255307
if postprocessors:

picard/ui/options/cover.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@
3232
TextOption,
3333
get_config,
3434
)
35-
from picard.const import DEFAULT_COVER_IMAGE_FILENAME
35+
from picard.const import (
36+
DEFAULT_COVER_IMAGE_FILENAME,
37+
DEFAULT_LOCAL_COVER_ART_REGEX,
38+
)
3639
from picard.coverart.providers import cover_art_providers
3740

3841
from picard.ui.checkbox_list_item import CheckboxListItem
@@ -61,6 +64,8 @@ class CoverOptionsPage(OptionsPage):
6164
BoolOption("setting", "save_images_overwrite", False),
6265
BoolOption("setting", "save_only_one_front_image", False),
6366
BoolOption("setting", "image_type_as_filename", False),
67+
BoolOption("setting", "load_local_cover_art", False),
68+
TextOption("setting", "local_cover_regex", DEFAULT_LOCAL_COVER_ART_REGEX),
6469
ListOption("setting", "ca_providers", [
6570
('Cover Art Archive', True),
6671
('UrlRelationships', True),

ui/provider_options_local.ui

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)