1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-25 10:43:13 +02:00

chore(cli): use upload api and update documentation (#6927)

* use fetch api

* bump version

* add documentation

* revert to using file blob
This commit is contained in:
Jonathan Jogenfors 2024-02-06 11:00:35 +01:00 committed by GitHub
parent ce6dc3b7af
commit 755444e9a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 43 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@immich/cli", "name": "@immich/cli",
"version": "2.0.6", "version": "2.0.7",
"description": "Command Line Interface (CLI) for Immich", "description": "Command Line Interface (CLI) for Immich",
"type": "module", "type": "module",
"exports": "./dist/index.js", "exports": "./dist/index.js",

View File

@ -7,14 +7,15 @@ import { basename } from 'node:path';
import { access, constants, stat, unlink } from 'node:fs/promises'; import { access, constants, stat, unlink } from 'node:fs/promises';
import { createHash } from 'node:crypto'; import { createHash } from 'node:crypto';
import os from 'node:os'; import os from 'node:os';
import { UploadFileRequest } from '@immich/sdk';
class Asset { class Asset {
readonly path: string; readonly path: string;
readonly deviceId!: string; readonly deviceId!: string;
deviceAssetId?: string; deviceAssetId?: string;
fileCreatedAt?: string; fileCreatedAt?: Date;
fileModifiedAt?: string; fileModifiedAt?: Date;
sidecarPath?: string; sidecarPath?: string;
fileSize!: number; fileSize!: number;
albumName?: string; albumName?: string;
@ -26,13 +27,13 @@ class Asset {
async prepare() { async prepare() {
const stats = await stat(this.path); const stats = await stat(this.path);
this.deviceAssetId = `${basename(this.path)}-${stats.size}`.replaceAll(/\s+/g, ''); this.deviceAssetId = `${basename(this.path)}-${stats.size}`.replaceAll(/\s+/g, '');
this.fileCreatedAt = stats.mtime.toISOString(); this.fileCreatedAt = stats.mtime;
this.fileModifiedAt = stats.mtime.toISOString(); this.fileModifiedAt = stats.mtime;
this.fileSize = stats.size; this.fileSize = stats.size;
this.albumName = this.extractAlbumName(); this.albumName = this.extractAlbumName();
} }
async getUploadFormData(): Promise<FormData> { async getUploadFileRequest(): Promise<UploadFileRequest> {
if (!this.deviceAssetId) { if (!this.deviceAssetId) {
throw new Error('Device asset id not set'); throw new Error('Device asset id not set');
} }
@ -51,25 +52,15 @@ class Asset {
sidecarData = new File([await fs.openAsBlob(sideCarPath)], basename(sideCarPath)); sidecarData = new File([await fs.openAsBlob(sideCarPath)], basename(sideCarPath));
} catch {} } catch {}
const data: any = { return {
assetData: new File([await fs.openAsBlob(this.path)], basename(this.path)), assetData: new File([await fs.openAsBlob(this.path)], basename(this.path)),
deviceAssetId: this.deviceAssetId, deviceAssetId: this.deviceAssetId,
deviceId: 'CLI', deviceId: 'CLI',
fileCreatedAt: this.fileCreatedAt, fileCreatedAt: this.fileCreatedAt,
fileModifiedAt: this.fileModifiedAt, 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<void> { async delete(): Promise<void> {
@ -197,10 +188,9 @@ export class UploadCommand extends BaseCommand {
if (!skipAsset && !options.dryRun) { if (!skipAsset && !options.dryRun) {
if (!skipUpload) { if (!skipUpload) {
const formData = await asset.getUploadFormData(); const fileRequest = await asset.getUploadFileRequest();
const response = await this.uploadAsset(formData); const response = await this.immichApi.assetApi.uploadFile(fileRequest);
const json = await response.json(); existingAssetId = response.id;
existingAssetId = json.id;
uploadCounter++; uploadCounter++;
totalSizeUploaded += asset.fileSize; totalSizeUploaded += asset.fileSize;
} }
@ -258,21 +248,4 @@ export class UploadCommand extends BaseCommand {
} }
} }
} }
private async uploadAsset(data: FormData): Promise<Response> {
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;
}
} }

View File

@ -24,7 +24,7 @@ program
.addOption(new Option('-r, --recursive', 'Recursive').env('IMMICH_RECURSIVE').default(false)) .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('-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('-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( .addOption(
new Option('-a, --album', 'Automatically create albums based on folder name') new Option('-a, --album', 'Automatically create albums based on folder name')
.env('IMMICH_AUTO_CREATE_ALBUM') .env('IMMICH_AUTO_CREATE_ALBUM')

View File

@ -39,10 +39,11 @@ immich
``` ```
Usage: immich [options] [command] Usage: immich [options] [command]
Immich command line interface Command line interface for Immich
Options: Options:
-V, --version output the version number -V, --version output the version number
-d, --config Configuration directory (env: IMMICH_CONFIG_DIR)
-h, --help display help for command -h, --help display help for command
Commands: Commands:
@ -69,7 +70,9 @@ Options:
-r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE) -r, --recursive Recursive (default: false, env: IMMICH_RECURSIVE)
-i, --ignore [paths...] Paths to ignore (env: IMMICH_IGNORE_PATHS) -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, --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 Automatically create albums based on folder name (default: false, env: IMMICH_AUTO_CREATE_ALBUM)
-A, --album-name <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) -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) --delete Delete local assets after upload (env: IMMICH_DELETE_ASSETS)
--help display help for command --help display help for command
@ -91,7 +94,7 @@ For instance,
immich login-key http://192.168.1.216:2283/api HFEJ38DNSDUEG 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. 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/ 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. 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 ```bash
@ -133,6 +142,12 @@ immich upload --ignore **/Raw/** --recursive directory/
immich upload --ignore **/Raw/** **/*.tif --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 ### Obtain the API Key
The API key can be obtained in the user setting panel on the web interface. The API key can be obtained in the user setting panel on the web interface.