Compare commits

...

2 commits

16 changed files with 216 additions and 29 deletions

68
api/oembed.php Normal file
View file

@ -0,0 +1,68 @@
<?php
require __DIR__ . "/Classes/BaseHandler.php";
use App\Classes\BaseHandler;
use GuzzleHttp\Client;
class OembedHandler extends BaseHandler
{
public function handleRequest(): void
{
$requestUrl = $_GET['url'] ?? null;
if (!$requestUrl) $this->sendErrorResponse('Missing url parameter', 400);
$parsed = parse_url($requestUrl);
$relativePath = $parsed['path'] ?? null;
if (!$relativePath) $this->sendErrorResponse('Invalid url', 400);
if ($relativePath !== '/' && str_ends_with($relativePath, '/')) $relativePath = rtrim($relativePath, '/');
$globals = $this->fetchGlobals();
$results = $this->fetchFromApi('optimized_oembed', 'url=eq.' . urlencode($relativePath));
if (!empty($results)) {
$item = $results[0];
$this->sendResponse([
'version' => '1.0',
'type' => 'link',
'title' => $item['title'],
'author_name' => $globals['author'],
'provider_name' => $globals['site_name'],
'provider_url' => $globals['url'],
'thumbnail_url' => $globals['url'] . $item['image_url'],
'html' => '<a href="' . htmlspecialchars($item['url']) . '">' . htmlspecialchars($item['title']) . '</a>',
]);
}
$segments = explode('/', trim($relativePath, '/'));
if (count($segments) === 1 && $segments[0] !== '') {
$title = ucwords(str_replace('-', ' ', $segments[0])) . ' • ' . $globals['author'];
$this->sendResponse([
'version' => '1.0',
'type' => 'link',
'title' => $title,
'author_name' => $globals['author'],
'provider_name' => $globals['site_name'],
'provider_url' => $globals['url'],
'thumbnail_url' => $globals['url'] . $globals['avatar'],
'html' => '<a href="' . htmlspecialchars($relativePath) . '">' . htmlspecialchars($title) . '</a>',
]);
}
$this->sendErrorResponse('No match found', 404);
}
private function fetchGlobals(): array
{
$globals = $this->fetchFromApi('optimized_globals', 'limit=1');
return $globals[0] ?? [
'author' => 'Cory Dransfeldt',
'site_name' => 'coryd.dev',
'url' => 'https://www.coryd.dev',
'avatar' => ''
];
}
}
$handler = new OembedHandler();
$handler->handleRequest();

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "coryd.dev", "name": "coryd.dev",
"version": "3.8.4", "version": "4.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "coryd.dev", "name": "coryd.dev",
"version": "3.8.4", "version": "4.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"html-minifier-terser": "7.2.0", "html-minifier-terser": "7.2.0",

View file

