@huggingface/hub
Version:
Utilities to interact with the Hugging Face hub
93 lines (80 loc) • 2.51 kB
text/typescript
import { HUB_URL } from "../consts";
import { createApiError, InvalidApiResponseFormatError } from "../error";
import type { CredentialsParams, RepoDesignation } from "../types/public";
import { checkCredentials } from "../utils/checkCredentials";
import { toRepoId } from "../utils/toRepoId";
export interface FileDownloadInfoOutput {
size: number;
etag: string;
/**
* In case of LFS file, link to download directly from cloud provider
*/
downloadLink: string | null;
}
/**
* @returns null when the file doesn't exist
*/
export async function fileDownloadInfo(
params: {
repo: RepoDesignation;
path: string;
revision?: string;
hubUrl?: string;
/**
* Custom fetch function to use instead of the default one, for example to use a proxy or edit headers.
*/
fetch?: typeof fetch;
/**
* To get the raw pointer file behind a LFS file
*/
raw?: boolean;
/**
* To avoid the content-disposition header in the `downloadLink` for LFS files
*
* So that on browsers you can use the URL in an iframe for example
*/
noContentDisposition?: boolean;
} & Partial<CredentialsParams>
): Promise<FileDownloadInfoOutput | null> {
const accessToken = checkCredentials(params);
const repoId = toRepoId(params.repo);
const hubUrl = params.hubUrl ?? HUB_URL;
const url =
`${hubUrl}/${repoId.type === "model" ? "" : `${repoId.type}s/`}${repoId.name}/${
params.raw ? "raw" : "resolve"
}/${encodeURIComponent(params.revision ?? "main")}/${params.path}` +
(params.noContentDisposition ? "?noContentDisposition=1" : "");
const resp = await (params.fetch ?? fetch)(url, {
method: "GET",
headers: {
...(accessToken && {
Authorization: `Bearer ${accessToken}`,
}),
Range: "bytes=0-0",
},
});
if (resp.status === 404 && resp.headers.get("X-Error-Code") === "EntryNotFound") {
return null;
}
if (!resp.ok) {
throw await createApiError(resp);
}
const etag = resp.headers.get("ETag");
if (!etag) {
throw new InvalidApiResponseFormatError("Expected ETag");
}
const contentRangeHeader = resp.headers.get("content-range");
if (!contentRangeHeader) {
throw new InvalidApiResponseFormatError("Expected size information");
}
const [, parsedSize] = contentRangeHeader.split("/");
const size = parseInt(parsedSize);
if (isNaN(size)) {
throw new InvalidApiResponseFormatError("Invalid file size received");
}
return {
etag,
size,
downloadLink: new URL(resp.url).hostname !== new URL(hubUrl).hostname ? resp.url : null,
};
}