diff --git a/app/feed.json/route.ts b/app/feed.json/route.ts
index 8b8f64f8..3422017e 100644
--- a/app/feed.json/route.ts
+++ b/app/feed.json/route.ts
@@ -1,11 +1,11 @@
import { getPhotosCached } from '@/photo/cache';
import { INFINITE_SCROLL_FEED_INITIAL } from '@/photo';
-import { formatPhotoForFeed } from '@/app/feed';
import {
BASE_URL,
PUBLIC_FEED_ENABLED,
META_TITLE,
} from '@/app/config';
+import { formatPhotoForFeedJson } from '@/app/feed';
export const dynamic = 'force-static';
@@ -20,7 +20,7 @@ export async function GET() {
title: META_TITLE,
url: BASE_URL,
},
- photos: photos.map(formatPhotoForFeed),
+ photos: photos.map(formatPhotoForFeedJson),
});
} else {
return new Response('Feed disabled', { status: 404 });
diff --git a/src/app/feed.ts b/src/app/feed.ts
index 5ccbe104..ba16e94f 100644
--- a/src/app/feed.ts
+++ b/src/app/feed.ts
@@ -1,17 +1,31 @@
-import { Photo } from '@/photo';
+import { descriptionForPhoto, Photo, titleForPhoto } from '@/photo';
import { absolutePathForPhoto } from './paths';
-import { getNextImageUrlForRequest } from '@/platforms/next-image';
-import { formatDate } from '@/utility/date';
+import {
+ getNextImageUrlForRequest,
+ NextImageSize,
+} from '@/platforms/next-image';
+import { formatDate, formatDateFromPostgresString } from '@/utility/date';
-export const FEED_PHOTO_WIDTH_CONTENT = 1080;
-export const FEED_PHOTO_WIDTH_THUMB = 640;
+export const API_PHOTO_REQUEST_LIMIT = 40;
-export interface PublicFeed {
+export const FEED_PHOTO_WIDTH_SMALL = 200;
+export const FEED_PHOTO_WIDTH_MEDIUM = 640;
+export const FEED_PHOTO_WIDTH_LARGE = 1200;
+
+export interface PublicFeedJson {
meta: {
title: string
url: string
}
- photos: PublicFeedPhoto[]
+ photos: PublicFeedPhotoJson[]
+}
+
+export interface PublicFeedRss {
+ meta: {
+ title: string
+ url: string
+ }
+ photos: PublicFeedPhotoRss[]
}
interface PublicFeedMedia {
@@ -20,7 +34,21 @@ interface PublicFeedMedia {
height: number
}
-interface PublicFeedPhoto {
+interface PublicFeedPhotoJson {
+ id: string
+ title?: string
+ url: string
+ make?: string
+ model?: string
+ tags?: string[]
+ takenAtNaive: string
+ src: Record<
+ 'small' | 'medium' | 'large',
+ PublicFeedMedia
+ >
+}
+
+interface PublicFeedPhotoRss {
id: string
title?: string
description?: string
@@ -32,56 +60,66 @@ interface PublicFeedPhoto {
>
}
-export const formatPhotoForFeed = (photo: Photo): PublicFeedPhoto => ({
+const generateFeedMedia = (
+ photo: Photo,
+ size: NextImageSize,
+): PublicFeedMedia => ({
+ url: getNextImageUrlForRequest({ imageUrl: photo.url, size }),
+ width: size,
+ height: Math.round(size / photo.aspectRatio),
+});
+
+const getCoreFeedFields = (photo: Photo) => ({
id: photo.id,
- title: photo.title,
- description: photo.caption,
- link: absolutePathForPhoto({ photo }),
- publicationDate: photo.createdAt,
- media: {
- content: {
- url: getNextImageUrlForRequest({
- imageUrl: photo.url,
- size: FEED_PHOTO_WIDTH_CONTENT,
- }),
- width: FEED_PHOTO_WIDTH_CONTENT,
- height: Math.round(FEED_PHOTO_WIDTH_CONTENT / photo.aspectRatio),
- },
- thumb: {
- url: getNextImageUrlForRequest({
- imageUrl: photo.url,
- size: FEED_PHOTO_WIDTH_THUMB,
- }),
- width: FEED_PHOTO_WIDTH_THUMB,
- height: Math.round(FEED_PHOTO_WIDTH_THUMB / photo.aspectRatio),
- },
+ title: titleForPhoto(photo),
+ description: descriptionForPhoto(photo),
+});
+
+export const formatPhotoForFeedJson = (photo: Photo): PublicFeedPhotoJson => ({
+ ...getCoreFeedFields(photo),
+ url: absolutePathForPhoto({ photo }),
+ ...photo.make && { make: photo.make },
+ ...photo.model && { model: photo.model },
+ ...photo.tags.length > 0 && { tags: photo.tags },
+ takenAtNaive: formatDateFromPostgresString(photo.takenAtNaive),
+ src: {
+ small: generateFeedMedia(photo, FEED_PHOTO_WIDTH_SMALL),
+ medium: generateFeedMedia(photo, FEED_PHOTO_WIDTH_MEDIUM),
+ large: generateFeedMedia(photo, FEED_PHOTO_WIDTH_LARGE),
},
});
-export const feedPhotoToXml = (photo: PublicFeedPhoto): string => {
+export const formatPhotoForFeedRss = (photo: Photo): PublicFeedPhotoRss => ({
+ ...getCoreFeedFields(photo),
+ link: absolutePathForPhoto({ photo }),
+ publicationDate: photo.createdAt,
+ media: {
+ content: generateFeedMedia(photo, FEED_PHOTO_WIDTH_LARGE),
+ thumb: generateFeedMedia(photo, FEED_PHOTO_WIDTH_MEDIUM),
+ },
+});
+
+export const feedPhotoToXml = (photo: PublicFeedPhotoRss): string => {
const formattedDate = formatDate({
date: photo.publicationDate,
length: 'rss',
});
- const description = photo.description ?
- `
+ return `-
+ ${photo.title}
+ ${photo.link}
+ ${formattedDate}
+ ${photo.link}
+
- ` : '';
-
- return `
-
- ${photo.title}
- ${photo.link}
- ${formattedDate}
- ${photo.link}
- ${description}
-
-
-
-
`;
+
+
+
+
+ `;
};