Skip to content

Instantly share code, notes, and snippets.

@JulienRAVIA
Created January 18, 2026 20:21
Show Gist options
  • Select an option

  • Save JulienRAVIA/f7e7bd3e63e61a2b8a9aa92b586e0e8e to your computer and use it in GitHub Desktop.

Select an option

Save JulienRAVIA/f7e7bd3e63e61a2b8a9aa92b586e0e8e to your computer and use it in GitHub Desktop.
SensCritique
// ==UserScript==
// @name SensCritique - Ajouter au calendrier Google
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Ajoute un bouton "Ajouter au calendrier" pour chaque film sur les pages de sorties cinéma SensCritique
// @author JulienRAVIA
// @match https://www.senscritique.com/films/sorties-cinema/*/semaine/*
// @match https://www.senscritique.com/films/sorties-cinema/*/*
// @match https://www.senscritique.com/films/sorties-cinema/*
// @match https://www.senscritique.com/films/cette-semaine
// @icon https://www.senscritique.com/favicon.ico
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Fonction pour formater la date pour Google Calendar (YYYYMMDD)
function formatDateForGoogle(date) {
const year = date.getFullYear();
const month = String(date. getMonth() + 1).padStart(2, '0');
const day = String(date. getDate()).padStart(2, '0');
return `${year}${month}${day}`;
}
// Fonction pour formater la date en français
function formatDate(date) {
const moisFrancais = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'];
const parts = date.trim().split(' ');
const jour = parts[0];
const mois = moisFrancais.indexOf(parts[1].toLowerCase()) + 1;
const annee = parts[2];
return new Date(annee, mois - 1, jour);
}
// Fonction pour créer le lien Google Calendar
function createGoogleCalendarLink(title, date, synopsis, creators, otherInfos, url) {
const dateStr = formatDateForGoogle(date);
const params = new URLSearchParams({
action: 'TEMPLATE',
text: `${title}`,
dates: `${dateStr}/${dateStr}`,
details: `${otherInfos}.\n\n${creators}.\n\n${synopsis}.\n\n<a href="${url}">${url}</a>`,
ctz: 'Europe/Paris'
});
return `https://calendar.google.com/calendar/render?${params. toString()}`;
}
// Fonction pour ajouter les boutons calendrier
function addCalendarButtons() {
// Sélectionner tous les éléments de film
// SensCritique utilise généralement des cartes pour afficher les films
const filmCards = document.querySelectorAll('[data-testid="product-list-item"]');
// Si pas de cartes trouvées, essayer une approche plus générique
let films = [];
if (filmCards.length === 0) {
// Chercher les liens vers les films
const filmLinks = document.querySelectorAll('a[href^="/film/"]');
const processedTitles = new Set();
filmLinks.forEach(link => {
const title = link.textContent.trim();
if (title && !processedTitles.has(title) && title. length > 1) {
// Trouver le conteneur parent le plus proche
let container = link.closest('li, article, div[class*="card"], div[class*="Card"], div[class*="item"], div[class*="Item"]');
if (container && ! container.querySelector('. sc-calendar-btn')) {
films.push({ element: container, title: title, link: link });
processedTitles.add(title);
}
}
});
} else {
filmCards.forEach(card => {
if (card.querySelector('.sc-calendar-btn')) return;
const titleElement = card.querySelector('[data-testid="product-title"]');
const url = titleElement.href;
const title = titleElement ? titleElement. textContent.trim() : null;
const cleanedTitle = title.replace(/\s*\(\d{4}\)/, '').trim();
const synopsis = card.querySelector('[data-testid="content"]')?.textContent.trim();
const creators = card.querySelector('[data-testid="creators"]')?.textContent.trim();
const otherInfos = card.querySelector('[data-testid="other-infos"]')?.textContent.trim();
const releaseDate = card.querySelector('[data-testid="date-release"]')?.textContent.trim();
const cleaned = releaseDate.replace('.', '').replace('Sortie : ', '').replace(/\s*\([^)]*\)/g, '');
if (title) {
films.push({ element: card, title: cleanedTitle, synopsis, otherInfos, creators, releaseDate: cleaned, url });
}
});
}
// Ajouter les boutons pour chaque film
films.forEach(film => {
if (film.element.querySelector('.sc-calendar-btn')) return;
const dateSortie = formatDate(film.releaseDate);
const calendarLink = createGoogleCalendarLink(film.title, dateSortie, film.synopsis, film.creators, film.otherInfos, film.url);
// Créer le conteneur du bouton
const buttonContainer = document.createElement('div');
buttonContainer.className = 'sc-calendar-btn';
buttonContainer.style.cssText = `
margin-top: 8px;
display: flex;
flex-direction: column;
gap: 4px;
`;
// Créer le bouton
const button = document.createElement('a');
button.href = calendarLink;
button.target = '_blank';
button. rel = 'noopener noreferrer';
button.textContent = 'Ajouter au calendrier';
button.style.cssText = `
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
color: rgb(52, 64, 84);
text-transform: uppercase;
text-decoration: none;
border-radius: 4px;
border: 1px solid rgb(208, 213, 221);
font-size: 12px;
font-weight: 500;
transition: all 0.2s ease;
background: rgb(250, 249, 246);
font-family: var(--font-sourceCodePro);
width: fit-content;
`;
button.addEventListener('mouseenter', () => {
button.style.transform = 'translateY(-1px)';
button.style.boxShadow = '0 4px 8px rgba(0,0,0,0. 2)';
});
button.addEventListener('mouseleave', () => {
button.style.transform = 'translateY(0)';
button.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
});
buttonContainer.appendChild(button);
const filmContent = film.element.querySelector('[data-testid="content"]');
// Ajouter le bouton au conteneur du film
filmContent.appendChild(buttonContainer);
});
console.log(`SensCritique Calendar: ${films.length} bouton(s) ajouté(s)`);
}
// Observer pour détecter les changements dans le DOM (chargement dynamique)
function observeDOM() {
const observer = new MutationObserver((mutations) => {
let shouldUpdate = false;
mutations.forEach(mutation => {
if (mutation.addedNodes.length > 0) {
shouldUpdate = true;
}
});
if (shouldUpdate) {
setTimeout(addCalendarButtons, 500);
console.log('SensCritique Calendar:zefezfezf Impossible d\'extraire les informations de date de l\'URL');
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// Initialisation
function init() {
// Attendre que la page soit chargée
if (document.readyState === 'loading') {
document. addEventListener('DOMContentLoaded', () => {
setTimeout(addCalendarButtons, 1000);
observeDOM();
console.log('SensCritique Calendar:zefezfeezfzfzf Impossible d\'extraire les informations de date de l\'URL');
});
} else {
setTimeout(addCalendarButtons, 1000);
console.log('SensCritique Calendar:zefezfezfzefezf Impossible d\'extraire les informations de date de l\'URL');
observeDOM();
}
// Réexécuter lors des changements de navigation (SPA)
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
setTimeout(addCalendarButtons, 1000);
}
}).observe(document, { subtree: true, childList: true });
}
init();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment