magic dithering
This commit is contained in:
@ -14,9 +14,12 @@ import pandas as pd
|
||||
import re
|
||||
from enum import Enum
|
||||
|
||||
class CardStyle(str,Enum):
|
||||
MAX_ALBUM_IMG_SIZE = 512
|
||||
|
||||
class CardStyle(str, Enum):
|
||||
FULL_ALBUM_COVER = "full album cover"
|
||||
MINIMAL = "minimal"
|
||||
PRINT_FRIENDLY_BW = "dithered bw album cover (slow, print friendly)"
|
||||
|
||||
class Hipsterfy(object):
|
||||
|
||||
@ -92,7 +95,7 @@ class HipsterfyPlaylistItem(object):
|
||||
if additional_properties:
|
||||
for prop in additional_properties:
|
||||
value = getattr(self, f"{prop}", "N/A")
|
||||
additional_html += f"<div style='margin: 4px 0; color:#fff;'><strong style='color:#fff;'>{prop.replace('_', ' ').title()}:</strong> <span style='color:#fff;'>{value}</span></div>"
|
||||
additional_html += f"<div style='margin: 4px 0; color:#e0e0e0;'><strong style='color:#fff;'>{prop.replace('_', ' ').title()}:</strong> <span style='color:#fff;'>{value}</span></div>"
|
||||
|
||||
# Style für die innere Box (Textfeld)
|
||||
inner_box_style_full = """
|
||||
@ -129,6 +132,54 @@ class HipsterfyPlaylistItem(object):
|
||||
z-index: 1;
|
||||
"""
|
||||
|
||||
if CardStyle(card_style) == CardStyle.PRINT_FRIENDLY_BW and self.album_images and len(self.album_images) > 0:
|
||||
album_b64 = self.get_bw_album_img_base64()
|
||||
outer_style = f"""
|
||||
width: 300px; height: 300px;
|
||||
border: 2px solid #222;
|
||||
border-radius: 18px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: url('data:image/png;base64,{album_b64}') center center/cover no-repeat;
|
||||
background-blend-mode: multiply;
|
||||
background-color: #fff;
|
||||
margin: 18px;
|
||||
box-shadow: none;
|
||||
color: #222;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
"""
|
||||
inner_box_style = """
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
margin: auto;
|
||||
background: rgba(255,255,255,0.9);
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
||||
padding: 18px 12px 12px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
"""
|
||||
primary_color = "#222"
|
||||
secondary_color = "#444"
|
||||
additional_html = additional_html.replace("color:#fff", f"color:{secondary_color}")
|
||||
additional_html = additional_html.replace("color:#e0e0e0", f"color:{secondary_color}")
|
||||
return f"""
|
||||
<div style="{outer_style}">
|
||||
<div style="{inner_box_style}">
|
||||
<p style="margin: 0 0 8px 0; font-size: 1.1em; color: {secondary_color};">{getattr(self, secondary_property)}</p>
|
||||
<h3 style="margin: 0 0 8px 0; font-size: 1.25em; color: {primary_color};">{getattr(self, primary_property)}</h3>
|
||||
{additional_html}
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
# FULL_ALBUM_COVER: Albumcover als Hintergrund, weiße Schrift, abgedunkelte Box
|
||||
if CardStyle(card_style) == CardStyle.FULL_ALBUM_COVER and self.album_images and len(self.album_images) > 0:
|
||||
album_url = self.album_images[0]['url']
|
||||
@ -151,7 +202,6 @@ class HipsterfyPlaylistItem(object):
|
||||
transition: box-shadow 0.2s;
|
||||
"""
|
||||
text_color = "#fff"
|
||||
additional_html = additional_html.replace("color:#fff", f"color:{text_color}")
|
||||
primary_color = "#fff"
|
||||
secondary_color = "#e0e0e0"
|
||||
inner_box_style = inner_box_style_full
|
||||
@ -313,6 +363,32 @@ class HipsterfyPlaylistItem(object):
|
||||
def qr_html(self):
|
||||
return self._qr_html
|
||||
|
||||
def get_bw_album_img_html(self):
|
||||
b64 = self.get_bw_album_img_base64()
|
||||
if b64:
|
||||
return f'<img src="data:image/png;base64,{b64}" style="width:{MAX_ALBUM_IMG_SIZE}px;height:{MAX_ALBUM_IMG_SIZE}px;border-radius:12px;box-shadow:0 2px 8px #aaa;margin-bottom:8px;" alt="Album Art BW"/>'
|
||||
return ""
|
||||
|
||||
def get_bw_album_img_base64(self):
|
||||
if self.album_images and len(self.album_images) > 0:
|
||||
url = self.album_images[0]['url']
|
||||
try:
|
||||
import requests
|
||||
from PIL import Image, ImageOps, ImageEnhance
|
||||
response = requests.get(url)
|
||||
img = Image.open(BytesIO(response.content)).convert("L")
|
||||
img.thumbnail((MAX_ALBUM_IMG_SIZE, MAX_ALBUM_IMG_SIZE))
|
||||
img = ImageOps.autocontrast(img)
|
||||
img = ImageEnhance.Contrast(img).enhance(2.0) # Kontrast erhöhen
|
||||
img = img.convert("1", dither=Image.FLOYDSTEINBERG, colors=12) # Stärkeres Dithering
|
||||
buffer = BytesIO()
|
||||
img.save(buffer, format="PNG")
|
||||
img_str = base64.b64encode(buffer.getvalue()).decode()
|
||||
return img_str
|
||||
except Exception:
|
||||
return ""
|
||||
return ""
|
||||
|
||||
|
||||
class HipsterfyPlaylist(object):
|
||||
def __init__(self, playlist_uri, hipsterfy:Hipsterfy):
|
||||
|
Reference in New Issue
Block a user