Merge pull request #72 from quoije/2026
Promote 2026 updates to prod
This commit was merged in pull request #72.
This commit is contained in:
@@ -6,6 +6,7 @@ const movieSchema = new mongoose.Schema({
|
|||||||
description: { type: String, required: false },
|
description: { type: String, required: false },
|
||||||
rating: { type: String, required: false },
|
rating: { type: String, required: false },
|
||||||
poster: { type: String, required: false },
|
poster: { type: String, required: false },
|
||||||
|
cam: { type: Boolean, required: false, default: false },
|
||||||
// Oscar pool year / edition (used to segment movies + stats)
|
// Oscar pool year / edition (used to segment movies + stats)
|
||||||
year: { type: Number, required: false, index: true },
|
year: { type: Number, required: false, index: true },
|
||||||
category: { type: String, required: true },
|
category: { type: String, required: true },
|
||||||
|
|||||||
@@ -653,6 +653,12 @@
|
|||||||
<label for="category" class="form-label">Catégorie</label>
|
<label for="category" class="form-label">Catégorie</label>
|
||||||
<input type="text" class="form-control" id="category" placeholder="Best Picture, etc." required>
|
<input type="text" class="form-control" id="category" placeholder="Best Picture, etc." required>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 col-md-4 d-flex align-items-end">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="cam_flag">
|
||||||
|
<label class="form-check-label" for="cam_flag">Tag CAM</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="accordion" id="adminAddMovieAccordion">
|
<div class="accordion" id="adminAddMovieAccordion">
|
||||||
@@ -1314,6 +1320,12 @@
|
|||||||
<label for="edit_rating" class="form-label">Rating (imdbRating)</label>
|
<label for="edit_rating" class="form-label">Rating (imdbRating)</label>
|
||||||
<input type="text" class="form-control" id="edit_rating" placeholder="7.9">
|
<input type="text" class="form-control" id="edit_rating" placeholder="7.9">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-6 d-flex align-items-end">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="edit_cam_flag">
|
||||||
|
<label class="form-check-label" for="edit_cam_flag">Tag CAM</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
|
|||||||
@@ -220,6 +220,10 @@ footer {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cam-badge {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
/* Stats → "Films regardés" modal */
|
/* Stats → "Films regardés" modal */
|
||||||
.stats-movies-grid {
|
.stats-movies-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ window.onload = async function () {
|
|||||||
const editRatingEl = document.getElementById('edit_rating');
|
const editRatingEl = document.getElementById('edit_rating');
|
||||||
const editPosterEl = document.getElementById('edit_poster');
|
const editPosterEl = document.getElementById('edit_poster');
|
||||||
const editDescriptionEl = document.getElementById('edit_description');
|
const editDescriptionEl = document.getElementById('edit_description');
|
||||||
|
const editCamFlagEl = document.getElementById('edit_cam_flag');
|
||||||
|
|
||||||
let moviesById = new Map();
|
let moviesById = new Map();
|
||||||
let activeYear = null;
|
let activeYear = null;
|
||||||
@@ -1921,6 +1922,7 @@ window.onload = async function () {
|
|||||||
const year = parseYear(yearInput.value);
|
const year = parseYear(yearInput.value);
|
||||||
const imdb_id = document.getElementById('imdb_id').value.trim();
|
const imdb_id = document.getElementById('imdb_id').value.trim();
|
||||||
const category = document.getElementById('category').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 vod_link = document.getElementById('vod_link').value.trim();
|
||||||
const player_mode = (document.getElementById('player_mode')?.value || 'auto').trim();
|
const player_mode = (document.getElementById('player_mode')?.value || 'auto').trim();
|
||||||
const video_src = document.getElementById('video_src')?.value?.trim() || '';
|
const video_src = document.getElementById('video_src')?.value?.trim() || '';
|
||||||
@@ -1944,7 +1946,7 @@ window.onload = async function () {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${token}`
|
'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) {
|
if (res.ok) {
|
||||||
@@ -2199,6 +2201,7 @@ window.onload = async function () {
|
|||||||
editRatingEl.value = movie.rating || '';
|
editRatingEl.value = movie.rating || '';
|
||||||
editPosterEl.value = movie.poster || '';
|
editPosterEl.value = movie.poster || '';
|
||||||
editDescriptionEl.value = movie.description || '';
|
editDescriptionEl.value = movie.description || '';
|
||||||
|
if (editCamFlagEl) editCamFlagEl.checked = !!movie.cam;
|
||||||
|
|
||||||
if (editMovieModalTitleEl) {
|
if (editMovieModalTitleEl) {
|
||||||
const label = movie.title ? `Modifier: ${movie.title}` : 'Modifier le film';
|
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 video_file = (editVideoFileEl?.value || '').trim();
|
||||||
const embed_src = (editEmbedSrcEl?.value || '').trim();
|
const embed_src = (editEmbedSrcEl?.value || '').trim();
|
||||||
const refreshOmdb = !!editRefreshOmdbEl.checked;
|
const refreshOmdb = !!editRefreshOmdbEl.checked;
|
||||||
|
const cam = !!editCamFlagEl?.checked;
|
||||||
|
|
||||||
const title = editTitleEl.value;
|
const title = editTitleEl.value;
|
||||||
const rating = editRatingEl.value;
|
const rating = editRatingEl.value;
|
||||||
@@ -2255,7 +2259,8 @@ window.onload = async function () {
|
|||||||
video_src,
|
video_src,
|
||||||
video_file,
|
video_file,
|
||||||
embed_src,
|
embed_src,
|
||||||
refreshOmdb
|
refreshOmdb,
|
||||||
|
cam
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only send manual fields if we're not refreshing from OMDb
|
// Only send manual fields if we're not refreshing from OMDb
|
||||||
|
|||||||
+10
-6
@@ -448,6 +448,14 @@ window.addEventListener('DOMContentLoaded', async function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const camBadge = movie?.cam ? '<span class="badge bg-warning text-dark position-absolute top-0 end-0 m-2 cam-badge">CAM</span>' : '';
|
||||||
|
const posterBlock = `
|
||||||
|
<div class="text-center pt-3 px-3 position-relative movie-poster-wrap">
|
||||||
|
${camBadge}
|
||||||
|
<img src="${movie.poster}" class="img-fluid rounded movie-poster" alt="${movie.title}">
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
const movieDiv = document.createElement('div');
|
const movieDiv = document.createElement('div');
|
||||||
movieDiv.classList.add('col-12', 'col-sm-6', 'col-lg-4', 'movie-card');
|
movieDiv.classList.add('col-12', 'col-sm-6', 'col-lg-4', 'movie-card');
|
||||||
movieDiv.setAttribute('data-imdb-id', movie.imdb_id);
|
movieDiv.setAttribute('data-imdb-id', movie.imdb_id);
|
||||||
@@ -457,13 +465,9 @@ window.addEventListener('DOMContentLoaded', async function () {
|
|||||||
${
|
${
|
||||||
isClickable
|
isClickable
|
||||||
? `<a href="${playerUrl}" target="_self" rel="noopener noreferrer" class="text-decoration-none">
|
? `<a href="${playerUrl}" target="_self" rel="noopener noreferrer" class="text-decoration-none">
|
||||||
<div class="text-center pt-3 px-3">
|
${posterBlock}
|
||||||
<img src="${movie.poster}" class="img-fluid rounded movie-poster" alt="${movie.title}">
|
|
||||||
</div>
|
|
||||||
</a>`
|
</a>`
|
||||||
: `<div class="text-center pt-3 px-3">
|
: `${posterBlock}`
|
||||||
<img src="${movie.poster}" class="img-fluid rounded movie-poster" alt="${movie.title}">
|
|
||||||
</div>`
|
|
||||||
}
|
}
|
||||||
<div class="card-body d-flex flex-column">
|
<div class="card-body d-flex flex-column">
|
||||||
<div class="d-flex justify-content-between align-items-start gap-2">
|
<div class="d-flex justify-content-between align-items-start gap-2">
|
||||||
|
|||||||
+6
-2
@@ -148,7 +148,7 @@ router.get("/", async (req, res) => {
|
|||||||
|
|
||||||
const projection = isChecklist
|
const projection = isChecklist
|
||||||
? "imdb_id title"
|
? "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 cacheKey = `movies:list:${year || "all"}:${isChecklist ? "checklist" : "films"}`;
|
||||||
const cached = cacheGet(cacheKey);
|
const cached = cacheGet(cacheKey);
|
||||||
@@ -333,7 +333,7 @@ router.patch("/users/updateWatchedMovies", async (req, res) => {
|
|||||||
|
|
||||||
// Add movie
|
// Add movie
|
||||||
router.post("/add", async (req, res) => {
|
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 {
|
try {
|
||||||
// Get the token from the Authorization header
|
// Get the token from the Authorization header
|
||||||
@@ -390,6 +390,7 @@ router.post("/add", async (req, res) => {
|
|||||||
description: movieDetails.description,
|
description: movieDetails.description,
|
||||||
rating: movieDetails.rating,
|
rating: movieDetails.rating,
|
||||||
poster: movieDetails.poster,
|
poster: movieDetails.poster,
|
||||||
|
cam: !!cam,
|
||||||
year: parsedYear,
|
year: parsedYear,
|
||||||
category,
|
category,
|
||||||
vod_link: normalizedVod || undefined,
|
vod_link: normalizedVod || undefined,
|
||||||
@@ -491,6 +492,7 @@ router.put("/:id", async (req, res) => {
|
|||||||
embed_src,
|
embed_src,
|
||||||
video_file,
|
video_file,
|
||||||
refreshOmdb,
|
refreshOmdb,
|
||||||
|
cam,
|
||||||
} = req.body || {};
|
} = req.body || {};
|
||||||
|
|
||||||
if (imdb_id !== undefined && imdb_id !== null && !isValidImdbId(imdb_id)) {
|
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 refreshOmdb is true, overwrite OMDb-derived fields from the imdb_id
|
||||||
|
if (cam !== undefined) movie.cam = !!cam;
|
||||||
|
|
||||||
if (refreshOmdb) {
|
if (refreshOmdb) {
|
||||||
try {
|
try {
|
||||||
const details = await fetchMovieDetailsFromOmdb(movie.imdb_id);
|
const details = await fetchMovieDetailsFromOmdb(movie.imdb_id);
|
||||||
|
|||||||
Reference in New Issue
Block a user