Add remove rating feature
All checks were successful
Docker / docker (push) Successful in 1m51s

Bouton ✕ sur les films notés 10/10 pour supprimer la note via DELETE /api/rate/:id.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dev
2026-03-04 13:54:28 +00:00
parent 2aa2634e6d
commit 9c70834bcb
4 changed files with 64 additions and 2 deletions

12
main.py
View File

@@ -187,4 +187,16 @@ async def rate_movie(request: Request, trakt_id: int, body: RateBody):
return {"success": True} return {"success": True}
@app.delete("/api/rate/{trakt_id}")
async def remove_rating(request: Request, trakt_id: int):
token = request.session.get("access_token")
if not token:
raise HTTPException(401, "Not authenticated")
trakt = TraktClient(TRAKT_CLIENT_ID, token)
await trakt.remove_rating(trakt_id)
cache_del(f"movies_{token[:16]}")
return {"success": True}
app.mount("/static", StaticFiles(directory="static"), name="static") app.mount("/static", StaticFiles(directory="static"), name="static")

View File

@@ -160,9 +160,11 @@ function buildRow(movie) {
).join(''); ).join('');
const skipLabel = isSkipped ? 'Remettre' : 'Passer'; const skipLabel = isSkipped ? 'Remettre' : 'Passer';
const skipBtn = `<button class="skip-btn" title="${skipLabel}">${skipLabel}</button>`; const skipBtn = `<button class="skip-btn">${skipLabel}</button>`;
const removeBtn = movie.current_rating !== null
? `<button class="remove-btn" title="Supprimer la note">✕</button>` : '';
const ratingEl = `<div class="movie-rating">${btns}${skipBtn}</div>`; const ratingEl = `<div class="movie-rating">${btns}${removeBtn}${skipBtn}</div>`;
row.innerHTML = poster + info + synopsis + ratingEl; row.innerHTML = poster + info + synopsis + ratingEl;
@@ -196,6 +198,12 @@ function buildRow(movie) {
}); });
}); });
// Remove rating button
const removeBtnEl = container.querySelector('.remove-btn');
if (removeBtnEl) {
removeBtnEl.addEventListener('click', () => removeRating(movie, row));
}
// Skip button // Skip button
container.querySelector('.skip-btn').addEventListener('click', () => { container.querySelector('.skip-btn').addEventListener('click', () => {
if (isSkipped) { if (isSkipped) {
@@ -211,6 +219,20 @@ function buildRow(movie) {
return row; return row;
} }
/* ── Remove rating ──────────────────────────────────────── */
async function removeRating(movie, row) {
row.style.pointerEvents = 'none';
try {
const r = await fetch(`/api/rate/${movie.trakt_id}`, { method: 'DELETE' });
if (!r.ok) throw new Error('Failed');
showToast(`${movie.title_fr} — note supprimée`);
animateOut(row);
} catch {
row.style.pointerEvents = '';
showToast('Erreur lors de la suppression', true);
}
}
/* ── Rate ───────────────────────────────────────────────── */ /* ── Rate ───────────────────────────────────────────────── */
async function rateMovie(movie, rating, row) { async function rateMovie(movie, rating, row) {
row.style.pointerEvents = 'none'; row.style.pointerEvents = 'none';

View File

@@ -316,6 +316,24 @@ main {
} }
.r-btn:active { transform: scale(.92); } .r-btn:active { transform: scale(.92); }
.remove-btn {
margin-left: 6px;
width: 24px;
height: 24px;
border-radius: 5px;
border: 1px solid rgba(244,63,94,.25);
background: transparent;
color: #f87191;
font-size: .75rem;
cursor: pointer;
font-family: inherit;
transition: border-color .15s, background .15s;
}
.remove-btn:hover {
border-color: rgba(244,63,94,.5);
background: rgba(244,63,94,.1);
}
.skip-btn { .skip-btn {
margin-left: 6px; margin-left: 6px;
padding: 0 9px; padding: 0 9px;

View File

@@ -23,6 +23,16 @@ class TraktClient:
ratings_r.raise_for_status() ratings_r.raise_for_status()
return watched_r.json(), ratings_r.json() return watched_r.json(), ratings_r.json()
async def remove_rating(self, trakt_id: int):
async with httpx.AsyncClient(timeout=30) as client:
r = await client.post(
f"{self.BASE_URL}/sync/ratings/remove",
headers=self.headers,
json={"movies": [{"ids": {"trakt": trakt_id}}]},
)
r.raise_for_status()
return r.json()
async def rate_movie(self, trakt_id: int, rating: int): async def rate_movie(self, trakt_id: int, rating: int):
async with httpx.AsyncClient(timeout=30) as client: async with httpx.AsyncClient(timeout=30) as client:
r = await client.post( r = await client.post(