feat(playlists): frontend foundation — page, add-to-playlist, nav
Add the Playlists page (left rail of playlists + detail with drag&drop reorder, inline rename, one-step delete, remove item, Play all / row-click playback via the existing PlayerModal) and an AddToPlaylist popover (portaled, multi-toggle + inline new-playlist) wired into the VideoCard hover overlay and the PlayerModal. New api client methods + Playlist types, a 'playlists' page route + account-menu entry, and trilingual strings. Local only — YouTube sync comes in later phases.
This commit is contained in:
parent
da9dcf93d5
commit
37804ee393
14 changed files with 634 additions and 4 deletions
|
|
@ -68,6 +68,26 @@ export interface FeedResponse {
|
|||
limit: number;
|
||||
}
|
||||
|
||||
export interface Playlist {
|
||||
id: number;
|
||||
name: string;
|
||||
kind: string; // "user" | "watch_later"
|
||||
source: string; // "local" | "youtube"
|
||||
yt_playlist_id: string | null;
|
||||
item_count: number;
|
||||
cover_thumbnail: string | null;
|
||||
has_video?: boolean; // only set when listed with ?contains=<video_id>
|
||||
}
|
||||
|
||||
export interface PlaylistDetail {
|
||||
id: number;
|
||||
name: string;
|
||||
kind: string;
|
||||
source: string;
|
||||
yt_playlist_id: string | null;
|
||||
items: Video[];
|
||||
}
|
||||
|
||||
export interface FeedFilters {
|
||||
tags: number[];
|
||||
tagMode: "or" | "and";
|
||||
|
|
@ -294,6 +314,28 @@ export const api = {
|
|||
req(`/api/channels/${id}/subscription`, { method: "DELETE" }),
|
||||
syncSubscriptions: () => req("/api/sync/subscriptions", { method: "POST" }),
|
||||
|
||||
// --- playlists ---
|
||||
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) => req(`/api/playlists/${id}`, { 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 }),
|
||||
}),
|
||||
|
||||
// --- quota usage ---
|
||||
myUsage: (): Promise<MyUsage> => req("/api/quota/my-usage"),
|
||||
adminQuota: (days = 30): Promise<AdminQuota> => req(`/api/quota/admin?days=${days}`),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue