Skip to content

Commit 1506d8e

Browse files
committed
touch lookup logic, extract extra metadata
1 parent a377213 commit 1506d8e

File tree

2 files changed

+88
-73
lines changed

2 files changed

+88
-73
lines changed

Diff for: cli.js

+34-19
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ const symbols = require('./src/symbols');
3131
const fileMgr = require('./src/file_mgr');
3232
const pFlatten = require('./src/p_flatten');
3333
const FreyrCore = require('./src/freyr');
34-
const MusicBrainz = require('./src/musicbrainz.js');
3534
const AuthServer = require('./src/cli_server');
3635
const AsyncQueue = require('./src/async_queue');
3736
const parseRange = require('./src/parse_range');
37+
const musicBrainz = require('./src/musicbrainz.js');
3838
const StackLogger = require('./src/stack_logger');
3939
const packageJson = require('./package.json');
4040
const streamUtils = require('./src/stream_utils');
@@ -44,7 +44,13 @@ function parseMeta(params) {
4444
return Object.entries(params || {})
4545
.filter(([, value]) => ![undefined, null].includes(value))
4646
.map(([key, value]) =>
47-
Array.isArray(value) ? value.map(tx => (tx ? [`--${key}`, ...(Array.isArray(tx) ? tx : [tx])] : '')) : [`--${key}`, value],
47+
Array.isArray(value)
48+
? value
49+
.filter(val => val !== undefined)
50+
.map((tx, args) =>
51+
(args = Array.isArray(tx) ? tx : [tx]).every(val => val !== undefined) ? [`--${key}`, ...args] : [],
52+
)
53+
: [`--${key}`, value],
4854
)
4955
.flat(Infinity);
5056
}
@@ -78,7 +84,7 @@ function wrapCliInterface(binaryName, binaryPath) {
7884
}
7985

