Move feed, channels, sync, playlists, tags, notifications, saved-views and quota out of the lib/api.ts god object into per-domain slice modules, each owning its own types + method slice and spread into the façade. The `api` object shape and all 54 call sites are unchanged; account/auth/setup/admin stay inline until S2. Reunites the interleaved tags read/CRUD that motivated the epic.
92 lines
3.4 KiB
TypeScript
92 lines
3.4 KiB
TypeScript
// Playlists: local + YouTube-linked, item add/remove/reorder, the built-in Watch later shortcut,
|
|
// and the push-to-YouTube plan/apply flow. Only touches core's req.
|
|
import { req } from "./core";
|
|
import type { Video } from "./feed";
|
|
|
|
// Not exported: consumers compare `playlist.kind`/`.source` against string literals, they never
|
|
// name these types — keeping them local satisfies the no-unused-exports gate (knip).
|
|
type PlaylistKind = "user" | "watch_later";
|
|
type PlaylistSource = "local" | "youtube";
|
|
|
|
export interface Playlist {
|
|
id: number;
|
|
name: string;
|
|
kind: PlaylistKind;
|
|
source: PlaylistSource;
|
|
yt_playlist_id: string | null;
|
|
dirty: boolean; // linked local playlist has edits not yet pushed to YouTube
|
|
item_count: number;
|
|
total_duration_seconds: number;
|
|
cover_thumbnail: string | null;
|
|
has_video?: boolean; // only set when listed with ?contains=<video_id>
|
|
}
|
|
|
|
export interface PlaylistDetail {
|
|
id: number;
|
|
name: string;
|
|
kind: PlaylistKind;
|
|
source: PlaylistSource;
|
|
yt_playlist_id: string | null;
|
|
dirty: boolean;
|
|
items: Video[];
|
|
}
|
|
|
|
export interface PushPlan {
|
|
action: "create" | "update";
|
|
to_insert: number;
|
|
to_delete: number;
|
|
to_reorder: number;
|
|
yt_extra: number; // items on YouTube that the push would remove (divergence)
|
|
units_estimate: number;
|
|
remaining_today: number;
|
|
affordable: boolean;
|
|
}
|
|
|
|
export interface PushResult {
|
|
created: number;
|
|
inserted: number;
|
|
deleted: number;
|
|
reordered: number;
|
|
failures: string[];
|
|
yt_playlist_id: string | null;
|
|
}
|
|
|
|
export const playlistsApi = {
|
|
playlists: (containsVideoId?: string): Promise<Playlist[]> =>
|
|
req(`/api/playlists${containsVideoId ? `?contains=${containsVideoId}` : ""}`),
|
|
playlist: (id: number): Promise<PlaylistDetail> => req(`/api/playlists/${id}`),
|
|
createPlaylist: (name: string): Promise<Playlist> =>
|
|
req("/api/playlists", { method: "POST", body: JSON.stringify({ name }) }),
|
|
renamePlaylist: (id: number, name: string): Promise<Playlist> =>
|
|
req(`/api/playlists/${id}`, { method: "PATCH", body: JSON.stringify({ name }) }),
|
|
deletePlaylist: (id: number, onYoutube = false) =>
|
|
req(`/api/playlists/${id}${onYoutube ? "?on_youtube=true" : ""}`, { method: "DELETE" }),
|
|
addToPlaylist: (id: number, videoId: string) =>
|
|
req(`/api/playlists/${id}/items`, {
|
|
method: "POST",
|
|
body: JSON.stringify({ video_id: videoId }),
|
|
}),
|
|
removeFromPlaylist: (id: number, videoId: string) =>
|
|
req(`/api/playlists/${id}/items/${videoId}`, { method: "DELETE" }),
|
|
reorderPlaylist: (id: number, videoIds: string[]) =>
|
|
req(`/api/playlists/${id}/order`, {
|
|
method: "PUT",
|
|
body: JSON.stringify({ video_ids: videoIds }),
|
|
}),
|
|
// Bookmark shortcut for the built-in Watch later playlist.
|
|
watchLaterAdd: (videoId: string) =>
|
|
req("/api/playlists/watch-later", {
|
|
method: "POST",
|
|
body: JSON.stringify({ video_id: videoId }),
|
|
}),
|
|
watchLaterRemove: (videoId: string) =>
|
|
req(`/api/playlists/watch-later/${videoId}`, { method: "DELETE" }),
|
|
syncYoutubePlaylists: (): Promise<{ synced: number; reason?: string }> =>
|
|
req("/api/playlists/sync-youtube", { method: "POST" }),
|
|
revertPlaylist: (id: number): Promise<{ items: number }> =>
|
|
req(`/api/playlists/${id}/revert-youtube`, { method: "POST" }),
|
|
playlistPushPlan: (id: number): Promise<PushPlan> => req(`/api/playlists/${id}/push-plan`),
|
|
pushPlaylist: (id: number): Promise<PushResult> =>
|
|
req(`/api/playlists/${id}/push`, { method: "POST" }),
|
|
};
|