Add runtime filter, kid-friendly toggle, surprise me, and re-roll

- Runtime quick-select buttons (Any/90m/2h/2.5h) filter movies by length
- Kid-friendly toggle forces PG-13 max and boosts Family/Animation genres
- Surprise Me picks a random mood prompt from 20 curated options
- Show Me More re-rolls same mood excluding already-shown movies
- Re-roll appends new results to the existing search history entry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 20:07:05 -07:00
parent d8c8b473ad
commit 9f96a91986
6 changed files with 194 additions and 18 deletions
+91 -3
View File
@@ -3,6 +3,34 @@
const API = '';
let currentUser = null;
let selectedUserIds = [];
let maxRuntime = null;
let kidFriendly = false;
let shownMovieIds = [];
let lastMood = '';
let currentHistoryId = null;
const SURPRISE_PROMPTS = [
"pizza night with the kids",
"something scary but not too gory",
"light fun movie after a hard week",
"80s action classic",
"mind-bending sci-fi",
"feel-good comedy",
"epic adventure",
"rainy Sunday afternoon",
"date night romance",
"underrated hidden gem",
"visually stunning cinematography",
"based on a true story",
"twisty thriller with a great ending",
"nostalgic 90s vibes",
"animated movie for all ages",
"something weird and quirky",
"inspiring sports movie",
"cozy mystery",
"space exploration",
"laugh out loud comedy",
];
// --- Auth ---
@@ -90,10 +118,18 @@ function toggleUser(userId, pill) {
// --- Mood / Recommendations ---
async function findMovies() {
async function findMovies(excludeIds = []) {
const mood = document.getElementById('mood-input').value.trim();
if (!mood) return;
// Track mood for re-roll; reset shown IDs if mood changed
const isReroll = excludeIds.length > 0;
if (mood !== lastMood) {
shownMovieIds = [];
currentHistoryId = null;
lastMood = mood;
}
document.getElementById('empty-state').classList.add('hidden');
document.getElementById('results').classList.add('hidden');
document.getElementById('error-state').classList.add('hidden');
@@ -101,10 +137,19 @@ async function findMovies() {
try {
const additionalIds = selectedUserIds.filter(id => id !== currentUser.id);
const payload = {
mood,
additional_user_ids: additionalIds,
exclude_ids: excludeIds,
kid_friendly: kidFriendly,
};
if (maxRuntime) payload.max_runtime = maxRuntime;
if (isReroll && currentHistoryId) payload.history_id = currentHistoryId;
const res = await fetch(`${API}/api/mood`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mood, additional_user_ids: additionalIds })
body: JSON.stringify(payload)
});
if (!res.ok) {
@@ -113,6 +158,17 @@ async function findMovies() {
}
const data = await res.json();
// Track shown movie IDs and history ID for re-roll
if (data.recommendations) {
data.recommendations.forEach(r => {
if (!shownMovieIds.includes(r.jellyfin_id)) {
shownMovieIds.push(r.jellyfin_id);
}
});
}
if (data.meta && data.meta.history_id) {
currentHistoryId = data.meta.history_id;
}
renderResults(data);
loadHistory();
} catch (err) {
@@ -293,7 +349,7 @@ document.getElementById('login-form').addEventListener('submit', async (e) => {
}
});
document.getElementById('find-btn').addEventListener('click', findMovies);
document.getElementById('find-btn').addEventListener('click', () => findMovies());
document.getElementById('mood-input').addEventListener('keydown', (e) => {
if (e.key === 'Enter') findMovies();
});
@@ -305,5 +361,37 @@ document.getElementById('show-history-btn').addEventListener('click', () => {
loadHistory();
});
// Runtime filter buttons
document.querySelectorAll('.runtime-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.runtime-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
maxRuntime = btn.dataset.runtime ? parseInt(btn.dataset.runtime) : null;
});
});
// Kid-friendly toggle
document.getElementById('kid-friendly-btn').addEventListener('click', () => {
kidFriendly = !kidFriendly;
const btn = document.getElementById('kid-friendly-btn');
if (kidFriendly) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
// Surprise me
document.getElementById('surprise-btn').addEventListener('click', () => {
const prompt = SURPRISE_PROMPTS[Math.floor(Math.random() * SURPRISE_PROMPTS.length)];
document.getElementById('mood-input').value = prompt;
findMovies();
});
// Re-roll
document.getElementById('reroll-btn').addEventListener('click', () => {
findMovies(shownMovieIds);
});
// --- Init ---
checkAuth();