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