chore: albums post
This commit is contained in:
parent
b2f2155682
commit
4b0cd57d03
2 changed files with 80 additions and 2 deletions
|
@ -0,0 +1,69 @@
|
||||||
|
---
|
||||||
|
date: '2023-06-08'
|
||||||
|
title: 'From ICS to JSON: surfacing anticipated albums'
|
||||||
|
draft: false
|
||||||
|
tags: ['development', 'music', 'automation']
|
||||||
|
image: https://cdn.coryd.dev/blog/album-releases.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
I use MusicHarbor by [Marcos Tanaka](https://marcosatanaka.com) to track upcoming albums from my favorite artists (typically by syncing [my last.fm data](https://www.last.fm/user/cdme_) with the app.) When I see something new that I want to add to my collection I throw it on a calendar creatively titled `Albums`.<!-- excerpt -->
|
||||||
|
|
||||||
|
My calendar sits over at <a href="https://pr.tn/ref/X775YX40Z50G" onclick="va('event',{name:'Proton referral',data:{location:'Referrals'}})">Proton</a> which lets you share calendars with other users or as a syncable/downloadable `ics` file. As another entry into a list of experiments done because they can be I decided to surface these album events on [my now page](https://coryd.dev/now)[^1].
|
||||||
|
|
||||||
|
To do this I installed `ics-to-json-extended` and created a data file:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const { AssetCache } = require('@11ty/eleventy-fetch')
|
||||||
|
const ics = require('ics-to-json-extended')
|
||||||
|
const { DateTime } = require('luxon')
|
||||||
|
|
||||||
|
module.exports = async function () {
|
||||||
|
const URL = process.env.SECRET_FEED_ALBUM_RELEASES
|
||||||
|
const icsToJson = ics.default
|
||||||
|
const asset = new AssetCache('album_release_data')
|
||||||
|
if (asset.isCacheValid('1h')) return await asset.getCachedValue()
|
||||||
|
const icsRes = await fetch(URL)
|
||||||
|
const icsData = await icsRes.text()
|
||||||
|
const data = icsToJson(icsData)
|
||||||
|
return data.filter((d) => DateTime.fromISO(d.startDate) > DateTime.now())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We surface the url, require the ICS conversion library, cache and convert the response using [Luxon](https://www.npmjs.com/package/luxon)'s `DateTime` interface to compare the current time and dates in the object returned from the calendar, which look like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
startDate: '20180505T020000Z',
|
||||||
|
endDate: '20180505T060000Z',
|
||||||
|
location: 'url',
|
||||||
|
summary: 'Artist - Album'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rendering the output is as simple as:
|
||||||
|
{% raw %}
|
||||||
|
|
||||||
|
```liquid
|
||||||
|
{% if albumReleases.size > 0 %}
|
||||||
|
<h2 class="m-0 text-xl flex flex-row items-center font-black leading-tight tracking-normal dark:text-gray-200 md:text-2xl mt-8 mb-4">
|
||||||
|
{% heroicon "solid" "calendar" "Albums I'm looking forward to" "height=28" %}
|
||||||
|
<div class="ml-1">Albums I'm looking forward to</div>
|
||||||
|
</h2>
|
||||||
|
<ul class="list-inside list-disc pl-5 md:pl-10">
|
||||||
|
{% for album in albumReleases %}
|
||||||
|
<li class="mt-1.5 mb-2">
|
||||||
|
<span class="font-bold">{{ album.startDate | readableDate }}: </span>
|
||||||
|
<a href="https://{{album.location}}" title="{{album.summary | escape}}">
|
||||||
|
{{album.summary}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% endraw %}
|
||||||
|
Leaving us with:
|
||||||
|
{% image '<https://cdn.coryd.dev/blog/album-releases.jpg>', 'Albums I\'m looking forward to', 'w-full', '600px' %}
|
||||||
|
|
||||||
|
[^1]: At this point, a dev playground.
|
|
@ -60,6 +60,7 @@ ELEVENTY_PRODUCTION=true eleventy && NODE_ENV=production npx tailwindcss -i ./ta
|
||||||
|
|
||||||
The site include's Prism for code syntax highlighting and this is embedded and minified in the `<head>` of each page at build time:
|
The site include's Prism for code syntax highlighting and this is embedded and minified in the `<head>` of each page at build time:
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
|
||||||
```liquid
|
```liquid
|
||||||
{% capture css %}
|
{% capture css %}
|
||||||
{% include "../assets/styles/prism.css" %}
|
{% include "../assets/styles/prism.css" %}
|
||||||
|
@ -68,16 +69,20 @@ The site include's Prism for code syntax highlighting and this is embedded and m
|
||||||
{{ css | cssmin }}
|
{{ css | cssmin }}
|
||||||
</style>
|
</style>
|
||||||
```
|
```
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
This is made possible by leveraging CleanCSS in (you guessed it) `.eleventy.js`:
|
This is made possible by leveraging CleanCSS in (you guessed it) `.eleventy.js`:
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const CleanCSS = require('clean-css')
|
const CleanCSS = require('clean-css')
|
||||||
...
|
...
|
||||||
// css filters
|
// css filters
|
||||||
eleventyConfig.addFilter('cssmin', (code) => new CleanCSS({}).minify(code).styles)
|
eleventyConfig.addFilter('cssmin', (code) => new CleanCSS({}).minify(code).styles)
|
||||||
```
|
```
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
|
|
||||||
## Minify HTML output
|
## Minify HTML output
|
||||||
|
|
||||||
Final HTML output for the site is minified when it's built using `@sherby/eleventy-plugin-files-minifier` which, [per the docs](https://www.npmjs.com/package/@sherby/eleventy-plugin-files-minifier):
|
Final HTML output for the site is minified when it's built using `@sherby/eleventy-plugin-files-minifier` which, [per the docs](https://www.npmjs.com/package/@sherby/eleventy-plugin-files-minifier):
|
||||||
|
@ -99,6 +104,7 @@ module.exports = function (eleventyConfig) {
|
||||||
|
|
||||||
Finally (and this is something that took me longer than it should have to do), we optimize images at build time using `@11ty/eleventy-image`, defining a shortcode in `.eleventy.js` as follows:
|
Finally (and this is something that took me longer than it should have to do), we optimize images at build time using `@11ty/eleventy-image`, defining a shortcode in `.eleventy.js` as follows:
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// image shortcode
|
// image shortcode
|
||||||
eleventyConfig.addShortcode('image', async function (src, alt, css, sizes, loading) {
|
eleventyConfig.addShortcode('image', async function (src, alt, css, sizes, loading) {
|
||||||
|
@ -120,17 +126,20 @@ eleventyConfig.addShortcode('image', async function (src, alt, css, sizes, loadi
|
||||||
return Image.generateHTML(metadata, imageAttributes)
|
return Image.generateHTML(metadata, imageAttributes)
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
This is most impactful on [my now page](https://coryd.dev/now) which is populated with quite a few images and, when used, looks something like this:
|
This is most impactful on [my now page](https://coryd.dev/now) which is populated with quite a few images and, when used, looks something like this:
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
|
||||||
```liquid
|
```liquid
|
||||||
{% image artistImg, artistName, 'rounded-lg', '225px', 'eager' %}
|
{% image artistImg, artistName, 'rounded-lg', '225px', 'eager' %}
|
||||||
```
|
```
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
For this page in particular, the images that are rendered above the fold are set to load as `eager` to mitigate performance impacts related to [too much lazy loading](https://web.dev/lcp-lazy-loading/). These images are fetched from caches hosted at [Bunny.net](https://bunny.net/?ref=revw3mehej) when the site is built.
|
For this page in particular, the images that are rendered above the fold are set to load as `eager` to mitigate performance impacts related to [too much lazy loading](https://web.dev/lcp-lazy-loading/). These images are fetched from caches hosted at <a href="https://bunny.net?ref=revw3mehej" onclick="va('event',{name:'Bunny.net referral',data:{location:'Referrals'}})">Bunny.net</a> when the site is built.
|
||||||
|
|
||||||
All of these boilerplate steps leave us with a quick to load, accessible and resilient site:
|
All of these boilerplate steps leave us with a quick to load, accessible and resilient site:
|
||||||
|
|
||||||
{% image 'https://cdn.coryd.dev/blog/page-speed.jpg', 'Pagespeed scores for coryd.dev/now', 'w-full', '600px' %}
|
{% image '<https://cdn.coryd.dev/blog/page-speed.jpg>', 'Pagespeed scores for coryd.dev/now', 'w-full', '600px' %}
|
||||||
|
|
||||||
[^1]: It's easy, flexible and helps mitigate my lack of an eye for design by providing safe baselines.
|
[^1]: It's easy, flexible and helps mitigate my lack of an eye for design by providing safe baselines.
|
||||||
|
|
Reference in a new issue