Workflow: build automation using n8n for Splitout Code Create Scheduled
Integrations: code, filter, googleSheets, httpRequest, limit, merge, noOp, scheduleTrigger, set, splitOut, spotify, stickyNote

{"meta": {"instanceId": "8e95de061dd3893a50b8b4c150c8084a7848fb1df63f53533941b7c91a8ab996"}, "nodes": [{"id": "6325369f-5881-4e4e-b71b-510a64b236ef", "name": "Retrieve relevant info", "type": "n8n-nodes-base.set", "position": [1260, 400], "parameters": {"mode": "raw", "options": {}, "jsonOutput": "={\n\"track\" : \"{{ $json.track.name.replaceAll('\"',\"'\") }}\",\n\"artist\":  \"{{ $json.track.artists[0].name }}\",\n\"album\" :\"{{ $json.track.album.name }}\",\n\"track_spotify_uri\" : \"{{ $json.track.uri }}\",\n\"track_spotify_id\" : \"{{ $json.track.id }}\",\n\"external_urls\": \"{{ $json.track.external_urls.spotify }}\",\n\"track_popularity\"  : \"{{ $json.track.popularity }}\",\n\"album_release_date\" : \"{{ $json.track.album.release_date.toDateTime().year }}\"\n}"}, "typeVersion": 3.4}, {"id": "2252fe16-6ee7-4fbe-b74e-d9bdcc7ad708", "name": "Batch preparation", "type": "n8n-nodes-base.code", "position": [1560, 280], "parameters": {"jsCode": "const items = $input.all();\nconst trackSpotifyIds = items.map((item) => item?.json?.track_spotify_id);\n\nconst aggregatedItems = [];\nfor (let i = 0; i < trackSpotifyIds.length; i += 100) {\n  aggregatedItems.push({\n    json: {\n      trackSpotifyIds: trackSpotifyIds.slice(i, i + 100),\n    },\n  });\n}\n\nreturn aggregatedItems;\n"}, "typeVersion": 2}, {"id": "83c181f8-ed18-41d7-8c7e-26b0dd320083", "name": "Get Track details", "type": "n8n-nodes-base.httpRequest", "position": [1980, 280], "parameters": {"url": "https://api.spotify.com/v1/audio-features", "options": {}, "sendQuery": true, "authentication": "predefinedCredentialType", "queryParameters": {"parameters": [{"name": "ids", "value": "={{ $json.trackSpotifyIds.join(\",\")}}"}]}, "nodeCredentialType": "spotifyOAuth2Api"}, "credentials": {"spotifyOAuth2Api": {"id": "S9iODAILG9yn19ta", "name": "Spotify account - Arnaud's"}}, "typeVersion": 4.2}, {"id": "6cf1afdd-7e62-4d76-a034-5e943e2db0ff", "name": "Split Out", "type": "n8n-nodes-base.splitOut", "position": [2200, 280], "parameters": {"options": {}, "fieldToSplitOut": "audio_features"}, "typeVersion": 1}, {"id": "fc3ab428-40f9-4439-83b6-8ecb125d510f", "name": "Anthropic Chat Model", "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic", "position": [4180, 1100], "parameters": {"options": {"temperature": 0.3, "maxTokensToSample": 8192}}, "credentials": {"anthropicApi": {"id": "SsGpCc91NlFBaH2I", "name": "Anthropic account - Bertrand"}}, "typeVersion": 1.2}, {"id": "e712d5c0-5045-4cd2-8324-5cde4fc37b2a", "name": "Get Playlist", "type": "n8n-nodes-base.spotify", "position": [1080, -71], "parameters": {"resource": "playlist", "operation": "getUserPlaylists"}, "credentials": {"spotifyOAuth2Api": {"id": "S9iODAILG9yn19ta", "name": "Spotify account - Arnaud's"}}, "typeVersion": 1}, {"id": "5d9d2abe-c85f-41a9-bb99-28a1306a8685", "name": "Get Tracks", "type": "n8n-nodes-base.spotify", "position": [1040, 400], "parameters": {"resource": "library", "returnAll": true}, "credentials": {"spotifyOAuth2Api": {"id": "S9iODAILG9yn19ta", "name": "Spotify account - Arnaud's"}}, "typeVersion": 1}, {"id": "9e5b30cb-db4c-445e-bd82-314740d6af64", "name": "Structured Output Parser", "type": "@n8n/n8n-nodes-langchain.outputParserStructured", "position": [4540, 1100], "parameters": {"schemaType": "manual", "inputSchema": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"array\",\n  \"items\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"playlistName\": {\n        \"type\": \"string\",\n        \"description\": \"The name of the playlist\"\n      },\n      \"uri\": {\n        \"type\": \"string\",\n        \"description\": \"The unique identifier for the playlist, in URI format\"\n      },\n      \"trackUris\": {\n        \"type\": \"array\",\n        \"items\": {\n          \"type\": \"string\",\n          \"description\": \"The unique identifier for each track in the playlist, in URI format\"\n        },\n        \"description\": \"A list of track URIs for the playlist\",\n        \"maxItems\": 1000\n      }\n    },\n    \"required\": [\"playlistName\", \"uri\", \"trackUris\"],\n    \"additionalProperties\": false\n  }\n}\n"}, "typeVersion": 1.2}, {"id": "8ddc9606-d70a-4a94-8dff-9ed17cec378e", "name": "Playlists informations", "type": "n8n-nodes-base.set", "position": [1520, -71], "parameters": {"mode": "raw", "options": {}, "jsonOutput": "={\n  \"playlist_name\": \"{{ $json.name }}\",\n  \"playlist_description\": \"{{ $json.description }}\",\n  \"playlist_spotify_uri\": \"{{ $json.uri }}\"\n}\n "}, "typeVersion": 3.4}, {"id": "ec99ed3b-3cd9-4dc2-a7c6-5099eaeea93b", "name": "Filter my playlist", "type": "n8n-nodes-base.filter", "position": [1300, -71], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "bad771d7-2f4c-43bb-996a-0e46bbf85231", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $json.owner.display_name }}", "rightValue": "Arnaud"}]}}, "typeVersion": 2.2}, {"id": "64e57339-2bf2-4dc7-bca7-3de7da80b6eb", "name": "Split Out1", "type": "n8n-nodes-base.splitOut", "position": [4700, 880], "parameters": {"options": {}, "fieldToSplitOut": "output"}, "typeVersion": 1}, {"id": "924f5b88-9dce-4acc-9ad6-0f25f804fcc5", "name": "Batch preparation1", "type": "n8n-nodes-base.code", "position": [5380, 880], "parameters": {"jsCode": "const items = $input.all();\nconst result = [];\n\nitems.forEach((item) => {\n  const trackUris = item.json.trackUris;\n  if (trackUris.length > 100) {\n    for (let i = 0; i < trackUris.length; i += 100) {\n      const newItem = { ...item.json, trackUris: trackUris.slice(i, i + 100) };\n      result.push(newItem);\n    }\n  } else {\n    result.push(item.json);\n  }\n});\n\nreturn result;\n"}, "typeVersion": 2}, {"id": "980ef09e-557d-4748-b92a-ceec9dc54a6b", "name": "Merge", "type": "n8n-nodes-base.merge", "position": [2400, 380], "parameters": {"mode": "combine", "options": {"disableDotNotation": false}, "advanced": true, "joinMode": "enrichInput2", "mergeByFields": {"values": [{"field1": "id", "field2": "track_spotify_id"}]}}, "typeVersion": 3}, {"id": "a6149a04-bd65-4e55-8c1b-5e18fd98c2e8", "name": "Simplify Tracks informations", "type": "n8n-nodes-base.set", "position": [2620, 380], "parameters": {"include": "except", "options": {}, "assignments": {"assignments": [{"id": "8bd9a8c4-0c95-43b0-8962-0e005504b6ee", "name": "date_added", "type": "string", "value": "={{ $now.format('yyyy-MM-dd') }}"}]}, "excludeFields": "track_spotify_id, external_urls, id, uri, track_href, analysis_url", "includeOtherFields": true}, "typeVersion": 3.4}, {"id": "96432403-f15f-4015-8024-72731e18b18d", "name": "Limit", "type": "n8n-nodes-base.limit", "position": [2860, 240], "parameters": {}, "typeVersion": 1}, {"id": "3efb9ee3-1955-40eb-9958-a5fb515f30c1", "name": "Get logged tracks", "type": "n8n-nodes-base.googleSheets", "position": [3120, 240], "parameters": {"options": {"dataLocationOnSheet": {"values": {"range": "A:B", "rangeDefinition": "specifyRangeA1"}}}, "sheetName": {"__rl": true, "mode": "list", "value": "gid=0", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/19VwKRDbsh8uU6xitnTXUjk1u73XCGThzyE8nv1YsP24/edit#gid=0", "cachedResultName": "tracks listing"}, "documentId": {"__rl": true, "mode": "url", "value": "https://docs.google.com/spreadsheets/d/19VwKRDbsh8uU6xitnTXUjk1u73XCGThzyE8nv1YsP24/edit?gid=0#gid=0"}, "combineFilters": "OR"}, "credentials": {"googleSheetsOAuth2Api": {"id": "8UJ5YBcPU0IOkjEd", "name": "Google Sheets - Arnaud Growth Perso"}}, "typeVersion": 4.5}, {"id": "58821bc3-254c-46d2-b882-d1995aaf3d46", "name": "Excluding logged tracks", "type": "n8n-nodes-base.merge", "position": [3380, 360], "parameters": {"mode": "combine", "options": {}, "joinMode": "keepNonMatches", "outputDataFrom": "input2", "fieldsToMatchString": "track_spotify_uri"}, "typeVersion": 3}, {"id": "8a28cd62-9316-487e-a8f7-dd5ed3eab6c8", "name": "Filter", "type": "n8n-nodes-base.filter", "position": [5120, 880], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "5457225f-104a-4d38-9481-d243ba656358", "operator": {"type": "array", "operation": "notEmpty", "singleValue": true}, "leftValue": "={{ $json.trackUris }}", "rightValue": ""}]}}, "typeVersion": 2.2}, {"id": "770a42f8-f4e5-44b8-a096-945db7c9f85e", "name": "Split Out2", "type": "n8n-nodes-base.splitOut", "disabled": true, "position": [5120, 520], "parameters": {"include": "allOtherFields", "options": {}, "fieldToSplitOut": "trackUris"}, "typeVersion": 1}, {"id": "da5c9b03-2ace-40af-9364-c9119eaef7b0", "name": "Manual Verification", "type": "n8n-nodes-base.merge", "disabled": true, "position": [5380, 480], "parameters": {"mode": "combine", "options": {}, "advanced": true, "joinMode": "enrichInput2", "mergeByFields": {"values": [{"field1": "track_spotify_uri", "field2": "trackUris"}]}}, "typeVersion": 3}, {"id": "98b3fca5-5b14-42e4-8e5f-5506643a54bb", "name": "Spotify", "type": "n8n-nodes-base.spotify", "onError": "continueErrorOutput", "position": [5640, 880], "parameters": {"id": "={{ $json.uri }}", "trackID": "={{ $json.trackUris.join(\",\") }}", "resource": "playlist", "additionalFields": {}}, "credentials": {"spotifyOAuth2Api": {"id": "S9iODAILG9yn19ta", "name": "Spotify account - Arnaud's"}}, "retryOnFail": true, "typeVersion": 1, "waitBetweenTries": 5000}, {"id": "536f7ed8-d3bf-4c95-8a7a-42f3a2f47e5c", "name": "Aggregate by 200 tracks", "type": "n8n-nodes-base.code", "position": [4080, 880], "parameters": {"jsCode": "const items = $input.all();\nconst chunkSize = 200;\nconst result = [];\n\nfor (let i = 0; i < items.length; i += chunkSize) {\n  const chunk = items.slice(i, i + chunkSize).map((item) => item.json);\n  result.push({json:{chunk}}); // Wrap each chunk in an object with a json property\n}\n\nreturn result;\n"}, "typeVersion": 2}, {"id": "e590ef66-4fc1-4b4d-a56c-f93db389500e", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [-1160, -280], "parameters": {"width": 1055, "height": 1188.074539731524, "content": "# Monthly Spotify Track Archiving and Playlist Classification\n\nThis n8n workflow allows you to automatically archive your monthly Spotify liked tracks in a Google Sheet, along with playlist details and descriptions. Based on this data, Claude 3.5 is used to classify each track into multiple playlists and add them in bulk.\n\n## Who is this template for?\nThis workflow template is perfect for Spotify users who want to systematically archive their listening history and organize their tracks into custom playlists.\n\n## What problem does this workflow solve?\nIt automates the monthly process of tracking, storing, and categorizing Spotify tracks into relevant playlists, helping users maintain well-organized music collections and keep a historical record of their listening habits.\n\n## Workflow Overview\n- **Trigger Options**: Can be initiated manually or on a set schedule.\n- **Spotify Playlists Retrieval**: Fetches the current playlists and filters them by owner.\n- **Track Details Collection**: Retrieves information such as track ID and popularity from the user\u2019s library.\n- **Audio Features Fetching**: Uses Spotify's API to get audio features for each track.\n- **Data Merging**: Combines track information with their audio features.\n- **Duplicate Checking**: Filters out tracks that have already been logged in Google Sheets.\n- **Data Logging**: Archives new tracks into a Google Sheet.\n- **AI Classification**: Uses an AI model to classify tracks into suitable playlists.\n- **Playlist Updates**: Adds classified tracks to the corresponding playlists.\n\n## Setup Instructions\n1. **Credentials Setup**:  \n   Make sure you have valid Spotify OAuth2 and Google Sheets access credentials.\n2. **Trigger Configuration**:  \n   Choose between manual or scheduled triggers to start the workflow.\n3. **Google Sheets Preparation**:  \n   Set up a Google Sheet with the necessary structure for logging track details.\n4. **Spotify Playlists Setup**:  \n   Have a diverse range of playlists and exhaustive description (see example) ready to accommodate different music genres and moods.\n\n## Customization Options\n- **Adjust Playlist Conditions**:  \n   Modify the AI model\u2019s classification criteria to align with your personal music preferences.\n- **Enhance Track Analysis**:  \n   Incorporate additional audio features or external data sources for more refined track categorization.\n- **Personalize Data Logging**:  \n   Customize which track attributes to log in Google Sheets based on your archival preferences.\n- **Configure Scheduling**:  \n   Set a preferred schedule for periodic track archiving, e.g., monthly or weekly.\n\n## Cost Estimate \nFor 300 tracks, the token usage amounts to approximately 60,000 tokens (58,000 for input and 2,000 for completion), costing around 20 cents with Claude 3.5 Sonnet (as of October 2024)."}, "typeVersion": 1}, {"id": "c6e33534-a923-4a1e-8d40-54c3d39f7352", "name": "Monthly Trigger", "type": "n8n-nodes-base.scheduleTrigger", "position": [660, 160], "parameters": {"rule": {"interval": [{"field": "months"}]}}, "typeVersion": 1.2}, {"id": "a085a6af-ede4-4e3a-9bf4-4c29e821af35", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [1000, -240], "parameters": {"width": 1729.2548791395811, "height": 349.93537232723713, "content": "**Get & Log Playlists informations**"}, "typeVersion": 1}, {"id": "ad33760b-7fa9-4246-806c-438fdf31247b", "name": "Get logged playlists", "type": "n8n-nodes-base.googleSheets", "position": [2000, -171], "parameters": {"options": {"dataLocationOnSheet": {"values": {"rangeDefinition": "detectAutomatically"}}}, "sheetName": {"__rl": true, "mode": "list", "value": 1684849334, "cachedResultUrl": "https://docs.google.com/spreadsheets/d/19VwKRDbsh8uU6xitnTXUjk1u73XCGThzyE8nv1YsP24/edit#gid=1684849334", "cachedResultName": "playslists listing"}, "documentId": {"__rl": true, "mode": "url", "value": "https://docs.google.com/spreadsheets/d/19VwKRDbsh8uU6xitnTXUjk1u73XCGThzyE8nv1YsP24/edit?gid=0#gid=0"}, "combineFilters": "OR"}, "credentials": {"googleSheetsOAuth2Api": {"id": "8UJ5YBcPU0IOkjEd", "name": "Google Sheets - Arnaud Growth Perso"}}, "typeVersion": 4.5}, {"id": "e2beb78f-227c-4ecf-bf90-377d49050646", "name": "Log new tracks", "type": "n8n-nodes-base.googleSheets", "position": [3680, 200], "parameters": {"columns": {"value": {}, "schema": [{"id": "track", "type": "string", "display": true, "removed": false, "required": false, "displayName": "track", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "artist", "type": "string", "display": true, "removed": false, "required": false, "displayName": "artist", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "album", "type": "string", "display": true, "removed": false, "required": false, "displayName": "album", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "track_spotify_id", "type": "string", "display": true, "removed": false, "required": false, "displayName": "track_spotify_id", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "external_urls", "type": "string", "display": true, "removed": false, "required": false, "displayName": "external_urls", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "track_popularity", "type": "string", "display": true, "removed": false, "required": false, "displayName": "track_popularity", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "album_release_date", "type": "string", "display": true, "removed": false, "required": false, "displayName": "album_release_date", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "danceability", "type": "string", "display": true, "removed": false, "required": false, "displayName": "danceability", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "energy", "type": "string", "display": true, "removed": false, "required": false, "displayName": "energy", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "key", "type": "string", "display": true, "removed": false, "required": false, "displayName": "key", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "loudness", "type": "string", "display": true, "removed": false, "required": false, "displayName": "loudness", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "mode", "type": "string", "display": true, "removed": false, "required": false, "displayName": "mode", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "speechiness", "type": "string", "display": true, "removed": false, "required": false, "displayName": "speechiness", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "acousticness", "type": "string", "display": true, "removed": false, "required": false, "displayName": "acousticness", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "instrumentalness", "type": "string", "display": true, "removed": false, "required": false, "displayName": "instrumentalness", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "liveness", "type": "string", "display": true, "removed": false, "required": false, "displayName": "liveness", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "valence", "type": "string", "display": true, "removed": false, "required": false, "displayName": "valence", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "tempo", "type": "string", "display": true, "removed": false, "required": false, "displayName": "tempo", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "type", "type": "string", "display": true, "removed": false, "required": false, "displayName": "type", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "id", "type": "string", "display": true, "removed": false, "required": false, "displayName": "id", "defaultMatch": true, "canBeUsedToMatch": true}, {"id": "uri", "type": "string", "display": true, "removed": false, "required": false, "displayName": "uri", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "track_href", "type": "string", "display": true, "removed": false, "required": false, "displayName": "track_href", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "analysis_url", "type": "string", "display": true, "removed": false, "required": false, "displayName": "analysis_url", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "duration_ms", "type": "string", "display": true, "removed": false, "required": false, "displayName": "duration_ms", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "time_signature", "type": "string", "display": true, "removed": false, "required": false, "displayName": "time_signature", "defaultMatch": false, "canBeUsedToMatch": true}], "mappingMode": "autoMapInputData", "matchingColumns": []}, "options": {"useAppend": true}, "operation": "append", "sheetName": {"__rl": true, "mode": "list", "value": "gid=0", "cachedResultUrl": "https://docs.google.com/spreadsheets/d/19VwKRDbsh8uU6xitnTXUjk1u73XCGThzyE8nv1YsP24/edit#gid=0", "cachedResultName": "tracks listing"}, "documentId": {"__rl": true, "mode": "url", "value": "https://docs.google.com/spreadsheets/d/19VwKRDbsh8uU6xitnTXUjk1u73XCGThzyE8nv1YsP24/edit?gid=0#gid=0"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "8UJ5YBcPU0IOkjEd", "name": "Google Sheets - Arnaud Growth Perso"}}, "typeVersion": 4.5}, {"id": "e9d311c8-d39c-481d-99dc-c89d360f3217", "name": "Log new playlists", "type": "n8n-nodes-base.googleSheets", "position": [2480, -91], "parameters": {"columns": {"value": {}, "schema": [{"id": "playlist_name", "type": "string", "display": true, "removed": false, "required": false, "displayName": "playlist_name", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "playlist_description", "type": "string", "display": true, "removed": false, "required": false, "displayName": "playlist_description", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "playlist_spotify_uri", "type": "string", "display": true, "removed": false, "required": false, "displayName": "playlist_spotify_uri", "defaultMatch": false, "canBeUsedToMatch": true}], "mappingMode": "autoMapInputData", "matchingColumns": []}, "options": {"useAppend": true}, "operation": "append", "sheetName": {"__rl": true, "mode": "list", "value": 1684849334, "cachedResultUrl": "https://docs.google.com/spreadsheets/d/19VwKRDbsh8uU6xitnTXUjk1u73XCGThzyE8nv1YsP24/edit#gid=1684849334", "cachedResultName": "playslists listing"}, "documentId": {"__rl": true, "mode": "url", "value": "https://docs.google.com/spreadsheets/d/19VwKRDbsh8uU6xitnTXUjk1u73XCGThzyE8nv1YsP24/edit?gid=0#gid=0"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "8UJ5YBcPU0IOkjEd", "name": "Google Sheets - Arnaud Growth Perso"}}, "typeVersion": 4.5}, {"id": "0e9dd47b-0bd3-4c8c-84c6-7ef566f41135", "name": "Excluding logged playlists", "type": "n8n-nodes-base.merge", "position": [2240, -91], "parameters": {"mode": "combine", "options": {}, "joinMode": "keepNonMatches", "outputDataFrom": "input2", "fieldsToMatchString": "playlist_spotify_uri"}, "typeVersion": 3}, {"id": "7e0f1d5b-d74b-474d-bde2-3966ab51e048", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [1000, 195.4666080114149], "parameters": {"width": 2831.0439846349473, "height": 394.4687643158222, "content": "**Get & Log Playlists informations**"}, "typeVersion": 1}, {"id": "b851790c-126a-43bd-a223-0a023d423309", "name": "Limit2", "type": "n8n-nodes-base.limit", "position": [1780, -171], "parameters": {}, "typeVersion": 1}, {"id": "f0ec1751-116a-4d14-b815-39f4ba989e33", "name": "Classify new tracks", "type": "n8n-nodes-base.noOp", "position": [3880, 460], "parameters": {}, "typeVersion": 1}, {"id": "38df0ed5-697d-489d-8d0c-2b18c2e017a8", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [3960, 740], "parameters": {"width": 726.2282986582347, "height": 562.9881279640259, "content": "**AI Classification**"}, "typeVersion": 1}, {"id": "5649c3b6-dc55-488f-9afc-106ac410fae1", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [5080, 760], "parameters": {"width": 858.3555537284071, "height": 309.3037982292949, "content": "**Update Spotify Playlists**"}, "typeVersion": 1}, {"id": "8410fc7d-64e3-4abf-b035-667945e84d64", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "position": [5080, 340], "parameters": {"width": 578.2457729796415, "height": 309.3037982292949, "content": "**Manual Verification**\nWe performed this merge to include the track name, making it easier to verify the AI's output. Adding the track name directly in the machine learning response would double the completion tokens, so it was avoided to keep token usage efficient."}, "typeVersion": 1}, {"id": "d59c316a-22d4-46f0-b97c-789e8c196ab1", "name": "Sticky Note6", "type": "n8n-nodes-base.stickyNote", "position": [-1140, 1040], "parameters": {"width": 610.3407699712512, "height": 922.4081979777811, "content": "### Playlists' Description Examples\n\n\n| Playlist Name           | Playlist Description                                                                                                                                             |\n|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| Classique               | Indulge in the timeless beauty of classical music with this refined playlist. From baroque to romantic periods, this collection showcases renowned compositions.   |\n| Poi                     | Find your flow with this dynamic playlist tailored for poi, staff, and ball juggling. Featuring rhythmic tracks that complement your movements.                   |\n| Pro Sound               | Boost your productivity and focus with this carefully selected mix of concentration-enhancing music. Ideal for work or study sessions.                           |\n| ChillySleep             | Drift off to dreamland with this soothing playlist of sleep-inducing tracks. Gentle melodies and ambient sounds create a peaceful atmosphere for restful sleep.  |\n| To Sing                 | Warm up your vocal cords and sing your heart out with karaoke-friendly tracks. Featuring popular songs, perfect for solo performances or group sing-alongs.      |\n| 1990s                   | Relive the diverse musical landscape of the 90s with this eclectic mix. From grunge to pop, hip-hop to electronic, this playlist showcases defining genres.       |\n| 1980s                   | Take a nostalgic trip back to the era of big hair and neon with this 80s playlist. Packed with iconic hits and forgotten gems, capturing the energy of the decade.|\n| Groove Up               | Elevate your mood and energy with this upbeat playlist. Featuring a mix of feel-good tracks across various genres to lift your spirits and get you moving.       |\n| Reggae & Dub            | Relax and unwind with the laid-back vibes of reggae and dub. This playlist combines classic reggae tunes with deep, spacious dub tracks for a chilled-out vibe.   |\n| Psytrance               | Embark on a mind-bending journey with this collection of psychedelic trance tracks. Ideal for late-night dance sessions or intense focus.                        |\n| Cumbia                  | Sway to the infectious rhythms of Cumbia with this lively playlist. Blending traditional Latin American sounds with modern interpretations for a danceable mix.  |\n| Funky Groove            | Get your body moving with this collection of funk and disco tracks. Featuring irresistible basslines and catchy rhythms, perfect for dance parties.              |\n| French Chanson          | Experience the romance and charm of France with this mix of classic and modern French songs, capturing the essence of French musical culture.                    |\n| Workout Motivation      | Push your limits and power through your exercise routine with this high-energy playlist. From warm-up to cool-down, these tracks will keep you motivated.        |\n| Cinematic Instrumentals | Immerse yourself in a world of atmospheric sounds with this collection of cinematic instrumental tracks, perfect for focus, relaxation, or contemplation.        |\n"}, "typeVersion": 1}, {"id": "d43ce92b-3831-4fd5-a59c-f9dcd7f1b8ea", "name": "Basic LLM Chain - AI Classification", "type": "@n8n/n8n-nodes-langchain.chainLlm", "position": [4280, 880], "parameters": {"text": "=#### Tracks to Analyze:\n<tracks_to_analyze>\n   {{ JSON.stringify($json.chunk) }}\n</tracks_to_analyze>", "messages": {"messageValues": [{"message": "You are an expert in music classification with extensive knowledge of genres, moods, and various musical elements. Your task is to analyze the provided tracks and generate a **comprehensive and exhaustive classification** to enhance my listening experience.\n\n### Process:\n\n1. **Identify Playlist Style**: For each of my personal playlist, use the information provided in <playlists_informations>, including the name and description, to understand its purpose and the types of tracks that are most suitable for it. Use this understanding to guide your classification decisions.\n\n2. **Identify Track Characteristics**: For each track in <tracks_to_analyze>, even if you don't have the audio, use the track's **title and artist**, along with relevant characteristics (including genre, mood, tempo, instrumentation, lyrical themes, and any other musical features), to infer these characteristics based on your expertise.\n\n3. **Playlist Assignment**: For each playlist, identify the most relevant tracks and assign them to the appropriate playlists based on their characteristics. A single track may belong to multiple playlists, so ensure you **exhaustively include it in all relevant categories**.\n\n#### Playlist Information:\n<playlists_informations>\n   {{ JSON.stringify($('Playlists informations').all()) }}\n</playlists_informations>\n\n### Examples\n\nFind below the track input and a sample response for reference.\n\n\n<tracks_to_analyze>\n[ {\"track\":\"William Tell (Guillaume Tell) Overture: Finale [Arr. for Euphonium by Jorijn Van Hese]\",\"artist\":\"Jorijn Van Hese\",\"album\":\"William Tell (Guillaume Tell) Overture: Finale [Arr. for Euphonium by Jorijn Van Hese]\",\"track_spotify_uri\":\"spotify:track:1I5L8EAVFpTnSAYptTJVrU\",\"track_popularity\":\"28\",\"album_release_date\":\"2018\",\"danceability\":0.561,\"energy\":0.236,\"key\":0,\"loudness\":-27.926,\"mode\":1,\"speechiness\":0.0491,\"acousticness\":0.995,\"instrumentalness\":0.934,\"liveness\":0.121,\"valence\":0.964,\"tempo\":102.216,\"type\":\"audio_features\",\"duration_ms\":120080,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"Geffen\",\"artist\":\"Barnt\",\"album\":\"Azari & III Presents - Body Language, Vol. 13\",\"track_spotify_uri\":\"spotify:track:7wVKbT4vwRaEEJ7fnu6Ota\",\"track_popularity\":\"13\",\"album_release_date\":\"2013\",\"danceability\":0.83,\"energy\":0.355,\"key\":1,\"loudness\":-12.172,\"mode\":1,\"speechiness\":0.0911,\"acousticness\":0.00151,\"instrumentalness\":0.934,\"liveness\":0.111,\"valence\":0.129,\"tempo\":118.947,\"type\":\"audio_features\",\"duration_ms\":486910,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"I Wan'na Be Like You (The Monkey Song)\",\"artist\":\"Louis Prima\",\"album\":\"The Jungle Book\",\"track_spotify_uri\":\"spotify:track:2EeVPGHq2I7fjeDfT6LEYX\",\"track_popularity\":\"58\",\"album_release_date\":\"1997\",\"danceability\":0.746,\"energy\":0.404,\"key\":7,\"loudness\":-15.09,\"mode\":0,\"speechiness\":0.0995,\"acousticness\":0.662,\"instrumentalness\":0.000238,\"liveness\":0.281,\"valence\":0.795,\"tempo\":96.317,\"type\":\"audio_features\",\"duration_ms\":279453,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"Linda Nena\",\"artist\":\"Juaneco Y Su Combo\",\"album\":\"The Roots of Chicha\",\"track_spotify_uri\":\"spotify:track:6QsovprLkdGeE9FSsOjuQA\",\"track_popularity\":\"0\",\"album_release_date\":\"2007\",\"danceability\":0.707,\"energy\":0.749,\"key\":4,\"loudness\":-6.36,\"mode\":0,\"speechiness\":0.0336,\"acousticness\":0.696,\"instrumentalness\":0.0000203,\"liveness\":0.104,\"valence\":0.97,\"tempo\":107.552,\"type\":\"audio_features\",\"duration_ms\":225013,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"Sonido Amazonico\",\"artist\":\"Los Mirlos\",\"album\":\"The Roots of Chicha\",\"track_spotify_uri\":\"spotify:track:3hH0sVIoIoPOTmMdjmXSob\",\"track_popularity\":\"0\",\"album_release_date\":\"2007\",\"danceability\":0.883,\"energy\":0.64,\"key\":3,\"loudness\":-6.637,\"mode\":1,\"speechiness\":0.0788,\"acousticness\":0.559,\"instrumentalness\":0.000408,\"liveness\":0.176,\"valence\":0.886,\"tempo\":100.832,\"type\":\"audio_features\",\"duration_ms\":155000,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"Para Elisa\",\"artist\":\"Los Destellos\",\"album\":\"The Roots of Chicha\",\"track_spotify_uri\":\"spotify:track:4Sd525AYAaYuiexGHTcoFy\",\"track_popularity\":\"0\",\"album_release_date\":\"2007\",\"danceability\":0.69,\"energy\":0.8,\"key\":11,\"loudness\":-11.125,\"mode\":1,\"speechiness\":0.0602,\"acousticness\":0.205,\"instrumentalness\":0.886,\"liveness\":0.0531,\"valence\":0.801,\"tempo\":113.401,\"type\":\"audio_features\",\"duration_ms\":166507,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"Stand By Me\",\"artist\":\"Ben E. King\",\"album\":\"Don't Play That Song (Mono)\",\"track_spotify_uri\":\"spotify:track:3SdTKo2uVsxFblQjpScoHy\",\"track_popularity\":\"75\",\"album_release_date\":\"1962\",\"danceability\":0.65,\"energy\":0.306,\"key\":9,\"loudness\":-9.443,\"mode\":1,\"speechiness\":0.0393,\"acousticness\":0.57,\"instrumentalness\":0.00000707,\"liveness\":0.0707,\"valence\":0.605,\"tempo\":118.068,\"type\":\"audio_features\",\"duration_ms\":180056,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"One Night in Bangkok\",\"artist\":\"Murray Head\",\"album\":\"Emotions (My Favourite Songs)\",\"track_spotify_uri\":\"spotify:track:6erBowZaW6Ur3vNOWhS2zM\",\"track_popularity\":\"58\",\"album_release_date\":\"1980\",\"danceability\":0.892,\"energy\":0.578,\"key\":10,\"loudness\":-5.025,\"mode\":1,\"speechiness\":0.15,\"acousticness\":0.112,\"instrumentalness\":0.000315,\"liveness\":0.0897,\"valence\":0.621,\"tempo\":108.703,\"type\":\"audio_features\",\"duration_ms\":236067,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"The Big Tree\",\"artist\":\"Stand High Patrol\",\"album\":\"Midnight Walkers\",\"track_spotify_uri\":\"spotify:track:4ZpqCGtkgPn1Pxsgtmtc8O\",\"track_popularity\":\"50\",\"album_release_date\":\"2012\",\"danceability\":0.697,\"energy\":0.392,\"key\":2,\"loudness\":-9.713,\"mode\":1,\"speechiness\":0.0417,\"acousticness\":0.259,\"instrumentalness\":0.0000388,\"liveness\":0.0956,\"valence\":0.196,\"tempo\":167.002,\"type\":\"audio_features\",\"duration_ms\":241120,\"time_signature\":4,\"date_added\":\"2024-10-27\"}, {\"track\":\"Hotel California - 2013 Remaster\",\"artist\":\"Eagles\",\"album\":\"Hotel California (2013 Remaster)\",\"track_spotify_uri\":\"spotify:track:40riOy7x9W7GXjyGp4pjAv\",\"track_popularity\":\"82\",\"album_release_date\":\"1976\",\"danceability\":0.579,\"energy\":0.508,\"key\":2,\"loudness\":-9.484,\"mode\":1,\"speechiness\":0.027,\"acousticness\":0.00574,\"instrumentalness\":0.000494,\"liveness\":0.0575,\"valence\":0.609,\"tempo\":147.125,\"type\":\"audio_features\",\"duration_ms\":391376,\"time_signature\":4,\"date_added\":\"2024-10-27\"} ]\n</tracks_to_analyze>\n\nOutput : \n[\n    {\n        \"playlistName\": \"Classique\",\n        \"uri\": \"spotify:playlist:1AASnV7pZApr6JWCAWg94R\",\n        \"tracks\": [\n            {\n                \"trackName\": \"William Tell (Guillaume Tell) Overture: Finale [Arr. for Euphonium by Jorijn Van Hese]\",\n                \"trackUri\": \"spotify:track:1I5L8EAVFpTnSAYptTJVrU\"\n            }\n        ]\n    },\n    {\n        \"playlistName\": \"Pro Sound\",\n        \"uri\": \"spotify:playlist:7G27Ccw1vZdWt7uYrUMLwk\",\n        \"tracks\": [\n            {\n                \"trackName\": \"Geffen\",\n                \"trackUri\": \"spotify:track:7wVKbT4vwRaEEJ7fnu6Ota\"\n            }\n        ]\n    },\n    {\n        \"playlistName\": \"To Sing\",\n        \"uri\": \"spotify:playlist:7ts0Ccxw5UijIO8zQ8YJqh\",\n        \"tracks\": [\n            {\n                \"trackName\": \"I Wan'na Be Like You (The Monkey Song)\",\n                \"trackUri\": \"spotify:track:2EeVPGHq2I7fjeDfT6LEYX\"\n            },\n            {\n                \"trackName\": \"Stand By Me\",\n                \"trackUri\": \"spotify:track:3SdTKo2uVsxFblQjpScoHy\"\n            },\n            {\n                \"trackName\": \"One Night in Bangkok\",\n                \"trackUri\": \"spotify:track:6erBowZaW6Ur3vNOWhS2zM\"\n            },\n            {\n                \"trackName\": \"Hotel California - 2013 Remaster\",\n                \"trackUri\": \"spotify:track:40riOy7x9W7GXjyGp4pjAv\"\n            }\n        ]\n    },\n    {\n        \"playlistName\": \"1980s\",\n        \"uri\": \"spotify:playlist:6DqSzwNT9v7eKE3hbPAQtM\",\n        \"tracks\": [\n            {\n                \"trackName\": \"One Night in Bangkok\",\n                \"trackUri\": \"spotify:track:6erBowZaW6Ur3vNOWhS2zM\"\n            }\n        ]\n    },\n    {\n        \"playlistName\": \"Groove Up\",\n        \"uri\": \"spotify:playlist:4rBZMQPf0u6D5FDB82LjHb\",\n        \"tracks\": [\n            {\n                \"trackName\": \"I Wan'na Be Like You (The Monkey Song)\",\n                \"trackUri\": \"spotify:track:2EeVPGHq2I7fjeDfT6LEYX\"\n            },\n            {\n                \"trackName\": \"Stand By Me\",\n                \"trackUri\": \"spotify:track:3SdTKo2uVsxFblQjpScoHy\"\n            }\n        ]\n    },\n    {\n        \"playlistName\": \"Reggae & Dub\",\n        \"uri\": \"spotify:playlist:60khtG2acFWcFQUIGWrPW6\",\n        \"tracks\": [\n            {\n                \"trackName\": \"The Big Tree\",\n                \"trackUri\": \"spotify:track:4ZpqCGtkgPn1Pxsgtmtc8O\"\n            }\n        ]\n    },\n    {\n        \"playlistName\": \"Cumbia\",\n        \"uri\": \"spotify:playlist:1SwaCdO1tS2BbF8IL3WwXO\",\n        \"tracks\": [\n            {\n                \"trackName\": \"Linda Nena\",\n                \"trackUri\": \"spotify:track:6QsovprLkdGeE9FSsOjuQA\"\n            },\n            {\n                \"trackName\": \"Sonido Amazonico\",\n                \"trackUri\": \"spotify:track:3hH0sVIoIoPOTmMdjmXSob\"\n            },\n            {\n                \"trackName\": \"Para Elisa\",\n                \"trackUri\": \"spotify:track:4Sd525AYAaYuiexGHTcoFy\"\n            }\n        ]\n    },\n    {\n        \"playlistName\": \"Funky Groove\",\n        \"uri\": \"spotify:playlist:7jbAj4iensK9FEWsPUez67\",\n        \"tracks\": [\n            {\n                \"trackName\": \"I Wan'na Be Like You (The Monkey Song)\",\n                \"trackUri\": \"spotify:track:2EeVPGHq2I7fjeDfT6LEYX\"\n            },\n            {\n                \"trackName\": \"Stand By Me\",\n                \"trackUri\": \"spotify:track:3SdTKo2uVsxFblQjpScoHy\"\n            }\n        ]\n    }\n]\n\n### Output Requirements:\n\n1. **Exhaustiveness**: Ensure that at least **80% of the tracks** are categorized into playlists. Be thorough in your analysis to leave no relevant tracks unclassified.\n\n2. **Step-by-Step Approach**:\n   - **Think step by step** when classifying tracks, starting with a detailed analysis of their characteristics.\n   - **Review each playlist one by one**, assigning tracks based on their attributes to ensure a comprehensive and accurate classification.\n\n3. **Avoid Duplicates**: Do not include the same track more than once in the output unless it belongs to multiple playlists. Each track should appear only once in each playlist's list of tracks.\n\n4. **Only Use Provided Tracks & Playlists**: Classify tracks exclusively from the given list and assign them to the specified playlists. Do not include any tracks or playlists that are not part of the provided data.\n\n### Output Format:\n\nReturn the classification results in the following JSON structure, ensuring that the output is clear and well-organized.\n\n"}]}, "promptType": "define", "hasOutputParser": true}, "typeVersion": 1.4}], "pinData": {}, "connections": {"Limit": {"main": [[{"node": "Get logged tracks", "type": "main", "index": 0}]]}, "Merge": {"main": [[{"node": "Simplify Tracks informations", "type": "main", "index": 0}]]}, "Filter": {"main": [[{"node": "Batch preparation1", "type": "main", "index": 0}]]}, "Limit2": {"main": [[{"node": "Get logged playlists", "type": "main", "index": 0}]]}, "Split Out": {"main": [[{"node": "Merge", "type": "main", "index": 0}]]}, "Get Tracks": {"main": [[{"node": "Retrieve relevant info", "type": "main", "index": 0}]]}, "Split Out1": {"main": [[{"node": "Split Out2", "type": "main", "index": 0}, {"node": "Filter", "type": "main", "index": 0}]]}, "Split Out2": {"main": [[{"node": "Manual Verification", "type": "main", "index": 1}]]}, "Get Playlist": {"main": [[{"node": "Filter my playlist", "type": "main", "index": 0}]]}, "Monthly Trigger": {"main": [[{"node": "Get Playlist", "type": "main", "index": 0}, {"node": "Get Tracks", "type": "main", "index": 0}]]}, "Batch preparation": {"main": [[{"node": "Get Track details", "type": "main", "index": 0}]]}, "Get Track details": {"main": [[{"node": "Split Out", "type": "main", "index": 0}]]}, "Get logged tracks": {"main": [[{"node": "Excluding logged tracks", "type": "main", "index": 0}]]}, "Batch preparation1": {"main": [[{"node": "Spotify", "type": "main", "index": 0}]]}, "Filter my playlist": {"main": [[{"node": "Playlists informations", "type": "main", "index": 0}]]}, "Classify new tracks": {"main": [[{"node": "Aggregate by 200 tracks", "type": "main", "index": 0}, {"node": "Manual Verification", "type": "main", "index": 0}]]}, "Anthropic Chat Model": {"ai_languageModel": [[{"node": "Basic LLM Chain - AI Classification", "type": "ai_languageModel", "index": 0}]]}, "Get logged playlists": {"main": [[{"node": "Excluding logged playlists", "type": "main", "index": 0}]]}, "Playlists informations": {"main": [[{"node": "Excluding logged playlists", "type": "main", "index": 1}, {"node": "Limit2", "type": "main", "index": 0}]]}, "Retrieve relevant info": {"main": [[{"node": "Batch preparation", "type": "main", "index": 0}, {"node": "Merge", "type": "main", "index": 1}]]}, "Aggregate by 200 tracks": {"main": [[{"node": "Basic LLM Chain - AI Classification", "type": "main", "index": 0}]]}, "Excluding logged tracks": {"main": [[{"node": "Log new tracks", "type": "main", "index": 0}, {"node": "Classify new tracks", "type": "main", "index": 0}]]}, "Structured Output Parser": {"ai_outputParser": [[{"node": "Basic LLM Chain - AI Classification", "type": "ai_outputParser", "index": 0}]]}, "Excluding logged playlists": {"main": [[{"node": "Log new playlists", "type": "main", "index": 0}]]}, "Simplify Tracks informations": {"main": [[{"node": "Limit", "type": "main", "index": 0}, {"node": "Excluding logged tracks", "type": "main", "index": 1}]]}, "Basic LLM Chain - AI Classification": {"main": [[{"node": "Split Out1", "type": "main", "index": 0}]]}}}