feat(route): secretsanfrancisco.com (#19853)

* feat(route): secretsanfrancisco.com

* style: auto format

* chore: use feed link to fetch list

* refactor: use wp api to fetch content

* chore: rename section to category

* feat: caption

* chore: cr

* chore: cache first category

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: yifan.wang1004 <yifan.wang1004@bytedance.com>
This commit is contained in:
Ethan 2025-08-24 12:49:56 -07:00 committed by GitHub
parent ed425d65bf
commit 8d4c4d6fbc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 117 additions and 0 deletions

View File

@ -0,0 +1,7 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'Secret San francisco',
url: 'secretsanfrancisco.com',
lang: 'en',
};

View File

@ -0,0 +1,101 @@
import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { load } from 'cheerio';
import cache from '@/utils/cache';
export const route: Route = {
path: '/:category?',
categories: ['new-media'],
example: '/secretsanfrancisco/top-news',
parameters: { category: 'category name, can be found in url' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['secretsanfrancisco.com/:category'],
target: '/:category',
},
],
name: 'Category',
maintainers: ['EthanWng97'],
handler,
};
async function handler(ctx) {
const baseUrl = 'https://secretsanfrancisco.com';
const categoryApiPath = '/wp-json/wp/v2/categories';
const postApiPath = '/wp-json/wp/v2/posts';
// get category number
const categorySlug = ctx.req.param('category') || '';
let category;
if (categorySlug) {
category = await cache.tryGet(`${baseUrl}${categoryApiPath}`, async () => {
const { data } = await got(`${baseUrl}${categoryApiPath}`, {
searchParams: { slug: categorySlug },
});
if (!data || data.length === 0) {
throw new Error(`Category "${categorySlug}" not found`);
}
return data[0];
});
}
const categoryId = category?.id;
const categoryName = category?.name;
const categoryLink = category?.link;
// get posts
const postsUrl = `${baseUrl}${postApiPath}`;
const postsResponse = await got(postsUrl, {
searchParams: {
per_page: ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10,
_embed: '',
...(categoryId && { categories: categoryId }),
},
});
const items = postsResponse.data
.filter((item) => item.language === 'en')
.map((item) => {
const featuredMedia = item._embedded?.['wp:featuredmedia']?.find((v) => v.id === item.featured_media);
const image = featuredMedia?.source_url;
const altText = featuredMedia?.alt_text || featuredMedia?.title?.rendered;
let caption;
if (featuredMedia?.caption?.rendered) {
caption = load(featuredMedia?.caption?.rendered);
}
const single = {
title: item.title.rendered,
description: art(path.join(__dirname, 'templates/description.art'), {
content: item.content.rendered,
image,
altText,
caption: caption?.text() || '',
}),
link: item.link,
pubDate: parseDate(item.date_gmt),
updated: parseDate(item.modified_gmt),
image,
author: item._embedded.author[0].name,
};
return single;
});
return {
title: categoryName ? `Secret San Francisco - ${categoryName}` : 'Secret San Francisco',
link: categoryLink || `${baseUrl}/${categorySlug}`,
item: items,
};
}

View File

@ -0,0 +1,9 @@
{{ if image }}
<figure>
<img src="{{ image }}" alt="{{ altText }}">
<figcaption>{{ caption }}</figcaption>
</figure>
{{ /if }}
{{ if content }}
{{@ content }}
{{ /if }}