mirror of
https://github.com/spl0k/supysonic.git
synced 2024-11-09 11:42:16 +00:00
Don't open files twice when scanning
This commit is contained in:
parent
f3a12c78b4
commit
e0cd49d67b
@ -31,6 +31,7 @@ from zipstream import ZipFile
|
||||
|
||||
from .. import scanner
|
||||
from ..cache import CacheMiss
|
||||
from ..covers import get_embedded_cover
|
||||
from ..db import Track, Album, Artist, Folder, User, ClientPrefs, now
|
||||
from ..py23 import dict
|
||||
|
||||
@ -227,7 +228,7 @@ def cover_art():
|
||||
cover_path = cache.get(cache_key)
|
||||
except CacheMiss:
|
||||
res = get_entity(Track)
|
||||
art = res.extract_cover_art()
|
||||
art = get_embedded_cover(res.path)
|
||||
if not art:
|
||||
raise NotFound("Cover art")
|
||||
cover_path = cache.set(cache_key, art)
|
||||
|
@ -10,9 +10,14 @@
|
||||
import os.path
|
||||
import re
|
||||
|
||||
from base64 import b64decode
|
||||
from mutagen import File, FileType
|
||||
from mutagen.easyid3 import EasyID3
|
||||
from mutagen.flac import FLAC, Picture
|
||||
from mutagen._vorbis import VCommentDict
|
||||
from PIL import Image
|
||||
|
||||
from .py23 import scandir
|
||||
from .py23 import scandir, strtype
|
||||
|
||||
EXTENSIONS = (".jpg", ".jpeg", ".png", ".bmp")
|
||||
NAMING_SCORE_RULES = (
|
||||
@ -82,3 +87,48 @@ def find_cover_in_folder(path, album_name=None):
|
||||
return candidates[0]
|
||||
|
||||
return sorted(candidates, key=lambda c: c.score, reverse=True)[0]
|
||||
|
||||
|
||||
def get_embedded_cover(path):
|
||||
if not isinstance(path, strtype): # pragma: nocover
|
||||
raise TypeError("Expecting string, got " + str(type(path)))
|
||||
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
|
||||
metadata = File(path, easy=True)
|
||||
if not metadata:
|
||||
return None
|
||||
|
||||
if isinstance(metadata.tags, EasyID3):
|
||||
picture = metadata["pictures"][0]
|
||||
elif isinstance(metadata, FLAC):
|
||||
picture = metadata.pictures[0]
|
||||
elif isinstance(metadata.tags, VCommentDict):
|
||||
picture = Picture(b64decode(metadata.tags["METADATA_BLOCK_PICTURE"][0]))
|
||||
else:
|
||||
return None
|
||||
|
||||
return picture.data
|
||||
|
||||
|
||||
def has_embedded_cover(metadata):
|
||||
if not isinstance(metadata, FileType): # pragma: nocover
|
||||
raise TypeError("Expecting mutagen.FileType, got " + str(type(metadata)))
|
||||
|
||||
pictures = []
|
||||
if isinstance(metadata.tags, EasyID3):
|
||||
pictures = metadata.get("pictures", [])
|
||||
elif isinstance(metadata, FLAC):
|
||||
pictures = metadata.pictures
|
||||
elif isinstance(metadata.tags, VCommentDict):
|
||||
pictures = metadata.tags.get("METADATA_BLOCK_PICTURE", [])
|
||||
|
||||
return len(pictures) > 0
|
||||
|
||||
|
||||
def _get_id3_apic(id3, key):
|
||||
return id3.getall("APIC")
|
||||
|
||||
|
||||
EasyID3.RegisterKey("pictures", _get_id3_apic)
|
||||
|
@ -7,10 +7,8 @@
|
||||
#
|
||||
# Distributed under terms of the GNU AGPLv3 license.
|
||||
|
||||
import base64
|
||||
import importlib
|
||||
import mimetypes
|
||||
import mutagen
|
||||
import os.path
|
||||
import pkg_resources
|
||||
import time
|
||||
@ -353,32 +351,6 @@ class Track(PathMixin, db.Entity):
|
||||
+ self.title
|
||||
).lower()
|
||||
|
||||
def extract_cover_art(self):
|
||||
return Track._extract_cover_art(self.path)
|
||||
|
||||
@staticmethod
|
||||
def _extract_cover_art(path):
|
||||
if os.path.exists(path):
|
||||
metadata = mutagen.File(path)
|
||||
if metadata:
|
||||
if (
|
||||
isinstance(metadata.tags, mutagen.id3.ID3Tags)
|
||||
and len(metadata.tags.getall("APIC")) > 0
|
||||
):
|
||||
return metadata.tags.getall("APIC")[0].data
|
||||
elif isinstance(metadata, mutagen.flac.FLAC) and len(metadata.pictures):
|
||||
return metadata.pictures[0].data
|
||||
elif (
|
||||
isinstance(metadata.tags, mutagen._vorbis.VCommentDict)
|
||||
and "METADATA_BLOCK_PICTURE" in metadata.tags
|
||||
and len(metadata.tags["METADATA_BLOCK_PICTURE"]) > 0
|
||||
):
|
||||
picture = mutagen.flac.Picture(
|
||||
base64.b64decode(metadata.tags["METADATA_BLOCK_PICTURE"][0])
|
||||
)
|
||||
return picture.data
|
||||
return None
|
||||
|
||||
|
||||
class User(db.Entity):
|
||||
_table_ = "user"
|
||||
|
@ -16,12 +16,13 @@ from datetime import datetime
|
||||
from pony.orm import db_session
|
||||
from threading import Thread, Event
|
||||
|
||||
from .covers import find_cover_in_folder, CoverFile
|
||||
from .covers import find_cover_in_folder, has_embedded_cover, CoverFile
|
||||
from .db import Folder, Artist, Album, Track, User
|
||||
from .db import StarredFolder, StarredArtist, StarredAlbum, StarredTrack
|
||||
from .db import RatingFolder, RatingTrack
|
||||
from .py23 import scandir, strtype, DirEntry, Queue, QueueEmpty
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -247,7 +248,7 @@ class Scanner(Thread):
|
||||
)
|
||||
trdict["genre"] = self.__try_read_tag(tag, "genre")
|
||||
trdict["duration"] = int(tag.info.length)
|
||||
trdict["has_art"] = bool(Track._extract_cover_art(path))
|
||||
trdict["has_art"] = has_embedded_cover(tag)
|
||||
|
||||
trdict["bitrate"] = (
|
||||
int(
|
||||
|
Loading…
Reference in New Issue
Block a user