-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExtractor.js
More file actions
143 lines (107 loc) · 3.26 KB
/
Copy pathExtractor.js
File metadata and controls
143 lines (107 loc) · 3.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
(async () => {
const sleep = ms => new Promise(r => setTimeout(r, ms));
console.clear();
console.log("🚀 Spotify full playlist extractor started...");
const findScroller = () => {
return [...document.querySelectorAll("div")]
.filter(el => {
const s = getComputedStyle(el);
return (
el.scrollHeight > el.clientHeight + 300 &&
(s.overflowY === "auto" || s.overflowY === "scroll")
);
})
.sort((a, b) => b.scrollHeight - a.scrollHeight)[0];
};
const scroller = findScroller();
if (!scroller) {
console.error("❌ Could not find playlist scroller.");
return;
}
console.log("✅ Scroller found.");
const tracks = new Map();
const collectVisibleTracks = () => {
const rows = [
...document.querySelectorAll('[data-testid="tracklist-row"]')
];
for (const row of rows) {
const links = [...row.querySelectorAll("a")];
const trackLink = links.find(a =>
a.href.includes("/track/")
);
if (!trackLink) continue;
const title = trackLink.textContent.trim();
if (!title) continue;
// Use Spotify track URL as the unique ID
const trackId = trackLink.href.split("?")[0];
const artists = links
.filter(a => a.href.includes("/artist/"))
.map(a => a.textContent.trim())
.filter(Boolean);
tracks.set(trackId, {
title,
artists: [...new Set(artists)]
});
}
};
// Start from the top
scroller.scrollTop = 0;
await sleep(1500);
collectVisibleTracks();
let lastPosition = -1;
let unchanged = 0;
for (let i = 0; i < 1000; i++) {
collectVisibleTracks();
const before = tracks.size;
// Scroll by one viewport
scroller.scrollTop += Math.max(
400,
Math.floor(scroller.clientHeight * 0.7)
);
await sleep(600);
collectVisibleTracks();
const after = tracks.size;
const position = scroller.scrollTop;
console.log(
`📜 ${i + 1} | Tracks collected: ${after} | Scroll: ${Math.round(position)} / ${Math.round(scroller.scrollHeight)}`
);
// Detect bottom
const atBottom =
scroller.scrollTop + scroller.clientHeight >=
scroller.scrollHeight - 50;
if (position === lastPosition && after === before) {
unchanged++;
} else {
unchanged = 0;
}
lastPosition = position;
if (atBottom && unchanged >= 5) {
break;
}
// Safety limit
if (i >= 999) {
console.warn("⚠️ Safety limit reached.");
break;
}
}
// Final collection
collectVisibleTracks();
const result = [...tracks.values()]
.map((track, index) =>
`${index + 1}. ${track.title} — ${track.artists.join(", ")}`
)
.join("\n");
console.log("");
console.log("======================================");
console.log(`🎵 TOTAL TRACKS FOUND: ${tracks.size}`);
console.log("======================================");
console.log(result);
// Put the result into a global variable
window.spotifyPlaylistResult = result;
console.log("");
console.log("📦 Result saved as:");
console.log("spotifyPlaylistResult");
console.log("");
console.log("💡 To copy it manually, run:");
console.log("navigator.clipboard.writeText(spotifyPlaylistResult)");
})();