{ "kind": "open-flow-workflow", "version": 1, "name": "Sync Spotify Liked Songs to a Playlist", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_21ff57d699335ed8": { "name": "Sync now", "kind": "start", "inputDefs": [] }, "n_018918303e44de6e": { "name": "Every day at midnight Berlin", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 0 * * *", "timezone": "Europe/Berlin" } ] }, "n_0ed93c246fc92f90": { "inputs": { "name": { "kind": "value", "value": "YOUR_PLAYLIST_NAME" } }, "name": "Choose the playlist", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "name" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "name" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "startedAt" } ], "name": "Choose the playlist", "moduleId": "n_65023297a0c2505b", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_b58774da6aa8b1de": { "inputs": { "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b58774da6aa8b1de", "output": "state" } ] }, "page": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_bf92648165c4bc90", "output": "output" } ] } }, "name": "Collect liked tracks", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "page" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "offset" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect liked tracks", "moduleId": "n_08ab8d17ee7595a8", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a6f173726d1b9673": { "inputs": {}, "name": "More liked tracks pages", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_b58774da6aa8b1de", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_bf92648165c4bc90": { "inputs": { "limit": { "kind": "value", "value": 50 }, "offset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b58774da6aa8b1de", "output": "offset" } ] } }, "name": "Read liked tracks page", "kind": "task", "task": { "inputs": [ { "description": "Maximum number of items to return.", "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 50, "description": "Maximum number of items to return." }, "nullable": true, "handle": "limit", "value": null }, { "description": "Zero-based index of the first item to return.", "jsonSchema": { "type": "integer", "minimum": 0, "description": "Zero-based index of the first item to return." }, "nullable": true, "handle": "offset", "value": null }, { "description": "An ISO 3166-1 alpha-2 country code or from_token to apply market filtering.", "jsonSchema": { "anyOf": [ { "const": "from_token", "description": "A union variant." }, { "type": "string", "minLength": 2, "maxLength": 2, "description": "An ISO 3166-1 alpha-2 country code." } ], "description": "An ISO 3166-1 alpha-2 country code or from_token to apply market filtering." }, "nullable": true, "handle": "market", "value": null } ], "outputs": [ { "description": "Spotify saved-item page.", "jsonSchema": { "type": "object", "properties": { "href": { "type": "string", "description": "Spotify API URL for this page." }, "limit": { "type": "integer", "description": "Maximum number of items returned." }, "next": { "type": [ "string", "null" ], "description": "URL for the next page when available." }, "offset": { "type": "integer", "description": "Zero-based index of the first returned item." }, "previous": { "type": [ "string", "null" ], "description": "URL for the previous page when available." }, "total": { "type": "integer", "description": "Total number of saved items." }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Spotify object with provider-specific fields." }, "description": "Saved-item wrappers returned by Spotify." } }, "required": [ "href", "limit", "offset", "total", "items" ], "additionalProperties": true, "description": "Spotify saved-item page." }, "nullable": false, "handle": "output" } ], "name": "Read liked tracks page", "executor": { "kind": "connector", "action": "spotify.get_user_saved_tracks", "outputMode": "value" } } }, "n_056e3fa41d4564ab": { "inputs": { "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_056e3fa41d4564ab", "output": "state" } ] }, "page": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4a6991aead77c03", "output": "output" } ] } }, "name": "Collect playlists", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "page" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "offset" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect playlists", "moduleId": "n_f24e025e510627f4", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_9071e35e8177327c": { "inputs": {}, "name": "More playlists pages", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_056e3fa41d4564ab", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_e4a6991aead77c03": { "inputs": { "limit": { "kind": "value", "value": 50 }, "offset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_056e3fa41d4564ab", "output": "offset" } ] } }, "name": "Read playlists page", "kind": "task", "task": { "inputs": [ { "description": "Maximum number of items to return.", "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 50, "description": "Maximum number of items to return." }, "nullable": true, "handle": "limit", "value": null }, { "description": "Zero-based index of the first item to return.", "jsonSchema": { "type": "integer", "minimum": 0, "description": "Zero-based index of the first item to return." }, "nullable": true, "handle": "offset", "value": null } ], "outputs": [ { "description": "Spotify paging object.", "jsonSchema": { "type": "object", "properties": { "href": { "type": "string", "description": "Spotify API URL for this page." }, "limit": { "type": "integer", "description": "Maximum number of items returned." }, "next": { "type": [ "string", "null" ], "description": "URL for the next page when available." }, "offset": { "type": "integer", "description": "Zero-based index of the first returned item." }, "previous": { "type": [ "string", "null" ], "description": "URL for the previous page when available." }, "total": { "type": "integer", "description": "Total number of matched items." }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": { "description": "A record value." }, "description": "Spotify object with provider-specific fields." }, "description": "Spotify items on the current page." } }, "required": [ "href", "limit", "offset", "total", "items" ], "additionalProperties": true, "description": "Spotify paging object." }, "nullable": false, "handle": "output" } ], "name": "Read playlists page", "executor": { "kind": "connector", "action": "spotify.get_current_user_playlists", "outputMode": "value" } } }, "n_49d556eee17bf905": { "inputs": { "playlists": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_056e3fa41d4564ab", "output": "items" } ] }, "name": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0ed93c246fc92f90", "output": "name" } ] } }, "name": "Find the named playlist", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "playlists" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "name" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "name" } ], "name": "Find the named playlist", "moduleId": "n_f899b9696293fcfb", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_609ec7b1127dd11c": { "inputs": { "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_609ec7b1127dd11c", "output": "state" } ] }, "page": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_277e48674a0789b0", "output": "page" } ] } }, "name": "Collect playlist tracks", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "page" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "offset" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect playlist tracks", "moduleId": "n_1ed892d3ce8eb16c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_635edd16c793bf7d": { "inputs": {}, "name": "More playlist tracks pages", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_609ec7b1127dd11c", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_19095a883621ba7a": { "inputs": { "playlistId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_49d556eee17bf905", "output": "id" } ] }, "limit": { "kind": "value", "value": 50 }, "offset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_609ec7b1127dd11c", "output": "offset" } ] } }, "name": "Read playlist tracks page", "kind": "task", "task": { "inputs": [ { "description": "Spotify playlist ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Spotify playlist ID." }, "nullable": false, "handle": "playlistId" }, { "description": "Maximum number of items to return.", "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 50, "description": "Maximum number of items to return." }, "nullable": true, "handle": "limit", "value": null }, { "description": "Zero-based index of the first item to return.", "jsonSchema": { "type": "integer", "minimum": 0, "description": "Zero-based index of the first item to return." }, "nullable": true, "handle": "offset", "value": null }, { "description": "An ISO 3166-1 alpha-2 country code or from_token to apply market filtering.", "jsonSchema": { "anyOf": [ { "const": "from_token", "description": "A union variant." }, { "type": "string", "minLength": 2, "maxLength": 2, "description": "An ISO 3166-1 alpha-2 country code." } ], "description": "An ISO 3166-1 alpha-2 country code or from_token to apply market filtering." }, "nullable": true, "handle": "market", "value": null } ], "outputs": [ { "description": "Spotify API URL for this page.", "jsonSchema": { "type": "string", "description": "Spotify API URL for this page." }, "nullable": false, "handle": "href" }, { "description": "Maximum number of items returned.", "jsonSchema": { "type": "integer", "description": "Maximum number of items returned." }, "nullable": false, "handle": "limit" }, { "description": "URL for the next page when available.", "jsonSchema": { "type": [ "string", "null" ], "description": "URL for the next page when available." }, "nullable": true, "handle": "next" }, { "description": "Zero-based index of the first returned item.", "jsonSchema": { "type": "integer", "description": "Zero-based index of the first returned item." }, "nullable": false, "handle": "offset" }, { "description": "URL for the previous page when available.", "jsonSchema": { "type": [ "string", "null" ], "description": "URL for the previous page when available." }, "nullable": true, "handle": "previous" }, { "description": "Total number of playlist items.", "jsonSchema": { "type": "integer", "description": "Total number of playlist items." }, "nullable": false, "handle": "total" }, { "description": "Playlist track entries.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "added_at": { "type": [ "string", "null" ], "description": "Timestamp when the item was added." }, "is_local": { "type": "boolean", "description": "Whether the playlist item references a local file." }, "added_by": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "Spotify user ID." }, "display_name": { "type": [ "string", "null" ], "description": "Spotify display name." }, "uri": { "type": "string", "description": "Spotify user URI." }, "href": { "type": "string", "description": "Spotify user API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Owner external URLs." } }, "required": [ "id" ], "additionalProperties": true, "description": "User who added the item." }, "track": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "Spotify track ID." }, "name": { "type": "string", "description": "Track name." }, "uri": { "type": "string", "description": "Spotify track URI." }, "href": { "type": "string", "description": "Spotify track API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "duration_ms": { "type": "integer", "description": "Track duration in milliseconds." }, "explicit": { "type": "boolean", "description": "Whether the track contains explicit content." }, "disc_number": { "type": "integer", "description": "Disc number." }, "track_number": { "type": "integer", "description": "Track number within the disc." }, "popularity": { "type": "integer", "description": "Track popularity score." }, "preview_url": { "type": [ "string", "null" ], "description": "Preview audio URL when available." }, "is_local": { "type": "boolean", "description": "Whether the track is a local file." }, "available_markets": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Markets where the track is available." }, "artists": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Spotify artist ID." }, "name": { "type": "string", "description": "Artist name." }, "uri": { "type": "string", "description": "Spotify artist URI." }, "href": { "type": "string", "description": "Spotify artist API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "popularity": { "type": "integer", "description": "Artist popularity score." }, "genres": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Artist genres." }, "followers": { "type": "object", "properties": { "href": { "type": [ "string", "null" ], "description": "Followers API URL when available." }, "total": { "type": "integer", "description": "Follower count." } }, "additionalProperties": false, "description": "Follower summary." }, "images": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "description": "Image URL." }, "height": { "type": [ "integer", "null" ], "description": "Image height in pixels." }, "width": { "type": [ "integer", "null" ], "description": "Image width in pixels." } }, "required": [ "url" ], "additionalProperties": false, "description": "Spotify image object." }, "description": "Artist images." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Artist external URLs." } }, "required": [ "id", "name", "uri", "href", "type" ], "additionalProperties": true, "description": "Spotify artist." }, "description": "Track artists." }, "album": { "type": "object", "properties": { "id": { "type": "string", "description": "Spotify album ID." }, "name": { "type": "string", "description": "Album name." }, "uri": { "type": "string", "description": "Spotify album URI." }, "href": { "type": "string", "description": "Spotify album API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "album_type": { "type": "string", "description": "Album type." }, "total_tracks": { "type": "integer", "description": "Total number of tracks." }, "release_date": { "type": "string", "description": "Album release date." }, "release_date_precision": { "type": "string", "description": "Album release date precision." }, "available_markets": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Markets where the album is available." }, "images": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "description": "Image URL." }, "height": { "type": [ "integer", "null" ], "description": "Image height in pixels." }, "width": { "type": [ "integer", "null" ], "description": "Image width in pixels." } }, "required": [ "url" ], "additionalProperties": false, "description": "Spotify image object." }, "description": "Album cover images." }, "artists": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Spotify artist ID." }, "name": { "type": "string", "description": "Artist name." }, "uri": { "type": "string", "description": "Spotify artist URI." }, "href": { "type": "string", "description": "Spotify artist API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "popularity": { "type": "integer", "description": "Artist popularity score." }, "genres": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Artist genres." }, "followers": { "type": "object", "properties": { "href": { "type": [ "string", "null" ], "description": "Followers API URL when available." }, "total": { "type": "integer", "description": "Follower count." } }, "additionalProperties": false, "description": "Follower summary." }, "images": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "description": "Image URL." }, "height": { "type": [ "integer", "null" ], "description": "Image height in pixels." }, "width": { "type": [ "integer", "null" ], "description": "Image width in pixels." } }, "required": [ "url" ], "additionalProperties": false, "description": "Spotify image object." }, "description": "Artist images." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Artist external URLs." } }, "required": [ "id", "name", "uri", "href", "type" ], "additionalProperties": true, "description": "Spotify artist." }, "description": "Album artists." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Album external URLs." } }, "required": [ "id", "name", "uri", "href", "type" ], "additionalProperties": true, "description": "Album summary." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Track external URLs." } }, "required": [ "id", "name", "uri", "href", "type", "duration_ms", "artists" ], "additionalProperties": true, "description": "Track referenced by the playlist item." } }, "additionalProperties": true, "description": "Playlist track entry." }, "description": "Playlist track entries." }, "nullable": false, "handle": "items" } ], "name": "Read playlist tracks page", "executor": { "kind": "connector", "action": "spotify.get_playlist_items" } } }, "n_277e48674a0789b0": { "inputs": { "items": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19095a883621ba7a", "output": "items" } ] }, "next": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19095a883621ba7a", "output": "next" } ] }, "offset": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19095a883621ba7a", "output": "offset" } ] }, "total": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19095a883621ba7a", "output": "total" } ] } }, "name": "Prepare playlist page", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "added_at": { "type": [ "string", "null" ], "description": "Timestamp when the item was added." }, "is_local": { "type": "boolean", "description": "Whether the playlist item references a local file." }, "added_by": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "Spotify user ID." }, "display_name": { "type": [ "string", "null" ], "description": "Spotify display name." }, "uri": { "type": "string", "description": "Spotify user URI." }, "href": { "type": "string", "description": "Spotify user API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Owner external URLs." } }, "required": [ "id" ], "additionalProperties": true, "description": "User who added the item." }, "track": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "Spotify track ID." }, "name": { "type": "string", "description": "Track name." }, "uri": { "type": "string", "description": "Spotify track URI." }, "href": { "type": "string", "description": "Spotify track API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "duration_ms": { "type": "integer", "description": "Track duration in milliseconds." }, "explicit": { "type": "boolean", "description": "Whether the track contains explicit content." }, "disc_number": { "type": "integer", "description": "Disc number." }, "track_number": { "type": "integer", "description": "Track number within the disc." }, "popularity": { "type": "integer", "description": "Track popularity score." }, "preview_url": { "type": [ "string", "null" ], "description": "Preview audio URL when available." }, "is_local": { "type": "boolean", "description": "Whether the track is a local file." }, "available_markets": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Markets where the track is available." }, "artists": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Spotify artist ID." }, "name": { "type": "string", "description": "Artist name." }, "uri": { "type": "string", "description": "Spotify artist URI." }, "href": { "type": "string", "description": "Spotify artist API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "popularity": { "type": "integer", "description": "Artist popularity score." }, "genres": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Artist genres." }, "followers": { "type": "object", "properties": { "href": { "type": [ "string", "null" ], "description": "Followers API URL when available." }, "total": { "type": "integer", "description": "Follower count." } }, "additionalProperties": false, "description": "Follower summary." }, "images": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "description": "Image URL." }, "height": { "type": [ "integer", "null" ], "description": "Image height in pixels." }, "width": { "type": [ "integer", "null" ], "description": "Image width in pixels." } }, "required": [ "url" ], "additionalProperties": false, "description": "Spotify image object." }, "description": "Artist images." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Artist external URLs." } }, "required": [ "id", "name", "uri", "href", "type" ], "additionalProperties": true, "description": "Spotify artist." }, "description": "Track artists." }, "album": { "type": "object", "properties": { "id": { "type": "string", "description": "Spotify album ID." }, "name": { "type": "string", "description": "Album name." }, "uri": { "type": "string", "description": "Spotify album URI." }, "href": { "type": "string", "description": "Spotify album API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "album_type": { "type": "string", "description": "Album type." }, "total_tracks": { "type": "integer", "description": "Total number of tracks." }, "release_date": { "type": "string", "description": "Album release date." }, "release_date_precision": { "type": "string", "description": "Album release date precision." }, "available_markets": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Markets where the album is available." }, "images": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "description": "Image URL." }, "height": { "type": [ "integer", "null" ], "description": "Image height in pixels." }, "width": { "type": [ "integer", "null" ], "description": "Image width in pixels." } }, "required": [ "url" ], "additionalProperties": false, "description": "Spotify image object." }, "description": "Album cover images." }, "artists": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Spotify artist ID." }, "name": { "type": "string", "description": "Artist name." }, "uri": { "type": "string", "description": "Spotify artist URI." }, "href": { "type": "string", "description": "Spotify artist API URL." }, "type": { "type": "string", "description": "Spotify object type." }, "popularity": { "type": "integer", "description": "Artist popularity score." }, "genres": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Artist genres." }, "followers": { "type": "object", "properties": { "href": { "type": [ "string", "null" ], "description": "Followers API URL when available." }, "total": { "type": "integer", "description": "Follower count." } }, "additionalProperties": false, "description": "Follower summary." }, "images": { "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "description": "Image URL." }, "height": { "type": [ "integer", "null" ], "description": "Image height in pixels." }, "width": { "type": [ "integer", "null" ], "description": "Image width in pixels." } }, "required": [ "url" ], "additionalProperties": false, "description": "Spotify image object." }, "description": "Artist images." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Artist external URLs." } }, "required": [ "id", "name", "uri", "href", "type" ], "additionalProperties": true, "description": "Spotify artist." }, "description": "Album artists." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Album external URLs." } }, "required": [ "id", "name", "uri", "href", "type" ], "additionalProperties": true, "description": "Album summary." }, "external_urls": { "type": "object", "additionalProperties": { "type": "string", "description": "A record value." }, "description": "Track external URLs." } }, "required": [ "id", "name", "uri", "href", "type", "duration_ms", "artists" ], "additionalProperties": true, "description": "Track referenced by the playlist item." } }, "additionalProperties": true, "description": "Playlist track entry." }, "description": "Playlist track entries." }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": [ "string", "null" ], "description": "URL for the next page when available." }, "nullable": true, "handle": "next" }, { "jsonSchema": { "type": "integer", "description": "Zero-based index of the first returned item." }, "nullable": false, "handle": "offset" }, { "jsonSchema": { "type": "integer", "description": "Total number of playlist items." }, "nullable": false, "handle": "total" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "page" } ], "name": "Prepare playlist page", "moduleId": "n_bb67ae010bec0138", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_c66f409a1023de4b": { "inputs": { "saved": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b58774da6aa8b1de", "output": "items" } ] }, "tracks": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_609ec7b1127dd11c", "output": "items" } ] }, "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_49d556eee17bf905", "output": "id" } ] }, "name": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_49d556eee17bf905", "output": "name" } ] } }, "name": "Compare track collections", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "saved" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "tracks" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "name" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "add" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "remove" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "name" } ], "name": "Compare track collections", "moduleId": "n_64809724eb75d2e8", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_f69029693358ba5b": { "inputs": { "items": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c66f409a1023de4b", "output": "add" } ] }, "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c66f409a1023de4b", "output": "id" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f69029693358ba5b", "output": "state" } ] } }, "name": "Prepare next additions", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "description": "Spotify URI." }, "minItems": 1, "maxItems": 100, "description": "Spotify URIs in the order Spotify should use." }, "nullable": false, "handle": "uris" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "uri": { "type": "string", "minLength": 1, "description": "Spotify URI of the playlist item to remove." }, "positions": { "type": "array", "items": { "type": "integer", "minimum": 0, "description": "An array item." }, "minItems": 1, "description": "Zero-based positions to remove for this URI when targeting specific occurrences." } }, "required": [ "uri" ], "additionalProperties": false, "description": "Playlist item removal target." }, "minItems": 1, "maxItems": 100, "description": "Playlist items Spotify should remove." }, "nullable": false, "handle": "removeItems" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Prepare next additions", "moduleId": "n_c10c328e09f367c5", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_bd4a3336cd572328": { "inputs": {}, "name": "More tracks to add", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_f69029693358ba5b", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_c72ee8c5e749634c": { "inputs": { "playlistId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f69029693358ba5b", "output": "id" } ] }, "uris": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f69029693358ba5b", "output": "uris" } ] } }, "name": "Add a track", "kind": "task", "task": { "inputs": [ { "description": "Spotify playlist ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Spotify playlist ID." }, "nullable": false, "handle": "playlistId" }, { "description": "Spotify URIs in the order Spotify should use.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "description": "Spotify URI." }, "minItems": 1, "maxItems": 100, "description": "Spotify URIs in the order Spotify should use." }, "nullable": false, "handle": "uris" }, { "description": "Zero-based insertion position.", "jsonSchema": { "type": "integer", "minimum": 0, "description": "Zero-based insertion position." }, "nullable": true, "handle": "position", "value": null } ], "outputs": [ { "description": "Playlist snapshot identifier returned by Spotify.", "jsonSchema": { "type": "string", "description": "Playlist snapshot identifier returned by Spotify." }, "nullable": false, "handle": "snapshotId" } ], "name": "Add a track", "executor": { "kind": "connector", "action": "spotify.add_items_to_playlist" } } }, "n_5c2962d243d286dd": { "inputs": {}, "name": "Any additions", "kind": "condition", "matchMode": "first", "cases": [ { "output": "notify", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_f69029693358ba5b", "output": "count" } }, "operator": ">", "right": { "kind": "value", "value": 0 } } ] } ] } ] }, "n_011dc318838181d9": { "inputs": { "name": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c66f409a1023de4b", "output": "name" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f69029693358ba5b", "output": "count" } ] }, "startedAt": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0ed93c246fc92f90", "output": "startedAt" } ] }, "kind": { "kind": "value", "value": "add" } }, "name": "Prepare additions report", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "name" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "startedAt" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "kind" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "The message body to send. Markdown excluding HTML is allowed." }, "nullable": false, "handle": "message" } ], "name": "Prepare additions report", "moduleId": "n_23ea697f07afd663", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_35f23a7741006a39": { "inputs": { "message": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_011dc318838181d9", "output": "message" } ] }, "extras": { "kind": "value", "value": { "client::display": { "contentType": "text/markdown" } } } }, "name": "Send additions report", "kind": "task", "task": { "inputs": [ { "description": "The message body to send. Markdown excluding HTML is allowed.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The message body to send. Markdown excluding HTML is allowed." }, "nullable": false, "handle": "message" }, { "description": "An optional title for the Gotify message.", "jsonSchema": { "type": "string", "minLength": 1, "description": "An optional title for the Gotify message." }, "nullable": true, "handle": "title", "value": null }, { "description": "An optional message priority. If omitted, Gotify uses the application default priority.", "jsonSchema": { "type": "integer", "description": "An optional message priority. If omitted, Gotify uses the application default priority." }, "nullable": true, "handle": "priority", "value": null }, { "description": "Optional Gotify message extras keyed by the official namespace format.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Optional Gotify message extras keyed by the official namespace format." }, "nullable": true, "handle": "extras", "value": null } ], "outputs": [ { "description": "The Gotify message id.", "jsonSchema": { "type": "integer", "description": "The Gotify message id." }, "nullable": false, "handle": "id" }, { "description": "The id of the Gotify application that sent this message.", "jsonSchema": { "type": "integer", "description": "The id of the Gotify application that sent this message." }, "nullable": false, "handle": "appid" }, { "description": "The Gotify message body. Markdown excluding HTML is allowed.", "jsonSchema": { "type": "string", "description": "The Gotify message body. Markdown excluding HTML is allowed." }, "nullable": false, "handle": "message" }, { "description": "The date and time when the message was created.", "jsonSchema": { "type": "string", "description": "The date and time when the message was created." }, "nullable": false, "handle": "date" }, { "description": "The optional Gotify message title.", "jsonSchema": { "type": "string", "description": "The optional Gotify message title." }, "nullable": true, "handle": "title" }, { "description": "The Gotify message priority.", "jsonSchema": { "type": "integer", "description": "The Gotify message priority." }, "nullable": true, "handle": "priority" }, { "description": "Optional Gotify message extras keyed by the official namespace format.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Optional Gotify message extras keyed by the official namespace format." }, "nullable": true, "handle": "extras" } ], "name": "Send additions report", "executor": { "kind": "connector", "action": "gotify.send_message" } } }, "n_aaaaebab7cca0da8": { "inputs": { "items": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c66f409a1023de4b", "output": "remove" } ] }, "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c66f409a1023de4b", "output": "id" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aaaaebab7cca0da8", "output": "state" } ] } }, "name": "Prepare next deletions", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "id" }, { "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1, "description": "Spotify URI." }, "minItems": 1, "maxItems": 100, "description": "Spotify URIs in the order Spotify should use." }, "nullable": false, "handle": "uris" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "uri": { "type": "string", "minLength": 1, "description": "Spotify URI of the playlist item to remove." }, "positions": { "type": "array", "items": { "type": "integer", "minimum": 0, "description": "An array item." }, "minItems": 1, "description": "Zero-based positions to remove for this URI when targeting specific occurrences." } }, "required": [ "uri" ], "additionalProperties": false, "description": "Playlist item removal target." }, "minItems": 1, "maxItems": 100, "description": "Playlist items Spotify should remove." }, "nullable": false, "handle": "removeItems" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Prepare next deletions", "moduleId": "n_d66c849f0424346c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a343e7943c35b69d": { "inputs": {}, "name": "More tracks to remove", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_aaaaebab7cca0da8", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_1277c2fc5bc52b5c": { "inputs": { "playlistId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aaaaebab7cca0da8", "output": "id" } ] }, "items": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aaaaebab7cca0da8", "output": "removeItems" } ] } }, "name": "Remove a track", "kind": "task", "task": { "inputs": [ { "description": "Spotify playlist ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Spotify playlist ID." }, "nullable": false, "handle": "playlistId" }, { "description": "Playlist items Spotify should remove.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "uri": { "type": "string", "minLength": 1, "description": "Spotify URI of the playlist item to remove." }, "positions": { "type": "array", "items": { "type": "integer", "minimum": 0, "description": "An array item." }, "minItems": 1, "description": "Zero-based positions to remove for this URI when targeting specific occurrences." } }, "required": [ "uri" ], "additionalProperties": false, "description": "Playlist item removal target." }, "minItems": 1, "maxItems": 100, "description": "Playlist items Spotify should remove." }, "nullable": false, "handle": "items" }, { "description": "Playlist snapshot identifier used to target a known version.", "jsonSchema": { "type": "string", "description": "Playlist snapshot identifier used to target a known version." }, "nullable": true, "handle": "snapshotId", "value": null } ], "outputs": [ { "description": "Playlist snapshot identifier returned by Spotify.", "jsonSchema": { "type": "string", "description": "Playlist snapshot identifier returned by Spotify." }, "nullable": false, "handle": "snapshotId" } ], "name": "Remove a track", "executor": { "kind": "connector", "action": "spotify.remove_playlist_items" } } }, "n_dc2e41fb1dc4ba49": { "inputs": {}, "name": "Any deletions", "kind": "condition", "matchMode": "first", "cases": [ { "output": "notify", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_aaaaebab7cca0da8", "output": "count" } }, "operator": ">", "right": { "kind": "value", "value": 0 } } ] } ] } ] }, "n_22435921f1d74de8": { "inputs": { "name": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c66f409a1023de4b", "output": "name" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aaaaebab7cca0da8", "output": "count" } ] }, "startedAt": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0ed93c246fc92f90", "output": "startedAt" } ] }, "kind": { "kind": "value", "value": "remove" } }, "name": "Prepare deletions report", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "name" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "startedAt" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "kind" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "The message body to send. Markdown excluding HTML is allowed." }, "nullable": false, "handle": "message" } ], "name": "Prepare deletions report", "moduleId": "n_88792e5ad935f83c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_8b9e9f13d5fecb3c": { "inputs": { "message": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_22435921f1d74de8", "output": "message" } ] }, "extras": { "kind": "value", "value": { "client::display": { "contentType": "text/markdown" } } } }, "name": "Send deletions report", "kind": "task", "task": { "inputs": [ { "description": "The message body to send. Markdown excluding HTML is allowed.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The message body to send. Markdown excluding HTML is allowed." }, "nullable": false, "handle": "message" }, { "description": "An optional title for the Gotify message.", "jsonSchema": { "type": "string", "minLength": 1, "description": "An optional title for the Gotify message." }, "nullable": true, "handle": "title", "value": null }, { "description": "An optional message priority. If omitted, Gotify uses the application default priority.", "jsonSchema": { "type": "integer", "description": "An optional message priority. If omitted, Gotify uses the application default priority." }, "nullable": true, "handle": "priority", "value": null }, { "description": "Optional Gotify message extras keyed by the official namespace format.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Optional Gotify message extras keyed by the official namespace format." }, "nullable": true, "handle": "extras", "value": null } ], "outputs": [ { "description": "The Gotify message id.", "jsonSchema": { "type": "integer", "description": "The Gotify message id." }, "nullable": false, "handle": "id" }, { "description": "The id of the Gotify application that sent this message.", "jsonSchema": { "type": "integer", "description": "The id of the Gotify application that sent this message." }, "nullable": false, "handle": "appid" }, { "description": "The Gotify message body. Markdown excluding HTML is allowed.", "jsonSchema": { "type": "string", "description": "The Gotify message body. Markdown excluding HTML is allowed." }, "nullable": false, "handle": "message" }, { "description": "The date and time when the message was created.", "jsonSchema": { "type": "string", "description": "The date and time when the message was created." }, "nullable": false, "handle": "date" }, { "description": "The optional Gotify message title.", "jsonSchema": { "type": "string", "description": "The optional Gotify message title." }, "nullable": true, "handle": "title" }, { "description": "The Gotify message priority.", "jsonSchema": { "type": "integer", "description": "The Gotify message priority." }, "nullable": true, "handle": "priority" }, { "description": "Optional Gotify message extras keyed by the official namespace format.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Optional Gotify message extras keyed by the official namespace format." }, "nullable": true, "handle": "extras" } ], "name": "Send deletions report", "executor": { "kind": "connector", "action": "gotify.send_message" } } } }, "edges": [ { "source": "n_b58774da6aa8b1de", "target": "n_a6f173726d1b9673" }, { "source": "n_a6f173726d1b9673", "target": "n_bf92648165c4bc90", "sourceHandle": "more" }, { "source": "n_bf92648165c4bc90", "target": "n_b58774da6aa8b1de" }, { "source": "n_056e3fa41d4564ab", "target": "n_9071e35e8177327c" }, { "source": "n_9071e35e8177327c", "target": "n_e4a6991aead77c03", "sourceHandle": "more" }, { "source": "n_e4a6991aead77c03", "target": "n_056e3fa41d4564ab" }, { "source": "n_609ec7b1127dd11c", "target": "n_635edd16c793bf7d" }, { "source": "n_635edd16c793bf7d", "target": "n_19095a883621ba7a", "sourceHandle": "more" }, { "source": "n_19095a883621ba7a", "target": "n_277e48674a0789b0" }, { "source": "n_277e48674a0789b0", "target": "n_609ec7b1127dd11c" }, { "source": "n_f69029693358ba5b", "target": "n_bd4a3336cd572328" }, { "source": "n_bd4a3336cd572328", "target": "n_c72ee8c5e749634c", "sourceHandle": "more" }, { "source": "n_c72ee8c5e749634c", "target": "n_f69029693358ba5b" }, { "source": "n_bd4a3336cd572328", "target": "n_5c2962d243d286dd", "sourceHandle": "otherwise" }, { "source": "n_5c2962d243d286dd", "target": "n_011dc318838181d9", "sourceHandle": "notify" }, { "source": "n_011dc318838181d9", "target": "n_35f23a7741006a39" }, { "source": "n_aaaaebab7cca0da8", "target": "n_a343e7943c35b69d" }, { "source": "n_a343e7943c35b69d", "target": "n_1277c2fc5bc52b5c", "sourceHandle": "more" }, { "source": "n_1277c2fc5bc52b5c", "target": "n_aaaaebab7cca0da8" }, { "source": "n_a343e7943c35b69d", "target": "n_dc2e41fb1dc4ba49", "sourceHandle": "otherwise" }, { "source": "n_dc2e41fb1dc4ba49", "target": "n_22435921f1d74de8", "sourceHandle": "notify" }, { "source": "n_22435921f1d74de8", "target": "n_8b9e9f13d5fecb3c" }, { "source": "n_21ff57d699335ed8", "target": "n_0ed93c246fc92f90" }, { "source": "n_018918303e44de6e", "target": "n_0ed93c246fc92f90" }, { "source": "n_0ed93c246fc92f90", "target": "n_b58774da6aa8b1de" }, { "source": "n_a6f173726d1b9673", "target": "n_056e3fa41d4564ab", "sourceHandle": "otherwise" }, { "source": "n_9071e35e8177327c", "target": "n_49d556eee17bf905", "sourceHandle": "otherwise" }, { "source": "n_49d556eee17bf905", "target": "n_609ec7b1127dd11c" }, { "source": "n_635edd16c793bf7d", "target": "n_c66f409a1023de4b", "sourceHandle": "otherwise" }, { "source": "n_c66f409a1023de4b", "target": "n_f69029693358ba5b" }, { "source": "n_5c2962d243d286dd", "target": "n_aaaaebab7cca0da8", "sourceHandle": "otherwise" }, { "source": "n_35f23a7741006a39", "target": "n_aaaaebab7cca0da8" } ] } }, "modules": { "n_65023297a0c2505b": { "name": "Choose the playlist", "imports": [], "source": "export default function({name}){if(!name||name===\"YOUR_PLAYLIST_NAME\")throw Error(\"Set the playlist name\");return {name,startedAt:Date.now()};}" }, "n_08ab8d17ee7595a8": { "name": "Collect liked tracks", "imports": [], "source": "export default function collect({page,previousState}){\n if(previousState===null)return {state:{items:[],offset:0},items:[],offset:0,more:true};\n if(!page||!Array.isArray(page.items)||!Number.isSafeInteger(page.offset)||page.offset!==previousState.offset||!Number.isSafeInteger(page.total)||page.total<0)throw Error('Invalid Spotify page');\n if(!Object.hasOwn(page,'next'))throw Error('Spotify page lacks next marker');\n const items=previousState.items.concat(page.items),next=page.next;\n if(next!==null&&next!==undefined&&(typeof next!=='string'||!next||page.items.length===0))throw Error('Invalid Spotify next page');\n const offset=page.offset+page.items.length;\n if(next!=null){const match=/[?&]offset=(\\d+)(?:&|$)/.exec(next);if(!match||Number(match[1])!==offset)throw Error('Invalid Spotify next offset');}\n return {state:{items,offset},items,offset,more:next!=null};\n}\n" }, "n_f24e025e510627f4": { "name": "Collect playlists", "imports": [], "source": "export default function collect({page,previousState}){\n if(previousState===null)return {state:{items:[],offset:0},items:[],offset:0,more:true};\n if(!page||!Array.isArray(page.items)||!Number.isSafeInteger(page.offset)||page.offset!==previousState.offset||!Number.isSafeInteger(page.total)||page.total<0)throw Error('Invalid Spotify page');\n if(!Object.hasOwn(page,'next'))throw Error('Spotify page lacks next marker');\n const items=previousState.items.concat(page.items),next=page.next;\n if(next!==null&&next!==undefined&&(typeof next!=='string'||!next||page.items.length===0))throw Error('Invalid Spotify next page');\n const offset=page.offset+page.items.length;\n if(next!=null){const match=/[?&]offset=(\\d+)(?:&|$)/.exec(next);if(!match||Number(match[1])!==offset)throw Error('Invalid Spotify next offset');}\n return {state:{items,offset},items,offset,more:next!=null};\n}\n" }, "n_f899b9696293fcfb": { "name": "Find the named playlist", "imports": [], "source": "export default function select({playlists,name}){\n if(typeof name!=='string'||!name.trim()||name==='YOUR_PLAYLIST_NAME')throw Error('Set the playlist name');\n const matches=playlists.filter(x=>x?.name===name);if(matches.length!==1)throw Error('Expected exactly one playlist with this name');\n const id=matches[0].id;if(typeof id!=='string'||!id)throw Error('Playlist has no ID');return {id,name};\n}\n" }, "n_1ed892d3ce8eb16c": { "name": "Collect playlist tracks", "imports": [], "source": "export default function collect({page,previousState}){\n if(previousState===null)return {state:{items:[],offset:0},items:[],offset:0,more:true};\n if(!page||!Array.isArray(page.items)||!Number.isSafeInteger(page.offset)||page.offset!==previousState.offset||!Number.isSafeInteger(page.total)||page.total<0)throw Error('Invalid Spotify page');\n if(!Object.hasOwn(page,'next'))throw Error('Spotify page lacks next marker');\n const items=previousState.items.concat(page.items),next=page.next;\n if(next!==null&&next!==undefined&&(typeof next!=='string'||!next||page.items.length===0))throw Error('Invalid Spotify next page');\n const offset=page.offset+page.items.length;\n if(next!=null){const match=/[?&]offset=(\\d+)(?:&|$)/.exec(next);if(!match||Number(match[1])!==offset)throw Error('Invalid Spotify next offset');}\n return {state:{items,offset},items,offset,more:next!=null};\n}\n" }, "n_bb67ae010bec0138": { "name": "Prepare playlist page", "imports": [], "source": "export default function({items,next,offset,total}){return {page:{items,next,offset,total}};}" }, "n_64809724eb75d2e8": { "name": "Compare track collections", "imports": [], "source": "export default function diff({saved,tracks,id,name}){\n const map=(entries,label)=>entries.map(x=>{if(typeof x?.track?.uri!=='string'||!x.track.uri||typeof x.added_at!=='string'||!Number.isFinite(Date.parse(x.added_at)))throw Error('Invalid '+label+' item');return {uri:x.track.uri,addedAt:x.added_at};}).sort((a,b)=>a.addedAt.localeCompare(b.addedAt));\n const liked=map(saved,'saved'),existing=map(tracks,'playlist');if(!liked.length)return {add:[],remove:[],id,name};\n const matched=new Set(),add=[];for(const x of liked){const index=existing.findIndex(y=>y.uri===x.uri);if(index<0)add.push({uri:x.uri});else matched.add(index)}const remove=existing.filter((_,i)=>!matched.has(i)).map(x=>({uri:x.uri}));return {add,remove,id,name};\n}\n" }, "n_c10c328e09f367c5": { "name": "Prepare next additions", "imports": [], "source": "export default function next({items,id,previousState}){const index=previousState===null?0:previousState.index+1;if(index>=items.length)return {state:{index},more:false,id,uris:['-'],removeItems:[{uri:'-'}],count:items.length};const uri=items[index]?.uri;if(typeof uri!=='string'||!uri)throw Error('Invalid track URI');return {state:{index},more:true,id,uris:[uri],removeItems:[{uri}],count:items.length};}\n" }, "n_23ea697f07afd663": { "name": "Prepare additions report", "imports": [], "source": "export default function message({name,count,startedAt,kind}){const minutes=((Date.now()-startedAt)/60000).toFixed(1),verb=kind==='add'?'Added':'Deleted';return {message:`### Sync of Liked Songs to ${name} finished.\\n#### ${verb} ${count} Songs in ${minutes} Minutes ${kind==='add'?'to':'from'} ${name}.`};}\n" }, "n_d66c849f0424346c": { "name": "Prepare next deletions", "imports": [], "source": "export default function next({items,id,previousState}){const index=previousState===null?0:previousState.index+1;if(index>=items.length)return {state:{index},more:false,id,uris:['-'],removeItems:[{uri:'-'}],count:items.length};const uri=items[index]?.uri;if(typeof uri!=='string'||!uri)throw Error('Invalid track URI');return {state:{index},more:true,id,uris:[uri],removeItems:[{uri}],count:items.length};}\n" }, "n_88792e5ad935f83c": { "name": "Prepare deletions report", "imports": [], "source": "export default function message({name,count,startedAt,kind}){const minutes=((Date.now()-startedAt)/60000).toFixed(1),verb=kind==='add'?'Added':'Deleted';return {message:`### Sync of Liked Songs to ${name} finished.\\n#### ${verb} ${count} Songs in ${minutes} Minutes ${kind==='add'?'to':'from'} ${name}.`};}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }