diff --git a/deno.lock b/deno.lock index 796244a..6a87656 100644 --- a/deno.lock +++ b/deno.lock @@ -5,7 +5,6 @@ "npm:@prisma/client@*": "6.6.0_prisma@6.6.0", "npm:@prisma/client@6.6.0": "6.6.0_prisma@6.6.0", "npm:@types/node@*": "22.5.4", - "npm:axios@^1.4.0": "1.7.7", "npm:city-timezones@^1.2.1": "1.3.0", "npm:discord.js@14.11.0": "14.11.0", "npm:discord.js@^14.18.0": "14.18.0", @@ -849,17 +848,6 @@ "array-ify@1.0.0": { "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==" }, - "asynckit@0.4.0": { - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "axios@1.7.7": { - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "dependencies": [ - "follow-redirects", - "form-data", - "proxy-from-env" - ] - }, "balanced-match@1.0.2": { "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, @@ -1027,12 +1015,6 @@ "color-name@1.1.4": { "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "combined-stream@1.0.8": { - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": [ - "delayed-stream" - ] - }, "common-ancestor-path@1.0.1": { "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==" }, @@ -1121,9 +1103,6 @@ "deep-extend@0.6.0": { "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, - "delayed-stream@1.0.0": { - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, "diff@5.2.0": { "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==" }, @@ -1386,9 +1365,6 @@ "super-regex" ] }, - "follow-redirects@1.15.9": { - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" - }, "foreground-child@3.3.1": { "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dependencies": [ @@ -1396,14 +1372,6 @@ "signal-exit" ] }, - "form-data@4.0.1": { - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dependencies": [ - "asynckit", - "combined-stream", - "mime-types" - ] - }, "from2@2.3.0": { "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", "dependencies": [ @@ -1977,15 +1945,6 @@ "picomatch@2.3.1" ] }, - "mime-db@1.52.0": { - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types@2.1.35": { - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": [ - "mime-db" - ] - }, "mime@4.0.7": { "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==" }, @@ -2577,9 +2536,6 @@ "protocols@2.0.1": { "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" }, - "proxy-from-env@1.1.0": { - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "qrcode-terminal@0.12.0": { "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==" }, @@ -3251,7 +3207,6 @@ "dependencies": [ "npm:@prisma/client@*", "npm:@prisma/client@6.6.0", - "npm:axios@^1.4.0", "npm:city-timezones@^1.2.1", "npm:discord.js@^14.18.0", "npm:moment-timezone@~0.5.43", diff --git a/import_map.json b/import_map.json index ca86444..b7d4e73 100644 --- a/import_map.json +++ b/import_map.json @@ -9,7 +9,6 @@ "@prisma/client/runtime/library": "npm:@prisma/client/runtime/library", "npm:@prisma/client": "npm:@prisma/client@6.6.0", "npm:prisma": "npm:prisma@^6.6.0", - "npm:axios": "npm:axios@^1.4.0", "npm:city-timezones": "npm:city-timezones@^1.2.1", "npm:moment-timezone": "npm:moment-timezone@^0.5.43", "npm:parse-url": "npm:parse-url@^8.1.0", diff --git a/src/lib/tvdb.ts b/src/lib/tvdb.ts index bdfd617..182ccb3 100644 --- a/src/lib/tvdb.ts +++ b/src/lib/tvdb.ts @@ -1,4 +1,3 @@ -import axios, { AxiosError, type AxiosRequestConfig } from "npm:axios" import { type SearchByRemoteIdResult, type SearchResult, @@ -6,49 +5,35 @@ import { } from "interfaces/tvdb.generated.ts" import { getEnv } from "lib/env.ts" -let token: string | undefined +const baseURL = "https://api4.thetvdb.com/v4" as const -async function getToken(): Promise { - if (token != null) return token +let token: string | undefined - try { - const response = await axios.post("https://api4.thetvdb.com/v4/login", { - apikey: getEnv("TVDB_API_KEY"), - pin: getEnv("TVDB_USER_PIN"), +async function getAuthToken(): Promise<{ Authorization: string }> { + if (token == null) { + const response = await fetch(`${baseURL}/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + apikey: getEnv("TVDB_API_KEY"), + pin: getEnv("TVDB_USER_PIN"), + }), }) - token = response.data.data.token - return response.data.data.token - } catch (error) { - logPossibleAxiosError(error, "Getting TVDB Token") - } - return undefined -} + if (!response.ok) { + throw new Error("Failed to get TVDB token", { + cause: await response.text(), + }) + } -function logPossibleAxiosError(error: unknown, errorPrefix: string): void { - if (error instanceof AxiosError) { - console.error(`Error ${errorPrefix}:`, { - url: error.config?.url, - code: error.code, - data: error.response?.data, - status: error.response?.status, - }) - } else { - console.error(`Unexpected Error ${errorPrefix}:`, error) + const data = await response.json() + if (!data?.data?.token) { + throw new Error("Invalid API response: Missing token") + } + token = data.data.token } -} -async function axiosOptions(): Promise { - const token = await getToken() - if (token == null) { - throw new Error("Failed to get TVDB token, couldn't build axios options") - } - return { - baseURL: "https://api4.thetvdb.com/v4", - headers: { - Authorization: `Bearer ${token}`, - }, - } + return { Authorization: `Bearer ${token}` } } export async function getSeriesByImdbId( @@ -109,21 +94,30 @@ export async function getSeries( tvdbId: number, ): Promise { try { - const options = await axiosOptions() - const response = await axios.get<{ - data?: SeriesExtendedRecord - }>(`/series/${tvdbId}/extended`, { - ...options, - headers: options.headers, - params: { - short: true, - meta: "episodes,translations", - }, + const params = new URLSearchParams({ + short: "true", + meta: "episodes,translations", }) + const response = await fetch( + `${baseURL}/series/${tvdbId}/extended?${params}`, + { + headers: await getAuthToken(), + }, + ) + + if (!response.ok) { + console.error(`Error Getting Extended Show Data:`, { + url: response.url, + status: response.status, + data: await response.text(), + }) + return undefined + } - return response.data?.data + const data: { data?: SeriesExtendedRecord } = await response.json() + return data.data } catch (error) { - logPossibleAxiosError(error, "Getting Extended Show Data") + console.error(`Unexpected Error Getting Extended Show Data:`, error) } return undefined } @@ -132,14 +126,26 @@ async function searchSeriesByImdbId( imdbId: string, ): Promise { try { - const options = await axiosOptions() - const response = await axios.get<{ - data?: SearchByRemoteIdResult[] - }>(`/search/remoteid/${imdbId}`, options) + const response = await fetch( + `${baseURL}/search/remoteid/${imdbId}`, + { + headers: await getAuthToken(), + }, + ) + + if (!response.ok) { + console.error(`Error Searching Series by IMDB ID:`, { + url: response.url, + status: response.status, + data: await response.text(), + }) + return undefined + } - return response.data.data?.at(0) + const data: { data?: SearchByRemoteIdResult[] } = await response.json() + return data.data?.at(0) } catch (error) { - logPossibleAxiosError(error, "Searching Series by IMDB ID") + console.error(`Unexpected Error Searching Series by IMDB ID:`, error) } return undefined } @@ -148,18 +154,35 @@ async function searchSeriesByName( query: string, ): Promise { try { - const options = await axiosOptions() - const response = await axios.get<{ - data: SearchResult[] - }>(`/search?type=series&limit=10&q=${query}`, options) + const params = new URLSearchParams({ + type: "series", + limit: "10", + q: query, + }) + const response = await fetch( + `${baseURL}/search?${params}`, + { + headers: await getAuthToken(), + }, + ) + + if (!response.ok) { + console.error(`Error Searching Series:`, { + url: response.url, + status: response.status, + data: await response.text(), + }) + return undefined + } - const searchResult = response.data?.data + const data: { data: SearchResult[] } = await response.json() + const searchResult = data?.data if (searchResult == null || searchResult[0].tvdb_id == null) { return undefined } return searchResult } catch (error) { - logPossibleAxiosError(error, "Searching Series") + console.error(`Unexpected Error Searching Series:`, error) } return undefined }