diff --git a/docs/docs/features/command-line-interface.md b/docs/docs/features/command-line-interface.md index 03e96e5080..c3eab1605f 100644 --- a/docs/docs/features/command-line-interface.md +++ b/docs/docs/features/command-line-interface.md @@ -99,6 +99,7 @@ Options: -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) + --visibility Set the visibility of uploaded assets (choices: "archive", "timeline", "hidden", "locked", env: IMMICH_VISIBILITY) -n, --dry-run Don't perform any actions, just show what will be done (default: false, env: IMMICH_DRY_RUN) -c, --concurrency Number of assets to upload at the same time (default: 4, env: IMMICH_UPLOAD_CONCURRENCY) -j, --json-output Output detailed information in json format (default: false, env: IMMICH_JSON_OUTPUT) @@ -176,6 +177,12 @@ By default, hidden files are skipped. If you want to include hidden files, use t immich upload --include-hidden --recursive directory/ ``` +You can set the visibility of uploaded assets to `archive`, `timeline`, `hidden`, or `locked` with the `--visibility` option: + +```bash +immich upload --visibility archive --recursive directory/ +``` + You can use the `--json-output` option to get a json printed which includes three keys: `newFiles`, `duplicates` and `newAssets`. Due to some logging output you will need to strip the first three lines of output to get the json. diff --git a/packages/cli/src/commands/asset.spec.ts b/packages/cli/src/commands/asset.spec.ts index f179b350c9..4959e43642 100644 --- a/packages/cli/src/commands/asset.spec.ts +++ b/packages/cli/src/commands/asset.spec.ts @@ -4,7 +4,14 @@ import path from 'node:path'; import { setTimeout as sleep } from 'node:timers/promises'; import { describe, expect, it, MockedFunction, vi } from 'vitest'; -import { AssetRejectReason, AssetUploadAction, checkBulkUpload, defaults, getSupportedMediaTypes } from '@immich/sdk'; +import { + AssetRejectReason, + AssetUploadAction, + AssetVisibility, + checkBulkUpload, + defaults, + getSupportedMediaTypes, +} from '@immich/sdk'; import createFetchMock from 'vitest-fetch-mock'; import { @@ -102,6 +109,20 @@ describe('uploadFiles', () => { await expect(uploadFiles([testFilePath], { concurrency: 1 })).resolves.toEqual([]); }); + + it('uploads assets with the specified visibility', async () => { + fetchMocker.doMockIf(new RegExp(`${baseUrl}/assets$`), function () { + return { + status: 200, + body: JSON.stringify({ id: 'fc5621b1-86f6-44a1-9905-403e607df9f5', status: 'created' }), + }; + }); + + await uploadFiles([testFilePath], { concurrency: 1, visibility: AssetVisibility.Hidden }); + + const formData = fetchMocker.mock.calls[0]?.[1]?.body as FormData; + expect(formData.get('visibility')).toBe('hidden'); + }); }); describe('checkForDuplicates', () => { diff --git a/packages/cli/src/commands/asset.ts b/packages/cli/src/commands/asset.ts index b0a9037289..1c3d0163a8 100644 --- a/packages/cli/src/commands/asset.ts +++ b/packages/cli/src/commands/asset.ts @@ -4,6 +4,7 @@ import { AssetMediaResponseDto, AssetMediaStatus, AssetUploadAction, + AssetVisibility, Permission, addAssetsToAlbum, checkBulkUpload, @@ -39,6 +40,7 @@ export interface UploadOptionsDto { deleteDuplicates?: boolean; album?: boolean; albumName?: string; + visibility?: AssetVisibility; includeHidden?: boolean; concurrency: number; progress?: boolean; @@ -306,10 +308,8 @@ export const checkForDuplicates = async (files: string[], { concurrency, skipHas return { newFiles, duplicates }; }; -export const uploadFiles = async ( - files: string[], - { dryRun, concurrency, progress }: UploadOptionsDto, -): Promise => { +export const uploadFiles = async (files: string[], options: UploadOptionsDto): Promise => { + const { dryRun, concurrency, progress } = options; if (files.length === 0) { console.log('All assets were already uploaded, nothing to do.'); return []; @@ -358,7 +358,7 @@ export const uploadFiles = async ( throw new Error(`Stats not found for ${filepath}`); } - const response = await uploadFile(filepath, stats); + const response = await uploadFile(filepath, stats, options); newAssets.push({ id: response.id, filepath }); if (response.status === AssetMediaStatus.Duplicate) { duplicateCount++; @@ -400,7 +400,11 @@ export const uploadFiles = async ( return newAssets; }; -const uploadFile = async (input: string, stats: Stats): Promise => { +const uploadFile = async ( + input: string, + stats: Stats, + { visibility }: UploadOptionsDto, +): Promise => { const { baseUrl, headers } = defaults; const formData = new FormData(); @@ -409,6 +413,9 @@ const uploadFile = async (input: string, stats: Stats): Promise', 'Set the visibility of uploaded assets') + .choices(Object.values(AssetVisibility)) + .env('IMMICH_VISIBILITY'), + ) .addOption( new Option('-n, --dry-run', "Don't perform any actions, just show what will be done") .env('IMMICH_DRY_RUN')