diff --git a/README.md b/README.md index 0618d56..f39e516 100644 --- a/README.md +++ b/README.md @@ -120,13 +120,18 @@ gdcli upload [options] Options: - `--name ` - Override filename - `--folder ` - Destination folder +- `--convert ` - Convert to Google format: `docs`, `sheets`, or `slides` Examples: ```bash gdcli you@gmail.com upload ./report.pdf gdcli you@gmail.com upload ./report.pdf --folder 1ABC123 --name "Q4 Report.pdf" +gdcli you@gmail.com upload ./README.md --convert docs +gdcli you@gmail.com upload ./data.csv --convert sheets ``` +**Note:** The `--convert` option uses Google Drive's import feature. Some conversions (notably Markdown to Google Docs with full formatting) work best with Google Workspace accounts. Personal Gmail accounts may have limited conversion support. + ### mkdir Create a folder. diff --git a/src/cli.ts b/src/cli.ts index d7923b6..b78293c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -45,6 +45,7 @@ DRIVE COMMANDS Options: --name Override filename --folder Destination folder + --convert Convert to Google format: docs, sheets, or slides gdcli mkdir [--parent ] Create a folder. @@ -84,6 +85,7 @@ EXAMPLES gdcli you@gmail.com download 1ABC123 gdcli you@gmail.com download 1ABC123 ./myfile.pdf gdcli you@gmail.com upload ./report.pdf --folder 1ABC123 + gdcli you@gmail.com upload ./README.md --convert docs gdcli you@gmail.com mkdir "New Folder" --parent 1ABC123 gdcli you@gmail.com delete 1ABC123 gdcli you@gmail.com move 1ABC123 1DEF456 @@ -341,12 +343,14 @@ async function handleUpload(account: string, args: string[]) { options: { name: { type: "string" }, folder: { type: "string" }, + convert: { type: "string" }, }, allowPositionals: true, }); const localPath = positionals[0]; - if (!localPath) error("Usage: upload [--name ] [--folder ]"); + if (!localPath) + error("Usage: upload [--name ] [--folder ] [--convert docs|sheets|slides]"); if (!fs.existsSync(localPath)) { error(`File not found: ${localPath}`); @@ -355,6 +359,7 @@ async function handleUpload(account: string, args: string[]) { const file = await service.upload(account, localPath, { name: values.name, folderId: values.folder, + convertTo: values.convert, }); console.log(`Uploaded: ${file.id}`); diff --git a/src/drive-service.ts b/src/drive-service.ts index ffef9d9..331481a 100644 --- a/src/drive-service.ts +++ b/src/drive-service.ts @@ -206,15 +206,23 @@ export class DriveService { async upload( email: string, localPath: string, - options: { name?: string; folderId?: string; mimeType?: string } = {}, + options: { name?: string; folderId?: string; mimeType?: string; convertTo?: string } = {}, ): Promise { const drive = this.getDriveClient(email); const fileName = options.name || path.basename(localPath); const mimeType = options.mimeType || this.guessMimeType(localPath); + // Map convert option to Google Workspace mimeType + const targetMimeType = options.convertTo ? this.getConvertMimeType(options.convertTo) : undefined; + + // Strip extension when converting, but preserve dotfiles (e.g., .env) + const { name: baseName, ext } = path.parse(fileName); + const uploadName = targetMimeType && ext ? baseName : fileName; + const fileMetadata: drive_v3.Schema$File = { - name: fileName, + name: uploadName, + mimeType: targetMimeType, parents: options.folderId ? [options.folderId] : undefined, }; @@ -223,13 +231,43 @@ export class DriveService { body: fs.createReadStream(localPath), }; - const response = await drive.files.create({ - requestBody: fileMetadata, - media, - fields: "id, name, mimeType, size, webViewLink", - }); + try { + const response = await drive.files.create({ + requestBody: fileMetadata, + media, + fields: "id, name, mimeType, size, webViewLink", + }); - return response.data; + return response.data; + } catch (e) { + // Provide clearer error for conversion failures + if (options.convertTo && e instanceof Error) { + const status = (e as NodeJS.ErrnoException & { code?: number }).code; + if (status === 400 || status === 403) { + throw new Error( + `Conversion to ${options.convertTo} failed: ${e.message}. ` + + "Note: Some conversions (e.g., Markdown to Docs) require a Google Workspace account.", + ); + } + } + throw e; + } + } + + private getConvertMimeType(convertTo: string): string { + const mimeTypes: Record = { + docs: "application/vnd.google-apps.document", + doc: "application/vnd.google-apps.document", + sheets: "application/vnd.google-apps.spreadsheet", + sheet: "application/vnd.google-apps.spreadsheet", + slides: "application/vnd.google-apps.presentation", + slide: "application/vnd.google-apps.presentation", + }; + const result = mimeTypes[convertTo.toLowerCase()]; + if (!result) { + throw new Error(`Unknown convert type: ${convertTo}. Use: docs, sheets, or slides`); + } + return result; } private guessMimeType(filePath: string): string { @@ -254,6 +292,7 @@ export class DriveService { ".zip": "application/zip", ".csv": "text/csv", ".md": "text/markdown", + ".mdx": "text/markdown", }; return mimeTypes[ext] || "application/octet-stream"; }