ripper: Add support for fetching album art
If the `PIL` module is available, which can be installed by activating the *art* extra for Rupert, the ripper will now download the album art for the CD and store it in a file named `folder.jpg`.pull/3/head
parent
943cf4dbd2
commit
7b3427fc2c
|
@ -1,2 +1,4 @@
|
||||||
/.venv
|
/.venv
|
||||||
/dist
|
/dist
|
||||||
|
__pycache__/
|
||||||
|
*.py[co]
|
||||||
|
|
|
@ -13,6 +13,7 @@ pydantic = "^1.7.3"
|
||||||
python-libdiscid = "^2.0.1"
|
python-libdiscid = "^2.0.1"
|
||||||
musicbrainzngs = "^0.7.1"
|
musicbrainzngs = "^0.7.1"
|
||||||
mutagen = "^1.45.1"
|
mutagen = "^1.45.1"
|
||||||
|
Pillow = {version = "^8.4.0", optional = true}
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pylint = "^2.6.0"
|
pylint = "^2.6.0"
|
||||||
|
@ -24,6 +25,7 @@ rope = "^0.18.0"
|
||||||
ripper = "rupert.main:main"
|
ripper = "rupert.main:main"
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[tool.poetry.extras]
|
||||||
|
art = ["pillow"]
|
||||||
udev = ["pyudev"]
|
udev = ["pyudev"]
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
|
|
|
@ -10,6 +10,10 @@ from .disc import Disc
|
||||||
musicbrainzngs.set_useragent('DCPlayer', '0.0.1', 'https://dcplayer.audio/')
|
musicbrainzngs.set_useragent('DCPlayer', '0.0.1', 'https://dcplayer.audio/')
|
||||||
|
|
||||||
|
|
||||||
|
class AlbumArtNotFound(Exception):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class Artist(pydantic.BaseModel):
|
class Artist(pydantic.BaseModel):
|
||||||
id: str
|
id: str
|
||||||
name: str
|
name: str
|
||||||
|
@ -58,6 +62,15 @@ class ReleaseResponse(pydantic.BaseModel):
|
||||||
release_count: int = pydantic.Field(alias='release-count')
|
release_count: int = pydantic.Field(alias='release-count')
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_album_art(release: Release) -> bytes:
|
||||||
|
try:
|
||||||
|
return musicbrainzngs.get_image_front(release.id)
|
||||||
|
except OSError as e:
|
||||||
|
raise AlbumArtNotFound(
|
||||||
|
'No album art found for release %s', release.id
|
||||||
|
) from e
|
||||||
|
|
||||||
|
|
||||||
def get_releases_from_disc(disc: Disc) -> ReleaseResponse:
|
def get_releases_from_disc(disc: Disc) -> ReleaseResponse:
|
||||||
res = musicbrainzngs.get_releases_by_discid(
|
res = musicbrainzngs.get_releases_by_discid(
|
||||||
disc.disc_id,
|
disc.disc_id,
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import enum
|
import enum
|
||||||
import glob
|
import glob
|
||||||
|
import io
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
|
@ -25,7 +26,12 @@ import mutagen
|
||||||
|
|
||||||
from . import inotify
|
from . import inotify
|
||||||
from .disc import DiscDrive
|
from .disc import DiscDrive
|
||||||
from .musicbrainz import Release
|
from .musicbrainz import AlbumArtNotFound, Release, fetch_album_art
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import Image
|
||||||
|
except ImportError:
|
||||||
|
Image = None # type: ignore
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -310,6 +316,8 @@ class Ripper:
|
||||||
for filename in glob.glob('track*.cdda.wav'):
|
for filename in glob.glob('track*.cdda.wav'):
|
||||||
os.unlink(filename)
|
os.unlink(filename)
|
||||||
|
|
||||||
|
if Image is not None:
|
||||||
|
threading.Thread(target=self.fetch_album_art).start()
|
||||||
self._process_thread.start()
|
self._process_thread.start()
|
||||||
self._encode_thread.start()
|
self._encode_thread.start()
|
||||||
self._rip_thread.start()
|
self._rip_thread.start()
|
||||||
|
@ -336,6 +344,17 @@ class Ripper:
|
||||||
def on_complete(self, message: str, is_err: bool) -> None:
|
def on_complete(self, message: str, is_err: bool) -> None:
|
||||||
self._status_queue.put((None, (message, is_err)))
|
self._status_queue.put((None, (message, is_err)))
|
||||||
|
|
||||||
|
def fetch_album_art(self) -> None:
|
||||||
|
try:
|
||||||
|
data = fetch_album_art(self.release)
|
||||||
|
except AlbumArtNotFound:
|
||||||
|
log.error('No album art found for %s', self.release.title)
|
||||||
|
buf = io.BytesIO(data)
|
||||||
|
img = Image.open(buf)
|
||||||
|
img.thumbnail((1000, 1000))
|
||||||
|
with open('folder.jpg', 'wb') as f:
|
||||||
|
img.save(f)
|
||||||
|
|
||||||
|
|
||||||
def safe_name(name: str) -> str:
|
def safe_name(name: str) -> str:
|
||||||
for k, v in FILENAME_SAFE_MAP.items():
|
for k, v in FILENAME_SAFE_MAP.items():
|
||||||
|
|
Loading…
Reference in New Issue