{ "active": false, "connections": [ { "sourceNodeId": "node_1785733293322", "sourceOutput": "loop", "targetInput": "data", "targetNodeId": "node_1785733345099" }, { "sourceNodeId": "node_1785733345099", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_dedupe" }, { "sourceNodeId": "node_dedupe", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_if_new" }, { "sourceNodeId": "node_1785761774374", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_1785732919652" }, { "sourceNodeId": "node_1785733004181", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_1785732919652" }, { "sourceNodeId": "node_if_new", "sourceOutput": "false", "targetInput": "data", "targetNodeId": "node_1785733293322" }, { "sourceNodeId": "node_1785832349865", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_1785733293322" }, { "sourceNodeId": "node_1785909802844", "sourceOutput": "false", "targetInput": "data", "targetNodeId": "node_gen_failed" }, { "sourceNodeId": "node_gen_failed", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_1785733293322" }, { "sourceNodeId": "node_1785732919652", "sourceOutput": "main", "targetInput": "input1", "targetNodeId": "node_1786088830206_58567" }, { "sourceNodeId": "node_1786088853294_0", "sourceOutput": "main", "targetInput": "input2", "targetNodeId": "node_1786088830206_58567" }, { "sourceNodeId": "node_1785733004181", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_1786088853294_0" }, { "sourceNodeId": "node_1785761774374", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_1786088853294_0" }, { "sourceNodeId": "node_1786088830206_58567", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_1785733293322" }, { "sourceNodeId": "node_1785733293322", "sourceOutput": "done", "targetInput": "data", "targetNodeId": "node_no_feed" }, { "sourceNodeId": "node_no_feed", "sourceOutput": "true", "targetInput": "data", "targetNodeId": "node_feeds_down" }, { "sourceNodeId": "node_if_new", "sourceOutput": "true", "targetInput": "data", "targetNodeId": "node_call_process" }, { "sourceNodeId": "node_call_process", "sourceOutput": "main", "targetInput": "data", "targetNodeId": "node_1785909802844" }, { "sourceNodeId": "node_1785909802844", "sourceOutput": "true", "targetInput": "data", "targetNodeId": "node_1785832349865" } ], "name": "35photo2anime - sdcpp", "nodes": [ { "config": { "_customLabel": "Flicker", "detectNewItems": false, "maxItems": 20, "retries": 3, "retryDelayMs": 6000, "retryMaxDelayMs": 30000, "skipOnError": true, "timeout": 30000, "url": "https://api.flickr.com/services/feeds/photos_public.gne?tags=nude&format=rss2", "userAgent": "smartbotic-rss/1.0 (+https://smartbotics.ai)" }, "disabled": true, "id": "node_1785732919652", "name": "35photo pro", "position": { "x": 3160, "y": 180 }, "type": "rss-reader" }, { "config": { "label": "Execute" }, "disabled": false, "id": "node_1785733004181", "name": "Click Trigger", "position": { "x": 3140, "y": 0 }, "type": "click-trigger" }, { "config": { "continueOnError": true, "indexVariableName": "index", "inputField": "data.merged", "itemVariableName": "item", "maxIterations": 10, "outputField": "results" }, "disabled": false, "id": "node_1785733293322", "name": "Loop", "position": { "x": 3300, "y": 540 }, "type": "loop" }, { "config": { "authType": "none", "contentType": "", "downloadCollection": "nsfw_temp", "downloadTtlHours": 24, "failOnErrorStatus": true, "followRedirects": true, "method": "GET", "responseMode": "binary", "retries": 0, "retryDelayMs": 1000, "retryMaxDelayMs": 30000, "storeDownload": false, "timeout": 30000, "url": "{{data.loop.item.enclosure.url}}" }, "disabled": false, "id": "node_1785733345099", "name": "Fetch Image", "position": { "x": 3280, "y": 720 }, "type": "http-request" }, { "config": { "cronExpression": "0 * * * *", "maxConcurrent": 1, "maxRunMinutes": 0, "mode": "interval", "overlapPolicy": "skip", "pollInterval": 15, "timezone": "Europe/Budapest", "triggerName": "35photo every 5m" }, "disabled": false, "id": "node_1785761774374", "name": "Schedule Trigger", "position": { "x": 3460, "y": 0 }, "type": "schedule-trigger" }, { "config": { "code": "function findFetch(node, depth) {\n if (!node || typeof node !== 'object' || depth > 6) { return null; }\n if (node.file && typeof node.file.data === 'string' && node.file.data.length > 0) { return node; }\n const keys = Object.keys(node);\n for (let i = 0; i < keys.length; i++) {\n const found = findFetch(node[keys[i]], depth + 1);\n if (found) { return found; }\n }\n return null;\n}\n\nconst fetched = findFetch(input, 0);\nif (!fetched) { throw new Error('No fetched image found in input'); }\n\nconst hash = fetched.checksum || (fetched.file && fetched.file.checksum) ||\n smartbotic.utils.sha256(fetched.file.data);\nconst url = fetched.url || '';\nconst now = Date.now();\n\nconst existing = smartbotic.storage.get('image_hashes', hash);\n\nif (existing && existing.found && existing.document) {\n const doc = existing.document;\n const urls = doc.urls && typeof doc.urls.length === 'number' ? doc.urls : [];\n if (url && urls.indexOf(url) === -1) { urls.push(url); }\n\n smartbotic.storage.update('image_hashes', hash, {\n lastSeenAt: now,\n seenCount: (Number(doc.seenCount) || 1) + 1,\n urls: urls\n }, 0, true);\n\n smartbotic.log.info('Duplicate image skipped: ' + hash.substring(0, 16) + ' seen ' +\n ((Number(doc.seenCount) || 1) + 1) + ' times');\n\n return {\n isNew: false,\n checksum: hash,\n url: url,\n fileId: doc.fileId || '',\n seenCount: (Number(doc.seenCount) || 1) + 1\n };\n}\n\n// A feed can carry the same image twice in one run, so two iterations reach here\n// having both seen \"not found\". The id is the content hash, so the second insert\n// collides. Treat that as the duplicate it is rather than letting it fail the run.\n// Guarded both ways because the runtime reports a rejected insert as success\n// false, while the underlying database error can also surface as a throw.\nlet inserted = null;\nlet insertError = '';\ntry {\n inserted = smartbotic.storage.insert('image_hashes', {\n firstSeenAt: now,\n lastSeenAt: now,\n seenCount: 1,\n urls: url ? [url] : [],\n mimeType: fetched.file.mimeType || '',\n size: fetched.file.size || 0\n }, hash, 0);\n} catch (e) {\n insertError = e && e.message ? e.message : String(e);\n}\n\nif (insertError || !inserted || inserted.success === false) {\n const detail = insertError || (inserted && inserted.error) || 'unknown';\n smartbotic.log.info('Image already recorded in this run: ' + hash.substring(0, 16) + ' (' + detail + ')');\n return { isNew: false, checksum: hash, url: url, seenCount: 2 };\n}\n\nconst stored = smartbotic.storage.uploadFile(fetched.file.data, {\n name: 'source-' + hash.substring(0, 16) + '.img',\n mimeType: fetched.file.mimeType || 'image/jpeg',\n fileType: 'document',\n relatedId: hash\n});\nif (!stored.success) {\n throw new Error('Could not store the source image: ' + stored.error);\n}\n\nsmartbotic.storage.update('image_hashes', hash, { fileId: stored.id }, 0, true);\n\nreturn {\n fileId: stored.id,\n isNew: true,\n checksum: hash,\n url: url,\n seenCount: 1,\n file: fetched.file\n};\n", "timeout": 30 }, "disabled": false, "id": "node_dedupe", "name": "Dedupe Check", "position": { "x": 3280, "y": 860 }, "type": "code" }, { "config": { "combineWith": "and", "conditions": [ { "field": "data.result.isNew", "operator": "is_true", "value": "" } ] }, "disabled": false, "id": "node_if_new", "name": "Is New Image", "position": { "x": 3280, "y": 980 }, "type": "if-condition" }, { "config": { "action": "send", "baseUrl": "https://cloud.fsociety.hu", "credentialId": "cred_c3c55bce-82b4-41ef-a061-bc679b386579", "limit": 20, "message": "Image is done.\n**Original** URL: {{$node[\"Fetch Image\"].url}}\n**NEW**:\nPost: {{$node[\"Process Image\"].result.url}}", "retries": 1, "silent": false, "skipOnError": false, "timeoutMs": 30000, "token": "REDACTED-conversation-token" }, "disabled": true, "id": "node_1785832349865", "name": "Nextcloud Talk", "position": { "x": 3540, "y": 2420 }, "type": "nextcloud-talk" }, { "config": { "_customLabel": "Did the image get made", "combineWith": "and", "conditions": [ { "field": "data.result.succeeded", "operator": "is_true", "value": "" } ] }, "disabled": false, "id": "node_1785909802844", "name": "IF Condition", "position": { "x": 3400, "y": 1760 }, "type": "if-condition" }, { "config": { "action": "send", "baseUrl": "https://cloud.fsociety.hu", "credentialId": "cred_c3c55bce-82b4-41ef-a061-bc679b386579", "limit": 20, "message": "SD.cpp did not produce an image, moving on to the next photo.\n**Original** URL: {{$node[\"Fetch Image\"].url}}\n**Reason**: {{$node[\"Process Image\"].result.error}}", "retries": 1, "silent": true, "skipOnError": true, "timeoutMs": 30000, "token": "REDACTED-conversation-token" }, "disabled": false, "id": "node_gen_failed", "name": "Say It Failed", "position": { "x": 3280, "y": 1900 }, "type": "nextcloud-talk" }, { "config": { "key": "enclosure.url", "mode": "combine-by-key", "path1": "items", "path2": "items" }, "disabled": false, "id": "node_1786088830206_58567", "name": "Merge", "position": { "x": 3300, "y": 360 }, "type": "merge" }, { "config": { "_customLabel": "35photo ru", "detectNewItems": false, "maxItems": 0, "retries": 3, "retryDelayMs": 6000, "retryMaxDelayMs": 30000, "skipOnError": true, "timeout": 30000, "url": "https://35photo.ru/rss/photo_new.xml", "userAgent": "smartbotic-rss/1.0 (+https://smartbotics.ai)" }, "disabled": false, "id": "node_1786088853294_0", "name": "35photo ru", "position": { "x": 3460, "y": 180 }, "type": "rss-reader" }, { "config": { "_customLabel": "Did any feed answer", "combineWith": "and", "conditions": [ { "field": "{{$node[\"Merge\"].count}}", "operator": "equals", "value": "0" } ] }, "disabled": false, "id": "node_no_feed", "name": "Did Any Feed Answer", "position": { "x": 3300, "y": 3240 }, "type": "if-condition" }, { "config": { "action": "send", "baseUrl": "https://cloud.fsociety.hu", "credentialId": "cred_c3c55bce-82b4-41ef-a061-bc679b386579", "limit": 20, "message": "35photo2anime found no photos at all this run - both feeds came back empty or unreachable. Nothing was generated.", "retries": 1, "silent": true, "skipOnError": true, "timeoutMs": 30000, "token": "REDACTED-conversation-token" }, "disabled": false, "id": "node_feeds_down", "name": "Say No Feed Answered", "position": { "x": 3300, "y": 3420 }, "type": "nextcloud-talk" }, { "config": { "fields": [ { "name": "file", "value": "{{$node['Fetch Image'].file}}" } ], "inputSource": "fields", "workflowId": "wf_58ed4490-dad0-4ea3-9c89-7122339dadb9" }, "disabled": false, "id": "node_call_process", "name": "Process Image", "position": { "x": 3540, "y": 1200 }, "type": "call-workflow" } ], "settings": { "continueOnError": false, "errorWorkflowId": "wf_766fcbcb-6345-4e9c-b7f8-f219505ee45c", "storagePermissions": { "collections": { "anime_images": "read-write", "files": "read-write", "image_hashes": "read-write", "nsfw_images": "read-write", "nsfw_temp": "read-write" }, "defaultAccess": "none" } } }