8086
if (typeof file === 'string') {
81-
spawn(binaryPath, [file, ...parseMeta(args)], { env: extendPathOnEnv(path) }).on('close', cb);
87+
spawn(binaryPath, [file, ...parseMeta(args)], {env: extendPathOnEnv(path)}).on('close', cb);
8288
}
8389
};
8490
}
@@ -850,14 +856,18 @@ async function init(queries, options) {
850856
gapless: options.gapless, // pgap
851857
rDNSatom: [
852858
// ----
853-
['Digital Media', 'name=MEDIA', 'domain=com.apple.iTunes'],
854859
[track.isrc, 'name=ISRC', 'domain=com.apple.iTunes'],
855-
[track.musicBrainz.trackId, 'name=MusicBrainzTrackId', 'domain=com.apple.iTunes'],
856-
[track.musicBrainz.artistId, 'name=MusicBrainzArtistId', 'domain=com.apple.iTunes'],
857-
[track.musicBrainz.albumId, 'name=MusicBrainzAlbumId', 'domain=com.apple.iTunes'],
858-
[track.musicBrainz.albumArtistId, 'name=MusicBrainzAlbumArtistId', 'domain=com.apple.iTunes'],
859860
[track.artists[0], 'name=ARTISTS', 'domain=com.apple.iTunes'],
860861
[track.label, 'name=LABEL', 'domain=com.apple.iTunes'],
862+
[track.musicBrainz.trackId, 'name="MusicBrainz Track Id"', 'domain=com.apple.iTunes'],
863+
[track.musicBrainz.artistId, 'name="MusicBrainz Artist Id"', 'domain=com.apple.iTunes'],
864+
[track.musicBrainz.artistId, 'name="MusicBrainz Album Artist Id"', 'domain=com.apple.iTunes'],
865+
[track.musicBrainz.releaseId, 'name="MusicBrainz Album Id"', 'domain=com.apple.iTunes'],
866+
[track.musicBrainz.releaseGroupId, 'name="MusicBrainz Release Group Id"', 'domain=com.apple.iTunes'],
867+
[track.musicBrainz.barcode, 'name=BARCODE', 'domain=com.apple.iTunes'],
868+
[track.musicBrainz.releaseStatus, 'name="MusicBrainz Album Status"', 'domain=com.apple.iTunes'],
869+
[track.musicBrainz.script, 'name=SCRIPT', 'domain=com.apple.iTunes'],
870+
[track.musicBrainz.media, 'name=MEDIA', 'domain=com.apple.iTunes'],
861871
[`${meta.service[symbols.meta].DESC}: ${track.uri}`, 'name=SOURCE', 'domain=com.apple.iTunes'],
862872
[
863873
`${audioSource.service[symbols.meta].DESC}: ${audioSource.source.videoId}`,
@@ -882,12 +892,12 @@ async function init(queries, options) {
882892
encodingTool: `freyr-js cli v${packageJson.version}`, // ©too
883893
encodedBy: 'd3vc0dr', // ©enc
884894
artwork: files.image.file.name, // covr
885-
// sortOrder: [
886-
// ['name', 'NAME'], // sonm
887-
// ['album', 'NAME'], // soal
888-
// ['artist', 'NAME'], // soar
889-
// ['albumartist', 'NAME'], // soaa
890-
// ],
895+
sortOrder: [
896+
// ['name', 'NAME'], // sonm
897+
// ['album', 'NAME'], // soal
898+
['artist', track.musicBrainz.artistSortOrder], // soar
899+
['albumartist', track.musicBrainz.artistSortOrder], // soaa
900+
],
891901
})
892902
.finally(() => files.image.file.removeCallback())
893903
.catch(err => Promise.reject({err, code: 8}));
@@ -989,6 +999,10 @@ async function init(queries, options) {
989999
}
9901000
trackLogger.log('| [\u2022] Track exists. Overwriting...');
9911001
}
1002+
track.musicBrainz = await processPromise(props.extraTrackMeta, trackLogger, {
1003+
onInit: '| \u27a4 Pulling extra metadata...',
1004+
onErr: '[skipped]',
1005+
});
9921006
trackLogger.log('| \u27a4 Collating sources...');
9931007
const audioSource = await props.collectSources((service, sourcesPromise) =>
9941008
processPromise(sourcesPromise, trackLogger, {
@@ -1004,8 +1018,6 @@ async function init(queries, options) {
10041018
});
10051019
if (!audioFeeds || audioFeeds.err) return {meta, err: (audioFeeds || {}).err, code: 2};
10061020

1007-
track.musicBrainz = await MusicBrainz.gatherMusicBrainzMetadata(track, trackLogger);
1008-
10091021
const feedMeta = audioFeeds.formats.sort((meta1, meta2) => (meta1.abr > meta2.abr ? -1 : meta1.abr < meta2.abr ? 1 : 0))[0];
10101022
meta.fingerprint = crypto.createHash('md5').update(`${audioSource.source.videoId} ${feedMeta.format_id}`).digest('hex');
10111023
const files = await downloadQueue
@@ -1047,11 +1059,14 @@ async function init(queries, options) {
10471059
const outFilePath = xpath.join(outFileDir, outFileName);
10481060
const fileExists = fs.existsSync(outFilePath);
10491061
const processTrack = !fileExists || options.force;
1050-
let collectSources;
1051-
if (processTrack) collectSources = buildSourceCollectorFor(track, results => results[0]);
1062+
let collectSources, extraTrackMeta;
1063+
if (processTrack) {
1064+
collectSources = buildSourceCollectorFor(track, results => results[0]);
1065+
if (track.isrc) extraTrackMeta = musicBrainz.lookupISRC(track.isrc, options.storefront);
1066+
}
10521067
const meta = {trackName, outFileDir, outFilePath, track, service};
10531068
return trackQueue
1054-
.push({track, meta, props: {collectSources, fileExists, processTrack, logger}})
1069+
.push({track, meta, props: {collectSources, extraTrackMeta, fileExists, processTrack, logger}})
10551070
.then(trackObject => ({...trackObject, meta}))
10561071
.catch(errObject => ({meta, code: 10, ...errObject}));
10571072
},

Diff for: src/musicbrainz.js

+54-54
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,57 @@
1-
async function gatherMusicBrainzMetadata(track, trackLogger)
2-
{
3-
var musicBrainz = [];
4-
if (track.isrc !== "") {
5-
trackLogger.print('| [\u2022] Obtaining MusicBrainz metadata...');
6-
7-
const got = require('got');
8-
var parser = require('xml2js');
9-
10-
await got(`https://musicbrainz.org/ws/2/isrc/${track.isrc}?inc=artist-credits+releases`).then(response => {
11-
trackLogger.write('[done]\n');
12-
try {
13-
// Should 'explicitArray: false' be used ?
14-
parser.parseString(response.body, { trim: true, mergeAttrs: true }, function (err, result) {
15-
const recording = result.metadata.isrc[0]['recording-list'][0]['recording'][0];
16-
17-
try {
18-
musicBrainz.trackId = recording['id'][0];
19-
} catch { };
20-
trackLogger.log(`| \u27a4 TrackId: ${musicBrainz.trackId}`);
21-
try {
22-
musicBrainz.artistId = recording['artist-credit'][0]['name-credit'][0]['artist'][0]['id'][0];
23-
} catch { };
24-
trackLogger.log(`| \u27a4 ArtistId: ${musicBrainz.artistId}`);
25-
26-
// Searching for a matching album
27-
const releases = recording['release-list'][0]['release'].filter(obj => {
28-
const title = obj.title[0].replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"'); // Removing weird characters that can cause fails
29-
return track.album.localeCompare(title) == 0;
30-
});
31-
32-
try {
33-
musicBrainz.albumId = releases[0]['id'][0];
34-
bim.hello = true;
35-
}
36-
catch { };
37-
trackLogger.log(`| \u27a4 AlbumId: ${musicBrainz.albumId}`);
38-
try {
39-
musicBrainz.albumArtistId = releases[0]['artist-credit'][0]['name-credit'][0]['artist'][0]['id'][0];
40-
} catch { };
41-
trackLogger.log(`| \u27a4 AlbumArtistId: ${musicBrainz.albumArtistId}`);
42-
});
43-
} catch (error) {
44-
trackLogger.log(error);
45-
}
46-
//
47-
}).catch(error => {
48-
trackLogger.write(`[failed, ${error.message}]\n`);
49-
});
50-
}
51-
return musicBrainz;
1+
const got = require('got');
2+
const {parseStringPromise: xml2js} = require('xml2js');
3+
4+
class MusicBrainzError extends Error {
5+
constructor(message, statusCode) {
6+
super(message);
7+
if (statusCode) this.statusCode = statusCode;
8+
}
9+
}
10+
11+
async function query(entity_type, entity, args) {
12+
let response = await got(`https://musicbrainz.org/ws/2/${entity_type}/${entity}?inc=artists+releases+discids`, {
13+
searchParams: {...args, ...('inc' in args ? {inc: args.inc.join('+')} : {}), ...(args.json ? {fmt: 'json'} : {})},
14+
});
15+
let body;
16+
try {
17+
body = response.body.startsWith('<?xml')
18+
? await xml2js(response.body, {trim: true, mergeAttrs: true, explicitRoot: false, explicitArray: false})
19+
: JSON.parse(response.body);
20+
} catch {
21+
throw new MusicBrainzError('Invalid Server Response');
22+
}
23+
if (response.statusCode !== 200) {
24+
throw new MusicBrainzError(body.error || 'An error occurred', response.statusCode);
25+
}
26+
return body;
5227
}
5328

54-
module.exports = {
55-
gatherMusicBrainzMetadata,
29+
async function lookupISRC(isrc, storefront) {
30+
let {
31+
recording: {
32+
id: trackId,
33+
'release-list': {release: releases},
34+
},
35+
} = (await query('isrc', isrc, {inc: ['releases']})).isrc['recording-list'];
36+
releases = Array.isArray(releases) ? releases : [releases];
37+
38+
let {id: releaseId} = releases.find(release => release.country === storefront) || releases[0];
39+
40+
let release = await query('release', releaseId, {inc: ['artists', 'release-groups', 'media'], json: true});
41+
42+
let {artist: artistMeta} = release['artist-credit'][0];
43+
return {
44+
trackId,
45+
releaseId,
46+
artistId: artistMeta.id,
47+
artistSortOrder: artistMeta['sort-name'],
48+
releaseGroupId: release['release-group'].id,
49+
releaseType: release['release-group']['primary-type'],
50+
barcode: release.barcode,
51+
releaseStatus: release.status.toLowerCase(),
52+
script: release['text-representation'].script,
53+
media: release.media[0].format,
5654
};
57-
55+
}
56+
57+
module.exports = {lookupISRC};

0 commit comments

Comments
 (0)