feat: default image compression type

This commit is contained in:
diced
2025-08-27 17:26:19 -07:00
parent fdb0312dbe
commit ef13ef755c
10 changed files with 74 additions and 4 deletions

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "public"."Zipline" ADD COLUMN "filesDefaultCompressionFormat" TEXT DEFAULT 'jpg';

View File

@@ -42,6 +42,7 @@ model Zipline {
filesRemoveGpsMetadata Boolean @default(false)
filesRandomWordsNumAdjectives Int @default(2)
filesRandomWordsSeparator String @default("-")
filesDefaultCompressionFormat String? @default("jpg")
urlsRoute String @default("/go")
urlsLength Int @default(6)

View File

@@ -35,6 +35,7 @@ export default function Files({
filesRemoveGpsMetadata: boolean;
filesRandomWordsNumAdjectives: number;
filesRandomWordsSeparator: string;
filesDefaultCompressionFormat: string;
}>({
initialValues: {
filesRoute: '/u',
@@ -48,6 +49,7 @@ export default function Files({
filesRemoveGpsMetadata: false,
filesRandomWordsNumAdjectives: 3,
filesRandomWordsSeparator: '-',
filesDefaultCompressionFormat: 'jpg',
},
enhanceGetInputProps: (payload) => ({
disabled: data?.tampered?.includes(payload.field) || false,
@@ -98,6 +100,7 @@ export default function Files({
filesRemoveGpsMetadata: data.settings.filesRemoveGpsMetadata ?? false,
filesRandomWordsNumAdjectives: data.settings.filesRandomWordsNumAdjectives ?? 3,
filesRandomWordsSeparator: data.settings.filesRandomWordsSeparator ?? '-',
filesDefaultCompressionFormat: data.settings.filesDefaultCompressionFormat ?? 'jpg',
});
}, [data]);
@@ -186,6 +189,19 @@ export default function Files({
placeholder='-'
{...form.getInputProps('filesRandomWordsSeparator')}
/>
<Select
label='Default Compression Format'
description='The default image compression format to use when only a compression percent is specified.'
placeholder='jpg'
data={[
{ value: 'jpg', label: '.jpg' },
{ value: 'png', label: '.png' },
{ value: 'webp', label: '.webp' },
{ value: 'jxl', label: '.jxl' },
]}
{...form.getInputProps('filesDefaultCompressionFormat')}
/>
</SimpleGrid>
<Button type='submit' mt='md' loading={isLoading} leftSection={<IconDeviceFloppy size='1rem' />}>

View File

@@ -199,7 +199,44 @@ export default function UploadOptionsButton({ folder, numFiles }: { folder?: str
description='The file name format to use when upload this file, the "File name" field will override this value.'
leftSection={<IconWriting size='1rem' />}
value={options.format}
onChange={(value) => setOption('format', value || ('default' as any))}
onChange={(value) => setOption('format', (value as any) || 'default')}
comboboxProps={{
withinPortal: true,
portalProps: {
style: {
zIndex: 100000000,
},
},
}}
/>
<Select
data={[
{ value: 'default', label: `Default (.${config.files.defaultCompressionFormat ?? 'jpg'})` },
{ value: 'jpg', label: '.jpg' },
{ value: 'png', label: '.png' },
{ value: 'webp', label: '.webp' },
{ value: 'jxl', label: '.jxl' },
]}
label={
<>
Compression Format{' '}
{options.imageCompressionFormat !== 'default' ? (
<Badge variant='outline' size='xs'>
saved
</Badge>
) : null}
</>
}
description={
<>
The image compression format to use <b>only when a compression percent is specified</b>. Leave
at &quot;default&quot; to use the server default compression format.
</>
}
leftSection={<IconFileInfo size='1rem' />}
value={options.imageCompressionFormat || 'default'}
onChange={(value) => setOption('imageCompressionFormat', (value as any) || 'default')}
comboboxProps={{
withinPortal: true,
portalProps: {
@@ -221,7 +258,7 @@ export default function UploadOptionsButton({ folder, numFiles }: { folder?: str
) : null}
</>
}
description='The compression level to use on images (only). Leave blank to disable compression.'
description='The compression level to use on images (only). The above format will be used to compress images. Leave blank to disable compression.'
leftSection={<IconPercentage size='1rem' />}
max={100}
min={0}

View File

@@ -28,6 +28,7 @@ export const DATABASE_TO_PROP = {
filesRemoveGpsMetadata: 'files.removeGpsMetadata',
filesRandomWordsNumAdjectives: 'files.randomWordsNumAdjectives',
filesRandomWordsSeparator: 'files.randomWordsSeparator',
filesDefaultCompressionFormat: 'files.defaultCompressionFormat',
urlsRoute: 'urls.route',
urlsLength: 'urls.length',

View File

@@ -57,6 +57,7 @@ export const ENVS = [
env('files.removeGpsMetadata', 'FILES_REMOVE_GPS_METADATA', 'boolean', true),
env('files.randomWordsNumAdjectives', 'FILES_RANDOM_WORDS_NUM_ADJECTIVES', 'number', true),
env('files.randomWordsSeparator', 'FILES_RANDOM_WORDS_SEPARATOR', 'string', true),
env('files.defaultCompressionFormat', 'FILES_DEFAULT_COMPRESSION_FORMAT', 'string', true),
env('urls.route', 'URLS_ROUTE', 'string', true),
env('urls.length', 'URLS_LENGTH', 'number', true),

View File

@@ -4,6 +4,7 @@ import { type ZodIssue, z } from 'zod';
import { log } from '../logger';
import { ParsedConfig } from './read';
import { PROP_TO_ENV } from './read/env';
import { checkOutput, COMPRESS_TYPES } from '../compress';
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
@@ -97,6 +98,10 @@ export const schema = z.object({
removeGpsMetadata: z.boolean().default(false),
randomWordsNumAdjectives: z.number().default(3),
randomWordsSeparator: z.string().default('-'),
defaultCompressionFormat: z
.enum(COMPRESS_TYPES)
.default('jpg')
.refine((v) => checkOutput(v), 'System does not support outputting this image format.'),
}),
urls: z.object({
route: z.string().startsWith('/').min(1).trim().toLowerCase().default('/go'),

View File

@@ -6,6 +6,7 @@ export const defaultUploadOptions: UploadOptionsStore['options'] = {
deletesAt: 'default',
format: 'default',
imageCompressionPercent: null,
imageCompressionFormat: 'default',
maxViews: null,
addOriginalName: false,
overrides_returnDomain: null,
@@ -22,6 +23,7 @@ export type UploadOptionsStore = {
deletesAt: string | 'never';
format: Config['files']['defaultFormat'] | 'default';
imageCompressionPercent: number | null;
imageCompressionFormat: Config['files']['defaultCompressionFormat'] | 'default';
maxViews: number | null;
addOriginalName: boolean | null;
overrides_returnDomain: string | null;

View File

@@ -1,6 +1,7 @@
import ms from 'ms';
import { Config } from '../config/validate';
import { checkOutput, COMPRESS_TYPES, CompressType } from '../compress';
import { config } from '../config';
// from ms@3.0.0-canary.1
type Unit =
@@ -148,7 +149,7 @@ function parsePercent(header: keyof UploadHeaders, percent: string) {
function headerError(header: keyof UploadHeaders, message: string) {
return {
header,
message: `[${header}]: ${message}`
message: `[${header}]: ${message}`,
};
}
@@ -215,7 +216,7 @@ export function parseHeaders(headers: UploadHeaders, fileConfig: Config['files']
if (typeof percent === 'object') return percent;
response.imageCompression = {
type: 'jpg',
type: config.files.defaultCompressionFormat,
percent,
};
}

View File

@@ -1,4 +1,5 @@
import { bytes } from '@/lib/bytes';
import { checkOutput, COMPRESS_TYPES } from '@/lib/compress';
import { reloadSettings } from '@/lib/config';
import type { readDatabaseSettings } from '@/lib/config/read/db';
import { safeConfig } from '@/lib/config/safe';
@@ -153,6 +154,9 @@ export default fastifyPlugin(
filesRemoveGpsMetadata: z.boolean(),
filesRandomWordsNumAdjectives: z.number().min(1).max(20),
filesRandomWordsSeparator: z.string(),
filesDefaultCompressionFormat: z
.enum(COMPRESS_TYPES)
.refine((v) => checkOutput(v), 'System does not support outputting this image format.'),
urlsRoute: z
.string()