feat(wave-v2): add LLM reranking, mood modes, auto-rotation scheduler and Navidrome auth

This commit is contained in:
Antigravity committed 2026-10-12 15:29:43 +03:00
1 parent 3b3d591bfd
commit fb1a5e01dd
11 files changed
+736 -37

No files matched your search

+31 -3
View File
@@ -307,17 +307,31 @@ document.addEventListener("DOMContentLoaded", () => {
const waveLoadingText = document.getElementById("waveLoadingText");
const waveGenerateBtn = document.getElementById("waveGenerateBtn");
const waveRotateBtn = document.getElementById("waveRotateBtn");
const waveMoodSelect = document.getElementById("waveMoodSelect");
const waveContextLabel = document.getElementById("waveContextLabel");
fetchWaveStatus();
if (waveGenerateBtn) {
waveGenerateBtn.addEventListener("click", async () => {
waveGenerateBtn.disabled = true;
if (waveLoadingText) waveLoadingText.style.display = "inline-block";
const mood = waveMoodSelect ? waveMoodSelect.value : "auto";
if (waveLoadingText) {
waveLoadingText.innerHTML = `<span class="spinner-sm"></span> Qwen 2.5 отбирает треки под вайб...`;
waveLoadingText.style.display = "inline-block";
}
try {
const res = await fetch("api/wave/generate", { method: "POST" });
const res = await fetch("api/wave/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mood: mood })
});
if (res.status === 401) {
window.location.href = "login";
return;
}
const data = await res.json();
alert(`Волна обновлена! Добавлено: ${data.added_count || 0} треков.`);
alert(`🌊 Волна обновлена под настроение [${data.mood_label || mood}]!\nДобавлено новых треков: ${data.added_count || 0}.`);
await fetchWaveStatus();
} catch (err) {
alert("Ошибка генерации волны: " + err);
@@ -333,6 +347,10 @@ document.addEventListener("DOMContentLoaded", () => {
waveRotateBtn.disabled = true;
try {
const res = await fetch("api/wave/rotate", { method: "POST" });
if (res.status === 401) {
window.location.href = "login";
return;
}
const data = await res.json();
const promMsg = (data.promoted_count > 0) ? `⭐ Перенесено в библиотеку: ${data.promoted_count}` : "Нет лайкнутых для переноса";
const purMsg = (data.purged_count > 0) ? `🗑️ Удалено устаревших: ${data.purged_count}` : "Нет устаревших";
@@ -349,6 +367,10 @@ document.addEventListener("DOMContentLoaded", () => {
async function fetchWaveStatus() {
try {
const res = await fetch("api/wave/status");
if (res.status === 401) {
window.location.href = "login";
return;
}
const data = await res.json();
renderWaveStatus(data);
} catch (err) {
@@ -360,6 +382,12 @@ document.addEventListener("DOMContentLoaded", () => {
if (waveCountLabel) waveCountLabel.textContent = `${data.count || 0} / ${data.capacity || 25} треков`;
if (waveSizeLabel) waveSizeLabel.textContent = `${data.total_size_mb || 0} МБ`;
// Context label
if (waveContextLabel && data.current_context) {
waveContextLabel.textContent = data.current_context.label || "🕒 Авто";
waveContextLabel.title = data.current_context.desc || "";
}
// Anchors
if (waveAnchorsContainer && waveAnchorsList) {
const anchors = data.anchor_artists || [];
+80
View File
@@ -731,3 +731,83 @@ input[type="text"]:focus {
vertical-align: middle;
margin-right: 0.3rem;
}
/* Auth & User Profile */
.badge-user {
background-color: rgba(99, 102, 241, 0.2);
color: #a5b4fc;
border: 1px solid rgba(99, 102, 241, 0.4);
}
.btn-logout {
background-color: rgba(239, 68, 68, 0.15);
color: #fca5a5;
border: 1px solid rgba(239, 68, 68, 0.3);
padding: 0.25rem 0.6rem;
border-radius: 6px;
text-decoration: none;
font-size: 0.78rem;
font-weight: 500;
transition: all 0.2s;
}
.btn-logout:hover {
background-color: rgba(239, 68, 68, 0.3);
color: #ffffff;
}
/* Phase 2: LLM & Mood */
.badge-llm {
background-color: rgba(168, 85, 247, 0.2);
color: #c084fc;
border: 1px solid rgba(168, 85, 247, 0.4);
}
.wave-mood-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin: 0.8rem 0;
flex-wrap: wrap;
background-color: rgba(15, 23, 42, 0.5);
padding: 0.6rem 0.8rem;
border-radius: 8px;
border: 1px solid var(--border-color);
}
.wave-mood-group {
display: flex;
align-items: center;
gap: 0.6rem;
}
.wave-mood-label {
font-size: 0.82rem;
font-weight: 500;
color: var(--text-secondary);
}
.wave-mood-select {
background-color: #0f172a;
color: var(--text-primary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 0.35rem 0.7rem;
font-size: 0.85rem;
outline: none;
cursor: pointer;
transition: border-color 0.2s;
}
.wave-mood-select:focus {
border-color: var(--accent-color);
}
.badge-context {
background-color: rgba(14, 165, 233, 0.15);
color: #38bdf8;
border: 1px solid rgba(14, 165, 233, 0.35);
font-size: 0.8rem;
}