mirror of
https://github.com/spl0k/supysonic.git
synced 2024-11-09 19:52:16 +00:00
Art extraction is now done as a method of Track; cover_art() only extracts cover art if given a track ID; Cache embeded art
This commit is contained in:
parent
7106d95cee
commit
4bbcbd0acf
@ -7,15 +7,12 @@
|
||||
#
|
||||
# Distributed under terms of the GNU AGPLv3 license.
|
||||
|
||||
import base64
|
||||
import codecs
|
||||
import mimetypes
|
||||
import mutagen
|
||||
import os.path
|
||||
import requests
|
||||
import shlex
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from flask import request, Response, send_file
|
||||
from flask import current_app
|
||||
@ -134,27 +131,27 @@ def download_media():
|
||||
|
||||
@api.route('/getCoverArt.view', methods = [ 'GET', 'POST' ])
|
||||
def cover_art():
|
||||
res = get_entity(Folder)
|
||||
if not res.cover_art or not os.path.isfile(os.path.join(res.path, res.cover_art)):
|
||||
# Check for embeded metadata in songs
|
||||
temp_cover = tempfile.NamedTemporaryFile()
|
||||
cover_path = temp_cover.name
|
||||
for track in res.tracks:
|
||||
song = mutagen.File(track.path)
|
||||
if isinstance(song.tags, mutagen.id3.ID3Tags) and len(song.tags.getall('APIC')) > 0:
|
||||
temp_cover.write(song.tags.getall('APIC')[0].data)
|
||||
break
|
||||
elif isinstance(song, mutagen.flac.FLAC) and len(song.pictures):
|
||||
temp_cover.write(song.pictures[0].data)
|
||||
break
|
||||
elif isinstance(song.tags, mutagen._vorbis.VCommentDict) and 'METADATA_BLOCK_PICTURE' in song.tags and len(song.tags['METADATA_BLOCK_PICTURE']) > 0:
|
||||
picture = mutagen.flac.Picture(base64.b64decode(song.tags['METADATA_BLOCK_PICTURE'][0]))
|
||||
temp_cover.write(picture.data)
|
||||
break
|
||||
else:
|
||||
eid = request.values['id']
|
||||
if Folder.exists(id=eid):
|
||||
res = get_entity(Folder)
|
||||
if not res.cover_art or not os.path.isfile(os.path.join(res.path, res.cover_art)):
|
||||
raise NotFound('Cover art')
|
||||
else:
|
||||
cover_path = os.path.join(res.path, res.cover_art)
|
||||
elif Track.exists(id=eid):
|
||||
embed_cache = os.path.join(current_app.config['WEBAPP']['cache_dir'], 'embeded_art')
|
||||
cover_path = os.path.join(embed_cache, eid)
|
||||
if not os.path.exists(cover_path):
|
||||
res = get_entity(Track)
|
||||
art = res.extract_cover_art()
|
||||
if not art:
|
||||
raise NotFound('Cover art')
|
||||
#Art found, save to cache
|
||||
os.makedirs(embed_cache, exist_ok=True)
|
||||
with open(cover_path, 'wb') as cover_file:
|
||||
cover_file.write(art)
|
||||
else:
|
||||
raise NotFound('Entity')
|
||||
|
||||
size = request.values.get('size')
|
||||
if size:
|
||||
size = int(size)
|
||||
@ -234,4 +231,3 @@ def read_file_as_unicode(path):
|
||||
# Fallback to ASCII
|
||||
current_app.logger.debug('Reading file {} with ascii encoding'.format(path))
|
||||
return unicode(open(path, 'r').read())
|
||||
|
||||
|
@ -7,8 +7,10 @@
|
||||
#
|
||||
# Distributed under terms of the GNU AGPLv3 license.
|
||||
|
||||
import base64
|
||||
import importlib
|
||||
import mimetypes
|
||||
import mutagen
|
||||
import os.path
|
||||
import pkg_resources
|
||||
import time
|
||||
@ -101,6 +103,11 @@ class Folder(PathMixin, db.Entity):
|
||||
info['artist'] = self.parent.name
|
||||
if self.cover_art:
|
||||
info['coverArt'] = str(self.id)
|
||||
else:
|
||||
for track in self.tracks:
|
||||
if track.extract_cover_art():
|
||||
info['coverArt'] = str(track.id)
|
||||
break
|
||||
|
||||
try:
|
||||
starred = StarredFolder[user.id, self.id]
|
||||
@ -259,7 +266,9 @@ class Track(PathMixin, db.Entity):
|
||||
info['year'] = self.year
|
||||
if self.genre:
|
||||
info['genre'] = self.genre
|
||||
if self.folder.cover_art:
|
||||
if self.extract_cover_art():
|
||||
info['coverArt'] = str(self.id)
|
||||
elif self.folder.cover_art:
|
||||
info['coverArt'] = str(self.folder.id)
|
||||
|
||||
try:
|
||||
@ -293,6 +302,20 @@ class Track(PathMixin, db.Entity):
|
||||
|
||||
def sort_key(self):
|
||||
return (self.album.artist.name + self.album.name + ("%02i" % self.disc) + ("%02i" % self.number) + self.title).lower()
|
||||
|
||||
def extract_cover_art(self):
|
||||
if os.path.exists(self.path):
|
||||
metadata = mutagen.File(self.path)
|
||||
data = None
|
||||
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'
|
||||
|
@ -32,13 +32,6 @@ class MediaTestCase(ApiTestBase):
|
||||
)
|
||||
self.folderid = folder.id
|
||||
|
||||
folder_embeded_art = Folder(
|
||||
name = 'Root',
|
||||
path = os.path.abspath('tests/assets/folder'),
|
||||
root = True,
|
||||
)
|
||||
self.folderid_embeded = folder_embeded_art.id
|
||||
|
||||
artist = Artist(name = 'Artist')
|
||||
album = Album(artist = artist, name = 'Album')
|
||||
|
||||
@ -64,13 +57,14 @@ class MediaTestCase(ApiTestBase):
|
||||
artist = artist,
|
||||
album = album,
|
||||
path = os.path.abspath('tests/assets/folder/silence.mp3'),
|
||||
root_folder = folder_embeded_art,
|
||||
folder = folder_embeded_art,
|
||||
root_folder = folder,
|
||||
folder = folder,
|
||||
duration = 2,
|
||||
bitrate = 320,
|
||||
content_type = 'audio/mpeg',
|
||||
last_modification = 0
|
||||
)
|
||||
self.trackid_embeded_art = track_embeded_art.id
|
||||
|
||||
def test_stream(self):
|
||||
self._make_request('stream', error = 10)
|
||||
@ -143,7 +137,7 @@ class MediaTestCase(ApiTestBase):
|
||||
# TODO test non square covers
|
||||
|
||||
# Test extracting cover art from embeded media
|
||||
args['id'] = str(self.folderid_embeded)
|
||||
args['id'] = str(self.trackid_embeded_art)
|
||||
rv = self.client.get('/rest/getCoverArt.view', query_string = args)
|
||||
self.assertEqual(rv.status_code, 200)
|
||||
self.assertEqual(rv.mimetype, 'image/png')
|
||||
|
Loading…
Reference in New Issue
Block a user