itunesfm
Version:
Sync last.fm play counts with your iTunes library.
127 lines (126 loc) • 4.93 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const prompt_1 = __importDefault(require("prompt"));
const node_util_1 = require("node:util");
const lastfm_1 = require("./lastfm");
prompt_1.default.colors = false;
// Store ambiguous songs in this DB.
const MATCHING_FILE = path_1.default.resolve(__dirname, "../matching.json");
async function quickPrompt(message) {
while (true) {
const { result } = await prompt_1.default.get({
properties: { result: { message } },
});
if (result) {
return result;
}
}
}
async function promptForMatch(name, artist, matches) {
console.log("Multiple matches for %s by %s. Enter valid numbers (comma separated):", name || "??", artist || "??");
for (let i = 0; i < matches.length; i++) {
console.log("%d: %s by %s (%d plays)", i + 1, matches[i].name, matches[i].artist.name, matches[i].playcount);
}
const reply = await quickPrompt("Enter some numbers (0 for none, a for all)");
if (reply === "a" || reply === "A") {
return matches;
}
let result = [];
for (const num of reply.split(",")) {
const match = matches[parseInt(num, 10) - 1];
if (match != null) {
result.push(match);
}
}
return result;
}
async function main() {
try {
const { values, positionals } = (0, node_util_1.parseArgs)({
options: {
cache: { type: "boolean" },
limit: { type: "string" },
out: { type: "string" },
},
allowPositionals: true,
});
let provider;
if (os_1.default.platform() === "win32") {
provider = require("./providers/WindowsProvider.js");
}
else if (os_1.default.platform() === "darwin") {
provider = require("./providers/OSXProvider");
}
else {
throw new Error(`platform ${os_1.default.platform()} not supported`);
}
const tracksPromise = provider.getTracks();
let username = positionals[0];
if (username == null) {
username = await quickPrompt("Enter your last.fm username");
}
const useCached = values.cache || false;
const limit = values.limit ? parseInt(values.limit, 10) : undefined;
const outFile = values.out;
const topTracks = await (0, lastfm_1.getTopTracks)(username, useCached, limit);
if (outFile) {
fs_1.default.writeFileSync(outFile, topTracks.map((t) => JSON.stringify(t)).join("\n"));
console.log(`Wrote ${topTracks.length} tracks to ${outFile}`);
return;
}
const myTracks = await tracksPromise;
console.log("Found %d tracks locally, %d on last.fm.", Object.keys(myTracks).length, topTracks.length);
let matching = {};
try {
matching = JSON.parse(fs_1.default.readFileSync(MATCHING_FILE).toString());
}
catch (e) { }
const updates = {};
for (const id in myTracks) {
const { name, artist, playedCount } = myTracks[id];
const urls = matching[id];
let matches = await (0, lastfm_1.matchTrack)(topTracks, name, artist, urls);
if (matches.length === 0) {
console.warn(`warning: could not match ${name} by ${artist} (id = ${id})`);
if (urls != null) {
console.warn("additionally, you provided urls but none matched");
}
continue;
}
if (urls == null && matches.length > 1) {
matches = await promptForMatch(name, artist, matches);
matching[id] = matches.map((x) => x.url);
}
let matchPlayCount = 0;
for (const match of matches) {
matchPlayCount += parseInt(match.playcount, 10);
}
if (playedCount < matchPlayCount) {
console.log(`will update ${name}: ${artist} to ${matchPlayCount}`);
updates[id] = matchPlayCount;
}
}
if (Object.keys(updates).length === 0) {
console.log("No play counts were changed.");
}
else {
const ok = await quickPrompt("Save changes? y/n");
if (ok === "y") {
console.log("Saving changes..");
await provider.updateTracks(updates);
}
}
fs_1.default.writeFileSync(MATCHING_FILE, JSON.stringify(matching));
}
catch (e) {
console.error(e);
}
}
main();