import panel as pn import base64 from io import BytesIO from hipsterfy.hipsterfy import Hipsterfy, HipsterfyPlaylist, HipsterfyPlaylistItem, CardStyle pn.extension("filedownload", "notifications", "location") def create_panel_page(hipsterfy: Hipsterfy, playlist_uri: str=None) -> pn.Template: # create widgets playlist_uri = pn.widgets.TextInput(name='Playlist URI', value=playlist_uri or '', placeholder='Enter Spotify Playlist URI') primary_item = pn.widgets.Select(name='Primary Item', options=HipsterfyPlaylistItem.get_available_properties(), value="release_year") secondary_item = pn.widgets.Select(name='Secondary Item', options=HipsterfyPlaylistItem.get_available_properties(), value="title") additional_items = pn.widgets.MultiChoice(name='Additional Items', options=HipsterfyPlaylistItem.get_available_properties(), value=["artists", "album", "popularity"]) card_style = pn.widgets.Select(name='Card Style', options=[style.value for style in CardStyle], value=CardStyle.FULL_ALBUM_COVER.value) cards_per_row_widget = pn.widgets.IntSlider(name="Cards per Row in print", start=1, end=8, value=4, step=1) pn.state.location.sync(playlist_uri, {"value": "playlist_uri"}) pn.state.location.sync(primary_item, {"value": "primary_item"}) pn.state.location.sync(secondary_item, {"value": "secondary_item"}) pn.state.location.sync(additional_items, {"value": "additional_items"}) pn.state.location.sync(card_style, {"value": "card_style"}) pn.state.location.sync(cards_per_row_widget, {"value": "cards_per_row"}) create_preview_button = pn.widgets.Button(name='Parse Playlist and Generate Cards', button_type='primary', sizing_mode='stretch_width') front_cards_column = pn.Column(sizing_mode='stretch_width') back_cards_column = pn.Column(sizing_mode='stretch_width') # FileDownload-Buttons (werden erst nach Preview-Generierung angezeigt) download_front_html_button = pn.widgets.FileDownload( label="Download Front Cards (HTML)", button_type="success", visible=False, filename="hipsterfy_front_cards.html", sizing_mode='stretch_width' ) download_back_html_button = pn.widgets.FileDownload( label="Download Back Cards (HTML)", button_type="success", visible=False, filename="hipsterfy_back_cards.html", sizing_mode='stretch_width' ) playlist_instructions = pn.pane.HTML( """

Playlist Instructions

Select any Spotify playlist and click the "Parse Playlist and Generate Cards" button to generate the cards.

Note: The playlist must be public accessible. Also dynamically individualized playlists (unfortunately a lot of spotify official playlists) Will not work.

""", sizing_mode='stretch_width' ) print_instructions = pn.pane.HTML( """

Print Instructions

To print the cards:

  1. Click on the "Download Front Cards" and "Download Back Cards" buttons to download the HTML files.
  2. Open the downloaded files in your web browser.
  3. Use your browser's print function (usually Ctrl+P or Cmd+P) to print the cards.
  4. Make sure to first print all front cards, then all back cards on the back side of the paper.
  5. Adjust the print settings to ensure the cards fit well on the page.
""", sizing_mode='stretch_width' ) print_instructions.visible = False # Initially hidden, will be shown after preview generation def generate_html(cards, row_reverse=False): html = "Print Cards" html += """ """ html += "" cards_per_row = cards_per_row_widget.value # <-- Widget-Wert verwenden # Karten in Reihen zu je 3 anordnen for i in range(0, len(cards), cards_per_row): row = cards[i:i+cards_per_row] if row_reverse: row = row[::-1] html += '
' for card in row: html += f'
{card}
' html += '
' html += "" return html.encode() # FileDownload erwartet Bytes def update_download_buttons(): # Front Cards cards = [pane.object for pane in front_cards_column[1:]] download_front_html_button.callback = lambda: BytesIO(generate_html(cards, row_reverse=False)) download_front_html_button.visible = True # Back Cards cards_back = [pane.object for pane in back_cards_column[1:]] download_back_html_button.callback = lambda: BytesIO(generate_html(cards_back, row_reverse=True)) download_back_html_button.visible = True print_instructions.visible = True # Show print instructions after generating preview pn.state.notifications.success("Download buttons updated successfully!. Download and print them from the sidebar") def create_preview(event): try: pn.state.notifications.info("Generating preview for playlist...") # clear previous cards front_cards_column.clear() back_cards_column.clear() front_cards_column.loading = True back_cards_column.loading = True front_cards_column.append(pn.pane.HTML("

Front Cards

", sizing_mode='stretch_width')) back_cards_column.append(pn.pane.HTML("

Back Cards

", sizing_mode='stretch_width')) # get playlist URI uri = playlist_uri.value.strip() if not uri: pn.state.notifications.error("Please enter a valid Spotify Playlist URI.") return # create HipsterfyPlaylist instance hipsterfy_playlist = HipsterfyPlaylist(uri, hipsterfy) new_back_cards = [] new_front_cards = [] # generate front and back cards for each item in the playlist for item in hipsterfy_playlist.get_tracks_data(): front_card = item.generate_hipsterfy_front_card(include_preview=False, card_style=CardStyle(card_style.value)) back_card = item.generate_hipsterfy_back_card( primary_property=primary_item.value, secondary_property=secondary_item.value, additional_properties=additional_items.value, card_style=CardStyle(card_style.value) ) new_front_cards.append(pn.pane.HTML(front_card, sizing_mode='stretch_width')) new_back_cards.append(pn.pane.HTML(back_card, sizing_mode='stretch_width')) for card in new_front_cards: front_cards_column.append(card) for card in new_back_cards: back_cards_column.append(card) pn.state.notifications.success("Preview generated successfully!") pn.state.notifications.info("Prepare to download the cards as HTML files...") update_download_buttons() except Exception as e: pn.state.notifications.error(f"Error generating preview: {str(e)}") finally: front_cards_column.loading = False back_cards_column.loading = False create_preview_button.on_click(create_preview) template = pn.template.FastListTemplate( title='Hipsterfy', sidebar=[ playlist_instructions, playlist_uri, primary_item, secondary_item, additional_items, card_style, cards_per_row_widget, create_preview_button, download_front_html_button, download_back_html_button, print_instructions ], main=[pn.Row(front_cards_column, back_cards_column, sizing_mode='stretch_width')], accent_base_color='indigo', header_background='indigo', header_color='white' ) return template