Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ export interface FrontierAPI {
*/
downloadLFSFile: (projectPath: string, oid: string, size: number) => Promise<Buffer>;

/**
* Resolve a (typically presigned) download URL for a single LFS object
* WITHOUT downloading the bytes. Used for streaming media directly from the
* object store (e.g. an R2 presigned URL fed to a <video> element).
*
* @returns href plus any headers the GET requires. When `header` is empty
* the URL is self-contained and safe for a browser to fetch directly.
* `expiresInMs` (when present) is how long the href stays valid.
* @throws Error if not authenticated, no remote URL, or the batch fails.
*/
getLFSDownloadUrl: (
projectPath: string,
oid: string,
size: number
) => Promise<{ href: string; header: Record<string, string>; expiresInMs?: number }>;

/**
* Get repository tree (list files in a directory) from GitLab
* @param projectUrl - The git URL of the project
Expand Down Expand Up @@ -538,6 +554,57 @@ export async function activate(context: vscode.ExtensionContext) {
}
},

getLFSDownloadUrl: async (
projectPath: string,
oid: string,
size: number
): Promise<{ href: string; header: Record<string, string>; expiresInMs?: number }> => {
const { GitService } = await import("./git/GitService");
const gitService = new GitService(stateManager);

// Validate inputs (mirror downloadLFSFile)
if (!projectPath) {
throw new Error("Project path is required");
}
if (!oid || !/^[a-f0-9]{64}$/i.test(oid)) {
throw new Error("Invalid OID format (expected 64-character hex string)");
}
if (!size || size <= 0) {
throw new Error("Invalid size (must be positive number)");
}

const remoteUrl = await gitService.getRemoteUrl(projectPath);
if (!remoteUrl) {
throw new Error(
"This project doesn't have a corresponding project on the server. It may not be set up for syncing."
);
}

const { GitService: GitServiceStatic } = await import("./git/GitService");
const { cleanUrl, auth: embedded } = GitServiceStatic.parseGitUrl(remoteUrl);
const lfsBaseUrl = cleanUrl.endsWith(".git") ? cleanUrl : `${cleanUrl}.git`;

let auth: { username?: string; password?: string; token?: string } | undefined =
embedded;
if (!auth || !auth.password) {
try {
const { GitLabService } = await import("./gitlab/GitLabService");
const gl = new GitLabService(authenticationProvider);
await gl.initializeWithRetry?.();
const token = await gl.getToken();
if (!token) {
throw new Error("Not authenticated. Please log in to stream media files.");
}
auth = { username: "oauth2", password: token };
} catch (e) {
throw new Error("Not authenticated. Please log in to stream media files.");
}
}

const { getLFSDownloadAction } = await import("./git/GitService");
return getLFSDownloadAction({ url: lfsBaseUrl, headers: {}, auth }, { oid, size });
},

getRepositoryTree: async (
projectUrl: string,
path: string = "",
Expand Down
Loading
Loading