Add support for deprecated COVERART in ogg containers and update relevant documentation (#2376)
This commit is contained in:
parent
d65fb8e640
commit
830b0a485f
|
@ -4,6 +4,7 @@ import logging
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
|
import magic
|
||||||
import mutagen._util
|
import mutagen._util
|
||||||
import mutagen.flac
|
import mutagen.flac
|
||||||
import mutagen.oggtheora
|
import mutagen.oggtheora
|
||||||
|
@ -131,6 +132,28 @@ def clean_flac_pictures(apic):
|
||||||
return pictures
|
return pictures
|
||||||
|
|
||||||
|
|
||||||
|
def clean_ogg_coverart(metadata_block_picture):
|
||||||
|
pictures = []
|
||||||
|
for b64_data in [metadata_block_picture]:
|
||||||
|
try:
|
||||||
|
data = base64.b64decode(b64_data)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
mime = magic.Magic(mime=True)
|
||||||
|
mime.from_buffer(data)
|
||||||
|
|
||||||
|
pictures.append(
|
||||||
|
{
|
||||||
|
"mimetype": mime.from_buffer(data),
|
||||||
|
"content": data,
|
||||||
|
"description": "",
|
||||||
|
"type": mutagen.id3.PictureType.COVER_FRONT,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return pictures
|
||||||
|
|
||||||
|
|
||||||
def clean_ogg_pictures(metadata_block_picture):
|
def clean_ogg_pictures(metadata_block_picture):
|
||||||
pictures = []
|
pictures = []
|
||||||
for b64_data in [metadata_block_picture]:
|
for b64_data in [metadata_block_picture]:
|
||||||
|
@ -196,10 +219,16 @@ CONF = {
|
||||||
"license": {},
|
"license": {},
|
||||||
"copyright": {},
|
"copyright": {},
|
||||||
"genre": {},
|
"genre": {},
|
||||||
"pictures": {
|
"pictures": [
|
||||||
"field": "metadata_block_picture",
|
{
|
||||||
"to_application": clean_ogg_pictures,
|
"field": "metadata_block_picture",
|
||||||
},
|
"to_application": clean_ogg_pictures,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "coverart",
|
||||||
|
"to_application": clean_ogg_coverart,
|
||||||
|
},
|
||||||
|
],
|
||||||
"comment": {"field": "comment"},
|
"comment": {"field": "comment"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -221,10 +250,16 @@ CONF = {
|
||||||
"license": {},
|
"license": {},
|
||||||
"copyright": {},
|
"copyright": {},
|
||||||
"genre": {},
|
"genre": {},
|
||||||
"pictures": {
|
"pictures": [
|
||||||
"field": "metadata_block_picture",
|
{
|
||||||
"to_application": clean_ogg_pictures,
|
"field": "metadata_block_picture",
|
||||||
},
|
"to_application": clean_ogg_pictures,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "coverart",
|
||||||
|
"to_application": clean_ogg_coverart,
|
||||||
|
},
|
||||||
|
],
|
||||||
"comment": {"field": "comment"},
|
"comment": {"field": "comment"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -415,25 +450,30 @@ class Metadata(Mapping):
|
||||||
|
|
||||||
def _get_from_self(self, key, default=NODEFAULT):
|
def _get_from_self(self, key, default=NODEFAULT):
|
||||||
try:
|
try:
|
||||||
field_conf = self._conf["fields"][key]
|
field_confs = self._conf["fields"][key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise UnsupportedTag(f"{key} is not supported for this file format")
|
raise UnsupportedTag(f"{key} is not supported for this file format")
|
||||||
real_key = field_conf.get("field", key)
|
if not isinstance(field_confs, list):
|
||||||
try:
|
field_confs = [field_confs]
|
||||||
getter = field_conf.get("getter", self._conf["getter"])
|
|
||||||
v = getter(self._file, real_key)
|
|
||||||
except KeyError:
|
|
||||||
if default == NODEFAULT:
|
|
||||||
raise TagNotFound(real_key)
|
|
||||||
return default
|
|
||||||
|
|
||||||
converter = field_conf.get("to_application")
|
for field_conf in field_confs:
|
||||||
if converter:
|
real_key = field_conf.get("field", key)
|
||||||
v = converter(v)
|
try:
|
||||||
field = VALIDATION.get(key)
|
getter = field_conf.get("getter", self._conf["getter"])
|
||||||
if field:
|
v = getter(self._file, real_key)
|
||||||
v = field.to_python(v)
|
except KeyError:
|
||||||
return v
|
continue
|
||||||
|
|
||||||
|
converter = field_conf.get("to_application")
|
||||||
|
if converter:
|
||||||
|
v = converter(v)
|
||||||
|
field = VALIDATION.get(key)
|
||||||
|
if field:
|
||||||
|
v = field.to_python(v)
|
||||||
|
return v
|
||||||
|
if default == NODEFAULT:
|
||||||
|
raise TagNotFound(real_key)
|
||||||
|
return default
|
||||||
|
|
||||||
def get_picture(self, *picture_types):
|
def get_picture(self, *picture_types):
|
||||||
if not picture_types:
|
if not picture_types:
|
||||||
|
|
Binary file not shown.
|
@ -187,6 +187,7 @@ def test_can_get_metadata_from_id3_aiff_file(field, value):
|
||||||
"with_cover.ogg",
|
"with_cover.ogg",
|
||||||
"with_cover.opus",
|
"with_cover.opus",
|
||||||
"test.m4a",
|
"test.m4a",
|
||||||
|
"test_coverart.ogg",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_can_get_pictures(name):
|
def test_can_get_pictures(name):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Add support for deprecated COVERART fields in ogg files.
|
|
@ -200,7 +200,7 @@ Funkwhale imports the music in your storage directory into the specified library
|
||||||
|
|
||||||
Funkwhale attempts to import album art for your music library. The import process checks for the following.
|
Funkwhale attempts to import album art for your music library. The import process checks for the following.
|
||||||
|
|
||||||
1. The cover embedded in the audio files (works with FLAC and MP3 files).
|
1. The cover embedded in the audio files (works with FLAC, OGG, MP3 and MP4 files, possibly others).
|
||||||
2. A `cover.jpg` or `cover.png` in the the track's directory.
|
2. A `cover.jpg` or `cover.png` in the the track's directory.
|
||||||
3. An `mbid` in the file's tags. If there is an `mbid`, the import process tries to fetch cover art from Musicbrainz.
|
3. An `mbid` in the file's tags. If there is an `mbid`, the import process tries to fetch cover art from Musicbrainz.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue