{
  "id": "o3uM1xDLTAKw4D6E",
  "name": "🎬 Pompeo — Media Library Sync [Schedule]",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtHour": 3
            }
          ]
        }
      },
      "id": "b0ccc657-6f9e-43b7-b31e-e753e7c90c53",
      "name": "⏰ Cron",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        0,
        304
      ]
    },
    {
      "parameters": {
        "url": "http://radarr.media.svc.cluster.local:7878/radarr/api/v3/movie?apikey=922d1405ab1147019d98a2997d941765",
        "options": {}
      },
      "id": "4b7a2a61-e7ca-41e4-b6fc-b1d112697306",
      "name": "🎬 HTTP Radarr",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        224,
        192
      ]
    },
    {
      "parameters": {
        "url": "http://sonarr.media.svc.cluster.local:8989/sonarr/api/v3/series?apikey=22140655993a4ff6bf12314813ec6982",
        "options": {}
      },
      "id": "a23916f3-e727-4553-bb62-ad3f2602c1f2",
      "name": "📺 HTTP Sonarr",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        224,
        432
      ]
    },
    {
      "parameters": {},
      "id": "986ab3c1-3474-451a-bfa9-dfbeffbaca91",
      "name": "🔀 Merge",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 2,
      "position": [
        496,
        304
      ]
    },
    {
      "parameters": {
        "jsCode": "const allItems = $input.all();\nconst movies = [];\nconst series = [];\n\nfor (const item of allItems) {\n  const d = item.json;\n  // d may be a full array response or a single object\n  const src = Array.isArray(d) ? d : [d];\n  for (const s of src) {\n    if (s != null && s.tmdbId !== undefined) {\n      movies.push({\n        type: 'movie',\n        title: s.title,\n        year: s.year,\n        genres: s.genres || [],\n        status: s.hasFile ? 'available' : 'missing',\n        source: 'radarr',\n        source_id: s.tmdbId,\n        monitored: s.monitored\n      });\n    } else if (s != null && s.tvdbId !== undefined) {\n      series.push({\n        type: 'series',\n        title: s.title,\n        year: s.year,\n        genres: s.genres || [],\n        status: s.monitored ? 'monitored' : 'unmonitored',\n        source: 'sonarr',\n        source_id: s.tvdbId,\n        monitored: s.monitored\n      });\n    }\n  }\n}\n\nconst items = [...movies, ...series];\n\nconst genreMap = {};\nfor (const item of items) {\n  for (const genre of (item.genres || [])) {\n    if (!genreMap[genre]) genreMap[genre] = [];\n    genreMap[genre].push(item.title);\n  }\n}\n\nlet library_summary = '';\nfor (const [genre, titles] of Object.entries(genreMap)) {\n  library_summary += genre + ': ' + titles.join(', ') + '\\n';\n}\n\nreturn [{ json: { items, library_summary } }];"
      },
      "id": "a4d4acff-c5b1-450f-8428-402ca6e5969c",
      "name": "🔀 Merge Libreria",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        704,
        304
      ]
    },
    {
      "parameters": {
        "url": "https://api.github.com/copilot_internal/v2/token",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "options": {}
      },
      "id": "57d1cfcb-a838-4382-99d9-4e2aff920511",
      "name": "🔑 Ottieni Token Copilot",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        928,
        304
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "vBwUxlzKrX3oDHyN",
          "name": "GitHub Copilot OAuth Token"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const mergeData = $('🔀 Merge Libreria').first().json;\nconst library_summary = mergeData.library_summary;\n\nconst prompt = 'Analizza questa libreria multimediale personale e restituisci un JSON con:\\n' +\n  '{\\n' +\n  '  \"top_genres\": [\"lista\", \"dei\", \"generi\", \"preferiti\"],\\n' +\n  '  \"preferred_types\": \"movie|series|both\",\\n' +\n  '  \"library_stats\": {\"total_movies\": N, \"total_series\": N, \"available_movies\": N},\\n' +\n  '  \"taste_summary\": \"frase descrittiva del gusto cinematografico in italiano\",\\n' +\n  '  \"notable_patterns\": [\"pattern1\", \"pattern2\"]\\n' +\n  '}\\n\\n' +\n  'Libreria:\\n' + library_summary;\n\nreturn [{ json: { prompt } }];"
      },
      "id": "ee1918af-97fd-4c45-8d22-9e9808a47842",
      "name": "📝 Build Prompt LLM",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1136,
        304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.githubcopilot.com/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $('🔑 Ottieni Token Copilot').first().json.token }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Copilot-Integration-Id",
              "value": "vscode-chat"
            },
            {
              "name": "Editor-Version",
              "value": "vscode/1.85.0"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({\"model\":\"gpt-4.1\",\"messages\":[{\"role\":\"system\",\"content\":\"Rispondi SOLO con JSON valido.\"},{\"role\":\"user\",\"content\": $('📝 Build Prompt LLM').first().json.prompt}],\"response_format\":{\"type\":\"json_object\"},\"max_tokens\":2048}) }}",
        "options": {}
      },
      "id": "709416ec-5b5a-4735-a985-9a98822b3910",
      "name": "🤖 GPT-4.1 Analisi",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1328,
        304
      ]
    },
    {
      "parameters": {
        "jsCode": "const content = $input.first().json.choices[0].message.content;\nreturn [{ json: JSON.parse(content) }];"
      },
      "id": "e2888c8c-f2cf-4920-bab6-a32bf632b54f",
      "name": "🔍 Parse LLM",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1504,
        304
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO memory_facts (user_id, source, category, subject, detail, expires_at, source_ref)\nVALUES (\n  'martin',\n  'media_library',\n  'media_preferences',\n  'Preferenze Media Martin',\n  '{{ $json.detail_json }}'::jsonb,\n  NOW() + INTERVAL '7 days',\n  'media_preferences_summary'\n)\nON CONFLICT (user_id, source, source_ref) WHERE source_ref IS NOT NULL\nDO UPDATE SET\n  detail = EXCLUDED.detail,\n  expires_at = EXCLUDED.expires_at,\n  created_at = NOW();",
        "options": {}
      },
      "id": "04eb6255-231f-4ed6-bd35-45970280c1cd",
      "name": "💾 PG — Upsert Preferenze",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        1840,
        304
      ],
      "credentials": {
        "postgres": {
          "id": "mRqzxhSboGscolqI",
          "name": "Pompeo — PostgreSQL"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $('🔀 Merge Libreria').first().json.items;\nreturn items.map(item => ({ json: item }));"
      },
      "id": "3fd8b3f4-f409-40e8-a747-2539248a959a",
      "name": "📋 Espandi Items",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        304
      ]
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "09919f3a-5ccb-4003-96b0-f096f9c8e802",
      "name": "🔁 Loop Items",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        2208,
        304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://ollama.ai.svc.cluster.local:11434/api/embeddings",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({\"model\":\"nomic-embed-text\",\"prompt\": $json.title + \" \" + ($json.year||\"\") + \" \" + ($json.genres||[]).join(\" \") + \" \" + ($json.type||\"\")}) }}",
        "options": {}
      },
      "id": "5b1aabe1-58fe-4290-baa6-8bb02ad144d8",
      "name": "🔢 Ollama Embed",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2448,
        128
      ]
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "http://qdrant.persistence.svc.cluster.local:6333/collections/media_preferences/points",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "api-key",
              "value": "__Montecarlo00!"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.qdrant_body }}",
        "options": {}
      },
      "id": "3b04cf16-e00b-4e3d-aef7-beea09153db0",
      "name": "🗄️ Qdrant Upsert",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2688,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "const parsed = $input.first().json;\n// Serialize to JSON and escape single quotes for SQL injection safety\nconst detailJson = JSON.stringify(parsed).split(\"'\").join(\"''\");\nreturn [{ json: { detail_json: detailJson } }];"
      },
      "id": "bf16855c-423d-4eb7-b483-a54af827036f",
      "name": "🔧 Prepara Detail",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1680,
        304
      ]
    },
    {
      "id": "e52627b8-ef32-47e1-94e4-8ecbdf2cc02b",
      "name": "🔧 Prepara Qdrant",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2468,
        320
      ],
      "parameters": {
        "jsCode": "const item = $('🔁 Loop Items').item.json;\nconst embedding = $('🔢 Ollama Embed').item.json.embedding;\n\n// Use source_id as integer point ID (hash if needed)\nconst rawId = item.source_id;\nlet pointId = Math.abs(parseInt(rawId) || 0) % 2147483647;\nif (pointId === 0) pointId = Math.abs(String(rawId).split('').reduce((a,c) => a + c.charCodeAt(0), 0)) % 2147483647;\n\nconst payload = {\n  points: [{\n    id: pointId,\n    vector: embedding,\n    payload: {\n      title: item.title || '',\n      year: item.year || null,\n      type: item.type || '',\n      genres: item.genres || [],\n      status: item.status || '',\n      source: item.source || '',\n      source_id: rawId,\n      expires_at: new Date(Date.now() + 180 * 24 * 60 * 60 * 1000).toISOString()\n    }\n  }]\n};\n\nreturn [{ json: { qdrant_body: JSON.stringify(payload) } }];"
      }
    }
  ],
  "connections": {
    "⏰ Cron": {
      "main": [
        [
          {
            "node": "🎬 HTTP Radarr",
            "type": "main",
            "index": 0
          },
          {
            "node": "📺 HTTP Sonarr",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🎬 HTTP Radarr": {
      "main": [
        [
          {
            "node": "🔀 Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📺 HTTP Sonarr": {
      "main": [
        [
          {
            "node": "🔀 Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "🔀 Merge": {
      "main": [
        [
          {
            "node": "🔀 Merge Libreria",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔀 Merge Libreria": {
      "main": [
        [
          {
            "node": "🔑 Ottieni Token Copilot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔑 Ottieni Token Copilot": {
      "main": [
        [
          {
            "node": "📝 Build Prompt LLM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📝 Build Prompt LLM": {
      "main": [
        [
          {
            "node": "🤖 GPT-4.1 Analisi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🤖 GPT-4.1 Analisi": {
      "main": [
        [
          {
            "node": "🔍 Parse LLM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔍 Parse LLM": {
      "main": [
        [
          {
            "node": "🔧 Prepara Detail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "💾 PG — Upsert Preferenze": {
      "main": [
        [
          {
            "node": "📋 Espandi Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📋 Espandi Items": {
      "main": [
        [
          {
            "node": "🔁 Loop Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔁 Loop Items": {
      "main": [
        [],
        [
          {
            "node": "🔢 Ollama Embed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔢 Ollama Embed": {
      "main": [
        [
          {
            "node": "🔧 Prepara Qdrant",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🗄️ Qdrant Upsert": {
      "main": [
        [
          {
            "node": "🔁 Loop Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔧 Prepara Detail": {
      "main": [
        [
          {
            "node": "💾 PG — Upsert Preferenze",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔧 Prepara Qdrant": {
      "main": [
        [
          {
            "node": "🗄️ Qdrant Upsert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "triggerCount": 1,
  "versionId": "ef7c42b4-fddc-4e53-913a-47761d08f5a6",
  "owner": {
    "type": "personal",
    "projectId": "Hdttz401OqqtObPo",
    "projectName": "Martin Tahiraj <tahiraj.martin@gmail.com>",
    "personalEmail": "tahiraj.martin@gmail.com"
  },
  "parentFolderId": null,
  "isArchived": false
}