Last active
July 14, 2025 15:33
-
-
Save antonl-dev/efe9e7c097fd5a9e1f65b0fe4779cc91 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Song Launchpad</title> | |
| <style> | |
| :root { | |
| --bg-gradient: linear-gradient(135deg, #2c004f, #6b006d, #b51778, #f05454); | |
| --card-bg: rgba(20, 18, 30, 0.8); | |
| --text-color: #f0f0f0; | |
| --accent-color: #e44d87; | |
| --border-color: rgba(255, 255, 255, 0.2); | |
| --shadow-color: rgba(0, 0, 0, 0.5); | |
| --input-bg: rgba(0, 0, 0, 0.3); | |
| } | |
| html, body { | |
| height: 100%; | |
| margin: 0; | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| color: var(--text-color); | |
| } | |
| body { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: var(--bg-gradient); | |
| background-size: 200% 200%; | |
| animation: gradient-animation 15s ease infinite; | |
| padding: 20px; | |
| box-sizing: border-box; | |
| } | |
| @keyframes gradient-animation { | |
| 0% { background-position: 0% 50%; } | |
| 50% { background-position: 100% 50%; } | |
| 100% { background-position: 0% 50%; } | |
| } | |
| .launchpad { | |
| width: 100%; | |
| max-width: 600px; | |
| background: var(--card-bg); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border: 1px solid var(--border-color); | |
| border-radius: 16px; | |
| padding: 30px; | |
| box-shadow: 0 8px 32px 0 var(--shadow-color); | |
| text-align: center; | |
| } | |
| h1 { | |
| margin-top: 0; | |
| margin-bottom: 10px; | |
| font-weight: 600; | |
| } | |
| p { | |
| margin-top: 0; | |
| margin-bottom: 25px; | |
| opacity: 0.8; | |
| font-size: 0.9em; | |
| } | |
| #song-input { | |
| width: 100%; | |
| min-height: 60px; | |
| padding: 15px; | |
| box-sizing: border-box; | |
| background-color: var(--input-bg); | |
| border: 2px solid var(--border-color); | |
| border-radius: 10px; | |
| color: var(--text-color); | |
| font-size: 1.1em; | |
| resize: vertical; | |
| transition: all 0.3s ease; | |
| } | |
| #song-input:focus, #song-input.drag-over { | |
| border-color: var(--accent-color); | |
| outline: none; | |
| box-shadow: 0 0 15px rgba(228, 77, 135, 0.5); | |
| } | |
| #song-input::placeholder { | |
| color: rgba(255, 255, 255, 0.4); | |
| } | |
| .platforms-container { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); | |
| gap: 15px; | |
| margin-top: 30px; | |
| } | |
| .platform-link { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 60px; | |
| height: 60px; | |
| background-color: var(--input-bg); | |
| border: 1px solid var(--border-color); | |
| border-radius: 50%; | |
| text-decoration: none; | |
| transition: all 0.2s ease-in-out; | |
| user-select: none; | |
| /* For fallback text */ | |
| color: var(--text-color); | |
| font-size: 1.5em; | |
| font-weight: bold; | |
| } | |
| .platform-link:hover { | |
| /* The --brand-color is set via JS for each button */ | |
| background-color: var(--brand-color, var(--accent-color)); | |
| border-color: var(--brand-color, var(--accent-color)); | |
| transform: scale(1.1); | |
| } | |
| .platform-link img { | |
| width: 55%; | |
| height: 55%; | |
| filter: invert(1); /* Makes icons white */ | |
| transition: filter 0.2s ease-in-out; | |
| } | |
| .platform-link:hover img { | |
| filter: invert(0); /* On hover, shows original icon color if it's dark on a light bg */ | |
| } | |
| /* Specific filter override for icons that are already light */ | |
| .platform-link[data-key="y"]:hover img, | |
| .platform-link[data-key="b"]:hover img, | |
| .platform-link[data-key="c"]:hover img { | |
| filter: invert(1) brightness(1.5); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="launchpad"> | |
| <h1>Song Launchpad</h1> | |
| <p>Paste or drag & drop a song title below, then click a service to search.</p> | |
| <textarea id="song-input" placeholder="e.g., Run To You - Sam Bird , Papa Zeus"></textarea> | |
| <div id="platforms-container" class="platforms-container"> | |
| <!-- Platform links will be generated here by JavaScript --> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const inputArea = document.getElementById('song-input'); | |
| const platformsContainer = document.getElementById('platforms-container'); | |
| // Data now includes icon URLs and brand colors | |
| const searchProviders = [ | |
| { name: "YouTube Music", key: "y", base_url: "https://music.youtube.com/search?q=", encoding_style: "query_plus", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/youtubemusic.svg", color: "#FF0000" }, | |
| { name: "Spotify", key: "s", base_url: "https://open.spotify.com/search/", encoding_style: "path_percent", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/spotify.svg", color: "#1DB954" }, | |
| { name: "Apple Music", key: "a", base_url: "https://music.apple.com/us/search?term=", encoding_style: "path_percent", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/apple.svg", color: "#FA57C1" }, | |
| { name: "SoundCloud", key: "c", base_url: "https://soundcloud.com/search?q=", encoding_style: "path_percent", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/soundcloud.svg", color: "#FF5500" }, | |
| { name: "Deezer", key: "d", base_url: "https://www.deezer.com/search/", encoding_style: "path_percent", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/deezer.svg", color: "#FEAA2D" }, | |
| { name: "Bandcamp", key: "b", base_url: "https://bandcamp.com/search?q=", encoding_style: "bandcamp_special", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/bandcamp.svg", color: "#1DA0C3" }, | |
| { name: "DuckDuckGo", key: "w", base_url: "https://duckduckgo.com/?q=", encoding_style: "query_plus", icon_url: "https://cdn.jsdelivr.net/npm/simple-icons@v11/icons/duckduckgo.svg", color: "#DE5833" } | |
| ]; | |
| function encodeSongTitle(title, style) { | |
| switch (style) { | |
| case "query_plus": return encodeURIComponent(title).replace(/%20/g, '+'); | |
| case "path_percent": return encodeURIComponent(title); | |
| case "bandcamp_special": return encodeURIComponent(title.replace(/[\s.-]/g, '+')); | |
| default: return encodeURIComponent(title); | |
| } | |
| } | |
| searchProviders.forEach(provider => { | |
| const link = document.createElement('a'); | |
| link.className = 'platform-link'; | |
| link.href = '#'; | |
| link.title = `Search on ${provider.name}`; | |
| link.setAttribute('data-key', provider.key); | |
| // Set the brand color as a CSS custom property for the hover effect | |
| link.style.setProperty('--brand-color', provider.color); | |
| const icon = document.createElement('img'); | |
| icon.src = provider.icon_url; | |
| icon.alt = provider.name; | |
| // --- Fallback Mechanism --- | |
| // If the icon fails to load, remove it and show the text key instead. | |
| icon.onerror = function() { | |
| link.textContent = provider.key.toUpperCase(); | |
| }; | |
| link.appendChild(icon); | |
| link.addEventListener('click', (e) => { | |
| e.preventDefault(); | |
| const songTitle = inputArea.value.trim(); | |
| if (!songTitle) { | |
| inputArea.focus(); | |
| inputArea.placeholder = "Please enter a song title first!"; | |
| return; | |
| } | |
| const encodedTitle = encodeSongTitle(songTitle, provider.encoding_style); | |
| const finalUrl = provider.base_url + encodedTitle; | |
| window.open(finalUrl, '_blank'); | |
| }); | |
| platformsContainer.appendChild(link); | |
| }); | |
| // --- Drag and Drop functionality --- | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| inputArea.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation(); }, false); | |
| }); | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| inputArea.addEventListener(eventName, () => inputArea.classList.add('drag-over'), false); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| inputArea.addEventListener(eventName, () => inputArea.classList.remove('drag-over'), false); | |
| }); | |
| inputArea.addEventListener('drop', e => { | |
| const droppedText = e.dataTransfer.getData('text/plain'); | |
| if (droppedText) inputArea.value = droppedText; | |
| }, false); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment