Skip to content

Instantly share code, notes, and snippets.

@stevenvachon
Last active September 17, 2022 14:54
Show Gist options
  • Save stevenvachon/ff9ed11603303ef66fd67a9a2b02a1bf to your computer and use it in GitHub Desktop.
Save stevenvachon/ff9ed11603303ef66fd67a9a2b02a1bf to your computer and use it in GitHub Desktop.
Apple Music/iTunes library audit

music-library-audit

Find file location errors within an Apple Music/iTunes library file.

This provides some insight into the "some of the songs were not imported because they could not be found" error.

Note: Only files referenced with file:// are supported. Any others will be reported as not found.

Installation

Node.js >= 12 is required. Type this at the command line:

npm install gist:ff9ed11603303ef66fd67a9a2b02a1bf -g

Uninstallation

npm uninstall music-library-audit -g

Usage

cd (change directory) to the location of your Library.xml file and run this command:

music-library-audit

If you encounter issues with undefined locations (probably for remote purchases), you can ignore them with:

music-library-audit --ignore-parse-errors
'use strict';
const {constants: {F_OK}, promises: {access, readFile}} = require('fs');
const ItunesLibrary = require('node-itunes-library');
class TrackLocationError extends Error {
constructor(tracks) {
super('Some tracks could not be found at their locations');
this.tracks = tracks;
}
}
class TrackLocationParseError extends Error {
constructor(tracks) {
super('There was an error parsing the location of some tracks');
this.tracks = tracks;
}
}
const checkTrack = ({track, url}) => access(url, F_OK)
.then(() => ({
result: true,
track
}))
.catch(error => ({
result: false,
track
}));
const getTracks = async () => {
const library = new ItunesLibrary();
await library.open('./Library.xml');
const locationless = [];
const locations = [];
(await library.getTracks()).forEach(track => {
try {
const url = new URL(track.location);
locations.push({track, url});
} catch {
locationless.push(track);
}
});
if (locationless.length > 0 && process.argv[2] !== '--ignore-parse-errors') {
throw new TrackLocationError(locationless);
} else {
return locations;
}
};
module.exports = () => getTracks()
.then(async tracks => {
const nonExistents = (await Promise.all(tracks.map(checkTrack)))
.filter(({result}) => !result)
.map(({track}) => track);
if (nonExistents.length === 0) {
console.log('All tracks exist on disk(s)');
} else {
throw new TrackLocationError(nonExistents);
}
})
.catch(error => {
if (error instanceof TrackLocationError || error instanceof TrackLocationParseError) {
console.error(error.message);
error.tracks.forEach(track => console.error(track));
} else {
console.error(error.message);
}
});
#!/usr/bin/env node
require('./index.js')();
{
"private": true,
"name": "music-library-audit",
"version": "0.0.1",
"license": "MIT",
"author": "Steven Vachon <contact@svachon.com> (https://svachon.com)",
"bin": "music-library-audit",
"dependencies": {
"node-itunes-library": "1.1.2"
},
"engines": {
"node": ">= 12"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment