feat: initial commit

This commit is contained in:
Cory Dransfeldt 2025-03-27 16:46:02 -07:00
commit e214116e40
No known key found for this signature in database
253 changed files with 17406 additions and 0 deletions

View file

@ -0,0 +1,50 @@
{% assign media = artists | concat:books | concat:genres | concat:movies | concat:posts | concat:shows %}
{% if media.size > 0 %}
<div class="associated-media">
{% assign sections =
"artists:headphones:music:Related artist(s)," | append:
"books:books:books:Related book(s)," | append:
"genres:headphones:music:Related genre(s)," | append:
"movies:movie:movies:Related movie(s)," | append:
"posts:article:article:Related post(s)," | append:
"shows:device-tv-old:tv:Related show(s)"
| split:"," %}
{% for section in sections %}
{% assign parts = section | split:":" %}
{% assign key = parts[0] %}
{% assign icon = parts[1] %}
{% assign css_class = parts[2] %}
{% assign label = parts[3] %}
{% case key %}
{% when "artists" %} {% assign items = artists %}
{% when "books" %} {% assign items = books %}
{% when "genres" %} {% assign items = genres %}
{% when "movies" %} {% assign items = movies %}
{% when "posts" %} {% assign items = posts %}
{% when "shows" %} {% assign items = shows %}
{% endcase %}
{% if items and items.size > 0 %}
<p id="{{ key }}" class="{{ css_class }}">
{% tablericon icon %}
{{ label }}
</p>
<ul>
{% for item in items %}
<li class="{{ css_class }}">
<a href="{{ item.url }}">{{ item.title | default:item.name }}</a>
{% if key == "artists" and item.total_plays > 0 %}
({{ item.total_plays }} {{ item.total_plays | pluralize:"play" }})
{% elsif key == "books" %}
by {{ item.author }}
{% elsif key == "movies" or key == "shows" %}
({{ item.year }})
{% elsif key == "posts" %}
({{ item.date | date:"%B %e, %Y" }})
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
</div>
{% endif %}

View file

@ -0,0 +1,3 @@
<div class="banner calendar">
<p>{% tablericon "calendar" %} <a href="{{ url }}">{{ text }}</a>.</p>
</div>

View file

@ -0,0 +1,5 @@
<div class="banner coffee">
<p>
{% tablericon "coffee" %} <a class="coffee" href="https://buymeacoffee.com/cory">If you found this post helpful, you can buy me a coffee.</a>
</p>
</div>

View file

@ -0,0 +1,3 @@
<div class="banner error">
<p>{% tablericon "alert-circle" %} {{ text }}</p>
</div>

View file

@ -0,0 +1,3 @@
<div class="banner github">
<p>{% tablericon "brand-github" %} Take a look at <a href="{{ url }}">the GitHub repository for this project</a>. (Give it a star if you feel like it.)</p>
</div>

View file

@ -0,0 +1,8 @@
{%- if url -%}
<div class="banner mastodon">
<p>
{% tablericon "brand-mastodon" %}
<a class="mastodon" href="{{ url }}"> Discuss this post on Mastodon. </a>
</p>
</div>
{%- endif -%}

View file

@ -0,0 +1,3 @@
<div class="banner npm">
<p>{% tablericon "brand-npm" %} <a href="{{ url }}">You can take a look at this package on NPM</a> or install it by running <code>{{ command }}</code>.</p>
</div>

View file

@ -0,0 +1,5 @@
{%- if isOldPost -%}
<div class="banner old-post">
<p>{% tablericon "clock-x" %} This post is over 3 years old. I've probably changed my mind since it was written and it <em>could</em> be out of date.</p>
</div>
{%- endif -%}

View file

@ -0,0 +1,3 @@
<div class="banner rss">
<p>{% tablericon "rss" %} <a href="{{ url }}">{{ text }}</a>.</p>
</div>

View file

@ -0,0 +1,3 @@
<div class="banner warning">
<p>{% tablericon "alert-triangle" %} {{ text }}</p>
</div>

View file

@ -0,0 +1,17 @@
<img
srcset="
{{ globals.cdn_url }}{{ image }}?class=bannersm&type=webp 256w,
{{ globals.cdn_url }}{{ image }}?class=bannermd&type=webp 512w,
{{ globals.cdn_url }}{{ image }}?class=bannerbase&type=webp 1024w
"
sizes="(max-width: 450px) 256px,
(max-width: 850px) 512px
1024px"
src="{{ globals.cdn_url }}{{ image }}?class=bannersm&type=webp"
alt="{{ alt | replaceQuotes }}"
class="image-banner"
loading="lazy"
decoding="async"
width="720"
height="480"
/>

View file

