init poc
This commit is contained in:
commit
820cc6f209
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
npm init -y && npm install express cheerio axios cors
|
||||||
|
node server.js
|
||||||
|
Ouvre frontend.html dans ton navigateur.
|
||||||
|
Tape une recherche, genre “minecraft”.
|
||||||
|
Laisse tourner.
|
||||||
|
Si t’as une erreur, lis le message. Pleure, ou corrige.
|
47
aggregateur-sample.html
Normal file
47
aggregateur-sample.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Agrégateur Parents</title>
|
||||||
|
<style>
|
||||||
|
.film { border:1px solid #ccc; margin:10px; padding:10px; }
|
||||||
|
.film img { max-height: 180px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="films"></div>
|
||||||
|
<script>
|
||||||
|
// Appelle ton backend qui utilise le module JS au-dessus
|
||||||
|
// Ici, on simule avec des données
|
||||||
|
const films = [
|
||||||
|
{
|
||||||
|
title: "A Minecraft Movie",
|
||||||
|
year: "2025",
|
||||||
|
imgSrc: "https://www.cinecheck.be/media/n1hojmmh/a-minecraft-movie-ov-_ps_1_jpg_sd-high_2025-warner-bros-entertainment-inc-all-rights-reserved-photo-credit-courtesy-warner-bros-pictures.jpg",
|
||||||
|
link: "https://www.cinecheck.be/films/a-minecraft-movie/",
|
||||||
|
marks: ["9 ans", "Peur"],
|
||||||
|
summary: "A Minecraft Movie, van Warner Bros. Pictures en Legendary Pictures, ...",
|
||||||
|
details: [
|
||||||
|
{ type: "angst", description: "La film contient des scènes effrayantes..." }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const container = document.getElementById('films');
|
||||||
|
films.forEach(f => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'film';
|
||||||
|
div.innerHTML = `
|
||||||
|
<h2>${f.title} (${f.year || "?"})</h2>
|
||||||
|
<img src="${f.imgSrc}" alt="cover">
|
||||||
|
<div><b>Conseil d'âge:</b> ${f.marks.join(', ')}</div>
|
||||||
|
<p>${f.summary}</p>
|
||||||
|
<ul>
|
||||||
|
${f.details.map(d => `<li><b>${d.type}:</b> ${d.description}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
<a href="${f.link}" target="_blank">Voir sur Cinecheck</a>
|
||||||
|
`;
|
||||||
|
container.appendChild(div);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
75
cinecheck-adapter.js
Normal file
75
cinecheck-adapter.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
const cheerio = require('cheerio');
|
||||||
|
|
||||||
|
const CINECHECK_BASE = 'https://www.cinecheck.be';
|
||||||
|
|
||||||
|
async function searchMovies(query) {
|
||||||
|
const url = `${CINECHECK_BASE}/umbraco/surface/searchresults/search?query=${encodeURIComponent(query)}&producties=0&amount=5`;
|
||||||
|
const res = await axios.get(url, {
|
||||||
|
headers: {
|
||||||
|
'x-umb-culture': 'fr-BE',
|
||||||
|
'x-umb-key': '0a0c11a9-ece8-4dc8-8578-e5aab235d9ff',
|
||||||
|
'x-requested-with': 'XMLHttpRequest',
|
||||||
|
'User-Agent': 'Mozilla/5.0',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const $ = cheerio.load(res.data);
|
||||||
|
const results = [];
|
||||||
|
$('.c-search__result').each((_, el) => {
|
||||||
|
const title = $(el).find('.c-search__title').text().trim().replace(/\s*\(.+?\)\s*$/, '');
|
||||||
|
const yearMatch = $(el).find('.c-search__title').text().match(/\((\d{4})\)/);
|
||||||
|
const year = yearMatch ? yearMatch[1] : null;
|
||||||
|
const imgSrc = $(el).find('img.c-search__image').attr('src')
|
||||||
|
? CINECHECK_BASE + $(el).find('img.c-search__image').attr('src')
|
||||||
|
: null;
|
||||||
|
const link = $(el).find('a.c-search__hiddenlink').attr('href')
|
||||||
|
? CINECHECK_BASE + $(el).find('a.c-search__hiddenlink').attr('href')
|
||||||
|
: null;
|
||||||
|
if (title && link) {
|
||||||
|
results.push({ title, year, imgSrc, link });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMovieClassification(movieUrl) {
|
||||||
|
const res = await axios.get(movieUrl, {
|
||||||
|
headers: { 'User-Agent': 'Mozilla/5.0' }
|
||||||
|
});
|
||||||
|
const $ = cheerio.load(res.data);
|
||||||
|
|
||||||
|
const year = $('.c-movie__details .c-movie__label').first().text().trim() || null;
|
||||||
|
const genres = $('.c-movie__details .c-movie__label').eq(1).text().split(',').map(s => s.trim());
|
||||||
|
const img = $('.c-movie__cover img').attr('src')
|
||||||
|
? CINECHECK_BASE + $('.c-movie__cover img').attr('src')
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const marks = [];
|
||||||
|
$('.c-header__marks .c-header__mark').each((_, el) => {
|
||||||
|
const label = $(el).find('span.vh').text().trim();
|
||||||
|
if (label) marks.push(label);
|
||||||
|
});
|
||||||
|
|
||||||
|
const details = [];
|
||||||
|
$('.c-classificatie__item').each((_, el) => {
|
||||||
|
const type = $(el).find('svg use').first().attr('xlink:href') || '';
|
||||||
|
const typeName = type.split('#')[1] || '';
|
||||||
|
const description = $(el).find('.js-classificatie-text').text().trim();
|
||||||
|
if (typeName && description) {
|
||||||
|
details.push({ type: typeName, description });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const summary = $('.c-movie__introtext p').first().text().trim();
|
||||||
|
|
||||||
|
return {
|
||||||
|
year,
|
||||||
|
genres,
|
||||||
|
img,
|
||||||
|
marks,
|
||||||
|
details,
|
||||||
|
summary
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { searchMovies, getMovieClassification };
|
57
frontend.html
Normal file
57
frontend.html
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Agrégateur de films pour parents</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: system-ui, sans-serif; background: #252525; color: #ececec; }
|
||||||
|
.film { border:1px solid #444; margin:16px 0; padding:16px; border-radius: 6px; background: #181818; }
|
||||||
|
.film img { max-height: 180px; float:right; margin-left:16px; }
|
||||||
|
.film h2 { margin-top:0;}
|
||||||
|
.searchbox { margin:32px 0; }
|
||||||
|
input[type="text"] { font-size:1.2em; width:22em; background:#111; color:#fff; border:1px solid #444; border-radius:4px; padding:6px; }
|
||||||
|
button { font-size:1.1em; padding:6px 14px; background:#333; color:#fff; border-radius:4px; border:1px solid #555; }
|
||||||
|
.marks { font-size:0.95em; color:#b4ffb4; margin-bottom:6px; }
|
||||||
|
.details { font-size:0.93em; color: #ffbdbd;}
|
||||||
|
.year { color:#aaa;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Cinécheck Agrégateur</h1>
|
||||||
|
<div class="searchbox">
|
||||||
|
<input type="text" id="q" placeholder="Recherche... (ex: Minecraft)" />
|
||||||
|
<button onclick="search()">Go</button>
|
||||||
|
</div>
|
||||||
|
<div id="films"></div>
|
||||||
|
<script>
|
||||||
|
async function search() {
|
||||||
|
const q = document.getElementById('q').value.trim();
|
||||||
|
if (!q) return;
|
||||||
|
document.getElementById('films').innerHTML = 'Loading...';
|
||||||
|
const res = await fetch(`http://localhost:3000/search?q=${encodeURIComponent(q)}`);
|
||||||
|
const films = await res.json();
|
||||||
|
if (!Array.isArray(films) || !films.length) {
|
||||||
|
document.getElementById('films').innerHTML = "Rien trouvé. Essaie autre chose.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.getElementById('films').innerHTML = '';
|
||||||
|
films.forEach(f => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'film';
|
||||||
|
div.innerHTML = `
|
||||||
|
<h2>${f.title} ${f.year ? `<span class="year">(${f.year})</span>` : ''}</h2>
|
||||||
|
${f.img ? `<img src="${f.img}" alt="cover">` : ""}
|
||||||
|
<div class="marks"><b>Conseil d'âge:</b> ${f.marks ? f.marks.join(', ') : '-'}</div>
|
||||||
|
<div class="summary">${f.summary || 'Pas de résumé.'}</div>
|
||||||
|
<ul class="details">
|
||||||
|
${f.details && f.details.length ? f.details.map(d => `<li><b>${d.type}:</b> ${d.description}</li>`).join('') : ''}
|
||||||
|
</ul>
|
||||||
|
<a href="${f.link}" target="_blank" style="color:#7af;">Voir sur Cinecheck</a>
|
||||||
|
`;
|
||||||
|
document.getElementById('films').appendChild(div);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.getElementById('q').addEventListener('keydown', e => { if (e.key === 'Enter') search(); });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
47
index.html
Normal file
47
index.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Agrégateur Parents</title>
|
||||||
|
<style>
|
||||||
|
.film { border:1px solid #ccc; margin:10px; padding:10px; }
|
||||||
|
.film img { max-height: 180px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="films"></div>
|
||||||
|
<script>
|
||||||
|
// Appelle ton backend qui utilise le module JS au-dessus
|
||||||
|
// Ici, on simule avec des données
|
||||||
|
const films = [
|
||||||
|
{
|
||||||
|
title: "A Minecraft Movie",
|
||||||
|
year: "2025",
|
||||||
|
imgSrc: "https://www.cinecheck.be/media/n1hojmmh/a-minecraft-movie-ov-_ps_1_jpg_sd-high_2025-warner-bros-entertainment-inc-all-rights-reserved-photo-credit-courtesy-warner-bros-pictures.jpg",
|
||||||
|
link: "https://www.cinecheck.be/films/a-minecraft-movie/",
|
||||||
|
marks: ["9 ans", "Peur"],
|
||||||
|
summary: "A Minecraft Movie, van Warner Bros. Pictures en Legendary Pictures, ...",
|
||||||
|
details: [
|
||||||
|
{ type: "angst", description: "La film contient des scènes effrayantes..." }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const container = document.getElementById('films');
|
||||||
|
films.forEach(f => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'film';
|
||||||
|
div.innerHTML = `
|
||||||
|
<h2>${f.title} (${f.year || "?"})</h2>
|
||||||
|
<img src="${f.imgSrc}" alt="cover">
|
||||||
|
<div><b>Conseil d'âge:</b> ${f.marks.join(', ')}</div>
|
||||||
|
<p>${f.summary}</p>
|
||||||
|
<ul>
|
||||||
|
${f.details.map(d => `<li><b>${d.type}:</b> ${d.description}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
<a href="${f.link}" target="_blank">Voir sur Cinecheck</a>
|
||||||
|
`;
|
||||||
|
container.appendChild(div);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1238
package-lock.json
generated
Normal file
1238
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
package.json
Normal file
19
package.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "cine-kids",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "cinecheck-adapter.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "node server.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.9.0",
|
||||||
|
"cheerio": "^1.0.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^5.1.0"
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
25
server.js
Normal file
25
server.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const cors = require('cors');
|
||||||
|
const cinecheck = require('./cinecheck-adapter');
|
||||||
|
const app = express();
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
app.get('/search', async (req, res) => {
|
||||||
|
const q = req.query.q;
|
||||||
|
if (!q) return res.status(400).json({ error: "Missing query" });
|
||||||
|
try {
|
||||||
|
const results = await cinecheck.searchMovies(q);
|
||||||
|
// Enrichir chaque film avec la classification (en parallèle, le minimum pour survivre)
|
||||||
|
const enriched = await Promise.all(results.map(async m => {
|
||||||
|
const details = await cinecheck.getMovieClassification(m.link);
|
||||||
|
return { ...m, ...details };
|
||||||
|
}));
|
||||||
|
res.json(enriched);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).json({ error: e.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(3000, () => {
|
||||||
|
console.log('Backend prêt. http://localhost:3000');
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user