mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +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 .. 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)
|
||||||
|
@ -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)
|
||||||
|
@ -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"
|
||||||
|
@ -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(
|
||||||
|
Loading…
Reference in New Issue
Block a user