init after v1 corruption
This commit is contained in:
470
workflows/kRZyX9H2uDHHncpE.json
Normal file
470
workflows/kRZyX9H2uDHHncpE.json
Normal file
@@ -0,0 +1,470 @@
|
||||
{
|
||||
"id": "kRZyX9H2uDHHncpE",
|
||||
"name": "MAM Transmission Manager",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "start",
|
||||
"name": "Start",
|
||||
"position": [
|
||||
224,
|
||||
128
|
||||
],
|
||||
"type": "n8n-nodes-base.executeWorkflowTrigger",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://seed-1.dfw.ben.io:9091/transmission/rpc",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpBasicAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-Transmission-Session-Id",
|
||||
"value": "={{ $json.session_id }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ $json.transmission_body }}",
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"fullResponse": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "http-add-torrent",
|
||||
"name": "Add Torrent to Transmission",
|
||||
"position": [
|
||||
2192,
|
||||
128
|
||||
],
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"credentials": {
|
||||
"httpBasicAuth": {
|
||||
"id": "iymUPilnVhfL3h5D",
|
||||
"name": "transmission"
|
||||
}
|
||||
},
|
||||
"onError": "continueRegularOutput"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const responses = $input.all();\nconst originals = $('Start').all();\nconst mergedPayloads = $('Merge Session Context').all();\n\nfunction getOriginal(index) {\n return originals[index]?.json || originals[0]?.json || {};\n}\n\nfunction getSessionId(index) {\n return mergedPayloads[index]?.json?.session_id || mergedPayloads[0]?.json?.session_id || '';\n}\n\nconst outputs = [];\n\nfor (let i = 0; i < responses.length; i++) {\n const item = responses[i];\n const body = item.json.body || item.json;\n const result = body?.result;\n const args = body?.arguments || {};\n const torrentInfo = args['torrent-added'] || args['torrent-duplicate'];\n const sessionId = getSessionId(i);\n const originalInput = getOriginal(i);\n\n if (item.error || (!torrentInfo && result !== 'duplicate' && result !== 'success')) {\n outputs.push({\n json: {\n status: 'failed',\n error: item.error?.message || result || 'Unknown error adding torrent',\n torrent_id: null,\n session_id: sessionId,\n original_input: originalInput\n }\n });\n continue;\n }\n\n if (!torrentInfo) {\n outputs.push({\n json: {\n status: 'failed',\n error: 'Could not extract torrent info from response',\n torrent_id: null,\n session_id: sessionId,\n original_input: originalInput\n }\n });\n continue;\n }\n\n outputs.push({\n json: {\n status: 'queued',\n torrent_id: torrentInfo.id,\n torrent_hash: torrentInfo.hashString,\n torrent_name: torrentInfo.name,\n session_id: sessionId,\n original_input: originalInput\n }\n });\n}\n\nreturn outputs;"
|
||||
},
|
||||
"id": "code-extract-torrent-id",
|
||||
"name": "Extract Torrent ID",
|
||||
"position": [
|
||||
2416,
|
||||
128
|
||||
],
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"amount": 30
|
||||
},
|
||||
"id": "wait-60s",
|
||||
"name": "Wait 60 Seconds",
|
||||
"position": [
|
||||
2688,
|
||||
288
|
||||
],
|
||||
"type": "n8n-nodes-base.wait",
|
||||
"typeVersion": 1.1,
|
||||
"webhookId": "ae0f8400-3629-4965-989e-11e14a60a260"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://seed-1.dfw.ben.io:9091/transmission/rpc",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpBasicAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-Transmission-Session-Id",
|
||||
"value": "={{ $json.session_id || '' }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ { method: 'torrent-get', arguments: { ids: [$json.torrent_id], fields: ['id','name','status','percentDone','files','downloadDir', 'hashString'] } } }}",
|
||||
"options": {}
|
||||
},
|
||||
"id": "http-check-status",
|
||||
"name": "Check Torrent Status",
|
||||
"position": [
|
||||
2688,
|
||||
64
|
||||
],
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"credentials": {
|
||||
"httpBasicAuth": {
|
||||
"id": "iymUPilnVhfL3h5D",
|
||||
"name": "transmission"
|
||||
}
|
||||
},
|
||||
"onError": "continueRegularOutput"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Check if torrent download is complete\n// Transmission status codes: 0=stopped, 4=downloading, 6=seeding\nconst item = $input.first();\nconst response = item.json;\n\nif (item.error || !response.arguments || !response.arguments.torrents) {\n return [{\n json: {\n complete: false,\n status: 'error',\n error: item.error?.message || 'Failed to get torrent status',\n retry: true\n }\n }];\n}\n\nconst torrent = response.arguments.torrents[0];\n\nif (!torrent) {\n return [{\n json: {\n complete: false,\n status: 'error',\n error: 'Torrent not found in Transmission',\n retry: false\n }\n }];\n}\n\nconst isComplete = torrent.status === 6 || (torrent.status === 0 && torrent.percentDone === 1);\n\n// Get original input to preserve history_id\nconst waitNode = $('Wait 60 Seconds').last();\nconst originalInput = waitNode ? (waitNode.json.original_input || {}) : {};\nconst historyId = originalInput.history_id;\n\nif (isComplete) {\n const downloadDir = torrent.downloadDir || '/home/seed_/.transmission/downloads/audio';\n \n // Find largest file (likely the main audiobook) to handle multi-file/directory torrents\n let largestFile = null;\n let maxBytes = -1;\n \n if (torrent.files && torrent.files.length > 0) {\n for (const file of torrent.files) {\n if (file.length > maxBytes) {\n maxBytes = file.length;\n largestFile = file;\n }\n }\n }\n \n // Use largest file path if found, otherwise fallback to torrent name (single file)\n let relativePath = torrent.name;\n if (largestFile) {\n relativePath = largestFile.name;\n }\n \n const filePath = `${downloadDir}/${relativePath}`;\n \n return [{\n json: {\n complete: true,\n status: 'complete',\n file_path: filePath,\n filename: relativePath.split('/').pop(), // Just the filename part\n torrent_id: torrent.id,\n torrent_name: torrent.name,\n torrent_hash: torrent.hashString,\n percent_done: torrent.percentDone,\n transmission_status: torrent.status,\n history_id: historyId,\n debug_original_keys: Object.keys(originalInput),\n debug_wait_node_exists: !!waitNode\n }\n }];\n} else {\n return [{\n json: {\n complete: false,\n status: 'downloading',\n percent_done: torrent.percentDone,\n transmission_status: torrent.status,\n retry: true\n }\n }];\n}"
|
||||
},
|
||||
"id": "code-check-completion",
|
||||
"name": "Check If Complete",
|
||||
"position": [
|
||||
2912,
|
||||
64
|
||||
],
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"conditions": [
|
||||
{
|
||||
"id": "condition-1",
|
||||
"leftValue": "={{ $json.complete }}",
|
||||
"operator": {
|
||||
"operation": "equals",
|
||||
"type": "boolean"
|
||||
},
|
||||
"rightValue": true
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict"
|
||||
}
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "if-complete",
|
||||
"name": "Is Download Complete?",
|
||||
"position": [
|
||||
3136,
|
||||
64
|
||||
],
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "allIncomingItems",
|
||||
"options": {}
|
||||
},
|
||||
"id": "respond-complete",
|
||||
"name": "Respond Complete",
|
||||
"position": [
|
||||
3360,
|
||||
16
|
||||
],
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "keep-context",
|
||||
"name": "torrent_id",
|
||||
"type": "string",
|
||||
"value": "={{ $('Extract Torrent ID').item.json.torrent_id }}"
|
||||
},
|
||||
{
|
||||
"id": "keep-hash",
|
||||
"name": "torrent_hash",
|
||||
"type": "string",
|
||||
"value": "={{ $('Extract Torrent ID').item.json.torrent_hash }}"
|
||||
},
|
||||
{
|
||||
"id": "keep-original",
|
||||
"name": "original_input",
|
||||
"type": "object",
|
||||
"value": "={{ $('Extract Torrent ID').item.json.original_input }}"
|
||||
},
|
||||
{
|
||||
"id": "keep-session",
|
||||
"name": "session_id",
|
||||
"type": "string",
|
||||
"value": "={{ $json.session_id || '' }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "loop-back",
|
||||
"name": "Loop Back to Wait",
|
||||
"position": [
|
||||
3360,
|
||||
240
|
||||
],
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://seed-1.dfw.ben.io:9091/transmission/rpc",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpBasicAuth",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ { method: 'session-get' } }}",
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"fullResponse": true,
|
||||
"neverError": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "http-request-session",
|
||||
"name": "Request Transmission Session",
|
||||
"position": [
|
||||
896,
|
||||
192
|
||||
],
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"credentials": {
|
||||
"httpBasicAuth": {
|
||||
"id": "iymUPilnVhfL3h5D",
|
||||
"name": "transmission"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const item = $input.first();\nconst data = item.json || {};\nconst headers = data.headers || {};\nconst sessionId = headers['x-transmission-session-id'] || headers['X-Transmission-Session-Id'];\n\nif (!sessionId) {\n throw new Error('Transmission session header missing');\n}\n\nreturn [{ json: { session_id: sessionId } }];"
|
||||
},
|
||||
"id": "code-session-header",
|
||||
"name": "Extract Session Header",
|
||||
"position": [
|
||||
1168,
|
||||
192
|
||||
],
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Loop over all items (books)\nreturn items.map(item => {\n // Get the Base64 file string safely\n const binaryData = item.binary.data.data;\n \n // Create the exact JSON body Transmission expects\n const payload = {\n method: \"torrent-add\",\n arguments: {\n metainfo: binaryData,\n \"download-dir\": \"/home/seed_/.transmission/downloads/audio\", // Using your seedbox path\n paused: true\n }\n };\n\n // Return it as a normal JSON field called 'transmission_body'\n return {\n json: {\n ...item.json,\n transmission_body: payload\n }\n };\n});"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1936,
|
||||
128
|
||||
],
|
||||
"id": "0b011613-50e9-4612-ac6e-1f6ac0e39ebb",
|
||||
"name": "Code in JavaScript"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// 1. Get the Session ID from the \"Extract\" node\n// (We use .first() because there is only one session ID)\nconst sessionNode = $('Extract Session Header').first();\n// Robustly find the ID whether it's in headers or root\nconst sessionId = sessionNode.json.headers ? sessionNode.json.headers['x-transmission-session-id'] : sessionNode.json.session_id;\n\n// 2. Get all Files from the \"Start\" node\nconst files = $('Start').all();\n\n// 3. Map the ID onto every file while PRESERVING the binary data\nreturn files.map(file => {\n return {\n json: {\n ...file.json,\n session_id: sessionId\n },\n binary: file.binary // <--- This is the critical part we were losing!\n };\n});"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1712,
|
||||
128
|
||||
],
|
||||
"id": "d6d9f145-6b10-452f-aa08-2dc669c3b4d6",
|
||||
"name": "Merge Session to Files"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"combineBy": "combineByPosition",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1488,
|
||||
128
|
||||
],
|
||||
"id": "dde23141-d708-48e7-942f-08aa1d749189",
|
||||
"name": "Merge Session Context"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Add Torrent to Transmission": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"index": 0,
|
||||
"node": "Extract Torrent ID",
|
||||
"type": "main"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check If Complete": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"index": 0,
|
||||
"node": "Is Download Complete?",
|
||||
"type": "main"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check Torrent Status": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"index": 0,
|
||||
"node": "Check If Complete",
|
||||
"type": "main"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Session Header": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge Session Context",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Torrent ID": {
|
||||
"main": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
"Is Download Complete?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"index": 0,
|
||||
"node": "Respond Complete",
|
||||
"type": "main"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"index": 0,
|
||||
"node": "Loop Back to Wait",
|
||||
"type": "main"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Back to Wait": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"index": 0,
|
||||
"node": "Wait 60 Seconds",
|
||||
"type": "main"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Request Transmission Session": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"index": 0,
|
||||
"node": "Extract Session Header",
|
||||
"type": "main"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Start": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Request Transmission Session",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge Session Context",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Wait 60 Seconds": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"index": 0,
|
||||
"node": "Check Torrent Status",
|
||||
"type": "main"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code in JavaScript": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Add Torrent to Transmission",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge Session to Files": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code in JavaScript",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge Session Context": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge Session to Files",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {},
|
||||
"triggerCount": 0,
|
||||
"versionId": "ef553d09-89f5-4f19-9003-0f2040bf82ba",
|
||||
"owner": {
|
||||
"type": "personal",
|
||||
"projectId": "FeLO36wNUAcn61Wj",
|
||||
"projectName": "Ben W <admin@ben.io>",
|
||||
"personalEmail": "admin@ben.io"
|
||||
},
|
||||
"parentFolderId": "6tDyZCwqELStb6Ik",
|
||||
"isArchived": false
|
||||
}
|
||||
Reference in New Issue
Block a user