mirror of
https://codeberg.org/PLG-Development/PLG-MuDiCS
synced 2026-07-06 00:47:09 +00:00
file api added, bugs in file view fixed
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ArrowRight, Folder, Play, RefreshCcwDot, TriangleAlert } from 'lucide-svelte';
|
import { ArrowRight, Ban, FileIcon, Folder, Play, RefreshCcwDot, TriangleAlert } from 'lucide-svelte';
|
||||||
import {
|
import {
|
||||||
current_height,
|
current_height,
|
||||||
get_selectable_color_classes,
|
get_selectable_color_classes,
|
||||||
@@ -20,6 +20,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';
|
||||||
|
|
||||||
let { file } = $props<{ file: FolderElement }>();
|
let { file } = $props<{ file: FolderElement }>();
|
||||||
|
|
||||||
@@ -93,12 +94,14 @@
|
|||||||
<div class="flex flex-row h-{$current_height.file} w-full">
|
<div class="flex flex-row h-{$current_height.file} w-full">
|
||||||
<div class="h-{$current_height.file} aspect-square max-w-15 flex">
|
<div class="h-{$current_height.file} aspect-square max-w-15 flex">
|
||||||
<Button
|
<Button
|
||||||
|
disabled={!is_folder && get_file_type(file) === null}
|
||||||
|
title={!is_folder && get_file_type(file) === null ? 'Dateityp nicht unterstützt' : ''}
|
||||||
className="flex rounded-l-lg rounded-r-none {is_folder
|
className="flex rounded-l-lg rounded-r-none {is_folder
|
||||||
? 'text-stone-450'
|
? 'text-stone-450'
|
||||||
: 'text-stone-800'} w-full"
|
: 'text-stone-800'} w-full"
|
||||||
div_class="w-full"
|
div_class="w-full"
|
||||||
bg={get_selectable_color_classes(
|
bg={get_selectable_color_classes(
|
||||||
!is_folder,
|
!is_folder && get_file_type(file) !== null,
|
||||||
{
|
{
|
||||||
bg: true
|
bg: true
|
||||||
},
|
},
|
||||||
@@ -127,8 +130,10 @@
|
|||||||
<ArrowRight class="size-full" strokeWidth="3" />
|
<ArrowRight class="size-full" strokeWidth="3" />
|
||||||
{:else if get_display_ids_where_file_is_missing($current_file_path, file, $selected_display_ids, $all_files)[0].length !== 0}
|
{:else if get_display_ids_where_file_is_missing($current_file_path, file, $selected_display_ids, $all_files)[0].length !== 0}
|
||||||
<RefreshPlay className="size-full" />
|
<RefreshPlay className="size-full" />
|
||||||
{:else}
|
{:else if get_file_type(file) !== null}
|
||||||
<Play class="size-full" strokeWidth="3" />
|
<Play class="size-full" strokeWidth="3" />
|
||||||
|
{:else}
|
||||||
|
<Ban class="size-full" strokeWidth="3" />
|
||||||
{/if}
|
{/if}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,7 +149,7 @@
|
|||||||
hover: true,
|
hover: true,
|
||||||
active: true,
|
active: true,
|
||||||
text: true
|
text: true
|
||||||
})} rounded-r-lg transition-colors duration-200 gap-4 flex flex-row justify-between cursor-pointer group w-full h-full min-w-0"
|
})} rounded-r-lg transition-colors duration-200 gap-4 flex flex-row justify-between cursor-pointer group w-full min-w-0"
|
||||||
>
|
>
|
||||||
<div class="flex flex-row gap-2 min-w-0 w-full">
|
<div class="flex flex-row gap-2 min-w-0 w-full">
|
||||||
<div class="aspect-square rounded-md flex justify-center items-center p-2">
|
<div class="aspect-square rounded-md flex justify-center items-center p-2">
|
||||||
@@ -155,6 +160,8 @@
|
|||||||
{:else if get_file_type(file)?.icon}
|
{:else if get_file_type(file)?.icon}
|
||||||
{@const Icon = get_file_type(file)?.icon}
|
{@const Icon = get_file_type(file)?.icon}
|
||||||
<Icon class="size-full" />
|
<Icon class="size-full" />
|
||||||
|
{:else}
|
||||||
|
<FileIcon />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="content-center truncate select-none w-full" title={file.name}>
|
<div class="content-center truncate select-none w-full" title={file.name}>
|
||||||
@@ -215,8 +222,8 @@
|
|||||||
is_selected(file.id, $selected_file_ids)
|
is_selected(file.id, $selected_file_ids)
|
||||||
)} duration-200 transition-colors"
|
)} duration-200 transition-colors"
|
||||||
></div>
|
></div>
|
||||||
<div class="w-12 content-center text-center select-none text-xs whitespace-nowrap">
|
<div class="w-12 content-center text-center select-none text-xs whitespace-nowrap" title={get_file_size_display_string(file.size, 3)}>
|
||||||
{file.size}
|
{get_file_size_display_string(file.size)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -117,7 +117,7 @@
|
|||||||
open_path(0, $current_file_path);
|
open_path(0, $current_file_path);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<House class="size-full" />
|
<House class="size-full transition-all duration-100" strokeWidth={$current_file_path === '/' ? 2.7 : 2}/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{#if cut_folders !== 0}
|
{#if cut_folders !== 0}
|
||||||
|
|||||||
@@ -1,21 +1,62 @@
|
|||||||
|
import type { FolderElement } from "./types";
|
||||||
|
import { get_uuid } from "./utils";
|
||||||
|
|
||||||
|
interface FileInfo {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
size: string;
|
||||||
|
created: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_file_data(ip: string, path: string): Promise<FolderElement[]> {
|
||||||
export async function get_file_data(ip: string, path: string = './') {
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'content-type': 'application/json' },
|
headers: { 'content-type': 'application/json' },
|
||||||
body: `{"command":"cd ${path} && find . -maxdepth 1 -mindepth 1 -print0 | while IFS= read -r -d \'\' f; do\n typ=$(file -b --mime-type -- \"$f\")\n size=$(stat -c \'%s\' -- \"$f\")\n created=$(stat -c \'%w\' -- \"$f\")\n [ \"$created\" = \"-\" ] && created=$(stat -c \'%y\' -- \"$f\")\n jq -n --arg name \"$f\" --arg type \"$typ\" --arg size \"$size\" --arg created \"$created\" \'{name:$name, type:$type, size:($size|tonumber), created:$created}\' | tr -d \'\\n\'\n echo\n done\n"}`
|
body: JSON.stringify({
|
||||||
|
command: `cd .${path} && find . -maxdepth 1 -mindepth 1 -print0 | while IFS= read -r -d '' f; do
|
||||||
|
typ=$(file -b --mime-type -- "$f")
|
||||||
|
size=$(stat -c '%s' -- "$f")
|
||||||
|
created=$(stat -c '%w' -- "$f")
|
||||||
|
[ "$created" = "-" ] && created=$(stat -c '%y' -- "$f")
|
||||||
|
jq -n --arg name "$f" --arg type "$typ" --arg size "$size" --arg created "$created" \
|
||||||
|
'{name:$name, type:$type, size:($size|tostring), created:$created}' | tr -d '\n'
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
` })
|
||||||
};
|
};
|
||||||
console.log(request(ip, '/shellCommand', options));
|
const raw_response = await request(ip, '/shellCommand', options);
|
||||||
|
const response: FileInfo[] = raw_response.stdout.trim()
|
||||||
|
.split("\n")
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((line: string) => JSON.parse(line) as FileInfo);
|
||||||
|
|
||||||
|
const folder_element_list: FolderElement[] = [];
|
||||||
|
|
||||||
|
for (const response_element of response) {
|
||||||
|
// filter hidden files (start with '.' -> './.config')
|
||||||
|
if (response_element.name.charAt(2) !== '.') {
|
||||||
|
const folder_element: FolderElement = {
|
||||||
|
id: get_uuid(),
|
||||||
|
hash: JSON.stringify(response),
|
||||||
|
thumbnail: null,
|
||||||
|
name: response_element.name.slice(2), // remove "./"
|
||||||
|
type: response_element.type,
|
||||||
|
date_created: new Date(response_element.created),
|
||||||
|
size: Number(response_element.size),
|
||||||
|
};
|
||||||
|
folder_element_list.push(folder_element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return folder_element_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function request(ip: string, api_route: string, options: { method: string, headers?: Record<string, string>, body?: string }) {
|
async function request(ip: string, api_route: string, options: { method: string, headers?: Record<string, string>, body?: any }): Promise<any | null> {
|
||||||
try {
|
try {
|
||||||
const url = `http://${ip}:1323/api${api_route}`;
|
const url = `http://${ip}:1323/api${api_route}`;
|
||||||
|
console.log(url)
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url, options);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { get, writable, type Writable } from "svelte/store";
|
import { get, writable, type Writable } from "svelte/store";
|
||||||
import type { Display, DisplayGroup } from "../types";
|
import type { Display, DisplayGroup } from "../types";
|
||||||
import { is_selected, select, selected_display_ids } from "./select";
|
import { is_selected, select, selected_display_ids } from "./select";
|
||||||
|
import { get_uuid } from "../utils";
|
||||||
|
|
||||||
export const displays: Writable<DisplayGroup[]> = writable<DisplayGroup[]>([{
|
export const displays: Writable<DisplayGroup[]> = writable<DisplayGroup[]>([{
|
||||||
id: crypto.randomUUID(),
|
id: get_uuid(),
|
||||||
data: []
|
data: []
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
||||||
function add_display(ip: string, mac: string, name: string, status: string) {
|
function add_display(ip: string, mac: string, name: string, status: string) {
|
||||||
displays.update((displays: DisplayGroup[]) => {
|
displays.update((displays: DisplayGroup[]) => {
|
||||||
displays[0].data.push({ id: crypto.randomUUID(), ip, mac, name, status });
|
displays[0].data.push({ id: get_uuid(), ip, mac, name, status });
|
||||||
return displays;
|
return displays;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -58,7 +59,7 @@ export function get_display_by_id(display_id: string) {
|
|||||||
export function add_empty_display_group() {
|
export function add_empty_display_group() {
|
||||||
displays.update((displays: DisplayGroup[]) => {
|
displays.update((displays: DisplayGroup[]) => {
|
||||||
displays.push({
|
displays.push({
|
||||||
id: crypto.randomUUID(),
|
id: get_uuid(),
|
||||||
data: [],
|
data: [],
|
||||||
});
|
});
|
||||||
return displays;
|
return displays;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { FolderElement } from "../types";
|
|||||||
import { displays } from "./displays";
|
import { displays } from "./displays";
|
||||||
import { selected_display_ids, selected_file_ids } from "./select";
|
import { selected_display_ids, selected_file_ids } from "./select";
|
||||||
import { get_file_data } from "../api_handler";
|
import { get_file_data } from "../api_handler";
|
||||||
|
import { get_uuid } from "../utils";
|
||||||
|
|
||||||
export const all_files: Writable<Record<string, Record<string, FolderElement[]>>> = writable<Record<string, Record<string, FolderElement[]>>>({});
|
export const all_files: Writable<Record<string, Record<string, FolderElement[]>>> = writable<Record<string, Record<string, FolderElement[]>>>({});
|
||||||
// {
|
// {
|
||||||
@@ -59,17 +60,19 @@ export function updates_files_on_display(display_id: string, new_folder_elements
|
|||||||
files[file_path] = {};
|
files[file_path] = {};
|
||||||
}
|
}
|
||||||
for (const new_folder_element of new_folder_elements) {
|
for (const new_folder_element of new_folder_elements) {
|
||||||
new_folder_element.id = crypto.randomUUID();
|
new_folder_element.id = get_uuid();
|
||||||
}
|
}
|
||||||
files[file_path][display_id] = new_folder_elements;
|
files[file_path][display_id] = new_folder_elements;
|
||||||
return files;
|
return files;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_files_on_all_displays() {
|
async function get_files_on_all_displays(current_file_path: string) {
|
||||||
for (const display_group of get(displays)) {
|
for (const display_group of get(displays)) {
|
||||||
for (const display of display_group.data) {
|
for (const display of display_group.data) {
|
||||||
get_file_data(display.ip)
|
const new_folder_elements = await get_file_data(display.ip, current_file_path);
|
||||||
|
updates_files_on_display(display.id, new_folder_elements, current_file_path)
|
||||||
|
console.log(new_folder_elements)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +80,7 @@ function get_files_on_all_displays() {
|
|||||||
|
|
||||||
export function get_current_folder_elements(all_files: Record<string, Record<string, FolderElement[]>>, current_file_path: string, selected_display_ids: string[]) {
|
export function get_current_folder_elements(all_files: Record<string, Record<string, FolderElement[]>>, current_file_path: string, selected_display_ids: string[]) {
|
||||||
if (!all_files.hasOwnProperty(current_file_path)) {
|
if (!all_files.hasOwnProperty(current_file_path)) {
|
||||||
get_files_on_all_displays();
|
setTimeout(async () => { await get_files_on_all_displays(current_file_path) }, 0);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const files_on_display_array = all_files[current_file_path];
|
const files_on_display_array = all_files[current_file_path];
|
||||||
|
|||||||
@@ -46,12 +46,12 @@ export const supported_file_types: Record<string, SupportedFileType> = {
|
|||||||
|
|
||||||
export type FolderElement = {
|
export type FolderElement = {
|
||||||
id?: string;
|
id?: string;
|
||||||
hash: string;
|
hash: string | null;
|
||||||
thumbnail: Blob | null;
|
thumbnail: Blob | null;
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
date_created: Date;
|
date_created: Date;
|
||||||
size: string;
|
size: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export function get_uuid(): string {
|
||||||
|
return crypto.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get_file_size_display_string(size: number, toFixed: number|null = null): string {
|
||||||
|
if (size === 0) return "0 B";
|
||||||
|
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
||||||
|
|
||||||
|
const i = Math.floor(Math.log(size) / Math.log(k));
|
||||||
|
const value = size / Math.pow(k, i);
|
||||||
|
|
||||||
|
const size_string = `${value.toFixed(toFixed !== null ? toFixed : Math.max(0, 2 - Math.floor(Math.log10(value))))} ${sizes[i]}`;
|
||||||
|
|
||||||
|
return size_string.replace('.', ',');
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ patch {
|
|||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"command": "find . -maxdepth 1 -mindepth 1 -print0 | while IFS= read -r -d '' f; do\n typ=$(file -b --mime-type -- \"$f\")\n size=$(stat -c '%s' -- \"$f\")\n created=$(stat -c '%w' -- \"$f\")\n [ \"$created\" = \"-\" ] && created=$(stat -c '%y' -- \"$f\")\n jq -n --arg name \"$f\" --arg type \"$typ\" --arg size \"$size\" --arg created \"$created\" '{name:$name, type:$type, size:($size|tonumber), created:$created}' | tr -d '\\n'\n echo\n done\n"
|
"command": "find . -maxdepth 1 -mindepth 1 -print0 | xargs -0 -I{} bash -c 'typ=$(file -b --mime-type -- \"{}\"); size=$(stat -c \"%s\" -- \"{}\"); created=$(stat -c \"%w\" -- \"{}\"); [ \"$created\" = \"-\" ] && created=$(stat -c \"%y\" -- \"{}\"); jq -n --arg name \"{}\" --arg type \"$typ\" --arg size \"$size\" --arg created \"$created\" \"{name:\\$name, type:\\$type, size:\\$size, created:\\$created}\"'"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user