From 3f388df502fcf5563da63a88c5b89b7c64ab428a Mon Sep 17 00:00:00 2001 From: TonyRL Date: Fri, 24 May 2024 20:01:42 +0000 Subject: [PATCH 1/2] feat(route): imdb top charts --- lib/routes/imdb/chart.ts | 61 ++++++++++++++++ lib/routes/imdb/namespace.ts | 6 ++ lib/routes/imdb/templates/chart.art | 15 ++++ lib/routes/imdb/types.ts | 103 ++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+) create mode 100644 lib/routes/imdb/chart.ts create mode 100644 lib/routes/imdb/namespace.ts create mode 100644 lib/routes/imdb/templates/chart.art create mode 100644 lib/routes/imdb/types.ts diff --git a/lib/routes/imdb/chart.ts b/lib/routes/imdb/chart.ts new file mode 100644 index 00000000000000..8aba17f44ca9fa --- /dev/null +++ b/lib/routes/imdb/chart.ts @@ -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', 'charts.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, + }; +} diff --git a/lib/routes/imdb/namespace.ts b/lib/routes/imdb/namespace.ts new file mode 100644 index 00000000000000..d4912d3469e349 --- /dev/null +++ b/lib/routes/imdb/namespace.ts @@ -0,0 +1,6 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'IMDb', + url: 'www.imdb.com', +}; diff --git a/lib/routes/imdb/templates/chart.art b/lib/routes/imdb/templates/chart.art new file mode 100644 index 00000000000000..57134175a511fa --- /dev/null +++ b/lib/routes/imdb/templates/chart.art @@ -0,0 +1,15 @@ +{{ if primaryImage.url }} +
+ {{ primaryImage.caption.plainText }} +
{{ primaryImage.caption.plainText }}
+
+
+{{ /if }} + +Original title: {{ originalTitleText.text }}
+ +{{ if certificate }}{{ certificate.rating }}{{ /if }} +{{ if ratingsSummary.aggregateRating }}IMDb RATING: {{ ratingsSummary.aggregateRating }}/10 ({{ ratingsSummary.voteCount }}){{ /if }} +

+ +{{ plot.plotText.plainText }} diff --git a/lib/routes/imdb/types.ts b/lib/routes/imdb/types.ts new file mode 100644 index 00000000000000..aa7e998ab7572a --- /dev/null +++ b/lib/routes/imdb/types.ts @@ -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: 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; +} From 671fee1459e55ee69a4b7d7a1863f5dc467992fc Mon Sep 17 00:00:00 2001 From: TonyRL Date: Fri, 24 May 2024 20:10:06 +0000 Subject: [PATCH 2/2] fix: typo --- lib/routes/imdb/chart.ts | 2 +- lib/routes/imdb/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/routes/imdb/chart.ts b/lib/routes/imdb/chart.ts index 8aba17f44ca9fa..0c935de309965b 100644 --- a/lib/routes/imdb/chart.ts +++ b/lib/routes/imdb/chart.ts @@ -8,7 +8,7 @@ import { getCurrentPath } from '@/utils/helpers'; import { art } from '@/utils/render'; const __dirname = getCurrentPath(import.meta.url); -const render = (data) => art(path.join(__dirname, 'templates', 'charts.art'), data); +const render = (data) => art(path.join(__dirname, 'templates', 'chart.art'), data); export const route: Route = { path: '/chart/:chart?', diff --git a/lib/routes/imdb/types.ts b/lib/routes/imdb/types.ts index aa7e998ab7572a..06ef24f3fc9484 100644 --- a/lib/routes/imdb/types.ts +++ b/lib/routes/imdb/types.ts @@ -49,7 +49,7 @@ interface Title { }; releaseYear: { year: number; - endYear: null; + endYear: number | null; __typename: string; }; ratingsSummary: {