AutomationFlows β€Ί General β€Ί Automated Spotify Playlist Organizer - Sort and Queue Tracks by Popularity

Automated Spotify Playlist Organizer - Sort and Queue Tracks by Popularity

ByArthur Dimeglio @wakizara on n8n.io

🎧 Automated Spotify Playlist Organizer β€” Sort and Queue Tracks by Popularity

Event triggerβ˜…β˜…β˜…β˜…β˜† complexity12 nodesSpotify
General Trigger: Event Nodes: 12 Complexity: β˜…β˜…β˜…β˜…β˜† Added:

This workflow corresponds to n8n.io template #9888 β€” we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide β†’

Download .json
{
  "id": "Q7bb75lt3cXobMuU",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Spotify DJ AI",
  "tags": [],
  "nodes": [
    {
      "id": "cc585aa3-5d73-4032-adf7-e7a40e050963",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -336,
        -16
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "0f1db10b-3c41-449c-aa59-bf19929f3ac6",
      "name": "Get a user's playlists",
      "type": "n8n-nodes-base.spotify",
      "position": [
        -64,
        -16
      ],
      "parameters": {
        "resource": "playlist",
        "operation": "getUserPlaylists",
        "returnAll": true
      },
      "typeVersion": 1
    },
    {
      "id": "d7083e5b-9233-4177-afea-a64f801a7698",
      "name": "Add a song to a queue",
      "type": "n8n-nodes-base.spotify",
      "position": [
        1296,
        0
      ],
      "parameters": {
        "id": "=spotify:track:{{ $json.id }}"
      },
      "typeVersion": 1
    },
    {
      "id": "9b6865cb-44ef-4c3e-a03d-0f81eafd6241",
      "name": "Get a playlist's tracks by URI or ID",
      "type": "n8n-nodes-base.spotify",
      "position": [
        496,
        -16
      ],
      "parameters": {
        "id": "{{ $json.uri }}",
        "resource": "playlist",
        "operation": "get"
      },
      "typeVersion": 1
    },
    {
      "id": "eb38ca65-09e1-4440-a426-57e11268ed28",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1008,
        -16
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "11b1ab8e-f4ed-4a61-92b3-74286425f878",
      "name": "Clean & Deduplicate",
      "type": "n8n-nodes-base.code",
      "position": [
        224,
        -16
      ],
      "parameters": {
        "jsCode": "// 1) items[0].json might be an array of playlists\n// 2) or each item in items[] might already represent a playlist (items[].json)\nconst source = Array.isArray(items[0]?.json)\n  ? items[0].json                     // Case 1: The first item's JSON is already an array \u2192 use it directly\n  : items.map(i => i.json);           // Case 2: Each item is a separate playlist \u2192 collect them all\n\n// Keep only the relevant fields: name, uri, and total track count\nconst minimal = source\n  .map(p => ({\n    name: p?.name,                    // Playlist name\n    uri: p?.uri,                      // Spotify URI (unique identifier)\n    total: p?.tracks?.total ?? null,  // Total number of tracks (null if not available)\n  }))\n  .filter(p => p.name && p.uri);      // Exclude incomplete entries\n\n// Deduplicate by URI (optional but recommended)\nconst seen = new Set();\nconst dedup = minimal.filter(p => (\n  seen.has(p.uri)                     // If we've already seen this URI \u2192 skip it\n    ? false\n    : (seen.add(p.uri), true)         // Otherwise, mark as seen and keep it\n));\n\n// Return the result formatted for n8n\nreturn dedup.map(j => ({ json: j })); // n8n expects an array of { json: ... } objects"
      },
      "typeVersion": 2
    },
    {
      "id": "71d6e171-5d97-45b6-b462-39f6502ae39c",
      "name": "Playlist reorganizer",
      "type": "n8n-nodes-base.code",
      "position": [
        752,
        -16
      ],
      "parameters": {
        "jsCode": "// ID cible pris dynamiquement depuis le 1er item\nconst targetId = items[0]?.json?.id;\n\nconst processedPlaylists = new Set(); // \u00e9vite de retraiter la m\u00eame playlist\nconst seenTrackIds = new Set();       // \u00e9vite les doublons de tracks\nconst out = [];\n\nfor (const item of items) {\n  const pl = item.json;\n  if (!pl || pl.id !== targetId) continue;\n\n  // Si on a d\u00e9j\u00e0 trait\u00e9 cette playlist, on skip\n  if (processedPlaylists.has(pl.id)) continue;\n  processedPlaylists.add(pl.id);\n\n  const tracks = pl?.tracks?.items ?? [];\n  for (const t of tracks) {\n    const tr = t?.track;\n    const tid = tr?.id;\n    if (!tid) continue;                   // skip si pas d'id (local/null)\n    if (seenTrackIds.has(tid)) continue;  // d\u00e9j\u00e0 vu \u2192 skip\n    seenTrackIds.add(tid);\n\n    out.push({\n      playlistId: pl.id,\n      playlistName: pl.name,\n      id: tid,\n      name: tr?.name,\n      popularity: tr?.popularity ?? -1,\n      artists: (tr?.artists ?? []).map(a => a.name).join(\", \"),\n      album: tr?.album?.name,\n      added_at: t.added_at,\n      url: tr?.external_urls?.spotify,\n    });\n  }\n}\n\n// Tri ASC popularit\u00e9 (valeurs manquantes en fin)\nout.sort((a, b) => {\n  const pa = a.popularity ?? -1;\n  const pb = b.popularity ?? -1;\n  if (pa === -1 && pb !== -1) return 1;\n  if (pb === -1 && pa !== -1) return -1;\n  return pa - pb;\n});\n\n// Format n8n\nreturn out.map(j => ({ json: j }));"
      },
      "typeVersion": 2
    },
    {
      "id": "13679add-3de9-4411-932f-f34818ce9f6f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -128,
        -128
      ],
      "parameters": {
        "height": 368,
        "content": "## This node retrieve all your playlist"
      },
      "typeVersion": 1
    },
    {
      "id": "db178070-9da6-443a-b4ad-553ea73a724b",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        -208
      ],
      "parameters": {
        "width": 288,
        "height": 448,
        "content": "## This node clean and deduplicate the previous output. \n\n**It displays a list of all your playlists with their ids**"
      },
      "typeVersion": 1
    },
    {
      "id": "68cbf32b-8650-493c-8fb3-99cd1395d50a",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        432,
        -128
      ],
      "parameters": {
        "width": 224,
        "height": 368,
        "content": "## This node extract all the song\n"
      },
      "typeVersion": 1
    },
    {
      "id": "3f9a5db7-9e26-498a-9ca1-b3285f60eb41",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        688,
        -192
      ],
      "parameters": {
        "width": 224,
        "height": 432,
        "content": "## This node reorganize your playlist sorting by popularity"
      },
      "typeVersion": 1
    },
    {
      "id": "4b587aaa-af95-4bb2-9cf1-b48d50ff40ca",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        928,
        -192
      ],
      "parameters": {
        "width": 544,
        "height": 432,
        "content": "## This loop add songs in queue so you can enjoy it for your evening :)"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "4c44b96e-1190-4e31-86aa-47231c0e6d86",
  "connections": {
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Add a song to a queue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean & Deduplicate": {
      "main": [
        [
          {
            "node": "Get a playlist's tracks by URI or ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Playlist reorganizer": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add a song to a queue": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a user's playlists": {
      "main": [
        [
          {
            "node": "Clean & Deduplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a playlist's tracks by URI or ID": {
      "main": [
        [
          {
            "node": "Playlist reorganizer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Get a user's playlists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

🎧 Automated Spotify Playlist Organizer β€” Sort and Queue Tracks by Popularity

Source: https://n8n.io/workflows/9888/ β€” original creator credit. Request a take-down β†’

More General workflows β†’ Β· Browse all categories β†’

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

Comparedatasets. Uses compareDatasets, spotify, youTube, splitInBatches. Event-driven trigger; 12 nodes.

Spotify, YouTube
General

Sample Spotify. Uses manualTrigger, spotify. Event-driven trigger; 2 nodes.

Spotify
General

Blotato. Uses googleSheets, @blotato/n8n-nodes-blotato. Event-driven trigger; 65 nodes.

Google Sheets, @Blotato/N8N Nodes Blotato
General

This template is a hands-on, practical exam designed to help you master n8n Expressionsβ€”the key to accessing and manipulating data in your workflows.

Stop And Error
General

This template is a hands-on, practical exam designed to test your understanding of the fundamental JSON data types. It's the perfect way to solidify your knowledge after learning the basics.

Stop And Error