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'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):