mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 09:36:08 +00:00
feat: make asset downloads opt-in via "Include assets" toggle
Project/selected downloads now include only videos by default. Add an "Include assets" checkbox toggle to both download dropdowns (default off) that adds b-rolls and other attached assets to the download when enabled. - buildProjectDownloadManifest gains an includeAssets option (default false). - Download route reads ?assets=1 and passes it through.
This commit is contained in:
@@ -27,8 +27,10 @@ import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
@@ -105,6 +107,7 @@ export function ProjectContentClient({
|
||||
const [selectedVideoIds, setSelectedVideoIds] = useState<string[]>([]);
|
||||
const [selectionMode, setSelectionMode] = useState(false);
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const [includeAssetsInDownload, setIncludeAssetsInDownload] = useState(false);
|
||||
const [isDeletingSelected, setIsDeletingSelected] = useState(false);
|
||||
const [showDeleteSelectedDialog, setShowDeleteSelectedDialog] = useState(false);
|
||||
const [showMoveSelectedDialog, setShowMoveSelectedDialog] = useState(false);
|
||||
@@ -184,7 +187,7 @@ export function ProjectContentClient({
|
||||
}, []);
|
||||
|
||||
const startProjectDownload = useCallback(
|
||||
async (videoIds?: string[], options?: { allVersions?: boolean }) => {
|
||||
async (videoIds?: string[], options?: { allVersions?: boolean; includeAssets?: boolean }) => {
|
||||
if (!canDownloadProject || isDownloading) return;
|
||||
|
||||
const searchParams = new URLSearchParams();
|
||||
@@ -194,6 +197,9 @@ export function ProjectContentClient({
|
||||
if (options?.allVersions) {
|
||||
searchParams.set('versions', 'all');
|
||||
}
|
||||
if (options?.includeAssets) {
|
||||
searchParams.set('assets', '1');
|
||||
}
|
||||
const query = searchParams.toString() ? `?${searchParams.toString()}` : '';
|
||||
|
||||
setIsDownloading(true);
|
||||
@@ -359,11 +365,28 @@ export function ProjectContentClient({
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => startProjectDownload()}>
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={includeAssetsInDownload}
|
||||
onCheckedChange={(checked) => setIncludeAssetsInDownload(checked === true)}
|
||||
onSelect={(event) => event.preventDefault()}
|
||||
>
|
||||
Include assets
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() =>
|
||||
startProjectDownload(undefined, { includeAssets: includeAssetsInDownload })
|
||||
}
|
||||
>
|
||||
Latest version only
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => startProjectDownload(undefined, { allVersions: true })}
|
||||
onClick={() =>
|
||||
startProjectDownload(undefined, {
|
||||
allVersions: true,
|
||||
includeAssets: includeAssetsInDownload,
|
||||
})
|
||||
}
|
||||
>
|
||||
All versions
|
||||
</DropdownMenuItem>
|
||||
@@ -446,11 +469,30 @@ export function ProjectContentClient({
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => startProjectDownload(selectedVideoIds)}>
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={includeAssetsInDownload}
|
||||
onCheckedChange={(checked) => setIncludeAssetsInDownload(checked === true)}
|
||||
onSelect={(event) => event.preventDefault()}
|
||||
>
|
||||
Include assets
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() =>
|
||||
startProjectDownload(selectedVideoIds, {
|
||||
includeAssets: includeAssetsInDownload,
|
||||
})
|
||||
}
|
||||
>
|
||||
Latest version only
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => startProjectDownload(selectedVideoIds, { allVersions: true })}
|
||||
onClick={() =>
|
||||
startProjectDownload(selectedVideoIds, {
|
||||
allVersions: true,
|
||||
includeAssets: includeAssetsInDownload,
|
||||
})
|
||||
}
|
||||
>
|
||||
All versions
|
||||
</DropdownMenuItem>
|
||||
|
||||
@@ -23,6 +23,7 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
|
||||
const { projectId } = await params;
|
||||
const requestedVideoIds = parseRequestedVideoIds(request.nextUrl.searchParams.get('videoIds'));
|
||||
const includeAllVersions = request.nextUrl.searchParams.get('versions') === 'all';
|
||||
const includeAssets = request.nextUrl.searchParams.get('assets') === '1';
|
||||
|
||||
if (requestedVideoIds && requestedVideoIds.length === 0) {
|
||||
return apiErrors.badRequest('At least one video must be selected for download');
|
||||
@@ -93,7 +94,10 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
|
||||
}
|
||||
}
|
||||
|
||||
const manifest = buildProjectDownloadManifest(project.name, videos, { includeAllVersions });
|
||||
const manifest = buildProjectDownloadManifest(project.name, videos, {
|
||||
includeAllVersions,
|
||||
includeAssets,
|
||||
});
|
||||
const validationError = validateProjectDownloadManifest(manifest);
|
||||
if (validationError) {
|
||||
return apiErrors.badRequest(validationError);
|
||||
|
||||
+13
-9
@@ -223,6 +223,8 @@ function bigintToSafeNumber(value: bigint): number | null {
|
||||
export type BuildProjectDownloadManifestOptions = {
|
||||
/** Include every version of each video. Defaults to latest version only. */
|
||||
includeAllVersions?: boolean;
|
||||
/** Include b-rolls and other attached assets. Defaults to videos only. */
|
||||
includeAssets?: boolean;
|
||||
};
|
||||
|
||||
export function buildProjectDownloadManifest(
|
||||
@@ -230,7 +232,7 @@ export function buildProjectDownloadManifest(
|
||||
videos: VideoRow[],
|
||||
options: BuildProjectDownloadManifestOptions = {}
|
||||
): ProjectDownloadManifest {
|
||||
const { includeAllVersions = false } = options;
|
||||
const { includeAllVersions = false, includeAssets = false } = options;
|
||||
const files: ProjectDownloadManifestFile[] = [];
|
||||
const usedNames = new Set<string>();
|
||||
|
||||
@@ -257,15 +259,17 @@ export function buildProjectDownloadManifest(
|
||||
});
|
||||
}
|
||||
|
||||
for (const asset of video.assets) {
|
||||
const url = assetDownloadUrl(video.id, asset);
|
||||
if (!url) continue;
|
||||
if (includeAssets) {
|
||||
for (const asset of video.assets) {
|
||||
const url = assetDownloadUrl(video.id, asset);
|
||||
if (!url) continue;
|
||||
|
||||
files.push({
|
||||
fileName: makeUniqueName(buildAssetFileName(videoIndex, videoTitle, asset), usedNames),
|
||||
url,
|
||||
sizeBytes: bigintToSafeNumber(asset.sizeBytes),
|
||||
});
|
||||
files.push({
|
||||
fileName: makeUniqueName(buildAssetFileName(videoIndex, videoTitle, asset), usedNames),
|
||||
url,
|
||||
sizeBytes: bigintToSafeNumber(asset.sizeBytes),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user