Application web 100 % statique pour importer des fichiers .ics (ou flux iCalendar) directement dans Google Agenda — sans backend, sans serveur, hébergeable gratuitement sur GitHub Pages.
Deux implémentations coexistent dans ce dépôt. La version statique (
index.html+js/) est celle décrite ci-dessous et celle déployée sur GitHub Pages. Une seconde version, Flask, est également présente (app.py+templates/) : elle offre moins de fonctionnalités et s'exécute en local uniquement. Voir Variante Flask.
- 📂 Upload d'un fichier
.ics(glisser-déposer) - 🔗 Import depuis une URL de flux iCalendar
- 👀 Prévisualisation des événements avant import (avec sélection individuelle)
- 🗂 Choix de l'agenda de destination
- 🔎 Gestion des doublons : ignorer · mettre à jour · dupliquer
- 🔐 OAuth2 côté client — aucun mot de passe stocké, aucun serveur
La configuration se fait une seule fois et prend environ 10 minutes.
| Version statique | Variante Flask | |
|---|---|---|
| Compte Google | ✅ requis | ✅ requis |
| Navigateur récent | ✅ requis | ✅ requis |
| Python | 3.x — uniquement pour servir les fichiers ; n'importe quel serveur HTTP fait l'affaire | 3.8 ou plus (imposé par Flask 3.0) |
| Dépendances à installer | ❌ aucune — les bibliothèques sont déjà dans js/ |
pip install -r requirements.txt |
git clone https://github.com/BretzelCoder/GoogleAgendaAssistant.git
cd GoogleAgendaAssistantSans Git : Code → Download ZIP sur la page GitHub du dépôt, puis décompressez-le. Toutes les commandes qui suivent s'exécutent depuis la racine du dossier obtenu.
Si votre objectif est d'héberger l'application plutôt que de l'utiliser en local, allez directement à Déploiement sur GitHub Pages.
- Rendez-vous sur console.cloud.google.com
- Cliquez sur le sélecteur de projet en haut → Nouveau projet
- Donnez-lui un nom (ex.
ics-to-gcal) et confirmez
- Dans le menu : APIs & Services → Enable APIs and Services
- Recherchez Google Calendar API
- Cliquez sur le résultat puis sur Enable
⚠️ Sans cette étape, vous obtiendrez l'erreur "Google Calendar API has not been used in project" lors de l'import.
- APIs & Services → OAuth consent screen
- Choisissez External → Create
- Remplissez les champs obligatoires (nom de l'app, email de support)
- Cliquez Save and Continue jusqu'à la section Test users
- Dans Test users → + Add users → ajoutez votre adresse Gmail
- Terminez la configuration
⚠️ Tant que l'app est en mode Testing, seuls les emails de la liste peuvent se connecter. Pour un usage personnel, c'est suffisant.
- APIs & Services → Credentials → Create Credentials → OAuth client ID
- Type d'application : Application Web
- Dans Origines JavaScript autorisées, ajoutez :
http://localhost:8080 https://votre-compte.github.io - Cliquez Create
- Copiez le Client ID généré (format
XXXXXXXX.apps.googleusercontent.com)
Le Client ID est une valeur publique — il identifie votre application, pas vous.
Google refuse l'OAuth depuis un fichier ouvert en file://. Il faut servir le dossier en HTTP,
sur le port 8080 (c'est l'origine déclarée à l'étape 4) :
# Depuis la racine du dépôt
python -m http.server 8080Puis ouvrez http://localhost:8080.
Toute autre méthode fonctionne (
npx serve -l 8080, extension Live Server, …) tant que l'URL finale correspond exactement à une origine JavaScript autorisée de votre Client ID.
Collez votre Client ID dans le champ en haut de la page et cliquez Enregistrer. La page se recharge automatiquement.
Le Client ID est stocké dans le
localStoragedu navigateur (cléics_gcal_client_id). Il reste donc en place d'une session à l'autre, et par navigateur.
Cliquez sur Se connecter avec Google et sélectionnez votre compte Gmail.
Si Chrome bloque la popup : cliquez sur l'icône de popup bloquée dans la barre d'adresse et autorisez
localhost:8080(ou votre domaine GitHub Pages).
Deux options :
- Fichier .ics — glissez-déposez le fichier ou cliquez pour parcourir
- URL — collez l'adresse d'un flux iCalendar distant
Cliquez ensuite sur 🔍 Analyser.
⚠️ Certaines URLs de flux iCalendar refusent les requêtes depuis un navigateur (CORS). Si l'analyse échoue avec une URL, téléchargez le fichier.icsmanuellement et utilisez l'upload.
Un tableau liste tous les événements détectés avec leur titre, dates et indicateurs (récurrent, lieu, invalide). Vous pouvez :
- Décocher des événements pour les exclure de l'import
- Utiliser la case en en-tête pour tout sélectionner / désélectionner
- Agenda de destination — sélectionnez l'agenda Google dans lequel importer
- Gestion des doublons :
- Ignorer — les événements déjà présents (même UID) sont ignorés
- Mettre à jour — les événements existants sont écrasés
- Importer quand même — crée un doublon sans vérification
Cliquez sur ⬆️ Importer. Une barre de progression s'affiche. À la fin, un résumé indique le nombre d'événements importés, mis à jour, ignorés et les éventuelles erreurs.
| Erreur | Cause | Solution |
|---|---|---|
| access_denied | Votre email n'est pas dans les utilisateurs test | APIs & Services → OAuth consent screen → Test users → ajoutez votre email |
| Google Calendar API has not been used | L'API n'est pas activée | APIs & Services → Enable APIs → activez Google Calendar API |
| La popup a été bloquée | Chrome bloque la popup OAuth | Autorisez les popups pour ce domaine dans la barre d'adresse |
| Origine non autorisée | L'URL d'où vous accédez n'est pas déclarée | Ajoutez l'URL dans Origines JavaScript autorisées de votre Client ID |
| Fichier ICS invalide | Le fichier est corrompu ou mal encodé | Vérifiez que le fichier s'ouvre correctement dans un éditeur de texte |
| CORS ou réseau | L'URL du flux refuse les requêtes navigateur | Téléchargez le .ics manuellement et utilisez l'upload, ou passez par la variante Flask |
| Client ID invalide | Le format saisi ne se termine pas par .apps.googleusercontent.com |
Recopiez le Client ID complet depuis Google Cloud Console |
| Impossible de charger Google | Le script Google Identity Services n'a pas pu être chargé (bloqueur de pub, hors-ligne) | Désactivez le bloqueur sur ce domaine et rechargez |
| Le bouton Se connecter reste grisé | Aucun Client ID enregistré | Renseignez le Client ID dans le champ en haut de page (étape 6) |
La page ne fait rien en file:// |
L'OAuth Google exige une origine HTTP | Servez le dossier via python -m http.server 8080 (étape 5) |
- Forkez ou clonez ce dépôt sur votre compte GitHub
- Dans les paramètres du dépôt : Settings → Pages → Source : Deploy from a branch → main / (root)
- Votre app sera disponible sur
https://votre-compte.github.io/nom-du-repo - Ajoutez cette URL dans les Origines JavaScript autorisées de votre Client ID Google (étape 4 ci-dessus)
Le dépôt contient aussi une implémentation Flask (app.py), antérieure et plus simple.
Elle fait l'OAuth côté serveur, ce qui implique un client secret et donc un vrai serveur :
elle ne peut pas être déployée sur GitHub Pages.
Elle reste utile si vous préférez ne pas exposer de Client ID dans le navigateur, ou si les restrictions CORS de votre flux ICS bloquent la version statique (le téléchargement de l'URL est fait par le serveur Python, pas par le navigateur).
| Fonctionnalité | Statique | Flask |
|---|---|---|
Upload .ics / URL de flux |
✅ | ✅ |
| Téléchargement d'URL sans blocage CORS | ❌ | ✅ |
| Prévisualisation + sélection des événements | ✅ | ❌ |
| Choix de l'agenda de destination | ✅ | ✅ |
| Stratégie de doublons configurable | ✅ | ❌ (toujours import par UID) |
Événements récurrents (RRULE) |
✅ | ❌ (récurrence non transmise) |
| Barre de progression | ✅ | ❌ |
| Hébergeable sur GitHub Pages | ✅ | ❌ |
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -r requirements.txtCette variante nécessite un identifiant OAuth avec secret, différent de celui de l'étape 4 :
- APIs & Services → Credentials → Create Credentials → OAuth client ID → Application Web
- Dans URI de redirection autorisés, ajoutez :
http://localhost:5000/oauth2callback - Download JSON et enregistrez le fichier sous
credentials.jsonà la racine du dépôt
🔐
credentials.jsoncontient un client secret : ne le commitez jamais. Il est listé dans.gitignore.
# Facultatif : fixer la clé de session pour rester connecté d'un redémarrage à l'autre
# Windows PowerShell
$env:SECRET_KEY = "une-valeur-aleatoire"
# macOS / Linux
export SECRET_KEY="une-valeur-aleatoire"
python app.pyL'application écoute sur http://localhost:5000.
Variables d'environnement reconnues :
| Variable | Défaut | Rôle |
|---|---|---|
SECRET_KEY |
aléatoire à chaque démarrage | Clé de signature des sessions Flask |
GOOGLE_CLIENT_SECRETS |
credentials.json |
Chemin du fichier d'identifiants OAuth |
FLASK_DEBUG |
désactivé | 1 active le débogueur Werkzeug (console d'exécution de code — développement uniquement) |
⚠️ app.pyforceOAUTHLIB_INSECURE_TRANSPORT=1(OAuth autorisé en HTTP). C'est acceptable parce que le serveur n'écoute que sur127.0.0.1, pas pour une exposition sur le réseau.
- Aucun serveur : toute la logique s'exécute dans votre navigateur
- Aucune donnée transmise : ni le fichier ICS, ni le token Google ne quittent votre navigateur — les seules requêtes sortantes vont vers les API Google
- Token en mémoire : le token d'accès n'est jamais persisté ; il disparaît au rechargement de la page. Seul le Client ID est conservé (
localStorage) - Token temporaire : le token OAuth expire après 1 heure (standard Google)
- Client ID public : c'est normal — seuls les domaines que vous autorisez explicitement peuvent l'utiliser
- Aucun jeton dans le cookie : les sessions Flask sont signées mais non chiffrées, donc lisibles par le navigateur. Le cookie ne contient qu'un identifiant opaque ; token et refresh token restent en mémoire du serveur et disparaissent à l'arrêt
- Écoute locale seulement (
127.0.0.1), débogueur désactivé par défaut - URLs distantes filtrées : le téléchargement d'un ICS par URL refuse les adresses privées, locales et réservées, y compris après redirection (protection SSRF)
credentials.jsoncontient un client secret : gardez-le hors du dépôt- Prévue pour un usage local uniquement (OAuth en HTTP autorisé)
GoogleAgendaAssistant/
│
│ ── Version statique (principale, déployée sur GitHub Pages) ──
├── index.html # Page principale (app complète, 3 étapes)
├── css/
│ └── style.css # Styles
├── js/
│ ├── ical.min.js # Bibliothèque tierce de parsing iCalendar (ical.js 1.5.0, MPL 2.0)
│ ├── parser.js # ICSParser — VEVENT → objets Google Calendar
│ ├── gcal.js # GCal — client REST Calendar API + OAuth (GIS)
│ └── app.js # Logique & interface utilisateur
│
│ ── Variante Flask (locale, optionnelle) ──
├── app.py # Serveur Flask (OAuth côté serveur + import)
├── templates/
│ └── index.html # Gabarit Jinja2 (CSS et JS inline)
├── requirements.txt # Dépendances Python de la variante Flask
│
├── README.md # Ce fichier — installation et usage
├── CLAUDE.md # Documentation interne (architecture, conventions)
├── LICENSE # MIT
└── THIRD-PARTY-NOTICES.md # Licences des composants tiers (ical.js — MPL 2.0)
requirements.txtne concerne que la variante Flask. La version statique n'a aucune dépendance à installer : les bibliothèques sont déjà dansjs/.
MIT — libre d'utilisation, de modification et de redistribution. Voir LICENSE.
Exception : js/ical.min.js est une bibliothèque tierce
(ical.js) sous MPL 2.0. Cette licence étant un
copyleft au niveau du fichier, elle n'affecte pas le reste du projet, qui demeure sous MIT.
Détail des composants tiers et de leurs licences : THIRD-PARTY-NOTICES.md.