use old jquery
This commit is contained in:
parent
b3bfbc3ac7
commit
ed67ac61cb
3 changed files with 175 additions and 207 deletions
|
|
@ -4,6 +4,7 @@
|
||||||
<title>Monochrome Legacy</title>
|
<title>Monochrome Legacy</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<link rel="stylesheet" type="text/css" href="legacy.css">
|
<link rel="stylesheet" type="text/css" href="legacy.css">
|
||||||
|
<script type="text/javascript" src="jquery.min.js"></script>
|
||||||
<script type="text/javascript" src="legacy.js"></script>
|
<script type="text/javascript" src="legacy.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
32
legacy/jquery.min.js
vendored
Normal file
32
legacy/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
349
legacy/legacy.js
349
legacy/legacy.js
|
|
@ -1,5 +1,5 @@
|
||||||
// strictly ES5/Legacy JS
|
// jQuery 1.12.4 Legacy Support
|
||||||
(function () {
|
$(document).ready(function () {
|
||||||
var currentApiUrl = ""; // Will be set from instances.json
|
var currentApiUrl = ""; // Will be set from instances.json
|
||||||
var isHttpFallback = false;
|
var isHttpFallback = false;
|
||||||
var FALLBACK_INSTANCES = [
|
var FALLBACK_INSTANCES = [
|
||||||
|
|
@ -12,34 +12,63 @@
|
||||||
"https://tidal.kinoplus.online",
|
"https://tidal.kinoplus.online",
|
||||||
"https://tidal-api.binimum.org",
|
"https://tidal-api.binimum.org",
|
||||||
];
|
];
|
||||||
var audioPlayer;
|
var audioPlayer = $("#audio-player")[0];
|
||||||
var currentTrackInfo;
|
var currentTrackInfo = $("#now-playing-info");
|
||||||
|
|
||||||
window.onload = function () {
|
// Initial Load
|
||||||
audioPlayer = document.getElementById("audio-player");
|
fetchInstances(function (url) {
|
||||||
currentTrackInfo = document.getElementById("now-playing-info");
|
currentApiUrl = url;
|
||||||
|
loadRecentTracks();
|
||||||
|
});
|
||||||
|
|
||||||
fetchInstances(function (url) {
|
// Event Bindings
|
||||||
currentApiUrl = url;
|
$("#btn-home").click(function (e) {
|
||||||
loadRecentTracks();
|
e.preventDefault();
|
||||||
});
|
loadRecentTracks();
|
||||||
|
});
|
||||||
|
|
||||||
// Simple event delegation if we wanted, but explicit bindings are safer for old browsers
|
$("#search-form").submit(function (e) {
|
||||||
var btnHome = document.getElementById("btn-home");
|
e.preventDefault();
|
||||||
if (btnHome) {
|
var query = $("#search-input").val();
|
||||||
btnHome.onclick = function () {
|
performSearch(query);
|
||||||
loadRecentTracks();
|
});
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Search button removed, using persistent form now
|
|
||||||
|
|
||||||
var searchForm = document.getElementById("search-form");
|
// Global functions exposed for inline onclicks
|
||||||
searchForm.onsubmit = function () {
|
window.playTrack = function (id) {
|
||||||
var query = document.getElementById("search-input").value;
|
apiRequest(
|
||||||
performSearch(query);
|
"/track/?id=" + id + "&quality=HIGH",
|
||||||
return false;
|
function (data) {
|
||||||
};
|
if (data && data.data && data.data.manifest) {
|
||||||
|
try {
|
||||||
|
var manifestStr = base64Decode(data.data.manifest);
|
||||||
|
var manifest = JSON.parse(manifestStr);
|
||||||
|
if (manifest.urls && manifest.urls.length > 0) {
|
||||||
|
var streamUrl = manifest.urls[0];
|
||||||
|
// Check if HTML5 audio is supported and has play method
|
||||||
|
if (audioPlayer && typeof audioPlayer.play === 'function') {
|
||||||
|
audioPlayer.src = streamUrl;
|
||||||
|
audioPlayer.play();
|
||||||
|
} else {
|
||||||
|
playWithEmbed(streamUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTrackInfo.length) {
|
||||||
|
currentTrackInfo.html("Now Playing...");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("No stream URLs found.");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error parsing playback manifest: " + e.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Invalid track data received.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function (err) {
|
||||||
|
alert("Error playing track: " + err);
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadRecentTracks() {
|
function loadRecentTracks() {
|
||||||
|
|
@ -48,9 +77,9 @@
|
||||||
"/search/?s=a&limit=20",
|
"/search/?s=a&limit=20",
|
||||||
function (data) {
|
function (data) {
|
||||||
if (data && data.data && data.data.items) {
|
if (data && data.data && data.data.items) {
|
||||||
renderTracks(data.data.items, "Recently Added / Popular");
|
renderTracks(data.data.items, "Recently Added / Popular");
|
||||||
} else {
|
} else {
|
||||||
setContent("No recent tracks found.");
|
setContent("No recent tracks found.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function (err) {
|
function (err) {
|
||||||
|
|
@ -60,36 +89,26 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchInstances(callback) {
|
function fetchInstances(callback) {
|
||||||
var xhr = createXHR();
|
// using $.ajax directly to handle errors robustly
|
||||||
xhr.open("GET", "instances.json", true);
|
$.ajax({
|
||||||
xhr.onreadystatechange = function () {
|
url: "instances.json",
|
||||||
if (xhr.readyState === 4) {
|
dataType: "json",
|
||||||
if (xhr.status >= 200 && xhr.status < 300) {
|
success: function (instances) {
|
||||||
try {
|
if (instances && instances.length > 0) {
|
||||||
var instances = JSON.parse(xhr.responseText);
|
var randomUrl =
|
||||||
if (instances && instances.length > 0) {
|
instances[Math.floor(Math.random() * instances.length)];
|
||||||
var randomUrl =
|
if (randomUrl.charAt(randomUrl.length - 1) === "/") {
|
||||||
instances[Math.floor(Math.random() * instances.length)];
|
randomUrl = randomUrl.substring(0, randomUrl.length - 1);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
callback(randomUrl);
|
||||||
} else {
|
} else {
|
||||||
useFallback(callback);
|
useFallback(callback);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
useFallback(callback);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
xhr.onerror = function () {
|
|
||||||
useFallback(callback);
|
|
||||||
};
|
|
||||||
xhr.send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function useFallback(callback) {
|
function useFallback(callback) {
|
||||||
|
|
@ -99,12 +118,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function performSearch(query) {
|
function performSearch(query) {
|
||||||
var resultsDiv = document.getElementById("search-results");
|
var resultsDiv = $("#search-results");
|
||||||
if (!resultsDiv) {
|
if (resultsDiv.length === 0) {
|
||||||
setContent('<div id="search-results">Searching...</div>');
|
setContent('<div id="search-results">Searching...</div>');
|
||||||
resultsDiv = document.getElementById("search-results");
|
resultsDiv = $("#search-results");
|
||||||
} else {
|
} else {
|
||||||
resultsDiv.innerHTML = "Searching...";
|
resultsDiv.html("Searching...");
|
||||||
}
|
}
|
||||||
|
|
||||||
apiRequest(
|
apiRequest(
|
||||||
|
|
@ -112,17 +131,16 @@
|
||||||
function (data) {
|
function (data) {
|
||||||
var tracks = (data && data.data && data.data.items) ? data.data.items : [];
|
var tracks = (data && data.data && data.data.items) ? data.data.items : [];
|
||||||
if (tracks.length === 0) {
|
if (tracks.length === 0) {
|
||||||
resultsDiv.innerHTML = "No results found.";
|
resultsDiv.html("No results found.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Manually render table string to avoid complex DOM manipulation
|
|
||||||
var html =
|
var html =
|
||||||
'<table width="100%" border="1" cellpadding="2" cellspacing="0">';
|
'<table width="100%" border="1" cellpadding="2" cellspacing="0">';
|
||||||
html +=
|
html +=
|
||||||
'<tr bgcolor="#bbbbbb"><th>Play</th><th>Title</th><th>Artist</th><th>Album</th></tr>';
|
'<tr bgcolor="#bbbbbb"><th>Play</th><th>Title</th><th>Artist</th><th>Album</th></tr>';
|
||||||
|
|
||||||
for (var i = 0; i < tracks.length; i++) {
|
$.each(tracks, function (i, t) {
|
||||||
var t = tracks[i];
|
|
||||||
var safeTitle = escapeHtml(t.title);
|
var safeTitle = escapeHtml(t.title);
|
||||||
var safeArtist = escapeHtml(t.artist.name);
|
var safeArtist = escapeHtml(t.artist.name);
|
||||||
var safeAlbum = escapeHtml(t.album.title);
|
var safeAlbum = escapeHtml(t.album.title);
|
||||||
|
|
@ -136,13 +154,13 @@
|
||||||
html += "<td>" + safeArtist + "</td>";
|
html += "<td>" + safeArtist + "</td>";
|
||||||
html += "<td>" + safeAlbum + "</td>";
|
html += "<td>" + safeAlbum + "</td>";
|
||||||
html += "</tr>";
|
html += "</tr>";
|
||||||
}
|
});
|
||||||
html += "</table>";
|
html += "</table>";
|
||||||
|
|
||||||
resultsDiv.innerHTML = html;
|
resultsDiv.html(html);
|
||||||
},
|
},
|
||||||
function (err) {
|
function (err) {
|
||||||
resultsDiv.innerHTML = "Error: " + err;
|
resultsDiv.html("Error: " + err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -153,8 +171,7 @@
|
||||||
html +=
|
html +=
|
||||||
'<tr bgcolor="#bbbbbb"><th>Play</th><th>Title</th><th>Artist</th><th>Album</th></tr>';
|
'<tr bgcolor="#bbbbbb"><th>Play</th><th>Title</th><th>Artist</th><th>Album</th></tr>';
|
||||||
|
|
||||||
for (var i = 0; i < tracks.length; i++) {
|
$.each(tracks, function (i, t) {
|
||||||
var t = tracks[i];
|
|
||||||
var safeTitle = escapeHtml(t.title);
|
var safeTitle = escapeHtml(t.title);
|
||||||
var safeArtist = escapeHtml(t.artist.name);
|
var safeArtist = escapeHtml(t.artist.name);
|
||||||
var safeAlbum = escapeHtml(t.album.title);
|
var safeAlbum = escapeHtml(t.album.title);
|
||||||
|
|
@ -168,99 +185,63 @@
|
||||||
html += "<td>" + safeArtist + "</td>";
|
html += "<td>" + safeArtist + "</td>";
|
||||||
html += "<td>" + safeAlbum + "</td>";
|
html += "<td>" + safeAlbum + "</td>";
|
||||||
html += "</tr>";
|
html += "</tr>";
|
||||||
}
|
});
|
||||||
html += "</table>";
|
html += "</table>";
|
||||||
|
|
||||||
setContent(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) {
|
|
||||||
var streamUrl = manifest.urls[0];
|
|
||||||
if (audioPlayer.play) {
|
|
||||||
audioPlayer.src = streamUrl;
|
|
||||||
audioPlayer.play();
|
|
||||||
} else {
|
|
||||||
// Fallback for IE/Older browsers
|
|
||||||
playWithEmbed(streamUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
function setContent(html) {
|
||||||
var content = document.getElementById("main-content");
|
$("#main-content").html(html);
|
||||||
content.innerHTML = html;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function apiRequest(endpoint, success, error) {
|
function apiRequest(endpoint, success, error) {
|
||||||
var xhr = createXHR();
|
|
||||||
// Use fallback HTTP if needed logic
|
|
||||||
var finalUrl = currentApiUrl;
|
var finalUrl = currentApiUrl;
|
||||||
if (isHttpFallback && finalUrl.indexOf("https://") === 0) {
|
if (isHttpFallback && finalUrl.indexOf("https://") === 0) {
|
||||||
finalUrl = "http://" + finalUrl.substring(8);
|
finalUrl = "http://" + finalUrl.substring(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
xhr.open("GET", finalUrl + endpoint, true);
|
$.ajax({
|
||||||
xhr.onreadystatechange = function () {
|
url: finalUrl + endpoint,
|
||||||
if (xhr.readyState === 4) {
|
method: "GET",
|
||||||
if (xhr.status >= 200 && xhr.status < 300) {
|
dataType: "json",
|
||||||
try {
|
success: function (data) {
|
||||||
var data = JSON.parse(xhr.responseText);
|
success(data);
|
||||||
success(data);
|
},
|
||||||
} catch (e) {
|
error: function (xhr, status, err) {
|
||||||
error("JSON Parse Error");
|
if (!isHttpFallback) {
|
||||||
}
|
isHttpFallback = true;
|
||||||
|
// Retry with fallback logic (recursive call will pick up isHttpFallback)
|
||||||
|
apiRequest(endpoint, success, error);
|
||||||
} else {
|
} else {
|
||||||
// If failed and not yet fallback, try fallback
|
error("Request Failed: " + status);
|
||||||
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 playWithEmbed(url) {
|
||||||
|
var container = $("#audio-container");
|
||||||
|
if (container.length === 0) {
|
||||||
|
if (audioPlayer && audioPlayer.parentNode) {
|
||||||
|
$(audioPlayer.parentNode).attr("id", "audio-container");
|
||||||
|
container = $("#audio-container");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container.length) {
|
||||||
|
var embedDiv = $("#embed-container");
|
||||||
|
if (embedDiv.length === 0) {
|
||||||
|
embedDiv = $('<div id="embed-container"></div>');
|
||||||
|
container.append(embedDiv);
|
||||||
|
}
|
||||||
|
// Use html() to set innerHTML properly
|
||||||
|
var embedHtml = '<embed type="application/x-mplayer2" src="' + url + '" autostart="true" width="0" height="0" enablejavascript="true"></embed>';
|
||||||
|
embedDiv.html(embedHtml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
function escapeHtml(text) {
|
function escapeHtml(text) {
|
||||||
if (!text) return "";
|
if (!text) return "";
|
||||||
return text
|
return text
|
||||||
|
|
@ -271,70 +252,24 @@
|
||||||
.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) {
|
function base64Decode(str) {
|
||||||
if (window.atob) {
|
if (window.atob) {
|
||||||
return window.atob(str);
|
return window.atob(str);
|
||||||
}
|
}
|
||||||
// Polyfill for IE
|
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||||
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
var output = "";
|
||||||
var output = "";
|
str = String(str).replace(/=+$/, '');
|
||||||
str = String(str).replace(/=+$/, '');
|
if (str.length % 4 == 1) {
|
||||||
if (str.length % 4 == 1) {
|
throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
|
||||||
throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
|
}
|
||||||
}
|
for (
|
||||||
for (
|
var bc = 0, bs = 0, buffer, i = 0;
|
||||||
var bc = 0, bs = 0, buffer, i = 0;
|
buffer = str.charAt(i++);
|
||||||
buffer = str.charAt(i++);
|
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
|
||||||
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
|
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
|
||||||
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
|
) {
|
||||||
) {
|
buffer = chars.indexOf(buffer);
|
||||||
buffer = chars.indexOf(buffer);
|
}
|
||||||
}
|
return output;
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
function playWithEmbed(url) {
|
|
||||||
var container = document.getElementById("audio-container");
|
|
||||||
if (!container) {
|
|
||||||
// If no container, try to find parent of audio player
|
|
||||||
if (audioPlayer && audioPlayer.parentNode) {
|
|
||||||
container = audioPlayer.parentNode;
|
|
||||||
// Give it an ID for future reference
|
|
||||||
container.id = "audio-container";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container) {
|
|
||||||
// Clear previous embeds
|
|
||||||
// Removing innerHTML is the brute force way but effective for legacy
|
|
||||||
// However, we want to keep the text status if it's in there (it's not, status is sibling in .controls)
|
|
||||||
// The audio player is in .controls.
|
|
||||||
|
|
||||||
// Let's create a specific div for embed if it doesn't exist, appended to container
|
|
||||||
var embedDiv = document.getElementById("embed-container");
|
|
||||||
if (!embedDiv) {
|
|
||||||
embedDiv = document.createElement("div");
|
|
||||||
embedDiv.id = "embed-container";
|
|
||||||
container.appendChild(embedDiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
var html = '<embed type="application/x-mplayer2" src="' + url + '" autostart="true" width="0" height="0" enablejavascript="true"></embed>';
|
|
||||||
embedDiv.innerHTML = html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue