Une feuille Google Sheets qui se remplit avec les séances d’un cinéma

Quatre-vingts lignes d’Apps Script collées une fois : un menu remplit la feuille avec le programme de la semaine d’un cinéma, film par film avec les liens de réservation, et un déclencheur le refait chaque matin.

5 min de lecture

Sur cette page
  1. Ce qu’il vous faut
  2. 1.Le script
  3. 2.Enregistrer la clé
  4. 3.Remplir la feuille
  5. 4.Chaque matin
  6. La publier
  7. Pour aller plus loin

Un tableur suffit souvent à un journal local, un site de mairie, un ciné-club ou un média étudiant : le programme du cinéma d’à côté, mis à jour tout seul, que toute l’équipe peut filtrer, trier ou publier. Ce tutoriel met les séances de la semaine d’un cinéma dans une feuille Google Sheets avec environ 80 lignes d’Apps Script, collées une fois. Aucun serveur, aucun déploiement, et une requête par jour, que le forfait gratuit couvre très largement.

Date        Heure  Film                        Durée    Version  Format  Réservation
2026-11-06  11:50  Sourires d'une nuit d'été   1 h 49   VOST     2D      https://www.cinema-lechampo.com/…
2026-11-06  11:55  Le Septième Sceau           1 h 36   VOST     2D      https://www.cinema-lechampo.com/…
2026-11-06  13:45  Affreux, sales et méchants  1 h 55   VOST     2D      https://www.cinema-lechampo.com/…

Ce qu’il vous faut

  • Un compte Google et une nouvelle feuille Google Sheets.
  • Une clé API Cinéma : créez un compte gratuit, confirmez votre adresse e-mail, puis créez une clé depuis le tableau de bord. Le forfait gratuit donne 100 requêtes par semaine ; cette feuille en consomme une par jour.
  • Le slug du cinéma. Cherchez-le par son nom :
Terminal
curl "https://api.api-cinema.com/v1/cinemas?q=champo" -H "Authorization: Bearer ac_live_…"

La réponse donne "slug": "le-champo-paris-5e-arrondissement". N’importe quel cinéma de l’API, en France, aux États-Unis ou en Australie, marche pareil.

Le script

Dans la feuille, ouvrez Extensions › Apps Script, remplacez le contenu de Code.gs par le script ci-dessous et enregistrez (l’icône disquette, ou Ctrl+S). Rechargez ensuite la feuille : un menu Cinéma apparaît à côté d’Aide.

Code.gs
// Une feuille Google Sheets qui se remplit avec les séances d’un cinéma, depuis API Cinéma.
// Menu « Cinéma » : enregistrer la clé une fois, mettre à jour maintenant, ou chaque matin.

const API = 'https://api.api-cinema.com/v1'
const HEADER = ['Date', 'Heure', 'Film', 'Durée', 'Version', 'Format', 'Réservation']

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('Cinéma')
    .addItem('Mettre à jour les séances', 'updateShowtimes')
    .addItem('Mettre à jour chaque matin', 'installDailyTrigger')
    .addSeparator()
    .addItem('Enregistrer la clé API…', 'setApiKey')
    .addToUi()
}

/** La clé vit dans les propriétés du script : jamais dans une cellule, jamais dans le code. */
function setApiKey() {
  const ui = SpreadsheetApp.getUi()
  const answer = ui.prompt('Clé API Cinéma', 'Collez votre clé (ac_live_…).', ui.ButtonSet.OK_CANCEL)
  if (answer.getSelectedButton() !== ui.Button.OK)
    return
  PropertiesService.getScriptProperties().setProperty('API_CINEMA_KEY', answer.getResponseText().trim())
}

function updateShowtimes() {
  const key = PropertiesService.getScriptProperties().getProperty('API_CINEMA_KEY')
  if (!key)
    throw new Error('Pas encore de clé API : Cinéma › Enregistrer la clé API…')

  const ss = SpreadsheetApp.getActiveSpreadsheet()
  const config = configSheet(ss)
  const slug = String(config.getRange('B1').getValue()).trim()
  const days = Math.max(1, Math.min(14, Number(config.getRange('B2').getValue()) || 7))
  if (!slug)
    throw new Error('Mettez le slug du cinéma dans Config!B1')

  const rows = fetchShowtimes(key, slug, days).map(s => [
    s.startsAt.slice(0, 10), // date et heure locales du cinéma, telles que l’API les donne
    s.startsAt.slice(11, 16),
    s.movie.title,
    s.movie.durationMin ? `${Math.floor(s.movie.durationMin / 60)} h ${String(s.movie.durationMin % 60).padStart(2, '0')}` : '',
    s.version,
    s.format,
    s.bookingUrl || '',
  ])

  const sheet = ss.getSheetByName('Séances') || ss.insertSheet('Séances')
  sheet.clearContents()
  sheet.getRange(1, 1, 1, HEADER.length).setValues([HEADER]).setFontWeight('bold')
  if (rows.length)
    sheet.getRange(2, 1, rows.length, HEADER.length).setValues(rows)
  sheet.setFrozenRows(1)
  sheet.autoResizeColumns(1, HEADER.length)
  config.getRange('B3').setValue(new Date())
}

