Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
51 changes: 12 additions & 39 deletions cli/src/commands/upload.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<FormData> {
async getUploadFileRequest(): Promise<UploadFileRequest> {
if (!this.deviceAssetId) {
throw new Error('Device asset id not set');
}
Expand All @@ -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<void> {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
}
2 changes: 1 addition & 1 deletion cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
19 changes: 17 additions & 2 deletions docs/docs/features/command-line-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 <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
Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down