feat(files): every file sits in a library folder, and keeps its own expiry
The Files library starts here. A user's every download, clip and upload now lands in a source folder made per origin (YouTube, Plex clips, uploads, one per other site), decided once in downloads/folders.py at every place a job is made. How long a file stays moves from the shared asset to the user's job: stamped from its folder when it is done, never for a file the user keeps. The GC warns each user once, then takes the expired file out of their library; the cache cap never evicts a kept file, and an evicted one leaves every holder's library. The media mount is /files now (FILES_ROOT, FILES_HOST_PATH, volume siftlode_files); docs/self-hosting.md says how to carry an old volume over. Migration 0102 places every existing job and carries each done file's expiry over from its asset. safe_abs_path takes a missing path and answers None, which drops one baselined type error.
This commit is contained in:
@@ -74,6 +74,9 @@ const job = (id: number, status: DownloadJob["status"]): DownloadJob => ({
|
||||
extra_links: [],
|
||||
can_download: status === "done",
|
||||
expired: false,
|
||||
folder_id: null,
|
||||
kept: false,
|
||||
expires_at: null,
|
||||
asset: null,
|
||||
});
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ const baseJob: DownloadJob = {
|
||||
extra_links: [],
|
||||
can_download: true,
|
||||
expired: false,
|
||||
folder_id: null,
|
||||
kept: false,
|
||||
expires_at: null,
|
||||
asset: null,
|
||||
};
|
||||
|
||||
@@ -73,7 +76,6 @@ const withAsset = (over: Partial<NonNullable<DownloadJob["asset"]>>): DownloadJo
|
||||
container: null,
|
||||
media_kind: "video",
|
||||
thumbnail_url: null,
|
||||
expires_at: null,
|
||||
...over,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -52,6 +52,9 @@ const baseJob: DownloadJob = {
|
||||
extra_links: [],
|
||||
can_download: true,
|
||||
expired: false,
|
||||
folder_id: null,
|
||||
kept: false,
|
||||
expires_at: null,
|
||||
asset: null,
|
||||
};
|
||||
|
||||
@@ -74,7 +77,6 @@ const withAsset = (
|
||||
container,
|
||||
media_kind: "video",
|
||||
thumbnail_url: null,
|
||||
expires_at: null,
|
||||
...over,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -40,7 +40,6 @@ interface DownloadAsset {
|
||||
// while still in flight (set at finalize). See backend downloads/mediakind.py.
|
||||
media_kind: "video" | "audio" | "file" | null;
|
||||
thumbnail_url: string | null;
|
||||
expires_at: string | null;
|
||||
}
|
||||
|
||||
export type DownloadStatus = "queued" | "running" | "paused" | "done" | "error" | "canceled";
|
||||
@@ -107,6 +106,11 @@ export interface DownloadJob {
|
||||
edit_spec?: EditSpec | Omit<ClipRequest, "display_name"> | null;
|
||||
can_download: boolean;
|
||||
expired: boolean;
|
||||
// The Files library (backend downloads/folders): its folder, whether the user keeps it past the
|
||||
// folder's retention, and when it goes (null = never).
|
||||
folder_id: number | null;
|
||||
kept: boolean;
|
||||
expires_at: string | null;
|
||||
asset: DownloadAsset | null;
|
||||
shared?: boolean;
|
||||
user_email?: string;
|
||||
|
||||
Reference in New Issue
Block a user