1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-12-23 01:16:18 +00:00

Don't open files twice when scanning

This commit is contained in:
Alban Féron 2019-07-07 16:35:33 +02:00
parent f3a12c78b4
commit e0cd49d67b
No known key found for this signature in database
GPG Key ID: 8CE0313646D16165
4 changed files with 56 additions and 32 deletions

View File

@ -31,6 +31,7 @@ from zipstream import ZipFile
from .. import scanner from .. import scanner
from ..cache import CacheMiss from ..cache import CacheMiss
from ..covers import get_embedded_cover
from ..db import Track, Album, Artist, Folder, User, ClientPrefs, now from ..db import Track, Album, Artist, Folder, User, ClientPrefs, now
from ..py23 import dict from ..py23 import dict
@ -227,7 +228,7 @@ def cover_art():
cover_path = cache.get(cache_key) cover_path = cache.get(cache_key)
except CacheMiss: except CacheMiss:
res = get_entity(Track) res = get_entity(Track)
art = res.extract_cover_art() art = get_embedded_cover(res.path)
if not art: if not art:
raise NotFound("Cover art") raise NotFound("Cover art")
cover_path = cache.set(cache_key, art) cover_path = cache.set(cache_key, art)

View File

@ -10,9 +10,14 @@
import os.path import os.path
import re 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 PIL import Image
from .py23 import scandir from .py23 import scandir, strtype
EXTENSIONS = (".jpg", ".jpeg", ".png", ".bmp") EXTENSIONS = (".jpg", ".jpeg", ".png", ".bmp")
NAMING_SCORE_RULES = ( NAMING_SCORE_RULES = (
@ -82,3 +87,48 @@ def find_cover_in_folder(path, album_name=None):
return candidates[0] return candidates[0]
return sorted(candidates, key=lambda c: c.score, reverse=True)[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)

View File

@ -7,10 +7,8 @@
# #
# Distributed under terms of the GNU AGPLv3 license. # Distributed under terms of the GNU AGPLv3 license.
import base64
import importlib import importlib
import mimetypes import mimetypes
import mutagen
import os.path import os.path
import pkg_resources import pkg_resources
import time import time
@ -353,32 +351,6 @@ class Track(PathMixin, db.Entity):
+ self.title + self.title
).lower() ).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): class User(db.Entity):
_table_ = "user" _table_ = "user"

View File

@ -16,12 +16,13 @@ from datetime import datetime
from pony.orm import db_session from pony.orm import db_session
from threading import Thread, Event 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 Folder, Artist, Album, Track, User
from .db import StarredFolder, StarredArtist, StarredAlbum, StarredTrack from .db import StarredFolder, StarredArtist, StarredAlbum, StarredTrack
from .db import RatingFolder, RatingTrack from .db import RatingFolder, RatingTrack
from .py23 import scandir, strtype, DirEntry, Queue, QueueEmpty from .py23 import scandir, strtype, DirEntry, Queue, QueueEmpty
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -247,7 +248,7 @@ class Scanner(Thread):
) )
trdict["genre"] = self.__try_read_tag(tag, "genre") trdict["genre"] = self.__try_read_tag(tag, "genre")
trdict["duration"] = int(tag.info.length) trdict["duration"] = int(tag.info.length)
trdict["has_art"] = bool(Track._extract_cover_art(path)) trdict["has_art"] = has_embedded_cover(tag)
trdict["bitrate"] = ( trdict["bitrate"] = (
int( int(