diff --git a/public/css/app.css b/public/css/app.css
index 8bb9df4..d94328e 100644
--- a/public/css/app.css
+++ b/public/css/app.css
@@ -220,6 +220,10 @@ footer {
z-index: 2;
}
+ .cam-badge {
+ z-index: 2;
+ }
+
/* Stats → "Films regardés" modal */
.stats-movies-grid {
display: grid;
diff --git a/public/js/app_control.js b/public/js/app_control.js
index 5038f72..4b61ff8 100644
--- a/public/js/app_control.js
+++ b/public/js/app_control.js
@@ -118,6 +118,7 @@ window.onload = async function () {
const editRatingEl = document.getElementById('edit_rating');
const editPosterEl = document.getElementById('edit_poster');
const editDescriptionEl = document.getElementById('edit_description');
+ const editCamFlagEl = document.getElementById('edit_cam_flag');
let moviesById = new Map();
let activeYear = null;
@@ -1921,6 +1922,7 @@ window.onload = async function () {
const year = parseYear(yearInput.value);
const imdb_id = document.getElementById('imdb_id').value.trim();
const category = document.getElementById('category').value.trim();
+ const cam = !!document.getElementById('cam_flag')?.checked;
const vod_link = document.getElementById('vod_link').value.trim();
const player_mode = (document.getElementById('player_mode')?.value || 'auto').trim();
const video_src = document.getElementById('video_src')?.value?.trim() || '';
@@ -1944,7 +1946,7 @@ window.onload = async function () {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
- body: JSON.stringify({ year, imdb_id, category, vod_link, player_mode, video_src, video_file, embed_src })
+ body: JSON.stringify({ year, imdb_id, category, vod_link, player_mode, video_src, video_file, embed_src, cam })
});
if (res.ok) {
@@ -2199,6 +2201,7 @@ window.onload = async function () {
editRatingEl.value = movie.rating || '';
editPosterEl.value = movie.poster || '';
editDescriptionEl.value = movie.description || '';
+ if (editCamFlagEl) editCamFlagEl.checked = !!movie.cam;
if (editMovieModalTitleEl) {
const label = movie.title ? `Modifier: ${movie.title}` : 'Modifier le film';
@@ -2221,6 +2224,7 @@ window.onload = async function () {
const video_file = (editVideoFileEl?.value || '').trim();
const embed_src = (editEmbedSrcEl?.value || '').trim();
const refreshOmdb = !!editRefreshOmdbEl.checked;
+ const cam = !!editCamFlagEl?.checked;
const title = editTitleEl.value;
const rating = editRatingEl.value;
@@ -2255,7 +2259,8 @@ window.onload = async function () {
video_src,
video_file,
embed_src,
- refreshOmdb
+ refreshOmdb,
+ cam
};
// Only send manual fields if we're not refreshing from OMDb
diff --git a/public/js/app_movies.js b/public/js/app_movies.js
index ddd1c43..25dc4c6 100644
--- a/public/js/app_movies.js
+++ b/public/js/app_movies.js
@@ -448,6 +448,14 @@ window.addEventListener('DOMContentLoaded', async function () {
}
}
+ const camBadge = movie?.cam ? '
+ `;
+
const movieDiv = document.createElement('div');
movieDiv.classList.add('col-12', 'col-sm-6', 'col-lg-4', 'movie-card');
movieDiv.setAttribute('data-imdb-id', movie.imdb_id);
@@ -457,13 +465,9 @@ window.addEventListener('DOMContentLoaded', async function () {
${
isClickable
? `
diff --git a/routes/movies.js b/routes/movies.js
index 0c85045..43094e8 100644
--- a/routes/movies.js
+++ b/routes/movies.js
@@ -148,7 +148,7 @@ router.get("/", async (req, res) => {
const projection = isChecklist
? "imdb_id title"
- : "imdb_id title description rating poster year category vod_link player_mode video_src embed_src video_file updatedAt createdAt";
+ : "imdb_id title description rating poster cam year category vod_link player_mode video_src embed_src video_file updatedAt createdAt";
const cacheKey = `movies:list:${year || "all"}:${isChecklist ? "checklist" : "films"}`;
const cached = cacheGet(cacheKey);
@@ -333,7 +333,7 @@ router.patch("/users/updateWatchedMovies", async (req, res) => {
// Add movie
router.post("/add", async (req, res) => {
- const { imdb_id, category, vod_link, year, player_mode, video_src, embed_src, video_file } = req.body;
+ const { imdb_id, category, vod_link, year, player_mode, video_src, embed_src, video_file, cam } = req.body;
try {
// Get the token from the Authorization header
@@ -390,6 +390,7 @@ router.post("/add", async (req, res) => {
description: movieDetails.description,
rating: movieDetails.rating,
poster: movieDetails.poster,
+ cam: !!cam,
year: parsedYear,
category,
vod_link: normalizedVod || undefined,
@@ -491,6 +492,7 @@ router.put("/:id", async (req, res) => {
embed_src,
video_file,
refreshOmdb,
+ cam,
} = req.body || {};
if (imdb_id !== undefined && imdb_id !== null && !isValidImdbId(imdb_id)) {
@@ -557,6 +559,8 @@ router.put("/:id", async (req, res) => {
}
// If refreshOmdb is true, overwrite OMDb-derived fields from the imdb_id
+ if (cam !== undefined) movie.cam = !!cam;
+
if (refreshOmdb) {
try {
const details = await fetchMovieDetailsFromOmdb(movie.imdb_id);