forked from download-directory/download-directory.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdownload.ts
More file actions
104 lines (90 loc) · 2.57 KB
/
Copy pathdownload.ts
File metadata and controls
104 lines (90 loc) · 2.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import {type ContentsReponseObject, type TreeResponseObject} from 'list-github-dir-content';
import pRetry, {type FailedAttemptError} from 'p-retry';
import authenticatedFetch from './authenticated-fetch.js';
function escapeFilepath(path: string) {
return path.replaceAll('#', '%23');
}
async function maybeResponseLfs(response: Response): Promise<boolean> {
const length = Number(response.headers.get('content-length'));
if (length > 128 && length < 140) {
const contents = await response.clone().text();
return contents.startsWith('version https://git-lfs.github.com/spec/v1');
}
return false;
}
type FileRequest = {
user: string;
repository: string;
reference: string;
file: TreeResponseObject | ContentsReponseObject;
signal: AbortSignal;
};
async function fetchPublicFile({
user,
repository,
reference,
file,
signal,
}: FileRequest) {
const response = await authenticatedFetch(
`https://raw.githubusercontent.com/${user}/${repository}/${reference}/${escapeFilepath(file.path)}`,
{signal},
);
if (!response.ok) {
throw new Error(`HTTP ${response.statusText} for ${file.path}`);
}
const lfsCompatibleResponse = (await maybeResponseLfs(response))
? await authenticatedFetch(
`https://media.githubusercontent.com/media/${user}/${repository}/${reference}/${escapeFilepath(file.path)}`,
{signal},
)
: response;
if (!response.ok) {
throw new Error(`HTTP ${response.statusText} for ${file.path}`);
}
return lfsCompatibleResponse.blob();
}
async function fetchPrivateFile({
file,
signal,
}: FileRequest) {
const response = await authenticatedFetch(file.url, {signal});
if (!response.ok) {
throw new Error(`HTTP ${response.statusText} for ${file.path}`);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const {content} = await response.json();
const decoder = await fetch(
`data:application/octet-stream;base64,${content}`,
);
return decoder.blob();
}
export async function downloadFile({
user,
repository,
reference,
file,
isPrivate,
signal,
}: {
user: string;
repository: string;
reference: string;
isPrivate: boolean;
file: TreeResponseObject | ContentsReponseObject;
signal: AbortSignal;
}) {
const fileRequest = {
user, repository, reference, file, signal,
};
const localDownload = async () =>
isPrivate
? fetchPrivateFile(fileRequest)
: fetchPublicFile(fileRequest);
const onFailedAttempt = (error: FailedAttemptError) => {
console.error(
`Error downloading ${file.path}. Attempt ${error.attemptNumber}. ${error.retriesLeft} retries left.`,
);
};
return pRetry(localDownload, {onFailedAttempt});
}