+52
-13
scripts/albums.ts
+52
-13
scripts/albums.ts
···
22
22
const PLAYLISTS_DIR = 'playlists';
23
23
const OUTPUT_FILE = 'albums.json';
24
24
const USER_AGENT = 'SpotifyAlbums/1.0 (https://github.com/yourusername/spotify-albums)';
25
+
const REQUEST_DELAY_MS = 1100;
25
26
26
27
async function readPlaylists(): Promise<Playlist[]> {
27
28
const files = await readdir(PLAYLISTS_DIR);
···
61
62
const artist = encodeURIComponent(album.artist);
62
63
const release = encodeURIComponent(album.name);
63
64
const url = `https://musicbrainz.org/ws/2/release/?query=artist:${artist}+release:${release}&limit=1&fmt=json`;
64
-
console.log(` MusicBrainz URL: ${url}`);
65
65
66
66
const response = await fetch(url, {
67
67
headers: {
68
68
'User-Agent': USER_AGENT
69
69
}
70
70
});
71
-
console.log(` MusicBrainz status: ${response.status}`);
72
71
73
72
if (!response.ok) {
74
-
console.log(` MusicBrainz failed: ${response.statusText}`);
73
+
console.log(` ✗ ${response.statusText}`);
75
74
return undefined;
76
75
}
77
76
78
77
const data = await response.json();
79
-
console.log(` MusicBrainz results count: ${data.releases?.length || 0}`);
80
78
81
79
if (data.releases && data.releases.length > 0) {
82
80
const result = data.releases[0];
83
-
console.log(` Found: ${result.title} (ID: ${result.id})`);
81
+
console.log(` ✓ ${result.id}`);
84
82
return result.id;
83
+
} else {
84
+
console.log(` ✗ no results`);
85
85
}
86
86
} catch (error) {
87
-
console.error(` MusicBrainz search error:`, (error as Error).message);
87
+
console.log(` ✗ ${(error as Error).message}`);
88
88
}
89
89
90
90
return undefined;
···
92
92
93
93
async function enrichAlbums(albums: Album[]): Promise<Album[]> {
94
94
const enriched: Album[] = [];
95
+
const startTime = Date.now();
95
96
96
-
for (const album of albums) {
97
-
console.log(`Looking up: ${album.artist} - ${album.name}`);
97
+
for (let i = 0; i < albums.length; i++) {
98
+
const album = albums[i];
99
+
const progress = `[${i + 1}/${albums.length}]`;
98
100
101
+
console.log(`${progress} ${album.artist} - ${album.name}`);
99
102
const musicbrainzId = await searchMusicBrainz(album);
100
103
101
104
enriched.push({
···
103
106
musicbrainz_id: musicbrainzId
104
107
});
105
108
106
-
await new Promise(resolve => setTimeout(resolve, 1000));
109
+
if (i < albums.length - 1) {
110
+
await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS));
111
+
}
112
+
113
+
if ((i + 1) % 50 === 0) {
114
+
const elapsed = Date.now() - startTime;
115
+
const avgTimePerAlbum = elapsed / (i + 1);
116
+
const remaining = (albums.length - i - 1) * avgTimePerAlbum;
117
+
const eta = Math.round(remaining / 1000 / 60);
118
+
console.log(`\nProgress: ${((i + 1) / albums.length * 100).toFixed(1)}% | ETA: ~${eta} minutes\n`);
119
+
}
107
120
}
108
121
109
122
return enriched;
110
123
}
111
124
125
+
async function loadExistingAlbums(): Promise<Set<string>> {
126
+
try {
127
+
const content = await readFile(OUTPUT_FILE, 'utf-8');
128
+
const existing = JSON.parse(content) as Album[];
129
+
const keys = new Set(existing.map(a => `${a.artist}:${a.name}`));
130
+
console.log(`Found ${keys.size} previously processed albums`);
131
+
return keys;
132
+
} catch {
133
+
return new Set();
134
+
}
135
+
}
136
+
112
137
async function main() {
113
138
try {
114
139
console.log('Reading playlists...');
···
122
147
123
148
console.log(`Found ${albums.length} unique albums`);
124
149
125
-
console.log('\nEnriching album data...');
126
-
const enrichedAlbums = await enrichAlbums(albums);
150
+
const existingKeys = await loadExistingAlbums();
151
+
const albumsToProcess = albums.filter(a => !existingKeys.has(`${a.artist}:${a.name}`));
127
152
128
-
const output = JSON.stringify(enrichedAlbums, null, 2);
153
+
if (existingKeys.size > 0) {
154
+
console.log(`Skipping ${existingKeys.size} already processed albums`);
155
+
console.log(`Processing ${albumsToProcess.length} remaining albums\n`);
156
+
} else {
157
+
console.log('Processing all albums\n');
158
+
}
159
+
160
+
const newAlbums = await enrichAlbums(albumsToProcess);
161
+
162
+
const existingAlbums = existingKeys.size > 0
163
+
? JSON.parse(await readFile(OUTPUT_FILE, 'utf-8')) as Album[]
164
+
: [];
165
+
166
+
const allAlbums = [...existingAlbums, ...newAlbums];
167
+
const output = JSON.stringify(allAlbums, null, 2);
129
168
await writeFile(OUTPUT_FILE, output, 'utf-8');
130
169
131
-
console.log(`\nSaved ${enrichedAlbums.length} albums to ${OUTPUT_FILE}`);
170
+
console.log(`\nSaved ${allAlbums.length} total albums to ${OUTPUT_FILE}`);
132
171
} catch (error) {
133
172
console.error('Error:', (error as Error).message);
134
173
}