diff --git a/.env b/.env
index 3541d8d0..6931b148 100644
--- a/.env
+++ b/.env
@@ -1,10 +1,8 @@
+API_KEY_LASTFM=
API_KEY_TRAKT=
API_KEY_MOVIEDB=
API_KEY_WEBMENTIONS_CORYD_DEV=
API_TOKEN_PINBOARD=
-API_APPLE_MUSIC_DEVELOPER_TOKEN=
-API_APPLE_MUSIC_USER_TOKEN=
-APPLE_RENEW_TOKEN_URL=
SITE_ID_CLICKY=
SITE_KEY_CLICKY=
SECRET_FEED_ALBUM_RELEASES=
diff --git a/config/mediaFilters.js b/config/mediaFilters.js
index 40f9f40c..ede64d69 100644
--- a/config/mediaFilters.js
+++ b/config/mediaFilters.js
@@ -8,12 +8,12 @@ module.exports = {
if (item.type === 'album') {
normalized['title'] = item['title']
normalized['alt'] = `${item['title']} by ${item['artist']}`
- normalized['subtext'] = item['artist']
+ normalized['subtext'] = `${item['plays']} plays`
}
if (item.type === 'artist') {
normalized['title'] = item['title']
normalized['alt'] = `${item['title']} at #${item['rank']}`
- normalized['subtext'] = `#${item['rank']}`
+ normalized['subtext'] = `${item['plays']} plays`
}
if (item.type === 'movie') normalized['alt'] = item['title']
if (item.type === 'book') {
diff --git a/netlify/edge-functions/now-playing.js b/netlify/edge-functions/now-playing.js
index f7ce50f5..314333a9 100644
--- a/netlify/edge-functions/now-playing.js
+++ b/netlify/edge-functions/now-playing.js
@@ -1,65 +1,44 @@
-const artistAliases = {
- aliases: [
- {
- artist: 'Aesop Rock',
- aliases: ['Aesop Rock & Homeboy Sandman', 'Aesop Rock & Blockhead'],
- },
- {
- artist: 'Fen',
- aliases: ['Sleepwalker & Fen'],
- },
- {
- artist: 'Free Throw',
- aliases: ['Free Throw, Hot Mulligan & Tades Sanville'],
- },
- {
- artist: 'Hot Mulligan',
- aliases: ['Hot Mulligan & Less Gravity'],
- },
- {
- artist: 'Osees',
- aliases: ['OCS', 'The Ohsees', 'Thee Oh Sees', "Thee Oh See's"],
- },
- {
- artist: 'SnΔΕ₯',
- aliases: ['Snet', 'Sne-T'],
- },
- {
- artist: 'Tom Waits',
- aliases: ['Tom Waits & Crystal Gayle', 'Crystal Gayle'],
- },
- ],
-}
-
-const aliasArtist = (artist) => {
- const aliased = artistAliases.aliases.find((alias) => alias.aliases.includes(artist))
- if (aliased) artist = aliased.artist
- return artist
-}
-
-const sanitizeTrack = (track) => {
- let sanitizedTrack = track
+const emojiMap = (genre, artist) => {
+ const DEFAULT = 'π§'
+ if (!genre) return DEFAULT // early return for bad input
+ if (artist === 'David Bowie') return 'π¨π»βπ€'
+ if (artist === 'Minor Threat') return 'π¨π»βπ¦²'
+ if (artist === 'Bruce Springsteen') return 'πΊπΈ'
+ if (genre.includes('death metal')) return 'π'
+ if (genre.includes('black metal')) return 'πͺ¦'
+ if (genre.includes('metal')) return 'π€'
+ if (genre.includes('emo') || genre.includes('blues')) return 'π’'
+ if (genre.includes('grind') || genre.includes('powerviolence')) return 'π«¨'
if (
- !track.includes('Deluxe') ||
- !track.includes('Special') ||
- !track.includes('Remastered') ||
- !track.includes('Full Dynamic') ||
- !track.includes('Expanded') ||
- !track.includes('Bonus Track')
+ genre.includes('country') ||
+ genre.includes('americana') ||
+ genre.includes('bluegrass') ||
+ genre.includes('folk')
)
- return sanitizedTrack
- if (track.includes(' [')) sanitizedTrack = track.split(' [')[0]
- if (track.includes(' (')) sanitizedTrack = track.split(' (')[0]
- return sanitizedTrack
+ return 'πͺ'
+ if (genre.includes('post-punk')) return 'π'
+ if (genre.includes('dance-punk')) return 'πͺ©'
+ if (genre.includes('punk') || genre.includes('hardcore')) return 'β'
+ if (genre.includes('hip hop')) return 'π€'
+ if (genre.includes('progressive') || genre.includes('experimental')) return 'π€'
+ if (genre.includes('jazz')) return 'πΊ'
+ if (genre.includes('psychedelic')) return 'π'
+ if (genre.includes('dance') || genre.includes('electronic')) return 'π»'
+ if (
+ genre.includes('alternative') ||
+ genre.includes('rock') ||
+ genre.includes('shoegaze') ||
+ genre.includes('screamo')
+ )
+ return 'πΈ'
+ return DEFAULT
}
export default async () => {
- // eslint-disable-next-line no-undef
- const API_APPLE_MUSIC_DEVELOPER_TOKEN = Netlify.env.get('API_APPLE_MUSIC_DEVELOPER_TOKEN')
- // eslint-disable-next-line no-undef
- const API_APPLE_MUSIC_USER_TOKEN = Netlify.env.get('API_APPLE_MUSIC_USER_TOKEN')
// eslint-disable-next-line no-undef
const TV_KEY = Netlify.env.get('API_KEY_TRAKT')
+ // eslint-disable-next-line no-undef
+ const MUSIC_KEY = Netlify.env.get('API_KEY_LASTFM')
const traktRes = await fetch('https://api.trakt.tv/users/cdransf/watching', {
headers: {
@@ -135,34 +114,28 @@ export default async () => {
}
}
- const trackRes = await fetch(
- 'https://api.music.apple.com/v1/me/recent/played/tracks?limit=1&extend=artistUrl',
- {
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${API_APPLE_MUSIC_DEVELOPER_TOKEN}`,
- 'music-user-token': `${API_APPLE_MUSIC_USER_TOKEN}`,
- },
- }
- )
- .then((data) => data.json())
- .catch()
- const track = trackRes.data?.[0]['attributes']
- const trackUrl = track['url']
- ? track['url']
- : `https://musicbrainz.org/taglookup/index?tag-lookup.artist=${track['artistName'].replace(
- /\s+/g,
- '+'
- )}&tag-lookup.track=${track['name'].replace(/\s+/g, '+')}`
- const artist = aliasArtist(track['artistName'])
- const artistUrl = track['artistUrl']
- ? track['artistUrl']
- : `https://musicbrainz.org/search?query=${track['artistName'].replace(/\s+/g, '+')}&type=artist`
+ const trackUrl = `https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=coryd_&api_key=${MUSIC_KEY}&limit=1&format=json`
+ const trackRes = await fetch(trackUrl, {
+ type: 'json',
+ }).catch()
+ const trackData = await trackRes.json()
+ const track = trackData['recenttracks']['track'][0]
+ const mbid = track['artist']['mbid']
+ let genre = ''
+
+ if (mbid && mbid !== '') {
+ const genreUrl = `https://musicbrainz.org/ws/2/artist/${mbid}?inc=aliases+genres&fmt=json`
+ const genreRes = await fetch(genreUrl, {
+ type: 'json',
+ }).catch()
+ const genreData = await genreRes.json()
+ genre = genreData.genres.sort((a, b) => b.count - a.count)[0]?.['name'] || ''
+ }
return Response.json({
- content: `π§ ${sanitizeTrack(
+ content: `${emojiMap(genre, track['artist']['#text'])} ${
track['name']
- )} by ${artist}`,
+ } by ${track['artist']['#text']}`,
})
}
diff --git a/src/_data/albums.js b/src/_data/albums.js
new file mode 100644
index 00000000..5f635c19
--- /dev/null
+++ b/src/_data/albums.js
@@ -0,0 +1,32 @@
+const EleventyFetch = require('@11ty/eleventy-fetch')
+const ALBUM_DENYLIST = ['no-love-deep-web', 'unremittance']
+
+module.exports = async function () {
+ const MUSIC_KEY = process.env.API_KEY_LASTFM
+ const url = `https://ws.audioscrobbler.com/2.0/?method=user.gettopalbums&user=coryd_&api_key=${MUSIC_KEY}&limit=8&format=json&period=7day`
+ const res = EleventyFetch(url, {
+ duration: '1h',
+ type: 'json',
+ }).catch()
+ const data = await res
+ return data['topalbums']['album'].map((album) => {
+ return {
+ title: album['name'],
+ artist: album['artist']['name'],
+ plays: album['playcount'],
+ rank: album['@attr']['rank'],
+ image: !ALBUM_DENYLIST.includes(album['name'].replace(/\s+/g, '-').toLowerCase())
+ ? album['image'][album['image'].length - 1]['#text'].replace(
+ 'https://lastfm.freetls.fastly.net',
+ 'https://cd-albums.b-cdn.net'
+ )
+ : `https://cdn.coryd.dev/albums/${album['name'].name
+ .replace(/\s+/g, '-')
+ .toLowerCase()}.jpg`,
+ url: album['mbid']
+ ? `https://musicbrainz.org/album/${album['mbid']}`
+ : `https://musicbrainz.org/search?query=${encodeURI(album['name'])}&type=release_group`,
+ type: 'album',
+ }
+ })
+}
diff --git a/src/_data/artists.js b/src/_data/artists.js
new file mode 100644
index 00000000..8160bcab
--- /dev/null
+++ b/src/_data/artists.js
@@ -0,0 +1,25 @@
+const EleventyFetch = require('@11ty/eleventy-fetch')
+
+module.exports = async function () {
+ const MUSIC_KEY = process.env.API_KEY_LASTFM
+ const url = `https://ws.audioscrobbler.com/2.0/?method=user.gettopartists&user=coryd_&api_key=${MUSIC_KEY}&limit=8&format=json&period=7day`
+ const res = EleventyFetch(url, {
+ duration: '1h',
+ type: 'json',
+ }).catch()
+ const data = await res
+ return data['topartists']['artist'].map((artist) => {
+ return {
+ title: artist['name'],
+ plays: artist['playcount'],
+ rank: artist['@attr']['rank'],
+ image:
+ `https://cdn.coryd.dev/artists/${artist['name'].replace(/\s+/g, '-').toLowerCase()}.jpg` ||
+ 'https://cdn.coryd.dev/artists/missing-artist.jpg',
+ url: artist['mbid']
+ ? `https://musicbrainz.org/artist/${artist['mbid']}`
+ : `https://musicbrainz.org/search?query=${encodeURI(artist['name'])}&type=artist`,
+ type: 'artist',
+ }
+ })
+}
diff --git a/src/_data/books.js b/src/_data/books.js
index 68dd1832..fe0623d7 100644
--- a/src/_data/books.js
+++ b/src/_data/books.js
@@ -34,7 +34,7 @@ module.exports = async function () {
data.push({
image: images[i].src.replace(
'https://cdn.thestorygraph.com',
- 'https://books.coryd.dev'
+ 'https://cd-books.b-cdn.net'
),
})
data.push({ url: `https://app.thestorygraph.com${urls[i].href}` })
@@ -52,7 +52,7 @@ module.exports = async function () {
data[i]['author'] = authors[i].textContent
data[i]['image'] = images[i].src.replace(
'https://cdn.thestorygraph.com',
- 'https://books.coryd.dev'
+ 'https://cd-books.b-cdn.net'
)
data[i]['url'] = `https://app.thestorygraph.com${urls[i].href}`
data[i]['percentage'] = percentages[i].textContent
diff --git a/src/_data/movies.js b/src/_data/movies.js
index d90af0a5..be3236de 100644
--- a/src/_data/movies.js
+++ b/src/_data/movies.js
@@ -36,7 +36,7 @@ module.exports = async function () {
})
const tmdbData = await tmdbRes
const posterPath = tmdbData['poster_path']
- movie.image = `https://movies.coryd.dev/t/p/w500${posterPath}`
+ movie.image = `https://cd-movies.b-cdn.net/t/p/w500${posterPath}`
}
return movies
diff --git a/src/_data/music.js b/src/_data/music.js
deleted file mode 100644
index 5659690c..00000000
--- a/src/_data/music.js
+++ /dev/null
@@ -1,91 +0,0 @@
-const { AssetCache } = require('@11ty/eleventy-fetch')
-const { aliasArtist, sanitizeMedia, sortByPlays } = require('../utils/media')
-
-module.exports = async function () {
- const API_APPLE_MUSIC_DEVELOPER_TOKEN = process.env.API_APPLE_MUSIC_DEVELOPER_TOKEN
- const API_APPLE_MUSIC_USER_TOKEN = process.env.API_APPLE_MUSIC_USER_TOKEN
- const APPLE_RENEW_TOKEN_URL = process.env.APPLE_RENEW_TOKEN_URL
- const asset = new AssetCache('recent_tracks_data')
- const PAGE_SIZE = 30
- const PAGES = 8
- const response = {
- artists: {},
- albums: {},
- }
-
- const RENEWED_MUSIC_TOKEN = await fetch(APPLE_RENEW_TOKEN_URL, {
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${API_APPLE_MUSIC_DEVELOPER_TOKEN}`,
- 'X-Apple-Music-User-Token': `${API_APPLE_MUSIC_USER_TOKEN}`,
- },
- })
- .then((data) => data.json())
- .catch()
-
- let CURRENT_PAGE = 0
- let res = []
-
- if (asset.isCacheValid('1h')) return await asset.getCachedValue()
-
- while (CURRENT_PAGE < PAGES) {
- 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`
- const tracks = await fetch(URL, {
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${API_APPLE_MUSIC_DEVELOPER_TOKEN}`,
- 'music-user-token': `${RENEWED_MUSIC_TOKEN['music-token']}`,
- },
- })
- .then((data) => data.json())
- .catch()
- res = tracks['data']?.length ? [...res, ...tracks['data']] : [...res]
- CURRENT_PAGE++
- }
-
- res.forEach((track) => {
- const artist = aliasArtist(track['attributes']['artistName'])
- const album = sanitizeMedia(track['attributes']['albumName'])
- if (!response['artists'][artist]) {
- response['artists'][artist] = {
- title: artist,
- image: `https://cdn.coryd.dev/artists/${artist.replace(/\s+/g, '-').toLowerCase()}.jpg`,
- url: track['attributes']['artistUrl']
- ? track['attributes']['artistUrl']
- : `https://musicbrainz.org/search?query=${track['attributes']['artistName'].replace(
- /\s+/g,
- '+'
- )}&type=artist`,
- plays: 1,
- type: 'artist',
- }
- } else {
- response['artists'][artist].plays++
- }
-
- // aggregate albums
- if (!response.albums[album]) {
- response.albums[album] = {
- title: album,
- artist: aliasArtist(track['attributes']['artistName']),
- image: track['attributes']['artwork']['url'].replace('{w}', '500').replace('{h}', '500'),
- url:
- track['relationships'] && track['relationships'].albums.data.length > 0
- ? track['relationships'].albums.data.pop().attributes.url
- : `https://musicbrainz.org/taglookup/index?tag-lookup.artist=${track['attributes'][
- 'artistName'
- ].replace(/\s+/g, '+')}&tag-lookup.release=${album.replace(/\s+/g, '+')}`,
- plays: 1,
- type: 'album',
- }
- } else {
- response.albums[album].plays++
- }
- })
- response.artists = sortByPlays(response.artists)
- response.albums = sortByPlays(response.albums)
- await asset.save(response, 'json')
- return response
-}
diff --git a/src/_data/nav.js b/src/_data/nav.js
index 1702bab9..cfd407d7 100644
--- a/src/_data/nav.js
+++ b/src/_data/nav.js
@@ -15,9 +15,9 @@ module.exports = async function () {
{ name: 'GitHub', url: 'https://github.com/cdransf', icon: 'brand-github' },
{ name: 'Mastodon', url: 'https://social.lol/@cory', icon: 'brand-mastodon' },
{
- name: 'Apple Music',
- url: 'https://music.apple.com/profile/cdransf',
- icon: 'device-airpods',
+ name: 'Last.fm',
+ url: 'https://www.last.fm/user/coryd_',
+ icon: 'headphones',
},
{ name: 'Trakt', url: 'https://trakt.tv/users/cdransf', icon: 'device-tv' },
{ name: 'The StoryGraph', url: 'https://app.thestorygraph.com/profile/coryd', icon: 'books' },
diff --git a/src/_data/tv.js b/src/_data/tv.js
index 80b68f1c..bcc5824d 100644
--- a/src/_data/tv.js
+++ b/src/_data/tv.js
@@ -74,7 +74,7 @@ module.exports = async function () {
})
const tmdbData = await tmdbRes
const posterPath = tmdbData['poster_path']
- episode.image = `https://movies.coryd.dev/t/p/w500${posterPath}`
+ episode.image = `https://cd-movies.b-cdn.net/t/p/w500${posterPath}`
}
return episodes
diff --git a/src/_includes/now.liquid b/src/_includes/now.liquid
index befb6f6f..caeb20cd 100644
--- a/src/_includes/now.liquid
+++ b/src/_includes/now.liquid
@@ -3,8 +3,8 @@ layout: main
---
{% render "partials/header.liquid", site: site, page: page, nav: nav %}
{{ content }}
-{% render "partials/now/media-grid.liquid", data:music.artists, icon: "microphone-2", title: "Artists", shape: "square", count: 8, loading: 'eager' %}
-{% render "partials/now/media-grid.liquid", data:music.albums, icon: "vinyl", title: "Albums", shape: "square", count: 8 %}
+{% render "partials/now/media-grid.liquid", data:artists, icon: "microphone-2", title: "Artists", shape: "square", count: 8, loading: 'eager' %}
+{% render "partials/now/media-grid.liquid", data:albums, icon: "vinyl", title: "Albums", shape: "square", count: 8 %}
{% render "partials/now/albumReleases.liquid", albumReleases:albumReleases %}
{% render "partials/now/media-grid.liquid", data:books, icon: "books", title: "Books", shape: "vertical", count: 6 %}
{% render "partials/now/links.liquid", links:links %}
diff --git a/src/posts/2023/now-playing-eleventy-netlify-edge-functions-emoji.md b/src/posts/2023/now-playing-eleventy-netlify-edge-functions-emoji.md
index e78537bd..f5434550 100644
--- a/src/posts/2023/now-playing-eleventy-netlify-edge-functions-emoji.md
+++ b/src/posts/2023/now-playing-eleventy-netlify-edge-functions-emoji.md
@@ -13,7 +13,7 @@ The function I've written works by making a pair of API calls: one to Last.fm wh
export default async () => {
// access our Last.fm API key and interpolate it into a call to their recent tracks endpoint
const MUSIC_KEY = Netlify.env.get('API_KEY_LASTFM')
- const trackUrl = `https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=cdrn_&api_key=${MUSIC_KEY}&limit=1&format=json`
+ const trackUrl = `https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=coryd_&api_key=${MUSIC_KEY}&limit=1&format=json`
// fetch the track data
const trackRes = await fetch(trackUrl, {
type: 'json',
@@ -168,6 +168,6 @@ Finally, if the page this all lives on is loaded by a client without JavaScript
All of this, yields the single line at the bottom of this image β updated on each visit.
-{% image 'https://cdn.coryd.dev/blog/now-playing.jpg', 'Now playing', 'border border-blue-600 dark:border-blue-400 rounded-lg overflow-hidden [&>*]:w-full' %}
+{% image '', 'Now playing', 'border border-blue-600 dark:border-blue-400 rounded-lg overflow-hidden [&>*]:w-full' %}
[^1]: Plus explicit conditions matching David Bowie and Minor Threat.
diff --git a/src/posts/2023/onward-to-the-storygraph.md b/src/posts/2023/onward-to-the-storygraph.md
index 1739705d..6e254f0f 100644
--- a/src/posts/2023/onward-to-the-storygraph.md
+++ b/src/posts/2023/onward-to-the-storygraph.md
@@ -37,7 +37,7 @@ module.exports = async function () {
if (data[index]) data[index]['author'] = author.textContent
})
doc.querySelectorAll('.md\\:block .book-cover img').forEach((image, index) => {
- const img = image.src.replace('https://cdn.thestorygraph.com', 'https://books.coryd.dev')
+ const img = image.src.replace('https://cdn.thestorygraph.com', 'https://cd-books.b-cdn.net')
if (!data[index]) data.push({ image: img })
if (data[index]) data[index]['image'] = img
})