chore: simpler charts

This commit is contained in:
Cory Dransfeldt 2023-07-22 07:59:49 -07:00
parent e8b8bb24b5
commit eaa43c41af
No known key found for this signature in database
7 changed files with 59 additions and 1092 deletions

View file

@ -1,124 +1,8 @@
const { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3')
const _ = require('lodash')
const artistGenres = require('./json/artist-genres.json')
const mockedMusic = require('./json/mocks/music.json')
const { getReadableData } = require('../utils/aws')
const { getKeyByValue } = require('../utils/arrays')
const { aliasArtist, sanitizeMedia } = require('../utils/media')
const { AssetCache } = require('@11ty/eleventy-fetch')
const { aliasArtist, sanitizeMedia, sortByPlays } = require('../utils/media')
const { titleCase } = require('../utils/grammar')
const getTracksOneHour = (tracks) => {
const TIMER_CEILING = 3600000 // 1 hour
const tracksOneHour = []
let trackIndex = 0
let trackTimer = 0
while (trackTimer < TIMER_CEILING) {
if (!tracks[trackIndex]) return tracksOneHour
trackTimer = trackTimer + parseInt(tracks[trackIndex].duration)
tracksOneHour.push(tracks[trackIndex])
trackIndex++
}
return tracksOneHour
}
const diffTracks = (cache, tracks) => {
const trackCompareSet = Object.values(tracks)
const cacheCompareSet = _.orderBy(Object.values(cache), ['time'], ['desc'])
const diffedTracks = {}
const cacheCompareOneHour = getTracksOneHour(cacheCompareSet)
const comparedTracks = _.differenceWith(trackCompareSet, cacheCompareOneHour, (a, b) =>
_.isEqual(a.id, b.id)
)
for (let i = 0; i < comparedTracks.length; i++)
diffedTracks[`${comparedTracks[i]?.id}-${comparedTracks[i].playTime}`] = comparedTracks[i]
return diffedTracks
}
const formatTracks = (tracks) => {
let formattedTracks = {}
let time = new Date().getTime()
Object.values(tracks).forEach((track) => {
const artistFormatted = titleCase(aliasArtist(track.attributes['artistName']))
const albumFormatted = titleCase(sanitizeMedia(track.attributes['albumName']))
const trackFormatted = sanitizeMedia(track.attributes['name'])
formattedTracks[`${track.id}-${time}`] = {
name: trackFormatted,
artist: artistFormatted,
album: albumFormatted,
art: track.attributes.artwork.url.replace('{w}', '300').replace('{h}', '300'),
url:
track['relationships'] && track['relationships'].albums.data.length > 0
? `https://song.link/${track['relationships'].albums.data.pop().attributes.url}`
: `https://rateyourmusic.com/search?searchtype=l&searchterm=${encodeURI(
albumFormatted
)}%20${encodeURI(artistFormatted)}`,
id: track.id,
playTime: time - parseInt(track.attributes['durationInMillis']),
duration: parseInt(track.attributes['durationInMillis']),
}
})
return formattedTracks
}
const deriveCharts = (tracks) => {
const charts = {
artists: {},
albums: {},
}
const tracksForLastWeek = Object.values(tracks).filter((track) => {
const currentDate = new Date()
const currentDateTime = new Date().getTime()
const lastWeek = new Date(currentDate.setDate(currentDate.getDate() - 7))
const lastWeekDateTime = lastWeek.getTime()
const trackDateTime = new Date(track.playTime).getTime()
return trackDateTime <= currentDateTime && trackDateTime > lastWeekDateTime
})
tracksForLastWeek.forEach((track) => {
if (!charts.artists[track.artist]) {
charts.artists[track.artist] = {
artist: track.artist,
genre: getKeyByValue(artistGenres, track.artist.replace(/\s+/g, '-').toLowerCase()),
url: `https://rateyourmusic.com/search?searchterm=${encodeURI(track.artist)}`,
plays: 1,
}
} else {
charts.artists[track.artist].plays++
}
if (!charts.albums[track.album]) {
charts.albums[track.album] = {
name: track.album,
artist: track.artist,
art: track.art,
url: track.url,
plays: 1,
}
} else {
charts.albums[track.album].plays++
}
})
return charts
}
module.exports = async function () {
const client = new S3Client({
credentials: {
accessKeyId: process.env.ACCESS_KEY_WASABI,
secretAccessKey: process.env.SECRET_KEY_WASABI,
},
endpoint: {
url: 'https://s3.us-west-1.wasabisys.com',
},
region: 'us-west-1',
})
const WASABI_BUCKET = process.env.BUCKET_WASABI
const APPLE_BEARER = process.env.API_BEARER_APPLE_MUSIC
const APPLE_MUSIC_TOKEN = process.env.API_TOKEN_APPLE_MUSIC
const APPLE_TOKEN_RESPONSE = await fetch(process.env.APPLE_RENEW_TOKEN_URL, {
@ -133,15 +17,21 @@ module.exports = async function () {
.then((data) => data.json())
.catch()
const APPLE_TOKEN = APPLE_TOKEN_RESPONSE['music-token']
const asset = new AssetCache('recent_tracks_data')
const PAGE_SIZE = 30
const TIMER_CEILING = 3600000 // 1 hour
let charts
const PAGES = 10
const charts = {
artists: {},
albums: {},
tracks: {},
}
let CURRENT_PAGE = 0
let trackTimer = 0
let res = []
let cachedTracks = mockedMusic
let hasNextPage = true
while (trackTimer < TIMER_CEILING) {
if (asset.isCacheValid('1h')) return await asset.getCachedValue()
while (CURRENT_PAGE < PAGES && hasNextPage) {
const URL = `https://api.music.apple.com/v1/me/recent/played/tracks?limit=${PAGE_SIZE}&offset=${
PAGE_SIZE * CURRENT_PAGE
}&include[songs]=albums&extend=artistUrl`
@ -154,45 +44,54 @@ module.exports = async function () {
})
.then((data) => data.json())
.catch()
tracks.data.forEach((track) => {
trackTimer = trackTimer + parseInt(track.attributes['durationInMillis'])
if (trackTimer >= TIMER_CEILING) return
res.push(track)
})
if (!tracks.next) hasNextPage = false
if (tracks.data.length) res = [...res, ...tracks.data]
CURRENT_PAGE++
}
res.forEach((track) => {
const formattedArtist = titleCase(aliasArtist(track.attributes['artistName']))
const formattedAlbum = titleCase(sanitizeMedia(track.attributes['albumName']))
const formattedTrack = sanitizeMedia(track.attributes['name'])
if (process.env.ELEVENTY_PRODUCTION === 'true') {
const cachedTracksOutput = await client.send(
new GetObjectCommand({
Bucket: WASABI_BUCKET,
Key: 'music.json',
})
)
const cachedTracksData = getReadableData(cachedTracksOutput.Body)
cachedTracks = await cachedTracksData.then((tracks) => JSON.parse(tracks)).catch()
}
if (!charts.artists[formattedArtist]) {
charts.artists[formattedArtist] = {
artist: formattedArtist,
url: `https://rateyourmusic.com/search?searchterm=${encodeURI(formattedArtist)}`,
plays: 1,
}
} else {
charts.artists[formattedArtist].plays++
}
const diffedTracks = diffTracks(cachedTracks, formatTracks(res))
const updatedCache = {
...cachedTracks,
...diffedTracks,
}
charts = deriveCharts(updatedCache)
charts.artists = _.orderBy(Object.values(charts.artists), ['plays'], ['desc']).splice(0, 8)
charts.albums = _.orderBy(Object.values(charts.albums), ['plays'], ['desc']).splice(0, 8)
if (!_.isEmpty(diffedTracks) && process.env.ELEVENTY_PRODUCTION === 'true') {
await client.send(
new PutObjectCommand({
Bucket: WASABI_BUCKET,
Key: 'music.json',
Body: JSON.stringify(updatedCache),
})
)
}
if (!charts.albums[formattedAlbum]) {
charts.albums[formattedAlbum] = {
name: formattedAlbum,
artist: formattedArtist,
art: track.attributes.artwork.url.replace('{w}', '300').replace('{h}', '300'),
url: track['relationships']
? `https://song.link/${track['relationships'].albums.data.pop().attributes.url}`
: `https://rateyourmusic.com/search?searchtype=l&searchterm=${encodeURI(
formattedAlbum
)}%20${encodeURI(formattedArtist)}`,
plays: 1,
}
} else {
charts.albums[formattedAlbum].plays++
}
if (!charts.tracks[formattedTrack]) {
charts.tracks[formattedTrack] = {
name: formattedTrack,
artist: formattedArtist,
plays: 1,
}
} else {
charts.tracks[formattedTrack].plays++
}
})
charts.artists = sortByPlays(charts.artists).splice(0, 8)
charts.albums = sortByPlays(charts.albums).splice(0, 8)
charts.tracks = sortByPlays(charts.tracks).splice(0, 5)
await asset.save(charts, 'json')
return charts
}