Skip to content

Commit

Permalink
feat: fetch file name
Browse files Browse the repository at this point in the history
  • Loading branch information
ido-pluto committed Nov 27, 2023
1 parent 36cf2f0 commit bf36283
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 9 deletions.
38 changes: 38 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"@commitlint/config-conventional": "^17.7.0",
"@semantic-release/exec": "^6.0.3",
"@types/cli-progress": "^3.11.0",
"@types/content-disposition": "^0.5.8",
"@types/fs-extra": "^11.0.1",
"@types/node": "^20.4.9",
"@types/progress-stream": "^2.0.2",
Expand All @@ -78,6 +79,7 @@
"chalk": "^5.3.0",
"cli-progress": "^3.12.0",
"commander": "^10.0.0",
"content-disposition": "^0.5.4",
"execa": "^7.2.0",
"fs-extra": "^11.1.1",
"level": "^8.0.0",
Expand Down
16 changes: 13 additions & 3 deletions src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,35 @@ import {Command} from "commander";
import {packageJson} from "../const.js";
import pullFileCLI from "../download/index.js";
import {truncateText} from "../utils/truncate-text.js";
import {FastDownload} from "../index.js";
import findDownloadDir from "./utils/find-download-dir.js";
import {setCommand} from "./commands/set.js";


const pullCommand = new Command();
pullCommand
.version(packageJson.version)
.description("Pull/copy files from remote server/local directory")
.argument("[files...]", "Files to pull/copy")
.option("-s --save [path]", "Save directory")
.option("-n --name [name]", "Downloaded file name")
.option("-f --full-name", "Show full name of the file while downloading, even if it long")
.action(async (files = [], {save, fullName}: { save?: string, fullName?: boolean }) => {
.action(async (files: string[] = [], {save, fullName, name}: { save?: string, fullName?: boolean, name?: string }) => {
if (files.length === 0) {
pullCommand.outputHelp();
process.exit(0);
}

const pullLogs: string[] = [];
for (const file of files) {
const fileName = path.basename(file);
for (const [index, file ] of Object.entries(files)) {
let fileName = path.basename(file);

if (name) {
fileName = files.length > 1 ? name + index : name;
} else if (file.startsWith("http")) {
fileName = await FastDownload.fetchFilename(file);
}

const downloadTag = fullName ? fileName : truncateText(fileName);
const downloadPath = path.join(save || await findDownloadDir(fileName), fileName);
const fileFullPath = new URL(file, pathToFileURL(process.cwd()));
Expand Down
36 changes: 30 additions & 6 deletions src/download/stream-progress/fast-download.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import TurboDownloader, {TurboDownloaderOptions} from "turbo-downloader";
import wretch from "wretch";
import fs from "fs-extra";
import contentDisposition from "content-disposition";
import {IStreamProgress} from "./istream-progress.js";

const DEFAULT_FILE_NAME = "file";

export default class FastDownload implements IStreamProgress {
private _downloader?: TurboDownloader.default;
private _redirectedURL?: string;

constructor(private _url: string, private _savePath: string, private _options?: Partial<TurboDownloaderOptions>) {
constructor(private _url: string, private _savePath: string, private _options?: Partial<TurboDownloaderOptions>) {
}

public async init() {
await this._fetchFileInfo();
await fs.ensureFile(this._savePath);

this._downloader = new FastDownload._TurboDownloaderClass({
url: await this._getRedirectedURL(),
url: this._redirectedURL!,
destFile: this._savePath,
chunkSize: 50 * 1024 * 1024,
concurrency: 8,
Expand All @@ -23,24 +27,44 @@ export default class FastDownload implements IStreamProgress {
});
}

private async _getRedirectedURL() {
private async _fetchFileInfo() {
const {url} = await wretch(this._url)
.head()
.res()
.catch(error => {
throw new Error(`Error while getting file head: ${error.status}`);
});
return this._redirectedURL = url;

this._redirectedURL = url;


}

public async progress(callback: (progressBytes: number, totalBytes: number) => void) {
if (!this._downloader)
throw new Error("Downloader is not initialized");
if (!this._downloader) throw new Error("Downloader is not initialized");
await (this._downloader as any).download(callback);
}

private static get _TurboDownloaderClass(): typeof TurboDownloader.default {
if (TurboDownloader && "default" in TurboDownloader) return TurboDownloader.default;
return TurboDownloader;
}

public static async fetchFilename(url: string, _default?: string) {
const contentDispositionHeader = await wretch(url)
.head()
.res(response => response.headers.get("content-disposition"))
.catch(error => {
throw new Error(`Error while getting file head: ${error.status}`);
});

const parsed = new URL(url);
_default ??= decodeURIComponent(parsed.pathname.split("/").pop() ?? DEFAULT_FILE_NAME);

if (!contentDispositionHeader)
return _default;

const {parameters} = contentDisposition.parse(contentDispositionHeader);
return parameters.filename ?? _default;
}
}

0 comments on commit bf36283

Please sign in to comment.