@ -0,0 +1,32 @@
{%- for block in blocks -%}
{%- case block.type -%}
{%- when "youtube_player" -%}
{% render "blocks/youtube-player.liquid",
url:block.url
%}
{%- when "github_banner" -%}
{% render "blocks/banners/github.liquid",
url:block.url
%}
{%- when "npm_banner" -%}
{% render "blocks/banners/npm.liquid",
url:block.url,
command:block.command
%}
{%- when "rss_banner" -%}
{% render "blocks/banners/rss.liquid",
url:block.url,
text:block.text
%}
{%- when "hero" -%}
{% render "blocks/hero.liquid",
globals:globals,
image:block.image,
alt:block.alt
%}
{%- when "markdown" -%}
{{ block.text | markdown }}
{%- when "divider" -%}
{{ block.markup | markdown }}
{%- endcase -%}
{%- endfor -%}

View file

@ -0,0 +1,29 @@
{%- capture labelContent -%}
{%- if icon -%}
{% tablericon icon %}
{%- elsif label -%}
{{ label }}
{%- endif -%}
{%- endcapture -%}
{% assign modalId = id | default:"modal-controls" %}
<noscript>
<input class="modal-input" id="{{ modalId }}" type="checkbox" tabindex="0" />
<label class="modal-open" for="{{ modalId }}">{{ labelContent }}</label>
<div class="modal-wrapper">
<div class="modal-body">
<label class="modal-close" for="{{ modalId }}">
{% tablericon "circle-x" %}
</label>
{{ content }}
</div>
</div>
</noscript>
<button class="modal-open client-side" data-modal-trigger="{{ modalId }}" data-modal-button>
{{ labelContent }}
</button>
<dialog id="dialog-{{ modalId }}" class="client-side">
<button class="modal-close" data-modal-button>
{% tablericon "circle-x" %}
</button>
{{ content }}
</dialog>

View file

@ -0,0 +1,7 @@
<script type="module" src="/assets/scripts/components/now-playing.js?v={% appVersion %}" defer></script>
<p class="{{ section }}">
<mark>Now playing</mark>
<now-playing>
<span class="content">{{ nowPlaying }}</span>
</now-playing>
</p>

View file

@ -0,0 +1,2 @@
<script type="module" src="/assets/scripts/components/youtube-video-element.js?v={% appVersion %}" defer></script>
<youtube-video controls src="{{ url }}"></youtube-video>

View file

@ -0,0 +1,80 @@
<?php
require __DIR__ . "/../../vendor/autoload.php";
require __DIR__ . "/../../server/utils/init.php";
use GuzzleHttp\Client;
$requestUri = $_SERVER["REQUEST_URI"];
$url = trim(parse_url($requestUri, PHP_URL_PATH), "/");
$postgrestUrl = $_ENV["POSTGREST_URL"] ?? getenv("POSTGREST_URL");
$postgrestApiKey = $_ENV["POSTGREST_API_KEY"] ?? getenv("POSTGREST_API_KEY");
if (strpos($url, "music/artists/") !== 0) {
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
$cacheKey = "artist_" . md5($url);
$artist = null;
$useRedis = false;
try {
if (extension_loaded('redis')) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$useRedis = true;
}
} catch (Exception $e) {
error_log("Redis not available: " . $e->getMessage());
}
if ($useRedis && $redis->exists($cacheKey)) {
$artist = json_decode($redis->get($cacheKey), true);
} else {
$apiUrl = "$postgrestUrl/optimized_artists?url=eq./" . $url;
$client = new Client();
try {
$response = $client->request("GET", $apiUrl, [
"headers" => [
"Accept" => "application/json",
"Authorization" => "Bearer {$postgrestApiKey}",
],
]);
$artistData = json_decode($response->getBody(), true);
if (!$artistData) {
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
$artist = $artistData[0];
if ($useRedis) {
$redis->setex($cacheKey, 3600, json_encode($artist));
}
} catch (Exception $e) {
error_log($e->getMessage());
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
}
$artist["description"] = parseMarkdown($artist["description"]);
$pageTitle = htmlspecialchars(
"Artists • " . $artist["name"],
ENT_QUOTES,
"UTF-8"
);
$pageDescription = truncateText(htmlspecialchars(
strip_tags($artist["description"]),
ENT_QUOTES,
"UTF-8"
), 250);
$ogImage = htmlspecialchars($artist["image"] . "?class=w800", ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri;
header("Cache-Control: public, max-age=3600");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + 3600) . " GMT");
?>

View file

@ -0,0 +1,103 @@
<?php
require __DIR__ . "/../vendor/autoload.php";
require __DIR__ . "/../server/utils/init.php";
use GuzzleHttp\Client;
$requestUri = $_SERVER["REQUEST_URI"];
$url = trim(parse_url($requestUri, PHP_URL_PATH), "/");
$postgrestUrl = $_ENV["POSTGREST_URL"] ?? getenv("POSTGREST_URL");
$postgrestApiKey = $_ENV["POSTGREST_API_KEY"] ?? getenv("POSTGREST_API_KEY");
if (preg_match('/^books\/years\/(\d{4})$/', $url, $matches)) {
$year = $matches[1];
$filePath = __DIR__ . "/years/{$year}.html";
if (file_exists($filePath)) {
echo file_get_contents($filePath);
exit();
} else {
echo file_get_contents(__DIR__ . "/../404/index.html");
exit();
}
}
if ($url === "books/years") {
echo file_get_contents(__DIR__ . "/../404/index.html");
exit();
}
if ($url === "books") {
readfile("index.html");
exit();
}
if (!preg_match('/^books\/[\w-]+$/', $url)) {
echo file_get_contents(__DIR__ . "/../404/index.html");
exit();
}
$cacheKey = "book_" . md5($url);
$book = null;
$useRedis = false;
try {
if (extension_loaded('redis')) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$useRedis = true;
}
} catch (Exception $e) {
error_log("Redis not available: " . $e->getMessage());
}
if ($useRedis && $redis->exists($cacheKey)) {
$book = json_decode($redis->get($cacheKey), true);
} else {
$apiUrl = "$postgrestUrl/optimized_books?url=eq./" . $url;
$client = new Client();
try {
$response = $client->request("GET", $apiUrl, [
"headers" => [
"Accept" => "application/json",
"Authorization" => "Bearer {$postgrestApiKey}",
],
]);
$bookData = json_decode($response->getBody(), true);
if (!$bookData) {
echo file_get_contents(__DIR__ . "/../404/index.html");
exit();
}
$book = $bookData[0];
if ($useRedis) {
$redis->setex($cacheKey, 3600, json_encode($book));
}
} catch (Exception $e) {
error_log($e->getMessage());
echo file_get_contents(__DIR__ . "/../404/index.html");
exit();
}
}
$book["description"] = parseMarkdown($book["description"]);
$pageTitle = htmlspecialchars(
"Books • " . $book["title"] . " by " . $book["author"],
ENT_QUOTES,
"UTF-8"
);
$pageDescription = truncateText(htmlspecialchars(
strip_tags($book["description"]),
ENT_QUOTES,
"UTF-8"
), 250);
$ogImage = htmlspecialchars($book["image"] . "?class=w800", ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri;
header("Cache-Control: public, max-age=3600");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + 3600) . " GMT");
?>

