updates to MAM workflows

This commit is contained in:
2026-01-12 17:38:18 +00:00
parent c0a38e8c8d
commit bf0728193a
3 changed files with 661 additions and 15 deletions

View File

@@ -102,7 +102,7 @@
{
"parameters": {
"language": "pythonNative",
"pythonCode": "import time\n\n# Output list\nresults = []\n\n# Define your time window (2 hours in seconds to match your node name)\n# 2 * 60 * 60 = 7200\nTIME_WINDOW = 2 * 60 * 60\ncurrent_time = time.time()\n\n# 1. Get the list of torrents from the HTTP response\ninput_data = _items[0]['json']\ntorrents = input_data.get('arguments', {}).get('torrents', [])\n\nfor torrent in torrents:\n # 2. Check if download is 100% complete (1.0)\n is_downloaded = torrent.get('percentDone') == 1\n \n # 3. Filter Logic (Check if added recently)\n added_date = torrent.get('addedDate', 0)\n age_in_seconds = current_time - added_date\n \n # Optional: Uncomment this if you ALSO only want completed items\n # is_complete = torrent.get('percentDone') == 1\n\n if age_in_seconds <= TIME_WINDOW: # and is_complete:\n # 4. Return the FULL object\n # We pass the entire 'torrent' dictionary directly\n results.append({\n \"json\": torrent\n })\n\nreturn results"
"pythonCode": "import time\n\n# Output list\nresults = []\n\n# Define your time window (2 hours in seconds to match your node name)\n# 2 * 60 * 60 = 7200\nTIME_WINDOW = 36 * 60 * 60\ncurrent_time = time.time()\n\n# 1. Get the list of torrents from the HTTP response\ninput_data = _items[0]['json']\ntorrents = input_data.get('arguments', {}).get('torrents', [])\n\nfor torrent in torrents:\n # 2. Check if download is 100% complete (1.0)\n is_downloaded = torrent.get('percentDone') == 1\n \n # 3. Filter Logic (Check if added recently)\n added_date = torrent.get('addedDate', 0)\n age_in_seconds = current_time - added_date\n \n # Optional: Uncomment this if you ALSO only want completed items\n is_complete = torrent.get('percentDone') == 1\n\n if age_in_seconds <= TIME_WINDOW: # and is_complete:\n # 4. Return the FULL object\n # We pass the entire 'torrent' dictionary directly\n results.append({\n \"json\": torrent\n })\n\nreturn results"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -124,13 +124,16 @@
},
"table": {
"__rl": true,
"value": "smb_general_books",
"value": "download_history",
"mode": "list",
"cachedResultName": "smb_general_books"
"cachedResultName": "download_history"
},
"where": {
"values": [
{}
{
"column": "torrent_hash",
"value": "={{ $json.hashString }}"
}
]
},
"options": {}
@@ -138,17 +141,551 @@
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
384,
-112
336,
-16
],
"id": "0fa625e2-3e86-4abe-a837-f876d2d08f10",
"name": "Select rows from a table",
"name": "Check download_history",
"credentials": {
"postgres": {
"id": "9grzZwW7Br6SzdV8",
"name": "n8n-media"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "2c8975f2-e286-4128-a04b-855242c68454",
"leftValue": "={{ $json.book_id }}",
"rightValue": "null",
"operator": {
"type": "string",
"operation": "notEquals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
544,
-16
],
"id": "319bd909-34d0-4ba0-be4f-9cb7266702d1",
"name": "check book_id"
},
{
"parameters": {
"operation": "upsert",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "smb_general_books",
"mode": "list",
"cachedResultName": "smb_general_books"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"identified": true,
"needs_review": false,
"book_folder": "={{ $json.meta_title }}",
"smb_path": "={{\n$json.category == 'anime'\n? '/mnt/nas/Anime/Audiobooks/' + $json.meta_series + '/' + $json.meta_title + '.m4b'\n: '/mnt/nas/audiobooks/Main/' + $json.meta_author + '/' + $json.meta_series + '/' + $json.meta_title + '.m4b'\n}}",
"author_folder": "={{ $json.meta_author }}",
"book_name": "={{ $json.meta_title }}",
"series_name": "={{ $json.meta_series }}",
"audible_asin": "={{ null }}",
"category": "={{\n$json.category == 'anime'\n? 'anime'\n: 'general'\n}}",
"volume_num": "={{ $json.meta_book_number }}"
},
"matchingColumns": [
"smb_path"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "smb_path",
"displayName": "smb_path",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "book_folder",
"displayName": "book_folder",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "author_folder",
"displayName": "author_folder",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "book_name",
"displayName": "book_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "series_name",
"displayName": "series_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "audible_asin",
"displayName": "audible_asin",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "identified",
"displayName": "identified",
"required": false,
"defaultMatch": false,
"display": true,
"type": "boolean",
"canBeUsedToMatch": false
},
{
"id": "needs_review",
"displayName": "needs_review",
"required": false,
"defaultMatch": false,
"display": true,
"type": "boolean",
"canBeUsedToMatch": false
},
{
"id": "retry_stage",
"displayName": "retry_stage",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "failure_reason",
"displayName": "failure_reason",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "category",
"displayName": "category",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "volume_num",
"displayName": "volume_num",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": false
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
848,
-32
],
"id": "473a1d96-9c64-4eee-81a0-3544cb7e10e3",
"name": "Insert or update rows in a table",
"credentials": {
"postgres": {
"id": "9grzZwW7Br6SzdV8",
"name": "n8n-media"
}
}
},
{
"parameters": {
"operation": "update",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"value": "download_history",
"mode": "list",
"cachedResultName": "download_history"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"torrent_hash": "={{ $('Check download_history').item.json.torrent_hash }}",
"book_id": "={{ $json.id }}"
},
"matchingColumns": [
"torrent_hash"
],
"schema": [
{
"id": "id",
"displayName": "id",
"required": false,
"defaultMatch": true,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "book_id",
"displayName": "book_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "torrent_url",
"displayName": "torrent_url",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "torrent_hash",
"displayName": "torrent_hash",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "filename",
"displayName": "filename",
"required": true,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "transmission_path",
"displayName": "transmission_path",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "gateway_path",
"displayName": "gateway_path",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "downloaded_at",
"displayName": "downloaded_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "created_at",
"displayName": "created_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "updated_at",
"displayName": "updated_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "dateTime",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "meta_title",
"displayName": "meta_title",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "meta_series",
"displayName": "meta_series",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "meta_book_number",
"displayName": "meta_book_number",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "meta_author",
"displayName": "meta_author",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "mam_id",
"displayName": "mam_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "error_log",
"displayName": "error_log",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"position": [
1056,
-32
],
"id": "973faa87-a40b-4092-800e-751e22002bc8",
"name": "Link Download",
"credentials": {
"postgres": {
"id": "9grzZwW7Br6SzdV8",
"name": "n8n-media"
}
}
},
{
"parameters": {
"mode": "runOnceForEachItem",
"language": "pythonNative",
"pythonCode": "# MODE: Run Once for Each Item\n# No imports needed\n\n# 1. Access the specific item for this run using '_item' (with underscore)\ndata = _item['json']\n\n# 2. Get Paths\nfull_dest_path = data.get('smb_path')\n# Try 'downloadDir' or fallback to 'transmission_path'\nsource_dir = data.get('downloadDir') or data.get('transmission_path')\n# Try 'name' or fallback to 'filename'\nsource_name = data.get('name') or data.get('filename')\n\n# Initialize defaults\nfull_source_path = None\ndest_folder = ''\ndest_filename = ''\n\nif full_dest_path and (source_dir or source_name):\n # Join Source Path\n if source_dir and source_name:\n full_source_path = f\"{source_dir.rstrip('/')}/{source_name.lstrip('/')}\"\n else:\n full_source_path = source_name \n\n # Split Destination Path\n if '/' in full_dest_path:\n dest_folder, dest_filename = full_dest_path.rsplit('/', 1)\n else:\n dest_filename = full_dest_path\n\n# Return the single dictionary for this item\nreturn {\n \"json\": {\n \"source_path\": full_source_path,\n \"destination_folder\": dest_folder,\n \"destination_filename\": dest_filename,\n \"full_destination_path\": full_dest_path,\n \"book_id\": data.get('book_id')\n }\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1488,
-32
],
"id": "01001046-ee17-4e6d-9977-46de26e530e4",
"name": "File Name Structure",
"executeOnce": false
},
{
"parameters": {
"workflowId": {
"__rl": true,
"value": "6S41oPplwN1S9Lz0",
"mode": "list",
"cachedResultUrl": "/workflow/6S41oPplwN1S9Lz0",
"cachedResultName": "MAM Remote File Transfer"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {},
"matchingColumns": [],
"schema": [],
"attemptToConvertTypes": false,
"convertFieldsToString": true
},
"mode": "each",
"options": {}
},
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.3,
"position": [
2064,
-16
],
"id": "ecc05269-aaae-49e5-b029-c63a953f82f9",
"name": "Call 'MAM Remote File Transfer'"
},
{
"parameters": {
"mode": "combine",
"advanced": true,
"mergeByFields": {
"values": [
{
"field1": "book_id",
"field2": "id"
}
]
},
"options": {
"fuzzyCompare": true
}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
1312,
-32
],
"id": "b2aeacf6-b76d-4f33-a62b-93087da796e9",
"name": "Merge DB Data"
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineByPosition",
"numberInputs": 3,
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
1760,
-16
],
"id": "e5546816-5ff2-42c2-8352-e2abae7ea635",
"name": "Merge"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
1088,
240
],
"id": "eddd2620-8c48-49ff-9b45-dc02091698ce",
"name": "No Operation, do nothing"
}
],
"connections": {
@@ -187,7 +724,116 @@
},
"Filter for last 2 hours": {
"main": [
[]
[
{
"node": "Check download_history",
"type": "main",
"index": 0
},
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"Check download_history": {
"main": [
[
{
"node": "check book_id",
"type": "main",
"index": 0
}
]
]
},
"check book_id": {
"main": [
[
{
"node": "Insert or update rows in a table",
"type": "main",
"index": 0
}
]
]
},
"Insert or update rows in a table": {
"main": [
[
{
"node": "Link Download",
"type": "main",
"index": 0
},
{
"node": "Merge DB Data",
"type": "main",
"index": 1
},
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Link Download": {
"main": [
[
{
"node": "Merge DB Data",
"type": "main",
"index": 0
}
]
]
},
"File Name Structure": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Merge DB Data": {
"main": [
[
{
"node": "File Name Structure",
"type": "main",
"index": 0
}
]
]
},
"No Operation, do nothing": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 2
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Call 'MAM Remote File Transfer'",
"type": "main",
"index": 0
}
]
]
}
},
@@ -196,7 +842,7 @@
"availableInMCP": false
},
"triggerCount": 0,
"versionId": "26ae4972-43fe-4776-b2ed-af9f7c88aa42",
"versionId": "4523fc65-8080-444f-a097-244dd7aee7d6",
"owner": {
"type": "personal",
"projectId": "FeLO36wNUAcn61Wj",