SPB Git

spb/crea-ka

Public

Créa·Ka — annuaire public cross-plateforme des créateurs de contenu québécois (crea-ka.com)

docs: documentation standardisée des 14 sources (générateur + fiches)

scripts/gen_connector_docs.py : générateur 100 % programmatique et rejouable
qui croise data/sources.json (famille, palier, accès, signal d identité),
l introspection statique (ast) de creaka/connectors/*.py et la BD live
(creators/doc JSON, accounts par plateforme, sync_log : cadence, alertes).
Écrit docs/connecteurs/INDEX.md + une fiche par source (sections fixes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Aug 18, 2026) parent 6d3e57d Browse files

16 changed files +1,594 −0

added docs/connecteurs/INDEX.md +22 −0
@@ -0,0 +1,22 @@
1+# Créa-Ka — Index des connecteurs
2+
3+_Généré automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**14 sources au registre** · **5664 créateurs** et **6527 comptes** en BD.
6+
7+| Source | Famille | Palier | Type d'accès | Backend | Découverts | Comptes (plateforme) | État | Dernier passage |
8+|---|---|---|---|---|---|---|---|---|
9+| [`listes-medias`](listes-medias.md) | decouverte | P3 | dataset local compilé (data/seed) | direct | 310 | — | actif | 2026-08-18T06:23… |
10+| [`wikidata-qc`](wikidata-qc.md) | decouverte | P3 | API SPARQL publique (Wikidata) | direct | 73 | — | actif | 2026-08-18T06:27… |
11+| [`onlyqueb`](onlyqueb.md) | decouverte | P3 | sitemap XML + JSON-LD des pages profil (S… | direct | 253 | 248 (onlyfans) | actif | 2026-08-18T06:27… |
12+| [`youtube-recherche`](youtube-recherche.md) | decouverte | P3 | API JSON | direct | 4595 | 4768 (youtube) | actif | 2026-08-18T07:22… |
13+| [`balados-itunes`](balados-itunes.md) | decouverte | P2 | API iTunes Search/Lookup (publique) | direct | 433 | 434 (podcast) | actif | 2026-08-18T06:23… |
14+| [`instagram-profil`](instagram-profil.md) | enrichissement | P4 | pages HTML publiques | Scrapfly | 0 | 460 (instagram) | actif | 2026-08-17T23:23… |
15+| [`tiktok-profil`](tiktok-profil.md) | enrichissement | P4 | pages HTML publiques | Scrapfly | 0 | 197 (tiktok) | actif | 2026-08-18T00:31… |
16+| [`youtube`](youtube.md) | enrichissement | P2 | API JSON | direct | 0 | 4768 (youtube) | actif | 2026-08-18T01:06… |
17+| [`twitch`](twitch.md) | enrichissement | P2 | API JSON | direct | 0 | 26 (twitch) | actif | 2026-08-18T01:06… |
18+| [`link-in-bio`](link-in-bio.md) | enrichissement | P1 | pages HTML publiques | Firecrawl | 0 | — | actif | 2026-08-18T06:20… |
19+| [`balados-rss`](balados-rss.md) | enrichissement | P1 | flux RSS publics | direct | 0 | 434 (podcast) | actif | 2026-08-18T06:17… |
20+| [`podcastindex`](podcastindex.md) | enrichissement | P2 | API JSON | direct | 0 | 434 (podcast) | prêt | — |
21+| [`x-profil`](x-profil.md) | enrichissement | P3 | pages HTML publiques | direct | 0 | 186 (x) | actif | 2026-08-18T06:15… |
22+| [`bio-liens`](bio-liens.md) | enrichissement | P1 | pages HTML publiques | direct | 0 | — | actif | 2026-08-18T06:17… |
added docs/connecteurs/balados-itunes.md +87 −0
@@ -0,0 +1,87 @@
1+# `balados-itunes` — connecteur decouverte (palier 2)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 433 · touchés : 433
6+
7+## Description de la source
8+
9+Connecteur DÉCOUVERTE — balados québécois via l'API de recherche iTunes/Apple Podcasts (OFFICIELLE, gratuite, sans clé). Palier 2-3 (§9). Filtre conservateur : marqueur québécois requis dans le nom du balado ou de l'artiste.
10+
11+- **Notes (registre)** : podcasteurs québécois : nom, artiste, pochette, genre, nb d'épisodes, flux RSS ; marqueur QC requis
12+- **Signal d'identité** : profil_source (0.98) (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/balados_itunes.py` — classe `BaladosItunesConnector` (`kind = "discovery"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : API de recherche iTunes/Apple Podcasts (officielle, gratuite, sans clé)
18+- **Type d'accès (code)** : API iTunes Search/Lookup (publique)
19+- **Endpoint de base** : https://itunes.apple.com/search
20+- **URLs du module** : https://itunes.apple.com/search
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli)
23+- **Politesse** : 1.5 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : clé(s) API requise(s) — voir statut/registre
25+
26+## Champs récupérés → schéma cible
27+
28+Source de **découverte** : produit des fiches `Creator` (tables `creators` + `accounts`). Complétude mesurée sur les 433 créateurs découverts par cette source (champ `doc.source`) ; exemple tiré d'une fiche réelle.
29+
30+| Champ du doc créateur | Contenu | Renseigné | Exemple réel |
31+|---|---|---|---|
32+| `display_name` | Nom public | 100 % | Relève et Repreneuriat au Québec - Le Podcast |
33+| `bio` | Bio | 97 % | Le balado qui met l’humain au cœur des transferts d’entreprise. Dans les années à venir, … |
34+| `region` | Région | 0 % | — |
35+| `city` | Ville | 0 % | — |
36+| `niches` | Niches (liste) | 100 % | actualite-opinion, arts, finance-affaires |
37+| `languages` | Langues (liste) | 100 % | fr |
38+| `creator_type` | Type de créateur | 100 % | podcasteur |
39+| `primary_platform` | Plateforme principale | 100 % | youtube |
40+| `platforms` | Comptes sociaux (liste) | 100 % | youtube:@releveetrepreneuriat, podcast:@1889244567 |
41+| `avatar_url` | Avatar | 100 % | https://is1-ssl.mzstatic.com/image/thumb/Podcasts211/v4/66/d3/96/66d3962a-1201-b271-25bb-… |
42+| `total_reach` | Portée totale (abonnés cumulés) | 0 % | 176 |
43+| `link_in_bio_url` | Lien-en-bio | 0 % | — |
44+
45+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `podcast` (434 comptes en BD, confiance moyenne 0.98).
46+
47+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
48+|---|---|---|---|
49+| `handle` | Handle | 100 % | — |
50+| `url` | URL du profil | 100 % | — |
51+| `followers` | Abonnés | 0 % | — |
52+| `verified` | Badge vérifié | 0 % | — |
53+| `confidence` | Confiance d'identité | 100 % | — |
54+| `signal` | Signal d'identité | 100 % | — |
55+| `metrics` | Métriques détaillées (JSON) | 100 % | — |
56+| `last_checked` | Dernière vérification | 100 % | — |
57+
58+## Fréquence & budget
59+
60+- **Cadence déclarée (registre)** : quotidienne (watch)
61+- **Cadence observée** (médiane sync_log) : ≈ 24 min
62+- **Dernier passage** : 2026-08-18T06:23:00Z — 433 créateurs, 433 comptes, +0 / ~433, 0 erreur(s), 131 s
63+- **Throttling** : 1.5 s entre requêtes (`request_delay`)
64+
65+## Volumétrie & complétude
66+
67+- **Créateurs découverts par la source** (`doc.source`) : 433 · **fiches touchées** (`source_ids`) : 433
68+- **Complétude clé (découverts)** : niches 100 % · avatar 100 % · portée 0 % · région 0 %
69+- **Comptes `podcast` en BD** : 434 — abonnés 0 % · métriques 100 % · dernière vérification 2026-08-18T06:23:00Z
70+- **Runs journalisés (60 derniers)** : 7, dont 0 avec erreurs
71+
72+## Erreurs connues & dépannage
73+
74+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
75+
76+Rejouer la source seule : `python3 run.py sync balados-itunes` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='balados-itunes' ORDER BY ts DESC LIMIT 5;`).
77+
78+## Licence, attribution & conditions
79+
80+- **Cadre d'accès (registre)** : API de recherche iTunes/Apple Podcasts (officielle, gratuite, sans clé)
81+- **API publique** utilisée selon ses conditions (pas de clé détournée, throttling poli) ; données limitées aux profils publics.
82+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
83+
84+## Historique
85+
86+- 2026-08-17 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
87+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/balados-rss.md +73 −0
@@ -0,0 +1,73 @@
1+# `balados-rss` — connecteur enrichissement (palier 1)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT « balados-rss » — lecture directe des flux RSS auto-déclarés des balados (AUCUNE clé requise). Palier 1 (§9). Cap 150 flux/passage, re-visite 7 j, timeout court, fetch parallèle poli (les flux vivent presque tous sur des hôtes différents).
10+
11+- **Notes (registre)** : nb d'épisodes réels, date du dernier épisode (détection dormant >12 mois), pochette → avatar, description → bio, catégories iTunes → niches, <link> → compte site-web/plateforme. Substitut sans clé de Podcast Index.
12+- **Signal d'identité** : cross_link (0.90) pour le lien officiel <link> du flux (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/balados_rss.py` — classe `BaladosRssConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : requêtes directes (flux RSS auto-déclarés des balados — AUCUNE clé) ; cap 150 flux/passage, re-visite 7 j, timeout court, fetch parallèle poli
18+- **Type d'accès (code)** : flux RSS publics
19+- **Endpoint de base** : http://www.itunes.com/dtds/podcast-1.0.dtd}
20+- **URLs du module** : http://www.itunes.com/dtds/podcast-1.0.dtd}
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli)
23+- **Politesse** : 0.2 s entre requêtes, timeout 12 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : clé(s) API requise(s) — voir statut/registre
25+
26+## Champs récupérés → schéma cible
27+
28+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `podcast` (434 comptes en BD, confiance moyenne 0.98).
29+
30+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
31+|---|---|---|---|
32+| `handle` | Handle | 100 % | — |
33+| `url` | URL du profil | 100 % | — |
34+| `followers` | Abonnés | 0 % | — |
35+| `verified` | Badge vérifié | 0 % | — |
36+| `confidence` | Confiance d'identité | 100 % | — |
37+| `signal` | Signal d'identité | 100 % | — |
38+| `metrics` | Métriques détaillées (JSON) | 100 % | — |
39+| `last_checked` | Dernière vérification | 100 % | — |
40+
41+## Fréquence & budget
42+
43+- **Cadence déclarée (registre)** : quotidienne (watch) — rotation complète des 433 balados en ~3 jours, puis hebdomadaire
44+- **Cadence observée** (médiane sync_log) : ≈ 17 min
45+- **Dernier passage** : 2026-08-18T06:17:19Z — 8 créateurs, 36 comptes, +0 / ~8, 5 erreur(s), 12 s
46+- **Throttling** : 0.2 s entre requêtes (`request_delay`)
47+
48+## Volumétrie & complétude
49+
50+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
51+- **Comptes `podcast` en BD** : 434 — abonnés 0 % · métriques 100 % · dernière vérification 2026-08-18T06:23:00Z
52+- **Runs journalisés (60 derniers)** : 4, dont 3 avec erreurs
53+
54+## Erreurs connues & dépannage
55+
56+| Passage | Erreurs | Alerte (sync_log) |
57+|---|---|---|
58+| 2026-08-18T06:17:19Z | 5 | — |
59+| 2026-08-18T06:17:07Z | 5 | — |
60+| 2026-08-18T06:16:42Z | 5 | — |
61+
62+Rejouer la source seule : `python3 run.py sync balados-rss` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='balados-rss' ORDER BY ts DESC LIMIT 5;`).
63+
64+## Licence, attribution & conditions
65+
66+- **Cadre d'accès (registre)** : requêtes directes (flux RSS auto-déclarés des balados — AUCUNE clé) ; cap 150 flux/passage, re-visite 7 j, timeout court, fetch parallèle poli
67+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
68+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
69+
70+## Historique
71+
72+- 2026-08-18 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
73+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/bio-liens.md +57 −0
@@ -0,0 +1,57 @@
1+# `bio-liens` — connecteur enrichissement (palier 1)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT « bio-liens » — découverte croisée inter-plateformes SANS AUCUNE requête : liaison conservatrice à partir des URLs COMPLÈTES déjà stockées dans les bios publiques. Palier 1 (§9) — passe en dernier (après link-in-bio).
10+
11+- **Notes (registre)** : passe conservatrice §12 : patrons sûrs seulement, cap 5 nouveaux comptes par fiche
12+- **Signal d'identité** : cross_link (0.90) — le créateur pointe lui-même vers son autre compte (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/bio_liens.py` — classe `BioLiensConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : AUCUNE requête — liaison croisée à partir des URLs COMPLÈTES déjà stockées dans les bios publiques (jamais de « @handle » textuel ambigu)
18+- **Type d'accès (code)** : pages HTML publiques
19+- **Endpoint de base** : —
20+- **Pagination** : réponse unique (pas de pagination)
21+- **Backend anti-bot / rendu** : requests direct
22+- **Politesse** : 0.0 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
23+- **Authentification** : aucune — contenu public / API anonyme
24+
25+## Champs récupérés → schéma cible
26+
27+Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`.
28+
29+## Fréquence & budget
30+
31+- **Cadence déclarée (registre)** : quotidienne (watch), en dernier du pipeline
32+- **Cadence observée** (médiane sync_log) : —
33+- **Dernier passage** : 2026-08-18T06:17:45Z — 26 créateurs, 63 comptes, +0 / ~26, 0 erreur(s), 0 s
34+- **Caps / budgets du module** : `MAX_NEW_PER_CREATOR` = 5
35+- **Throttling** : 0.0 s entre requêtes (`request_delay`)
36+
37+## Volumétrie & complétude
38+
39+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
40+- **Runs journalisés (60 derniers)** : 1, dont 0 avec erreurs
41+
42+## Erreurs connues & dépannage
43+
44+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
45+
46+Rejouer la source seule : `python3 run.py sync bio-liens` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='bio-liens' ORDER BY ts DESC LIMIT 5;`).
47+
48+## Licence, attribution & conditions
49+
50+- **Cadre d'accès (registre)** : AUCUNE requête — liaison croisée à partir des URLs COMPLÈTES déjà stockées dans les bios publiques (jamais de « @handle » textuel ambigu)
51+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
52+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
53+
54+## Historique
55+
56+- 2026-08-18 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
57+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/instagram-profil.md +69 −0
@@ -0,0 +1,69 @@
1+# `instagram-profil` — connecteur enrichissement (palier 4)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : enrichissement (enrichment) · Backend : Scrapfly · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT Instagram — profil public par créateur : abonnés, badge vérifié, bio, liens de bio (→ découverte croisée des autres plateformes + page link-in-bio). Mode d'accès : Scrapfly (plateforme hostile au scraping, pas d'API publique de lecture) — palier 4 du catalogue (§9), CGU/rate-limit respectés (§15).
10+
11+- **Notes (registre)** : abonnés, badge, bio, liens de bio → link_in_bio_url + comptes croisés
12+- **Signal d'identité** : cross_link (0.90) pour les liens de bio découverts (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/instagram.py` — classe `InstagramConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : Scrapfly (profil public web_profile_info — pas d'API publique de lecture)
18+- **Type d'accès (code)** : pages HTML publiques
19+- **Endpoint de base** : https://i.instagram.com/api/v1/users/web_profile_info/
20+- **URLs du module** : https://i.instagram.com/api/v1/users/web_profile_info/
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : Scrapfly (asp + render_js — contournement anti-bot)
23+- **Politesse** : 1.5 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : aucune — contenu public / API anonyme
25+
26+## Champs récupérés → schéma cible
27+
28+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `instagram` (460 comptes en BD, confiance moyenne 0.88).
29+
30+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
31+|---|---|---|---|
32+| `handle` | Handle | 100 % | catherinepaiz |
33+| `url` | URL du profil | 100 % | https://www.instagram.com/catherinepaiz/ |
34+| `followers` | Abonnés | 72 % | 7023891 |
35+| `verified` | Badge vérifié | 59 % | 1 |
36+| `confidence` | Confiance d'identité | 100 % | 0.85 |
37+| `signal` | Signal d'identité | 100 % | base_publique |
38+| `metrics` | Métriques détaillées (JSON) | 59 % | {"following": 248, "posts": 667, "is_business": false} |
39+| `last_checked` | Dernière vérification | 100 % | 2026-08-18T06:27:51Z |
40+
41+## Fréquence & budget
42+
43+- **Cadence déclarée (registre)** : hebdomadaire (gros créateurs) / mensuelle
44+- **Cadence observée** (médiane sync_log) : ≈ 87 min
45+- **Dernier passage** : 2026-08-17T23:23:45Z — 271 créateurs, 542 comptes, +0 / ~271, 0 erreur(s), 0 s
46+- **Throttling** : 1.5 s entre requêtes (`request_delay`)
47+
48+## Volumétrie & complétude
49+
50+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
51+- **Comptes `instagram` en BD** : 460 — abonnés 72 % · métriques 59 % · dernière vérification 2026-08-18T06:27:51Z
52+- **Runs journalisés (60 derniers)** : 3, dont 0 avec erreurs
53+
54+## Erreurs connues & dépannage
55+
56+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
57+
58+Rejouer la source seule : `python3 run.py sync instagram-profil` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='instagram-profil' ORDER BY ts DESC LIMIT 5;`).
59+
60+## Licence, attribution & conditions
61+
62+- **Cadre d'accès (registre)** : Scrapfly (profil public web_profile_info — pas d'API publique de lecture)
63+- **API publique** utilisée selon ses conditions (pas de clé détournée, throttling poli) ; données limitées aux profils publics.
64+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
65+
66+## Historique
67+
68+- 2026-08-17 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
69+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/link-in-bio.md +56 −0
@@ -0,0 +1,56 @@
1+# `link-in-bio` — connecteur enrichissement (palier 1)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : enrichissement (enrichment) · Backend : Firecrawl · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT générique « link-in-bio » (templatisé) — Linktree, Beacons, Bio.link, Milkshake, Campsite, Snipfeed. Mode d'accès : requêtes directes (pages publiques SSR), repli Firecrawl. Palier 1 du catalogue (§9) — le levier nº 1 : une page de liens révèle D'UN COUP tous les comptes d'un créateur.
10+
11+- **Notes (registre)** : une page → TOUS les comptes du créateur ; consomme les link_in_bio_url découvertes par instagram-profil/tiktok-profil
12+- **Signal d'identité** : link_in_bio (0.95) — le signal le plus fort (§12.1) (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/linkinbio.py` — classe `LinkInBioConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : requêtes directes (pages publiques SSR : linktr.ee, beacons.ai, bio.link, msha.ke, campsite.bio, snipfeed.co), repli Firecrawl
18+- **Type d'accès (code)** : pages HTML publiques
19+- **Endpoint de base** : —
20+- **Pagination** : réponse unique (pas de pagination)
21+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli) ; Firecrawl (HTML rendu, JavaScript exécuté)
22+- **Politesse** : 1.2 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
23+- **Authentification** : aucune — contenu public / API anonyme
24+
25+## Champs récupérés → schéma cible
26+
27+Aucune donnée attribuable à cette source en BD pour l'instant (clés manquantes ou source en attente) — schéma cible : `creators` + `accounts`.
28+
29+## Fréquence & budget
30+
31+- **Cadence déclarée (registre)** : mensuelle
32+- **Cadence observée** (médiane sync_log) : —
33+- **Dernier passage** : 2026-08-18T06:20:19Z — 74 créateurs, 288 comptes, +0 / ~74, 0 erreur(s), 114 s
34+- **Throttling** : 1.2 s entre requêtes (`request_delay`)
35+
36+## Volumétrie & complétude
37+
38+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
39+- **Runs journalisés (60 derniers)** : 2, dont 0 avec erreurs
40+
41+## Erreurs connues & dépannage
42+
43+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
44+
45+Rejouer la source seule : `python3 run.py sync link-in-bio` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='link-in-bio' ORDER BY ts DESC LIMIT 5;`).
46+
47+## Licence, attribution & conditions
48+
49+- **Cadre d'accès (registre)** : requêtes directes (pages publiques SSR : linktr.ee, beacons.ai, bio.link, msha.ke, campsite.bio, snipfeed.co), repli Firecrawl
50+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
51+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
52+
53+## Historique
54+
55+- 2026-08-18 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
56+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/listes-medias.md +72 −0
@@ -0,0 +1,72 @@
1+# `listes-medias` — connecteur decouverte (palier 3)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 310 · touchés : 310
6+
7+## Description de la source
8+
9+Connecteur DÉCOUVERTE — listes & palmarès médias québécois compilés (dataset local data/seed/qc_influenceurs.json, croisement de listes publiques : billie, frank, metricool, Favikon QC…). Mode d'accès : dataset local (aucune requête réseau). Palier 3 du catalogue (§9).
10+
11+- **Notes (registre)** : 376 créateurs québécois, handles par plateforme + audiences approximatives
12+- **Signal d'identité** : listes_medias (0.85) (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/listes_medias.py` — classe `ListesMediasConnector` (`kind = "discovery"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : dataset local (listes & palmarès médias publics compilés : billie, frank, metricool, Favikon QC, canadult…)
18+- **Type d'accès (code)** : dataset local compilé (data/seed)
19+- **Endpoint de base** : —
20+- **Pagination** : réponse unique (pas de pagination)
21+- **Backend anti-bot / rendu** : requests direct
22+- **Politesse** : 0.8 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
23+- **Authentification** : aucune — contenu public / API anonyme
24+
25+## Champs récupérés → schéma cible
26+
27+Source de **découverte** : produit des fiches `Creator` (tables `creators` + `accounts`). Complétude mesurée sur les 310 créateurs découverts par cette source (champ `doc.source`) ; exemple tiré d'une fiche réelle.
28+
29+| Champ du doc créateur | Contenu | Renseigné | Exemple réel |
30+|---|---|---|---|
31+| `display_name` | Nom public | 100 % | Enola Bédard |
32+| `bio` | Bio | 66 % | 📍Los Angeles 📩: [retiré] Tik Tok 17M Youtube 5M |
33+| `region` | Région | 49 % | — |
34+| `city` | Ville | 49 % | — |
35+| `niches` | Niches (liste) | 100 % | danse, lifestyle |
36+| `languages` | Langues (liste) | 100 % | en, fr |
37+| `creator_type` | Type de créateur | 100 % | createur-tiktok |
38+| `primary_platform` | Plateforme principale | 100 % | tiktok |
39+| `platforms` | Comptes sociaux (liste) | 100 % | tiktok:@enola.bedard, youtube:@enola.bedard, instagram:@enola.bedard |
40+| `avatar_url` | Avatar | 68 % | https://scontent-lga3-2.cdninstagram.com/v/t51.82787-19/515154150_18510310135012441_32506… |
41+| `total_reach` | Portée totale (abonnés cumulés) | 90 % | 24500000 |
42+| `link_in_bio_url` | Lien-en-bio | 23 % | — |
43+
44+## Fréquence & budget
45+
46+- **Cadence déclarée (registre)** : mensuelle (recompilation du dataset)
47+- **Cadence observée** (médiane sync_log) : ≈ 22 min
48+- **Dernier passage** : 2026-08-18T06:23:00Z — 310 créateurs, 448 comptes, +0 / ~310, 0 erreur(s), 0 s
49+- **Throttling** : 0.8 s entre requêtes (`request_delay`)
50+
51+## Volumétrie & complétude
52+
53+- **Créateurs découverts par la source** (`doc.source`) : 310 · **fiches touchées** (`source_ids`) : 310
54+- **Complétude clé (découverts)** : niches 100 % · avatar 68 % · portée 90 % · région 49 %
55+- **Runs journalisés (60 derniers)** : 11, dont 0 avec erreurs
56+
57+## Erreurs connues & dépannage
58+
59+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
60+
61+Rejouer la source seule : `python3 run.py sync listes-medias` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='listes-medias' ORDER BY ts DESC LIMIT 5;`).
62+
63+## Licence, attribution & conditions
64+
65+- **Cadre d'accès (registre)** : dataset local (listes & palmarès médias publics compilés : billie, frank, metricool, Favikon QC, canadult…)
66+- **Données compilées** de listes et palmarès médias **publics** ; seuls des faits publics (nom, handle, audience approximative) sont conservés.
67+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
68+
69+## Historique
70+
71+- 2026-08-17 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
72+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/onlyqueb.md +87 −0
@@ -0,0 +1,87 @@
1+# `onlyqueb` — connecteur decouverte (palier 3)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 253 · touchés : 253
6+
7+## Description de la source
8+
9+Connecteur DÉCOUVERTE — OnlyQueb (onlyqueb.com), annuaire public de créatrices/créateurs québécois sur abonnement. Mode d'accès : requêtes directes (sitemap.xml + JSON-LD des pages profil, SSR). Palier 3 (§9). Les liens `sameAs` sont AUTO-DÉCLARÉS par la personne sur son profil → signal cross_link (0.90, §12.1).
10+
11+- **Notes (registre)** : annuaire public onlyqueb.com — créateurs québécois sur abonnement (18+) ; nom public, photo, liens OnlyFans/Instagram/TikTok/X
12+- **Signal d'identité** : cross_link (0.90) — liens sameAs auto-déclarés par la personne (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/onlyqueb.py` — classe `OnlyQuebConnector` (`kind = "discovery"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : requêtes directes (sitemap.xml + JSON-LD ProfilePage des pages profil publiques SSR)
18+- **Type d'accès (code)** : sitemap XML + JSON-LD des pages profil (SSR)
19+- **Endpoint de base** : https://onlyqueb.com/sitemap.xml
20+- **URLs du module** : https://onlyqueb.com/sitemap.xml
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli)
23+- **Politesse** : 1.0 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : aucune — contenu public / API anonyme
25+
26+## Champs récupérés → schéma cible
27+
28+Source de **découverte** : produit des fiches `Creator` (tables `creators` + `accounts`). Complétude mesurée sur les 253 créateurs découverts par cette source (champ `doc.source`) ; exemple tiré d'une fiche réelle.
29+
30+| Champ du doc créateur | Contenu | Renseigné | Exemple réel |
31+|---|---|---|---|
32+| `display_name` | Nom public | 100 % | Jade Lavoie |
33+| `bio` | Bio | 98 % | Deviens mon prochain acteur! 🔥 Je fais littéralement TOUT. Demande ce que tu veux et trou… |
34+| `region` | Région | 0 % | — |
35+| `city` | Ville | 0 % | — |
36+| `niches` | Niches (liste) | 100 % | autre |
37+| `languages` | Langues (liste) | 100 % | fr |
38+| `creator_type` | Type de créateur | 100 % | influenceur |
39+| `primary_platform` | Plateforme principale | 100 % | instagram |
40+| `platforms` | Comptes sociaux (liste) | 100 % | instagram:@jadelavoie, x:@missjadelavoie, onlyfans:@misslavoie |
41+| `avatar_url` | Avatar | 100 % | https://storage.googleapis.com/onlyqueb/MQZBiXsbMQa31zxj9P_mabNxNITFBxa6 |
42+| `total_reach` | Portée totale (abonnés cumulés) | 16 % | 1571113 |
43+| `link_in_bio_url` | Lien-en-bio | 2 % | — |
44+
45+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `onlyfans` (248 comptes en BD, confiance moyenne 0.90).
46+
47+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
48+|---|---|---|---|
49+| `handle` | Handle | 100 % | — |
50+| `url` | URL du profil | 100 % | — |
51+| `followers` | Abonnés | 0 % | — |
52+| `verified` | Badge vérifié | 0 % | — |
53+| `confidence` | Confiance d'identité | 100 % | — |
54+| `signal` | Signal d'identité | 100 % | — |
55+| `metrics` | Métriques détaillées (JSON) | 0 % | — |
56+| `last_checked` | Dernière vérification | 100 % | — |
57+
58+## Fréquence & budget
59+
60+- **Cadence déclarée (registre)** : hebdomadaire
61+- **Cadence observée** (médiane sync_log) : ≈ 24 min
62+- **Dernier passage** : 2026-08-18T06:27:32Z — 253 créateurs, 611 comptes, +0 / ~253, 0 erreur(s), 272 s
63+- **Throttling** : 1.0 s entre requêtes (`request_delay`)
64+
65+## Volumétrie & complétude
66+
67+- **Créateurs découverts par la source** (`doc.source`) : 253 · **fiches touchées** (`source_ids`) : 253
68+- **Complétude clé (découverts)** : niches 100 % · avatar 100 % · portée 16 % · région 0 %
69+- **Comptes `onlyfans` en BD** : 248 — abonnés 0 % · métriques 0 % · dernière vérification 2026-08-18T06:27:32Z
70+- **Runs journalisés (60 derniers)** : 7, dont 0 avec erreurs
71+
72+## Erreurs connues & dépannage
73+
74+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
75+
76+Rejouer la source seule : `python3 run.py sync onlyqueb` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='onlyqueb' ORDER BY ts DESC LIMIT 5;`).
77+
78+## Licence, attribution & conditions
79+
80+- **Cadre d'accès (registre)** : requêtes directes (sitemap.xml + JSON-LD ProfilePage des pages profil publiques SSR)
81+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
82+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
83+
84+## Historique
85+
86+- 2026-08-17 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
87+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/podcastindex.md +69 −0
@@ -0,0 +1,69 @@
1+# `podcastindex` — connecteur enrichissement (palier 2)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : prêt — clés requises** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT « podcastindex » — API Podcast Index (OFFICIELLE, gratuite ; clé + secret requis, inscription gratuite : https://api.podcastindex.org). Palier 2 (§9, §10). Sans clés : passage sauté PROPREMENT (SkipSource, comme twitch).
10+
11+- **Notes (registre)** : episodeCount, lastUpdateTime, itunesId, image, catégories des balados par feed_url. En attendant les clés, balados-rss couvre les mêmes champs sans clé.
12+- **Signal d'identité** : api_officielle (0.95) (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/podcastindex.py` — classe `PodcastIndexConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : API Podcast Index (officielle, GRATUITE — inscription requise : https://api.podcastindex.org ; PODCASTINDEX_API_KEY/SECRET dans .env) — sauté proprement sans clés
18+- **Type d'accès (code)** : API JSON
19+- **Endpoint de base** : https://api.podcastindex.org/api/1.0/podcasts/byfeedurl
20+- **URLs du module** : https://api.podcastindex.org · https://api.podcastindex.org/api/1.0/podcasts/byfeedurl
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli)
23+- **Politesse** : 0.4 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : clé(s) API requise(s) — voir statut/registre
25+
26+## Champs récupérés → schéma cible
27+
28+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `podcast` (434 comptes en BD, confiance moyenne 0.98).
29+
30+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
31+|---|---|---|---|
32+| `handle` | Handle | 100 % | — |
33+| `url` | URL du profil | 100 % | — |
34+| `followers` | Abonnés | 0 % | — |
35+| `verified` | Badge vérifié | 0 % | — |
36+| `confidence` | Confiance d'identité | 100 % | — |
37+| `signal` | Signal d'identité | 100 % | — |
38+| `metrics` | Métriques détaillées (JSON) | 100 % | — |
39+| `last_checked` | Dernière vérification | 100 % | — |
40+
41+## Fréquence & budget
42+
43+- **Cadence déclarée (registre)** : quotidienne (watch), re-visite 7 j par balado
44+- **Cadence observée** (médiane sync_log) : —
45+- **Throttling** : 0.4 s entre requêtes (`request_delay`)
46+
47+## Volumétrie & complétude
48+
49+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
50+- **Comptes `podcast` en BD** : 434 — abonnés 0 % · métriques 100 % · dernière vérification 2026-08-18T06:23:00Z
51+- **Runs journalisés (60 derniers)** : 0, dont 0 avec erreurs
52+
53+## Erreurs connues & dépannage
54+
55+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
56+
57+**Statut du registre** : prêt — clés requises
58+
59+Rejouer la source seule : `python3 run.py sync podcastindex` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='podcastindex' ORDER BY ts DESC LIMIT 5;`).
60+
61+## Licence, attribution & conditions
62+
63+- **Cadre d'accès (registre)** : API Podcast Index (officielle, GRATUITE — inscription requise : https://api.podcastindex.org ; PODCASTINDEX_API_KEY/SECRET dans .env) — sauté proprement sans clés
64+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
65+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
66+
67+## Historique
68+
69+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/tiktok-profil.md +69 −0
@@ -0,0 +1,69 @@
1+# `tiktok-profil` — connecteur enrichissement (palier 4)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : enrichissement (enrichment) · Backend : Scrapfly · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT TikTok — profil public par créateur : abonnés, badge vérifié, bio, lien de bio (découverte croisée). Mode d'accès : Scrapfly (plateforme hostile au scraping, API officielle limitée aux comptes connectés) — palier 4 (§9), §15.
10+
11+- **Notes (registre)** : abonnés, badge, bio, lien de bio
12+- **Signal d'identité** : cross_link (0.90) pour le lien de bio (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/tiktok.py` — classe `TikTokConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : Scrapfly (JSON d'hydratation de la page publique @handle)
18+- **Type d'accès (code)** : pages HTML publiques
19+- **Endpoint de base** : https://www.tiktok.com/@{h}
20+- **URLs du module** : https://www.tiktok.com/@{h}
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : Scrapfly (asp + render_js — contournement anti-bot)
23+- **Politesse** : 1.5 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : aucune — contenu public / API anonyme
25+
26+## Champs récupérés → schéma cible
27+
28+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `tiktok` (197 comptes en BD, confiance moyenne 0.90).
29+
30+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
31+|---|---|---|---|
32+| `handle` | Handle | 100 % | enola.bedard |
33+| `url` | URL du profil | 100 % | https://www.tiktok.com/@enola.bedard |
34+| `followers` | Abonnés | 53 % | 17000000 |
35+| `verified` | Badge vérifié | 35 % | 1 |
36+| `confidence` | Confiance d'identité | 100 % | 0.85 |
37+| `signal` | Signal d'identité | 100 % | base_publique |
38+| `metrics` | Métriques détaillées (JSON) | 35 % | {"following": 813, "likes": 438100000, "videos": 2847} |
39+| `last_checked` | Dernière vérification | 100 % | 2026-08-18T06:27:51Z |
40+
41+## Fréquence & budget
42+
43+- **Cadence déclarée (registre)** : hebdomadaire (gros créateurs) / mensuelle
44+- **Cadence observée** (médiane sync_log) : —
45+- **Dernier passage** : 2026-08-18T00:31:47Z — 68 créateurs, 196 comptes, +0 / ~68, 0 erreur(s), 0 s
46+- **Throttling** : 1.5 s entre requêtes (`request_delay`)
47+
48+## Volumétrie & complétude
49+
50+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
51+- **Comptes `tiktok` en BD** : 197 — abonnés 53 % · métriques 35 % · dernière vérification 2026-08-18T06:27:51Z
52+- **Runs journalisés (60 derniers)** : 1, dont 0 avec erreurs
53+
54+## Erreurs connues & dépannage
55+
56+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
57+
58+Rejouer la source seule : `python3 run.py sync tiktok-profil` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='tiktok-profil' ORDER BY ts DESC LIMIT 5;`).
59+
60+## Licence, attribution & conditions
61+
62+- **Cadre d'accès (registre)** : Scrapfly (JSON d'hydratation de la page publique @handle)
63+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
64+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
65+
66+## Historique
67+
68+- 2026-08-18 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
69+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/twitch.md +71 −0
@@ -0,0 +1,71 @@
1+# `twitch` — connecteur enrichissement (palier 2)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT Twitch — existence + profil par créateur via l'API OFFICIELLE Helix (client_credentials). Palier 2 (§9, §10). Sans TWITCH_CLIENT_ID/TWITCH_CLIENT_SECRET : passage sauté proprement.
10+
11+- **Notes (registre)** : existence, type de diffuseur, description
12+- **Signal d'identité** : api_officielle (0.95) (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/twitch.py` — classe `TwitchConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : API officielle Helix (client_credentials) — sauté sans clés
18+- **Type d'accès (code)** : API JSON
19+- **Endpoint de base** : https://id.twitch.tv/oauth2/token
20+- **URLs du module** : https://id.twitch.tv/oauth2/token · https://api.twitch.tv/helix/users
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli)
23+- **Politesse** : 0.5 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : clé(s) API requise(s) — voir statut/registre
25+
26+## Champs récupérés → schéma cible
27+
28+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `twitch` (26 comptes en BD, confiance moyenne 0.89).
29+
30+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
31+|---|---|---|---|
32+| `handle` | Handle | 100 % | heyraion |
33+| `url` | URL du profil | 100 % | https://www.twitch.tv/heyraion |
34+| `followers` | Abonnés | 15 % | 37900 |
35+| `verified` | Badge vérifié | 0 % | — |
36+| `confidence` | Confiance d'identité | 100 % | 0.85 |
37+| `signal` | Signal d'identité | 100 % | listes_medias |
38+| `metrics` | Métriques détaillées (JSON) | 0 % | — |
39+| `last_checked` | Dernière vérification | 100 % | 2026-08-18T06:23:00Z |
40+
41+## Fréquence & budget
42+
43+- **Cadence déclarée (registre)** : hebdomadaire
44+- **Cadence observée** (médiane sync_log) : —
45+- **Dernier passage** : 2026-08-18T01:06:19Z — 0 créateurs, 0 comptes, +0 / ~0, 0 erreur(s), 0 s
46+- **Throttling** : 0.5 s entre requêtes (`request_delay`)
47+
48+## Volumétrie & complétude
49+
50+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
51+- **Comptes `twitch` en BD** : 26 — abonnés 15 % · métriques 0 % · dernière vérification 2026-08-18T06:27:51Z
52+- **Runs journalisés (60 derniers)** : 1, dont 0 avec erreurs
53+
54+## Erreurs connues & dépannage
55+
56+| Passage | Erreurs | Alerte (sync_log) |
57+|---|---|---|
58+| 2026-08-18T01:06:19Z | 0 | 0 créateur retourné — source possiblement bloquée |
59+
60+Rejouer la source seule : `python3 run.py sync twitch` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='twitch' ORDER BY ts DESC LIMIT 5;`).
61+
62+## Licence, attribution & conditions
63+
64+- **Cadre d'accès (registre)** : API officielle Helix (client_credentials) — sauté sans clés
65+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
66+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
67+
68+## Historique
69+
70+- 2026-08-18 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
71+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/wikidata-qc.md +75 −0
@@ -0,0 +1,75 @@
1+# `wikidata-qc` — connecteur decouverte (palier 3)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 73 · touchés : 73
6+
7+## Description de la source
8+
9+Connecteur DÉCOUVERTE — créateurs québécois recensés dans Wikidata (personnes nées ou résidant au Québec, occupation créateur, avec handles sociaux curés P2003/P7085/P2397/P5797/P2002). Mode d'accès : API SPARQL publique de Wikidata (gratuite, conforme). Palier 3 (§9).
10+
11+- **Notes (registre)** : humoristes, youtubeurs, vidéastes web, streamers, podcasteurs notables ; is_minor levé via l'année de naissance publique
12+- **Signal d'identité** : base_publique (0.85) (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/wikidata_qc.py` — classe `WikidataQcConnector` (`kind = "discovery"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : API SPARQL publique Wikidata (nés/résidant au Québec, occupation créateur, handles sociaux curés)
18+- **Type d'accès (code)** : API SPARQL publique (Wikidata)
19+- **Endpoint de base** : https://query.wikidata.org/sparql
20+- **URLs du module** : https://query.wikidata.org/sparql · https://www.youtube.com/channel/{handle}
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli)
23+- **Politesse** : 2.0 s entre requêtes, timeout 90 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : aucune — contenu public / API anonyme
25+
26+## Champs récupérés → schéma cible
27+
28+Source de **découverte** : produit des fiches `Creator` (tables `creators` + `accounts`). Complétude mesurée sur les 73 créateurs découverts par cette source (champ `doc.source`) ; exemple tiré d'une fiche réelle.
29+
30+| Champ du doc créateur | Contenu | Renseigné | Exemple réel |
31+|---|---|---|---|
32+| `display_name` | Nom public | 100 % | Nigel Braun |
33+| `bio` | Bio | 60 % | Capturing the natural beauty of chemistry Find me on YouTube: @NileRed/@NileBlue |
34+| `region` | Région | 0 % | — |
35+| `city` | Ville | 0 % | — |
36+| `niches` | Niches (liste) | 100 % | lifestyle |
37+| `languages` | Langues (liste) | 100 % | fr |
38+| `creator_type` | Type de créateur | 100 % | influenceur |
39+| `primary_platform` | Plateforme principale | 100 % | youtube |
40+| `platforms` | Comptes sociaux (liste) | 99 % | youtube:@nilered, instagram:@nile.red, x:@nilered2 |
41+| `avatar_url` | Avatar | 63 % | https://scontent-yyz1-1.cdninstagram.com/v/t51.2885-19/441008830_1124225362150102_4410959… |
42+| `total_reach` | Portée totale (abonnés cumulés) | 63 % | 12245225 |
43+| `link_in_bio_url` | Lien-en-bio | 19 % | — |
44+
45+## Fréquence & budget
46+
47+- **Cadence déclarée (registre)** : hebdomadaire
48+- **Cadence observée** (médiane sync_log) : ≈ 30 min
49+- **Dernier passage** : 2026-08-18T06:27:51Z — 83 créateurs, 172 comptes, +0 / ~83, 0 erreur(s), 18 s
50+- **Throttling** : 2.0 s entre requêtes (`request_delay`)
51+
52+## Volumétrie & complétude
53+
54+- **Créateurs découverts par la source** (`doc.source`) : 73 · **fiches touchées** (`source_ids`) : 73
55+- **Complétude clé (découverts)** : niches 100 % · avatar 63 % · portée 63 % · région 0 %
56+- **Runs journalisés (60 derniers)** : 9, dont 1 avec erreurs
57+
58+## Erreurs connues & dépannage
59+
60+| Passage | Erreurs | Alerte (sync_log) |
61+|---|---|---|
62+| 2026-08-18T05:55:35Z | 1 | — |
63+
64+Rejouer la source seule : `python3 run.py sync wikidata-qc` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='wikidata-qc' ORDER BY ts DESC LIMIT 5;`).
65+
66+## Licence, attribution & conditions
67+
68+- **Cadre d'accès (registre)** : API SPARQL publique Wikidata (nés/résidant au Québec, occupation créateur, handles sociaux curés)
69+- **Licence** : données Wikidata sous CC0 — réutilisation libre, mention « Source : Wikidata » affichée par courtoisie.
70+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
71+
72+## Historique
73+
74+- 2026-08-17 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
75+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/x-profil.md +74 −0
@@ -0,0 +1,74 @@
1+# `x-profil` — connecteur enrichissement (palier 3)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT « x-profil » — abonnés X (Twitter) SANS clé via le service public de syndication (widgets d'intégration officiels) syndication.twitter.com/srv/timeline-profile/screen-name/{handle}, repli page publique x.com/{handle}. Palier 3 (§9). Facebook sondé aussi : page publique bloquée sans clé (HTTP 400) → aucune voie fiable, on passe (documenté dans data/sources.json).
10+
11+- **Notes (registre)** : abonnés + abonnements + badge + avatar X. Ancien widget followbutton MORT (vérifié 2026-08-18 : réponse vide) ; page x.com : mur de connexion après ~15-20 req/IP (constaté 2026-08-18) → repli seulement. Facebook : page publique bloquée sans clé (HTTP 400, vérifié 2026-08-18) et Scrapfly non justifié → aucune voie fiable, on passe.
12+- **Signal d'identité** : aucun nouveau rattachement — métriques du compte déjà rattaché seulement (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/x_profil.py` — classe `XProfilConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : requêtes directes (service public de syndication des widgets officiels : syndication.twitter.com/srv/timeline-profile — objet user complet dans __NEXT_DATA__ ; repli très limité page x.com) ; cap 150/passage, re-visite 7 j, délai 2 s
18+- **Type d'accès (code)** : pages HTML publiques
19+- **Endpoint de base** : https://syndication.twitter.com/srv/timeline-profile/
20+- **URLs du module** : https://syndication.twitter.com/srv/timeline-profile/ · https://x.com/{h}
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : requests direct
23+- **Politesse** : 2.0 s entre requêtes, timeout 15 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : aucune — contenu public / API anonyme
25+
26+## Champs récupérés → schéma cible
27+
28+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `x` (186 comptes en BD, confiance moyenne 0.89).
29+
30+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
31+|---|---|---|---|
32+| `handle` | Handle | 100 % | xqc |
33+| `url` | URL du profil | 100 % | https://x.com/xqc |
34+| `followers` | Abonnés | 10 % | 1598794 |
35+| `verified` | Badge vérifié | 0 % | — |
36+| `confidence` | Confiance d'identité | 100 % | 0.9 |
37+| `signal` | Signal d'identité | 100 % | cross_link |
38+| `metrics` | Métriques détaillées (JSON) | 9 % | {"following": 526, "x_checked": "2026-08-18T06:14:55Z"} |
39+| `last_checked` | Dernière vérification | 100 % | 2026-08-18T06:27:51Z |
40+
41+## Fréquence & budget
42+
43+- **Cadence déclarée (registre)** : quotidienne (watch), rotation hebdomadaire
44+- **Cadence observée** (médiane sync_log) : ≈ 6 min
45+- **Dernier passage** : 2026-08-18T06:15:49Z — 9 créateurs, 35 comptes, +0 / ~9, 1 erreur(s), 68 s
46+- **Throttling** : 2.0 s entre requêtes (`request_delay`)
47+
48+## Volumétrie & complétude
49+
50+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
51+- **Comptes `x` en BD** : 186 — abonnés 10 % · métriques 9 % · dernière vérification 2026-08-18T06:27:51Z
52+- **Runs journalisés (60 derniers)** : 3, dont 3 avec erreurs
53+
54+## Erreurs connues & dépannage
55+
56+| Passage | Erreurs | Alerte (sync_log) |
57+|---|---|---|
58+| 2026-08-18T06:15:49Z | 1 | — |
59+| 2026-08-18T06:12:35Z | 171 | — |
60+| 2026-08-18T06:02:56Z | 1 | — |
61+
62+Rejouer la source seule : `python3 run.py sync x-profil` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='x-profil' ORDER BY ts DESC LIMIT 5;`).
63+
64+## Licence, attribution & conditions
65+
66+- **Cadre d'accès (registre)** : requêtes directes (service public de syndication des widgets officiels : syndication.twitter.com/srv/timeline-profile — objet user complet dans __NEXT_DATA__ ; repli très limité page x.com) ; cap 150/passage, re-visite 7 j, délai 2 s
67+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
68+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
69+
70+## Historique
71+
72+- 2026-08-18 — date mentionnée au registre (voir notes/statut).
73+- 2026-08-18 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
74+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/youtube-recherche.md +87 −0
@@ -0,0 +1,87 @@
1+# `youtube-recherche` — connecteur decouverte (palier 3)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : decouverte (discovery) · Backend : direct · Créateurs découverts : 4595 · touchés : 4595
6+
7+## Description de la source
8+
9+Connecteur DÉCOUVERTE — chaînes YouTube québécoises via la recherche publique (filtre « chaînes »), requêtes thématiques × marqueurs QC. Mode d'accès : requêtes directes (HTML public, ytInitialData). Palier 3 (§9). Filtre conservateur : marqueur québécois OBLIGATOIRE dans le titre ou la description de la chaîne.
10+
11+- **Notes (registre)** : filtre conservateur : marqueur québécois requis dans titre/description de la chaîne
12+- **Signal d'identité** : profil_source (0.98) — la chaîne est le compte d'origine de la fiche (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/youtube_recherche.py` — classe `YouTubeRechercheConnector` (`kind = "discovery"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : requêtes directes (recherche publique YouTube filtre « chaînes », ytInitialData) — ~120 requêtes thématiques × marqueurs QC
18+- **Type d'accès (code)** : API JSON
19+- **Endpoint de base** : https://www.youtube.com/results
20+- **URLs du module** : https://www.youtube.com/results · https://www.youtube.com/youtubei/v1/search
21+- **Pagination** : curseur / continuation
22+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli)
23+- **Politesse** : 1.0 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : aucune — contenu public / API anonyme
25+
26+## Champs récupérés → schéma cible
27+
28+Source de **découverte** : produit des fiches `Creator` (tables `creators` + `accounts`). Complétude mesurée sur les 4595 créateurs découverts par cette source (champ `doc.source`) ; exemple tiré d'une fiche réelle.
29+
30+| Champ du doc créateur | Contenu | Renseigné | Exemple réel |
31+|---|---|---|---|
32+| `display_name` | Nom public | 100 % | Ça commence aujourd'hui - France Télévisions |
33+| `bio` | Bio | 88 % | La vérité plus qu'ailleurs avec Faustine Bollaert. |
34+| `region` | Région | 0 % | — |
35+| `city` | Ville | 0 % | — |
36+| `niches` | Niches (liste) | 100 % | sport-fitness |
37+| `languages` | Langues (liste) | 100 % | fr |
38+| `creator_type` | Type de créateur | 100 % | youtubeur |
39+| `primary_platform` | Plateforme principale | 100 % | youtube |
40+| `platforms` | Comptes sociaux (liste) | 100 % | youtube:@%c3%87acommenceaujourdhui-francet%c3%a9l |
41+| `avatar_url` | Avatar | 90 % | https://yt3.ggpht.com/34WaPmV58PCfc9W7IvR_oAVkTcQrRyYMenz4phwmrE7LPm8f4zJfOIc2Jq4XkMZZsYj… |
42+| `total_reach` | Portée totale (abonnés cumulés) | 96 % | 1860000 |
43+| `link_in_bio_url` | Lien-en-bio | 0 % | — |
44+
45+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `youtube` (4768 comptes en BD, confiance moyenne 0.98).
46+
47+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
48+|---|---|---|---|
49+| `handle` | Handle | 100 % | nilered |
50+| `url` | URL du profil | 100 % | https://www.youtube.com/@nilered |
51+| `followers` | Abonnés | 93 % | 10900000 |
52+| `verified` | Badge vérifié | 0 % | — |
53+| `confidence` | Confiance d'identité | 100 % | 0.9 |
54+| `signal` | Signal d'identité | 100 % | cross_link |
55+| `metrics` | Métriques détaillées (JSON) | 0 % | — |
56+| `last_checked` | Dernière vérification | 100 % | 2026-08-18T00:32:23Z |
57+
58+## Fréquence & budget
59+
60+- **Cadence déclarée (registre)** : quotidienne (watch) — la couverture s'accumule à chaque passage
61+- **Cadence observée** (médiane sync_log) : ≈ 5.3 h
62+- **Dernier passage** : 2026-08-18T07:22:54Z — 4155 créateurs, 4155 comptes, +555 / ~3600, 0 erreur(s), 3304 s
63+- **Throttling** : 1.0 s entre requêtes (`request_delay`)
64+
65+## Volumétrie & complétude
66+
67+- **Créateurs découverts par la source** (`doc.source`) : 4595 · **fiches touchées** (`source_ids`) : 4595
68+- **Complétude clé (découverts)** : niches 100 % · avatar 90 % · portée 96 % · région 0 %
69+- **Comptes `youtube` en BD** : 4768 — abonnés 93 % · métriques 0 % · dernière vérification 2026-08-18T07:22:54Z
70+- **Runs journalisés (60 derniers)** : 3, dont 0 avec erreurs
71+
72+## Erreurs connues & dépannage
73+
74+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
75+
76+Rejouer la source seule : `python3 run.py sync youtube-recherche` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='youtube-recherche' ORDER BY ts DESC LIMIT 5;`).
77+
78+## Licence, attribution & conditions
79+
80+- **Cadre d'accès (registre)** : requêtes directes (recherche publique YouTube filtre « chaînes », ytInitialData) — ~120 requêtes thématiques × marqueurs QC
81+- **Profils publics uniquement** : UA identifiable `CreaKaBot/1.0 (+https://www.crea-ka.com/bot; contact@spboucher.ai)`, throttling poli, respect des opt-out (`data/optout.json`) et du volet éthique (`creaka/ethics.py` — mineurs exclus via `is_minor`).
82+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
83+
84+## Historique
85+
86+- 2026-08-17 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
87+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added docs/connecteurs/youtube.md +69 −0
@@ -0,0 +1,69 @@
1+# `youtube` — connecteur enrichissement (palier 2)
2+
3+_Fiche générée automatiquement par `scripts/gen_connector_docs.py` le 2026-08-18 03:30 — ne pas éditer à la main, régénérer._
4+
5+**État : actif** · Famille : enrichissement (enrichment) · Backend : direct · Créateurs découverts : 0 · touchés : 0
6+
7+## Description de la source
8+
9+Connecteur ENRICHISSEMENT YouTube — abonnés + badge par créateur. Mode d'accès : YouTube Data API v3 (OFFICIELLE, privilégiée §10) si YOUTUBE_API_KEY est définie ; sinon repli page publique /@handle (requêtes directes polies). Palier 2 du catalogue (§9).
10+
11+- **Notes (registre)** : abonnés par chaîne
12+- **Signal d'identité** : api_officielle (0.95) quand confirmé par l'API (colonne `accounts.signal` / `confidence`)
13+- **Module** : `creaka/connectors/youtube.py` — classe `YouTubeConnector` (`kind = "enrichment"`)
14+
15+## Accès
16+
17+- **Accès (registre)** : YouTube Data API v3 (officielle) si YOUTUBE_API_KEY, sinon page publique /@handle
18+- **Type d'accès (code)** : API JSON
19+- **Endpoint de base** : https://www.googleapis.com/youtube/v3/channels?part=statistics,snippet&forHandle={h}&key={key}
20+- **URLs du module** : https://www.googleapis.com/youtube/v3/channels · https://www.youtube.com/@{h}
21+- **Pagination** : réponse unique (pas de pagination)
22+- **Backend anti-bot / rendu** : requests direct (session UA CreaKaBot, throttling poli)
23+- **Politesse** : 1.0 s entre requêtes, timeout 30 s, UA `CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`
24+- **Authentification** : aucune — contenu public / API anonyme
25+
26+## Champs récupérés → schéma cible
27+
28+Cible d'**enrichissement** : colonnes de la table `accounts` pour la/les plateforme(s) `youtube` (4768 comptes en BD, confiance moyenne 0.98).
29+
30+| Colonne `accounts` | Contenu | Renseignée | Exemple réel |
31+|---|---|---|---|
32+| `handle` | Handle | 100 % | nilered |
33+| `url` | URL du profil | 100 % | https://www.youtube.com/@nilered |
34+| `followers` | Abonnés | 93 % | 10900000 |
35+| `verified` | Badge vérifié | 0 % | — |
36+| `confidence` | Confiance d'identité | 100 % | 0.9 |
37+| `signal` | Signal d'identité | 100 % | cross_link |
38+| `metrics` | Métriques détaillées (JSON) | 0 % | — |
39+| `last_checked` | Dernière vérification | 100 % | 2026-08-18T00:32:23Z |
40+
41+## Fréquence & budget
42+
43+- **Cadence déclarée (registre)** : hebdomadaire
44+- **Cadence observée** (médiane sync_log) : —
45+- **Dernier passage** : 2026-08-18T01:06:18Z — 1487 créateurs, 1576 comptes, +0 / ~1487, 0 erreur(s), 0 s
46+- **Throttling** : 1.0 s entre requêtes (`request_delay`)
47+
48+## Volumétrie & complétude
49+
50+- **Créateurs découverts par la source** (`doc.source`) : 0 · **fiches touchées** (`source_ids`) : 0
51+- **Comptes `youtube` en BD** : 4768 — abonnés 93 % · métriques 0 % · dernière vérification 2026-08-18T07:22:54Z
52+- **Runs journalisés (60 derniers)** : 1, dont 0 avec erreurs
53+
54+## Erreurs connues & dépannage
55+
56+Aucune erreur ni alerte dans les 60 derniers runs journalisés.
57+
58+Rejouer la source seule : `python3 run.py sync youtube` · vérifier `sync_log` (`SELECT * FROM sync_log WHERE source='youtube' ORDER BY ts DESC LIMIT 5;`).
59+
60+## Licence, attribution & conditions
61+
62+- **Cadre d'accès (registre)** : YouTube Data API v3 (officielle) si YOUTUBE_API_KEY, sinon page publique /@handle
63+- **API publique** utilisée selon ses conditions (pas de clé détournée, throttling poli) ; données limitées aux profils publics.
64+- Retrait sur demande : contact@spboucher.ai (opt-out honoré à la prochaine ingestion).
65+
66+## Historique
67+
68+- 2026-08-18 — plus ancien passage journalisé dans `sync_log` (fenêtre des 60 derniers).
69+- 2026-08-18 — vague d'enrichissement : standardisation de la documentation des connecteurs (fiche générée par `scripts/gen_connector_docs.py`).
added scripts/gen_connector_docs.py +557 −0
@@ -0,0 +1,557 @@
1+#!/usr/bin/env python3
2+# ==============================================================================
3+# Author: Simon-Pierre Boucher <contact@spboucher.ai>
4+# File: scripts/gen_connector_docs.py
5+# Desc: Documentation STANDARDISÉE des connecteurs Créa-Ka — génère
6+# docs/connecteurs/INDEX.md + une fiche docs/connecteurs/<id>.md par
7+# source du registre, de façon 100 % programmatique et rejouable :
8+# 1. registre data/sources.json (famille, palier, accès, cadence,
9+# signal d'identité, notes, statut) ;
10+# 2. introspection STATIQUE (ast) de creaka/connectors/*.py :
11+# classe, kind (discovery/enrichment), backend, endpoints,
12+# pagination, constantes de budget — sans exécuter le code ;
13+# 3. BD live data/creaka.db : créateurs découverts/enrichis par
14+# source (doc JSON), complétude des champs, comptes par
15+# plateforme, dernier sync, cadence observée, alertes récentes.
16+# Usage : python3 scripts/gen_connector_docs.py (racine du projet)
17+# ==============================================================================
18+from __future__ import annotations
19+
20+import ast
21+import json
22+import re
23+import sqlite3
24+import statistics
25+from datetime import datetime, timezone
26+from pathlib import Path
27+
28+ROOT = Path(__file__).resolve().parents[1]
29+CONN_DIR = ROOT / "creaka" / "connectors"
30+DOCS_DIR = ROOT / "docs" / "connecteurs"
31+DB_PATH = ROOT / "data" / "creaka.db"
32+SOURCES_JSON = ROOT / "data" / "sources.json"
33+
34+SKIP_MODULES = {"__init__", "base"}
35+URL_RE = re.compile(r"https?://[^\s\"'\\)>,;]+")
36+DATE_RE = re.compile(r"\d{4}-\d{2}-\d{2}")
37+INFRA_HOSTS = ("api.firecrawl.dev", "api.scrapfly.io", "crea-ka.com")
38+
39+# plateformes de la table `accounts` alimentées par chaque source d'enrichissement
40+SOURCE_PLATFORMS = {
41+ "instagram-profil": ("instagram",),
42+ "tiktok-profil": ("tiktok",),
43+ "youtube": ("youtube",),
44+ "youtube-recherche": ("youtube",),
45+ "twitch": ("twitch",),
46+ "x-profil": ("x",),
47+ "balados-rss": ("podcast",),
48+ "balados-itunes": ("podcast",),
49+ "podcastindex": ("podcast",),
50+ "onlyqueb": ("onlyfans",),
51+}
52+
53+# champs du doc créateur documentés (complétude par source de découverte)
54+DOC_FIELDS = [
55+ ("display_name", "Nom public"),
56+ ("bio", "Bio"),
57+ ("region", "Région"),
58+ ("city", "Ville"),
59+ ("niches", "Niches (liste)"),
60+ ("languages", "Langues (liste)"),
61+ ("creator_type", "Type de créateur"),
62+ ("primary_platform", "Plateforme principale"),
63+ ("platforms", "Comptes sociaux (liste)"),
64+ ("avatar_url", "Avatar"),
65+ ("total_reach", "Portée totale (abonnés cumulés)"),
66+ ("link_in_bio_url", "Lien-en-bio"),
67+]
68+
69+ACC_FIELDS = [
70+ ("handle", "Handle", "handle IS NOT NULL AND handle != ''"),
71+ ("url", "URL du profil", "url IS NOT NULL AND url != ''"),
72+ ("followers", "Abonnés", "followers IS NOT NULL"),
73+ ("verified", "Badge vérifié", "verified IS NOT NULL"),
74+ ("confidence", "Confiance d'identité", "confidence IS NOT NULL"),
75+ ("signal", "Signal d'identité", "signal IS NOT NULL AND signal != ''"),
76+ ("metrics", "Métriques détaillées (JSON)",
77+ "metrics IS NOT NULL AND length(metrics) > 4"),
78+ ("last_checked", "Dernière vérification",
79+ "last_checked IS NOT NULL AND last_checked != ''"),
80+]
81+
82+
83+def esc(s, limit: int = 100) -> str:
84+ s = str(s).replace("\\", "\\\\").replace("|", "\\|")
85+ s = re.sub(r"\s+", " ", s).strip()
86+ return s[: limit - 1] + "…" if len(s) > limit else s
87+
88+
89+def pct(n, d) -> str:
90+ return f"{100.0 * (n or 0) / d:.0f} %" if d else "—"
91+
92+
93+def parse_iso(ts: str) -> float | None:
94+ try:
95+ return datetime.fromisoformat(ts.replace("Z", "+00:00")).timestamp()
96+ except (ValueError, AttributeError):
97+ return None
98+
99+
100+def fmt_secs(sec: float) -> str:
101+ if sec < 5400:
102+ return f"≈ {sec / 60:.0f} min"
103+ if sec < 129600:
104+ return f"≈ {sec / 3600:.1f} h"
105+ return f"≈ {sec / 86400:.1f} j"
106+
107+
108+def filled(v) -> bool:
109+ return bool(v)
110+
111+
112+# -- introspection statique ------------------------------------------------------
113+
114+def banner_description(text: str) -> str:
115+ lines, started = [], False
116+ for raw in text.splitlines():
117+ if not raw.startswith("#"):
118+ if started:
119+ break
120+ continue
121+ body = raw.lstrip("#").strip()
122+ if set(body) <= {"-", "="}:
123+ continue
124+ if not started:
125+ if body.startswith("Desc:"):
126+ started = True
127+ lines.append(body[len("Desc:"):].strip())
128+ continue
129+ if re.match(r"^(Author|File):", body):
130+ break
131+ lines.append(body)
132+ return " ".join(l for l in lines if l).strip()
133+
134+
135+def introspect_module(path: Path) -> list[dict]:
136+ text = path.read_text(encoding="utf-8")
137+ try:
138+ tree = ast.parse(text)
139+ except SyntaxError:
140+ return []
141+ constants: dict = {}
142+ for node in tree.body:
143+ if isinstance(node, ast.Assign) and len(node.targets) == 1 \
144+ and isinstance(node.targets[0], ast.Name) \
145+ and node.targets[0].id.isupper():
146+ try:
147+ constants[node.targets[0].id] = ast.literal_eval(node.value)
148+ except (ValueError, TypeError, SyntaxError):
149+ seg = ast.get_source_segment(text, node.value) or ""
150+ urls = URL_RE.findall(seg)
151+ constants[node.targets[0].id] = urls if len(urls) > 1 else \
152+ (urls[0] if urls else None)
153+ urls_all = []
154+ for u in URL_RE.findall(text):
155+ u = u.rstrip('".')
156+ host = u.split("//", 1)[-1].split("/", 1)[0]
157+ if "." not in host: # fragment de f-string, pas une vraie URL
158+ continue
159+ if not any(h in u for h in INFRA_HOSTS) and u not in urls_all:
160+ urls_all.append(u)
161+ endpoint = None
162+ for name in ("BASE", "BASE_URL", "API", "API_URL", "API_BASE", "SPARQL",
163+ "ENDPOINT", "ROOT", "URL", "SEARCH_URL", "LOOKUP_URL"):
164+ v = constants.get(name)
165+ if isinstance(v, str) and v.startswith("http"):
166+ endpoint = v
167+ break
168+ if not endpoint and urls_all:
169+ endpoint = urls_all[0]
170+ budgets = {k: v for k, v in constants.items()
171+ if isinstance(v, (int, float)) and not isinstance(v, bool)
172+ and re.search(r"MAX|CAP|LIMIT|BUDGET|TTL|PER_PAGE|PAGES|DELAY|BATCH",
173+ k)}
174+ detailed, family = [], "direct"
175+ if re.search(r"self\.(get|post)\(|requests\.(get|post)\(", text):
176+ detailed.append("requests direct (session UA CreaKaBot, throttling poli)")
177+ if ".scrapfly(" in text:
178+ detailed.append("Scrapfly (asp + render_js — contournement anti-bot)")
179+ family = "Scrapfly"
180+ if "get_rendered(" in text:
181+ detailed.append("Firecrawl (HTML rendu, JavaScript exécuté)")
182+ family = "Firecrawl" if family == "direct" else family
183+ if not detailed:
184+ detailed.append("requests direct")
185+ low = text.lower()
186+ if "sparql" in low:
187+ flavor = "API SPARQL publique (Wikidata)"
188+ elif "graphql" in low:
189+ flavor = "API GraphQL"
190+ elif "itunes.apple.com" in low:
191+ flavor = "API iTunes Search/Lookup (publique)"
192+ elif "rss" in low and "feedparser" in low or "<rss" in low:
193+ flavor = "flux RSS publics"
194+ elif "sitemap" in low:
195+ flavor = "sitemap XML + JSON-LD des pages profil (SSR)"
196+ elif "json-ld" in low or "jsonld" in low or "profilepage" in low:
197+ flavor = "JSON-LD des pages profil publiques"
198+ elif re.search(r"\.json\(\)", text) and re.search(r"api[./_]", low):
199+ flavor = "API JSON"
200+ elif "dataset" in low or "seed" in low and "csv" in low:
201+ flavor = "dataset local compilé (data/seed)"
202+ else:
203+ flavor = "pages HTML publiques"
204+ hits = []
205+ if re.search(r"[?&]page=|[\"']page[\"']\s*[:=]", low):
206+ hits.append("pagination par numéro de page")
207+ if re.search(r"[?&]offset=|[\"']offset[\"']", low):
208+ hits.append("pagination par offset")
209+ if "cursor" in low or "continuation" in low:
210+ hits.append("curseur / continuation")
211+ if not hits:
212+ hits.append("réponse unique (pas de pagination)")
213+ base = {"module": path.stem, "path": f"creaka/connectors/{path.stem}.py",
214+ "banner": banner_description(text), "endpoint": endpoint,
215+ "urls": urls_all[:5], "budgets": budgets, "backends": detailed,
216+ "backend_family": family, "flavor": flavor,
217+ "pagination": " ; ".join(hits)}
218+ entries = []
219+ for node in tree.body:
220+ if not isinstance(node, ast.ClassDef):
221+ continue
222+ bases = {getattr(b, "id", getattr(b, "attr", "")) for b in node.bases}
223+ if "BaseConnector" not in bases:
224+ continue
225+ attrs = {"request_delay": 0.8, "timeout": 30, "kind": "discovery",
226+ "source_id": ""}
227+ for sub in node.body:
228+ if isinstance(sub, ast.Assign) and len(sub.targets) == 1 \
229+ and isinstance(sub.targets[0], ast.Name):
230+ try:
231+ attrs[sub.targets[0].id] = ast.literal_eval(sub.value)
232+ except (ValueError, TypeError, SyntaxError):
233+ pass
234+ if attrs["source_id"]:
235+ entries.append({**base, "class": node.name,
236+ "class_doc": ast.get_docstring(node) or "",
237+ **attrs})
238+ return entries
239+
240+
241+# -- BD live ----------------------------------------------------------------------
242+
243+def load_creators(con) -> list[dict]:
244+ out = []
245+ for (doc,) in con.execute("SELECT doc FROM creators"):
246+ try:
247+ out.append(json.loads(doc))
248+ except ValueError:
249+ pass
250+ return out
251+
252+
253+def db_stats(con, sid: str, creators: list[dict]) -> dict:
254+ mine = [c for c in creators if c.get("source") == sid]
255+ touched = sum(1 for c in creators
256+ if sid in (c.get("source_ids") or []) or c.get("source") == sid)
257+ doc_fill = {f: sum(1 for c in mine if filled(c.get(f)))
258+ for f, _l in DOC_FIELDS}
259+ sample = max(mine, key=lambda c: c.get("total_reach") or 0) if mine else None
260+
261+ acc = None
262+ acc_sample = None
263+ platforms = SOURCE_PLATFORMS.get(sid)
264+ if platforms:
265+ ph = ",".join("?" * len(platforms))
266+ parts = ", ".join(
267+ f"sum(CASE WHEN {cond} THEN 1 ELSE 0 END) AS f_{col}"
268+ for col, _l, cond in ACC_FIELDS)
269+ acc = con.execute(
270+ f"SELECT count(*) AS n, avg(confidence) AS conf, "
271+ f"max(last_checked) AS checked, {parts} FROM accounts "
272+ f"WHERE platform IN ({ph})", platforms).fetchone()
273+ acc_sample = con.execute(
274+ f"SELECT * FROM accounts WHERE platform IN ({ph}) AND followers IS "
275+ f"NOT NULL ORDER BY followers DESC LIMIT 1", platforms).fetchone()
276+
277+ runs = con.execute(
278+ "SELECT ts, creators, accounts, avg_confidence, added, updated, "
279+ "errors, seconds, alert FROM sync_log WHERE source=? "
280+ "ORDER BY ts DESC LIMIT 60", (sid,)).fetchall()
281+ ok_ts = sorted(t for t in (parse_iso(r["ts"]) for r in runs) if t)
282+ cadence = None
283+ if len(ok_ts) >= 3:
284+ deltas = [b - a for a, b in zip(ok_ts, ok_ts[1:]) if b - a > 60]
285+ if deltas:
286+ cadence = statistics.median(deltas)
287+ alerts = [r for r in runs if (r["errors"] or 0) > 0 or r["alert"]][:5]
288+ return {"mine": len(mine), "touched": touched, "doc_fill": doc_fill,
289+ "sample": sample, "acc": acc, "acc_sample": acc_sample,
290+ "platforms": platforms, "runs": runs, "cadence": cadence,
291+ "last": runs[0] if runs else None, "alerts": alerts}
292+
293+
294+# -- rendu ---------------------------------------------------------------------------
295+
296+def render_fiche(reg: dict, entry: dict | None, st: dict, now: str) -> str:
297+ sid = reg["id"]
298+ etat = reg.get("statut", "actif")
299+ fam = reg.get("famille", "—")
300+ out = [f"# `{sid}` — connecteur {fam} (palier {reg.get('palier', '—')})", "",
301+ f"_Fiche générée automatiquement par "
302+ f"`scripts/gen_connector_docs.py` le {now} — ne pas éditer à la "
303+ f"main, régénérer._", ""]
304+ kind = entry.get("kind") if entry else fam
305+ out.append(f"**État : {esc(etat, 80)}** · Famille : {fam} ({kind}) · "
306+ f"Backend : {entry['backend_family'] if entry else '—'} · "
307+ f"Créateurs découverts : {st['mine']} · touchés : "
308+ f"{st['touched']}")
309+ out.append("")
310+
311+ out.append("## Description de la source")
312+ out.append("")
313+ if entry and entry["banner"]:
314+ out.append(entry["banner"])
315+ out.append("")
316+ out.append(f"- **Notes (registre)** : {reg.get('notes', '—')}")
317+ out.append(f"- **Signal d'identité** : {reg.get('signal_identite', '—')} "
318+ f"(colonne `accounts.signal` / `confidence`)")
319+ if entry:
320+ out.append(f"- **Module** : `{entry['path']}` — classe "
321+ f"`{entry['class']}` (`kind = \"{entry['kind']}\"`)")
322+ else:
323+ out.append("- **Module** : introuvable (vérifier le registre)")
324+ out.append("")
325+
326+ out.append("## Accès")
327+ out.append("")
328+ out.append(f"- **Accès (registre)** : {reg.get('acces', '—')}")
329+ if entry:
330+ out.append(f"- **Type d'accès (code)** : {entry['flavor']}")
331+ out.append(f"- **Endpoint de base** : {entry['endpoint'] or '—'}")
332+ if entry["urls"]:
333+ out.append("- **URLs du module** : " + " · ".join(entry["urls"][:4]))
334+ out.append(f"- **Pagination** : {entry['pagination']}")
335+ out.append(f"- **Backend anti-bot / rendu** : "
336+ f"{' ; '.join(entry['backends'])}")
337+ out.append(f"- **Politesse** : {entry['request_delay']} s entre "
338+ f"requêtes, timeout {entry['timeout']} s, UA "
339+ f"`CreaKaBot/1.0 (+https://www.crea-ka.com/bot)`")
340+ needs_key = "clé" in (reg.get("acces") or "").lower() \
341+ or "clé" in str(etat).lower()
342+ auth = ("clé(s) API requise(s) — voir statut/registre" if needs_key
343+ else "aucune — contenu public / API anonyme")
344+ out.append(f"- **Authentification** : {auth}")
345+ out.append("")
346+
347+ out.append("## Champs récupérés → schéma cible")
348+ out.append("")
349+ if st["mine"]:
350+ out.append(f"Source de **découverte** : produit des fiches `Creator` "
351+ f"(tables `creators` + `accounts`). Complétude mesurée sur "
352+ f"les {st['mine']} créateurs découverts par cette source "
353+ f"(champ `doc.source`) ; exemple tiré d'une fiche réelle.")
354+ out.append("")
355+ out.append("| Champ du doc créateur | Contenu | Renseigné | Exemple réel |")
356+ out.append("|---|---|---|---|")
357+ s = st["sample"] or {}
358+ for f, label in DOC_FIELDS:
359+ v = s.get(f)
360+ if f == "platforms" and v:
361+ ex = esc(", ".join(f"{p.get('platform')}:@{p.get('handle')}"
362+ for p in v[:3]), 90)
363+ elif isinstance(v, list):
364+ ex = esc(", ".join(map(str, v[:4])), 90)
365+ else:
366+ ex = esc(v, 90) if filled(v) else "—"
367+ out.append(f"| `{f}` | {label} | "
368+ f"{pct(st['doc_fill'][f], st['mine'])} | {ex} |")
369+ out.append("")
370+ if st["acc"] is not None and st["acc"]["n"]:
371+ a = st["acc"]
372+ plats = ", ".join(st["platforms"])
373+ out.append(f"Cible d'**enrichissement** : colonnes de la table "
374+ f"`accounts` pour la/les plateforme(s) `{plats}` "
375+ f"({a['n']} comptes en BD, confiance moyenne "
376+ f"{a['conf']:.2f}).")
377+ out.append("")
378+ out.append("| Colonne `accounts` | Contenu | Renseignée | Exemple réel |")
379+ out.append("|---|---|---|---|")
380+ sm = st["acc_sample"]
381+ for col, label, _c in ACC_FIELDS:
382+ ex = esc(sm[col], 90) if sm is not None and sm[col] not in (
383+ None, "") else "—"
384+ out.append(f"| `{col}` | {label} | {pct(a[f'f_{col}'], a['n'])} "
385+ f"| {ex} |")
386+ out.append("")
387+ if not st["mine"] and (st["acc"] is None or not st["acc"]["n"]):
388+ out.append("Aucune donnée attribuable à cette source en BD pour "
389+ "l'instant (clés manquantes ou source en attente) — schéma "
390+ "cible : `creators` + `accounts`.")
391+ out.append("")
392+
393+ out.append("## Fréquence & budget")
394+ out.append("")
395+ out.append(f"- **Cadence déclarée (registre)** : {reg.get('cadence', '—')}")
396+ cad = fmt_secs(st["cadence"]) if st["cadence"] else "—"
397+ out.append(f"- **Cadence observée** (médiane sync_log) : {cad}")
398+ last = st["last"]
399+ if last:
400+ out.append(f"- **Dernier passage** : {esc(last['ts'], 20)} — "
401+ f"{last['creators'] or 0} créateurs, {last['accounts'] or 0} "
402+ f"comptes, +{last['added'] or 0} / ~{last['updated'] or 0}, "
403+ f"{last['errors'] or 0} erreur(s), "
404+ f"{(last['seconds'] or 0):.0f} s")
405+ if entry and entry["budgets"]:
406+ caps = ", ".join(f"`{k}` = {v}" for k, v in sorted(entry["budgets"].items()))
407+ out.append(f"- **Caps / budgets du module** : {caps}")
408+ if entry:
409+ out.append(f"- **Throttling** : {entry['request_delay']} s entre "
410+ f"requêtes (`request_delay`)")
411+ out.append("")
412+
413+ out.append("## Volumétrie & complétude")
414+ out.append("")
415+ out.append(f"- **Créateurs découverts par la source** (`doc.source`) : "
416+ f"{st['mine']} · **fiches touchées** (`source_ids`) : "
417+ f"{st['touched']}")
418+ if st["mine"]:
419+ out.append(f"- **Complétude clé (découverts)** : niches "
420+ f"{pct(st['doc_fill']['niches'], st['mine'])} · avatar "
421+ f"{pct(st['doc_fill']['avatar_url'], st['mine'])} · portée "
422+ f"{pct(st['doc_fill']['total_reach'], st['mine'])} · région "
423+ f"{pct(st['doc_fill']['region'], st['mine'])}")
424+ if st["acc"] is not None and st["acc"]["n"]:
425+ a = st["acc"]
426+ out.append(f"- **Comptes `{', '.join(st['platforms'])}` en BD** : "
427+ f"{a['n']} — abonnés {pct(a['f_followers'], a['n'])} · "
428+ f"métriques {pct(a['f_metrics'], a['n'])} · dernière "
429+ f"vérification {esc(a['checked'] or '—', 20)}")
430+ out.append(f"- **Runs journalisés (60 derniers)** : {len(st['runs'])}, "
431+ f"dont {sum(1 for r in st['runs'] if (r['errors'] or 0) > 0)} "
432+ f"avec erreurs")
433+ out.append("")
434+
435+ out.append("## Erreurs connues & dépannage")
436+ out.append("")
437+ if st["alerts"]:
438+ out.append("| Passage | Erreurs | Alerte (sync_log) |")
439+ out.append("|---|---|---|")
440+ for r in st["alerts"]:
441+ out.append(f"| {esc(r['ts'], 20)} | {r['errors'] or 0} | "
442+ f"{esc(r['alert'] or '—', 160)} |")
443+ out.append("")
444+ else:
445+ out.append("Aucune erreur ni alerte dans les 60 derniers runs "
446+ "journalisés.")
447+ out.append("")
448+ if str(etat) != "actif":
449+ out.append(f"**Statut du registre** : {etat}")
450+ out.append("")
451+ out.append(f"Rejouer la source seule : `python3 run.py sync {sid}` · "
452+ f"vérifier `sync_log` (`SELECT * FROM sync_log WHERE "
453+ f"source='{sid}' ORDER BY ts DESC LIMIT 5;`).")
454+ out.append("")
455+
456+ out.append("## Licence, attribution & conditions")
457+ out.append("")
458+ out.append(f"- **Cadre d'accès (registre)** : {reg.get('acces', '—')}")
459+ low = (reg.get("acces") or "").lower()
460+ if "wikidata" in low or "sparql" in low:
461+ out.append("- **Licence** : données Wikidata sous CC0 — réutilisation "
462+ "libre, mention « Source : Wikidata » affichée par "
463+ "courtoisie.")
464+ elif "dataset local" in low or "listes" in sid:
465+ out.append("- **Données compilées** de listes et palmarès médias "
466+ "**publics** ; seuls des faits publics (nom, handle, "
467+ "audience approximative) sont conservés.")
468+ elif "api" in low and ("publique" in low or "itunes" in low):
469+ out.append("- **API publique** utilisée selon ses conditions (pas de "
470+ "clé détournée, throttling poli) ; données limitées aux "
471+ "profils publics.")
472+ else:
473+ out.append("- **Profils publics uniquement** : UA identifiable "
474+ "`CreaKaBot/1.0 (+https://www.crea-ka.com/bot; "
475+ "contact@spboucher.ai)`, throttling poli, respect des "
476+ "opt-out (`data/optout.json`) et du volet éthique "
477+ "(`creaka/ethics.py` — mineurs exclus via `is_minor`).")
478+ out.append("- Retrait sur demande : contact@spboucher.ai (opt-out honoré "
479+ "à la prochaine ingestion).")
480+ out.append("")
481+
482+ out.append("## Historique")
483+ out.append("")
484+ blob = " ".join(str(reg.get(k, "")) for k in ("notes", "statut", "acces"))
485+ for d in sorted({m.group(0) for m in DATE_RE.finditer(blob)}):
486+ out.append(f"- {d} — date mentionnée au registre (voir notes/statut).")
487+ if st["runs"]:
488+ first = st["runs"][-1]["ts"]
489+ out.append(f"- {str(first)[:10]} — plus ancien passage journalisé dans "
490+ f"`sync_log` (fenêtre des 60 derniers).")
491+ out.append("- 2026-08-18 — vague d'enrichissement : standardisation de la "
492+ "documentation des connecteurs (fiche générée par "
493+ "`scripts/gen_connector_docs.py`).")
494+ out.append("")
495+ return "\n".join(out)
496+
497+
498+def main() -> None:
499+ now = datetime.now().strftime("%Y-%m-%d %H:%M")
500+ registry = json.loads(SOURCES_JSON.read_text(encoding="utf-8"))["sources"]
501+ con = sqlite3.connect(DB_PATH)
502+ con.row_factory = sqlite3.Row
503+ creators = load_creators(con)
504+
505+ by_sid: dict[str, dict] = {}
506+ for path in sorted(CONN_DIR.glob("*.py")):
507+ if path.stem in SKIP_MODULES:
508+ continue
509+ for e in introspect_module(path):
510+ by_sid[e["source_id"]] = e
511+
512+ DOCS_DIR.mkdir(parents=True, exist_ok=True)
513+ for old in DOCS_DIR.glob("*.md"):
514+ old.unlink()
515+
516+ rows = []
517+ for reg in registry:
518+ sid = reg["id"]
519+ entry = by_sid.get(sid)
520+ st = db_stats(con, sid, creators)
521+ (DOCS_DIR / f"{sid}.md").write_text(
522+ render_fiche(reg, entry, st, now), encoding="utf-8")
523+ last = st["last"]
524+ acc = st["acc"]
525+ comptes = (f"{acc['n']} ({', '.join(st['platforms'])})"
526+ if acc is not None and acc["n"] else "—")
527+ rows.append(
528+ f"| [`{sid}`]({sid}.md) | {reg.get('famille', '—')} "
529+ f"| P{reg.get('palier', '—')} "
530+ f"| {esc(entry['flavor'] if entry else '—', 42)} "
531+ f"| {entry['backend_family'] if entry else '—'} "
532+ f"| {st['mine']} | {comptes} "
533+ f"| {esc(str(reg.get('statut', 'actif')).split('—')[0], 26)} "
534+ f"| {esc(last['ts'], 17) if last else '—'} |")
535+
536+ n_creators = con.execute("SELECT count(*) FROM creators").fetchone()[0]
537+ n_accounts = con.execute("SELECT count(*) FROM accounts").fetchone()[0]
538+ idx = [
539+ "# Créa-Ka — Index des connecteurs", "",
540+ f"_Généré automatiquement par `scripts/gen_connector_docs.py` le {now} "
541+ f"— ne pas éditer à la main, régénérer._", "",
542+ f"**{len(registry)} sources au registre** · **{n_creators} créateurs** "
543+ f"et **{n_accounts} comptes** en BD.", "",
544+ "| Source | Famille | Palier | Type d'accès | Backend | Découverts "
545+ "| Comptes (plateforme) | État | Dernier passage |",
546+ "|---|---|---|---|---|---|---|---|---|",
547+ ]
548+ idx.extend(rows)
549+ idx.append("")
550+ (DOCS_DIR / "INDEX.md").write_text("\n".join(idx), encoding="utf-8")
551+ con.close()
552+ print(f"[gen_connector_docs] {len(registry)} fiches + INDEX.md écrits dans "
553+ f"{DOCS_DIR}")
554+
555+
556+if __name__ == "__main__":
557+ main()
558
Menu

Theme