View file

@ -0,0 +1,71 @@
<?php
require __DIR__ . "/../../vendor/autoload.php";
require __DIR__ . "/../../server/utils/init.php";
use GuzzleHttp\Client;
$requestUri = $_SERVER["REQUEST_URI"];
$url = trim(parse_url($requestUri, PHP_URL_PATH), "/");
$postgrestUrl = $_ENV["POSTGREST_URL"] ?? getenv("POSTGREST_URL");
$postgrestApiKey = $_ENV["POSTGREST_API_KEY"] ?? getenv("POSTGREST_API_KEY");
if (!preg_match('/^music\/genres\/[\w-]+$/', $url)) {
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
$cacheKey = "genre_" . md5($url);
$genre = null;
$useRedis = false;
try {
if (extension_loaded('redis')) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$useRedis = true;
}
} catch (Exception $e) {
error_log("Redis not available: " . $e->getMessage());
}
if ($useRedis && $redis->exists($cacheKey)) {
$genre = json_decode($redis->get($cacheKey), true);
} else {
$apiUrl = "$postgrestUrl/optimized_genres?url=eq./" . $url;
$client = new Client();
try {
$response = $client->request("GET", $apiUrl, [
"headers" => [
"Accept" => "application/json",
"Authorization" => "Bearer {$postgrestApiKey}",
],
]);
$genreData = json_decode($response->getBody(), true);
if (!$genreData) {
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
$genre = $genreData[0];
if ($useRedis) {
$redis->setex($cacheKey, 3600, json_encode($genre));
}
} catch (Exception $e) {
error_log($e->getMessage());
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
}
$pageTitle = htmlspecialchars("Genres • " . $genre["name"], ENT_QUOTES, "UTF-8");
$pageDescription = truncateText(htmlspecialchars(strip_tags($genre["description"]), ENT_QUOTES, "UTF-8"), 250);
$ogImage = htmlspecialchars($genre["artists"][0]["image"] . "?class=w800", ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri;
header("Cache-Control: public, max-age=3600");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + 3600) . " GMT");
?>

View file

@ -0,0 +1,80 @@
<?php
require __DIR__ . "/../../vendor/autoload.php";
require __DIR__ . "/../../server/utils/init.php";
use GuzzleHttp\Client;
$requestUri = $_SERVER["REQUEST_URI"];
$url = trim(parse_url($requestUri, PHP_URL_PATH), "/");
$postgrestUrl = $_ENV["POSTGREST_URL"] ?? getenv("POSTGREST_URL");
$postgrestApiKey = $_ENV["POSTGREST_API_KEY"] ?? getenv("POSTGREST_API_KEY");
if (!preg_match('/^watching\/movies\/[\w-]+$/', $url)) {
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
$cacheKey = "movie_" . md5($url);
$movie = null;
$useRedis = false;
try {
if (extension_loaded('redis')) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$useRedis = true;
}
} catch (Exception $e) {
error_log("Redis not available: " . $e->getMessage());
}
if ($useRedis && $redis->exists($cacheKey)) {
$movie = json_decode($redis->get($cacheKey), true);
} else {
$apiUrl = "$postgrestUrl/optimized_movies?url=eq./" . $url;
$client = new Client();
try {
$response = $client->request("GET", $apiUrl, [
"headers" => [
"Accept" => "application/json",
"Authorization" => "Bearer {$postgrestApiKey}",
],
]);
$movieData = json_decode($response->getBody(), true);
if (!$movieData) {
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
$movie = $movieData[0];
if ($useRedis) {
$redis->setex($cacheKey, 3600, json_encode($movie));
}
} catch (Exception $e) {
error_log($e->getMessage());
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
}
$movie["description"] = parseMarkdown($movie["description"]);
$pageTitle = htmlspecialchars(
"Movies • " . $movie["title"],
ENT_QUOTES,
"UTF-8"
);
$pageDescription = truncateText(htmlspecialchars(
strip_tags($movie["description"]),
ENT_QUOTES,
"UTF-8"
), 250);
$ogImage = htmlspecialchars($movie["backdrop"] . "?class=w800", ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri;
header("Cache-Control: public, max-age=3600");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + 3600) . " GMT");
?>

View file

@ -0,0 +1,71 @@
<?php
require __DIR__ . "/../../vendor/autoload.php";
require __DIR__ . "/../../server/utils/init.php";
use GuzzleHttp\Client;
$requestUri = $_SERVER["REQUEST_URI"];
$url = trim(parse_url($requestUri, PHP_URL_PATH), "/");
$postgrestUrl = $_ENV["POSTGREST_URL"] ?? getenv("POSTGREST_URL");
$postgrestApiKey = $_ENV["POSTGREST_API_KEY"] ?? getenv("POSTGREST_API_KEY");
if (!preg_match('/^watching\/shows\/[\w-]+$/', $url)) {
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
$cacheKey = "show_" . md5($url);
$show = null;
$useRedis = false;
try {
if (extension_loaded('redis')) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$useRedis = true;
}
} catch (Exception $e) {
error_log("Redis not available: " . $e->getMessage());
}
if ($useRedis && $redis->exists($cacheKey)) {
$show = json_decode($redis->get($cacheKey), true);
} else {
$apiUrl = "$postgrestUrl/optimized_shows?url=eq./" . $url;
$client = new Client();
try {
$response = $client->request("GET", $apiUrl, [
"headers" => [
"Accept" => "application/json",
"Authorization" => "Bearer {$postgrestApiKey}",
],
]);
$showData = json_decode($response->getBody(), true);
if (!$showData) {
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
$show = $showData[0];
if ($useRedis) {
$redis->setex($cacheKey, 3600, json_encode($show));
}
} catch (Exception $e) {
error_log($e->getMessage());
echo file_get_contents(__DIR__ . "/../../404/index.html");
exit();
}
}
$pageTitle = htmlspecialchars("Show • " . $show["title"], ENT_QUOTES, "UTF-8");
$pageDescription = truncateText(htmlspecialchars(strip_tags($show["description"]), ENT_QUOTES, "UTF-8"), 250);
$ogImage = htmlspecialchars($show["image"] . "?class=w800", ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri;
header("Cache-Control: public, max-age=3600");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + 3600) . " GMT");
?>

View file

@ -0,0 +1,8 @@
<article class="intro">
{{ intro }}
{% render "blocks/now-playing.liquid",
nowPlaying:nowPlaying
section:"music"
%}
<hr />
</article>

View file

@ -0,0 +1,60 @@
<article>
<h2>
{% tablericon "activity" %}
Recent activity
</h2>
<p>
<a href="/posts" class="article">Posts</a> •
<a href="/links" class="link">Links</a> •
<a href="/watching" class="movies">Watching</a> •
<a href="/books" class="books">Books</a>
</p>
{%- for item in items -%}
<article class="{{ item.type }}">
<aside>
<time datetime="{{ item.content_date }}">
{{ item.content_date | date:"%B %e, %Y" }}
</time>
• {{ item.label }}
{%- if item.notes -%}
{% assign notes = item.notes | prepend: "### Notes\n" | markdown %}
• {% render "blocks/modal.liquid",
label:"Notes",
content:notes,
id:item.content_date
%}
{%- endif -%}
</aside>
<h3>
{%- if item.type == "concerts" -%}
{%- capture artistName -%}
{%- if item.artist_url -%}
<a href="{{ item.artist_url }}">{{ item.title | split: ' at ' | first }}</a>
{%- else -%}
{{ item.title | split: ' at ' | first }}
{%- endif -%}
{%- endcapture -%}
{%- capture venue -%}
{%- if item.venue_lat and item.venue_lon -%}
<a href="https://www.openstreetmap.org/?mlat={{ item.venue_lat }}&mlon={{ item.venue_lon }}#map=18/{{ item.venue_lat }}/{{ item.venue_lon }}">{{ item.venue_name }}</a>
{%- else -%}
{{ item.venue_name }}
{%- endif -%}
{%- endcapture -%}
{{ artistName }}
{% if venue %} at {{ venue }}{% endif %}
{%- else -%}
<a href="{{ item.url | prepend: globals.url }}">{{ item.title }}</a>
{%- if item.type == "link" and item.author -%}
<span> via </span>
{%- if item.author.url -%}
<a href="{{ item.author.url }}">{{ item.author.name }}</a>
{%- else -%}
{{ item.author.name }}
{%- endif -%}
{%- endif -%}
{%- endif -%}
</h3>
</article>
{%- endfor -%}
</article>

View file

@ -0,0 +1,19 @@
<article>
{% render "media/grid.liquid",
globals:globals,
data:media.recentMusic,
count:8,
loading:"eager"
%}
{% render "media/grid.liquid",
globals:globals,
data:media.recentWatchedRead,
shape:"vertical",
count:6
loading:"eager"
%}
{% render "blocks/banners/rss.liquid",
url:"/feeds",
text:"Subscribe to my movies, books, links or activity feed(s)"
%}
</article>

View file

@ -0,0 +1,19 @@
{%- assign updateTime = "" -%}
{%- if updated == "now" -%}
{%- assign updateTime = "now" | date:"%B %-d, %l:%M %P", "America/Los_Angeles" -%}
{%- elsif pageUpdated -%}
{%- assign updateTime = page.updated | date:"%B %-d, %l:%M %P", "America/Los_Angeles" -%}
{%- endif -%}
<footer{% unless updateTime %} style="margin-top:var(--spacing-3xl)"{% endunless %}>
{%- if updateTime -%}
<p class="updated"><em>This page was last updated on {{ updateTime | strip }}.</em></p>
{%- endif -%}
{% render "nav/social.liquid",
page:page,
links:nav.footer_icons
%}
{% render "nav/secondary.liquid",
page:page,
links:nav.footer_text
%}
</footer>

View file

@ -0,0 +1,13 @@
<section class="main-title">
<h1>
{%- if page.url == "/" -%}
{{ globals.site_name }}
{%- else -%}
<a href="/" tabindex="0">{{ globals.site_name }}</a>
{%- endif -%}
</h1>
{% render "nav/primary.liquid",
page:page,
nav:nav
%}
</section>

View file

@ -0,0 +1,47 @@
<div class="media-grid {{ shape | default: square }}">
{%- assign loadingStrategy = loading | default:"lazy" -%}
{%- for item in data limit:count -%}
{%- assign alt = item.grid.alt | replaceQuotes -%}
{%- assign imageUrl = item.grid.image -%}
<a href="{{ item.grid.url }}" title="{{ alt }}">
<div class="media-grid-item">
{%- if item.grid.title or item.grid.subtext -%}
<div class="meta-text {{ item.type }}">
{% if item.grid.title %}
<div class="header">{{ item.grid.title }}</div>
{% endif %}
{% if item.grid.subtext %}
<div class="subheader">{{ item.grid.subtext }}</div>
{% endif %}
</mark>
</div>
{%- endif -%}
{%- assign imageClass = "square" -%}
{%- assign width = 150 -%}
{%- assign height = 150 -%}
{%- case shape -%}
{%- when "vertical" -%}
{%- assign imageClass = "vertical" -%}
{%- assign width = 120 -%}
{%- assign height = 184 -%}
{%- endcase -%}
<img
srcset="
{{ globals.cdn_url }}{{ imageUrl }}?class={{ imageClass }}sm&type=webp {{ width }}w,
{{ globals.cdn_url }}{{ imageUrl }}?class={{ imageClass }}md&type=webp {{ width | times:2 }}w
"
sizes="(max-width: 450px) {{ width }}px, {{ width | times:2 }}px"
src="{{ globals.cdn_url }}{{ imageUrl }}?class={{ imageClass }}sm&type=webp"
alt="{{ alt }}"
loading="{{ loadingStrategy }}"
decoding="async"
width="{{ width }}"
height="{{ height }}"
/>
</div>
</a>
{%- endfor -%}
</div>
{% render "nav/paginator.liquid",
pagination:pagination
%}

View file

@ -0,0 +1,13 @@
<div class="chart-item">
<div class="chart-item-info">
<a class="title" href="{{ item.chart.url }}">{{ item.chart.title }}</a>
{%- assign playsLabel = item.chart.plays | pluralize:"play" -%}
<span class="subheader">{{ item.chart.artist }}</span>
<span class="subheader">{{ item.chart.plays }} {{ playsLabel }}</span>
</div>
<div class="chart-item-progress">
{% render "media/progress-bar.liquid",
percentage:item.chart.percentage
%}
</div>
</div>

View file

@ -0,0 +1,24 @@
<div class="music-chart">
<ol type="1">
{%- if count -%}
{%- for item in data limit:count -%}
<li value="{{ item.chart.rank }}">
{% render "media/music/charts/item.liquid",
item:item
%}
</li>
{%- endfor -%}
{%- else -%}
{%- for item in pagination.items -%}
<li value="{{ item.chart.rank }}">
{% render "media/music/charts/item.liquid",
item:item
%}
</li>
{%- endfor -%}
{%- endif -%}
</ol>
</div>
{% render "nav/paginator.liquid",
pagination:pagination
%}

View file

@ -0,0 +1,30 @@
<div class="music-chart">
{%- for item in data limit:10 -%}
<div class="chart-item">
<div class="meta">
<a href="{{ item.chart.url }}">
<img
srcset="
{{ globals.cdn_url }}{{ item.chart.image }}?class=w50&type=webp 50w,
{{ globals.cdn_url }}{{ item.chart.image }}?class=w100&type=webp 100w
"
sizes="(max-width: 450px) 50px, 100px"
src="{{ globals.cdn_url }}{{ item.chart.image }}?class=w50&type=webp"
alt="{{ item.chart.alt | replaceQuotes }}"
loading="lazy"
decoding="async"
width="64"
height="64"
/>
</a>
<div class="meta-text">
<a class="title" href="{{ item.chart.url }}">{{ item.chart.title }}</a>
<span class="subheader">{{ item.chart.subtext }}</span>
</div>
</div>
<time datetime="item.chart.played_at">
{{ item.chart.played_at | date:"%B %-d, %-I:%M%p", "America/Los_Angeles" }}
</time>
</div>
{%- endfor -%}
</div>

View file

@ -0,0 +1,21 @@
<table class="music-ranking">
<tr>
<th>Albums (all time)</th>
<th>Artist</th>
<th>Plays</th>
<th>Year</th>
</tr>
{% for album in topAlbums %}
<tr>
<td>
<div>
<img src="{{ globals.cdn_url }}{{ album.table.image }}?class=w100" alt="{{ album.table.alt }}" width="50">
{{ album.table.title }}
</div>
</td>
<td><a href="{{ album.table.url }}">{{ album.table.artist }}</a></td>
<td>{{ album.table.plays }}</td>
<td>{{ album.table.year }}</td>
</tr>
{% endfor %}
</table>

View file

@ -0,0 +1,19 @@
<table class="music-ranking">
<tr>
<th>Artists (all time)</th>
<th>Genre</th>
<th>Plays</th>
</tr>
{% for artist in topArtists %}
<tr>
<td>
<div>
<img src="{{ globals.cdn_url }}{{ artist.table.image }}?class=w100" alt="{{ artist.table.alt }}" width="50">
<a href="{{ artist.table.url }}">{{ artist.table.title }}</a>
</div>
</td>
<td>{{ artist.table.emoji }} <a href="{{ artist.table.genre_url }}">{{ artist.table.genre }}</a></td>
<td>{{ artist.table.plays }}</td>
</tr>
{% endfor %}
</table>

View file

@ -0,0 +1,3 @@
{%- if percentage -%}
<progress value="{{ percentage }}" max="100">{{ percentage }}%</progress>
{%- endif -%}

View file

@ -0,0 +1,18 @@
<a href="{{ movie.url }}">
<div class="watching hero">
<div class="meta-text highlight-text">
<div class="header">{{ movie.title }}</div>
<div class="subheader">
{%- if movie.rating -%}
<span class="rating">{{ movie.rating }} </span>
{%- endif -%}
({{ movie.year }})
</div>
</div>
{% render "blocks/hero.liquid",
globals:globals,
image:movie.backdrop,
alt:movie.title
%}
</div>
</a>

View file

@ -0,0 +1,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light dark" />
<meta name="theme-color" content="{{ globals.theme_color }}" />
<meta name="fediverse:creator" content="{{ globals.mastodon }}" />
<meta name="generator" content="{{ eleventy.generator }}" />
<link href="{{ globals.cdn_url }}{{ globals.avatar_transparent }}?class=w50&v={% appVersion %}" rel="icon" sizes="any" />
<link href="{{ globals.cdn_url }}{{ globals.avatar_transparent }}?class=w50&v={% appVersion %}&type=svg" rel="icon" type="image/svg+xml" />
<link href="{{ globals.cdn_url }}{{ globals.avatar }}?class=w800&v={% appVersion %}" rel="apple-touch-icon" />
<link rel="alternate" href="{{ globals.url }}/feeds/posts.xml" title="Posts • {{ globals.site_name }}" />
<link rel="alternate" href="{{ globals.url }}/feeds/links.xml" title="Links • {{ globals.site_name }}" type="application/rss+xml" />
<link rel="alternate" href="{{ globals.url }}/feeds/movies.xml" title="Movies • {{ globals.site_name }}" type="application/rss+xml" />
<link rel="alternate" href="{{ globals.url }}/feeds/books.xml" title="Books • {{ globals.site_name }}" type="application/rss+xml" />
<link rel="alternate" href="{{ globals.url }}/feeds/all.xml" title="All activity • {{ globals.site_name }}" type="application/rss+xml" />

View file

@ -0,0 +1,7 @@
<title><?= htmlspecialchars($pageTitle ?? "{{ pageTitle }}", ENT_QUOTES, 'UTF-8') ?> • {{ globals.site_name }}</title>
<meta name="description" content="<?= htmlspecialchars($pageDescription ?? '{{ pageDescription | escape }}', ENT_QUOTES, 'UTF-8') ?>" />
<meta property="og:title" content="<?= htmlspecialchars($pageTitle ?? '{{ pageTitle }}', ENT_QUOTES, 'UTF-8') ?> • {{ globals.site_name }}" />
<meta property="og:description" content="<?= htmlspecialchars($pageDescription ?? '{{ pageDescription | escape }}', ENT_QUOTES, 'UTF-8') ?>" />
<meta property="og:image" content="<?= htmlspecialchars("{{ globals.cdn_url }}" . ($ogImage ?? '{{ ogImage }}'), ENT_QUOTES, 'UTF-8') ?>" />
<meta property="og:url" content="<?= htmlspecialchars($fullUrl ?? '{{ fullUrl }}', ENT_QUOTES, 'UTF-8') ?>" />
<link rel="canonical" href="<?= htmlspecialchars($fullUrl ?? '{{ fullUrl }}', ENT_QUOTES, 'UTF-8') ?>" />

View file

@ -0,0 +1,94 @@
{%- assign fullUrl = globals.url | append: page.url -%}
{%- capture pageTitle -%}
{%- if page.title -%}
{{ page.title | append: ' • ' | append: globals.site_name }}
{%- elsif title -%}
{{ title | append: ' • ' | append: globals.site_name }}
{%- else -%}
{{ globals.site_name }}
{%- endif -%}
{%- endcapture -%}
{%- capture pageDescription -%}
{% if page.description %}
{{ page.description }}
{% elsif description %}
{{ description }}
{% else %}
{{ globals.site_description }}
{% endif %}
{%- endcapture -%}
{%- assign ogImage = globals.cdn_url | append: globals.avatar -%}
{%- case schema -%}
{%- when 'artist' -%}
{% render "fetchers/artist.php.liquid" %}
{%- when 'genre' -%}
{% render "fetchers/genre.php.liquid" %}
{%- when 'book' -%}
{% render "fetchers/book.php.liquid" %}
{%- when 'movie' -%}
{% render "fetchers/movie.php.liquid" %}
{%- when 'show' -%}
{% render "fetchers/show.php.liquid" %}
{%- when 'blog' -%}
{%- assign pageTitle = post.title -%}
{%- assign pageDescription = post.description -%}
{%- assign ogImage = globals.cdn_url | append: post.image -%}
{%- when 'music-index', 'music-week-artists' -%}
{%- assign ogImage = globals.cdn_url | append: music.week.artists[0].grid.image -%}
{%- when 'music-week-albums', 'music-week-tracks' -%}
{%- assign ogImage = globals.cdn_url | append: music.week.albums[0].grid.image -%}
{%- when 'music-month-artists' -%}
{%- assign ogImage = globals.cdn_url | append: music.month.artists[0].grid.image -%}
{%- when 'music-month-albums' -%}
{%- assign ogImage = globals.cdn_url | append: music.month.albums[0].grid.image -%}
{%- when 'music-releases' -%}
{%- assign ogImage = globals.cdn_url | append: albumReleases.upcoming[0].grid.image -%}
{%- when 'books' -%}
{%- assign overviewBook = books.all | filterBooksByStatus: 'started' | reverse | first %}
{%- assign ogImage = globals.cdn_url | append: overviewBook.image -%}
{%- when 'books-year' -%}
{%- assign pageTitle = 'Books' | append: ' • ' | append: year.value | append: ' • ' | append: globals.site_name -%}
{%- capture pageDescription -%}
Here's what I read in {{ year.value }}.
{%- endcapture -%}
{%- assign bookData = year.data | filterBooksByStatus: 'finished' -%}
{%- assign bookYear = bookData | shuffleArray | first -%}
{%- assign ogImage = globals.cdn_url | append: bookYear.image -%}
{%- when 'favorite-movies' -%}
{%- assign favoriteMovie = movies.favorites | shuffleArray | first %}
{%- assign ogImage = globals.cdn_url | append: favoriteMovie.backdrop -%}
{%- when 'favorite-shows' -%}
{%- assign favoriteShow = tv.favorites | shuffleArray | first %}
{%- assign ogImage = globals.cdn_url | append: favoriteShow.backdrop -%}
{%- when 'watching' -%}
{%- assign overviewMovie = movies.recentlyWatched | first %}
{%- assign ogImage = globals.cdn_url | append: overviewMovie.backdrop -%}
{%- when 'upcoming-shows' -%}
{%- assign upcomingShow = upcomingShows.watching | shuffleArray | first %}
{%- assign ogImage = globals.cdn_url | append: upcomingShow.backdrop -%}
{%- when 'page' -%}
{%- assign pageDescription = page.description -%}
{% endcase %}
{%- if type == 'dynamic' -%}
{% render "metadata/dynamic.php.liquid"
fullUrl:fullUrl,
pageTitle:pageTitle,
pageDescription:pageDescription,
ogImage:ogImage,
globals:globals,
%}
{%- else -%}
{% render "metadata/static.liquid"
fullUrl:fullUrl,
pageTitle:pageTitle,
pageDescription:pageDescription
ogImage:ogImage,
globals:globals,
%}
{%- endif -%}
{% render "metadata/base.liquid"
pageTitle:pageTitle,
globals:globals,
eleventy:eleventy,
appVersion:appVersion,
%}

View file

@ -0,0 +1,9 @@
{%- assign description = pageDescription | markdown | strip_html | htmlTruncate | escape -%}
<title>{{- pageTitle -}}</title>
<meta property="og:title" content="{{- pageTitle -}}" />
<meta name="description" content="{{- description -}}" />
<meta property="og:description" content="{{- description -}}" />
<meta property="og:type" content="article" />
<meta property="og:url" content="{{ fullUrl }}" />
<link rel="canonical" href="{{ fullUrl }}" />
<meta property="og:image" content="{{ ogImage }}?class=w800" />

View file

@ -0,0 +1,30 @@
{%- assign categoryUrl = link.permalink | downcase -%}
{%- assign isHttp = categoryUrl contains "http" -%}
{%- if categoryUrl | isLinkActive:page.url -%}
<span
class="active {{ link.class }}"
aria-current="page"
>
{%- if link.icon -%}
{% tablericon link.icon %}
<span>{{ link.title }}</span>
{%- else -%}
{{ link.title }}
{%- endif -%}
</span>
{%- else -%}
<a
class="{% if link.icon %}{{ link.icon | downcase }} icon {% endif %}{{ link.class }}"
href="{{ categoryUrl }}"
{% if isHttp -%} rel="me" {%- endif %}
title="{{ link.title }}"
aria-label="{{ link.title }}"
>
{%- if link.icon -%}
{% tablericon link.icon %}
<span>{{ link.title }}</span>
{%- else -%}
{{ link.title }}
{%- endif -%}
</a>
{%- endif -%}

View file

@ -0,0 +1,47 @@
{%- assign pageCount = pagination.pages.size | default:0 -%}
{%- assign hidePagination = pageCount <= 1 -%}
{%- unless hidePagination -%}
<script type="module" src="/assets/scripts/components/select-pagination.js?v={% appVersion %}" defer></script>
<nav aria-label="Pagination" class="pagination">
{%- assign prevHref = pagination.href.previous -%}
{%- assign nextHref = pagination.href.next -%}
{% if prevHref %}
<a
href="{{ prevHref }}"
aria-label="Previous page"
>
{% tablericon "arrow-left" %}
</a>
{% else %}
<span>
{% tablericon "arrow-left" %}
</span>
{% endif %}
<select-pagination>
<select class="client-side" aria-label="Page selection">
{%- for pageEntry in pagination.pages -%}
<option value="{{ forloop.index | minus:1 }}">
{{ forloop.index }} of {{ pagination.links.size }}
</option>
{%- endfor -%}
</select>
<noscript>
<p>
<span aria-current="page">{{ pagination.pageNumber | plus:1 }}</span> of {{ pagination.links.size }}
</p>
</noscript>
</select-pagination>
{% if nextHref %}
<a
href="{{ nextHref }}"
aria-label="Next page"
>
{% tablericon "arrow-right" %}
</a>
{% else %}
<span>
{% tablericon "arrow-right" %}
</span>
{% endif %}
</nav>
{%- endunless -%}

View file

@ -0,0 +1,12 @@
<nav class="primary-navigation">
<ul class="nav-list">
{%- for link in nav.primary -%}
<li>
{% render "nav/link.liquid",
page:page,
link:link
%}
</li>
{%- endfor -%}
</ul>
</nav>

View file

@ -0,0 +1,9 @@
<nav aria-label="Secondary site navigation" class="sub-pages">
{%- for link in links -%}
{% render "nav/link.liquid",
page:page,
link:link
%}
{% unless forloop.last %}<span>•</span>{% endunless %}
{%- endfor -%}
</nav>

View file

@ -0,0 +1,8 @@
<nav aria-label="Social icons" class="social">
{%- for link in links -%}
{% render "nav/link.liquid",
page:page,
link:link
%}
{%- endfor -%}
</nav>