import { HttpError } from "@sttracker/plugin-sdk/http"; import { definePlugin } from "@sttracker/plugin-sdk/types"; import { BASE, clientCreds, post, toCredentials, type TokenResponse } from "./src/api.ts"; import { searchHint } from "./src/map.ts"; import { pull, pullChanges } from "./src/sync.ts"; interface DeviceCode { device_code: string; user_code: string; verification_url: string; expires_in: number; interval: number } export default definePlugin({ manifest: { id: "trakt", name: "Trakt", version: "0.1.0", sdkVersion: 1, rateLimit: { perMinute: 100 }, mediaTypes: ["movie", "show", "season", "episode"], capabilities: ["pull", "pullChanges", "lookup"], config: [ { key: "clientId", label: "Client ID", type: "string", required: true }, { key: "clientSecret", label: "Client secret", type: "secret", required: true }, ], attribution: "STTracker uses Trakt but is not endorsed or certified by Trakt.", }, deviceAuth: { async start(ctx) { const { clientId } = clientCreds(ctx); const d = await post(ctx, "/oauth/device/code", { client_id: clientId }); return { userCode: d.user_code, verificationUrl: d.verification_url, deviceCode: d.device_code, intervalSec: d.interval, expiresInSec: d.expires_in }; }, async poll(ctx, deviceCode) { const { clientId, clientSecret } = clientCreds(ctx); try { const t = await post(ctx, "/oauth/device/token", { code: deviceCode, client_id: clientId, client_secret: clientSecret }); return toCredentials(t, ctx.now()); } catch (e) { // 400 = pending, 429 = polling too fast; anything else (404/409/410/418) is terminal. if (e instanceof HttpError && (e.status === 400 || e.status === 429)) return "pending"; throw e; } }, }, pull, pullChanges, async lookup(ctx, q) { const { clientId } = clientCreds(ctx); const type = q.mediaType === "movie" || q.mediaType === "show" ? q.mediaType : "movie,show"; const res = await ctx.http.json[0][]>(`${BASE}/search/${type}`, { headers: { "trakt-api-version": "2", "trakt-api-key": clientId }, query: { query: q.title, years: q.year }, }); return res.flatMap((r) => { const h = searchHint(r); return h ? [h] : []; }); }, });