fix(experimental): use new RT API (sorta) (#3179)

pull/3184/head
Ryan Cohen 1 year ago committed by GitHub
parent 76a7ceb758
commit 357cab87ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -69,6 +69,30 @@ class ExternalAPI {
return response.data; return response.data;
} }
protected async post<T>(
endpoint: string,
data: Record<string, unknown>,
config?: AxiosRequestConfig,
ttl?: number
): Promise<T> {
const cacheKey = this.serializeCacheKey(endpoint, {
config: config?.params,
data,
});
const cachedItem = this.cache?.get<T>(cacheKey);
if (cachedItem) {
return cachedItem;
}
const response = await this.axios.post<T>(endpoint, data, config);
if (this.cache) {
this.cache.set(cacheKey, response.data, ttl ?? DEFAULT_TTL);
}
return response.data;
}
protected async getRolling<T>( protected async getRolling<T>(
endpoint: string, endpoint: string,
config?: AxiosRequestConfig, config?: AxiosRequestConfig,

@ -1,28 +1,40 @@
import cacheManager from '@server/lib/cache'; import cacheManager from '@server/lib/cache';
import { getSettings } from '@server/lib/settings';
import ExternalAPI from './externalapi'; import ExternalAPI from './externalapi';
interface RTSearchResult { interface RTAlgoliaSearchResponse {
meterClass: 'certified_fresh' | 'fresh' | 'rotten'; results: {
meterScore: number; hits: RTAlgoliaHit[];
url: string; index: 'content_rt' | 'people_rt';
}[];
} }
interface RTTvSearchResult extends RTSearchResult { interface RTAlgoliaHit {
emsId: string;
emsVersionId: string;
tmsId: string;
type: string;
title: string; title: string;
startYear: number; titles: string[];
endYear: number; description: string;
} releaseYear: string;
interface RTMovieSearchResult extends RTSearchResult { rating: string;
name: string; genres: string[];
url: string; updateDate: string;
year: number; isEmsSearchable: boolean;
} rtId: number;
vanity: string;
interface RTMultiSearchResponse { aka: string[];
tvCount: number; posterImageUrl: string;
tvSeries: RTTvSearchResult[]; rottenTomatoes: {
movieCount: number; audienceScore: number;
movies: RTMovieSearchResult[]; criticsIconUrl: string;
wantToSeeCount: number;
audienceIconUrl: string;
scoreSentiment: string;
certifiedFresh: boolean;
criticsScore: number;
};
} }
export interface RTRating { export interface RTRating {
@ -47,13 +59,20 @@ export interface RTRating {
*/ */
class RottenTomatoes extends ExternalAPI { class RottenTomatoes extends ExternalAPI {
constructor() { constructor() {
const settings = getSettings();
super( super(
'https://www.rottentomatoes.com/api/private', 'https://79frdp12pn-dsn.algolia.net/1/indexes/*',
{}, {
'x-algolia-agent':
'Algolia%20for%20JavaScript%20(4.14.3)%3B%20Browser%20(lite)',
'x-algolia-api-key': '175588f6e5f8319b27702e4cc4013561',
'x-algolia-application-id': '79FRDP12PN',
},
{ {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Accept: 'application/json', Accept: 'application/json',
'x-algolia-usertoken': settings.clientId,
}, },
nodeCache: cacheManager.getCache('rt').data, nodeCache: cacheManager.getCache('rt').data,
} }
@ -61,14 +80,11 @@ class RottenTomatoes extends ExternalAPI {
} }
/** /**
* Search the 1.0 api for the movie title * Search the RT algolia api for the movie title
* *
* We compare the release date to make sure its the correct * We compare the release date to make sure its the correct
* match. But it's not guaranteed to have results. * 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 name Movie name
* @param year Release Year * @param year Release Year
*/ */
@ -77,30 +93,45 @@ class RottenTomatoes extends ExternalAPI {
year: number year: number
): Promise<RTRating | null> { ): Promise<RTRating | null> {
try { try {
const data = await this.get<RTMultiSearchResponse>('/v2.0/search/', { const data = await this.post<RTAlgoliaSearchResponse>('/queries', {
params: { q: name, limit: 10 }, requests: [
{
indexName: 'content_rt',
query: name,
params: 'filters=isEmsSearchable%20%3D%201&hitsPerPage=20',
},
],
}); });
const contentResults = data.results.find((r) => r.index === 'content_rt');
if (!contentResults) {
return null;
}
// First, attempt to match exact name and year // First, attempt to match exact name and year
let movie = data.movies.find( let movie = contentResults.hits.find(
(movie) => movie.year === year && movie.name === name (movie) => movie.releaseYear === year.toString() && movie.title === name
); );
// If we don't find a movie, try to match partial name and year // If we don't find a movie, try to match partial name and year
if (!movie) { if (!movie) {
movie = data.movies.find( movie = contentResults.hits.find(
(movie) => movie.year === year && movie.name.includes(name) (movie) =>
movie.releaseYear === year.toString() && movie.title.includes(name)
); );
} }
// If we still dont find a movie, try to match just on year // If we still dont find a movie, try to match just on year
if (!movie) { if (!movie) {
movie = data.movies.find((movie) => movie.year === year); movie = contentResults.hits.find(
(movie) => movie.releaseYear === year.toString()
);
} }
// One last try, try exact name match only // One last try, try exact name match only
if (!movie) { if (!movie) {
movie = data.movies.find((movie) => movie.name === name); movie = contentResults.hits.find((movie) => movie.title === name);
} }
if (!movie) { if (!movie) {
@ -108,16 +139,15 @@ class RottenTomatoes extends ExternalAPI {
} }
return { return {
title: movie.name, title: movie.title,
url: `https://www.rottentomatoes.com${movie.url}`, url: `https://www.rottentomatoes.com/m/${movie.vanity}`,
criticsRating: criticsRating: movie.rottenTomatoes.certifiedFresh
movie.meterClass === 'certified_fresh' ? 'Certified Fresh'
? 'Certified Fresh' : movie.rottenTomatoes.criticsScore >= 60
: movie.meterClass === 'fresh' ? 'Fresh'
? 'Fresh' : 'Rotten',
: 'Rotten', criticsScore: movie.rottenTomatoes.criticsScore,
criticsScore: movie.meterScore, year: Number(movie.releaseYear),
year: movie.year,
}; };
} catch (e) { } catch (e) {
throw new Error( throw new Error(
@ -131,14 +161,28 @@ class RottenTomatoes extends ExternalAPI {
year?: number year?: number
): Promise<RTRating | null> { ): Promise<RTRating | null> {
try { try {
const data = await this.get<RTMultiSearchResponse>('/v2.0/search/', { const data = await this.post<RTAlgoliaSearchResponse>('/queries', {
params: { q: name, limit: 10 }, requests: [
{
indexName: 'content_rt',
query: name,
params: 'filters=isEmsSearchable%20%3D%201&hitsPerPage=20',
},
],
}); });
let tvshow: RTTvSearchResult | undefined = data.tvSeries[0]; const contentResults = data.results.find((r) => r.index === 'content_rt');
if (!contentResults) {
return null;
}
let tvshow: RTAlgoliaHit | undefined = contentResults.hits[0];
if (year) { if (year) {
tvshow = data.tvSeries.find((series) => series.startYear === year); tvshow = contentResults.hits.find(
(series) => series.releaseYear === year.toString()
);
} }
if (!tvshow) { if (!tvshow) {
@ -147,10 +191,11 @@ class RottenTomatoes extends ExternalAPI {
return { return {
title: tvshow.title, title: tvshow.title,
url: `https://www.rottentomatoes.com${tvshow.url}`, url: `https://www.rottentomatoes.com/tv/${tvshow.vanity}`,
criticsRating: tvshow.meterClass === 'fresh' ? 'Fresh' : 'Rotten', criticsRating:
criticsScore: tvshow.meterScore, tvshow.rottenTomatoes.criticsScore >= 60 ? 'Fresh' : 'Rotten',
year: tvshow.startYear, criticsScore: tvshow.rottenTomatoes.criticsScore,
year: Number(tvshow.releaseYear),
}; };
} catch (e) { } catch (e) {
throw new Error(`[RT API] Failed to retrieve tv ratings: ${e.message}`); throw new Error(`[RT API] Failed to retrieve tv ratings: ${e.message}`);

Loading…
Cancel
Save