]> littlesong.place Git - littlesongplace.git/commitdiff
Palette swap gifs using user profile colors
authorChris Fulljames <christianfulljames@gmail.com>
Sat, 8 Feb 2025 00:32:50 +0000 (19:32 -0500)
committerChris Fulljames <christianfulljames@gmail.com>
Sat, 8 Feb 2025 00:32:50 +0000 (19:32 -0500)
main.py
static/player.js
templates/base.html
templates/profile.html
templates/song-list.html
todo.txt

diff --git a/main.py b/main.py
index 92c2a1ee2092d2b6070218b603487d909a167daf..856e062f04ef13792c6cef885072a46bb4c9e8c0 100644 (file)
--- a/main.py
+++ b/main.py
@@ -1,3 +1,4 @@
+import base64
 import json
 import logging
 import os
@@ -695,6 +696,23 @@ def sanitize_user_text(text):
                 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
 ################################################################################
index c55fdf065d3c390febd0ca1efe8d53187d5f11da..868e8dd0d8942a3b7aa5278f1c0fac122a1fc3df 100644 (file)
@@ -157,12 +157,14 @@ document.addEventListener("DOMContentLoaded", (event) => {
     // 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
index b1d4639a50fe8c87d73316aa400f06fb082a14d0..693afd35127d61016645ea0d0152cbe65675081b 100644 (file)
     </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>
index f078ed5a0b0afd97b633282a7ba91b5e05e87eca..1b8144773937160a67f31852c6e3a6f3f74d9e3b 100644 (file)
     <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({
index e0b546bc90a9b9016bdc96eef4e269ee64a58a40..7d391763827077dd8feb3cf084b40a1b157ef6e3 100644 (file)
                 <!-- 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(&#34;Are you sure you want to delete this song?&#34;)" 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>
@@ -160,13 +160,15 @@ function showDetails(event) {
                 // 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"));
             }
         }
     }
index d9cfa9dc8893ca26b067650fefefca30df344eee..cb9f37a8714f4f92f61f54fb4642ac82c3bd7e9c 100644 (file)
--- a/todo.txt
+++ b/todo.txt
@@ -1,8 +1,8 @@
 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