feat(route): imdb top charts (#15697)
* feat(route): imdb top charts * fix: typo
This commit is contained in:
parent
fe683a11e2
commit
bc3b3e21cd
|
|
@ -0,0 +1,61 @@
|
|||
import { Route } from '@/types';
|
||||
import ofetch from '@/utils/ofetch';
|
||||
import * as cheerio from 'cheerio';
|
||||
import type { Context } from 'hono';
|
||||
import { ChartTitleSearchConnection } from './types';
|
||||
import path from 'path';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
import { art } from '@/utils/render';
|
||||
|
||||
const __dirname = getCurrentPath(import.meta.url);
|
||||
const render = (data) => art(path.join(__dirname, 'templates', 'chart.art'), data);
|
||||
|
||||
export const route: Route = {
|
||||
path: '/chart/:chart?',
|
||||
categories: ['multimedia'],
|
||||
parameters: { chart: 'The chart to display, `top` by default' },
|
||||
example: '/imdb/chart',
|
||||
radar: [
|
||||
{
|
||||
source: ['www.imdb.com/chart/:chart/'],
|
||||
},
|
||||
],
|
||||
name: 'Charts',
|
||||
maintainers: ['TonyRL'],
|
||||
handler,
|
||||
url: 'www.imdb.com/chart/top/',
|
||||
description: `| Top 250 Movies | Most Popular Movies | Top 250 TV Shows | Most Popular TV Shows |
|
||||
| -------------- | ------------------- | ---------------- | --------------------- |
|
||||
| top | moviemeter | toptv | tvmeter |`,
|
||||
};
|
||||
|
||||
async function handler(ctx: Context) {
|
||||
const { chart = 'top' } = ctx.req.param();
|
||||
const baseUrl = 'https://www.imdb.com';
|
||||
const link = `${baseUrl}/chart/${chart}/`;
|
||||
|
||||
const response = await ofetch(link);
|
||||
const $ = cheerio.load(response);
|
||||
const nextData = JSON.parse($('script#__NEXT_DATA__').text());
|
||||
const chartTitles = nextData.props.pageProps.pageData.chartTitles as ChartTitleSearchConnection;
|
||||
|
||||
const items = chartTitles.edges.map(({ currentRank, node }) => ({
|
||||
title: `${currentRank}. ${node.titleText.text} (${node.releaseYear.year}${node.releaseYear.endYear ? `-${node.releaseYear.endYear}` : ''})`,
|
||||
description: render({
|
||||
primaryImage: node.primaryImage,
|
||||
originalTitleText: node.originalTitleText,
|
||||
certificate: node.certificate,
|
||||
ratingsSummary: node.ratingsSummary,
|
||||
plot: node.plot,
|
||||
}),
|
||||
link: `${baseUrl}/title/${node.id}`,
|
||||
category: node.titleGenres.genres.map((g) => chartTitles.genres.find((genre) => genre.filterId === g.genre.text)?.text),
|
||||
}));
|
||||
|
||||
return {
|
||||
title: $('head title').text(),
|
||||
description: $('head meta[name="description"]').attr('content'),
|
||||
link,
|
||||
item: items,
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import type { Namespace } from '@/types';
|
||||
|
||||
export const namespace: Namespace = {
|
||||
name: 'IMDb',
|
||||
url: 'www.imdb.com',
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{{ if primaryImage.url }}
|
||||
<figure>
|
||||
<img src="{{ primaryImage.url }}" alt="{{ primaryImage.caption.plainText }}" />
|
||||
<figcaption>{{ primaryImage.caption.plainText }}</figcaption>
|
||||
</figure>
|
||||
<br>
|
||||
{{ /if }}
|
||||
|
||||
Original title: {{ originalTitleText.text }}<br>
|
||||
|
||||
{{ if certificate }}{{ certificate.rating }}{{ /if }}
|
||||
{{ if ratingsSummary.aggregateRating }}IMDb RATING: {{ ratingsSummary.aggregateRating }}/10 ({{ ratingsSummary.voteCount }}){{ /if }}
|
||||
<br><br>
|
||||
|
||||
{{ plot.plotText.plainText }}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
interface SearchFacet {
|
||||
filterId: string;
|
||||
text: string;
|
||||
total: number;
|
||||
__typename: string;
|
||||
}
|
||||
|
||||
interface TitleGenres {
|
||||
genre: {
|
||||
text: string;
|
||||
__typename: string;
|
||||
};
|
||||
__typename: string;
|
||||
}
|
||||
|
||||
interface Title {
|
||||
id: string;
|
||||
titleText: {
|
||||
text: string;
|
||||
__typename: string;
|
||||
};
|
||||
titleType: {
|
||||
id: string;
|
||||
text: string;
|
||||
canHaveEpisodes: boolean;
|
||||
displayableProperty: {
|
||||
value: {
|
||||
plainText: string;
|
||||
__typename: string;
|
||||
};
|
||||
__typename: string;
|
||||
};
|
||||
__typename: string;
|
||||
};
|
||||
originalTitleText: {
|
||||
text: string;
|
||||
__typename: string;
|
||||
};
|
||||
primaryImage: {
|
||||
id: string;
|
||||
width: number;
|
||||
height: number;
|
||||
url: string;
|
||||
caption: {
|
||||
plainText: string;
|
||||
__typename: string;
|
||||
};
|
||||
__typename: string;
|
||||
};
|
||||
releaseYear: {
|
||||
year: number;
|
||||
endYear: number | null;
|
||||
__typename: string;
|
||||
};
|
||||
ratingsSummary: {
|
||||
aggregateRating: number;
|
||||
voteCount: number;
|
||||
__typename: string;
|
||||
};
|
||||
runtime: {
|
||||
seconds: number;
|
||||
__typename: string;
|
||||
};
|
||||
certificate: {
|
||||
rating: string;
|
||||
__typename: string;
|
||||
} | null;
|
||||
canRate: {
|
||||
isRatable: boolean;
|
||||
__typename: string;
|
||||
};
|
||||
titleGenres: {
|
||||
genres: TitleGenres[];
|
||||
__typename: string;
|
||||
};
|
||||
canHaveEpisodes: boolean;
|
||||
plot: {
|
||||
plotText: {
|
||||
plainText: string;
|
||||
__typename: string;
|
||||
};
|
||||
__typename: string;
|
||||
};
|
||||
latestTrailer: {
|
||||
id: string;
|
||||
__typename: string;
|
||||
} | null;
|
||||
series: null;
|
||||
__typename: string;
|
||||
}
|
||||
|
||||
interface ChartTitleEdge {
|
||||
currentRank: number;
|
||||
node: Title;
|
||||
__typename: string;
|
||||
}
|
||||
|
||||
export interface ChartTitleSearchConnection {
|
||||
edges: ChartTitleEdge[];
|
||||
genres: SearchFacet[];
|
||||
keywords: SearchFacet[];
|
||||
__typename: string;
|
||||
}
|
||||
Loading…
Reference in New Issue