feat(playlists): Sync to YouTube UI + delete-on-YouTube choice
Editable local playlists get an Export/Sync to YouTube button (write-scope gated): it fetches a dry-run plan, shows a confirm with the change counts, quota estimate and divergence warning, then pushes. An 'unsynced changes' badge and an accented YouTube icon mark linked playlists with local edits. Deleting a linked playlist offers 'delete on YouTube too' vs 'here only'. Trilingual strings (HU/EN/DE).
This commit is contained in:
parent
b89c00f909
commit
05c2399b94
6 changed files with 190 additions and 9 deletions
|
|
@ -75,6 +75,7 @@ export interface Playlist {
|
|||
kind: string; // "user" | "watch_later"
|
||||
source: string; // "local" | "youtube"
|
||||
yt_playlist_id: string | null;
|
||||
dirty: boolean; // linked local playlist has edits not yet pushed to YouTube
|
||||
item_count: number;
|
||||
cover_thumbnail: string | null;
|
||||
has_video?: boolean; // only set when listed with ?contains=<video_id>
|
||||
|
|
@ -86,9 +87,30 @@ export interface PlaylistDetail {
|
|||
kind: string;
|
||||
source: string;
|
||||
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 interface FeedFilters {
|
||||
tags: number[];
|
||||
tagMode: "or" | "and";
|
||||
|
|
@ -323,7 +345,8 @@ export const api = {
|
|||
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" }),
|
||||
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",
|
||||
|
|
@ -346,6 +369,10 @@ export const api = {
|
|||
req(`/api/playlists/watch-later/${videoId}`, { method: "DELETE" }),
|
||||
syncYoutubePlaylists: (): Promise<{ synced: number; reason?: string }> =>
|
||||
req("/api/playlists/sync-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" }),
|
||||
|
||||
// --- quota usage ---
|
||||
myUsage: (): Promise<MyUsage> => req("/api/quota/my-usage"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue