+import base64
import json
import logging
import os
attributes=allowed_attributes,
css_sanitizer=css_sanitizer)
+def get_gif_data():
+ gifs = []
+ static_path = Path(__file__).parent / "static"
+ for child in static_path.iterdir():
+ if child.suffix == ".gif":
+ with open(child, "rb") as gif:
+ b64 = base64.b64encode(gif.read()).decode()
+ gifs.append(f'<div class="img-data" id="{child.stem}" data-img-b64="{b64}"></div>')
+
+ gifs = "\n".join(gifs)
+ return gifs
+
+@app.context_processor
+def inject_global_vars():
+ return dict(gif_data=get_gif_data())
+
+
################################################################################
# Database
################################################################################
// Show pause button when audio is playing
var button = document.getElementById("play-pause-button");
audio.addEventListener("play", (event) => {
- button.src = "/static/lsp_btn_pause02.gif";
+ button.className = "lsp_btn_pause02";
+ button.src = customImage(document.getElementById("lsp_btn_pause02"));
})
// Show play button when audio is paused
audio.addEventListener("pause", (event) => {
- button.src = "/static/lsp_btn_play02.gif";
+ button.className = "lsp_btn_play02";
+ button.src = customImage(document.getElementById("lsp_btn_play02"));
})
// Audio position scrubbing
</head>
<body>
+ <!-- Embedded image data -->
+ {{ gif_data|safe }}
+
+ <script>
+
+ function customImage(element) {
+ // Customize an image by performing a palette swap on the .gif
+ // file. The source element must contain a data-img-b64 attribute
+ // containing the base64 representation of a .gif file. The byte
+ // indexes match .gifs from Aseprite, and may not work for all
+ // .gif files.
+
+ var style = window.getComputedStyle(document.body);
+ var bgcolor = style.getPropertyValue("--yellow");
+ var accolor = style.getPropertyValue("--purple");
+
+ // Convert base64 string to Uint8Array so we can modify it
+ var data = atob(element.dataset.imgB64);
+ var bytes = Uint8Array.from(data, c => c.charCodeAt(0));
+
+ // Replace background color palette bytes in gif file
+ bytes[16] = parseInt(bgcolor.substring(1, 3), 16);
+ bytes[17] = parseInt(bgcolor.substring(3, 5), 16);
+ bytes[18] = parseInt(bgcolor.substring(5, 7), 16);
+
+ // Replace foreground color palette bytes in gif file
+ bytes[19] = parseInt(accolor.substring(1, 3), 16);
+ bytes[20] = parseInt(accolor.substring(3, 5), 16);
+ bytes[21] = parseInt(accolor.substring(5, 7), 16);
+
+ // Convert Uint8Array back to base64 so we can use it in a src string
+ data = btoa(String.fromCharCode(...bytes));
+
+ // Embed base64 in a data string that can be used as an img src.
+ return `data:image/gif;base64, ${data}`;
+ }
+
+ function updateImageColors() {
+ // Perform a palette swap on all gifs based on current page colors
+
+ document.querySelectorAll(".img-data").forEach(e => {
+ document.querySelectorAll(`.${e.id}`).forEach(t => {
+ t.src = customImage(e);
+ });
+ });
+ }
+
+ // Update image colors on page load
+ document.addEventListener("DOMContentLoaded", updateImageColors);
+
+ </script>
+
<div class="page-header">
<div style="text-align: center;">
- <img src="/static/littlesongplace02.gif" class="title-image">
+ <img class="title-image littlesongplace02">
</div>
<!-- Navbar -->
<div class="navbar">
</div>
<div class="player-controls">
<button onclick="songPrevious()" class="player-button">
- <img src="/static/lsp_btn_prev02.gif" alt="Previous">
+ <img class="lsp_btn_prev02" alt="Previous">
</button>
<button onclick="songPlayPause()" class="player-button">
- <img src="/static/lsp_btn_pause02.gif" alt="Play" id="play-pause-button">
+ <img class="lsp_btn_pause02" alt="Play" id="play-pause-button">
</button>
<button onclick="songNext()" class="player-button">
- <img src="/static/lsp_btn_next02.gif" alt="Next">
+ <img class="lsp_btn_next02" alt="Next">
</button>
<input id="position-slider" name="song-position" type="range" min="0" max="1" step="any" value="0"/>
<span class="player-time" id="player-current-time">0:00</span>
<table>
<tr>
<td> <label for="bgcolor">Background</label> </td>
- <td> <input name="bgcolor" type="text" value="{{ user_bgcolor }}" data-coloris oninput="document.documentElement.style.setProperty('--yellow', this.value)"/> </td>
+ <td> <input name="bgcolor" type="text" value="{{ user_bgcolor }}" data-coloris oninput="updateColor('--yellow', this.value)"/> </td>
</tr>
<tr>
<td> <label for="fgcolor">Text</label> </td>
- <td> <input name="fgcolor" type="text" value="{{ user_fgcolor }}" data-coloris oninput="document.documentElement.style.setProperty('--black', this.value)"/> </td>
+ <td> <input name="fgcolor" type="text" value="{{ user_fgcolor }}" data-coloris oninput="updateColor('--black', this.value)"/> </td>
</tr>
<tr>
<td> <label for="accolor">Accent</label> </td>
- <td> <input name="accolor" type="text" value="{{ user_accolor }}" data-coloris oninput="document.documentElement.style.setProperty('--purple', this.value)"/> </td>
+ <td> <input name="accolor" type="text" value="{{ user_accolor }}" data-coloris oninput="updateColor('--purple', this.value)"/> </td>
</tr>
+ <script>
+ function updateColor(name, value) {
+ document.documentElement.style.setProperty(name, value);
+ updateImageColors();
+ }
+ </script>
</table>
<script>
Coloris({
<!-- Owner-Specific Buttons (Edit/Delete) -->
{% if session["userid"] == song.userid %}
<a href="/edit-song?songid={{ song.songid }}" class="song-list-button">
- <img src="/static/lsp_btn_edit02.gif" alt="Edit">
+ <img class="lsp_btn_edit02" alt="Edit">
</a>
<a href="/delete-song/{{ song.songid }}" onclick="return confirm("Are you sure you want to delete this song?")" class="song-list-button">
- <img src="/static/lsp_btn_delete02.gif" alt="Delete">
+ <img class="lsp_btn_delete02" alt="Delete">
</a>
{% endif %}
<!-- Details Button -->
<button onclick="return showDetails(event)" class="song-list-button">
- <img src="/static/lsp_btn_show02.gif" alt="Show Details">
+ <img class="lsp_btn_show02" alt="Show Details">
</button>
<!-- Play Button -->
<button onclick="return play(event)" class="song-list-button">
- <img src="/static/lsp_btn_play02.gif" alt="Play">
+ <img class="lsp_btn_play02" alt="Play">
</button>
</div>
</div>
// Show details
child.hidden = false;
event.target.alt = "Hide Details";
- event.target.src = "/static/lsp_btn_hide02.gif";
+ event.target.className = "lsp_btn_hide02";
+ event.target.src = customImage(document.getElementById("lsp_btn_hide02"));
}
else {
// Hide details
child.hidden = true;
event.target.alt = "Show Details";
- event.target.src = "/static/lsp_btn_show02.gif";
+ event.target.className = "lsp_btn_show02";
+ event.target.src = customImage(document.getElementById("lsp_btn_show02"));
}
}
}
NOW
-- Site color customization: apply custom colors to images
- YouTube importer
SOON
+- Apply user colors to songs in lists outside profile
- Player minimize button
- Shuffle all
- AJAX pages so songs can play during navigation