Files
n8n-backup-v2/workflows/WkAdUd9jXTtPagGO.json
2026-02-05 21:59:40 +00:00

136 lines
6.3 KiB
JSON

{
"id": "WkAdUd9jXTtPagGO",
"name": "MAM Series Matcher",
"nodes": [
{
"parameters": {},
"id": "trigger-execute-workflow",
"name": "When Called by Another Workflow",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1,
"position": [
224,
304
]
},
{
"parameters": {
"jsCode": "// Fuzzy match incoming title against followed series\n// Input: title from Execute Workflow trigger\n// Output: matched series or null\n\nconst inputItem = $input.first();\nconst title = inputItem.json.title || '';\nconst metadata = inputItem.json.metadata || {};\n\n// Get all followed series from Supabase\nconst seriesList = $('Get Active Followed Series').all();\n\n// Helper function to normalize strings for comparison\nfunction normalize(str) {\n return str.toLowerCase()\n .replace(/[^a-z0-9\\s]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n// Helper function to extract patterns from title\nfunction extractPatterns(title) {\n const patterns = {\n volume: null,\n author: null,\n cleanTitle: title\n };\n \n // Extract volume numbers (e.g., \"Vol 01\", \"Volume 1\", \"v01\")\n const volumeMatch = title.match(/\\b(?:vol(?:ume)?\\.?\\s*(\\d+)|v(\\d+))\\b/i);\n if (volumeMatch) {\n patterns.volume = parseInt(volumeMatch[1] || volumeMatch[2]);\n }\n \n // Extract author (common patterns: \"Author Name - Title\", \"[Author Name]\")\n const authorMatch1 = title.match(/^([^-\\[\\]]+?)\\s*-\\s*(.+)/);\n const authorMatch2 = title.match(/\\[([^\\]]+)\\]/);\n \n if (authorMatch1) {\n patterns.author = authorMatch1[1].trim();\n patterns.cleanTitle = authorMatch1[2].trim();\n } else if (authorMatch2) {\n patterns.author = authorMatch2[1].trim();\n }\n \n return patterns;\n}\n\n// Extract patterns from input title\nconst inputPatterns = extractPatterns(title);\nconst normalizedInput = normalize(inputPatterns.cleanTitle);\n\n// Try to match against series\nlet bestMatch = null;\nlet bestScore = 0;\n\nfor (const series of seriesList) {\n const seriesName = series.json.series_name || '';\n const author = series.json.author || '';\n const normalizedSeries = normalize(seriesName);\n const normalizedAuthor = normalize(author);\n \n let score = 0;\n \n // Check if series name is contained in title\n if (normalizedInput.includes(normalizedSeries)) {\n score += 50;\n }\n \n // Check if title is contained in series name\n if (normalizedSeries.includes(normalizedInput)) {\n score += 40;\n }\n \n // Check author match\n if (inputPatterns.author && normalizedAuthor) {\n if (normalize(inputPatterns.author).includes(normalizedAuthor)) {\n score += 30;\n }\n }\n \n // Simple word overlap check\n const inputWords = normalizedInput.split(' ').filter(w => w.length > 2);\n const seriesWords = normalizedSeries.split(' ').filter(w => w.length > 2);\n const commonWords = inputWords.filter(w => seriesWords.includes(w));\n score += commonWords.length * 5;\n \n if (score > bestScore) {\n bestScore = score;\n bestMatch = series.json;\n }\n}\n\n// Return match if score is above threshold\nif (bestMatch && bestScore >= 40) {\n return [{\n json: {\n matched: true,\n series_id: bestMatch.id,\n series_name: bestMatch.series_name,\n author: bestMatch.author,\n category: bestMatch.category,\n smb_path: bestMatch.smb_path,\n match_score: bestScore,\n original_title: title,\n extracted_volume: inputPatterns.volume,\n extracted_author: inputPatterns.author\n }\n }];\n} else {\n return [{\n json: {\n matched: false,\n series_id: null,\n series_name: null,\n author: null,\n category: null,\n smb_path: null,\n match_score: bestScore,\n original_title: title\n }\n }];\n}"
},
"id": "code-fuzzy-match",
"name": "Fuzzy Match Title",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
608,
304
]
},
{
"parameters": {
"respondWith": "allIncomingItems",
"options": {}
},
"id": "respond-to-workflow",
"name": "Respond to Workflow",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
800,
304
]
},
{
"parameters": {
"operation": "select",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "followed_series",
"mode": "list",
"cachedResultName": "followed_series"
},
"returnAll": true,
"where": {
"values": [
{
"column": "active",
"value": "true"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
432,
304
],
"id": "2f0708e1-996c-4705-b374-2a119fcd6b18",
"name": "Get Active Followed Series",
"credentials": {
"postgres": {
"id": "dsnKfvOBMkgU21Lt",
"name": "supabase postgres account"
}
}
}
],
"connections": {
"When Called by Another Workflow": {
"main": [
[
{
"node": "Get Active Followed Series",
"type": "main",
"index": 0
}
]
]
},
"Fuzzy Match Title": {
"main": [
[
{
"node": "Respond to Workflow",
"type": "main",
"index": 0
}
]
]
},
"Get Active Followed Series": {
"main": [
[
{
"node": "Fuzzy Match Title",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all",
"saveManualExecutions": true,
"saveExecutionProgress": true
},
"triggerCount": 0,
"versionId": "34e2aea3-0a6e-41fd-b277-207bfd0f6804",
"owner": {
"type": "personal",
"projectId": "FeLO36wNUAcn61Wj",
"projectName": "Ben W <admin@ben.io>",
"personalEmail": "admin@ben.io"
},
"parentFolderId": "6tDyZCwqELStb6Ik",
"isArchived": false
}