coomer-downloader 3.1.0 → 3.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +574 -394
- package/package.json +13 -5
- package/src/api/bunkr.ts +16 -17
- package/src/api/coomer-api.ts +25 -25
- package/src/api/gofile.ts +18 -14
- package/src/api/index.ts +20 -16
- package/src/api/nsfw.xxx.ts +9 -10
- package/src/api/plain-curl.ts +7 -11
- package/src/{args-handler.ts → cli/args-handler.ts} +1 -4
- package/src/cli/ui/app.tsx +40 -0
- package/src/cli/ui/components/file.tsx +44 -0
- package/src/cli/ui/components/filelist.tsx +52 -0
- package/src/cli/ui/components/index.ts +6 -0
- package/src/cli/ui/components/keyboardinfo.tsx +41 -0
- package/src/cli/ui/components/loading.tsx +20 -0
- package/src/cli/ui/components/preview.tsx +32 -0
- package/src/cli/ui/components/spinner.tsx +28 -0
- package/src/cli/ui/components/titlebar.tsx +15 -0
- package/src/cli/ui/hooks/downloader.ts +21 -0
- package/src/cli/ui/hooks/input.ts +17 -0
- package/src/cli/ui/index.tsx +7 -0
- package/src/cli/ui/store/index.ts +19 -0
- package/src/index.ts +15 -21
- package/src/services/downloader.ts +141 -0
- package/src/services/file.ts +95 -0
- package/src/types/index.ts +12 -20
- package/src/utils/filters.ts +11 -14
- package/src/utils/promise.ts +0 -50
- package/src/utils/requests.ts +2 -2
- package/src/utils/strings.ts +1 -19
- package/src/utils/timer.ts +10 -9
- package/tsconfig.json +2 -1
- package/src/utils/downloader.ts +0 -102
- package/src/utils/index.ts +0 -11
- package/src/utils/multibar.ts +0 -62
- /package/src/utils/{files.ts → io.ts} +0 -0
package/src/utils/downloader.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { Readable, Transform } from 'node:stream';
|
|
4
|
-
import { pipeline } from 'node:stream/promises';
|
|
5
|
-
import { Subject } from 'rxjs';
|
|
6
|
-
import { tryFixCoomerUrl } from '../api/coomer-api';
|
|
7
|
-
import type { DownloaderSubject, File } from '../types';
|
|
8
|
-
import { getFileSize, mkdir } from './files';
|
|
9
|
-
import { PromiseRetry } from './promise';
|
|
10
|
-
import { fetchByteRange } from './requests';
|
|
11
|
-
import { Timer } from './timer';
|
|
12
|
-
|
|
13
|
-
export const subject = new Subject<DownloaderSubject>();
|
|
14
|
-
|
|
15
|
-
const CHUNK_TIMEOUT = 30_000;
|
|
16
|
-
const CHUNK_FETCH_RETRIES = 5;
|
|
17
|
-
const FETCH_RETRIES = 7;
|
|
18
|
-
|
|
19
|
-
async function fetchStream(file: File, stream: Readable): Promise<void> {
|
|
20
|
-
const { timer, signal } = Timer.withSignal(CHUNK_TIMEOUT, 'CHUNK_TIMEOUT');
|
|
21
|
-
|
|
22
|
-
const fileStream = fs.createWriteStream(file.filepath as string, { flags: 'a' });
|
|
23
|
-
|
|
24
|
-
const progressStream = new Transform({
|
|
25
|
-
transform(chunk, _encoding, callback) {
|
|
26
|
-
this.push(chunk);
|
|
27
|
-
file.downloaded += chunk.length;
|
|
28
|
-
timer.reset();
|
|
29
|
-
subject.next({ type: 'CHUNK_DOWNLOADING_UPDATE', file });
|
|
30
|
-
callback();
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
subject.next({ type: 'CHUNK_DOWNLOADING_START', file });
|
|
36
|
-
await pipeline(stream, progressStream, fileStream, { signal });
|
|
37
|
-
} catch (error) {
|
|
38
|
-
console.error((error as Error).name === 'AbortError' ? signal.reason : error);
|
|
39
|
-
} finally {
|
|
40
|
-
subject.next({ type: 'CHUNK_DOWNLOADING_END', file });
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function downloadFile(file: File): Promise<void> {
|
|
45
|
-
file.downloaded = await getFileSize(file.filepath as string);
|
|
46
|
-
|
|
47
|
-
const response = await fetchByteRange(file.url, file.downloaded);
|
|
48
|
-
|
|
49
|
-
if (!response?.ok && response?.status !== 416) {
|
|
50
|
-
throw new Error(`HTTP error! status: ${response?.status}`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const contentLength = response.headers.get('Content-Length') as string;
|
|
54
|
-
|
|
55
|
-
if (!contentLength && file.downloaded > 0) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const restFileSize = parseInt(contentLength);
|
|
60
|
-
file.size = restFileSize + file.downloaded;
|
|
61
|
-
|
|
62
|
-
if (file.size > file.downloaded && response.body) {
|
|
63
|
-
const stream = Readable.fromWeb(response.body);
|
|
64
|
-
const sizeOld = file.downloaded;
|
|
65
|
-
|
|
66
|
-
await PromiseRetry.create({
|
|
67
|
-
retries: CHUNK_FETCH_RETRIES,
|
|
68
|
-
callback: () => {
|
|
69
|
-
if (sizeOld !== file.downloaded) {
|
|
70
|
-
return { newRetries: 5 };
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
}).execute(async () => await fetchStream(file, stream));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
subject.next({ type: 'FILE_DOWNLOADING_END' });
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export async function downloadFiles(data: File[], downloadDir: string): Promise<void> {
|
|
80
|
-
mkdir(downloadDir);
|
|
81
|
-
|
|
82
|
-
subject.next({ type: 'FILES_DOWNLOADING_START', filesCount: data.length });
|
|
83
|
-
|
|
84
|
-
for (const [_, file] of data.entries()) {
|
|
85
|
-
file.filepath = path.join(downloadDir, file.name);
|
|
86
|
-
|
|
87
|
-
subject.next({ type: 'FILE_DOWNLOADING_START' });
|
|
88
|
-
|
|
89
|
-
await PromiseRetry.create({
|
|
90
|
-
retries: FETCH_RETRIES,
|
|
91
|
-
callback: (retries) => {
|
|
92
|
-
if (/coomer|kemono/.test(file.url)) {
|
|
93
|
-
file.url = tryFixCoomerUrl(file.url, retries);
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
}).execute(async () => await downloadFile(file));
|
|
97
|
-
|
|
98
|
-
subject.next({ type: 'FILE_DOWNLOADING_END' });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
subject.next({ type: 'FILES_DOWNLOADING_END' });
|
|
102
|
-
}
|
package/src/utils/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export { downloadFiles } from './downloader';
|
|
2
|
-
export { getFileSize, mkdir } from './files';
|
|
3
|
-
export { filterKeywords, isImage, isVideo, testMediaType } from './filters';
|
|
4
|
-
export { createMultibar } from './multibar';
|
|
5
|
-
export {
|
|
6
|
-
fetchByteRange,
|
|
7
|
-
fetchWithGlobalHeader,
|
|
8
|
-
HeadersDefault,
|
|
9
|
-
setGlobalHeaders,
|
|
10
|
-
} from './requests';
|
|
11
|
-
export { b2mb } from './strings';
|
package/src/utils/multibar.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { MultiBar, type Options, type SingleBar } from 'cli-progress';
|
|
2
|
-
import { subject } from './downloader';
|
|
3
|
-
import { b2mb, formatNameStdout } from './strings';
|
|
4
|
-
|
|
5
|
-
const config: Options = {
|
|
6
|
-
clearOnComplete: true,
|
|
7
|
-
gracefulExit: true,
|
|
8
|
-
autopadding: true,
|
|
9
|
-
hideCursor: true,
|
|
10
|
-
format: '{percentage}% | {filename} | {value}/{total}{size}',
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function createMultibar() {
|
|
14
|
-
const multibar = new MultiBar(config);
|
|
15
|
-
let bar: SingleBar;
|
|
16
|
-
let minibar: SingleBar;
|
|
17
|
-
let filename: string;
|
|
18
|
-
let index = 0;
|
|
19
|
-
|
|
20
|
-
subject.subscribe({
|
|
21
|
-
next: ({ type, filesCount, file }) => {
|
|
22
|
-
switch (type) {
|
|
23
|
-
case 'FILES_DOWNLOADING_START':
|
|
24
|
-
bar?.stop();
|
|
25
|
-
bar = multibar.create(filesCount as number, 0);
|
|
26
|
-
break;
|
|
27
|
-
|
|
28
|
-
case 'FILES_DOWNLOADING_END':
|
|
29
|
-
bar?.stop();
|
|
30
|
-
break;
|
|
31
|
-
|
|
32
|
-
case 'FILE_DOWNLOADING_START':
|
|
33
|
-
bar?.update(++index, { filename: 'Downloaded files', size: '' });
|
|
34
|
-
break;
|
|
35
|
-
|
|
36
|
-
case 'FILE_DOWNLOADING_END':
|
|
37
|
-
multibar.remove(minibar);
|
|
38
|
-
break;
|
|
39
|
-
|
|
40
|
-
case 'CHUNK_DOWNLOADING_START':
|
|
41
|
-
multibar?.remove(minibar);
|
|
42
|
-
filename = formatNameStdout(file?.filepath as string);
|
|
43
|
-
minibar = multibar.create(b2mb(file?.size as number), b2mb(file?.downloaded as number));
|
|
44
|
-
break;
|
|
45
|
-
|
|
46
|
-
case 'CHUNK_DOWNLOADING_UPDATE':
|
|
47
|
-
minibar?.update(b2mb(file?.downloaded as number), {
|
|
48
|
-
filename: filename as string,
|
|
49
|
-
size: 'mb',
|
|
50
|
-
});
|
|
51
|
-
break;
|
|
52
|
-
|
|
53
|
-
case 'CHUNK_DOWNLOADING_END':
|
|
54
|
-
multibar?.remove(minibar);
|
|
55
|
-
break;
|
|
56
|
-
|
|
57
|
-
default:
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
}
|
|
File without changes
|