Begin combining feed and api

This commit is contained in:
Sam Becker 2025-06-12 16:31:45 -05:00
parent 0236461d99
commit ec698b61de
2 changed files with 91 additions and 53 deletions

View File

@ -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 });

View File

@ -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 ?
`<description>
return `<item>
<title>${photo.title}</title>
<link>${photo.link}</link>
<pubDate>${formattedDate}</pubDate>
<guid isPermaLink="true">${photo.link}</guid>
<description>
<![CDATA[${photo.description}]]>
</description>` : '';
return ` <item>
<title>${photo.title}</title>
<link>${photo.link}</link>
<pubDate>${formattedDate}</pubDate>
<guid isPermaLink="true">${photo.link}</guid>
${description}
<media:content url="${photo.media.content.url.replace(/&/g, '&amp;')}"
type="image/jpeg"
medium="image"
width="${photo.media.content.width}"
height="${photo.media.content.height}">
<media:thumbnail url="${photo.media.thumb.url.replace(/&/g, '&amp;')}"
width="${photo.media.thumb.width}"
height="${photo.media.thumb.height}" />
</media:content>
</item>`;
</description>
<media:content url="${photo.media.content.url.replace(/&/g, '&amp;')}"
type="image/jpeg"
medium="image"
width="${photo.media.content.width}"
height="${photo.media.content.height}">
<media:thumbnail url="${photo.media.thumb.url.replace(/&/g, '&amp;')}"
width="${photo.media.thumb.width}"
height="${photo.media.thumb.height}" />
</media:content>
</item>`;
};