Files
streamyfin/utils/streamystats/api.ts

325 lines
8.9 KiB
TypeScript

import axios from "axios";
import type {
AddWatchlistItemResponse,
CreateWatchlistRequest,
CreateWatchlistResponse,
DeleteWatchlistResponse,
GetWatchlistItemsParams,
GetWatchlistsResponse,
RemoveWatchlistItemResponse,
StreamystatsRecommendationsFullResponse,
StreamystatsRecommendationsIdsResponse,
StreamystatsRecommendationsParams,
StreamystatsSearchFullResponse,
StreamystatsSearchIdsResponse,
StreamystatsSearchParams,
StreamystatsWatchlistDetailFullResponse,
StreamystatsWatchlistDetailIdsResponse,
StreamystatsWatchlistDetailParams,
StreamystatsWatchlistsFullResponse,
StreamystatsWatchlistsParams,
UpdateWatchlistRequest,
UpdateWatchlistResponse,
} from "./types";
interface StreamystatsApiConfig {
serverUrl: string;
jellyfinToken: string;
}
export const createStreamystatsApi = (config: StreamystatsApiConfig) => {
const { serverUrl, jellyfinToken } = config;
const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl;
const headers = {
Authorization: `MediaBrowser Token="${jellyfinToken}"`,
};
const search = async (
params: StreamystatsSearchParams,
signal?: AbortSignal,
): Promise<
StreamystatsSearchIdsResponse | StreamystatsSearchFullResponse
> => {
const queryParams = new URLSearchParams();
queryParams.set("q", params.q);
if (params.limit) {
queryParams.set("limit", params.limit.toString());
}
if (params.format) {
queryParams.set("format", params.format);
}
if (params.type) {
queryParams.set("type", params.type);
}
const url = `${baseUrl}/api/search?${queryParams.toString()}`;
const response = await axios.get(url, { headers, signal });
return response.data;
};
const searchIds = async (
query: string,
type?: StreamystatsSearchParams["type"],
limit?: number,
signal?: AbortSignal,
): Promise<StreamystatsSearchIdsResponse> => {
return search(
{
q: query,
format: "ids",
type,
limit,
},
signal,
) as Promise<StreamystatsSearchIdsResponse>;
};
const searchFull = async (
query: string,
type?: StreamystatsSearchParams["type"],
limit?: number,
): Promise<StreamystatsSearchFullResponse> => {
return search({
q: query,
format: "full",
type,
limit,
}) as Promise<StreamystatsSearchFullResponse>;
};
const getRecommendations = async (
params: StreamystatsRecommendationsParams,
): Promise<
| StreamystatsRecommendationsIdsResponse
| StreamystatsRecommendationsFullResponse
> => {
const queryParams = new URLSearchParams();
if (params.serverId) {
queryParams.set("serverId", params.serverId.toString());
}
if (params.serverName) {
queryParams.set("serverName", params.serverName);
}
if (params.jellyfinServerId) {
queryParams.set("jellyfinServerId", params.jellyfinServerId);
}
if (params.limit) {
queryParams.set("limit", params.limit.toString());
}
if (params.type) {
queryParams.set("type", params.type);
}
if (params.range) {
queryParams.set("range", params.range);
}
if (params.format) {
queryParams.set("format", params.format);
}
if (params.includeBasedOn !== undefined) {
queryParams.set("includeBasedOn", params.includeBasedOn.toString());
}
if (params.includeReasons !== undefined) {
queryParams.set("includeReasons", params.includeReasons.toString());
}
const url = `${baseUrl}/api/recommendations?${queryParams.toString()}`;
const response = await axios.get(url, { headers });
return response.data;
};
const getRecommendationIds = async (
jellyfinServerId: string,
type?: StreamystatsRecommendationsParams["type"],
limit?: number,
): Promise<StreamystatsRecommendationsIdsResponse> => {
return getRecommendations({
jellyfinServerId,
format: "ids",
type,
limit,
includeBasedOn: false,
includeReasons: false,
}) as Promise<StreamystatsRecommendationsIdsResponse>;
};
const getPromotedWatchlists = async (
params: StreamystatsWatchlistsParams,
): Promise<StreamystatsWatchlistsFullResponse> => {
const queryParams = new URLSearchParams();
if (params.serverId) {
queryParams.set("serverId", params.serverId.toString());
}
if (params.serverName) {
queryParams.set("serverName", params.serverName);
}
if (params.serverUrl) {
queryParams.set("serverUrl", params.serverUrl);
}
if (params.jellyfinServerId) {
queryParams.set("jellyfinServerId", params.jellyfinServerId);
}
if (params.limit) {
queryParams.set("limit", params.limit.toString());
}
if (params.format) {
queryParams.set("format", params.format);
}
if (params.includePreview !== undefined) {
queryParams.set("includePreview", params.includePreview.toString());
}
const url = `${baseUrl}/api/watchlists/promoted?${queryParams.toString()}`;
const response = await axios.get(url, { headers });
return response.data;
};
const getWatchlistItemIds = async (
params: StreamystatsWatchlistDetailParams,
): Promise<StreamystatsWatchlistDetailIdsResponse> => {
const queryParams = new URLSearchParams();
queryParams.set("format", "ids");
if (params.serverId) {
queryParams.set("serverId", params.serverId.toString());
}
if (params.serverName) {
queryParams.set("serverName", params.serverName);
}
if (params.serverUrl) {
queryParams.set("serverUrl", params.serverUrl);
}
if (params.jellyfinServerId) {
queryParams.set("jellyfinServerId", params.jellyfinServerId);
}
const url = `${baseUrl}/api/watchlists/${params.watchlistId}?${queryParams.toString()}`;
const response = await axios.get(url, { headers });
return response.data;
};
/**
* Get all watchlists (own + public)
* GET /api/watchlists
*/
const getWatchlists = async (): Promise<GetWatchlistsResponse> => {
const url = `${baseUrl}/api/watchlists`;
const response = await axios.get(url, { headers });
return response.data;
};
/**
* Create a new watchlist
* POST /api/watchlists
*/
const createWatchlist = async (
data: CreateWatchlistRequest,
): Promise<CreateWatchlistResponse> => {
const url = `${baseUrl}/api/watchlists`;
const response = await axios.post(url, data, { headers });
return response.data;
};
/**
* Get a single watchlist with items
* GET /api/watchlists/[id]
*/
const getWatchlistDetail = async (
watchlistId: number,
params?: GetWatchlistItemsParams,
): Promise<StreamystatsWatchlistDetailFullResponse> => {
const queryParams = new URLSearchParams();
queryParams.set("format", "full");
if (params?.type) {
queryParams.set("type", params.type);
}
if (params?.sort) {
queryParams.set("sort", params.sort);
}
const url = `${baseUrl}/api/watchlists/${watchlistId}?${queryParams.toString()}`;
const response = await axios.get(url, { headers });
return response.data;
};
/**
* Update a watchlist (owner only)
* PATCH /api/watchlists/[id]
*/
const updateWatchlist = async (
watchlistId: number,
data: UpdateWatchlistRequest,
): Promise<UpdateWatchlistResponse> => {
const url = `${baseUrl}/api/watchlists/${watchlistId}`;
const response = await axios.patch(url, data, { headers });
return response.data;
};
/**
* Delete a watchlist (owner only)
* DELETE /api/watchlists/[id]
*/
const deleteWatchlist = async (
watchlistId: number,
): Promise<DeleteWatchlistResponse> => {
const url = `${baseUrl}/api/watchlists/${watchlistId}`;
const response = await axios.delete(url, { headers });
return response.data;
};
/**
* Add an item to a watchlist (owner only)
* POST /api/watchlists/[id]/items
*/
const addWatchlistItem = async (
watchlistId: number,
itemId: string,
): Promise<AddWatchlistItemResponse> => {
const url = `${baseUrl}/api/watchlists/${watchlistId}/items`;
const response = await axios.post(url, { itemId }, { headers });
return response.data;
};
/**
* Remove an item from a watchlist (owner only)
* DELETE /api/watchlists/[id]/items/[itemId]
*/
const removeWatchlistItem = async (
watchlistId: number,
itemId: string,
): Promise<RemoveWatchlistItemResponse> => {
const url = `${baseUrl}/api/watchlists/${watchlistId}/items/${itemId}`;
const response = await axios.delete(url, { headers });
return response.data;
};
return {
search,
searchIds,
searchFull,
getRecommendations,
getRecommendationIds,
getPromotedWatchlists,
getWatchlistItemIds,
// Watchlist CRUD
getWatchlists,
createWatchlist,
getWatchlistDetail,
updateWatchlist,
deleteWatchlist,
addWatchlistItem,
removeWatchlistItem,
};
};
export type StreamystatsApi = ReturnType<typeof createStreamystatsApi>;