@ -1,6 +1,6 @@
{ {
"name": "coryd.dev", "name": "coryd.dev",
"version": "3.8.4", "version": "4.0.0",
"description": "The source for my personal site. Built using 11ty (and other tools).", "description": "The source for my personal site. Built using 11ty (and other tools).",
"type": "module", "type": "module",
"engines": { "engines": {

View file

@ -0,0 +1,87 @@
CREATE OR REPLACE VIEW optimized_oembed AS
WITH oembed_data AS (
SELECT
'post' AS type,
p.url::TEXT AS url,
p.title AS title,
p.description AS description,
CASE
WHEN df.filename_disk IS NOT NULL AND df.filename_disk != '' AND df.filename_disk != '/' THEN df.filename_disk
ELSE (SELECT g.avatar FROM optimized_globals g LIMIT 1)
END AS image_url,
p.date AS content_date
FROM optimized_posts p
LEFT JOIN directus_files df ON p.image = df.filename_disk
UNION ALL
SELECT
'book' AS type,
b.url::TEXT AS url,
b.title AS title,
b.description AS description,
CASE
WHEN b.image IS NOT NULL AND b.image != '' THEN b.image
ELSE (SELECT g.avatar FROM optimized_globals g LIMIT 1)
END AS image_url,
b.date_finished AS content_date
FROM optimized_books b
UNION ALL
SELECT
'artist' AS type,
ar.url::TEXT AS url,
ar.name AS title,
ar.description AS description,
CASE
WHEN ar.image IS NOT NULL AND ar.image != '' THEN ar.image
ELSE (SELECT g.avatar FROM optimized_globals g LIMIT 1)
END AS image_url,
CURRENT_TIMESTAMP AS content_date
FROM optimized_artists ar
UNION ALL
SELECT
'genre' AS type,
g.url::TEXT AS url,
g.name AS title,
g.description AS description,
CASE
WHEN top_artist.image IS NOT NULL AND top_artist.image != '' THEN top_artist.image
ELSE (SELECT g.avatar FROM optimized_globals g LIMIT 1)
END AS image_url,
CURRENT_TIMESTAMP AS content_date
FROM optimized_genres g
LEFT JOIN LATERAL (
SELECT ar.image
FROM optimized_artists ar
WHERE (ar.genre->>'id')::bigint = g.id
ORDER BY ar.total_plays DESC
LIMIT 1
) top_artist ON true
UNION ALL
SELECT
'show' AS type,
s.url::TEXT AS url,
s.title AS title,
s.description AS description,
CASE
WHEN s.image IS NOT NULL AND s.image != '' THEN s.image
ELSE (SELECT g.avatar FROM optimized_globals g LIMIT 1)
END AS image_url,
s.last_watched_at AS content_date
FROM optimized_shows s
UNION ALL
SELECT
'movie' AS type,
m.url::TEXT AS url,
m.title AS title,
m.description AS description,
CASE
WHEN m.image IS NOT NULL AND m.image != '' THEN m.image
ELSE (SELECT g.avatar FROM optimized_globals g LIMIT 1)
END AS image_url,
m.last_watched AS content_date
FROM optimized_movies m
)
SELECT
ROW_NUMBER() OVER (ORDER BY url) AS id,
*
FROM
oembed_data;

View file

@ -25,18 +25,18 @@ SELECT json_build_object(
THEN TO_CHAR(se.air_date, 'FMMM/FMDD/YY') THEN TO_CHAR(se.air_date, 'FMMM/FMDD/YY')
ELSE TO_CHAR(se.air_date, 'FMMM/FMDD') ELSE TO_CHAR(se.air_date, 'FMMM/FMDD')
END END
) )
FROM scheduled_episodes se FROM scheduled_episodes se
WHERE se.show_id = s.id WHERE se.show_id = s.id
AND se.status IN ('upcoming', 'aired') AND se.status IN ('upcoming', 'aired')
AND NOT EXISTS ( AND NOT EXISTS (
SELECT 1 FROM episodes e SELECT 1 FROM episodes e
WHERE e.show = s.id WHERE e.show = s.id
AND e.season_number = se.season_number AND e.season_number = se.season_number
AND e.episode_number = se.episode_number AND e.episode_number = se.episode_number
) )
ORDER BY se.season_number ASC, se.episode_number ASC ORDER BY se.season_number ASC, se.episode_number ASC
LIMIT 1), LIMIT 1),
(SELECT CONCAT( (SELECT CONCAT(
'S', e.season_number, 'E', e.episode_number, '', 'S', e.season_number, 'E', e.episode_number, '',
CASE CASE
@ -44,11 +44,11 @@ SELECT json_build_object(
THEN TO_CHAR(e.last_watched_at, 'FMMM/FMDD/YY') THEN TO_CHAR(e.last_watched_at, 'FMMM/FMDD/YY')
ELSE TO_CHAR(e.last_watched_at, 'FMMM/FMDD') ELSE TO_CHAR(e.last_watched_at, 'FMMM/FMDD')
END END
) )
FROM episodes e FROM episodes e
WHERE e.show = s.id WHERE e.show = s.id
ORDER BY e.last_watched_at DESC, e.season_number DESC, e.episode_number DESC ORDER BY e.last_watched_at DESC, e.season_number DESC, e.episode_number DESC
LIMIT 1), LIMIT 1),
s.year::text s.year::text
) )
) AS grid, ) AS grid,
@ -59,10 +59,10 @@ SELECT json_build_object(
WHERE se.show_id = s.id WHERE se.show_id = s.id
AND se.status IN ('upcoming', 'aired') AND se.status IN ('upcoming', 'aired')
AND NOT EXISTS ( AND NOT EXISTS (
SELECT 1 FROM episodes e SELECT 1 FROM episodes e
WHERE e.show = s.id WHERE e.show = s.id
AND e.season_number = se.season_number AND e.season_number = se.season_number
AND e.episode_number = se.episode_number AND e.episode_number = se.episode_number
) )
ORDER BY se.season_number ASC, se.episode_number ASC ORDER BY se.season_number ASC, se.episode_number ASC
LIMIT 1 LIMIT 1
@ -73,10 +73,10 @@ SELECT json_build_object(
WHERE se.show_id = s.id WHERE se.show_id = s.id
AND se.status IN ('upcoming', 'aired') AND se.status IN ('upcoming', 'aired')
AND NOT EXISTS ( AND NOT EXISTS (
SELECT 1 FROM episodes e SELECT 1 FROM episodes e
WHERE e.show = s.id WHERE e.show = s.id
AND e.season_number = se.season_number AND e.season_number = se.season_number
AND e.episode_number = se.episode_number AND e.episode_number = se.episode_number
) )
ORDER BY se.season_number ASC, se.episode_number ASC ORDER BY se.season_number ASC, se.episode_number ASC
LIMIT 1 LIMIT 1
@ -122,7 +122,24 @@ SELECT json_build_object(
'backdrop', CONCAT('/', df_backdrop.filename_disk), 'backdrop', CONCAT('/', df_backdrop.filename_disk),
'url', s.slug, 'url', s.slug,
'alt', CONCAT('Poster from ', s.title), 'alt', CONCAT('Poster from ', s.title),
'subtext', s.year::text 'subtext', COALESCE(
(
SELECT CONCAT(
'S', se.season_number, 'E', se.episode_number, '',
CASE
WHEN EXTRACT(YEAR FROM se.air_date) < EXTRACT(YEAR FROM CURRENT_DATE)
THEN TO_CHAR(se.air_date, 'FMMM/FMDD/YY')
ELSE TO_CHAR(se.air_date, 'FMMM/FMDD')
END
)
FROM scheduled_episodes se
WHERE se.show_id = s.id
AND se.status IN ('upcoming', 'aired')
ORDER BY se.season_number ASC, se.episode_number ASC
LIMIT 1
),
s.year::text
)
) AS grid ) AS grid
FROM shows s FROM shows s
LEFT JOIN directus_files df_art ON s.art = df_art.id LEFT JOIN directus_files df_art ON s.art = df_art.id

View file

@ -75,6 +75,7 @@
), 250); ), 250);
$ogImage = htmlspecialchars($artist["image"], ENT_QUOTES, "UTF-8"); $ogImage = htmlspecialchars($artist["image"], ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri; $fullUrl = "https://www.coryd.dev" . $requestUri;
$oembedUrl = "https://www.coryd.dev/oembed" . $requestUri;
ob_start(); ob_start();

View file

@ -98,6 +98,7 @@
), 250); ), 250);
$ogImage = htmlspecialchars($book["image"], ENT_QUOTES, "UTF-8"); $ogImage = htmlspecialchars($book["image"], ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri; $fullUrl = "https://www.coryd.dev" . $requestUri;
$oembedUrl = "https://www.coryd.dev/oembed" . $requestUri;
ob_start(); ob_start();

View file

@ -66,6 +66,7 @@
$pageDescription = truncateText(htmlspecialchars(strip_tags($genre["description"]), ENT_QUOTES, "UTF-8"), 250); $pageDescription = truncateText(htmlspecialchars(strip_tags($genre["description"]), ENT_QUOTES, "UTF-8"), 250);
$ogImage = htmlspecialchars($genre["artists"][0]["image"], ENT_QUOTES, "UTF-8"); $ogImage = htmlspecialchars($genre["artists"][0]["image"], ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri; $fullUrl = "https://www.coryd.dev" . $requestUri;
$oembedUrl = "https://www.coryd.dev/oembed" . $requestUri;
ob_start(); ob_start();

View file

@ -75,6 +75,7 @@
), 250); ), 250);
$ogImage = htmlspecialchars($movie["backdrop"], ENT_QUOTES, "UTF-8"); $ogImage = htmlspecialchars($movie["backdrop"], ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri; $fullUrl = "https://www.coryd.dev" . $requestUri;
$oembedUrl = "https://www.coryd.dev/oembed" . $requestUri;
ob_start(); ob_start();

View file

@ -66,6 +66,7 @@
$pageDescription = truncateText(htmlspecialchars(strip_tags($show["description"]), ENT_QUOTES, "UTF-8"), 250); $pageDescription = truncateText(htmlspecialchars(strip_tags($show["description"]), ENT_QUOTES, "UTF-8"), 250);
$ogImage = htmlspecialchars($show["image"], ENT_QUOTES, "UTF-8"); $ogImage = htmlspecialchars($show["image"], ENT_QUOTES, "UTF-8");
$fullUrl = "https://www.coryd.dev" . $requestUri; $fullUrl = "https://www.coryd.dev" . $requestUri;
$oembedUrl = "https://www.coryd.dev/oembed" . $requestUri;
ob_start(); ob_start();

View file

@ -99,6 +99,7 @@ $pagination = [
$pageTitle = "#" . strtolower(ucfirst($tag)) . ""; $pageTitle = "#" . strtolower(ucfirst($tag)) . "";
$pageDescription = "All content tagged with #" . strtolower(ucfirst($tag)) . "."; $pageDescription = "All content tagged with #" . strtolower(ucfirst($tag)) . ".";
$fullUrl = "https://www.coryd.dev" . $requestUri; $fullUrl = "https://www.coryd.dev" . $requestUri;
$oembedUrl = "https://www.coryd.dev/oembed" . $requestUri;
ob_start(); ob_start();

View file

@ -2,6 +2,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light dark" /> <meta name="color-scheme" content="light dark" />
<meta name="theme-color" content="{{ globals.theme_color }}" /> <meta name="theme-color" content="{{ globals.theme_color }}" />
<meta name="msapplication-TileColor" content="{{ globals.theme_color }}">
<meta name="fediverse:creator" content="{{ globals.mastodon }}" /> <meta name="fediverse:creator" content="{{ globals.mastodon }}" />
<meta name="generator" content="{{ eleventy.generator }}" /> <meta name="generator" content="{{ eleventy.generator }}" />
<link href="{{ globals.url }}/og/w50/{% appVersion %}{{ globals.avatar_transparent }}" rel="icon" sizes="any" /> <link href="{{ globals.url }}/og/w50/{% appVersion %}{{ globals.avatar_transparent }}" rel="icon" sizes="any" />

View file

@ -4,6 +4,7 @@
<meta property="og:description" content="<?= cleanMeta($pageDescription ?? '{{ pageDescription | escape }}') ?>" /> <meta property="og:description" content="<?= cleanMeta($pageDescription ?? '{{ pageDescription | escape }}') ?>" />
<meta property="og:image" content="{{ globals.url }}<?= cleanMeta("/og/w800/{% appVersion %}" . ($ogImage ?? '{{ ogImage }}')) ?>" /> <meta property="og:image" content="{{ globals.url }}<?= cleanMeta("/og/w800/{% appVersion %}" . ($ogImage ?? '{{ ogImage }}')) ?>" />
<meta property="og:url" content="<?= cleanMeta($fullUrl ?? '{{ fullUrl }}') ?>" /> <meta property="og:url" content="<?= cleanMeta($fullUrl ?? '{{ fullUrl }}') ?>" />
<link rel="alternate" type="application/json+oembed" href="<?= cleanMeta($oembedUrl ?? '{{ oembedUrl }}') ?>" title="<?= cleanMeta($pageTitle ?? '{{ pageTitle }}') ?> • {{ globals.site_name }}">
<link rel="canonical" href="<?= cleanMeta($fullUrl ?? '{{ fullUrl }}') ?>" /> <link rel="canonical" href="<?= cleanMeta($fullUrl ?? '{{ fullUrl }}') ?>" />
<?php if (!empty($pagination)): ?> <?php if (!empty($pagination)): ?>
<?php if (!empty($pagination['href']['next'])): ?> <?php if (!empty($pagination['href']['next'])): ?>

View file

@ -1,4 +1,5 @@
{%- assign fullUrl = globals.url | append: page.url -%} {%- assign fullUrl = globals.url | append: page.url -%}
{%- assign oembedUrl = globals.url | append: "/oembed" | append: page.url -%}
{%- capture pageTitle -%} {%- capture pageTitle -%}
{%- if page.title -%} {%- if page.title -%}
{{ page.title | append: ' • ' | append: globals.site_name }} {{ page.title | append: ' • ' | append: globals.site_name }}
@ -74,6 +75,7 @@
{%- if type == 'dynamic' -%} {%- if type == 'dynamic' -%}
{% render "metadata/dynamic.php.liquid" {% render "metadata/dynamic.php.liquid"
fullUrl:fullUrl, fullUrl:fullUrl,
oembedUrl:oembedUrl,
pageTitle:pageTitle, pageTitle:pageTitle,
pageDescription:pageDescription, pageDescription:pageDescription,
ogImage:ogImage, ogImage:ogImage,
@ -82,6 +84,7 @@
{%- else -%} {%- else -%}
{% render "metadata/static.liquid" {% render "metadata/static.liquid"
fullUrl:fullUrl, fullUrl:fullUrl,
oembedUrl:oembedUrl,
pageTitle:pageTitle, pageTitle:pageTitle,
pageDescription:pageDescription, pageDescription:pageDescription,
ogImage:ogImage, ogImage:ogImage,

View file

@ -5,5 +5,6 @@
<meta property="og:description" content="{{ description }}" /> <meta property="og:description" content="{{ description }}" />
<meta property="og:type" content="article" /> <meta property="og:type" content="article" />
<meta property="og:url" content="{{ fullUrl }}" /> <meta property="og:url" content="{{ fullUrl }}" />
<link rel="alternate" type="application/json+oembed" href="{{ oembedUrl }}" title="{{ pageTitle }}">
<link rel="canonical" href="{{ fullUrl }}" /> <link rel="canonical" href="{{ fullUrl }}" />
<meta property="og:image" content="{{ globals.url }}/og/w800/{% appVersion %}{{ globals.avatar }}" /> <meta property="og:image" content="{{ globals.url }}/og/w800/{% appVersion %}{{ globals.avatar }}" />

View file

@ -50,6 +50,9 @@ RewriteRule ^tags/([^/]+)(?:/([0-9]+))?/?$ tags/index.php [L]
## open graph assets ## open graph assets
RewriteRule ^og/([a-z0-9\-]+)/([\d\.]+)/([a-f0-9\-]+)\.([a-z0-9]+)$ /api/og-image.php?id=$3&class=$1&v=$2&extension=$4 [L] RewriteRule ^og/([a-z0-9\-]+)/([\d\.]+)/([a-f0-9\-]+)\.([a-z0-9]+)$ /api/og-image.php?id=$3&class=$1&v=$2&extension=$4 [L]
## oembed
RewriteRule ^oembed/(.*)$ /api/oembed.php?url=https://www.coryd.dev/$1 [L,QSA]
{% for redirect in redirects -%} {% for redirect in redirects -%}
Redirect {{ redirect.status_code | default: "301" }} {{ redirect.source_url }} {{ redirect.destination_url }} Redirect {{ redirect.status_code | default: "301" }} {{ redirect.source_url }} {{ redirect.destination_url }}
{% endfor -%} {% endfor -%}