Files

57 lines
2.3 KiB
TypeScript

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<DeviceCode>(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<TokenResponse>(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<Parameters<typeof searchHint>[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] : [];
});
},
});