metacritic-ts 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/lib/utils.ts","../src/lib/parser.ts","../src/lib/service.ts","../src/lib/types.ts"],"sourcesContent":["export { MetacriticService } from './lib/service'\r\nexport * from './lib/types'\r\n","export function getSimilarity(a: string, b: string): number {\r\n if (a === b) return 1\r\n if (!a || !b) return 0\r\n\r\n const str1 = a.toLowerCase()\r\n const str2 = b.toLowerCase()\r\n const distance = levenshteinDistance(str1, str2)\r\n const maxLength = Math.max(str1.length, str2.length)\r\n return (maxLength - distance) / maxLength\r\n}\r\n\r\nfunction levenshteinDistance(a: string, b: string): number {\r\n const matrix: number[][] = []\r\n for (let i = 0; i <= b.length; i++) {\r\n matrix[i] = [i]\r\n }\r\n for (let j = 0; j <= a.length; j++) {\r\n matrix[0][j] = j\r\n }\r\n for (let i = 1; i <= b.length; i++) {\r\n for (let j = 1; j <= a.length; j++) {\r\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\r\n matrix[i][j] = matrix[i - 1][j - 1]\r\n } else {\r\n matrix[i][j] = Math.min(\r\n matrix[i - 1][j - 1] + 1,\r\n Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1)\r\n )\r\n }\r\n }\r\n }\r\n return matrix[b.length][a.length]\r\n}\r\n","import { getSimilarity } from './utils'\r\nimport { MetacriticEntry, RecordType, MetacriticSearchEntry } from './types'\r\n\r\nexport function parseSearchJsonResult(jsonString: string, searchKey: string, minSimilarity: number, sortBySimilarity: boolean = true): MetacriticSearchEntry[] {\r\n try {\r\n const parsedData = JSON.parse(jsonString)\r\n const entries: MetacriticSearchEntry[] = []\r\n\r\n const itemsList = parsedData.components.find((component: any) => component.meta.componentName === 'search').data.items\r\n for (const item of itemsList) {\r\n const entry = mapSearchEntry(item, searchKey)\r\n\r\n if (entry.similarity < minSimilarity) {\r\n continue\r\n }\r\n entries.push(entry)\r\n }\r\n\r\n return sortBySimilarity ? entries.sort((a, b) => b.similarity - a.similarity) : entries\r\n } catch (error) {\r\n console.error('Error parsing JSON:', error)\r\n return []\r\n }\r\n}\r\n\r\nexport function parseDetailJsonResult(jsonString: string, must: boolean): MetacriticEntry | null {\r\n try {\r\n const parsedData = JSON.parse(jsonString)\r\n return mapDetailEntry(parsedData, must)\r\n } catch (error) {\r\n console.error('Error parsing JSON:', error)\r\n return null\r\n }\r\n}\r\n\r\nfunction mapSearchEntry(data: any, searchKey: string): MetacriticSearchEntry {\r\n return {\r\n id: data.id,\r\n recordType: data.typeId as RecordType,\r\n title: data.title,\r\n slug: data.slug,\r\n criticScore: data.criticScoreSummary.score,\r\n must: data.mustSee || data.mustWatch || data.mustPlay,\r\n similarity: getSimilarity(data.title, searchKey)\r\n }\r\n}\r\n\r\nfunction mapDetailEntry(data: any, must: boolean): MetacriticEntry {\r\n const product = data.components.find((component: any) => component.meta.componentName === 'product').data.item\r\n const criticScoreSummary = data.components.find((component: any) => component.meta.componentName === 'critic-score-summary').data.item\r\n const userScoreSummary = data.components.find((component: any) => component.meta.componentName === 'user-score-summary').data.item\r\n\r\n return {\r\n id: product.id,\r\n recordType: product.typeId as RecordType,\r\n title: product.title,\r\n slug: product.slug,\r\n must: must,\r\n userScore: {\r\n score: userScoreSummary.score,\r\n maxScore: userScoreSummary.max,\r\n sentiment: userScoreSummary.sentiment,\r\n count: {\r\n positive: userScoreSummary.positiveCount,\r\n neutral: userScoreSummary.neutralCount,\r\n negative: userScoreSummary.negativeCount,\r\n total: userScoreSummary.reviewCount,\r\n }\r\n },\r\n criticScore: {\r\n score: criticScoreSummary.score,\r\n maxScore: criticScoreSummary.max,\r\n sentiment: criticScoreSummary.sentiment,\r\n count: {\r\n positive: criticScoreSummary.positiveCount,\r\n neutral: criticScoreSummary.neutralCount,\r\n negative: criticScoreSummary.negativeCount,\r\n total: criticScoreSummary.reviewCount,\r\n }\r\n },\r\n }\r\n}\r\n","import { parseDetailJsonResult, parseSearchJsonResult } from './parser'\r\nimport UserAgent from 'user-agents'\r\nimport { MetacriticEntry, RecordType, MetacriticSearchEntry } from './types'\r\n\r\nclass SearchInformations {\r\n apiKey: string\r\n\r\n constructor(scriptContent: string) {\r\n this.apiKey = this.extractApiFromScript(scriptContent)\r\n }\r\n\r\n extractApiFromScript(scriptContent: any) {\r\n const apiKeyPattern = /apiKey=([^\"&]+)/\r\n let matches = scriptContent.match(apiKeyPattern)\r\n if (matches) {\r\n return matches[1]\r\n }\r\n }\r\n}\r\n\r\nexport class MetacriticService {\r\n private minSimilarity: number\r\n private apiKey?: string\r\n\r\n static REFERER_HEADER = 'https://www.metacritic.com/'\r\n static HOMEPAGE_URL = 'https://www.metacritic.com/'\r\n static BASE_URL = 'https://backend.metacritic.com/composer/metacritic/pages/'\r\n static SEARCH_URL = MetacriticService.BASE_URL + 'search/'\r\n\r\n constructor(minSimilarity: number = 0.5) {\r\n this.minSimilarity = minSimilarity\r\n }\r\n\r\n async search(searchKey: string, recordType?: RecordType, sortBySimilarity: boolean = true): Promise<MetacriticSearchEntry[]> {\r\n if (!searchKey) {\r\n return []\r\n }\r\n\r\n if (!this.apiKey) {\r\n const searchInfo = await MetacriticService.sendApiKeyRetrievalWebsiteRequest()\r\n if (searchInfo) {\r\n this.apiKey = searchInfo.apiKey\r\n } else {\r\n console.error('Failed to retrieve API key')\r\n return []\r\n }\r\n }\r\n\r\n const jsonResult = await MetacriticService.sendSearchWebRequest(searchKey, this.apiKey, recordType)\r\n if (jsonResult) {\r\n return parseSearchJsonResult(jsonResult, searchKey, this.minSimilarity, sortBySimilarity)\r\n }\r\n\r\n return []\r\n }\r\n\r\n async getDetail(searchKey: string, recordType: RecordType, sortBySimilarity: boolean = true): Promise<MetacriticEntry | null> {\r\n if (!searchKey) {\r\n return null\r\n }\r\n\r\n if (!this.apiKey) {\r\n const searchInfo = await MetacriticService.sendApiKeyRetrievalWebsiteRequest()\r\n if (searchInfo) {\r\n this.apiKey = searchInfo.apiKey\r\n } else {\r\n console.error('Failed to retrieve API key')\r\n return null\r\n }\r\n }\r\n\r\n const searchResult = await this.search(searchKey, recordType, sortBySimilarity)\r\n if (searchResult && searchResult.length > 0) {\r\n const detailResult = await MetacriticService.sendDetailWebRequest(searchResult[0].slug, this.apiKey, recordType)\r\n\r\n if (detailResult) {\r\n return parseDetailJsonResult(detailResult, searchResult[0].must)\r\n }\r\n }\r\n\r\n return null\r\n }\r\n\r\n static async sendSearchWebRequest(searchKey: string, apiKey: string, recordType?: RecordType) {\r\n const headers = this.getRequestHeaders()\r\n\r\n const searchUrl = new URL(MetacriticService.SEARCH_URL + encodeURI(searchKey) + '/web')\r\n\r\n searchUrl.searchParams.append('apiKey', apiKey)\r\n\r\n if (recordType) {\r\n searchUrl.searchParams.append('mcoTypeId', recordType.toString())\r\n }\r\n\r\n try {\r\n const response = await fetch(searchUrl, {\r\n headers: headers,\r\n method: 'GET',\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n return await response.text()\r\n }\r\n } catch (error) {\r\n console.error('Error fetching search results:', error)\r\n }\r\n }\r\n\r\n static async sendDetailWebRequest(slug: string, apiKey: string, recordType: RecordType) {\r\n const headers = this.getRequestHeaders()\r\n\r\n let baseUrl = MetacriticService.BASE_URL\r\n switch (recordType) {\r\n case RecordType.Game:\r\n baseUrl = baseUrl + 'games/'\r\n break\r\n case RecordType.Movie:\r\n baseUrl = baseUrl + 'movies/'\r\n break\r\n case RecordType.TVShow:\r\n baseUrl = baseUrl + 'shows/'\r\n break\r\n default:\r\n console.error('Unsupported record type for detail request:', recordType)\r\n return\r\n }\r\n\r\n const detailUrl = new URL(baseUrl + encodeURI(slug) + '/web')\r\n\r\n detailUrl.searchParams.append('apiKey', apiKey)\r\n\r\n try {\r\n const response = await fetch(detailUrl, {\r\n headers: headers,\r\n method: 'GET',\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n return await response.text()\r\n }\r\n } catch (error) {\r\n console.error('Error fetching detail result:', error)\r\n }\r\n }\r\n\r\n static getRequestHeaders() {\r\n const minimumHeaders = this.getMinimalRequestHeaders()\r\n return {\r\n ...minimumHeaders,\r\n 'Content-Type': 'application/json',\r\n 'Accept': '*/*',\r\n 'Referer': MetacriticService.REFERER_HEADER,\r\n }\r\n }\r\n\r\n static getMinimalRequestHeaders() {\r\n const userAgent = new UserAgent()\r\n return {\r\n 'User-Agent': userAgent.toString(),\r\n }\r\n }\r\n\r\n static async sendApiKeyRetrievalWebsiteRequest(): Promise<SearchInformations | null> {\r\n const headers = this.getMinimalRequestHeaders()\r\n try {\r\n const response = await fetch(MetacriticService.HOMEPAGE_URL, {\r\n headers: headers,\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n const html = await response.text()\r\n\r\n const scriptPattern = /<script[^>]*>([\\s\\S]*?)<\\/script>/g\r\n const scripts: string[] = []\r\n let match\r\n while ((match = scriptPattern.exec(html)) !== null) {\r\n scripts.push(match[1])\r\n }\r\n\r\n for (const script of scripts) {\r\n const searchInfo = new SearchInformations(script)\r\n if (searchInfo.apiKey) {\r\n return searchInfo\r\n }\r\n }\r\n }\r\n\r\n return null\r\n } catch (error) {\r\n console.error('Error fetching website:', error)\r\n return null\r\n }\r\n }\r\n}\r\n","export enum RecordType {\r\n TVShow = 1,\r\n Movie = 2,\r\n Game = 13,\r\n}\r\n\r\nexport type MetacriticSearchEntry = {\r\n id: number\r\n recordType: RecordType\r\n title: string\r\n slug: string\r\n must: boolean\r\n criticScore: number\r\n similarity: number\r\n}\r\n\r\nexport type MetacriticEntry = {\r\n id: number\r\n recordType: RecordType\r\n title: string\r\n slug: string\r\n must: boolean\r\n userScore: Score\r\n criticScore: Score\r\n}\r\n\r\ntype Score = {\r\n score: number\r\n maxScore: number\r\n sentiment: string\r\n count: {\r\n positive: number\r\n neutral: number\r\n negative: number\r\n total: number\r\n }\r\n}\r\n"],"mappings":"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","names":["RecordType","UserAgent"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/utils.ts","../src/lib/parser.ts","../src/lib/service.ts","../src/lib/types.ts"],"sourcesContent":["export { MetacriticService } from './lib/service'\r\nexport * from './lib/types'\r\n","export function getSimilarity(a: string, b: string): number {\r\n if (a === b) return 1\r\n if (!a || !b) return 0\r\n\r\n const str1 = a.toLowerCase()\r\n const str2 = b.toLowerCase()\r\n const distance = levenshteinDistance(str1, str2)\r\n const maxLength = Math.max(str1.length, str2.length)\r\n return (maxLength - distance) / maxLength\r\n}\r\n\r\nfunction levenshteinDistance(a: string, b: string): number {\r\n const matrix: number[][] = []\r\n for (let i = 0; i <= b.length; i++) {\r\n matrix[i] = [i]\r\n }\r\n for (let j = 0; j <= a.length; j++) {\r\n matrix[0][j] = j\r\n }\r\n for (let i = 1; i <= b.length; i++) {\r\n for (let j = 1; j <= a.length; j++) {\r\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\r\n matrix[i][j] = matrix[i - 1][j - 1]\r\n } else {\r\n matrix[i][j] = Math.min(\r\n matrix[i - 1][j - 1] + 1,\r\n Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1)\r\n )\r\n }\r\n }\r\n }\r\n return matrix[b.length][a.length]\r\n}\r\n","import { getSimilarity } from './utils'\r\nimport { MetacriticEntry, RecordType, MetacriticSearchEntry } from './types'\r\n\r\nexport function parseSearchJsonResult(jsonString: string, searchKey: string, minSimilarity: number, sortBySimilarity: boolean = true): MetacriticSearchEntry[] {\r\n try {\r\n const parsedData = JSON.parse(jsonString)\r\n const entries: MetacriticSearchEntry[] = []\r\n\r\n const itemsList = parsedData.components.find((component: any) => component.meta.componentName === 'search').data.items\r\n for (const item of itemsList) {\r\n const entry = mapSearchEntry(item, searchKey)\r\n\r\n if (entry.similarity < minSimilarity) {\r\n continue\r\n }\r\n entries.push(entry)\r\n }\r\n\r\n return sortBySimilarity ? entries.sort((a, b) => b.similarity - a.similarity) : entries\r\n } catch (error) {\r\n console.error('Error parsing JSON:', error)\r\n return []\r\n }\r\n}\r\n\r\nexport function parseDetailJsonResult(jsonString: string, must: boolean): MetacriticEntry | null {\r\n try {\r\n const parsedData = JSON.parse(jsonString)\r\n return mapDetailEntry(parsedData, must)\r\n } catch (error) {\r\n console.error('Error parsing JSON:', error)\r\n return null\r\n }\r\n}\r\n\r\nfunction mapSearchEntry(data: any, searchKey: string): MetacriticSearchEntry {\r\n return {\r\n id: data.id,\r\n recordType: data.typeId as RecordType,\r\n title: data.title,\r\n slug: data.slug,\r\n criticScore: data.criticScoreSummary.score,\r\n must: data.mustSee || data.mustWatch || data.mustPlay,\r\n similarity: getSimilarity(data.title, searchKey)\r\n }\r\n}\r\n\r\nfunction mapDetailEntry(data: any, must: boolean): MetacriticEntry {\r\n const product = data.components.find((component: any) => component.meta.componentName === 'product').data.item\r\n const criticScoreSummary = data.components.find((component: any) => component.meta.componentName === 'critic-score-summary').data.item\r\n const userScoreSummary = data.components.find((component: any) => component.meta.componentName === 'user-score-summary').data.item\r\n\r\n return {\r\n id: product.id,\r\n recordType: product.typeId as RecordType,\r\n title: product.title,\r\n slug: product.slug,\r\n must: must,\r\n userScore: {\r\n score: userScoreSummary.score,\r\n maxScore: userScoreSummary.max,\r\n sentiment: userScoreSummary.sentiment,\r\n count: {\r\n positive: userScoreSummary.positiveCount,\r\n neutral: userScoreSummary.neutralCount,\r\n negative: userScoreSummary.negativeCount,\r\n total: userScoreSummary.reviewCount,\r\n }\r\n },\r\n criticScore: {\r\n score: criticScoreSummary.score,\r\n maxScore: criticScoreSummary.max,\r\n sentiment: criticScoreSummary.sentiment,\r\n count: {\r\n positive: criticScoreSummary.positiveCount,\r\n neutral: criticScoreSummary.neutralCount,\r\n negative: criticScoreSummary.negativeCount,\r\n total: criticScoreSummary.reviewCount,\r\n }\r\n },\r\n }\r\n}\r\n","import { parseDetailJsonResult, parseSearchJsonResult } from './parser'\r\nimport UserAgent from 'user-agents'\r\nimport { MetacriticEntry, RecordType, MetacriticSearchEntry } from './types'\r\n\r\nclass SearchInformations {\r\n apiKey: string\r\n\r\n constructor(scriptContent: string) {\r\n this.apiKey = this.extractApiFromScript(scriptContent)\r\n }\r\n\r\n extractApiFromScript(scriptContent: any) {\r\n const apiKeyPattern = /apiKey=([^\"&]+)/\r\n let matches = scriptContent.match(apiKeyPattern)\r\n if (matches) {\r\n return matches[1]\r\n }\r\n }\r\n}\r\n\r\nexport class MetacriticService {\r\n private minSimilarity: number\r\n private apiKey?: string\r\n\r\n static REFERER_HEADER = 'https://www.metacritic.com/'\r\n static HOMEPAGE_URL = 'https://www.metacritic.com/'\r\n static BASE_URL = 'https://backend.metacritic.com/composer/metacritic/pages/'\r\n static SEARCH_URL = MetacriticService.BASE_URL + 'search/'\r\n\r\n constructor(minSimilarity: number = 0.5) {\r\n this.minSimilarity = minSimilarity\r\n }\r\n\r\n async search(searchKey: string, recordType?: RecordType, sortBySimilarity: boolean = true): Promise<MetacriticSearchEntry[]> {\r\n if (!searchKey) {\r\n return []\r\n }\r\n\r\n if (!this.apiKey) {\r\n const searchInfo = await MetacriticService.sendApiKeyRetrievalWebsiteRequest()\r\n if (searchInfo) {\r\n this.apiKey = searchInfo.apiKey\r\n } else {\r\n console.error('Failed to retrieve API key')\r\n return []\r\n }\r\n }\r\n\r\n const jsonResult = await MetacriticService.sendSearchWebRequest(searchKey, this.apiKey, recordType)\r\n if (jsonResult) {\r\n return parseSearchJsonResult(jsonResult, searchKey, this.minSimilarity, sortBySimilarity)\r\n }\r\n\r\n return []\r\n }\r\n\r\n async getDetail(searchKey: string, recordType: RecordType, sortBySimilarity: boolean = true): Promise<MetacriticEntry | null> {\r\n if (!searchKey) {\r\n return null\r\n }\r\n\r\n if (!this.apiKey) {\r\n const searchInfo = await MetacriticService.sendApiKeyRetrievalWebsiteRequest()\r\n if (searchInfo) {\r\n this.apiKey = searchInfo.apiKey\r\n } else {\r\n console.error('Failed to retrieve API key')\r\n return null\r\n }\r\n }\r\n\r\n const searchResult = await this.search(searchKey, recordType, sortBySimilarity)\r\n if (searchResult && searchResult.length > 0) {\r\n const detailResult = await MetacriticService.sendDetailWebRequest(searchResult[0].slug, this.apiKey, recordType)\r\n\r\n if (detailResult) {\r\n return parseDetailJsonResult(detailResult, searchResult[0].must)\r\n }\r\n }\r\n\r\n return null\r\n }\r\n\r\n static async sendSearchWebRequest(searchKey: string, apiKey: string, recordType?: RecordType) {\r\n const headers = this.getRequestHeaders()\r\n\r\n const searchUrl = new URL(MetacriticService.SEARCH_URL + encodeURI(searchKey) + '/web')\r\n\r\n searchUrl.searchParams.append('apiKey', apiKey)\r\n\r\n if (recordType) {\r\n searchUrl.searchParams.append('mcoTypeId', recordType.toString())\r\n }\r\n\r\n try {\r\n const response = await fetch(searchUrl, {\r\n headers: headers,\r\n method: 'GET',\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n return await response.text()\r\n }\r\n } catch (error) {\r\n console.error('Error fetching search results:', error)\r\n }\r\n }\r\n\r\n static async sendDetailWebRequest(slug: string, apiKey: string, recordType: RecordType) {\r\n const headers = this.getRequestHeaders()\r\n\r\n let baseUrl = MetacriticService.BASE_URL\r\n switch (recordType) {\r\n case RecordType.Game:\r\n baseUrl = baseUrl + 'games/'\r\n break\r\n case RecordType.Movie:\r\n baseUrl = baseUrl + 'movies/'\r\n break\r\n case RecordType.TVShow:\r\n baseUrl = baseUrl + 'shows/'\r\n break\r\n default:\r\n console.error('Unsupported record type for detail request:', recordType)\r\n return\r\n }\r\n\r\n const detailUrl = new URL(baseUrl + encodeURI(slug) + '/web')\r\n\r\n detailUrl.searchParams.append('apiKey', apiKey)\r\n\r\n try {\r\n const response = await fetch(detailUrl, {\r\n headers: headers,\r\n method: 'GET',\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n return await response.text()\r\n }\r\n } catch (error) {\r\n console.error('Error fetching detail result:', error)\r\n }\r\n }\r\n\r\n static getRequestHeaders() {\r\n const minimumHeaders = this.getMinimalRequestHeaders()\r\n return {\r\n ...minimumHeaders,\r\n 'Content-Type': 'application/json',\r\n 'Accept': '*/*',\r\n 'Referer': MetacriticService.REFERER_HEADER,\r\n }\r\n }\r\n\r\n static getMinimalRequestHeaders() {\r\n const userAgent = new UserAgent()\r\n return {\r\n 'User-Agent': userAgent.toString(),\r\n }\r\n }\r\n\r\n static async sendApiKeyRetrievalWebsiteRequest(): Promise<SearchInformations | null> {\r\n const headers = this.getMinimalRequestHeaders()\r\n try {\r\n const response = await fetch(MetacriticService.HOMEPAGE_URL, {\r\n headers: headers,\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n const html = await response.text()\r\n\r\n const scriptPattern = /<script[^>]*>([\\s\\S]*?)<\\/script>/g\r\n const scripts: string[] = []\r\n let match\r\n while ((match = scriptPattern.exec(html)) !== null) {\r\n scripts.push(match[1])\r\n }\r\n\r\n for (const script of scripts) {\r\n const searchInfo = new SearchInformations(script)\r\n if (searchInfo.apiKey) {\r\n return searchInfo\r\n }\r\n }\r\n }\r\n\r\n return null\r\n } catch (error) {\r\n console.error('Error fetching website:', error)\r\n return null\r\n }\r\n }\r\n}\r\n","export enum RecordType {\r\n TVShow = 1,\r\n Movie = 2,\r\n Game = 13,\r\n}\r\n\r\nexport type MetacriticSearchEntry = {\r\n id: number\r\n recordType: RecordType\r\n title: string\r\n slug: string\r\n must: boolean\r\n criticScore: number\r\n similarity: number\r\n}\r\n\r\nexport type MetacriticEntry = {\r\n id: number\r\n recordType: RecordType\r\n title: string\r\n slug: string\r\n must: boolean\r\n userScore: Score\r\n criticScore: Score\r\n}\r\n\r\nexport type Score = {\r\n score: number\r\n maxScore: number\r\n sentiment: string\r\n count: {\r\n positive: number\r\n neutral: number\r\n negative: number\r\n total: number\r\n }\r\n}\r\n"],"mappings":"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","names":["RecordType","UserAgent"]}
package/dist/index.d.cts CHANGED
@@ -62,4 +62,4 @@ declare class MetacriticService {
62
62
  static sendApiKeyRetrievalWebsiteRequest(): Promise<SearchInformations | null>;
63
63
  }
64
64
 
65
- export { type MetacriticEntry, type MetacriticSearchEntry, MetacriticService, RecordType };
65
+ export { type MetacriticEntry, type MetacriticSearchEntry, MetacriticService, RecordType, type Score };
package/dist/index.d.ts CHANGED
@@ -62,4 +62,4 @@ declare class MetacriticService {
62
62
  static sendApiKeyRetrievalWebsiteRequest(): Promise<SearchInformations | null>;
63
63
  }
64
64
 
65
- export { type MetacriticEntry, type MetacriticSearchEntry, MetacriticService, RecordType };
65
+ export { type MetacriticEntry, type MetacriticSearchEntry, MetacriticService, RecordType, type Score };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/utils.ts","../src/lib/parser.ts","../src/lib/service.ts","../src/lib/types.ts"],"sourcesContent":["export function getSimilarity(a: string, b: string): number {\r\n if (a === b) return 1\r\n if (!a || !b) return 0\r\n\r\n const str1 = a.toLowerCase()\r\n const str2 = b.toLowerCase()\r\n const distance = levenshteinDistance(str1, str2)\r\n const maxLength = Math.max(str1.length, str2.length)\r\n return (maxLength - distance) / maxLength\r\n}\r\n\r\nfunction levenshteinDistance(a: string, b: string): number {\r\n const matrix: number[][] = []\r\n for (let i = 0; i <= b.length; i++) {\r\n matrix[i] = [i]\r\n }\r\n for (let j = 0; j <= a.length; j++) {\r\n matrix[0][j] = j\r\n }\r\n for (let i = 1; i <= b.length; i++) {\r\n for (let j = 1; j <= a.length; j++) {\r\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\r\n matrix[i][j] = matrix[i - 1][j - 1]\r\n } else {\r\n matrix[i][j] = Math.min(\r\n matrix[i - 1][j - 1] + 1,\r\n Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1)\r\n )\r\n }\r\n }\r\n }\r\n return matrix[b.length][a.length]\r\n}\r\n","import { getSimilarity } from './utils'\r\nimport { MetacriticEntry, RecordType, MetacriticSearchEntry } from './types'\r\n\r\nexport function parseSearchJsonResult(jsonString: string, searchKey: string, minSimilarity: number, sortBySimilarity: boolean = true): MetacriticSearchEntry[] {\r\n try {\r\n const parsedData = JSON.parse(jsonString)\r\n const entries: MetacriticSearchEntry[] = []\r\n\r\n const itemsList = parsedData.components.find((component: any) => component.meta.componentName === 'search').data.items\r\n for (const item of itemsList) {\r\n const entry = mapSearchEntry(item, searchKey)\r\n\r\n if (entry.similarity < minSimilarity) {\r\n continue\r\n }\r\n entries.push(entry)\r\n }\r\n\r\n return sortBySimilarity ? entries.sort((a, b) => b.similarity - a.similarity) : entries\r\n } catch (error) {\r\n console.error('Error parsing JSON:', error)\r\n return []\r\n }\r\n}\r\n\r\nexport function parseDetailJsonResult(jsonString: string, must: boolean): MetacriticEntry | null {\r\n try {\r\n const parsedData = JSON.parse(jsonString)\r\n return mapDetailEntry(parsedData, must)\r\n } catch (error) {\r\n console.error('Error parsing JSON:', error)\r\n return null\r\n }\r\n}\r\n\r\nfunction mapSearchEntry(data: any, searchKey: string): MetacriticSearchEntry {\r\n return {\r\n id: data.id,\r\n recordType: data.typeId as RecordType,\r\n title: data.title,\r\n slug: data.slug,\r\n criticScore: data.criticScoreSummary.score,\r\n must: data.mustSee || data.mustWatch || data.mustPlay,\r\n similarity: getSimilarity(data.title, searchKey)\r\n }\r\n}\r\n\r\nfunction mapDetailEntry(data: any, must: boolean): MetacriticEntry {\r\n const product = data.components.find((component: any) => component.meta.componentName === 'product').data.item\r\n const criticScoreSummary = data.components.find((component: any) => component.meta.componentName === 'critic-score-summary').data.item\r\n const userScoreSummary = data.components.find((component: any) => component.meta.componentName === 'user-score-summary').data.item\r\n\r\n return {\r\n id: product.id,\r\n recordType: product.typeId as RecordType,\r\n title: product.title,\r\n slug: product.slug,\r\n must: must,\r\n userScore: {\r\n score: userScoreSummary.score,\r\n maxScore: userScoreSummary.max,\r\n sentiment: userScoreSummary.sentiment,\r\n count: {\r\n positive: userScoreSummary.positiveCount,\r\n neutral: userScoreSummary.neutralCount,\r\n negative: userScoreSummary.negativeCount,\r\n total: userScoreSummary.reviewCount,\r\n }\r\n },\r\n criticScore: {\r\n score: criticScoreSummary.score,\r\n maxScore: criticScoreSummary.max,\r\n sentiment: criticScoreSummary.sentiment,\r\n count: {\r\n positive: criticScoreSummary.positiveCount,\r\n neutral: criticScoreSummary.neutralCount,\r\n negative: criticScoreSummary.negativeCount,\r\n total: criticScoreSummary.reviewCount,\r\n }\r\n },\r\n }\r\n}\r\n","import { parseDetailJsonResult, parseSearchJsonResult } from './parser'\r\nimport UserAgent from 'user-agents'\r\nimport { MetacriticEntry, RecordType, MetacriticSearchEntry } from './types'\r\n\r\nclass SearchInformations {\r\n apiKey: string\r\n\r\n constructor(scriptContent: string) {\r\n this.apiKey = this.extractApiFromScript(scriptContent)\r\n }\r\n\r\n extractApiFromScript(scriptContent: any) {\r\n const apiKeyPattern = /apiKey=([^\"&]+)/\r\n let matches = scriptContent.match(apiKeyPattern)\r\n if (matches) {\r\n return matches[1]\r\n }\r\n }\r\n}\r\n\r\nexport class MetacriticService {\r\n private minSimilarity: number\r\n private apiKey?: string\r\n\r\n static REFERER_HEADER = 'https://www.metacritic.com/'\r\n static HOMEPAGE_URL = 'https://www.metacritic.com/'\r\n static BASE_URL = 'https://backend.metacritic.com/composer/metacritic/pages/'\r\n static SEARCH_URL = MetacriticService.BASE_URL + 'search/'\r\n\r\n constructor(minSimilarity: number = 0.5) {\r\n this.minSimilarity = minSimilarity\r\n }\r\n\r\n async search(searchKey: string, recordType?: RecordType, sortBySimilarity: boolean = true): Promise<MetacriticSearchEntry[]> {\r\n if (!searchKey) {\r\n return []\r\n }\r\n\r\n if (!this.apiKey) {\r\n const searchInfo = await MetacriticService.sendApiKeyRetrievalWebsiteRequest()\r\n if (searchInfo) {\r\n this.apiKey = searchInfo.apiKey\r\n } else {\r\n console.error('Failed to retrieve API key')\r\n return []\r\n }\r\n }\r\n\r\n const jsonResult = await MetacriticService.sendSearchWebRequest(searchKey, this.apiKey, recordType)\r\n if (jsonResult) {\r\n return parseSearchJsonResult(jsonResult, searchKey, this.minSimilarity, sortBySimilarity)\r\n }\r\n\r\n return []\r\n }\r\n\r\n async getDetail(searchKey: string, recordType: RecordType, sortBySimilarity: boolean = true): Promise<MetacriticEntry | null> {\r\n if (!searchKey) {\r\n return null\r\n }\r\n\r\n if (!this.apiKey) {\r\n const searchInfo = await MetacriticService.sendApiKeyRetrievalWebsiteRequest()\r\n if (searchInfo) {\r\n this.apiKey = searchInfo.apiKey\r\n } else {\r\n console.error('Failed to retrieve API key')\r\n return null\r\n }\r\n }\r\n\r\n const searchResult = await this.search(searchKey, recordType, sortBySimilarity)\r\n if (searchResult && searchResult.length > 0) {\r\n const detailResult = await MetacriticService.sendDetailWebRequest(searchResult[0].slug, this.apiKey, recordType)\r\n\r\n if (detailResult) {\r\n return parseDetailJsonResult(detailResult, searchResult[0].must)\r\n }\r\n }\r\n\r\n return null\r\n }\r\n\r\n static async sendSearchWebRequest(searchKey: string, apiKey: string, recordType?: RecordType) {\r\n const headers = this.getRequestHeaders()\r\n\r\n const searchUrl = new URL(MetacriticService.SEARCH_URL + encodeURI(searchKey) + '/web')\r\n\r\n searchUrl.searchParams.append('apiKey', apiKey)\r\n\r\n if (recordType) {\r\n searchUrl.searchParams.append('mcoTypeId', recordType.toString())\r\n }\r\n\r\n try {\r\n const response = await fetch(searchUrl, {\r\n headers: headers,\r\n method: 'GET',\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n return await response.text()\r\n }\r\n } catch (error) {\r\n console.error('Error fetching search results:', error)\r\n }\r\n }\r\n\r\n static async sendDetailWebRequest(slug: string, apiKey: string, recordType: RecordType) {\r\n const headers = this.getRequestHeaders()\r\n\r\n let baseUrl = MetacriticService.BASE_URL\r\n switch (recordType) {\r\n case RecordType.Game:\r\n baseUrl = baseUrl + 'games/'\r\n break\r\n case RecordType.Movie:\r\n baseUrl = baseUrl + 'movies/'\r\n break\r\n case RecordType.TVShow:\r\n baseUrl = baseUrl + 'shows/'\r\n break\r\n default:\r\n console.error('Unsupported record type for detail request:', recordType)\r\n return\r\n }\r\n\r\n const detailUrl = new URL(baseUrl + encodeURI(slug) + '/web')\r\n\r\n detailUrl.searchParams.append('apiKey', apiKey)\r\n\r\n try {\r\n const response = await fetch(detailUrl, {\r\n headers: headers,\r\n method: 'GET',\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n return await response.text()\r\n }\r\n } catch (error) {\r\n console.error('Error fetching detail result:', error)\r\n }\r\n }\r\n\r\n static getRequestHeaders() {\r\n const minimumHeaders = this.getMinimalRequestHeaders()\r\n return {\r\n ...minimumHeaders,\r\n 'Content-Type': 'application/json',\r\n 'Accept': '*/*',\r\n 'Referer': MetacriticService.REFERER_HEADER,\r\n }\r\n }\r\n\r\n static getMinimalRequestHeaders() {\r\n const userAgent = new UserAgent()\r\n return {\r\n 'User-Agent': userAgent.toString(),\r\n }\r\n }\r\n\r\n static async sendApiKeyRetrievalWebsiteRequest(): Promise<SearchInformations | null> {\r\n const headers = this.getMinimalRequestHeaders()\r\n try {\r\n const response = await fetch(MetacriticService.HOMEPAGE_URL, {\r\n headers: headers,\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n const html = await response.text()\r\n\r\n const scriptPattern = /<script[^>]*>([\\s\\S]*?)<\\/script>/g\r\n const scripts: string[] = []\r\n let match\r\n while ((match = scriptPattern.exec(html)) !== null) {\r\n scripts.push(match[1])\r\n }\r\n\r\n for (const script of scripts) {\r\n const searchInfo = new SearchInformations(script)\r\n if (searchInfo.apiKey) {\r\n return searchInfo\r\n }\r\n }\r\n }\r\n\r\n return null\r\n } catch (error) {\r\n console.error('Error fetching website:', error)\r\n return null\r\n }\r\n }\r\n}\r\n","export enum RecordType {\r\n TVShow = 1,\r\n Movie = 2,\r\n Game = 13,\r\n}\r\n\r\nexport type MetacriticSearchEntry = {\r\n id: number\r\n recordType: RecordType\r\n title: string\r\n slug: string\r\n must: boolean\r\n criticScore: number\r\n similarity: number\r\n}\r\n\r\nexport type MetacriticEntry = {\r\n id: number\r\n recordType: RecordType\r\n title: string\r\n slug: string\r\n must: boolean\r\n userScore: Score\r\n criticScore: Score\r\n}\r\n\r\ntype Score = {\r\n score: number\r\n maxScore: number\r\n sentiment: string\r\n count: {\r\n positive: number\r\n neutral: number\r\n negative: number\r\n total: number\r\n }\r\n}\r\n"],"mappings":"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","names":["RecordType"]}
1
+ {"version":3,"sources":["../src/lib/utils.ts","../src/lib/parser.ts","../src/lib/service.ts","../src/lib/types.ts"],"sourcesContent":["export function getSimilarity(a: string, b: string): number {\r\n if (a === b) return 1\r\n if (!a || !b) return 0\r\n\r\n const str1 = a.toLowerCase()\r\n const str2 = b.toLowerCase()\r\n const distance = levenshteinDistance(str1, str2)\r\n const maxLength = Math.max(str1.length, str2.length)\r\n return (maxLength - distance) / maxLength\r\n}\r\n\r\nfunction levenshteinDistance(a: string, b: string): number {\r\n const matrix: number[][] = []\r\n for (let i = 0; i <= b.length; i++) {\r\n matrix[i] = [i]\r\n }\r\n for (let j = 0; j <= a.length; j++) {\r\n matrix[0][j] = j\r\n }\r\n for (let i = 1; i <= b.length; i++) {\r\n for (let j = 1; j <= a.length; j++) {\r\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\r\n matrix[i][j] = matrix[i - 1][j - 1]\r\n } else {\r\n matrix[i][j] = Math.min(\r\n matrix[i - 1][j - 1] + 1,\r\n Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1)\r\n )\r\n }\r\n }\r\n }\r\n return matrix[b.length][a.length]\r\n}\r\n","import { getSimilarity } from './utils'\r\nimport { MetacriticEntry, RecordType, MetacriticSearchEntry } from './types'\r\n\r\nexport function parseSearchJsonResult(jsonString: string, searchKey: string, minSimilarity: number, sortBySimilarity: boolean = true): MetacriticSearchEntry[] {\r\n try {\r\n const parsedData = JSON.parse(jsonString)\r\n const entries: MetacriticSearchEntry[] = []\r\n\r\n const itemsList = parsedData.components.find((component: any) => component.meta.componentName === 'search').data.items\r\n for (const item of itemsList) {\r\n const entry = mapSearchEntry(item, searchKey)\r\n\r\n if (entry.similarity < minSimilarity) {\r\n continue\r\n }\r\n entries.push(entry)\r\n }\r\n\r\n return sortBySimilarity ? entries.sort((a, b) => b.similarity - a.similarity) : entries\r\n } catch (error) {\r\n console.error('Error parsing JSON:', error)\r\n return []\r\n }\r\n}\r\n\r\nexport function parseDetailJsonResult(jsonString: string, must: boolean): MetacriticEntry | null {\r\n try {\r\n const parsedData = JSON.parse(jsonString)\r\n return mapDetailEntry(parsedData, must)\r\n } catch (error) {\r\n console.error('Error parsing JSON:', error)\r\n return null\r\n }\r\n}\r\n\r\nfunction mapSearchEntry(data: any, searchKey: string): MetacriticSearchEntry {\r\n return {\r\n id: data.id,\r\n recordType: data.typeId as RecordType,\r\n title: data.title,\r\n slug: data.slug,\r\n criticScore: data.criticScoreSummary.score,\r\n must: data.mustSee || data.mustWatch || data.mustPlay,\r\n similarity: getSimilarity(data.title, searchKey)\r\n }\r\n}\r\n\r\nfunction mapDetailEntry(data: any, must: boolean): MetacriticEntry {\r\n const product = data.components.find((component: any) => component.meta.componentName === 'product').data.item\r\n const criticScoreSummary = data.components.find((component: any) => component.meta.componentName === 'critic-score-summary').data.item\r\n const userScoreSummary = data.components.find((component: any) => component.meta.componentName === 'user-score-summary').data.item\r\n\r\n return {\r\n id: product.id,\r\n recordType: product.typeId as RecordType,\r\n title: product.title,\r\n slug: product.slug,\r\n must: must,\r\n userScore: {\r\n score: userScoreSummary.score,\r\n maxScore: userScoreSummary.max,\r\n sentiment: userScoreSummary.sentiment,\r\n count: {\r\n positive: userScoreSummary.positiveCount,\r\n neutral: userScoreSummary.neutralCount,\r\n negative: userScoreSummary.negativeCount,\r\n total: userScoreSummary.reviewCount,\r\n }\r\n },\r\n criticScore: {\r\n score: criticScoreSummary.score,\r\n maxScore: criticScoreSummary.max,\r\n sentiment: criticScoreSummary.sentiment,\r\n count: {\r\n positive: criticScoreSummary.positiveCount,\r\n neutral: criticScoreSummary.neutralCount,\r\n negative: criticScoreSummary.negativeCount,\r\n total: criticScoreSummary.reviewCount,\r\n }\r\n },\r\n }\r\n}\r\n","import { parseDetailJsonResult, parseSearchJsonResult } from './parser'\r\nimport UserAgent from 'user-agents'\r\nimport { MetacriticEntry, RecordType, MetacriticSearchEntry } from './types'\r\n\r\nclass SearchInformations {\r\n apiKey: string\r\n\r\n constructor(scriptContent: string) {\r\n this.apiKey = this.extractApiFromScript(scriptContent)\r\n }\r\n\r\n extractApiFromScript(scriptContent: any) {\r\n const apiKeyPattern = /apiKey=([^\"&]+)/\r\n let matches = scriptContent.match(apiKeyPattern)\r\n if (matches) {\r\n return matches[1]\r\n }\r\n }\r\n}\r\n\r\nexport class MetacriticService {\r\n private minSimilarity: number\r\n private apiKey?: string\r\n\r\n static REFERER_HEADER = 'https://www.metacritic.com/'\r\n static HOMEPAGE_URL = 'https://www.metacritic.com/'\r\n static BASE_URL = 'https://backend.metacritic.com/composer/metacritic/pages/'\r\n static SEARCH_URL = MetacriticService.BASE_URL + 'search/'\r\n\r\n constructor(minSimilarity: number = 0.5) {\r\n this.minSimilarity = minSimilarity\r\n }\r\n\r\n async search(searchKey: string, recordType?: RecordType, sortBySimilarity: boolean = true): Promise<MetacriticSearchEntry[]> {\r\n if (!searchKey) {\r\n return []\r\n }\r\n\r\n if (!this.apiKey) {\r\n const searchInfo = await MetacriticService.sendApiKeyRetrievalWebsiteRequest()\r\n if (searchInfo) {\r\n this.apiKey = searchInfo.apiKey\r\n } else {\r\n console.error('Failed to retrieve API key')\r\n return []\r\n }\r\n }\r\n\r\n const jsonResult = await MetacriticService.sendSearchWebRequest(searchKey, this.apiKey, recordType)\r\n if (jsonResult) {\r\n return parseSearchJsonResult(jsonResult, searchKey, this.minSimilarity, sortBySimilarity)\r\n }\r\n\r\n return []\r\n }\r\n\r\n async getDetail(searchKey: string, recordType: RecordType, sortBySimilarity: boolean = true): Promise<MetacriticEntry | null> {\r\n if (!searchKey) {\r\n return null\r\n }\r\n\r\n if (!this.apiKey) {\r\n const searchInfo = await MetacriticService.sendApiKeyRetrievalWebsiteRequest()\r\n if (searchInfo) {\r\n this.apiKey = searchInfo.apiKey\r\n } else {\r\n console.error('Failed to retrieve API key')\r\n return null\r\n }\r\n }\r\n\r\n const searchResult = await this.search(searchKey, recordType, sortBySimilarity)\r\n if (searchResult && searchResult.length > 0) {\r\n const detailResult = await MetacriticService.sendDetailWebRequest(searchResult[0].slug, this.apiKey, recordType)\r\n\r\n if (detailResult) {\r\n return parseDetailJsonResult(detailResult, searchResult[0].must)\r\n }\r\n }\r\n\r\n return null\r\n }\r\n\r\n static async sendSearchWebRequest(searchKey: string, apiKey: string, recordType?: RecordType) {\r\n const headers = this.getRequestHeaders()\r\n\r\n const searchUrl = new URL(MetacriticService.SEARCH_URL + encodeURI(searchKey) + '/web')\r\n\r\n searchUrl.searchParams.append('apiKey', apiKey)\r\n\r\n if (recordType) {\r\n searchUrl.searchParams.append('mcoTypeId', recordType.toString())\r\n }\r\n\r\n try {\r\n const response = await fetch(searchUrl, {\r\n headers: headers,\r\n method: 'GET',\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n return await response.text()\r\n }\r\n } catch (error) {\r\n console.error('Error fetching search results:', error)\r\n }\r\n }\r\n\r\n static async sendDetailWebRequest(slug: string, apiKey: string, recordType: RecordType) {\r\n const headers = this.getRequestHeaders()\r\n\r\n let baseUrl = MetacriticService.BASE_URL\r\n switch (recordType) {\r\n case RecordType.Game:\r\n baseUrl = baseUrl + 'games/'\r\n break\r\n case RecordType.Movie:\r\n baseUrl = baseUrl + 'movies/'\r\n break\r\n case RecordType.TVShow:\r\n baseUrl = baseUrl + 'shows/'\r\n break\r\n default:\r\n console.error('Unsupported record type for detail request:', recordType)\r\n return\r\n }\r\n\r\n const detailUrl = new URL(baseUrl + encodeURI(slug) + '/web')\r\n\r\n detailUrl.searchParams.append('apiKey', apiKey)\r\n\r\n try {\r\n const response = await fetch(detailUrl, {\r\n headers: headers,\r\n method: 'GET',\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n return await response.text()\r\n }\r\n } catch (error) {\r\n console.error('Error fetching detail result:', error)\r\n }\r\n }\r\n\r\n static getRequestHeaders() {\r\n const minimumHeaders = this.getMinimalRequestHeaders()\r\n return {\r\n ...minimumHeaders,\r\n 'Content-Type': 'application/json',\r\n 'Accept': '*/*',\r\n 'Referer': MetacriticService.REFERER_HEADER,\r\n }\r\n }\r\n\r\n static getMinimalRequestHeaders() {\r\n const userAgent = new UserAgent()\r\n return {\r\n 'User-Agent': userAgent.toString(),\r\n }\r\n }\r\n\r\n static async sendApiKeyRetrievalWebsiteRequest(): Promise<SearchInformations | null> {\r\n const headers = this.getMinimalRequestHeaders()\r\n try {\r\n const response = await fetch(MetacriticService.HOMEPAGE_URL, {\r\n headers: headers,\r\n signal: AbortSignal.timeout(60000)\r\n })\r\n\r\n if (response.ok) {\r\n const html = await response.text()\r\n\r\n const scriptPattern = /<script[^>]*>([\\s\\S]*?)<\\/script>/g\r\n const scripts: string[] = []\r\n let match\r\n while ((match = scriptPattern.exec(html)) !== null) {\r\n scripts.push(match[1])\r\n }\r\n\r\n for (const script of scripts) {\r\n const searchInfo = new SearchInformations(script)\r\n if (searchInfo.apiKey) {\r\n return searchInfo\r\n }\r\n }\r\n }\r\n\r\n return null\r\n } catch (error) {\r\n console.error('Error fetching website:', error)\r\n return null\r\n }\r\n }\r\n}\r\n","export enum RecordType {\r\n TVShow = 1,\r\n Movie = 2,\r\n Game = 13,\r\n}\r\n\r\nexport type MetacriticSearchEntry = {\r\n id: number\r\n recordType: RecordType\r\n title: string\r\n slug: string\r\n must: boolean\r\n criticScore: number\r\n similarity: number\r\n}\r\n\r\nexport type MetacriticEntry = {\r\n id: number\r\n recordType: RecordType\r\n title: string\r\n slug: string\r\n must: boolean\r\n userScore: Score\r\n criticScore: Score\r\n}\r\n\r\nexport type Score = {\r\n score: number\r\n maxScore: number\r\n sentiment: string\r\n count: {\r\n positive: number\r\n neutral: number\r\n negative: number\r\n total: number\r\n }\r\n}\r\n"],"mappings":"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","names":["RecordType"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metacritic-ts",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "TypeScript library to extrapolate data from Metacritic.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",