From 85abca7906ce842810f7e1a16b06356c9710beb9 Mon Sep 17 00:00:00 2001 From: "ext.jeremy.guillot@maxicoffee.domains" Date: Wed, 26 Mar 2025 22:52:48 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20ajout=20du=20lecteur=20de=20chapitres?= =?UTF-8?q?=20avec=20gestion=20des=20pages,=20des=20modes=20de=20lecture?= =?UTF-8?q?=20et=20des=20param=C3=A8tres=20de=20zoom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/front_vue.mdc | 30 +- .gitignore | 2 + .../reader/application/store/readerStore.js | 187 ++ .../domain/reader/domain/entities/Chapter.js | 30 + .../repository/ChapterRepositoryInterface.js | 31 + .../repository/ApiChapterRepository.js | 29 + .../presentation/components/ChapterReader.vue | 206 ++ .../reader/presentation/pages/ChapterPage.vue | 47 + assets/vue/app/router/index.js | 3 +- public/api-docs.json | 2937 +++++++++++++++++ 10 files changed, 3500 insertions(+), 2 deletions(-) create mode 100644 assets/vue/app/domain/reader/application/store/readerStore.js create mode 100644 assets/vue/app/domain/reader/domain/entities/Chapter.js create mode 100644 assets/vue/app/domain/reader/domain/repository/ChapterRepositoryInterface.js create mode 100644 assets/vue/app/domain/reader/infrastructure/repository/ApiChapterRepository.js create mode 100644 assets/vue/app/domain/reader/presentation/components/ChapterReader.vue create mode 100644 assets/vue/app/domain/reader/presentation/pages/ChapterPage.vue create mode 100644 public/api-docs.json diff --git a/.cursor/rules/front_vue.mdc b/.cursor/rules/front_vue.mdc index b4ca07f..f995ddf 100644 --- a/.cursor/rules/front_vue.mdc +++ b/.cursor/rules/front_vue.mdc @@ -1,10 +1,38 @@ --- description: -globs: +globs: *.vue,*.js alwaysApply: false --- # Architecture Frontend Vue.js +## Introduction +En tant que développeur front-end expérimenté spécialisé en Vue.js, vous devez suivre les meilleures pratiques et standards de développement établis pour ce projet. Votre expertise en Vue.js, TypeScript, et votre maîtrise des patterns de conception modernes sont essentiels pour maintenir une base de code cohérente et maintenable. + +## Stack Technique +- **Framework Principal**: Vue.js 3.x avec Composition API +- **Store Management**: Pinia 3.x +- **Routage**: Vue Router 4.x +- **Styling**: + - TailwindCSS 4.x pour les utilitaires CSS + - HeadlessUI pour les composants accessibles + - Heroicons pour l'iconographie +- **Build Tool**: Vite +- **Testing**: Vitest avec Vue Test Utils +- **Linting & Formatting**: + - ESLint avec la configuration Vue.js + - Prettier pour le formatage + +## Conventions de Nommage +- **Composants**: PascalCase (ex: `MangaCard.vue`, `SearchBar.vue`) +- **Fichiers**: + - Composants: PascalCase avec extension .vue + - Utilitaires: camelCase avec extension .js/.ts + - Tests: PascalCase.spec.ts +- **Props**: camelCase dans le template, PascalCase dans le script +- **Events**: kebab-case dans le template, camelCase dans le script +- **Stores**: camelCase avec suffixe "Store" (ex: `mangaStore.js`) +- **Composables**: camelCase avec préfixe "use" (ex: `useSearch.js`) + ## Structure Générale L'application Vue.js suit une architecture hexagonale (ports & adapters) avec une séparation claire des responsabilités. Le code est organisé en domaines distincts dans le dossier `assets/vue/`. Pour ce qui est du style, on utilise TailwindCss, Headlessui et Heroicons. diff --git a/.gitignore b/.gitignore index d3814ec..9fecd97 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ yarn-error.log /public/manga-images/ /public/cbz/ /public/images/ +src/Controller/TestController.php +.phpunit.cache/test-results diff --git a/assets/vue/app/domain/reader/application/store/readerStore.js b/assets/vue/app/domain/reader/application/store/readerStore.js new file mode 100644 index 0000000..940171a --- /dev/null +++ b/assets/vue/app/domain/reader/application/store/readerStore.js @@ -0,0 +1,187 @@ +import { defineStore } from 'pinia'; +import { Chapter } from '../../domain/entities/Chapter'; +import { ApiChapterRepository } from '../../infrastructure/repository/ApiChapterRepository'; + +export const useReaderStore = defineStore('reader', { + state: () => ({ + currentChapter: null, + currentPage: 0, + readingMode: 'single', // 'single' ou 'infinite' + readingDirection: 'ltr', // 'ltr' ou 'rtl' + zoom: 1, + isLoading: false, + error: null, + pages: [], + totalPages: 0, + _debug: { + lastUpdate: Date.now(), + lastAction: null + } + }), + + getters: { + isFirstPage: state => state.currentPage === 0, + isLastPage: state => state.currentPage === state.totalPages - 1, + currentPageData: state => { + const data = state.pages[state.currentPage]; + console.log('Getting currentPageData:', { + currentPage: state.currentPage, + hasData: !!data, + totalPages: state.totalPages, + pagesLength: state.pages.length, + lastUpdate: state._debug.lastUpdate, + lastAction: state._debug.lastAction + }); + return data; + } + }, + + actions: { + async loadChapter(chapterId) { + console.log('Loading chapter:', chapterId); + this.isLoading = true; + this.error = null; + this._debug.lastAction = 'loadChapter'; + try { + const repository = new ApiChapterRepository(); + + // Charger les informations du chapitre + console.log('Fetching chapter info...'); + const chapterData = await repository.getChapter(chapterId); + console.log('Chapter data received:', chapterData); + this.currentChapter = Chapter.create(chapterData); + + // Charger la liste des pages + console.log('Fetching pages info...'); + const pagesData = await repository.getChapterPages(chapterId); + console.log('Pages data received:', pagesData); + + // Initialiser le tableau avec des placeholders + this.pages = new Array(pagesData.totalItems).fill(null); + this.totalPages = pagesData.totalItems; + this._debug.lastUpdate = Date.now(); + + console.log('Pages array initialized:', { + length: this.pages.length, + totalPages: this.totalPages + }); + + // Charger la première page + if (this.totalPages > 0) { + console.log('Loading first page...'); + this.currentPage = 0; + await this.loadCurrentPageData(); + } else { + console.warn('No pages available for this chapter'); + } + } catch (error) { + console.error('Error loading chapter:', error); + this.error = error.message; + } finally { + this.isLoading = false; + } + }, + + async loadCurrentPageData() { + if (!this.currentChapter) { + console.error('No current chapter loaded'); + return; + } + + if (this.currentPage < 0 || this.currentPage >= this.totalPages) { + console.error('Invalid page index:', this.currentPage); + return; + } + + const pageNumber = this.currentPage + 1; // Convertir en 1-based pour l'API + console.log('Loading page data:', { + pageNumber, + chapterId: this.currentChapter.id + }); + + if (this.pages[this.currentPage]?.base64Content) { + console.log('Page already loaded, skipping fetch'); + return; + } + + this.isLoading = true; + this.error = null; + this._debug.lastAction = 'loadCurrentPageData'; + try { + const repository = new ApiChapterRepository(); + console.log('Fetching page from API...'); + const pageData = await repository.getChapterPage(this.currentChapter.id, pageNumber); + console.log('Page data received:', { + hasContent: !!pageData?.base64Content, + mimeType: pageData?.mimeType, + pageNumber: pageData?.pageNumber + }); + + // Vérifier que les données sont valides + if (!pageData || !pageData.base64Content) { + throw new Error("Données de page invalides reçues de l'API"); + } + + // Créer une nouvelle référence du tableau pour déclencher la réactivité + const newPages = [...this.pages]; + newPages[this.currentPage] = { + id: pageData.id, + pageNumber: pageData.pageNumber, + base64Content: pageData.base64Content, + mimeType: pageData.mimeType, + dimensions: pageData.dimensions + }; + this.pages = newPages; + this._debug.lastUpdate = Date.now(); + + console.log('Page data updated in store:', { + pageIndex: this.currentPage, + hasContent: !!this.pages[this.currentPage]?.base64Content + }); + } catch (error) { + console.error('Error loading page:', error); + this.error = error.message; + } finally { + this.isLoading = false; + } + }, + + async nextPage() { + if (!this.isLastPage) { + console.log('Moving to next page'); + this.currentPage++; + this._debug.lastAction = 'nextPage'; + this._debug.lastUpdate = Date.now(); + await this.loadCurrentPageData(); + } + }, + + async previousPage() { + if (!this.isFirstPage) { + console.log('Moving to previous page'); + this.currentPage--; + this._debug.lastAction = 'previousPage'; + this._debug.lastUpdate = Date.now(); + await this.loadCurrentPageData(); + } + }, + + setReadingMode(mode) { + this.readingMode = mode; + this._debug.lastAction = 'setReadingMode'; + this._debug.lastUpdate = Date.now(); + }, + + setReadingDirection(direction) { + this.readingDirection = direction; + this._debug.lastAction = 'setReadingDirection'; + this._debug.lastUpdate = Date.now(); + }, + + setZoom(level) { + this.zoom = level; + this._debug.lastAction = 'setZoom'; + this._debug.lastUpdate = Date.now(); + } + } +}); diff --git a/assets/vue/app/domain/reader/domain/entities/Chapter.js b/assets/vue/app/domain/reader/domain/entities/Chapter.js new file mode 100644 index 0000000..ea84aef --- /dev/null +++ b/assets/vue/app/domain/reader/domain/entities/Chapter.js @@ -0,0 +1,30 @@ +export class Chapter { + constructor({ id, mangaId, number, title, pages = [], read = false, lastReadPage = 0 }) { + this.id = id; + this.mangaId = mangaId; + this.number = number; + this.title = title; + this.pages = pages; + this.read = read; + this.lastReadPage = lastReadPage; + } + + static create(data) { + return new Chapter(data); + } + + markAsRead() { + this.read = true; + this.lastReadPage = this.pages.length; + } + + markAsUnread() { + this.read = false; + this.lastReadPage = 0; + } + + updateLastReadPage(pageNumber) { + this.lastReadPage = pageNumber; + this.read = pageNumber === this.pages.length; + } +} diff --git a/assets/vue/app/domain/reader/domain/repository/ChapterRepositoryInterface.js b/assets/vue/app/domain/reader/domain/repository/ChapterRepositoryInterface.js new file mode 100644 index 0000000..3cbac03 --- /dev/null +++ b/assets/vue/app/domain/reader/domain/repository/ChapterRepositoryInterface.js @@ -0,0 +1,31 @@ +export class ChapterRepositoryInterface { + /** + * Récupère les informations d'un chapitre + * @param {string} chapterId - L'identifiant du chapitre + * @returns {Promise} Les informations du chapitre + */ + async getChapter(chapterId) { + throw new Error('Method not implemented'); + } + + /** + * Récupère la liste des pages d'un chapitre + * @param {string} chapterId - L'identifiant du chapitre + * @param {number} page - Le numéro de page + * @param {number} itemsPerPage - Le nombre d'éléments par page + * @returns {Promise} La liste des pages avec leurs métadonnées + */ + async getChapterPages(chapterId, page = 1, itemsPerPage = 20) { + throw new Error('Method not implemented'); + } + + /** + * Récupère une page spécifique d'un chapitre + * @param {string} chapterId - L'identifiant du chapitre + * @param {number} pageNumber - Le numéro de la page + * @returns {Promise} Les données de la page + */ + async getChapterPage(chapterId, pageNumber) { + throw new Error('Method not implemented'); + } +} diff --git a/assets/vue/app/domain/reader/infrastructure/repository/ApiChapterRepository.js b/assets/vue/app/domain/reader/infrastructure/repository/ApiChapterRepository.js new file mode 100644 index 0000000..09665ff --- /dev/null +++ b/assets/vue/app/domain/reader/infrastructure/repository/ApiChapterRepository.js @@ -0,0 +1,29 @@ +import { ChapterRepositoryInterface } from '../../domain/repository/ChapterRepositoryInterface'; + +export class ApiChapterRepository extends ChapterRepositoryInterface { + async getChapter(chapterId) { + const response = await fetch(`/api/reader/chapter/${chapterId}`); + if (!response.ok) { + throw new Error('Failed to fetch chapter'); + } + return response.json(); + } + + async getChapterPages(chapterId, page = 1, itemsPerPage = 20) { + const response = await fetch( + `/api/reader/chapter/${chapterId}/pages?page=${page}&itemsPerPage=${itemsPerPage}` + ); + if (!response.ok) { + throw new Error('Failed to fetch chapter pages'); + } + return response.json(); + } + + async getChapterPage(chapterId, pageNumber) { + const response = await fetch(`/api/reader/chapter/${chapterId}/page/${pageNumber}`); + if (!response.ok) { + throw new Error('Failed to fetch chapter page'); + } + return response.json(); + } +} diff --git a/assets/vue/app/domain/reader/presentation/components/ChapterReader.vue b/assets/vue/app/domain/reader/presentation/components/ChapterReader.vue new file mode 100644 index 0000000..c808093 --- /dev/null +++ b/assets/vue/app/domain/reader/presentation/components/ChapterReader.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/assets/vue/app/domain/reader/presentation/pages/ChapterPage.vue b/assets/vue/app/domain/reader/presentation/pages/ChapterPage.vue new file mode 100644 index 0000000..90d917d --- /dev/null +++ b/assets/vue/app/domain/reader/presentation/pages/ChapterPage.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/assets/vue/app/router/index.js b/assets/vue/app/router/index.js index 05ca272..65592d3 100644 --- a/assets/vue/app/router/index.js +++ b/assets/vue/app/router/index.js @@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'; import Layout from '../shared/components/layout/Layout.vue'; import HomePage from '../domain/manga/presentation/pages/HomePage.vue'; import MangaDetails from '../domain/manga/presentation/pages/MangaDetails.vue'; +import ChapterPage from '../domain/reader/presentation/pages/ChapterPage.vue'; // Placeholder component for new routes const PlaceholderComponent = { @@ -48,7 +49,7 @@ const routes = [ { path: '/reader/:chapterId', name: 'reader', - component: PlaceholderComponent, + component: ChapterPage, props: { title: 'Lecteur' } }, // Pages placeholder avec chargement différé diff --git a/public/api-docs.json b/public/api-docs.json new file mode 100644 index 0000000..da1c507 --- /dev/null +++ b/public/api-docs.json @@ -0,0 +1,2937 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Mangarr API", + "description": "", + "version": "1.0.0" + }, + "servers": [ + { + "url": "/", + "description": "" + } + ], + "paths": { + "/api/manga/chapters/fetch": { + "post": { + "operationId": "api_mangachaptersfetch_post", + "tags": [ + "MangaChapters" + ], + "responses": { + "202": { + "description": "MangaChapters resource created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/MangaChapters" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters.jsonhal" + } + } + }, + "links": {} + }, + "400": { + "description": "Invalid input" + }, + "422": { + "description": "Unprocessable entity" + } + }, + "summary": "Creates a MangaChapters resource.", + "description": "Creates a MangaChapters resource.", + "parameters": [], + "requestBody": { + "description": "The new MangaChapters resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/MangaChapters" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters.jsonhal" + } + } + }, + "required": true + }, + "deprecated": false + }, + "parameters": [] + }, + "/api/mangadex-search": { + "get": { + "operationId": "api_mangadex-search_get", + "tags": [ + "Mangadex" + ], + "responses": { + "200": { + "description": "Mangadex resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Mangadex.MangaSearchCollection" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Mangadex.MangaSearchCollection.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Mangadex.MangaSearchCollection" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Mangadex.MangaSearchCollection.jsonhal" + } + } + } + }, + "404": { + "description": "Resource not found" + } + }, + "summary": "Retrieves a Mangadex resource.", + "description": "Retrieves a Mangadex resource.", + "parameters": [ + { + "name": "title", + "in": "query", + "description": "The title to search for", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string" + }, + "style": "form", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/mangas": { + "get": { + "operationId": "api_mangas_get_collection", + "tags": [ + "Manga" + ], + "responses": { + "200": { + "description": "Manga collection", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Manga.MangaCollection" + } + } + }, + "application/ld+json": { + "schema": { + "type": "object", + "properties": { + "hydra:member": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Manga.MangaCollection.jsonld" + } + }, + "hydra:totalItems": { + "type": "integer", + "minimum": 0 + }, + "hydra:view": { + "type": "object", + "properties": { + "@id": { + "type": "string", + "format": "iri-reference" + }, + "@type": { + "type": "string" + }, + "hydra:first": { + "type": "string", + "format": "iri-reference" + }, + "hydra:last": { + "type": "string", + "format": "iri-reference" + }, + "hydra:previous": { + "type": "string", + "format": "iri-reference" + }, + "hydra:next": { + "type": "string", + "format": "iri-reference" + } + }, + "example": { + "@id": "string", + "type": "string", + "hydra:first": "string", + "hydra:last": "string", + "hydra:previous": "string", + "hydra:next": "string" + } + }, + "hydra:search": { + "type": "object", + "properties": { + "@type": { + "type": "string" + }, + "hydra:template": { + "type": "string" + }, + "hydra:variableRepresentation": { + "type": "string" + }, + "hydra:mapping": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@type": { + "type": "string" + }, + "variable": { + "type": "string" + }, + "property": { + "type": [ + "string", + "null" + ] + }, + "required": { + "type": "boolean" + } + } + } + } + } + } + }, + "required": [ + "hydra:member" + ] + } + }, + "text/html": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Manga.MangaCollection" + } + } + }, + "application/hal+json": { + "schema": { + "type": "object", + "properties": { + "_embedded": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Manga.MangaCollection.jsonhal" + } + }, + "totalItems": { + "type": "integer", + "minimum": 0 + }, + "itemsPerPage": { + "type": "integer", + "minimum": 0 + }, + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + }, + "first": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + }, + "last": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + }, + "next": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + }, + "previous": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + } + }, + "required": [ + "_links", + "_embedded" + ] + } + } + } + } + }, + "summary": "Retrieves the collection of Manga resources.", + "description": "Retrieves the collection of Manga resources.", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "The collection page number", + "required": false, + "deprecated": false, + "allowEmptyValue": true, + "schema": { + "type": "integer", + "default": 1 + }, + "style": "form", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/mangas/by-id/{id}": { + "get": { + "operationId": "api_mangasby-id_id_get", + "tags": [ + "Manga" + ], + "responses": { + "200": { + "description": "Manga resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Manga.MangaDetail" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Manga.MangaDetail.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Manga.MangaDetail" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Manga.MangaDetail.jsonhal" + } + } + } + }, + "404": { + "description": "Resource not found" + } + }, + "summary": "Retrieves a Manga resource.", + "description": "Retrieves a Manga resource.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The manga identifier", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string" + }, + "style": "simple", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/mangas/by-slug/{slug}": { + "get": { + "operationId": "get_manga_by_slug", + "tags": [ + "Manga" + ], + "responses": { + "200": { + "description": "Manga resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Manga.MangaDetail" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Manga.MangaDetail.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Manga.MangaDetail" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Manga.MangaDetail.jsonhal" + } + } + } + }, + "404": { + "description": "Resource not found" + } + }, + "summary": "Retrieves a Manga resource.", + "description": "Retrieves a Manga resource.", + "parameters": [ + { + "name": "slug", + "in": "path", + "description": "GetMangaBySlugResource identifier", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string" + }, + "style": "simple", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/mangas/create": { + "post": { + "operationId": "api_mangascreate_post", + "tags": [ + "Manga" + ], + "responses": { + "201": { + "description": "Manga resource created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Manga" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Manga.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Manga" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Manga.jsonhal" + } + } + }, + "links": {} + }, + "400": { + "description": "Invalid input" + }, + "422": { + "description": "Unprocessable entity" + } + }, + "summary": "Create a new manga directly", + "description": "Creates a new manga with provided data", + "parameters": [], + "requestBody": { + "description": "The new Manga resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Manga" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Manga.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Manga" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Manga.jsonhal" + } + } + }, + "required": true + }, + "deprecated": false + }, + "parameters": [] + }, + "/api/mangas/create-from-mangadex": { + "post": { + "operationId": "api_mangascreate-from-mangadex_post", + "tags": [ + "Manga" + ], + "responses": { + "201": { + "description": "Manga resource created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Manga" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Manga.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Manga" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Manga.jsonhal" + } + } + }, + "links": {} + }, + "400": { + "description": "Invalid input" + }, + "422": { + "description": "Unprocessable entity" + } + }, + "summary": "Create a new manga from Mangadex", + "description": "Creates a new manga by fetching its data from Mangadex using an external ID", + "parameters": [], + "requestBody": { + "description": "The new Manga resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Manga" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Manga.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Manga" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Manga.jsonhal" + } + } + }, + "required": true + }, + "deprecated": false + }, + "parameters": [] + }, + "/api/mangas/search": { + "get": { + "operationId": "api_mangassearch_get", + "tags": [ + "Manga" + ], + "responses": { + "200": { + "description": "R\u00e9sultats de la recherche", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaSearchItem" + } + } + } + } + } + } + }, + "400": { + "description": "Param\u00e8tres de recherche invalides" + } + }, + "summary": "Recherche des mangas dans la biblioth\u00e8que locale", + "description": "Recherche des mangas par titre, slug ou auteur (minimum 3 caract\u00e8res)", + "parameters": [ + { + "name": "q", + "in": "query", + "description": "Terme de recherche (minimum 3 caract\u00e8res)", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string", + "minLength": 3 + }, + "style": "form", + "explode": false, + "allowReserved": false + }, + { + "name": "page", + "in": "query", + "description": "Num\u00e9ro de page", + "required": false, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "integer", + "default": 1, + "minimum": 1 + }, + "style": "form", + "explode": false, + "allowReserved": false + }, + { + "name": "limit", + "in": "query", + "description": "Nombre de r\u00e9sultats par page", + "required": false, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "integer", + "default": 20, + "minimum": 1, + "maximum": 50 + }, + "style": "form", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/mangas/{id}/chapters": { + "get": { + "operationId": "api_mangas_idchapters_get", + "tags": [ + "MangaChapters" + ], + "responses": { + "200": { + "description": "MangaChapters resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters.ChapterCollection" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters.ChapterCollection.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/MangaChapters.ChapterCollection" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/MangaChapters.ChapterCollection.jsonhal" + } + } + } + }, + "404": { + "description": "Resource not found" + } + }, + "summary": "Retrieves a MangaChapters resource.", + "description": "Retrieves a MangaChapters resource.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The manga identifier", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string" + }, + "style": "simple", + "explode": false, + "allowReserved": false + }, + { + "name": "page", + "in": "query", + "description": "The page number", + "required": false, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "integer", + "default": 1 + }, + "style": "form", + "explode": false, + "allowReserved": false + }, + { + "name": "limit", + "in": "query", + "description": "Number of items per page", + "required": false, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "integer", + "default": 20 + }, + "style": "form", + "explode": false, + "allowReserved": false + }, + { + "name": "sortOrder", + "in": "query", + "description": "Sort order for chapters", + "required": false, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc" + }, + "style": "form", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/reader/chapter/{chapterId}": { + "get": { + "operationId": "api_readerchapter_chapterId_get", + "tags": [ + "Reader" + ], + "responses": { + "200": { + "description": "Contexte du chapitre", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "number": { + "type": "string" + }, + "manga": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "navigation": { + "type": "object", + "properties": { + "previous": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string" + }, + "number": { + "type": "string" + } + } + }, + "next": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string" + }, + "number": { + "type": "string" + } + } + } + } + } + } + } + } + } + }, + "404": { + "description": "Chapitre non trouv\u00e9" + } + }, + "summary": "R\u00e9cup\u00e8re le contexte d'un chapitre", + "description": "Retourne les m\u00e9tadonn\u00e9es du chapitre et sa navigation", + "parameters": [ + { + "name": "chapterId", + "in": "path", + "description": "", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string" + }, + "style": "simple", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/reader/chapter/{chapterId}/page/{pageNumber}": { + "get": { + "operationId": "api_readerchapter_chapterIdpage_pageNumber_get", + "tags": [ + "Reader" + ], + "responses": { + "200": { + "description": "Page du chapitre", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "pageNumber": { + "type": "integer" + }, + "base64Content": { + "type": "string", + "description": "Contenu de l'image en base64" + }, + "mimeType": { + "type": "string", + "example": "image/jpeg" + }, + "dimensions": { + "type": "object", + "properties": { + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + } + } + } + } + } + }, + "404": { + "description": "Chapitre ou page non trouv\u00e9" + } + }, + "summary": "R\u00e9cup\u00e8re une page sp\u00e9cifique d'un chapitre", + "description": "Retourne le contenu d'une page en base64 avec ses m\u00e9tadonn\u00e9es", + "parameters": [ + { + "name": "chapterId", + "in": "path", + "description": "L'identifiant du chapitre", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string" + }, + "style": "simple", + "explode": false, + "allowReserved": false + }, + { + "name": "pageNumber", + "in": "path", + "description": "Le num\u00e9ro de la page \u00e0 r\u00e9cup\u00e9rer", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "integer", + "minimum": 1 + }, + "style": "simple", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/reader/chapter/{chapterId}/pages": { + "get": { + "operationId": "api_readerchapter_chapterIdpages_get", + "tags": [ + "Reader" + ], + "responses": { + "200": { + "description": "Collection pagin\u00e9e des pages du chapitre", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "number": { + "type": "integer" + }, + "dimensions": { + "type": "object", + "properties": { + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + } + } + } + }, + "totalItems": { + "type": "integer" + }, + "currentPage": { + "type": "integer" + }, + "itemsPerPage": { + "type": "integer" + }, + "totalPages": { + "type": "integer" + } + } + } + } + } + }, + "404": { + "description": "Chapitre non trouv\u00e9" + } + }, + "summary": "R\u00e9cup\u00e8re les pages d'un chapitre", + "description": "Retourne une collection pagin\u00e9e des pages du chapitre", + "parameters": [ + { + "name": "chapterId", + "in": "path", + "description": "", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string" + }, + "style": "simple", + "explode": false, + "allowReserved": false + }, + { + "name": "page", + "in": "query", + "description": "", + "required": false, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "integer", + "default": 1 + }, + "style": "form", + "explode": false, + "allowReserved": false + }, + { + "name": "itemsPerPage", + "in": "query", + "description": "", + "required": false, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "integer", + "default": 20 + }, + "style": "form", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + }, + "/api/scraping/chapters": { + "post": { + "operationId": "api_scrapingchapters_post", + "tags": [ + "Scraping" + ], + "responses": { + "202": { + "description": "Scraping resource created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Scraping" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Scraping.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Scraping" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Scraping.jsonhal" + } + } + }, + "links": {} + }, + "400": { + "description": "Invalid input" + }, + "422": { + "description": "Unprocessable entity" + } + }, + "summary": "Creates a Scraping resource.", + "description": "Creates a Scraping resource.", + "parameters": [], + "requestBody": { + "description": "The new Scraping resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Scraping" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Scraping.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Scraping" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Scraping.jsonhal" + } + } + }, + "required": true + }, + "deprecated": false + }, + "parameters": [] + }, + "/api/scraping/jobs/{jobId}/status": { + "get": { + "operationId": "api_scrapingjobs_jobIdstatus_get", + "tags": [ + "Scraping" + ], + "responses": { + "200": { + "description": "Scraping resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Scraping" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Scraping.jsonld" + } + }, + "text/html": { + "schema": { + "$ref": "#/components/schemas/Scraping" + } + }, + "application/hal+json": { + "schema": { + "$ref": "#/components/schemas/Scraping.jsonhal" + } + } + } + }, + "404": { + "description": "Resource not found" + } + }, + "summary": "Retrieves a Scraping resource.", + "description": "Retrieves a Scraping resource.", + "parameters": [ + { + "name": "jobId", + "in": "path", + "description": "ScrapingStatusResponse identifier", + "required": true, + "deprecated": false, + "allowEmptyValue": false, + "schema": { + "type": "string" + }, + "style": "simple", + "explode": false, + "allowReserved": false + } + ], + "deprecated": false + }, + "parameters": [] + } + }, + "components": { + "schemas": { + "ChapterListItem": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "id": { + "type": "string" + }, + "number": { + "type": "number" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "volume": { + "type": [ + "integer", + "null" + ] + }, + "isVisible": { + "type": "boolean" + }, + "createdAt": { + "type": "string" + } + } + }, + "ChapterListItem.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "id": { + "type": "string" + }, + "number": { + "type": "number" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "volume": { + "type": [ + "integer", + "null" + ] + }, + "isVisible": { + "type": "boolean" + }, + "createdAt": { + "type": "string" + } + } + }, + "ChapterListItem.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "id": { + "type": "string" + }, + "number": { + "type": "number" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "volume": { + "type": [ + "integer", + "null" + ] + }, + "isVisible": { + "type": "boolean" + }, + "createdAt": { + "type": "string" + } + } + }, + "Manga": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "title", + "slug", + "description", + "author", + "publicationYear", + "genres", + "status" + ], + "properties": { + "title": { + "minLength": 1, + "maxLength": 255, + "type": "string" + }, + "slug": { + "pattern": "^([a-z0-9-]+)$", + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "minimum": 1900, + "maximum": 2100, + "type": "integer" + }, + "genres": { + "minItems": 1, + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "enum": [ + "ongoing", + "completed", + "hiatus" + ], + "type": "string" + }, + "externalId": { + "type": [ + "string", + "null" + ] + }, + "imageUrl": { + "format": "uri", + "externalDocs": { + "url": "https://schema.org/url" + }, + "type": [ + "string", + "null" + ] + }, + "rating": { + "minimum": 0, + "maximum": 5, + "type": [ + "number", + "null" + ] + } + } + }, + "Manga.MangaCollection": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaListItem" + } + }, + "total": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "hasNextPage": { + "type": "boolean" + }, + "hasPreviousPage": { + "type": "boolean" + } + } + }, + "Manga.MangaCollection.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaListItem.jsonhal" + } + }, + "total": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "hasNextPage": { + "type": "boolean" + }, + "hasPreviousPage": { + "type": "boolean" + } + } + }, + "Manga.MangaCollection.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaListItem.jsonld" + } + }, + "total": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "hasNextPage": { + "type": "boolean" + }, + "hasPreviousPage": { + "type": "boolean" + } + } + }, + "Manga.MangaDetail": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "externalId": { + "type": [ + "string", + "null" + ] + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "rating": { + "type": [ + "number", + "null" + ] + } + } + }, + "Manga.MangaDetail.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + }, + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "externalId": { + "type": [ + "string", + "null" + ] + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "rating": { + "type": [ + "number", + "null" + ] + } + } + }, + "Manga.MangaDetail.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "externalId": { + "type": [ + "string", + "null" + ] + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "rating": { + "type": [ + "number", + "null" + ] + } + } + }, + "Manga.MangaSearchCollection": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaSearchItem" + } + } + } + }, + "Manga.MangaSearchCollection.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaSearchItem.jsonhal" + } + } + } + }, + "Manga.MangaSearchCollection.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaSearchItem.jsonld" + } + } + } + }, + "Manga.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "title", + "slug", + "description", + "author", + "publicationYear", + "genres", + "status" + ], + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + }, + "title": { + "minLength": 1, + "maxLength": 255, + "type": "string" + }, + "slug": { + "pattern": "^([a-z0-9-]+)$", + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "minimum": 1900, + "maximum": 2100, + "type": "integer" + }, + "genres": { + "minItems": 1, + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "enum": [ + "ongoing", + "completed", + "hiatus" + ], + "type": "string" + }, + "externalId": { + "type": [ + "string", + "null" + ] + }, + "imageUrl": { + "format": "uri", + "externalDocs": { + "url": "https://schema.org/url" + }, + "type": [ + "string", + "null" + ] + }, + "rating": { + "minimum": 0, + "maximum": 5, + "type": [ + "number", + "null" + ] + } + } + }, + "Manga.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "title", + "slug", + "description", + "author", + "publicationYear", + "genres", + "status" + ], + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "title": { + "minLength": 1, + "maxLength": 255, + "type": "string" + }, + "slug": { + "pattern": "^([a-z0-9-]+)$", + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "minimum": 1900, + "maximum": 2100, + "type": "integer" + }, + "genres": { + "minItems": 1, + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "enum": [ + "ongoing", + "completed", + "hiatus" + ], + "type": "string" + }, + "externalId": { + "type": [ + "string", + "null" + ] + }, + "imageUrl": { + "format": "uri", + "externalDocs": { + "url": "https://schema.org/url" + }, + "type": [ + "string", + "null" + ] + }, + "rating": { + "minimum": 0, + "maximum": 5, + "type": [ + "number", + "null" + ] + } + } + }, + "MangaChapters": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "externalId" + ], + "properties": { + "externalId": { + "type": "string" + } + } + }, + "MangaChapters.ChapterCollection": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChapterListItem" + } + }, + "total": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "hasNextPage": { + "type": "boolean" + }, + "hasPreviousPage": { + "type": "boolean" + } + } + }, + "MangaChapters.ChapterCollection.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChapterListItem.jsonhal" + } + }, + "total": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "hasNextPage": { + "type": "boolean" + }, + "hasPreviousPage": { + "type": "boolean" + } + } + }, + "MangaChapters.ChapterCollection.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChapterListItem.jsonld" + } + }, + "total": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "hasNextPage": { + "type": "boolean" + }, + "hasPreviousPage": { + "type": "boolean" + } + } + }, + "MangaChapters.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "externalId" + ], + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + }, + "externalId": { + "type": "string" + } + } + }, + "MangaChapters.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "externalId" + ], + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "externalId": { + "type": "string" + } + } + }, + "MangaListItem": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "rating": { + "type": [ + "number", + "null" + ] + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "MangaListItem.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "rating": { + "type": [ + "number", + "null" + ] + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "MangaListItem.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "rating": { + "type": [ + "number", + "null" + ] + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + }, + "MangaSearchItem": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "externalId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "thumbnailUrl": { + "type": [ + "string", + "null" + ] + }, + "rating": { + "type": [ + "number", + "null" + ] + } + } + }, + "MangaSearchItem.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "externalId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "thumbnailUrl": { + "type": [ + "string", + "null" + ] + }, + "rating": { + "type": [ + "number", + "null" + ] + } + } + }, + "MangaSearchItem.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "externalId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publicationYear": { + "type": "integer" + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "imageUrl": { + "type": [ + "string", + "null" + ] + }, + "thumbnailUrl": { + "type": [ + "string", + "null" + ] + }, + "rating": { + "type": [ + "number", + "null" + ] + } + } + }, + "Mangadex.MangaSearchCollection": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaSearchItem" + } + } + } + }, + "Mangadex.MangaSearchCollection.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaSearchItem.jsonhal" + } + } + } + }, + "Mangadex.MangaSearchCollection.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MangaSearchItem.jsonld" + } + } + } + }, + "Reader": { + "type": "object", + "description": "", + "deprecated": false + }, + "Reader.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + } + } + }, + "Reader.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + } + } + }, + "Scraping": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "mangaId", + "chapterNumber", + "sourceId" + ], + "properties": { + "mangaId": { + "description": "ID du manga", + "type": "string" + }, + "chapterNumber": { + "description": "Num\u00e9ro du chapitre", + "type": "string" + }, + "sourceId": { + "description": "ID de la source", + "type": "string" + } + } + }, + "Scraping.jsonhal": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "mangaId", + "chapterNumber", + "sourceId" + ], + "properties": { + "_links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "iri-reference" + } + } + } + } + }, + "mangaId": { + "description": "ID du manga", + "type": "string" + }, + "chapterNumber": { + "description": "Num\u00e9ro du chapitre", + "type": "string" + }, + "sourceId": { + "description": "ID de la source", + "type": "string" + } + } + }, + "Scraping.jsonld": { + "type": "object", + "description": "", + "deprecated": false, + "required": [ + "mangaId", + "chapterNumber", + "sourceId" + ], + "properties": { + "@context": { + "readOnly": true, + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "@vocab": { + "type": "string" + }, + "hydra": { + "type": "string", + "enum": [ + "http://www.w3.org/ns/hydra/core#" + ] + } + }, + "required": [ + "@vocab", + "hydra" + ], + "additionalProperties": true + } + ] + }, + "@id": { + "readOnly": true, + "type": "string" + }, + "@type": { + "readOnly": true, + "type": "string" + }, + "mangaId": { + "description": "ID du manga", + "type": "string" + }, + "chapterNumber": { + "description": "Num\u00e9ro du chapitre", + "type": "string" + }, + "sourceId": { + "description": "ID de la source", + "type": "string" + } + } + } + }, + "responses": {}, + "parameters": {}, + "examples": {}, + "requestBodies": {}, + "headers": {}, + "securitySchemes": { + "access_token": { + "type": "http", + "description": "", + "scheme": "bearer" + } + } + }, + "security": [ + { + "access_token": [] + } + ], + "tags": [] +} \ No newline at end of file