feat: add shanxi museum temporary exhibition route (#22046)

* feat: add shanxi museum temporary exhibition route

* chore: trigger ci

* fix: remove pubDate if no return information

* fix: remove redundancy

* fix:fix the timezone error and refactor the resData and list

* fix: remove ! check for imgurl

* fix: remove empty message and add trycatch
This commit is contained in:
Jiamin 2026-06-02 22:18:47 +08:00 committed by GitHub
parent caafd00f46
commit f82a357623
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 150 additions and 0 deletions

View File

@ -0,0 +1,10 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'Shanxi Museum',
url: 'www.shanximuseum.com.cn',
zh: {
name: '山西博物馆',
},
};

View File

@ -0,0 +1,140 @@
import { renderToString } from 'hono/jsx/dom/server';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { namespace } from './namespace';
export const route = {
path: '/exhibition/temporary/:type?',
categories: ['travel'],
example: '/shanximuseum/exhibition/temporary/now&future', // default w/o type, shows all exhibitions (now, future and past)
parameters: {
type: 'Temporary Exhibition type, supported values: now 正在展出、future即将展出、now&future正在展出&即将展出、past往期展览。Supports multiple status combinations separated by &, + or , (e.g., now&future). Default: All exhibitions (now, future and past).',
},
name: 'Temporary Exhibitions',
maintainers: ['magazian'],
radar: [
{
source: ['www.shanximuseum.com.cn/sx/exhibition/temporary.html'],
target: '/exhibition/temporary',
},
],
handler: async (ctx) => {
const typeParam = ctx.req.param('type');
// Support multiple status combinations separated by &, + or , (e.g., now&future)
const fetchTypes = typeParam ? [...new Set(typeParam.split(/[&+,|-]/))] : ['now', 'future', 'past'];
const baseUrl = 'https://www.shanximuseum.com.cn';
const apiUrl = `${baseUrl}/sx/exhibition`;
const apiConfig = {
now: { endpoint: '/temporary_now', name: '正在展出', query: {} },
future: {
endpoint: '/temporary_future',
name: '即将展出',
query: {},
},
past: {
endpoint: '/temporary_list', // the same endpoint as the exhibition list page, but with different query parameters
name: '往期展览',
query: {
offset: 0,
count: 20,
year: '',
keyword: '',
},
},
};
const museumName = namespace.zh?.name || namespace.name;
const titleTag = fetchTypes.length === 3 ? '全部展览' : fetchTypes.map((t) => apiConfig[t as keyof typeof apiConfig]?.name).join(' & ');
const responses = await Promise.all(
fetchTypes.map(async (t) => {
const config = apiConfig[t as keyof typeof apiConfig];
// add trycatch to prevent one failed request, espacially for now/future exhibition.
try {
const response = await got({
method: 'get',
url: `${apiUrl}${config.endpoint}`,
searchParams: {
...config.query,
_: Date.now(),
},
});
const resData = response.data?.data; // The actual data is nested under the 'data' property in the response
const list = Array.isArray(resData?.list) ? resData.list : []; // resData may be undefined or not an array, resDate.list is an array
return list.map((item) => {
const title = item.title;
const itemLink = item.fullurl;
const pubDate = timezone(parseDate(item.publishtime * 1000)); // Unix timestamp in seconds, convert to milliseconds
const startDate = item.start_time.split(' ')[0];
const endDate = item.end_time.split(' ')[0];
const fullDuration = item.display_time;
const location = item.area;
const imgUrl = `${baseUrl}${item.image}`;
const description = renderToString(
<div>
<img src={imgUrl} />
<br />
<p>
<b></b>
{location || '参考展览图片'}
{/* location is only defined on the exhibition poster, cannot fetch directly from the website */}
</p>
<p>
<b></b>
{startDate || '未定/常设'}
</p>
<p>
<b></b>
{endDate || '未定/常设'}
</p>
{fullDuration && (
<p>
<small>{fullDuration}</small>
</p>
)}
</div>
);
return {
title,
link: itemLink,
pubDate,
description,
// For further .ics file processing
_extra: {
museumName,
title,
location,
startDate, // format: YYYY-MM-DD or '未定/常设'
endDate, // format: YYYY-MM-DD or '未定/常设'
itemLink,
},
};
});
} catch {
return [];
}
})
);
const items = responses.flat();
return {
title: `${museumName} - 临时展览 - ${titleTag}`,
link: `${baseUrl}/sx/exhibition/temporary.html`,
language: 'zh-CN',
item: items,
};
},
};