update
This commit is contained in:
200
app/templates/index.html
Normal file
200
app/templates/index.html
Normal file
@@ -0,0 +1,200 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tidal DL Web</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Tidal DL Web</h1>
|
||||
|
||||
<div class="card">
|
||||
<h2>Search</h2>
|
||||
<input type="text" id="query" placeholder="Search for tracks, albums..." onkeypress="handleEnter(event)">
|
||||
<select id="type"
|
||||
style="padding: 10px; background: #2d2d2d; color: white; border: 1px solid #333; border-radius: 4px;">
|
||||
<option value="track">Track</option>
|
||||
<option value="album">Album</option>
|
||||
<option value="artist">Artist</option>
|
||||
<option value="playlist">Playlist</option>
|
||||
</select>
|
||||
<button onclick="search()">Search</button>
|
||||
</div>
|
||||
|
||||
<div id="results" class="card" style="display:none;">
|
||||
<h2>Results</h2>
|
||||
<div id="results-list"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Download Queue</h2>
|
||||
<div id="queue-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="card" style="margin-top: 20px; text-align: center; color: #888;">
|
||||
<small>System IP: <span id="system-ip">Loading...</span></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function checkIp() {
|
||||
try {
|
||||
const response = await fetch('/system/ip');
|
||||
const data = await response.json();
|
||||
document.getElementById('system-ip').textContent = data.ip;
|
||||
} catch (e) {
|
||||
document.getElementById('system-ip').textContent = 'Error';
|
||||
}
|
||||
}
|
||||
checkIp();
|
||||
|
||||
function handleEnter(e) {
|
||||
if (e.key === 'Enter') search();
|
||||
}
|
||||
|
||||
async function search() {
|
||||
const query = document.getElementById('query').value;
|
||||
const type = document.getElementById('type').value;
|
||||
if (!query) return;
|
||||
|
||||
const response = await fetch(`/search/?query=${encodeURIComponent(query)}&type=${type}`);
|
||||
const results = await response.json();
|
||||
|
||||
const list = document.getElementById('results-list');
|
||||
list.innerHTML = '';
|
||||
document.getElementById('results').style.display = 'block';
|
||||
|
||||
if (results.length === 0) {
|
||||
list.innerHTML = '<p>No results found.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Grouping Logic
|
||||
let grouped = {};
|
||||
let groupKey = '';
|
||||
|
||||
if (type === 'track') {
|
||||
groupKey = 'album'; // Group tracks by Album
|
||||
} else if (type === 'album') {
|
||||
groupKey = 'artist'; // Group albums by Artist
|
||||
} else {
|
||||
// No grouping for artist/playlist
|
||||
renderFlatList(results, list);
|
||||
return;
|
||||
}
|
||||
|
||||
results.forEach(item => {
|
||||
const key = item[groupKey] || 'Unknown';
|
||||
if (!grouped[key]) grouped[key] = [];
|
||||
grouped[key].push(item);
|
||||
});
|
||||
|
||||
for (const [groupName, items] of Object.entries(grouped)) {
|
||||
const details = document.createElement('details');
|
||||
details.open = true; // Open by default
|
||||
|
||||
const summary = document.createElement('summary');
|
||||
summary.textContent = `${groupName} (${items.length})`;
|
||||
details.appendChild(summary);
|
||||
|
||||
const groupList = document.createElement('div');
|
||||
groupList.className = 'group-list';
|
||||
|
||||
items.forEach(item => {
|
||||
const div = createResultItem(item);
|
||||
groupList.appendChild(div);
|
||||
});
|
||||
|
||||
details.appendChild(groupList);
|
||||
list.appendChild(details);
|
||||
}
|
||||
}
|
||||
|
||||
function renderFlatList(items, container) {
|
||||
items.forEach(item => {
|
||||
const div = createResultItem(item);
|
||||
container.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
function createResultItem(item) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'result-item';
|
||||
div.innerHTML = `
|
||||
<img src="${item.cover || '/static/placeholder.png'}" alt="Cover">
|
||||
<div class="result-info">
|
||||
<strong>${item.title}</strong><br>
|
||||
${item.artist ? item.artist + ' - ' : ''}${item.album || ''}
|
||||
</div>
|
||||
<button onclick="download('${item.type}', '${item.id}')">Download</button>
|
||||
`;
|
||||
return div;
|
||||
}
|
||||
|
||||
async function download(type, id) {
|
||||
await fetch('/download/', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ type, id })
|
||||
});
|
||||
updateQueue();
|
||||
}
|
||||
|
||||
async function updateQueue() {
|
||||
const response = await fetch('/download/queue');
|
||||
const queue = await response.json();
|
||||
|
||||
const list = document.getElementById('queue-list');
|
||||
list.innerHTML = '';
|
||||
|
||||
queue.forEach(item => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'queue-item';
|
||||
let controls = '';
|
||||
if (item.status === 'downloading' || item.status === 'queued') {
|
||||
if (item.control === 'pause') {
|
||||
controls += `<button onclick="controlTask('${item.id}', 'resume')">Resume</button>`;
|
||||
} else {
|
||||
controls += `<button onclick="controlTask('${item.id}', 'pause')">Pause</button>`;
|
||||
}
|
||||
controls += `<button onclick="controlTask('${item.id}', 'cancel')" style="background-color: #ff4444;">Cancel</button>`;
|
||||
}
|
||||
|
||||
div.innerHTML = `
|
||||
<div>
|
||||
<strong>${item.name}</strong><br>
|
||||
${item.type}
|
||||
${item.current_item ? '<br><small>' + item.current_item + '</small>' : ''}
|
||||
${item.total_items ? '<br><small>Track ' + item.current_index + ' of ' + item.total_items + '</small>' : ''}
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<div class="status-${item.status}">
|
||||
${item.status} ${item.progress ? Math.round(item.progress) + '%' : ''}
|
||||
</div>
|
||||
<div class="controls">
|
||||
${controls}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
list.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
async function controlTask(id, action) {
|
||||
await fetch(`/download/${action}/${id}`, { method: 'POST' });
|
||||
updateQueue();
|
||||
}
|
||||
|
||||
// Poll queue
|
||||
setInterval(updateQueue, 2000);
|
||||
updateQueue();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user