import { getCache, updateCache } from "./utils/cache"; import { constructRecord, createRecord, getLastTimestamp, } from "./utils/atproto"; import { buildTrackCache, getExtraSongData, isEligible, parseCSV, } from "./utils/data"; import consola from "consola"; import { lookupTrack } from "./utils/musicbrainz"; import type { AppleMusicPlaybackEvent } from "./types"; const logger = consola.withTag("AM -> TEAL.FM"); const dataExportPath = "/Users/bryceoliver/Desktop/Apple Music Activity/"; const playActivity = await parseCSV( dataExportPath, "Apple Music Play Activity", ) as AppleMusicPlaybackEvent[]; const dailyTrackHistory = await parseCSV( dataExportPath, "Apple Music - Play History Daily Tracks", ); buildTrackCache(dailyTrackHistory); const lastTimestamp = new Date(await getLastTimestamp()); logger.info("Last Teal.fm record timestamp: ", lastTimestamp.toDateString()); const progress = await getCache(); const eligible = playActivity.filter((event) => isEligible(event) && new Date(event["Event End Timestamp"]) > lastTimestamp ); const notYetImported = eligible.filter((event) => !progress.some((p) => p.songName === event["Song Name"] && p.timestamp === event["Event End Timestamp"] ) ); logger.info(`Importing ${notYetImported.length.toLocaleString()} tracks`); const events = notYetImported .sort( (a, b) => new Date(a["Event End Timestamp"]).getTime() - new Date(b["Event End Timestamp"]).getTime(), ); const missingTrackData = [ ...new Set( events.filter((x) => getExtraSongData(x["Song Name"]) == null).map(( x, ) => x["Song Name"]), ), ]; if (missingTrackData.length > 0) { logger.warn( "Missing track data! Please create an artist_overrides.json file to specify the artist for the following tracks", ); console.log(missingTrackData.join(", ")); process.exit(1); } for (const event of events) { const details = getExtraSongData(event["Song Name"])!; logger.log(`Resolving track ${event["Song Name"]} by ${details.artist}..`); const track = await lookupTrack( details.artist, event["Album Name"], event["Song Name"], )!; if (!track) { logger.error("Failed to resolve track."); process.exit(1); } logger.success( `Resolved track to recording "${track.recording.title}" and release "${track.release.title}"!`, ); const record = await constructRecord( event["Event End Timestamp"], event["Song Name"], track.recording, track.release, details.originId, ); console.log(record); await updateCache(event); await createRecord(record); }