feat(settings): upload a Google Takeout export in Settings › Account
A "YouTube history" section under YouTube access: the watch history and the Watch later csv are dropped or picked (one request each, each file's outcome said on its own), and the section then says what the import did - only the counts it produced - or that it is running, failed or was cut off by a restart. While it runs it asks again until it ends; a failed re-read shows the failure, not the previous answer. The drop zone the Files upload had inline is now ui/DropZone, used by both. The e2e seed clears the account's kept Takeout videos.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
// Feed, video watch-state/progress, and live YouTube search. `filterParams`/`feedQuery` translate
|
||||
// the shared FeedFilters into the query string the feed + facet endpoints both consume; they stay
|
||||
// module-local (no consumer names them). Only touches core's req/beacon.
|
||||
import { req, beacon } from "./core";
|
||||
import { req, beacon, uploadReq } from "./core";
|
||||
import type { RunStatus } from "./admin";
|
||||
import { TAG_CATEGORIES, type TagModes } from "./tagModes";
|
||||
|
||||
// Per-user watch status stored server-side (VALID_STATES in routes/feed.py). "in_progress" and
|
||||
@@ -148,6 +149,34 @@ function feedQuery(f: FeedFilters, cursor: string | null, limit: number): string
|
||||
return p.toString();
|
||||
}
|
||||
|
||||
// A Google Takeout file, uploaded: which one it was, the videos read from it, and its entries left
|
||||
// out (an ad, a view of something that is not a video, a row that could not be read).
|
||||
export interface TakeoutUpload {
|
||||
ok: true;
|
||||
kind: TakeoutKind;
|
||||
videos: number;
|
||||
entries: number;
|
||||
ads: number;
|
||||
other: number;
|
||||
malformed: number;
|
||||
}
|
||||
export type TakeoutKind = "watched" | "watch_later";
|
||||
// A kept video's outcome (`reader.models.TakeoutOutcome`), or "waiting" while the catalog does not
|
||||
// know it yet: watched / kept (the user's own state here stood) / short for the history; added /
|
||||
// present / missing for Watch later.
|
||||
export type TakeoutCounts = Partial<Record<string, number>>;
|
||||
export interface TakeoutStatus {
|
||||
run: {
|
||||
status: RunStatus;
|
||||
started_at: string | null;
|
||||
finished_at: string | null;
|
||||
result: Record<string, unknown> | null;
|
||||
failure: { failure: string; [key: string]: unknown } | null;
|
||||
} | null;
|
||||
watched?: TakeoutCounts;
|
||||
watch_later?: TakeoutCounts;
|
||||
}
|
||||
|
||||
export const feedApi = {
|
||||
feed: (f: FeedFilters, cursor: string | null, limit: number): Promise<FeedResponse> =>
|
||||
req(`/api/feed?${feedQuery(f, cursor, limit)}`),
|
||||
@@ -210,4 +239,7 @@ export const feedApi = {
|
||||
duration_seconds: Math.floor(durationSeconds),
|
||||
}),
|
||||
videoDetail: (id: string): Promise<VideoDetail> => req(`/api/videos/${id}`),
|
||||
// One file of a Google Takeout export, its bytes as they are; the server tells the two apart.
|
||||
takeoutUpload: (file: File): Promise<TakeoutUpload> => uploadReq("/api/takeout", file),
|
||||
takeoutStatus: (): Promise<TakeoutStatus> => req("/api/takeout"),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user