parent
236c4e5e61
commit
1694f60e8a
@ -0,0 +1,150 @@
|
|||||||
|
import axios, { AxiosInstance } from 'axios';
|
||||||
|
|
||||||
|
interface RTMovieOldSearchResult {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
year: number;
|
||||||
|
ratings: {
|
||||||
|
critics_rating: 'Certified Fresh' | 'Fresh' | 'Rotten';
|
||||||
|
critics_score: number;
|
||||||
|
audience_rating: 'Upright' | 'Spilled';
|
||||||
|
audience_score: number;
|
||||||
|
};
|
||||||
|
links: {
|
||||||
|
self: string;
|
||||||
|
alternate: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RTTvSearchResult {
|
||||||
|
title: string;
|
||||||
|
meterClass: 'fresh' | 'rotten';
|
||||||
|
meterScore: number;
|
||||||
|
url: string;
|
||||||
|
startYear: number;
|
||||||
|
endYear: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RTMovieSearchResponse {
|
||||||
|
total: number;
|
||||||
|
movies: RTMovieOldSearchResult[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RTMultiSearchResponse {
|
||||||
|
tvCount: number;
|
||||||
|
tvSeries: RTTvSearchResult[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RTRating {
|
||||||
|
title: string;
|
||||||
|
year: number;
|
||||||
|
criticsRating: 'Certified Fresh' | 'Fresh' | 'Rotten';
|
||||||
|
criticsScore: number;
|
||||||
|
audienceRating?: 'Upright' | 'Spilled';
|
||||||
|
audienceScore?: number;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a best-effort API. The Rotten Tomatoes API is technically
|
||||||
|
* private and getting access costs money/requires approval.
|
||||||
|
*
|
||||||
|
* They do, however, have a "public" api that they use to request the
|
||||||
|
* data on their own site. We use this to get ratings for movies/tv shows.
|
||||||
|
*
|
||||||
|
* Unfortunately, we need to do it by searching for the movie name, so it's
|
||||||
|
* not always accurate.
|
||||||
|
*/
|
||||||
|
class RottenTomatoes {
|
||||||
|
private axios: AxiosInstance;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.axios = axios.create({
|
||||||
|
baseURL: 'https://www.rottentomatoes.com/api/private',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the 1.0 api for the movie title
|
||||||
|
*
|
||||||
|
* We compare the release date to make sure its the correct
|
||||||
|
* match. But it's not guaranteed to have results.
|
||||||
|
*
|
||||||
|
* We use the 1.0 API here because the 2.0 search api does
|
||||||
|
* not return audience ratings.
|
||||||
|
*
|
||||||
|
* @param name Movie name
|
||||||
|
* @param year Release Year
|
||||||
|
*/
|
||||||
|
public async getMovieRatings(
|
||||||
|
name: string,
|
||||||
|
year: number
|
||||||
|
): Promise<RTRating | null> {
|
||||||
|
try {
|
||||||
|
const response = await this.axios.get<RTMovieSearchResponse>(
|
||||||
|
'/v1.0/movies',
|
||||||
|
{
|
||||||
|
params: { q: name },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const movie = response.data.movies.find((movie) => movie.year === year);
|
||||||
|
|
||||||
|
if (!movie) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: movie.title,
|
||||||
|
url: movie.links.alternate,
|
||||||
|
criticsRating: movie.ratings.critics_rating,
|
||||||
|
criticsScore: movie.ratings.critics_score,
|
||||||
|
audienceRating: movie.ratings.audience_rating,
|
||||||
|
audienceScore: movie.ratings.audience_score,
|
||||||
|
year: movie.year,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`[RT API] Failed to retrieve movie ratings: ${e.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getTVRatings(
|
||||||
|
name: string,
|
||||||
|
year: number
|
||||||
|
): Promise<RTRating | null> {
|
||||||
|
try {
|
||||||
|
const response = await this.axios.get<RTMultiSearchResponse>(
|
||||||
|
'/v2.0/search/',
|
||||||
|
{
|
||||||
|
params: { q: name, limit: 10 },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const tvshow = response.data.tvSeries.find(
|
||||||
|
(series) => series.startYear === year
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tvshow) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: tvshow.title,
|
||||||
|
url: `https://www.rottentomatoes.com${tvshow.url}`,
|
||||||
|
criticsRating: tvshow.meterClass === 'fresh' ? 'Fresh' : 'Rotten',
|
||||||
|
criticsScore: tvshow.meterScore,
|
||||||
|
year: tvshow.startYear,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`[RT API] Failed to retrieve tv ratings: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RottenTomatoes;
|
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.9 KiB |
Loading…
Reference in new issue