/** Toutes les séances du cinéma de maintenant à `days` jours, page par page. */
function fetchShowtimes(key, slug, days) {
  const to = new Date()
  to.setDate(to.getDate() + days - 1) // `to` est un jour entier : 7 jours = aujourd’hui et les 6 suivants
  const params = { to: Utilities.formatDate(to, Session.getScriptTimeZone(), 'yyyy-MM-dd'), include: 'movie', limit: 1000 }
  const all = []
  let cursor
  do {
    const query = Object.entries({ ...params, cursor })
      .filter(([, v]) => v !== undefined)
      .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
      .join('&')
    const res = UrlFetchApp.fetch(`${API}/cinemas/${encodeURIComponent(slug)}/showtimes?${query}`, {
      headers: { Authorization: `Bearer ${key}` },
      muteHttpExceptions: true,
    })
    if (res.getResponseCode() !== 200) {
      throw new Error(`API Cinéma a répondu ${res.getResponseCode()} : ${res.getContentText().slice(0, 200)}`)
    }
    const body = JSON.parse(res.getContentText())
    all.push(...body.data)
    cursor = body.meta && body.meta.nextCursor
  } while (cursor)
  return all
}

/** La feuille Config, créée avec des valeurs par défaut la première fois. */
function configSheet(ss) {
  let sheet = ss.getSheetByName('Config')
  if (!sheet) {
    sheet = ss.insertSheet('Config')
    sheet.getRange('A1:B3').setValues([
      ['Slug du cinéma', 'le-champo-paris-5e-arrondissement'],
      ['Jours', 7],
      ['Dernière mise à jour', ''],
    ])
    sheet.autoResizeColumns(1, 2)
  }
  return sheet
}

/** Une mise à jour par jour, vers 6 h du matin, dans le fuseau de la feuille. */
function installDailyTrigger() {
  ScriptApp.getProjectTriggers()
    .filter(t => t.getHandlerFunction() === 'updateShowtimes')
    .forEach(t => ScriptApp.deleteTrigger(t))
  ScriptApp.newTrigger('updateShowtimes').timeBased().everyDays(1).atHour(6).create()
  SpreadsheetApp.getUi().alert('La feuille Séances se mettra à jour chaque matin.')
}

Enregistrer la clé

Cinéma › Enregistrer la clé API… demande la clé et la range dans les propriétés du script : elle n’apparaît jamais dans une cellule, la feuille peut donc être partagée ou publiée sans la donner. Une chose à savoir : quiconque peut modifier la feuille peut ouvrir le script et lire ses propriétés. Partagez la feuille en Lecteur avec le public, gardez la modification pour les personnes de confiance, et révoquez la clé depuis le tableau de bord au moindre doute.

À la première exécution, Google vous demande d’autoriser le script : Vérifier les autorisations, choisissez votre compte, puis, le script étant le vôtre et non vérifié par Google, Paramètres avancés › Accéder à … (non sécurisé) › Autoriser. Il a besoin de deux choses : vos feuilles de calcul, et la connexion à un service externe (l’API).

Remplir la feuille

Cinéma › Mettre à jour les séances crée deux feuilles. Config contient le slug du cinéma (B1, Le Champo par défaut) et le nombre de jours (B2, 7, jusqu’à 14) ; Séances reçoit une ligne par séance, date, heure, film, durée, version, format et lien de réservation du cinéma, en-tête figé, colonnes ajustées. Changez le slug ou les jours dans Config, relancez : la feuille est réécrite.

Les horaires sont l’heure locale du cinéma, telle que l’API la renvoie : un cinéma de New York affiche l’heure de New York. Les séances déjà commencées sont laissées de côté, la liste part de maintenant. La durée et le lien de réservation viennent avec include=movie et les autres champs ; la documentation les liste tous.

Chaque matin

Cinéma › Mettre à jour chaque matin installe un déclencheur horaire : la feuille se met à jour vers 6 h, dans son fuseau, sans que personne ne l’ouvre. Cela fait une requête par jour pour une semaine de programme (un multiplexe sur 14 jours peut prendre deux ou trois pages, une requête chacune) : quelques requêtes par semaine sur un forfait qui en permet 150. Le déclencheur tourne avec votre compte ; si vous quittez l’organisation, confiez la feuille à quelqu’un d’autre et laissez-le le réinstaller.

La publier

  • Partager ou intégrer. Fichier › Partager › Publier sur le Web donne une page publique ou un <iframe> pour un site, mis à jour après chaque exécution.
  • Filtrer sur aujourd’hui. Dans une autre feuille, =FILTER(Séances!A2:G; Séances!A2:A = TEXT(AUJOURDHUI(); "yyyy-mm-dd")) garde les lignes du jour ; =QUERY(Séances!A:G; "select C, count(B) group by C") compte les séances par film.
  • Une autre feuille, un autre cinéma. =IMPORTRANGE depuis une seconde feuille, ou des lignes en plus dans Config et une boucle dessus dans updateShowtimes : une requête par cinéma.

Pour aller plus loin

  • La VO seulement. Ajoutez version: 'VOST' (ou VO) à params : l’API filtre avant de répondre.
  • Les affiches. s.movie.posterUrl est dans la réponse ; =IMAGE(H2) l’affiche dans une cellule.
  • Une ville plutôt qu’un cinéma. GET /v1/showtimes?city=Lyon&include=movie,cinema liste toutes les séances d’une ville, avec le cinéma sur chaque ligne : le même script avec une autre URL et une colonne de plus.

Les mêmes données alimentent le tutoriel Python (CSV et pandas, pour de plus gros exports) et le widget pour site web quand une page en a besoin en direct. Pour une newsletter ou un post chaque soir sans code, voyez le tutoriel n8n.