Fix storage template extension display (#2002)

* Display correct jpg file extension

* Fix typo in template directory

* Move storage template to correct spelling
This commit is contained in:
Jonathan Jogenfors
2023-03-15 20:39:29 +01:00
committed by GitHub
parent 08ed71e51e
commit 54831878e0
4 changed files with 4 additions and 4 deletions

View File

@@ -0,0 +1,239 @@
<script lang="ts">
import {
api,
SystemConfigStorageTemplateDto,
SystemConfigTemplateStorageOptionDto,
UserResponseDto
} from '@api';
import * as luxon from 'luxon';
import handlebar from 'handlebars';
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
import { fade } from 'svelte/transition';
import SupportedDatetimePanel from './supported-datetime-panel.svelte';
import SupportedVariablesPanel from './supported-variables-panel.svelte';
import SettingButtonsRow from '../setting-buttons-row.svelte';
import { isEqual } from 'lodash-es';
import {
notificationController,
NotificationType
} from '$lib/components/shared-components/notification/notification';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
export let storageConfig: SystemConfigStorageTemplateDto;
export let user: UserResponseDto;
let savedConfig: SystemConfigStorageTemplateDto;
let defaultConfig: SystemConfigStorageTemplateDto;
let templateOptions: SystemConfigTemplateStorageOptionDto;
let selectedPreset = '';
async function getConfigs() {
[savedConfig, defaultConfig, templateOptions] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data.storageTemplate),
api.systemConfigApi.getDefaults().then((res) => res.data.storageTemplate),
api.systemConfigApi.getStorageTemplateOptions().then((res) => res.data)
]);
selectedPreset = templateOptions.presetOptions[0];
}
const getSupportDateTimeFormat = async () => {
const { data } = await api.systemConfigApi.getStorageTemplateOptions();
return data;
};
$: parsedTemplate = () => {
try {
return renderTemplate(storageConfig.template);
} catch (error) {
return 'error';
}
};
const renderTemplate = (templateString: string) => {
const template = handlebar.compile(templateString, {
knownHelpers: undefined
});
const substitutions: Record<string, string> = {
filename: 'IMAGE_56437',
ext: 'jpg',
filetype: 'IMG',
filetypefull: 'IMAGE'
};
const dt = luxon.DateTime.fromISO(new Date('2022-09-04T20:03:05.250').toISOString());
const dateTokens = [
...templateOptions.yearOptions,
...templateOptions.monthOptions,
...templateOptions.dayOptions,
...templateOptions.hourOptions,
...templateOptions.minuteOptions,
...templateOptions.secondOptions
];
for (const token of dateTokens) {
substitutions[token] = dt.toFormat(token);
}
return template(substitutions);
};
async function reset() {
const { data: resetConfig } = await api.systemConfigApi.getConfig();
storageConfig.template = resetConfig.storageTemplate.template;
savedConfig.template = resetConfig.storageTemplate.template;
notificationController.show({
message: 'Reset storage template settings to the recent saved settings',
type: NotificationType.Info
});
}
async function saveSetting() {
try {
const { data: currentConfig } = await api.systemConfigApi.getConfig();
const result = await api.systemConfigApi.updateConfig({
...currentConfig,
storageTemplate: storageConfig
});
storageConfig.template = result.data.storageTemplate.template;
savedConfig.template = result.data.storageTemplate.template;
notificationController.show({
message: 'Storage template saved',
type: NotificationType.Info
});
} catch (e) {
console.error('Error [storage-template-settings] [saveSetting]', e);
notificationController.show({
message: 'Unable to save settings',
type: NotificationType.Error
});
}
}
async function resetToDefault() {
const { data: defaultConfig } = await api.systemConfigApi.getDefaults();
storageConfig.template = defaultConfig.storageTemplate.template;
notificationController.show({
message: 'Reset storage template to default',
type: NotificationType.Info
});
}
const handlePresetSelection = () => {
storageConfig.template = selectedPreset;
};
</script>
<section class="dark:text-immich-dark-fg">
{#await getConfigs() then}
<div id="directory-path-builder" class="m-4">
<h3 class="font-medium text-immich-primary dark:text-immich-dark-primary text-base">
Variables
</h3>
<section class="support-date">
{#await getSupportDateTimeFormat()}
<LoadingSpinner />
{:then options}
<div transition:fade={{ duration: 200 }}>
<SupportedDatetimePanel {options} />
</div>
{/await}
</section>
<section class="support-date">
<SupportedVariablesPanel />
</section>
<div class="mt-4 flex flex-col">
<h3 class="font-medium text-immich-primary dark:text-immich-dark-primary text-base">
Template
</h3>
<div class="text-xs my-2">
<h4>PREVIEW</h4>
</div>
<p class="text-xs">
Approximately path length limit : <span
class="font-semibold text-immich-primary dark:text-immich-dark-primary"
>{parsedTemplate().length + user.id.length + 'UPLOAD_LOCATION'.length}</span
>/260
</p>
<p class="text-xs">
{user.id} is the user's ID
</p>
<p
class="text-xs p-4 bg-gray-200 dark:bg-gray-700 dark:text-immich-dark-fg py-2 rounded-lg mt-2"
>
<span class="text-immich-fg/25 dark:text-immich-dark-fg/50"
>UPLOAD_LOCATION/{user.id}</span
>/{parsedTemplate()}.jpg
</p>
<form autocomplete="off" class="flex flex-col" on:submit|preventDefault>
<div class="flex flex-col my-2">
<label class="text-xs" for="presets">PRESET</label>
<select
class="text-sm bg-slate-200 p-2 rounded-lg mt-2 dark:bg-gray-600 hover:cursor-pointer"
name="presets"
id="preset-select"
bind:value={selectedPreset}
on:change={handlePresetSelection}
>
{#each templateOptions.presetOptions as preset}
<option value={preset}>{renderTemplate(preset)}</option>
{/each}
</select>
</div>
<div class="flex gap-2 align-bottom">
<SettingInputField
label="TEMPLATE"
required
inputType={SettingInputFieldType.TEXT}
bind:value={storageConfig.template}
isEdited={!(storageConfig.template === savedConfig.template)}
/>
<div class="flex-0">
<SettingInputField
label="EXTENSION"
inputType={SettingInputFieldType.TEXT}
value={'.jpg'}
disabled
/>
</div>
</div>
<div id="migration-info" class="text-sm mt-4">
<p>
Template changes will only apply to new assets. To retroactively apply the template to
previously uploaded assets, run the <a
href="/admin/jobs-status"
class="text-immich-primary dark:text-immich-dark-primary">Storage Migration Job</a
>
</p>
</div>
<SettingButtonsRow
on:reset={reset}
on:save={saveSetting}
on:reset-to-default={resetToDefault}
showResetToDefault={!isEqual(savedConfig, defaultConfig)}
/>
</form>
</div>
</div>
{/await}
</section>

View File

@@ -0,0 +1,78 @@
<script lang="ts">
import { SystemConfigTemplateStorageOptionDto } from '@api';
import * as luxon from 'luxon';
export let options: SystemConfigTemplateStorageOptionDto;
const getLuxonExample = (format: string) => {
return luxon.DateTime.fromISO(new Date('2022-09-04T20:03:05.250').toISOString()).toFormat(
format
);
};
</script>
<div class="text-xs mt-2">
<h4>DATE & TIME</h4>
</div>
<div class="text-xs bg-gray-200 dark:bg-gray-700 dark:text-immich-dark-fg p-4 mt-2 rounded-lg">
<div class="mb-2 text-gray-600 dark:text-immich-dark-fg">
<p>Asset's creation timestamp is used for the datetime information</p>
<p>Sample time 2022-09-04T20:03:05.250</p>
</div>
<div class="flex gap-[50px]">
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">YEAR</p>
<ul>
{#each options.yearOptions as yearFormat}
<li>{'{{'}{yearFormat}{'}}'} - {getLuxonExample(yearFormat)}</li>
{/each}
</ul>
</div>
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">MONTH</p>
<ul>
{#each options.monthOptions as monthFormat}
<li>{'{{'}{monthFormat}{'}}'} - {getLuxonExample(monthFormat)}</li>
{/each}
</ul>
</div>
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">DAY</p>
<ul>
{#each options.dayOptions as dayFormat}
<li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
{/each}
</ul>
</div>
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">HOUR</p>
<ul>
{#each options.hourOptions as dayFormat}
<li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
{/each}
</ul>
</div>
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">MINUTE</p>
<ul>
{#each options.minuteOptions as dayFormat}
<li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
{/each}
</ul>
</div>
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">SECOND</p>
<ul>
{#each options.secondOptions as dayFormat}
<li>{'{{'}{dayFormat}{'}}'} - {getLuxonExample(dayFormat)}</li>
{/each}
</ul>
</div>
</div>
</div>

View File

@@ -0,0 +1,29 @@
<div class="text-xs mt-4">
<h4>OTHER VARIABLES</h4>
</div>
<div class="text-xs bg-gray-200 dark:bg-gray-700 dark:text-immich-dark-fg p-4 mt-2 rounded-lg">
<div class="flex gap-[50px]">
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">FILE NAME</p>
<ul>
<li>{`{{filename}}`}</li>
</ul>
</div>
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">FILE EXTENSION</p>
<ul>
<li>{`{{ext}}`}</li>
</ul>
</div>
<div>
<p class="text-immich-primary font-medium dark:text-immich-dark-primary">FILE TYPE</p>
<ul>
<li>{`{{filetype}}`} - VID or IMG</li>
<li>{`{{filetypefull}}`} - VIDEO or IMAGE</li>
</ul>
</div>
</div>
</div>