From 755444e9a41d46e08624c670e977e3d1b673a2b3 Mon Sep 17 00:00:00 2001 From: Jonathan Jogenfors Date: Tue, 6 Feb 2024 11:00:35 +0100 Subject: [PATCH] chore(cli): use upload api and update documentation (#6927) * use fetch api * bump version * add documentation * revert to using file blob --- cli/package.json | 2 +- cli/src/commands/upload.command.ts | 51 +++++--------------- cli/src/index.ts | 2 +- docs/docs/features/command-line-interface.md | 19 +++++++- 4 files changed, 31 insertions(+), 43 deletions(-) diff --git a/cli/package.json b/cli/package.json index a20070f610..3c3c4a076b 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.0.6", + "version": "2.0.7", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", diff --git a/cli/src/commands/upload.command.ts b/cli/src/commands/upload.command.ts index 7933775faf..955d8768f5 100644 --- a/cli/src/commands/upload.command.ts +++ b/cli/src/commands/upload.command.ts @@ -7,14 +7,15 @@ import { basename } from 'node:path'; import { access, constants, stat, unlink } from 'node:fs/promises'; import { createHash } from 'node:crypto'; import os from 'node:os'; +import { UploadFileRequest } from '@immich/sdk'; class Asset { readonly path: string; readonly deviceId!: string; deviceAssetId?: string; - fileCreatedAt?: string; - fileModifiedAt?: string; + fileCreatedAt?: Date; + fileModifiedAt?: Date; sidecarPath?: string; fileSize!: number; albumName?: string; @@ -26,13 +27,13 @@ class Asset { async prepare() { const stats = await stat(this.path); this.deviceAssetId = `${basename(this.path)}-${stats.size}`.replaceAll(/\s+/g, ''); - this.fileCreatedAt = stats.mtime.toISOString(); - this.fileModifiedAt = stats.mtime.toISOString(); + this.fileCreatedAt = stats.mtime; + this.fileModifiedAt = stats.mtime; this.fileSize = stats.size; this.albumName = this.extractAlbumName(); } - async getUploadFormData(): Promise { + async getUploadFileRequest(): Promise { if (!this.deviceAssetId) { throw new Error('Device asset id not set'); } @@ -51,25 +52,15 @@ class Asset { sidecarData = new File([await fs.openAsBlob(sideCarPath)], basename(sideCarPath)); } catch {} - const data: any = { + return { assetData: new File([await fs.openAsBlob(this.path)], basename(this.path)), deviceAssetId: this.deviceAssetId, deviceId: 'CLI', fileCreatedAt: this.fileCreatedAt, fileModifiedAt: this.fileModifiedAt, - isFavorite: String(false), + isFavorite: false, + sidecarData, }; - const formData = new FormData(); - - for (const property in data) { - formData.append(property, data[property]); - } - - if (sidecarData) { - formData.append('sidecarData', sidecarData); - } - - return formData; } async delete(): Promise { @@ -197,10 +188,9 @@ export class UploadCommand extends BaseCommand { if (!skipAsset && !options.dryRun) { if (!skipUpload) { - const formData = await asset.getUploadFormData(); - const response = await this.uploadAsset(formData); - const json = await response.json(); - existingAssetId = json.id; + const fileRequest = await asset.getUploadFileRequest(); + const response = await this.immichApi.assetApi.uploadFile(fileRequest); + existingAssetId = response.id; uploadCounter++; totalSizeUploaded += asset.fileSize; } @@ -258,21 +248,4 @@ export class UploadCommand extends BaseCommand { } } } - - private async uploadAsset(data: FormData): Promise { - const url = this.immichApi.instanceUrl + '/asset/upload'; - - const response = await fetch(url, { - method: 'post', - redirect: 'error', - headers: { - 'x-api-key': this.immichApi.apiKey, - }, - body: data, - }); - if (response.status !== 200 && response.status !== 201) { - throw new Error(await response.text()); - } - return response; - } } diff --git a/cli/src/index.ts b/cli/src/index.ts index 6582b37956..cd16885d32 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -24,7 +24,7 @@ program .addOption(new Option('-r, --recursive', 'Recursive').env('IMMICH_RECURSIVE').default(false)) .addOption(new Option('-i, --ignore [paths...]', 'Paths to ignore').env('IMMICH_IGNORE_PATHS')) .addOption(new Option('-h, --skip-hash', "Don't hash files before upload").env('IMMICH_SKIP_HASH').default(false)) - .addOption(new Option('-i, --include-hidden', 'Include hidden folders').env('IMMICH_INCLUDE_HIDDEN').default(false)) + .addOption(new Option('-H, --include-hidden', 'Include hidden folders').env('IMMICH_INCLUDE_HIDDEN').default(false)) .addOption( new Option('-a, --album', 'Automatically create albums based on folder name') .env('IMMICH_AUTO_CREATE_ALBUM') diff --git a/docs/docs/features/command-line-interface.md b/docs/docs/features/command-line-interface.md index ee7ca1c22c..6f16461185 100644 --- a/docs/docs/features/command-line-interface.md +++ b/docs/docs/features/command-line-interface.md @@ -39,10 +39,11 @@ immich ``` Usage: immich [options] [command] -Immich command line interface +Command line interface for Immich Options: -V, --version output the version number + -d, --config Configuration directory (env: IMMICH_CONFIG_DIR) -h, --help display help for command Commands: @@ -69,7 +70,9 @@ Options: -r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE) -i, --ignore [paths...] Paths to ignore (env: IMMICH_IGNORE_PATHS) -h, --skip-hash Don't hash files before upload (default: false, env: IMMICH_SKIP_HASH) + -H, --include-hidden Include hidden folders (default: false, env: IMMICH_INCLUDE_HIDDEN) -a, --album Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM) + -A, --album-name Add all assets to specified album (env: IMMICH_ALBUM_NAME) -n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN) --delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS) --help display help for command @@ -91,7 +94,7 @@ For instance, immich login-key http://192.168.1.216:2283/api HFEJ38DNSDUEG ``` -This will store your credentials in a file in your home directory. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually. +This will store your credentials in a `auth.yml` file in the configuration directory which defaults to `~/.config/`. The directory can be set with the `-d` option or the environment variable `IMMICH_CONFIG_DIR`. Please keep the file secure, either by performing the logout command after you are done, or deleting it manually. Once you are authenticated, you can upload assets to your Immich server. @@ -123,6 +126,12 @@ You can automatically create albums based on the folder name by passing the `--a immich upload --album --recursive directory/ ``` +You can also choose to upload all assets to a specific album with the `--album-name` option. + +```bash +immich upload --album-name "My summer holiday" --recursive directory/ +``` + It is possible to skip assets matching a glob pattern by passing the `--ignore` option. See [the library documentation](docs/features/libraries.md) on how to use glob patterns. You can add several exclusion patterns if needed. ```bash @@ -133,6 +142,12 @@ immich upload --ignore **/Raw/** --recursive directory/ immich upload --ignore **/Raw/** **/*.tif --recursive directory/ ``` +By default, hidden files are skipped. If you want to include hidden files, use the `--include-hidden` option: + +```bash +immich upload --include-hidden --recursive directory/ +``` + ### Obtain the API Key The API key can be obtained in the user setting panel on the web interface.