diff --git a/src/consts.ts b/src/consts.ts index be8a6e6..671ab71 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import { AnalysisData } from "./types"; +import { AnalysisData, TrackAnalysis } from "./types"; export const PORT = process.env.PORT ?? 8080; export const SPOTIFY_CLIENT_ID = process.env.SPOTIFY_CLIENT_ID as string; @@ -12,11 +12,14 @@ export const UI_PATH = path.join(process.cwd(), 'dist', 'ui'); export const cookieName = 'auth_token' export const defaultAnalysis: AnalysisData = { - trackAnalysis: undefined, - albumAnalysis: undefined, - personnelAndPlaces: undefined, - artistBiography: undefined, - culturalContext: undefined, - recommendations: undefined, + track: undefined, + album: undefined, + artist: undefined, +} + +export const defaultTrackAnalysis: TrackAnalysis = { tags: [], } + + + diff --git a/src/spotify.ts b/src/spotify.ts index 37c1092..0391ae4 100644 --- a/src/spotify.ts +++ b/src/spotify.ts @@ -81,6 +81,8 @@ export const getRecentlyPlayed = async (session: Omit) export const convertSpotifyTrackToTrackDetails = (track: SpotifyTrack): TrackDetails => { return { trackId: track.id, + albumId: track.album.id, + artistId: track.artists[0].id, name: track.name, artist: track.artists.map((artist) => artist.name).join(', '), album: track.album.name, diff --git a/src/store.ts b/src/store.ts index 5fd5ee0..6dd19eb 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,21 +1,52 @@ import redisClient from "./redis"; -import { TrackDetails } from "./types"; +import { TrackDetails, AlbumAnalysis, TrackAnalysis, ArtistAnalysis } from "./types"; const TRACK_KEY_PREFIX = "track:"; const HISTORY_KEY_PREFIX = "history:"; +const ANALYSIS_KEY_PREFIX = "analysis:"; const getUserHistoryKey = (userId: string) => `${HISTORY_KEY_PREFIX}${userId}`; const getTrackKey = (trackId: string) => `${TRACK_KEY_PREFIX}${trackId}`; -// +const getArtistAnalysisKey = (artistId: string) => `${ANALYSIS_KEY_PREFIX}${artistId}`; +const getAlbumAnalysisKey = (albumId: string) => `${ANALYSIS_KEY_PREFIX}${albumId}`; +const getTrackAnalysisKey = (trackId: string) => `${ANALYSIS_KEY_PREFIX}${trackId}`; + +export const updateJSON = async (key: string, data: T) => { + await redisClient.set(key, JSON.stringify(data)); +}; + +export const getJSON = async (key: string): Promise => { + const data = await redisClient.get(key); + return data ? JSON.parse(data) : undefined; +}; export const updateTrack = async (trackId: string, trackDetails: TrackDetails) => { - // handle the different fields - await redisClient.set(getTrackKey(trackId), JSON.stringify(trackDetails)); + await updateJSON(getTrackKey(trackId), trackDetails); + + const analysis = trackDetails.analysis; + if (analysis?.track) { + await updateJSON(getTrackAnalysisKey(trackId), analysis.track); + } + if (analysis?.album) { + await updateJSON(getAlbumAnalysisKey(trackDetails.albumId), analysis.album); + } + if (analysis?.artist) { + await updateJSON(getArtistAnalysisKey(trackDetails.artistId), analysis.artist); + } }; -export const getTrack = async (trackId: string): Promise => { - const data = await redisClient.get(getTrackKey(trackId)); - return data ? JSON.parse(data) : null; +export const getTrack = async (trackId: string): Promise => { + const track = await getJSON(getTrackKey(trackId)); + if(!track) return undefined; + const analysis = { + album: await getJSON(getAlbumAnalysisKey(track.albumId)), + track: await getJSON(getTrackAnalysisKey(track.trackId)), + artist: await getJSON(getArtistAnalysisKey(track.artistId)), + } + return { + ...track, + analysis + }; }; export const pushHistory = async (userId: string, trackId: string) => { @@ -57,19 +88,20 @@ export const getRelatedTracks = async (trackId: string, userId: string, count = }; const findTracksWithCommonTags = (track: TrackDetails, tracks: TrackDetails[]) => { - const targetTags = track?.analysis?.tags ?? []; + const targetTags = track?.analysis?.track?.tags ?? []; const commonTracks = tracks.reduce((prev, curr) => { + const currTags = curr?.analysis?.track?.tags ?? []; if (curr.trackId === track.trackId) return prev; if (prev.some(t => t.trackId === curr.trackId)) return prev; - if (curr.analysis?.tags?.length === 0) return prev; - if (curr.analysis?.tags?.some(tag => targetTags.includes(tag))) { + if (currTags.length === 0) return prev; + if (currTags.some(tag => targetTags.includes(tag))) { return [...prev, curr]; } return prev; }, []); const countTagsInCommon = (track: TrackDetails) => { - const tags = track.analysis?.tags ?? []; + const tags = track.analysis?.track?.tags ?? []; return targetTags.filter(tag => tags.includes(tag)).length; }; return commonTracks.sort((a, b) => { diff --git a/src/types.ts b/src/types.ts index a92e91c..ac303e6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,6 +5,7 @@ export interface SpotifyImage { } export interface SpotifyArtist { + id: string; name: string; } @@ -13,6 +14,7 @@ export interface SpotifyTrack { name: string; artists: SpotifyArtist[]; album: { + id: string; name: string; images: SpotifyImage[]; release_date: string; @@ -35,6 +37,8 @@ export interface SpotifyTokenResponse { export interface TrackDetails { trackId: string; + albumId: string; + artistId: string; name: string; artist: string; album: string; @@ -52,11 +56,15 @@ export interface Recommendation { } export interface AnalysisData { - albumAnalysis?: string; - trackAnalysis?: string; + track?: TrackAnalysis, + album?: AlbumAnalysis, + artist?: ArtistAnalysis, + // video / images / audio +} + +export interface TrackAnalysis { personnelAndPlaces?: string; - artistBiography?: string; - culturalContext?: string; + analysis?: string; recommendations?: { albums: Recommendation[]; tracks: Recommendation[]; @@ -66,12 +74,21 @@ export interface AnalysisData { tags?: string[]; } +export interface AlbumAnalysis { + analysis?: string; + culturalContext?: string; +} + +export interface ArtistAnalysis { + biography?: string; +} + export interface SessionData { accessToken: string; refreshToken: string; expiresAt: number; spotifyId: string; -} +} export interface SpotifyUser { id: string;