metacritic-ts 1.0.3 → 1.1.0
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.
- package/README.md +24 -8
- package/dist/index.cjs +81 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -3
- package/dist/index.d.ts +13 -3
- package/dist/index.js +81 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -39,8 +39,10 @@ async function search() {
|
|
|
39
39
|
const results = await metacriticService.search('The Last of Us');
|
|
40
40
|
|
|
41
41
|
// This will result in a list of games, movies or tv shows
|
|
42
|
-
if (results) {
|
|
43
|
-
console.log('Search results:', results);
|
|
42
|
+
if (results.success) {
|
|
43
|
+
console.log('Search results:', results.data);
|
|
44
|
+
} else {
|
|
45
|
+
console.error('Search failed:', results.error);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -51,16 +53,18 @@ async function getDetail() {
|
|
|
51
53
|
const metacriticService = new MetacriticService();
|
|
52
54
|
|
|
53
55
|
// Get the details of a game
|
|
54
|
-
const details = await metacriticService.getDetail('The Last of Us Part II', RecordType.
|
|
56
|
+
const details = await metacriticService.getDetail('The Last of Us Part II', RecordType.Game);
|
|
55
57
|
|
|
56
58
|
// This will result in the details of the game, including the rating
|
|
57
|
-
if (details) {
|
|
58
|
-
console.log('Game details:', details);
|
|
59
|
+
if (details.success) {
|
|
60
|
+
console.log('Game details:', details.data);
|
|
61
|
+
} else {
|
|
62
|
+
console.error('Detail fetch failed:', details.error);
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
await search();
|
|
63
|
-
await
|
|
67
|
+
await getDetail();
|
|
64
68
|
```
|
|
65
69
|
|
|
66
70
|
## API
|
|
@@ -75,12 +79,12 @@ The main service class for interacting with the Metacritic website.
|
|
|
75
79
|
- `minSimilarity`: Optional parameter to set the minimum similarity threshold for search results to not be filtered out (Default: 0.5).
|
|
76
80
|
|
|
77
81
|
#### Methods
|
|
78
|
-
- `async search(searchKey: string): Promise<
|
|
82
|
+
- `async search(searchKey: string): Promise<SearchResult>`: Searches for games, movies or tv shows matching the provided search key.
|
|
79
83
|
- `searchKey`: The title to search for
|
|
80
84
|
- `recordType`: Optional record type to adjust search behavior, it hasn't a default value and will search for all types.
|
|
81
85
|
- `sortBySimilarity`: Optional boolean to sort the results by similarity to the search key (Default: true). If set to false, the results will leave to the order returned by the Metacritic API.
|
|
82
86
|
|
|
83
|
-
- `async getDetail(title: string, recordType: RecordType): Promise<
|
|
87
|
+
- `async getDetail(title: string, recordType: RecordType): Promise<DetailResult>`: Retrieves the details of a game, movie or tv show matching the provided title.
|
|
84
88
|
- `title`: The title to search for
|
|
85
89
|
- `recordType`: The type of record to retrieve (game, movie or tv show)
|
|
86
90
|
- `sortBySimilarity`: Optional boolean to sort the results by similarity to the search key (Default: true). Pay attention that this parameter is very important for the `getDetail` method, if set to false, the results will leave to the order returned by the Metacritic API and the first result may not be the one you are looking for.
|
|
@@ -101,6 +105,12 @@ An interface representing a search entry returned by the MetacriticService.
|
|
|
101
105
|
- `criticScore`: The critic score of the record.
|
|
102
106
|
- `similarity`: A computed value that indicates how similar the record is to the search term.
|
|
103
107
|
|
|
108
|
+
### `SearchResult`
|
|
109
|
+
An interface representing the `search` method result.
|
|
110
|
+
- `success`: Indicates whether the operation succeeded.
|
|
111
|
+
- `data`: The list of `MetacriticSearchEntry`.
|
|
112
|
+
- `error`: Optional error message when `success` is `false`.
|
|
113
|
+
|
|
104
114
|
### `MetacriticEntry`
|
|
105
115
|
An interface representing a record entry returned by the MetacriticService.
|
|
106
116
|
- `id`: The unique identifier for the record.
|
|
@@ -129,6 +139,12 @@ An interface representing a record entry returned by the MetacriticService.
|
|
|
129
139
|
- `neutral`: The number of neutral reviews.
|
|
130
140
|
- `total`: The total number of reviews.
|
|
131
141
|
|
|
142
|
+
### `DetailResult`
|
|
143
|
+
An interface representing the `getDetail` method result.
|
|
144
|
+
- `success`: Indicates whether the operation succeeded.
|
|
145
|
+
- `data`: A `MetacriticEntry` when found, otherwise `null`.
|
|
146
|
+
- `error`: Optional error message when `success` is `false`.
|
|
147
|
+
|
|
132
148
|
## Development
|
|
133
149
|
|
|
134
150
|
### Prerequisites
|
package/dist/index.cjs
CHANGED
|
@@ -159,7 +159,11 @@ var _MetacriticService = class _MetacriticService {
|
|
|
159
159
|
}
|
|
160
160
|
async search(searchKey, recordType, sortBySimilarity = true) {
|
|
161
161
|
if (!searchKey) {
|
|
162
|
-
return
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
data: [],
|
|
165
|
+
error: "Search key is required"
|
|
166
|
+
};
|
|
163
167
|
}
|
|
164
168
|
if (!this.apiKey) {
|
|
165
169
|
const searchInfo = await _MetacriticService.sendApiKeyRetrievalWebsiteRequest();
|
|
@@ -167,18 +171,42 @@ var _MetacriticService = class _MetacriticService {
|
|
|
167
171
|
this.apiKey = searchInfo.apiKey;
|
|
168
172
|
} else {
|
|
169
173
|
console.error("Failed to retrieve API key");
|
|
170
|
-
return
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
data: [],
|
|
177
|
+
error: "Failed to retrieve API key"
|
|
178
|
+
};
|
|
171
179
|
}
|
|
172
180
|
}
|
|
173
181
|
const jsonResult = await _MetacriticService.sendSearchWebRequest(searchKey, this.apiKey, recordType);
|
|
174
|
-
if (jsonResult) {
|
|
175
|
-
return
|
|
182
|
+
if (!jsonResult) {
|
|
183
|
+
return {
|
|
184
|
+
success: false,
|
|
185
|
+
data: [],
|
|
186
|
+
error: "Failed to fetch search results"
|
|
187
|
+
};
|
|
176
188
|
}
|
|
177
|
-
|
|
189
|
+
try {
|
|
190
|
+
JSON.parse(jsonResult);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
return {
|
|
193
|
+
success: false,
|
|
194
|
+
data: [],
|
|
195
|
+
error: "Invalid search response format"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
data: parseSearchJsonResult(jsonResult, searchKey, this.minSimilarity, sortBySimilarity)
|
|
201
|
+
};
|
|
178
202
|
}
|
|
179
203
|
async getDetail(searchKey, recordType, sortBySimilarity = true) {
|
|
180
204
|
if (!searchKey) {
|
|
181
|
-
return
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
data: null,
|
|
208
|
+
error: "Search key is required"
|
|
209
|
+
};
|
|
182
210
|
}
|
|
183
211
|
if (!this.apiKey) {
|
|
184
212
|
const searchInfo = await _MetacriticService.sendApiKeyRetrievalWebsiteRequest();
|
|
@@ -186,17 +214,57 @@ var _MetacriticService = class _MetacriticService {
|
|
|
186
214
|
this.apiKey = searchInfo.apiKey;
|
|
187
215
|
} else {
|
|
188
216
|
console.error("Failed to retrieve API key");
|
|
189
|
-
return
|
|
217
|
+
return {
|
|
218
|
+
success: false,
|
|
219
|
+
data: null,
|
|
220
|
+
error: "Failed to retrieve API key"
|
|
221
|
+
};
|
|
190
222
|
}
|
|
191
223
|
}
|
|
192
224
|
const searchResult = await this.search(searchKey, recordType, sortBySimilarity);
|
|
193
|
-
if (searchResult
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
225
|
+
if (!searchResult.success) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
data: null,
|
|
229
|
+
error: searchResult.error || "Failed to search detail entry"
|
|
230
|
+
};
|
|
198
231
|
}
|
|
199
|
-
|
|
232
|
+
if (searchResult.data.length === 0) {
|
|
233
|
+
return {
|
|
234
|
+
success: false,
|
|
235
|
+
data: null,
|
|
236
|
+
error: "No matching entry found"
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const detailResult = await _MetacriticService.sendDetailWebRequest(searchResult.data[0].slug, this.apiKey, recordType);
|
|
240
|
+
if (!detailResult) {
|
|
241
|
+
return {
|
|
242
|
+
success: false,
|
|
243
|
+
data: null,
|
|
244
|
+
error: "Failed to fetch detail result"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
JSON.parse(detailResult);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
return {
|
|
251
|
+
success: false,
|
|
252
|
+
data: null,
|
|
253
|
+
error: "Invalid detail response format"
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const parsedDetail = parseDetailJsonResult(detailResult, searchResult.data[0].must);
|
|
257
|
+
if (!parsedDetail) {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
data: null,
|
|
261
|
+
error: "Invalid detail response format"
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
success: true,
|
|
266
|
+
data: parsedDetail
|
|
267
|
+
};
|
|
200
268
|
}
|
|
201
269
|
static async sendSearchWebRequest(searchKey, apiKey, recordType) {
|
|
202
270
|
const headers = this.getRequestHeaders();
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lib/utils.ts","../src/lib/parser.ts","../src/lib/types.ts","../src/lib/service.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","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","import { parseDetailJsonResult, parseSearchJsonResult } from './parser'\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 static USER_AGENTS = [\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0',\r\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15',\r\n ]\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 = MetacriticService.USER_AGENTS[Math.floor(Math.random() * MetacriticService.USER_AGENTS.length)]\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"],"mappings":"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","names":["RecordType"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lib/utils.ts","../src/lib/parser.ts","../src/lib/types.ts","../src/lib/service.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","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 SearchResult = {\r\n success: boolean\r\n data: MetacriticSearchEntry[]\r\n error?: string\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 DetailResult = {\r\n success: boolean\r\n data: MetacriticEntry | null\r\n error?: string\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","import { parseDetailJsonResult, parseSearchJsonResult } from './parser'\r\nimport { DetailResult, RecordType, SearchResult } 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 static USER_AGENTS = [\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0',\r\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15',\r\n ]\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<SearchResult> {\r\n if (!searchKey) {\r\n return {\r\n success: false,\r\n data: [],\r\n error: 'Search key is required'\r\n }\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 success: false,\r\n data: [],\r\n error: 'Failed to retrieve API key'\r\n }\r\n }\r\n }\r\n\r\n const jsonResult = await MetacriticService.sendSearchWebRequest(searchKey, this.apiKey, recordType)\r\n if (!jsonResult) {\r\n return {\r\n success: false,\r\n data: [],\r\n error: 'Failed to fetch search results'\r\n }\r\n }\r\n\r\n try {\r\n JSON.parse(jsonResult)\r\n } catch {\r\n return {\r\n success: false,\r\n data: [],\r\n error: 'Invalid search response format'\r\n }\r\n }\r\n\r\n return {\r\n success: true,\r\n data: parseSearchJsonResult(jsonResult, searchKey, this.minSimilarity, sortBySimilarity)\r\n }\r\n }\r\n\r\n async getDetail(searchKey: string, recordType: RecordType, sortBySimilarity: boolean = true): Promise<DetailResult> {\r\n if (!searchKey) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'Search key is required'\r\n }\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 success: false,\r\n data: null,\r\n error: 'Failed to retrieve API key'\r\n }\r\n }\r\n }\r\n\r\n const searchResult = await this.search(searchKey, recordType, sortBySimilarity)\r\n if (!searchResult.success) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: searchResult.error || 'Failed to search detail entry'\r\n }\r\n }\r\n\r\n if (searchResult.data.length === 0) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'No matching entry found'\r\n }\r\n }\r\n\r\n const detailResult = await MetacriticService.sendDetailWebRequest(searchResult.data[0].slug, this.apiKey, recordType)\r\n\r\n if (!detailResult) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'Failed to fetch detail result'\r\n }\r\n }\r\n\r\n try {\r\n JSON.parse(detailResult)\r\n } catch {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'Invalid detail response format'\r\n }\r\n }\r\n\r\n const parsedDetail = parseDetailJsonResult(detailResult, searchResult.data[0].must)\r\n if (!parsedDetail) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'Invalid detail response format'\r\n }\r\n }\r\n\r\n return {\r\n success: true,\r\n data: parsedDetail\r\n }\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 = MetacriticService.USER_AGENTS[Math.floor(Math.random() * MetacriticService.USER_AGENTS.length)]\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"],"mappings":"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","names":["RecordType"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -12,6 +12,11 @@ type MetacriticSearchEntry = {
|
|
|
12
12
|
criticScore: number;
|
|
13
13
|
similarity: number;
|
|
14
14
|
};
|
|
15
|
+
type SearchResult = {
|
|
16
|
+
success: boolean;
|
|
17
|
+
data: MetacriticSearchEntry[];
|
|
18
|
+
error?: string;
|
|
19
|
+
};
|
|
15
20
|
type MetacriticEntry = {
|
|
16
21
|
id: number;
|
|
17
22
|
recordType: RecordType;
|
|
@@ -21,6 +26,11 @@ type MetacriticEntry = {
|
|
|
21
26
|
userScore: Score;
|
|
22
27
|
criticScore: Score;
|
|
23
28
|
};
|
|
29
|
+
type DetailResult = {
|
|
30
|
+
success: boolean;
|
|
31
|
+
data: MetacriticEntry | null;
|
|
32
|
+
error?: string;
|
|
33
|
+
};
|
|
24
34
|
type Score = {
|
|
25
35
|
score: number;
|
|
26
36
|
maxScore: number;
|
|
@@ -47,8 +57,8 @@ declare class MetacriticService {
|
|
|
47
57
|
static SEARCH_URL: string;
|
|
48
58
|
static USER_AGENTS: string[];
|
|
49
59
|
constructor(minSimilarity?: number);
|
|
50
|
-
search(searchKey: string, recordType?: RecordType, sortBySimilarity?: boolean): Promise<
|
|
51
|
-
getDetail(searchKey: string, recordType: RecordType, sortBySimilarity?: boolean): Promise<
|
|
60
|
+
search(searchKey: string, recordType?: RecordType, sortBySimilarity?: boolean): Promise<SearchResult>;
|
|
61
|
+
getDetail(searchKey: string, recordType: RecordType, sortBySimilarity?: boolean): Promise<DetailResult>;
|
|
52
62
|
static sendSearchWebRequest(searchKey: string, apiKey: string, recordType?: RecordType): Promise<string | undefined>;
|
|
53
63
|
static sendDetailWebRequest(slug: string, apiKey: string, recordType: RecordType): Promise<string | undefined>;
|
|
54
64
|
static getRequestHeaders(): {
|
|
@@ -63,4 +73,4 @@ declare class MetacriticService {
|
|
|
63
73
|
static sendApiKeyRetrievalWebsiteRequest(): Promise<SearchInformations | null>;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
|
-
export { type MetacriticEntry, type MetacriticSearchEntry, MetacriticService, RecordType, type Score };
|
|
76
|
+
export { type DetailResult, type MetacriticEntry, type MetacriticSearchEntry, MetacriticService, RecordType, type Score, type SearchResult };
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,11 @@ type MetacriticSearchEntry = {
|
|
|
12
12
|
criticScore: number;
|
|
13
13
|
similarity: number;
|
|
14
14
|
};
|
|
15
|
+
type SearchResult = {
|
|
16
|
+
success: boolean;
|
|
17
|
+
data: MetacriticSearchEntry[];
|
|
18
|
+
error?: string;
|
|
19
|
+
};
|
|
15
20
|
type MetacriticEntry = {
|
|
16
21
|
id: number;
|
|
17
22
|
recordType: RecordType;
|
|
@@ -21,6 +26,11 @@ type MetacriticEntry = {
|
|
|
21
26
|
userScore: Score;
|
|
22
27
|
criticScore: Score;
|
|
23
28
|
};
|
|
29
|
+
type DetailResult = {
|
|
30
|
+
success: boolean;
|
|
31
|
+
data: MetacriticEntry | null;
|
|
32
|
+
error?: string;
|
|
33
|
+
};
|
|
24
34
|
type Score = {
|
|
25
35
|
score: number;
|
|
26
36
|
maxScore: number;
|
|
@@ -47,8 +57,8 @@ declare class MetacriticService {
|
|
|
47
57
|
static SEARCH_URL: string;
|
|
48
58
|
static USER_AGENTS: string[];
|
|
49
59
|
constructor(minSimilarity?: number);
|
|
50
|
-
search(searchKey: string, recordType?: RecordType, sortBySimilarity?: boolean): Promise<
|
|
51
|
-
getDetail(searchKey: string, recordType: RecordType, sortBySimilarity?: boolean): Promise<
|
|
60
|
+
search(searchKey: string, recordType?: RecordType, sortBySimilarity?: boolean): Promise<SearchResult>;
|
|
61
|
+
getDetail(searchKey: string, recordType: RecordType, sortBySimilarity?: boolean): Promise<DetailResult>;
|
|
52
62
|
static sendSearchWebRequest(searchKey: string, apiKey: string, recordType?: RecordType): Promise<string | undefined>;
|
|
53
63
|
static sendDetailWebRequest(slug: string, apiKey: string, recordType: RecordType): Promise<string | undefined>;
|
|
54
64
|
static getRequestHeaders(): {
|
|
@@ -63,4 +73,4 @@ declare class MetacriticService {
|
|
|
63
73
|
static sendApiKeyRetrievalWebsiteRequest(): Promise<SearchInformations | null>;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
|
-
export { type MetacriticEntry, type MetacriticSearchEntry, MetacriticService, RecordType, type Score };
|
|
76
|
+
export { type DetailResult, type MetacriticEntry, type MetacriticSearchEntry, MetacriticService, RecordType, type Score, type SearchResult };
|
package/dist/index.js
CHANGED
|
@@ -132,7 +132,11 @@ var _MetacriticService = class _MetacriticService {
|
|
|
132
132
|
}
|
|
133
133
|
async search(searchKey, recordType, sortBySimilarity = true) {
|
|
134
134
|
if (!searchKey) {
|
|
135
|
-
return
|
|
135
|
+
return {
|
|
136
|
+
success: false,
|
|
137
|
+
data: [],
|
|
138
|
+
error: "Search key is required"
|
|
139
|
+
};
|
|
136
140
|
}
|
|
137
141
|
if (!this.apiKey) {
|
|
138
142
|
const searchInfo = await _MetacriticService.sendApiKeyRetrievalWebsiteRequest();
|
|
@@ -140,18 +144,42 @@ var _MetacriticService = class _MetacriticService {
|
|
|
140
144
|
this.apiKey = searchInfo.apiKey;
|
|
141
145
|
} else {
|
|
142
146
|
console.error("Failed to retrieve API key");
|
|
143
|
-
return
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
data: [],
|
|
150
|
+
error: "Failed to retrieve API key"
|
|
151
|
+
};
|
|
144
152
|
}
|
|
145
153
|
}
|
|
146
154
|
const jsonResult = await _MetacriticService.sendSearchWebRequest(searchKey, this.apiKey, recordType);
|
|
147
|
-
if (jsonResult) {
|
|
148
|
-
return
|
|
155
|
+
if (!jsonResult) {
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
data: [],
|
|
159
|
+
error: "Failed to fetch search results"
|
|
160
|
+
};
|
|
149
161
|
}
|
|
150
|
-
|
|
162
|
+
try {
|
|
163
|
+
JSON.parse(jsonResult);
|
|
164
|
+
} catch (e) {
|
|
165
|
+
return {
|
|
166
|
+
success: false,
|
|
167
|
+
data: [],
|
|
168
|
+
error: "Invalid search response format"
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
success: true,
|
|
173
|
+
data: parseSearchJsonResult(jsonResult, searchKey, this.minSimilarity, sortBySimilarity)
|
|
174
|
+
};
|
|
151
175
|
}
|
|
152
176
|
async getDetail(searchKey, recordType, sortBySimilarity = true) {
|
|
153
177
|
if (!searchKey) {
|
|
154
|
-
return
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
data: null,
|
|
181
|
+
error: "Search key is required"
|
|
182
|
+
};
|
|
155
183
|
}
|
|
156
184
|
if (!this.apiKey) {
|
|
157
185
|
const searchInfo = await _MetacriticService.sendApiKeyRetrievalWebsiteRequest();
|
|
@@ -159,17 +187,57 @@ var _MetacriticService = class _MetacriticService {
|
|
|
159
187
|
this.apiKey = searchInfo.apiKey;
|
|
160
188
|
} else {
|
|
161
189
|
console.error("Failed to retrieve API key");
|
|
162
|
-
return
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
data: null,
|
|
193
|
+
error: "Failed to retrieve API key"
|
|
194
|
+
};
|
|
163
195
|
}
|
|
164
196
|
}
|
|
165
197
|
const searchResult = await this.search(searchKey, recordType, sortBySimilarity);
|
|
166
|
-
if (searchResult
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
198
|
+
if (!searchResult.success) {
|
|
199
|
+
return {
|
|
200
|
+
success: false,
|
|
201
|
+
data: null,
|
|
202
|
+
error: searchResult.error || "Failed to search detail entry"
|
|
203
|
+
};
|
|
171
204
|
}
|
|
172
|
-
|
|
205
|
+
if (searchResult.data.length === 0) {
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
data: null,
|
|
209
|
+
error: "No matching entry found"
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const detailResult = await _MetacriticService.sendDetailWebRequest(searchResult.data[0].slug, this.apiKey, recordType);
|
|
213
|
+
if (!detailResult) {
|
|
214
|
+
return {
|
|
215
|
+
success: false,
|
|
216
|
+
data: null,
|
|
217
|
+
error: "Failed to fetch detail result"
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
JSON.parse(detailResult);
|
|
222
|
+
} catch (e) {
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
data: null,
|
|
226
|
+
error: "Invalid detail response format"
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const parsedDetail = parseDetailJsonResult(detailResult, searchResult.data[0].must);
|
|
230
|
+
if (!parsedDetail) {
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
data: null,
|
|
234
|
+
error: "Invalid detail response format"
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
success: true,
|
|
239
|
+
data: parsedDetail
|
|
240
|
+
};
|
|
173
241
|
}
|
|
174
242
|
static async sendSearchWebRequest(searchKey, apiKey, recordType) {
|
|
175
243
|
const headers = this.getRequestHeaders();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/utils.ts","../src/lib/parser.ts","../src/lib/types.ts","../src/lib/service.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","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","import { parseDetailJsonResult, parseSearchJsonResult } from './parser'\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 static USER_AGENTS = [\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0',\r\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15',\r\n ]\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 = MetacriticService.USER_AGENTS[Math.floor(Math.random() * MetacriticService.USER_AGENTS.length)]\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"],"mappings":"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","names":["RecordType"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/lib/parser.ts","../src/lib/types.ts","../src/lib/service.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","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 SearchResult = {\r\n success: boolean\r\n data: MetacriticSearchEntry[]\r\n error?: string\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 DetailResult = {\r\n success: boolean\r\n data: MetacriticEntry | null\r\n error?: string\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","import { parseDetailJsonResult, parseSearchJsonResult } from './parser'\r\nimport { DetailResult, RecordType, SearchResult } 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 static USER_AGENTS = [\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0',\r\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15',\r\n ]\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<SearchResult> {\r\n if (!searchKey) {\r\n return {\r\n success: false,\r\n data: [],\r\n error: 'Search key is required'\r\n }\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 success: false,\r\n data: [],\r\n error: 'Failed to retrieve API key'\r\n }\r\n }\r\n }\r\n\r\n const jsonResult = await MetacriticService.sendSearchWebRequest(searchKey, this.apiKey, recordType)\r\n if (!jsonResult) {\r\n return {\r\n success: false,\r\n data: [],\r\n error: 'Failed to fetch search results'\r\n }\r\n }\r\n\r\n try {\r\n JSON.parse(jsonResult)\r\n } catch {\r\n return {\r\n success: false,\r\n data: [],\r\n error: 'Invalid search response format'\r\n }\r\n }\r\n\r\n return {\r\n success: true,\r\n data: parseSearchJsonResult(jsonResult, searchKey, this.minSimilarity, sortBySimilarity)\r\n }\r\n }\r\n\r\n async getDetail(searchKey: string, recordType: RecordType, sortBySimilarity: boolean = true): Promise<DetailResult> {\r\n if (!searchKey) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'Search key is required'\r\n }\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 success: false,\r\n data: null,\r\n error: 'Failed to retrieve API key'\r\n }\r\n }\r\n }\r\n\r\n const searchResult = await this.search(searchKey, recordType, sortBySimilarity)\r\n if (!searchResult.success) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: searchResult.error || 'Failed to search detail entry'\r\n }\r\n }\r\n\r\n if (searchResult.data.length === 0) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'No matching entry found'\r\n }\r\n }\r\n\r\n const detailResult = await MetacriticService.sendDetailWebRequest(searchResult.data[0].slug, this.apiKey, recordType)\r\n\r\n if (!detailResult) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'Failed to fetch detail result'\r\n }\r\n }\r\n\r\n try {\r\n JSON.parse(detailResult)\r\n } catch {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'Invalid detail response format'\r\n }\r\n }\r\n\r\n const parsedDetail = parseDetailJsonResult(detailResult, searchResult.data[0].must)\r\n if (!parsedDetail) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: 'Invalid detail response format'\r\n }\r\n }\r\n\r\n return {\r\n success: true,\r\n data: parsedDetail\r\n }\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 = MetacriticService.USER_AGENTS[Math.floor(Math.random() * MetacriticService.USER_AGENTS.length)]\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"],"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
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "TypeScript library to extrapolate data from Metacritic.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -36,7 +36,6 @@
|
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/jest": "^29.5.14",
|
|
39
|
-
"@types/user-agents": "^1.0.4",
|
|
40
39
|
"jest": "^29.7.0",
|
|
41
40
|
"ts-jest": "^29.3.1",
|
|
42
41
|
"ts-node": "^10.9.2",
|