diff --git a/hipsterfy/hipsterfy.py b/hipsterfy/hipsterfy.py
index ff9759b..f38ed21 100644
--- a/hipsterfy/hipsterfy.py
+++ b/hipsterfy/hipsterfy.py
@@ -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"
{prop.replace('_', ' ').title()}: {value}
"
+ additional_html += f"{prop.replace('_', ' ').title()}: {value}
"
# 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"""
+
+
+
{getattr(self, secondary_property)}
+
{getattr(self, primary_property)}
+ {additional_html}
+
+
+ """
# 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'
'
+ 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):