mirror of
https://codeberg.org/PLG-Development/PLG-MuDiCS
synced 2026-07-06 00:47:09 +00:00
finalize thumbnails and add shared supported_file_types json
This commit is contained in:
@@ -20,8 +20,62 @@
|
|||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
import FolderElementObject from './FolderElementObject.svelte';
|
import FolderElementObject from './FolderElementObject.svelte';
|
||||||
import PopUp from './PopUp.svelte';
|
import PopUp from './PopUp.svelte';
|
||||||
|
import type { PopupContent } from '../ts/types';
|
||||||
|
import TextInput from './TextInput.svelte';
|
||||||
|
import { is_valid_name } from '../ts/utils';
|
||||||
|
|
||||||
|
let current_name: string = '';
|
||||||
|
let current_valid: boolean = false;
|
||||||
|
|
||||||
|
let popup_content: PopupContent = $state({
|
||||||
|
open: false,
|
||||||
|
snippet: null,
|
||||||
|
title: '',
|
||||||
|
closable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function popup_close_function() {
|
||||||
|
popup_content.open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const show_new_folder_popup = () => {
|
||||||
|
popup_content = {
|
||||||
|
open: true,
|
||||||
|
snippet: new_folder_popup,
|
||||||
|
title: 'Neuen Ordner erstellen',
|
||||||
|
title_icon: FolderPlus,
|
||||||
|
closable: true
|
||||||
|
};
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#snippet new_folder_popup()}
|
||||||
|
<div>
|
||||||
|
<TextInput
|
||||||
|
current_value={current_name}
|
||||||
|
{current_valid}
|
||||||
|
title="Ordnername"
|
||||||
|
is_valid_function={(input: string) => {
|
||||||
|
if (input.startsWith('.')) return [false, 'Name darf nicht mit . beginnen'];
|
||||||
|
const trimmed_input = input.trim();
|
||||||
|
if (trimmed_input.length === 0 || trimmed_input.length > 50)
|
||||||
|
return [false, 'Ungültige Länge'];
|
||||||
|
if (!is_valid_name(trimmed_input)) return [false, 'Name enthält ungültige Zeichen'];
|
||||||
|
if (
|
||||||
|
get_current_folder_elements($all_files, $current_file_path, $selected_display_ids).some(
|
||||||
|
(e) => e.name === trimmed_input
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return [false, 'Name bereits verwendet'];
|
||||||
|
return [true, 'Gültiger Name'];
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row justify-end gap-2">
|
||||||
|
<Button className="px-4 font-bold" click_function={popup_close_function}>Fertig</Button>
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
<div class="bg-stone-800 h-full rounded-2xl grid grid-rows-[2.5rem_1fr] min-h-0">
|
<div class="bg-stone-800 h-full rounded-2xl grid grid-rows-[2.5rem_1fr] min-h-0">
|
||||||
<div class="bg-stone-700 flex justify-between w-full p-1 rounded-t-2xl min-w-0 gap-2">
|
<div class="bg-stone-700 flex justify-between w-full p-1 rounded-t-2xl min-w-0 gap-2">
|
||||||
<span class="text-xl font-bold pl-2 content-center truncate min-w-0">
|
<span class="text-xl font-bold pl-2 content-center truncate min-w-0">
|
||||||
@@ -59,7 +113,8 @@
|
|||||||
<div class="flex flex-row gap-2 shrink-0">
|
<div class="flex flex-row gap-2 shrink-0">
|
||||||
<Button
|
<Button
|
||||||
title="Neuen Ordner erstellen (Neuen Ordner mit ausgewählten Objekten erstellen)"
|
title="Neuen Ordner erstellen (Neuen Ordner mit ausgewählten Objekten erstellen)"
|
||||||
className="px-3 flex"><FolderPlus /></Button
|
className="px-3 flex"
|
||||||
|
click_function={show_new_folder_popup}><FolderPlus /></Button
|
||||||
>
|
>
|
||||||
<div class="border border-stone-700 my-1"></div>
|
<div class="border border-stone-700 my-1"></div>
|
||||||
<Button title="Datei(en) hochladen" className="px-3 flex"><Upload /></Button>
|
<Button title="Datei(en) hochladen" className="px-3 flex"><Upload /></Button>
|
||||||
@@ -125,8 +180,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <PopUp title="Test" title_icon={Info}>
|
<PopUp
|
||||||
<div>Hier kann beliebiges HTML übergeben werden!</div>
|
content={popup_content}
|
||||||
</PopUp> -->
|
close_function={popup_close_function}
|
||||||
|
className="rounded-b-2xl"
|
||||||
|
snippet_container_class="overflow-hidden"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
get_shifted_color
|
get_shifted_color
|
||||||
} from '../ts/stores/ui_behavior';
|
} from '../ts/stores/ui_behavior';
|
||||||
import Button from './Button.svelte';
|
import Button from './Button.svelte';
|
||||||
import { supported_file_types, type FolderElement, type SupportedFileType } from '../ts/types';
|
import { supported_file_type_icon, type FolderElement, type SupportedFileType } from '../ts/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
is_selected,
|
is_selected,
|
||||||
select,
|
select,
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
get_display_ids_where_file_is_missing
|
get_display_ids_where_file_is_missing
|
||||||
} from '../ts/stores/files';
|
} from '../ts/stores/files';
|
||||||
import RefreshPlay from './RefreshPlay.svelte';
|
import RefreshPlay from './RefreshPlay.svelte';
|
||||||
import { get_file_size_display_string } from '../ts/utils';
|
import { get_file_size_display_string, get_file_type } from '../ts/utils';
|
||||||
import { open_file } from '../ts/api_handler';
|
import { open_file } from '../ts/api_handler';
|
||||||
import {
|
import {
|
||||||
displays,
|
displays,
|
||||||
@@ -55,22 +56,6 @@
|
|||||||
|
|
||||||
const is_folder = file.type === 'inode/directory';
|
const is_folder = file.type === 'inode/directory';
|
||||||
|
|
||||||
function get_file_type(file: FolderElement): SupportedFileType | null {
|
|
||||||
for (const key of Object.keys(supported_file_types)) {
|
|
||||||
if (file.type === supported_file_types[key].mime_type) {
|
|
||||||
return supported_file_types[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Fallback:
|
|
||||||
const extension = file.name.split('.').pop();
|
|
||||||
if (extension) {
|
|
||||||
if (Object.keys(supported_file_types).includes('.' + extension)) {
|
|
||||||
return supported_file_types['.' + extension];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_created_string(date_object: Date, full_string = false) {
|
function get_created_string(date_object: Date, full_string = false) {
|
||||||
if (full_string) {
|
if (full_string) {
|
||||||
return (
|
return (
|
||||||
@@ -191,11 +176,11 @@
|
|||||||
<img
|
<img
|
||||||
src={thumbnail_url}
|
src={thumbnail_url}
|
||||||
alt="file_thumbnail"
|
alt="file_thumbnail"
|
||||||
class="object-contain size-full select-none block"
|
class="object-contain size-full select-none block p-1"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
/>
|
/>
|
||||||
{:else if get_file_type(file)?.icon}
|
{:else if supported_file_type_icon[get_file_type(file)?.display_name || '']}
|
||||||
{@const Icon = get_file_type(file)?.icon}
|
{@const Icon = supported_file_type_icon[get_file_type(file)?.display_name || '']}
|
||||||
<Icon class="size-full p-2" />
|
<Icon class="size-full p-2" />
|
||||||
{:else}
|
{:else}
|
||||||
<FileIcon class="size-full p-2" />
|
<FileIcon class="size-full p-2" />
|
||||||
|
|||||||
@@ -49,7 +49,10 @@
|
|||||||
{title}:
|
{title}:
|
||||||
</div>
|
</div>
|
||||||
{#if is_valid_function && focussed}
|
{#if is_valid_function && focussed}
|
||||||
<div class={current_valid ? "text-green-400" : "text-red-400"} transition:fade={{ duration: 100 }}>
|
<div
|
||||||
|
class={current_valid ? 'text-green-400' : 'text-red-400'}
|
||||||
|
transition:fade={{ duration: 100 }}
|
||||||
|
>
|
||||||
{current_info}
|
{current_info}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { get, writable, type Writable } from "svelte/store";
|
import { get, writable, type Writable } from "svelte/store";
|
||||||
import { get_thumbnail_blob } from "../api_handler";
|
import { get_thumbnail_blob } from "../api_handler";
|
||||||
import { supported_file_types, type FolderElement } from "../types";
|
import { type FolderElement } from "../types";
|
||||||
import { db } from "../indexdb/file_thumbnails.db";
|
import { db } from "../indexdb/file_thumbnails.db";
|
||||||
|
import { get_file_type } from "../utils";
|
||||||
|
|
||||||
export const active_thumbnail_urls: Writable<string[]> = writable<string[]>([]);
|
export const active_thumbnail_urls: Writable<string[]> = writable<string[]>([]);
|
||||||
|
|
||||||
export async function generate_thumbnail(display_ip: string, path: string, folder_element: FolderElement): Promise<void> {
|
export async function generate_thumbnail(display_ip: string, path: string, folder_element: FolderElement): Promise<void> {
|
||||||
if (!Object.values(supported_file_types).some(e => e.mime_type === folder_element.type) || !folder_element.hash) return;
|
const supported_file_type = get_file_type(folder_element);
|
||||||
|
if (!supported_file_type || !folder_element.hash) return;
|
||||||
const hash: string = folder_element.hash;
|
const hash: string = folder_element.hash;
|
||||||
if (await db.thumbnail_blobs.get(hash)) return;
|
if (await db.thumbnail_blobs.get(hash)) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +1,18 @@
|
|||||||
import { FileBox, FileImage, FileVideoCamera, ImagePlay, type X } from "lucide-svelte";
|
import { FileBox, FileImage, FileText, FileVideoCamera, ImagePlay, type X } from "lucide-svelte";
|
||||||
import type { Snippet } from "svelte";
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
export type SupportedFileType = {
|
export type SupportedFileType = {
|
||||||
display_name:
|
display_name: string;
|
||||||
string; mime_type:
|
mime_type: string;
|
||||||
string; icon: typeof X;
|
|
||||||
};
|
};
|
||||||
|
export const supported_file_type_icon: Record<string, typeof X> = {
|
||||||
export const supported_file_types: Record<string, SupportedFileType> = {
|
'MP4': FileVideoCamera,
|
||||||
'.mp4': {
|
'JPG': FileImage,
|
||||||
display_name: 'MP4',
|
'PNG': FileImage,
|
||||||
mime_type: 'video/mp4',
|
'GIF': ImagePlay,
|
||||||
icon: FileVideoCamera,
|
'PPTX': FileBox,
|
||||||
},
|
'ODP': FileBox,
|
||||||
'.jpg': {
|
'PDF': FileText
|
||||||
display_name: 'JPG',
|
|
||||||
mime_type: 'image/jpg',
|
|
||||||
icon: FileImage,
|
|
||||||
},
|
|
||||||
'.jpeg': {
|
|
||||||
display_name: 'JPG',
|
|
||||||
mime_type: 'image/jpeg',
|
|
||||||
icon: FileImage,
|
|
||||||
},
|
|
||||||
'.png': {
|
|
||||||
display_name: 'PNG',
|
|
||||||
mime_type: 'image/png',
|
|
||||||
icon: FileImage,
|
|
||||||
},
|
|
||||||
'.gif': {
|
|
||||||
display_name: 'GIF',
|
|
||||||
mime_type: 'image/gif',
|
|
||||||
icon: ImagePlay,
|
|
||||||
},
|
|
||||||
'.pptx': {
|
|
||||||
display_name: 'PPTX',
|
|
||||||
mime_type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
||||||
icon: FileBox,
|
|
||||||
},
|
|
||||||
'.odp': {
|
|
||||||
display_name: 'ODP',
|
|
||||||
mime_type: 'application/vnd.oasis.opendocument.presentation',
|
|
||||||
icon: FileBox,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FolderElement = {
|
export type FolderElement = {
|
||||||
|
|||||||
@@ -1,19 +1,40 @@
|
|||||||
export function get_uuid(): string {
|
import type { FolderElement, SupportedFileType } from "./types";
|
||||||
return crypto.randomUUID();
|
import supported_file_types_json from './../../../../shared/supported_file_types.json';
|
||||||
|
|
||||||
|
const supported_file_types: Record<string, SupportedFileType> = supported_file_types_json as Record<string, SupportedFileType>;
|
||||||
|
|
||||||
|
export function get_file_type(file: FolderElement): SupportedFileType | null {
|
||||||
|
for (const key of Object.keys(supported_file_types)) {
|
||||||
|
if (file.type === supported_file_types[key].mime_type) {
|
||||||
|
return supported_file_types[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback:
|
||||||
|
const extension = file.name.split('.').pop();
|
||||||
|
if (extension) {
|
||||||
|
if (Object.keys(supported_file_types).includes('.' + extension)) {
|
||||||
|
return supported_file_types['.' + extension];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_file_size_display_string(size: number, toFixed: number|null = null): string {
|
export function get_uuid(): string {
|
||||||
if (size === 0) return "0 B";
|
return crypto.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
const k = 1024;
|
export function get_file_size_display_string(size: number, toFixed: number | null = null): string {
|
||||||
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
if (size === 0) return "0 B";
|
||||||
|
|
||||||
const i = Math.floor(Math.log(size) / Math.log(k));
|
const k = 1024;
|
||||||
const value = size / Math.pow(k, i);
|
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
||||||
|
|
||||||
const size_string = `${value.toFixed(toFixed !== null ? toFixed : Math.max(0, 2 - Math.floor(Math.log10(value))))} ${sizes[i]}`;
|
const i = Math.floor(Math.log(size) / Math.log(k));
|
||||||
|
const value = size / Math.pow(k, i);
|
||||||
|
|
||||||
return size_string.replace('.', ',');
|
const size_string = `${value.toFixed(toFixed !== null ? toFixed : Math.max(0, 2 - Math.floor(Math.log10(value))))} ${sizes[i]}`;
|
||||||
|
|
||||||
|
return size_string.replace('.', ',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -40,3 +61,9 @@ export async function image_content_hash(blob: Blob, size = 32): Promise<number>
|
|||||||
return hash >>> 0; // unsigned int
|
return hash >>> 0; // unsigned int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function is_valid_name(input: string): boolean {
|
||||||
|
return /^[\p{L}\p{N}\p{M}\-_.+,()[\]{}@!§$%&=~^ ]+$/u.test(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
".mp4": {
|
||||||
|
"display_name": "MP4",
|
||||||
|
"mime_type": "video/mp4"
|
||||||
|
},
|
||||||
|
".jpg": {
|
||||||
|
"display_name": "JPG",
|
||||||
|
"mime_type": "image/jpg"
|
||||||
|
},
|
||||||
|
".jpeg": {
|
||||||
|
"display_name": "JPG",
|
||||||
|
"mime_type": "image/jpeg"
|
||||||
|
},
|
||||||
|
".png": {
|
||||||
|
"display_name": "PNG",
|
||||||
|
"mime_type": "image/png"
|
||||||
|
},
|
||||||
|
".gif": {
|
||||||
|
"display_name": "GIF",
|
||||||
|
"mime_type": "image/gif"
|
||||||
|
},
|
||||||
|
".pptx": {
|
||||||
|
"display_name": "PPTX",
|
||||||
|
"mime_type": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
||||||
|
},
|
||||||
|
".odp": {
|
||||||
|
"display_name": "ODP",
|
||||||
|
"mime_type": "application/vnd.oasis.opendocument.presentation"
|
||||||
|
},
|
||||||
|
".pdf": {
|
||||||
|
"display_name": "PDF",
|
||||||
|
"mime_type": "application/pdf"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user