{"openapi":"3.0.1","info":{"title":"Quran Qalun","description":"API du compagnon de memorisation coranique en riwayat **Qalun 'an Nafi'**.\n\n### Numerotation\nTous les numeros de versets - catalogue, plages demandees, progression - sont en numerotation\n**madani**, celle des moshafs Qalun. Ce n'est pas la numerotation kufi/Hafs : Al-Baqara compte\n**285** versets ici, et non 286. Un client qui melange les deux decale la plupart des sourates\nd'un verset.\n\n### Audio\n`GET /api/v1/audio` renvoie un extrait mp3 decoupe a la volee. L'endpoint accepte l'en-tete\n`Range`, donc un `<audio>` peut y chercher sans tout telecharger, et il ne demande aucune\nauthentification : un element `<audio>` n'envoie pas d'en-tete. Il est donc limite par adresse\nIP, et peut exiger une signature `&exp=&sig=` si `qalun.signing.enabled` est actif.\n\n### Donnees personnelles\nAucune ne transite par l'URL audio, jamais. Les routes `/api/v1/learners` en portent, elles.\n","license":{"name":"Usage personnel"},"version":"v1"},"servers":[{"url":"https://vps-fc03cabd.vps.ovh.ca","description":"Cette instance"}],"tags":[{"name":"Catalogue","description":"Ce que cette instance peut servir : recitateurs et sourates disponibles."},{"name":"Audio","description":"Production des extraits mp3."},{"name":"Apprentissage","description":"Boucle de memorisation : apprenants, devoir de la semaine, notes."}],"paths":{"/api/v1/learners":{"post":{"tags":["Apprentissage"],"summary":"Creer ou retrouver un apprenant","description":"Idempotent : le meme `alexaUserId` retrouve toujours le meme apprenant plutot que d'en\ncreer un second. Repond `201` dans les deux cas, avec l'`id` a reutiliser pour toutes les\nautres routes.","operationId":"create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LearnerRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LearnerView"}}}}}}},"/api/v1/learners/{id}/reviews":{"post":{"tags":["Apprentissage"],"summary":"Noter un segment","description":"Enregistre une auto-evaluation. Sans `itemId`, la note porte sur le prochain segment du\ndevoir de la semaine, ce qui permet d'enchainer ecoute puis note sans rien retenir entre\nles deux appels.\n\nLa note pilote la replanification : `A_REVOIR` ramene le segment plus tot, `ACQUIS`\nl'espace.","operationId":"review","parameters":[{"name":"id","in":"path","description":"Identifiant de l'apprenant, renvoye a la creation.","required":true,"schema":{"type":"integer","format":"int64"},"example":1}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SegmentView"}}}}}}},"/api/v1/learners/{id}/reciter":{"post":{"tags":["Apprentissage"],"summary":"Changer de recitateur","description":"Ne touche pas a la progression : les numeros de versets sont madani chez les trois\nrecitateurs, donc ce qui est acquis le reste.","operationId":"reciter","parameters":[{"name":"id","in":"path","description":"Identifiant de l'apprenant, renvoye a la creation.","required":true,"schema":{"type":"integer","format":"int64"},"example":1}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReciterRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LearnerView"}}}}}}},"/api/v1/learners/{id}/pace":{"post":{"tags":["Apprentissage"],"summary":"Changer le rythme hebdomadaire","description":"Ajuste l'objectif de versets par semaine. La valeur est bornee par\n`qalun.learning.min-weekly` et `max-weekly` : demander davantage que le maximum ne leve\npas d'erreur, la cible est simplement plafonnee.","operationId":"pace","parameters":[{"name":"id","in":"path","description":"Identifiant de l'apprenant, renvoye a la creation.","required":true,"schema":{"type":"integer","format":"int64"},"example":1}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaceRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LearnerView"}}}}}}},"/api/v1/surahs":{"get":{"tags":["Catalogue"],"summary":"Sourates servables par un recitateur","description":"Seules les sourates dont l'audio et le minutage sont presents sont listees, dans l'ordre.\n`ayahCount` est le nombre de versets en numerotation **madani** : c'est la borne a\nrespecter pour `end` sur `/api/v1/audio`.","operationId":"surahs","parameters":[{"name":"reciter","in":"query","description":"Cle du recitateur ; celui par defaut si absent.","required":false,"schema":{"type":"string"},"example":"husary"}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SurahView"}}}}}}}},"/api/v1/reciters":{"get":{"tags":["Catalogue"],"summary":"Recitateurs disponibles","description":"Les moshafs Qalun dont cette instance possede les minutages par verset. `surahsAvailable`\ndit combien de sourates sont reellement servables : une instance fraiche, avant\nl'ingestion de l'audio, renvoie 0 partout.","operationId":"reciters","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ReciterView"}}}}}}}},"/api/v1/learners/{id}":{"get":{"tags":["Apprentissage"],"summary":"Etat d'un apprenant","operationId":"get","parameters":[{"name":"id","in":"path","description":"Identifiant de l'apprenant, renvoye a la creation.","required":true,"schema":{"type":"integer","format":"int64"},"example":1}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/LearnerView"}}}}}}},"/api/v1/learners/{id}/progress":{"get":{"tags":["Apprentissage"],"summary":"Progression d'un apprenant","description":"Ce qui est acquis, en cours et restant, en versets madani.","operationId":"progress","parameters":[{"name":"id","in":"path","description":"Identifiant de l'apprenant, renvoye a la creation.","required":true,"schema":{"type":"integer","format":"int64"},"example":1}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ProgressView"}}}}}}},"/api/v1/learners/{id}/next":{"get":{"tags":["Apprentissage"],"summary":"Prochain segment a reviser","description":"Le premier segment non encore note cette semaine.","operationId":"next","parameters":[{"name":"id","in":"path","description":"Identifiant de l'apprenant, renvoye a la creation.","required":true,"schema":{"type":"integer","format":"int64"},"example":1}],"responses":{"200":{"description":"Le segment a travailler.","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SegmentView"}}}},"204":{"description":"La semaine est terminee, plus rien a reviser."}}}},"/api/v1/learners/{id}/assignment":{"get":{"tags":["Apprentissage"],"summary":"Devoir de la semaine","description":"Genere le devoir au premier appel de la semaine, puis renvoie toujours le meme : appeler\ncette route plusieurs fois ne redistribue pas le travail.","operationId":"assignment","parameters":[{"name":"id","in":"path","description":"Identifiant de l'apprenant, renvoye a la creation.","required":true,"schema":{"type":"integer","format":"int64"},"example":1}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AssignmentView"}}}}}}},"/api/v1/audio":{"get":{"tags":["Audio"],"summary":"Extrait audio d'une plage de versets","description":"Decoupe a la volee la plage demandee dans la recitation de la sourate et la renvoie en mp3.\n\nLes bornes `begin` et `end` sont **inclusives** et en numerotation **madani** ; `end` ne\npeut pas depasser le `ayahCount` que `/api/v1/surahs` donne pour cette sourate.\n\nLa reponse accepte l'en-tete `Range` et repond alors `206 Partial Content`, ce qui permet\na un lecteur de chercher dans un long extrait sans le telecharger entierement. Le meme\nquadruplet (recitateur, sourate, plage, profil) rend toujours les memes octets, donc la\nreponse est marquee immuable et peut etre mise en cache indefiniment.\n\nAjouter `format=json` renvoie les metadonnees de la plage sans produire l'audio - utile\npour connaitre la duree avant de lancer le telechargement.\n\nCet endpoint n'exige aucune authentification et est limite par adresse IP.","operationId":"audio","parameters":[{"name":"format","in":"query","description":"Mettre `json` pour recevoir les metadonnees de la plage au lieu de l'audio.","schema":{"type":"string","enum":["json"]}},{"name":"surah","in":"query","description":"Numero de sourate, 1 a 114.","required":true,"schema":{"type":"integer","format":"int32"},"example":2},{"name":"begin","in":"query","description":"Premier verset de la plage, inclus (madani).","required":true,"schema":{"type":"integer","format":"int32"},"example":255},{"name":"end","in":"query","description":"Dernier verset de la plage, inclus (madani).","required":true,"schema":{"type":"integer","format":"int32"},"example":257},{"name":"reciter","in":"query","description":"Cle du recitateur ; celui par defaut si absent.","required":false,"schema":{"type":"string"},"example":"husary"},{"name":"profile","in":"query","description":"Profil d'encodage ; le profil standard si absent.","required":false,"schema":{"type":"string"}},{"name":"includeBasmala","in":"query","description":"Prefixer la basmala. Sans effet lorsque la plage commence au verset 1.","required":false,"schema":{"type":"boolean","default":false}},{"name":"exp","in":"query","description":"Date d'expiration de la signature, en secondes epoch. Requis seulement si `qalun.signing.enabled` est actif.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"sig","in":"query","description":"Signature HMAC de l'URL. Requise seulement si `qalun.signing.enabled` est actif.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"L'extrait mp3, ou ses metadonnees si `format=json`. Les en-tetes `X-Start-Ms`,\n`X-End-Ms`, `X-Duration-Ms`, `X-Ayah-Count` et `X-Surah-Ayah-Count` accompagnent\nl'audio.","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}},"application/json":{"schema":{"$ref":"#/components/schemas/ChunkMetadata"}}}},"206":{"description":"Reponse partielle a un en-tete `Range`.","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Plage invalide, ou `end` au-dela du dernier verset.","content":{"application/problem+json":{}}},"403":{"description":"Signature `sig` absente, invalide ou expiree.","content":{"application/problem+json":{}}},"404":{"description":"Recitateur inconnu, ou sourate non ingeree.","content":{"application/problem+json":{}}},"429":{"description":"Limite par adresse IP atteinte.","content":{"application/problem+json":{}}}}}}},"components":{"schemas":{"LearnerRequest":{"required":["alexaUserId"],"type":"object","properties":{"alexaUserId":{"type":"string","description":"Identifiant stable choisi par le client. Sert de cle de rapprochement : la meme valeur retrouve toujours le meme apprenant.","example":"poste-salon"},"locale":{"type":"string","example":"fr-FR"},"timezone":{"type":"string","example":"Europe/Paris"}},"description":"Identifie l'apprenant a creer ou a retrouver."},"LearnerView":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"locale":{"type":"string"},"timezone":{"type":"string"},"reciterKey":{"type":"string","description":"Recitateur choisi par cet apprenant.","example":"husary"},"weeklyTargetAyat":{"type":"integer","description":"Versets vises par semaine.","format":"int32","example":6}},"description":"Etat d'un apprenant."},"ReviewRequest":{"required":["grade"],"type":"object","properties":{"itemId":{"type":"integer","description":"Segment note. Si absent, la note porte sur le segment qu'aurait renvoye `/next`.","format":"int64"},"grade":{"type":"string","description":"Note attribuee.","example":"ACQUIS","enum":["ACQUIS","PRESQUE","A_REVOIR"]}},"description":"Une auto-evaluation sur un segment."},"SegmentView":{"type":"object","properties":{"itemId":{"type":"integer","format":"int64"},"curriculumOrder":{"type":"integer","format":"int32"},"surah":{"type":"integer","format":"int32"},"begin":{"type":"integer","format":"int32"},"end":{"type":"integer","format":"int32"},"ayahCount":{"type":"integer","format":"int32"},"label":{"type":"string"},"meaningFr":{"type":"string"},"prayerReady":{"type":"boolean"},"difficulty":{"type":"integer","format":"int32"},"kind":{"type":"string"},"state":{"type":"string"},"dueOn":{"type":"string","format":"date"},"durationMs":{"type":"integer","format":"int64"},"audioUrl":{"type":"string"}}},"ReciterRequest":{"required":["reciter"],"type":"object","properties":{"reciter":{"type":"string","description":"Cle du nouveau recitateur.","example":"dokali"}}},"PaceRequest":{"type":"object","properties":{"delta":{"type":"integer","description":"Variation du nombre de versets par semaine, positive ou negative.","format":"int32","example":2},"direction":{"type":"string","description":"Sens du changement, si `delta` est absent ; vaut deux versets.","example":"plus","enum":["plus","moins"]}},"description":"Changement de rythme : `delta` explicite, ou `direction` a defaut."},"SurahView":{"type":"object","properties":{"number":{"type":"integer","description":"Numero de sourate, 1 a 114.","format":"int32","example":2},"nameAr":{"type":"string"},"nameFr":{"type":"string"},"ayahCount":{"type":"integer","description":"Nombre de versets en numerotation **madani** (Al-Baqara : 285).","format":"int32","example":285},"durationMs":{"type":"integer","description":"Duree de la recitation complete, en millisecondes.","format":"int64","example":10524473}},"description":"Une sourate servable, avec son compte de versets madani."},"ReciterView":{"type":"object","properties":{"key":{"type":"string","description":"Identifiant a passer en parametre `reciter`.","example":"husary"},"nameFr":{"type":"string"},"nameAr":{"type":"string"},"moshafId":{"type":"integer","description":"Identifiant du moshaf chez mp3quran.net. Designe le moshaf, pas le recitateur.","format":"int32","example":270},"riwaya":{"type":"string","description":"Toujours `qalun`.","example":"qalun"},"numbering":{"type":"string","description":"Toujours `madani`.","example":"madani"},"surahsAvailable":{"type":"integer","description":"Sourates reellement servables, sur 114.","format":"int32","example":114},"isDefault":{"type":"boolean","description":"Recitateur utilise quand `reciter` est omis."}},"description":"Un moshaf Qalun servable par cette instance."},"ProgressView":{"type":"object","properties":{"learnerId":{"type":"integer","format":"int64"},"locale":{"type":"string"},"timezone":{"type":"string"},"reciterKey":{"type":"string"},"weeklyTargetAyat":{"type":"integer","format":"int32"},"curriculum":{"type":"string"},"curriculumSegments":{"type":"integer","format":"int32"},"segmentsStarted":{"type":"integer","format":"int32"},"segmentsMastered":{"type":"integer","format":"int32"},"ayatInProgress":{"type":"integer","format":"int32"},"prayerReadySurahs":{"type":"integer","format":"int32"},"prayerReadyLabels":{"type":"array","items":{"type":"string"}},"dueNow":{"type":"integer","format":"int32"},"reviewsRecorded":{"type":"integer","format":"int64"},"currentWeek":{"type":"string"}}},"AssignmentView":{"type":"object","properties":{"learnerId":{"type":"integer","format":"int64"},"isoWeek":{"type":"string"},"weekStart":{"type":"string","format":"date"},"weekEnd":{"type":"string","format":"date"},"status":{"type":"string"},"targetAyat":{"type":"integer","format":"int32"},"plannedAyat":{"type":"integer","format":"int32"},"generatedAt":{"type":"string","format":"date-time"},"remaining":{"type":"integer","format":"int32"},"items":{"type":"array","items":{"$ref":"#/components/schemas/SegmentView"}}}},"ChunkMetadata":{"type":"object","properties":{"reciter":{"type":"string"},"riwaya":{"type":"string","description":"Toujours `qalun`.","example":"qalun"},"numbering":{"type":"string","description":"Toujours `madani`.","example":"madani"},"surah":{"type":"integer","format":"int32"},"begin":{"type":"integer","format":"int32"},"end":{"type":"integer","format":"int32"},"ayahCount":{"type":"integer","description":"Versets de la sourate entiere, en madani.","format":"int32","example":285},"rangeAyahCount":{"type":"integer","description":"Versets contenus dans cet extrait.","format":"int32","example":3},"includesBasmala":{"type":"boolean"},"profile":{"type":"string"},"startMs":{"type":"integer","description":"Debut de l'extrait dans la recitation complete, en ms.","format":"int64"},"endMs":{"type":"integer","description":"Fin de l'extrait dans la recitation complete, en ms.","format":"int64"},"durationMs":{"type":"integer","description":"Duree de l'extrait, en ms.","format":"int64"},"audioUrl":{"type":"string","description":"URL absolue a donner a un lecteur pour obtenir le mp3."}},"description":"Metadonnees d'une plage, renvoyees par `format=json`."}}}}