feat: data storage

This commit is contained in:
Cory Dransfeldt 2024-05-07 17:09:47 -07:00
parent bcc6ea0987
commit d8137dca96
No known key found for this signature in database
29 changed files with 428 additions and 14449 deletions

View file

@ -1,90 +1,78 @@
import { getStore } from '@netlify/blobs'
import { createClient } from '@supabase/supabase-js'
import { DateTime } from 'luxon'
const SUPABASE_URL = Netlify.env.get('SUPABASE_URL')
const SUPABASE_KEY = Netlify.env.get('SUPABASE_API_KEY')
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)
const sanitizeMediaString = (string) => string.normalize('NFD').replace(/[\u0300-\u036f\u2010—\.\?\(\)\[\]\{\}]/g, '').replace(/\.{3}/g, '')
const weekKey = () => {
const currentDate = DateTime.now();
return `${currentDate.year}-${currentDate.weekNumber}`
}
const filterOldScrobbles = (scrobbles) => {
const windowEnd = DateTime.now().minus({ days: 7 });
return scrobbles.filter(scrobble => {
const timestamp = DateTime.fromISO(scrobble.timestamp);
return timestamp >= windowEnd;
});
}
export default async (request) => {
const ACCOUNT_ID_PLEX = Netlify.env.get('ACCOUNT_ID_PLEX');
const MUSIC_KEY = Netlify.env.get('API_KEY_LASTFM');
const params = new URL(request['url']).searchParams
const ACCOUNT_ID_PLEX = process.env.ACCOUNT_ID_PLEX
const params = new URL(request.url).searchParams
const id = params.get('id')
if (!id) return new Response(JSON.stringify({
status: 'Bad request',
}),
{ headers: { "Content-Type": "application/json" } }
)
if (id !== ACCOUNT_ID_PLEX) return new Response(JSON.stringify({
status: 'Forbidden',
}),
{ headers: { "Content-Type": "application/json" } }
)
if (!id) return new Response(JSON.stringify({ status: 'Bad request' }), { headers: { "Content-Type": "application/json" } })
if (id !== ACCOUNT_ID_PLEX) return new Response(JSON.stringify({ status: 'Forbidden' }), { headers: { "Content-Type": "application/json" } })
const data = await request.formData()
const payload = JSON.parse(data.get('payload'))
const artists = getStore('artists')
const scrobbles = getStore('scrobbles')
if (payload?.event === 'media.scrobble') {
const artist = payload['Metadata']['grandparentTitle']
const album = payload['Metadata']['parentTitle']
const track = payload['Metadata']['title']
const trackNumber = payload['Metadata']['index']
const timestamp = DateTime.now()
const artistsMap = await artists.get('artists-map', { type: 'json' })
const artistSanitizedKey = `${sanitizeMediaString(artist).replace(/\s+/g, '-').toLowerCase()}`
const trackScrobbleData = {
track,
album,
artist,
trackNumber,
timestamp,
genre: artistsMap[artistSanitizedKey]?.['genre'] || ''
const artist = payload.Metadata.grandparentTitle
const album = payload.Metadata.parentTitle
const track = payload.Metadata.title
const listenedAt = DateTime.now().toISO()
const artistKey = sanitizeMediaString(artist).replace(/\s+/g, '-').toLowerCase()
const albumKey = `${artistKey}-${sanitizeMediaString(album).replace(/\s+/g, '-').toLowerCase()}`
const { data: albumData, error: albumError } = await supabase
.from('albums')
.select('*')
.eq('key', albumKey)
.single()
if (albumError && albumError.code === 'PGRST116') {
const albumImageUrl = `https://coryd.dev/media/albums/${albumKey}.jpg`
const albumMBID = null
const { error: insertAlbumError } = await supabase.from('albums').insert([
{
mbid: albumMBID,
image: albumImageUrl,
key: albumKey,
name: album,
tentative: true
}
])
if (insertAlbumError) {
console.error('Error inserting album into Supabase:', insertAlbumError.message)
return new Response(JSON.stringify({ status: 'error', message: insertAlbumError.message }), { headers: { "Content-Type": "application/json" } })
}
} else if (albumError) {
console.error('Error querying album from Supabase:', albumError.message)
return new Response(JSON.stringify({ status: 'error', message: albumError.message }), { headers: { "Content-Type": "application/json" } })
}
const scrobbleData = await scrobbles.get(`${weekKey()}`, { type: 'json'})
const windowData = await scrobbles.get('window', { type: 'json'})
const artistUrl = (artistsMap[artistSanitizedKey]?.['mbid'] && artistsMap[artistSanitizedKey]?.['mbid'] !== '') ? `http://musicbrainz.org/artist/${artistsMap[artistSanitizedKey]?.['mbid']}` : `https://musicbrainz.org/search?query=${artist.replace(
/\s+/g,
'+'
)}&type=artist`
const { error: listenError } = await supabase.from('listens').insert([
{
artist_name: artist,
album_name: album,
track_name: track,
listened_at: listenedAt,
album_key: albumKey
}
])
await scrobbles.setJSON('now-playing', {...trackScrobbleData, ...{ url: artistUrl }})
let scrobbleUpdate = scrobbleData
let windowUpdate = windowData;
if (scrobbleUpdate?.['data']) scrobbleUpdate['data'].push(trackScrobbleData)
if (!scrobbleUpdate?.['data']) scrobbleUpdate = { data: [trackScrobbleData] }
if (windowData?.['data']) windowUpdate['data'].push(trackScrobbleData)
if (!windowData?.['data']) windowUpdate = { data: [trackScrobbleData] }
windowUpdate = { data: filterOldScrobbles(windowUpdate.data) }
await scrobbles.setJSON(`${weekKey()}`, scrobbleUpdate)
await scrobbles.setJSON('window', windowUpdate)
if (listenError) {
console.error('Error inserting data into Supabase:', listenError.message)
return new Response(JSON.stringify({ status: 'error', message: listenError.message }), { headers: { "Content-Type": "application/json" } })
}
}
return new Response(JSON.stringify({
status: 'success',
}),
{ headers: { "Content-Type": "application/json" } }
)
return new Response(JSON.stringify({ status: 'success' }), { headers: { "Content-Type": "application/json" } })
}
export const config = {
path: '/api/scrobble',
}
}