{ "id": "K07e4PPANXDkmQsr", "name": "🎞️ Pompeo β€” Jellyfin Watch History [Schedule]", "nodes": [ { "parameters": { "rule": { "interval": [ { "triggerAtHour": 4 } ] } }, "id": "b76a6172-d497-42a5-b286-56263e0de523", "name": "⏰ Cron", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1, "position": [ 0, 304 ] }, { "parameters": { "url": "http://jellyfin.media.svc.cluster.local:8096/Users/42369255a7c64917a28fc26d4c7f8265/Items?Recursive=true&IncludeItemTypes=Movie,Episode&Fields=Genres,UserData,SeriesName,ParentIndexNumber&SortBy=DatePlayed&SortOrder=Descending&Limit=100", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "MediaBrowser Token=\"d153606c1ca54574a20d2b40fcf1b02e\"" } ] }, "options": {} }, "id": "6bfda9a6-3b1f-46cb-ac05-0c19b9720e30", "name": "🎞️ HTTP Jellyfin", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 224, 304 ] }, { "parameters": { "jsCode": "const response = $input.first().json;\nconst allItems = response.Items || [];\n\nconst now = new Date();\nconst ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);\n\nconst filtered = allItems\n .filter(d => d.UserData && d.UserData.PlayCount > 0)\n .filter(d => {\n if (!d.UserData.LastPlayedDate) return false;\n return new Date(d.UserData.LastPlayedDate) >= ninetyDaysAgo;\n })\n .map(d => ({\n title: d.Type === 'Episode'\n ? (d.SeriesName || '') + ' S' + String(d.ParentIndexNumber || '')\n : (d.Name || ''),\n type: d.Type === 'Episode' ? 'episode' : 'movie',\n genres: d.Genres || [],\n play_count: d.UserData.PlayCount,\n played_pct: d.UserData.PlayedPercentage || 0,\n last_played: d.UserData.LastPlayedDate,\n is_favorite: d.UserData.IsFavorite || false\n }));\n\nlet watch_summary = 'Contenuti visti di recente:\\n';\nfor (const item of filtered) {\n const dateStr = item.last_played\n ? new Date(item.last_played).toLocaleDateString('it-IT')\n : 'N/A';\n watch_summary += '- ' + item.title + ' (' + item.type + ', ' + dateStr + ') - ' + item.play_count + 'x\\n';\n}\n\nreturn [{ json: { items: filtered, watch_summary } }];" }, "id": "11e8a16f-4b77-4d1e-987b-3b269d577f08", "name": "πŸ”€ Filtra Visti", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 448, 304 ] }, { "parameters": { "conditions": { "number": [ { "value1": "={{ $json.items.length }}", "operation": "larger" } ] } }, "id": "19de4739-efe2-495e-969a-fbb01cbd7eeb", "name": "❓ Ha Visti?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 672, 304 ] }, { "parameters": {}, "id": "d0a1c6e7-06eb-4d74-ac8b-c3c18abbcdbb", "name": "β›” Stop", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [ 880, 480 ] }, { "parameters": { "url": "https://api.github.com/copilot_internal/v2/token", "authentication": "predefinedCredentialType", "nodeCredentialType": "httpHeaderAuth", "options": {} }, "id": "52f1351c-594b-4e33-8c50-5318b7de67ff", "name": "πŸ”‘ Token Copilot", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 880, 192 ], "credentials": { "httpHeaderAuth": { "id": "vBwUxlzKrX3oDHyN", "name": "GitHub Copilot OAuth Token" } } }, { "parameters": { "jsCode": "const data = $input.first().json;\nconst watch_summary = data.watch_summary;\n\nconst prompt = 'Analizza questi contenuti guardati di recente da Martin e restituisci un JSON:\\n' +\n '{\\n' +\n ' \"recent_favorites\": [\"titoli\", \"piΓΉ\", \"visti\"],\\n' +\n ' \"preferred_genres\": [\"generi\", \"prevalenti\"],\\n' +\n ' \"watch_patterns\": \"descrizione in italiano dei pattern di visione\",\\n' +\n ' \"completion_rate\": \"alta|media|bassa (basato su played_pct medio)\",\\n' +\n ' \"notes\": \"osservazioni utili per l\\'assistente personale\"\\n' +\n '}\\n\\n' +\n 'Cronologia:\\n' + watch_summary;\n\nreturn [{ json: { prompt } }];" }, "id": "5d4d889c-bda7-4f7e-8873-277f2b8727d5", "name": "πŸ“ Build Prompt", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1104, 192 ] }, { "parameters": { "method": "POST", "url": "https://api.githubcopilot.com/chat/completions", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "Authorization", "value": "={{ 'Bearer ' + $('πŸ”‘ 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').first().json.prompt}],\"response_format\":{\"type\":\"json_object\"},\"max_tokens\":1024}) }}", "options": {} }, "id": "5135bca5-5f8b-4464-83d1-495ec9855a4a", "name": "πŸ€– GPT-4.1", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 1328, 192 ] }, { "parameters": { "jsCode": "const content = $input.first().json.choices[0].message.content;\nreturn [{ json: JSON.parse(content) }];" }, "id": "8dd4c239-79c2-47de-b898-f0f3d5b00e40", "name": "πŸ” Parse", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1552, 192 ] }, { "parameters": { "operation": "executeQuery", "query": "INSERT INTO memory_facts (user_id, source, category, subject, detail, expires_at, source_ref)\nVALUES (\n 'martin',\n 'jellyfin',\n 'watch_history',\n 'Cronologia Visione Recente',\n '{{ $json.detail_json }}'::jsonb,\n NOW() + INTERVAL '30 days',\n 'watch_history_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": "93ca06bb-5a19-4931-a023-e701b367ce58", "name": "πŸ’Ύ PG β€” Upsert Cronologia", "type": "n8n-nodes-base.postgres", "typeVersion": 2, "position": [ 1904, 192 ], "credentials": { "postgres": { "id": "mRqzxhSboGscolqI", "name": "Pompeo β€” PostgreSQL" } } }, { "parameters": { "jsCode": "const parsed = $input.first().json;\nconst detailJson = JSON.stringify(parsed).split(\"'\").join(\"''\");\nreturn [{ json: { detail_json: detailJson } }];" }, "id": "599a48c0-dc77-4699-9060-ebccec1cc92c", "name": "πŸ”§ Prepara Detail B2", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1728, 192 ] } ], "connections": { "⏰ Cron": { "main": [ [ { "node": "🎞️ HTTP Jellyfin", "type": "main", "index": 0 } ] ] }, "🎞️ HTTP Jellyfin": { "main": [ [ { "node": "πŸ”€ Filtra Visti", "type": "main", "index": 0 } ] ] }, "πŸ”€ Filtra Visti": { "main": [ [ { "node": "❓ Ha Visti?", "type": "main", "index": 0 } ] ] }, "❓ Ha Visti?": { "main": [ [ { "node": "πŸ”‘ Token Copilot", "type": "main", "index": 0 } ], [ { "node": "β›” Stop", "type": "main", "index": 0 } ] ] }, "πŸ”‘ Token Copilot": { "main": [ [ { "node": "πŸ“ Build Prompt", "type": "main", "index": 0 } ] ] }, "πŸ“ Build Prompt": { "main": [ [ { "node": "πŸ€– GPT-4.1", "type": "main", "index": 0 } ] ] }, "πŸ€– GPT-4.1": { "main": [ [ { "node": "πŸ” Parse", "type": "main", "index": 0 } ] ] }, "πŸ” Parse": { "main": [ [ { "node": "πŸ”§ Prepara Detail B2", "type": "main", "index": 0 } ] ] }, "πŸ”§ Prepara Detail B2": { "main": [ [ { "node": "πŸ’Ύ PG β€” Upsert Cronologia", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1", "callerPolicy": "workflowsFromSameOwner", "availableInMCP": false }, "triggerCount": 1, "versionId": "bbcf28d7-652d-4aa9-b2a0-74d5ec29b56c", "owner": { "type": "personal", "projectId": "Hdttz401OqqtObPo", "projectName": "Martin Tahiraj ", "personalEmail": "tahiraj.martin@gmail.com" }, "parentFolderId": null, "isArchived": false }