magic dithering
This commit is contained in:
@ -14,9 +14,12 @@ import pandas as pd
|
|||||||
import re
|
import re
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
MAX_ALBUM_IMG_SIZE = 512
|
||||||
|
|
||||||
class CardStyle(str, Enum):
|
class CardStyle(str, Enum):
|
||||||
FULL_ALBUM_COVER = "full album cover"
|
FULL_ALBUM_COVER = "full album cover"
|
||||||
MINIMAL = "minimal"
|
MINIMAL = "minimal"
|
||||||
|
PRINT_FRIENDLY_BW = "dithered bw album cover (slow, print friendly)"
|
||||||
|
|
||||||
class Hipsterfy(object):
|
class Hipsterfy(object):
|
||||||
|
|
||||||
@ -92,7 +95,7 @@ class HipsterfyPlaylistItem(object):
|
|||||||
if additional_properties:
|
if additional_properties:
|
||||||
for prop in additional_properties:
|
for prop in additional_properties:
|
||||||
value = getattr(self, f"{prop}", "N/A")
|
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)
|
# Style für die innere Box (Textfeld)
|
||||||
inner_box_style_full = """
|
inner_box_style_full = """
|
||||||
@ -129,6 +132,54 @@ class HipsterfyPlaylistItem(object):
|
|||||||
z-index: 1;
|
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
|
# 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:
|
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']
|
album_url = self.album_images[0]['url']
|
||||||
@ -151,7 +202,6 @@ class HipsterfyPlaylistItem(object):
|
|||||||
transition: box-shadow 0.2s;
|
transition: box-shadow 0.2s;
|
||||||
"""
|
"""
|
||||||
text_color = "#fff"
|
text_color = "#fff"
|
||||||
additional_html = additional_html.replace("color:#fff", f"color:{text_color}")
|
|
||||||
primary_color = "#fff"
|
primary_color = "#fff"
|
||||||
secondary_color = "#e0e0e0"
|
secondary_color = "#e0e0e0"
|
||||||
inner_box_style = inner_box_style_full
|
inner_box_style = inner_box_style_full
|
||||||
@ -313,6 +363,32 @@ class HipsterfyPlaylistItem(object):
|
|||||||
def qr_html(self):
|
def qr_html(self):
|
||||||
return self._qr_html
|
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):
|
class HipsterfyPlaylist(object):
|
||||||
def __init__(self, playlist_uri, hipsterfy:Hipsterfy):
|
def __init__(self, playlist_uri, hipsterfy:Hipsterfy):
|
||||||
|
Reference in New Issue
Block a user