');
+ 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 +=
+ '
Play
Title
Artist
Album
';
+
+ 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 += "
" + safeTitle + "
";
+ html += "
" + safeArtist + "
";
+ html += "
" + safeAlbum + "
";
+ html += "
";
+ }
+ html += "
";
+
+ resultsDiv.innerHTML = html;
+ },
+ function (err) {
+ resultsDiv.innerHTML = "Error: " + err;
+ }
+ );
+ }
+
+ function renderTracks(tracks, title) {
+ var html = "
" + title + "
";
+ html += '
';
+ html +=
+ '
Play
Title
Artist
Album
';
+
+ 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 += "
" + safeTitle + "
";
+ html += "
" + safeArtist + "
";
+ html += "
" + safeAlbum + "
";
+ html += "
";
+ }
+ html += "
";
+
+ 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;
+ }
+})();