Scanner de livres n8n
Ceci est unContent Creation, Multimodal AIworkflow d'automatisation du domainecontenant 16 nœuds.Utilise principalement des nœuds comme Set, Code, Webhook, HttpRequest, OpenAi. Extraire et vérifier les titres de livres à partir de photos d'étagère de livres avec GPT-4o et Google Books
- •Point de terminaison HTTP Webhook (généré automatiquement par n8n)
- •Peut nécessiter les informations d'identification d'authentification de l'API cible
- •Clé API OpenAI
Nœuds utilisés (16)
Catégorie
{
"id": "n1UbcJnl7I5rMDIe",
"meta": {
"instanceId": "7d302cb44fba0420b6a4deb04edff9d7c47e83ef1f3f66f89fe519337b882186",
"templateCredsSetupCompleted": true
},
"name": "n8n submission book scanner",
"tags": [],
"nodes": [
{
"id": "8fc53cb4-d7bc-4fcb-8c6d-1ee90b4ad5b4",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
-896,
1376
],
"webhookId": "365ea003-fe66-4211-ae03-69f1456d768e",
"parameters": {
"path": "365ea003-fe66-4211-ae03-69f1456d768e",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "a140d496-9098-43b2-97df-089a811a909d",
"name": "Répondre à Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
592,
1376
],
"parameters": {
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"respondWith": "json",
"responseBody": "={{$json}}"
},
"typeVersion": 1.4
},
{
"id": "050a5432-4d7e-4855-b09f-5ca40a9e0999",
"name": "Analyser l'image",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
-448,
1376
],
"parameters": {
"text": "=You are a STRICT transformer. Analyze the image of book spines and return only clearly readable titles and authors. \nDo NOT guess. If the author isn't clearly visible, set \"author\": null.\nNormalize capitalization. Deduplicate by title. \nOutput STRICT JSON only:\n{\"books\":[{\"title\":\"string\",\"author\":\"string|null\"}]}\n",
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o-mini",
"cachedResultName": "GPT-4O-MINI"
},
"options": {},
"resource": "image",
"imageUrls": "={{$json.image}}\n",
"operation": "analyze"
},
"credentials": {
"openAiApi": {
"id": "iJ4uczBur5RBMvV4",
"name": "OpenAi account"
}
},
"typeVersion": 1.8
},
{
"id": "50c4d4b8-b164-47fb-b9c6-2234f3cb5952",
"name": "Note adhésive",
"type": "n8n-nodes-base.stickyNote",
"position": [
-992,
1280
],
"parameters": {
"height": 80,
"content": "Webhook connects to front end that passes on JSON with imageURL (string)"
},
"typeVersion": 1
},
{
"id": "d245e1df-a5a2-487c-bd8f-41601be2f05c",
"name": "Note adhésive1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-736,
1520
],
"parameters": {
"height": 80,
"content": "Input is normalized"
},
"typeVersion": 1
},
{
"id": "2c44a9af-f395-45ff-bf4f-91a55c829d3e",
"name": "Note adhésive2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-512,
1280
],
"parameters": {
"height": 80,
"content": "Image is analyzed and transformed."
},
"typeVersion": 1
},
{
"id": "1ec82294-d70b-4ff6-ab34-c7ef156a41b2",
"name": "Note adhésive3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-288,
1520
],
"parameters": {
"content": "Splits the output (in this case, books) into individual items in preparation for the next step which is to verify the book against a known source to confirm the title and author."
},
"typeVersion": 1
},
{
"id": "44f65fae-f829-4b6b-976b-001d071b82ee",
"name": "Note adhésive4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-96,
1280
],
"parameters": {
"height": 80,
"content": "Confirms each title against Google Books"
},
"typeVersion": 1
},
{
"id": "917a2da3-3691-4904-8cbc-4c00ee4ed72e",
"name": "Note adhésive5",
"type": "n8n-nodes-base.stickyNote",
"position": [
112,
1536
],
"parameters": {
"height": 80,
"content": "Normalizes data"
},
"typeVersion": 1
},
{
"id": "62b507e0-5b7d-48a3-9e44-aa4ccf339331",
"name": "Note adhésive6",
"type": "n8n-nodes-base.stickyNote",
"position": [
304,
1280
],
"parameters": {
"height": 80,
"content": "Reaggregates book list and dedupes"
},
"typeVersion": 1
},
{
"id": "94b96cbc-3bd4-4db8-83ff-eb57b4e516c3",
"name": "Note adhésive7",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
1520
],
"parameters": {
"height": 80,
"content": "Returns list back to frontend."
},
"typeVersion": 1
},
{
"id": "c4796062-4c64-474a-85b9-8306585c18b1",
"name": "Entrée normalisée",
"type": "n8n-nodes-base.set",
"position": [
-672,
1376
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "94b88376-fed9-46cf-882f-d4c0d7670350",
"name": "image",
"type": "string",
"value": "={{ ($json.body?.imageUrl || $json.body?.image || $json.imageUrl || $json.image || '').trim() }}\n"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "dbe25007-44da-403b-89bf-bcbf57591d58",
"name": "Division de la liste d'éléments",
"type": "n8n-nodes-base.code",
"position": [
-240,
1376
],
"parameters": {
"jsCode": "const items = await $input.all();\nconst out = [];\n\nfunction stripCodeFence(s) {\n return String(s || '')\n .replace(/^```json\\s*/i, '')\n .replace(/^```\\s*/i, '')\n .replace(/```$/, '')\n .trim();\n}\n\nfunction firstAuthor(a) {\n if (!a) return null;\n // keep a single name for better \"inauthor:\" matching\n const s = String(a);\n const parts = s.split(/\\s*(?:,| and |&)\\s*/i);\n return (parts[0] || '').trim() || null;\n}\n\nfor (const item of items) {\n let books = null;\n\n // Case 1: you already have an object with books[]\n if (Array.isArray(item.json?.books)) {\n books = item.json.books;\n }\n\n // Case 2: you have a string in `content` with ```json ... ```\n if (!books && typeof item.json?.content === 'string') {\n const cleaned = stripCodeFence(item.json.content);\n try {\n const parsed = JSON.parse(cleaned);\n if (Array.isArray(parsed.books)) books = parsed.books;\n } catch (e) {\n // ignore; we'll fall back\n }\n }\n\n if (!books) continue;\n\n for (const b of books) {\n out.push({\n json: {\n title: b.title,\n author: b.author ?? null,\n // helper field only for the search query:\n searchAuthor: firstAuthor(b.author)\n }\n });\n }\n}\n\nreturn out;\n"
},
"typeVersion": 2
},
{
"id": "2017d011-6d85-4844-a08a-0a7e88ae052d",
"name": "Validation du titre",
"type": "n8n-nodes-base.httpRequest",
"position": [
-32,
1376
],
"parameters": {
"url": "https://www.googleapis.com/books/v1/volumes",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "=q",
"value": "={{ \n 'intitle:\"' + $json.title.replace(/\"/g,'') + '\"' +\n ($json.searchAuthor ? ' inauthor:\"' + $json.searchAuthor.replace(/\"/g,'') + '\"' : '')\n}}\n"
},
{
"name": "maxResults",
"value": "5"
},
{
"name": "printType",
"value": "books"
},
{
"name": "orderBy",
"value": "relevance"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "7d94ce36-fc7c-44cb-8df6-6f13293160a4",
"name": "Données normalisées",
"type": "n8n-nodes-base.set",
"position": [
176,
1376
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "67464bb0-8615-41a5-8408-11a95708d200",
"name": "=title",
"type": "string",
"value": "={{ $json.items?.[0]?.volumeInfo?.title || $prevNode('Code').json.title }}\n"
},
{
"id": "8db9c2f0-3193-428e-adab-2745f397233c",
"name": "author",
"type": "string",
"value": "={{ $json.items?.[0]?.volumeInfo?.authors?.[0] || $prevNode('Code').json.author || null }}\n"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "99d74571-f35d-4086-93ce-f7f67768a8f0",
"name": "Réagrégation de la liste",
"type": "n8n-nodes-base.code",
"position": [
384,
1376
],
"parameters": {
"jsCode": "const items = await $input.all();\nconst seen = new Set();\nconst books = [];\n\nfor (const it of items) {\n const t = (it.json.title || '').toLowerCase().trim();\n if (t && !seen.has(t)) {\n seen.add(t);\n books.push({ title: it.json.title, author: it.json.author ?? null });\n }\n}\n\nreturn [{ json: { books } }];\n"
},
"typeVersion": 2
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "5f454fe8-f8b7-4302-820e-ec24a00f13bd",
"connections": {
"8fc53cb4-d7bc-4fcb-8c6d-1ee90b4ad5b4": {
"main": [
[
{
"node": "c4796062-4c64-474a-85b9-8306585c18b1",
"type": "main",
"index": 0
}
]
]
},
"050a5432-4d7e-4855-b09f-5ca40a9e0999": {
"main": [
[
{
"node": "dbe25007-44da-403b-89bf-bcbf57591d58",
"type": "main",
"index": 0
}
]
]
},
"7d94ce36-fc7c-44cb-8df6-6f13293160a4": {
"main": [
[
{
"node": "99d74571-f35d-4086-93ce-f7f67768a8f0",
"type": "main",
"index": 0
}
]
]
},
"dbe25007-44da-403b-89bf-bcbf57591d58": {
"main": [
[
{
"node": "2017d011-6d85-4844-a08a-0a7e88ae052d",
"type": "main",
"index": 0
}
]
]
},
"c4796062-4c64-474a-85b9-8306585c18b1": {
"main": [
[
{
"node": "050a5432-4d7e-4855-b09f-5ca40a9e0999",
"type": "main",
"index": 0
}
]
]
},
"2017d011-6d85-4844-a08a-0a7e88ae052d": {
"main": [
[
{
"node": "7d94ce36-fc7c-44cb-8df6-6f13293160a4",
"type": "main",
"index": 0
}
]
]
},
"99d74571-f35d-4086-93ce-f7f67768a8f0": {
"main": [
[
{
"node": "a140d496-9098-43b2-97df-089a811a909d",
"type": "main",
"index": 0
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Avancé - Création de contenu, IA Multimodale
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
Arlene Martin
@arlenemartinPartager ce workflow