mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00: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:
parent
ce6dc3b7af
commit
755444e9a4
4 changed files with 31 additions and 43 deletions
|
@ -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",
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue