diff --git a/legacy/getie3.gif b/legacy/getie3.gif new file mode 100644 index 0000000..19a7353 Binary files /dev/null and b/legacy/getie3.gif differ diff --git a/legacy/getnet3.gif b/legacy/getnet3.gif new file mode 100644 index 0000000..06fe341 Binary files /dev/null and b/legacy/getnet3.gif differ diff --git a/legacy/index.html b/legacy/index.html new file mode 100644 index 0000000..bdde588 --- /dev/null +++ b/legacy/index.html @@ -0,0 +1,81 @@ + + + + Monochrome Legacy + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Monochrome Music
+ music +
+ Welcome to Monochrome Music Legacy Edition check out the latest hits new songs added daily under construction sign guestbook +
+
+
Menu
+
+ +
+ +
+
+ IE Logo

+ Netscape Logo

+ Free and open software +
+
+
+ +
+ + Status: Ready to play. + +

+ +
+ + +
+ Search Music: +
+ + +
+
+ +
+

Loading...

+
+
+
+ + + diff --git a/legacy/legacy.css b/legacy/legacy.css new file mode 100644 index 0000000..095f2e0 --- /dev/null +++ b/legacy/legacy.css @@ -0,0 +1,98 @@ +/* Retro 90s Styling */ +body { + background-color: #c0c0c0; + font-family: "Times New Roman", Times, serif; + color: #000000; + margin: 0; + padding: 0; + background-image: url("assets/bg_texture.gif"); /* Fallback if not found is just grey */ +} + +a { + color: #000000; + text-decoration: underline; +} + +a:visited { + color: #444444; +} + +a:active { + color: #666666; +} + +h1, +h2, +h3 { + font-family: Arial, Helvetica, sans-serif; + margin-top: 5px; + margin-bottom: 5px; +} + +table { + border-collapse: collapse; /* Not strictly 90s but makes it cleaner */ +} + +.beveled-box { + border-top: 2px solid #ffffff; + border-left: 2px solid #ffffff; + border-right: 2px solid #808080; + border-bottom: 2px solid #808080; + background-color: #c0c0c0; + padding: 5px; +} + +.inset-box { + border-top: 2px solid #808080; + border-left: 2px solid #808080; + border-right: 2px solid #ffffff; + border-bottom: 2px solid #ffffff; + background-color: #ffffff; + padding: 2px; +} + +.marquee-container { + background-color: #000000; + color: #ffffff; + font-weight: bold; + font-family: "Courier New", Courier, monospace; + padding: 2px; + overflow: hidden; + white-space: nowrap; +} + +.sidebar-link { + display: block; + margin-bottom: 10px; +} + +.track-row td { + padding: 2px 5px; + border: 1px solid #808080; +} + +.controls { + text-align: center; + padding: 10px; + background-color: #a0a0a0; + border: 2px outset #ffffff; +} + +button { + background-color: #c0c0c0; + border-top: 2px solid #ffffff; + border-left: 2px solid #ffffff; + border-right: 2px solid #000000; + border-bottom: 2px solid #000000; + padding: 2px 10px; + font-family: Arial, sans-serif; + font-size: 12px; + cursor: pointer; +} + +button:active { + border-top: 2px solid #000000; + border-left: 2px solid #000000; + border-right: 2px solid #ffffff; + border-bottom: 2px solid #ffffff; +} diff --git a/legacy/legacy.js b/legacy/legacy.js new file mode 100644 index 0000000..1c5da55 --- /dev/null +++ b/legacy/legacy.js @@ -0,0 +1,304 @@ +// strictly ES5/Legacy JS +(function () { + var currentApiUrl = ""; // Will be set from instances.json + var isHttpFallback = false; + var FALLBACK_INSTANCES = [ + "https://triton.squid.wtf", + "https://wolf.qqdl.site", + "https://maus.qqdl.site", + "https://vogel.qqdl.site", + "https://katze.qqdl.site", + "https://hund.qqdl.site", + "https://tidal.kinoplus.online", + "https://tidal-api.binimum.org", + ]; + var audioPlayer; + var currentTrackInfo; + + window.onload = function () { + audioPlayer = document.getElementById("audio-player"); + currentTrackInfo = document.getElementById("now-playing-info"); + + fetchInstances(function (url) { + currentApiUrl = url; + loadRecentTracks(); + }); + + // Simple event delegation if we wanted, but explicit bindings are safer for old browsers + var btnHome = document.getElementById("btn-home"); + if (btnHome) { + btnHome.onclick = function () { + loadRecentTracks(); + return false; + }; + } + // Search button removed, using persistent form now + + var searchForm = document.getElementById("search-form"); + searchForm.onsubmit = function () { + var query = document.getElementById("search-input").value; + performSearch(query); + return false; + }; + }; + + function loadRecentTracks() { + setContent("Loading recent tracks..."); + apiRequest( + "/search/?s=a&limit=20", + function (data) { + if (data && data.data && data.data.items) { + renderTracks(data.data.items, "Recently Added / Popular"); + } else { + setContent("No recent tracks found."); + } + }, + function (err) { + setContent("Error loading tracks: " + err); + } + ); + } + + function fetchInstances(callback) { + var xhr = createXHR(); + xhr.open("GET", "instances.json", true); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 300) { + try { + var instances = JSON.parse(xhr.responseText); + if (instances && instances.length > 0) { + var randomUrl = + instances[Math.floor(Math.random() * instances.length)]; + // Remove trailing slash if present + if (randomUrl.charAt(randomUrl.length - 1) === "/") { + randomUrl = randomUrl.substring(0, randomUrl.length - 1); + } + callback(randomUrl); + } else { + useFallback(callback); + } + } catch (e) { + useFallback(callback); + } + } else { + useFallback(callback); + } + } + }; + xhr.onerror = function () { + useFallback(callback); + }; + xhr.send(); + } + + function useFallback(callback) { + var randomUrl = + FALLBACK_INSTANCES[Math.floor(Math.random() * FALLBACK_INSTANCES.length)]; + callback(randomUrl); + } + + function performSearch(query) { + var resultsDiv = document.getElementById("search-results"); + if (!resultsDiv) { + setContent('
Searching...
'); + resultsDiv = document.getElementById("search-results"); + } else { + resultsDiv.innerHTML = "Searching..."; + } + + apiRequest( + "/search/?s=" + encodeURIComponent(query) + "&limit=25", + function (data) { + var tracks = (data && data.data && data.data.items) ? data.data.items : []; + if (tracks.length === 0) { + resultsDiv.innerHTML = "No results found."; + return; + } + // Manually render table string to avoid complex DOM manipulation + var html = + ''; + html += + ''; + + for (var i = 0; i < tracks.length; i++) { + var t = tracks[i]; + var safeTitle = escapeHtml(t.title); + var safeArtist = escapeHtml(t.artist.name); + var safeAlbum = escapeHtml(t.album.title); + + html += ''; + html += + '"; + html += ""; + html += ""; + html += ""; + html += ""; + } + html += "
PlayTitleArtistAlbum
" + safeTitle + "" + safeArtist + "" + safeAlbum + "
"; + + resultsDiv.innerHTML = html; + }, + function (err) { + resultsDiv.innerHTML = "Error: " + err; + } + ); + } + + function renderTracks(tracks, title) { + var html = "

" + title + "

"; + html += ''; + html += + ''; + + for (var i = 0; i < tracks.length; i++) { + var t = tracks[i]; + var safeTitle = escapeHtml(t.title); + var safeArtist = escapeHtml(t.artist.name); + var safeAlbum = escapeHtml(t.album.title); + + html += ''; + html += + '"; + html += ""; + html += ""; + html += ""; + html += ""; + } + html += "
PlayTitleArtistAlbum
" + safeTitle + "" + safeArtist + "" + safeAlbum + "
"; + + setContent(html); + } + + // Global player function + window.playTrack = function (id) { + apiRequest( + "/track/?id=" + id + "&quality=HIGH", + function (data) { + if (data && data.data && data.data.manifest) { + try { + // Manifest is Base64 encoded JSON + var manifestStr = base64Decode(data.data.manifest); + var manifest = JSON.parse(manifestStr); + if (manifest.urls && manifest.urls.length > 0) { + audioPlayer.src = manifest.urls[0]; + audioPlayer.play(); + + if (currentTrackInfo) { + // We don't get full track info in playback response easily without another call or passing it, + // but for legacy we might accept just "Now Playing..." or maybe we pass it? + // For now simplicity: + currentTrackInfo.innerHTML = "Now Playing..."; + } + } else { + alert("No stream URLs found in manifest."); + } + } catch (e) { + alert("Error parsing playback manifest: " + e.message); + } + } else { + alert("Invalid track data received."); + } + }, + function (err) { + alert("Error playing track: " + err); + } + ); + }; + + function setContent(html) { + var content = document.getElementById("main-content"); + content.innerHTML = html; + } + + function apiRequest(endpoint, success, error) { + var xhr = createXHR(); + // Use fallback HTTP if needed logic + var finalUrl = currentApiUrl; + if (isHttpFallback && finalUrl.indexOf("https://") === 0) { + finalUrl = "http://" + finalUrl.substring(8); + } + + xhr.open("GET", finalUrl + endpoint, true); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status >= 200 && xhr.status < 300) { + try { + var data = JSON.parse(xhr.responseText); + success(data); + } catch (e) { + error("JSON Parse Error"); + } + } else { + // If failed and not yet fallback, try fallback + if(!isHttpFallback) { + isHttpFallback = true; + apiRequest(endpoint, success, error); + } else { + error("HTTP " + xhr.status); + } + } + } + }; + xhr.onerror = function () { + if(!isHttpFallback) { + isHttpFallback = true; + apiRequest(endpoint, success, error); + } else { + error("Network Error"); + } + }; + xhr.send(); + } + + function escapeHtml(text) { + if (!text) return ""; + return text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + function createXHR() { + if (window.XMLHttpRequest) { + return new XMLHttpRequest(); + } + // IE5/6 support + try { + return new ActiveXObject("Msxml2.XMLHTTP"); + } catch (e) {} + try { + return new ActiveXObject("Microsoft.XMLHTTP"); + } catch (e) {} + alert("Your browser does not support AJAX!"); + return null; + } + + function base64Decode(str) { + if (window.atob) { + return window.atob(str); + } + // Polyfill for IE + var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + var output = ""; + str = String(str).replace(/=+$/, ''); + if (str.length % 4 == 1) { + throw new Error("'atob' failed: The string to be decoded is not correctly encoded."); + } + for ( + var bc = 0, bs = 0, buffer, i = 0; + buffer = str.charAt(i++); + ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, + bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 + ) { + buffer = chars.indexOf(buffer); + } + return output; + } +})();