mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +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.
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
import base64
|
|
||||||
import codecs
|
import codecs
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import mutagen
|
|
||||||
import os.path
|
import os.path
|
||||||
import requests
|
import requests
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from flask import request, Response, send_file
|
from flask import request, Response, send_file
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
@ -134,27 +131,27 @@ def download_media():
|
|||||||
|
|
||||||
@api.route('/getCoverArt.view', methods = [ 'GET', 'POST' ])
|
@api.route('/getCoverArt.view', methods = [ 'GET', 'POST' ])
|
||||||
def cover_art():
|
def cover_art():
|
||||||
res = get_entity(Folder)
|
eid = request.values['id']
|
||||||
if not res.cover_art or not os.path.isfile(os.path.join(res.path, res.cover_art)):
|
if Folder.exists(id=eid):
|
||||||
# Check for embeded metadata in songs
|
res = get_entity(Folder)
|
||||||
temp_cover = tempfile.NamedTemporaryFile()
|
if not res.cover_art or not os.path.isfile(os.path.join(res.path, res.cover_art)):
|
||||||
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:
|
|
||||||
raise NotFound('Cover art')
|
raise NotFound('Cover art')
|
||||||
else:
|
|
||||||
cover_path = os.path.join(res.path, res.cover_art)
|
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')
|
size = request.values.get('size')
|
||||||
if size:
|
if size:
|
||||||
size = int(size)
|
size = int(size)
|
||||||
@ -234,4 +231,3 @@ def read_file_as_unicode(path):
|
|||||||
# Fallback to ASCII
|
# Fallback to ASCII
|
||||||
current_app.logger.debug('Reading file {} with ascii encoding'.format(path))
|
current_app.logger.debug('Reading file {} with ascii encoding'.format(path))
|
||||||
return unicode(open(path, 'r').read())
|
return unicode(open(path, 'r').read())
|
||||||
|
|
||||||
|
@ -7,8 +7,10 @@
|
|||||||
#
|
#
|
||||||
# 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
|
||||||
@ -101,6 +103,11 @@ class Folder(PathMixin, db.Entity):
|
|||||||
info['artist'] = self.parent.name
|
info['artist'] = self.parent.name
|
||||||
if self.cover_art:
|
if self.cover_art:
|
||||||
info['coverArt'] = str(self.id)
|
info['coverArt'] = str(self.id)
|
||||||
|
else:
|
||||||
|
for track in self.tracks:
|
||||||
|
if track.extract_cover_art():
|
||||||
|
info['coverArt'] = str(track.id)
|
||||||
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
starred = StarredFolder[user.id, self.id]
|
starred = StarredFolder[user.id, self.id]
|
||||||
@ -259,7 +266,9 @@ class Track(PathMixin, db.Entity):
|
|||||||
info['year'] = self.year
|
info['year'] = self.year
|
||||||
if self.genre:
|
if self.genre:
|
||||||
info['genre'] = 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)
|
info['coverArt'] = str(self.folder.id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -293,6 +302,20 @@ class Track(PathMixin, db.Entity):
|
|||||||
|
|
||||||
def sort_key(self):
|
def sort_key(self):
|
||||||
return (self.album.artist.name + self.album.name + ("%02i" % self.disc) + ("%02i" % self.number) + self.title).lower()
|
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):
|
class User(db.Entity):
|
||||||
_table_ = 'user'
|
_table_ = 'user'
|
||||||
|
@ -32,13 +32,6 @@ class MediaTestCase(ApiTestBase):
|
|||||||
)
|
)
|
||||||
self.folderid = folder.id
|
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')
|
artist = Artist(name = 'Artist')
|
||||||
album = Album(artist = artist, name = 'Album')
|
album = Album(artist = artist, name = 'Album')
|
||||||
|
|
||||||
@ -64,13 +57,14 @@ class MediaTestCase(ApiTestBase):
|
|||||||
artist = artist,
|
artist = artist,
|
||||||
album = album,
|
album = album,
|
||||||
path = os.path.abspath('tests/assets/folder/silence.mp3'),
|
path = os.path.abspath('tests/assets/folder/silence.mp3'),
|
||||||
root_folder = folder_embeded_art,
|
root_folder = folder,
|
||||||
folder = folder_embeded_art,
|
folder = folder,
|
||||||
duration = 2,
|
duration = 2,
|
||||||
bitrate = 320,
|
bitrate = 320,
|
||||||
content_type = 'audio/mpeg',
|
content_type = 'audio/mpeg',
|
||||||
last_modification = 0
|
last_modification = 0
|
||||||
)
|
)
|
||||||
|
self.trackid_embeded_art = track_embeded_art.id
|
||||||
|
|
||||||
def test_stream(self):
|
def test_stream(self):
|
||||||
self._make_request('stream', error = 10)
|
self._make_request('stream', error = 10)
|
||||||
@ -143,7 +137,7 @@ class MediaTestCase(ApiTestBase):
|
|||||||
# TODO test non square covers
|
# TODO test non square covers
|
||||||
|
|
||||||
# Test extracting cover art from embeded media
|
# 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)
|
rv = self.client.get('/rest/getCoverArt.view', query_string = args)
|
||||||
self.assertEqual(rv.status_code, 200)
|
self.assertEqual(rv.status_code, 200)
|
||||||
self.assertEqual(rv.mimetype, 'image/png')
|
self.assertEqual(rv.mimetype, 'image/png')
|
||||||
|
Loading…
Reference in New Issue
Block a user