Created
August 6, 2020 22:18
-
-
Save dnicolson/8afbb33344845d72e873fe3a33b84406 to your computer and use it in GitHub Desktop.
Sets the grouping field of an iTunes/Music selection with artist MusicBrainz tags via Node.js with a couple of OSA calls
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const osa = require('osa2'); | |
const fetch = require('node-fetch'); | |
const cliProgress = require('cli-progress'); | |
// Return a list of unique artists based on the selection | |
const fetchArtists = osa(() => | |
(Application('iTunes') || Application('Music')) | |
.selection() | |
.map((x) => x.artist()) | |
.filter((v, i, a) => a.indexOf(v) === i), | |
); | |
// Update the grouping tag for the artist | |
const setGrouping = osa((artist, tags) => { | |
const songs = (Application('iTunes') || Application('Music')).playlists | |
.whose({ name: 'Library' })[0] | |
.tracks.whose({ artist: { _equals: artist } }); | |
for (const song of Array(songs)) { | |
song.grouping.set(tags); | |
} | |
}); | |
// Return the tags for an artist or throw an error | |
const fetchTags = (artist) => | |
new Promise((resolve, reject) => { | |
(async () => { | |
try { | |
const response = await fetch( | |
`http://musicbrainz.org/ws/2/artist/?fmt=json&query=artist:${encodeURIComponent(artist)}`, | |
); | |
if (response.status !== 200) { | |
throw new Error(`HTTP resonse ${response.status}`); | |
} | |
const data = await response.json(); | |
resolve( | |
data.artists[0].tags | |
.filter((t) => t.count > 0) | |
.map((t) => t.name) | |
.join(', '), | |
); | |
} catch (e) { | |
if (e.message.match(/'filter'/)) { | |
reject('Artist not found'); | |
} else if (e.message.match(/'tags'/)) { | |
reject('Tags not found'); | |
} | |
reject(e.message); | |
} | |
})(); | |
}); | |
// Ensure each interation happens no more than once every 1000ms | |
const rateLimitCheck = async () => { | |
if (!this.lastRequestTime) { | |
this.lastRequestTime = 0; | |
} | |
const timeDiff = Date.now() - this.lastRequestTime; | |
if (timeDiff < 1000) { | |
await new Promise((r) => setTimeout(r, 1000 - timeDiff)); | |
} | |
this.lastRequestTime = Date.now(); | |
}; | |
// Main function | |
(async () => { | |
const artists = await fetchArtists(); | |
const missingArtists = []; | |
const bar1 = new cliProgress.SingleBar({ stopOnComplete: true }, cliProgress.Presets.shades_classic); | |
bar1.start(artists.length, 0); | |
for (const artist of artists) { | |
await rateLimitCheck(); | |
try { | |
const tags = await fetchTags(artist); | |
await setGrouping(artist, tags); | |
} catch (error) { | |
missingArtists.push([artist, error.message ?? error]); | |
continue; | |
} finally { | |
bar1.increment(); | |
} | |
} | |
console.table(missingArtists); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment