From cd7d074d317f7e7cd3169d1f365fe67c3b8ecafd Mon Sep 17 00:00:00 2001 From: Sam Cao Date: Mon, 26 Aug 2024 14:42:57 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20list=20view=20to=20li?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/format.ts | 9 +- src/routes/list.svelte | 420 ++++++++++++++++++++++++++++++-------- src/routes/toolbar.svelte | 8 +- 3 files changed, 343 insertions(+), 94 deletions(-) diff --git a/src/lib/format.ts b/src/lib/format.ts index 66ba43f..c5d7dca 100644 --- a/src/lib/format.ts +++ b/src/lib/format.ts @@ -71,6 +71,9 @@ export function transformResolution( if (rowData["Height"] && rowData["Width"]) { return `${rowData["Width"]}x${rowData["Height"]}`; } + if (rowData["Video:Height"] && rowData["Video:Width"]) { + return `${rowData["Video:Width"]}x${rowData["Video:Height"]}`; + } return ""; } @@ -106,8 +109,10 @@ export function transformTime( rowData: Record, _rowIndex: number ): string { - if (rowData["Duration"]) { - const duration = parseInt(rowData["Duration"]); + if (rowData["Duration"] || rowData["General:Duration"]) { + const duration = parseInt( + rowData["Duration"] ?? rowData["General:Duration"] + ); const totalSeconds = Math.floor(duration / 1000.0); const totalMinutes = Math.floor(totalSeconds / 60.0); const totalHours = Math.floor(totalMinutes / 60.0); diff --git a/src/routes/list.svelte b/src/routes/list.svelte index 62b2feb..0409271 100644 --- a/src/routes/list.svelte +++ b/src/routes/list.svelte @@ -18,7 +18,16 @@ import { getMatches } from "@tauri-apps/api/cli"; import { onMount } from "svelte"; - import { Button, Card, Header, Table, TextField, Tooltip } from "svelte-ux"; + import { + Button, + ButtonGroup, + Card, + type ColumnDef, + Header, + Table, + TextField, + Tooltip, + } from "svelte-ux"; import { openDirectoryDialog, openFileDialog } from "../lib/dialog"; import { deleteMediaFile, @@ -44,96 +53,257 @@ transformTime, } from "../lib/format"; - interface PropertyFormat { - format: - | (( - value: any, - rowData: Record, - rowIndex: number - ) => string) - | undefined; - header: string | null; + enum ViewType { + Card, + List, + } + + class PropertyDefinition { + format: ( + value: any, + rowData: Record, + rowIndex: number + ) => string; + headerForCardView: string | null; + headerForListView: string | null; name: string; virtual: boolean; - } + inCardView: boolean; + inListView: boolean; + + constructor( + name: string, + format: ( + value: any, + rowData: Record, + rowIndex: number + ) => string = transformDefault, + headerForCardView: string | null = null, + headerForListView: string | null = null, + virtual: boolean = false, + inCardView: boolean = false, + inListView: boolean = false + ) { + this.format = format; + this.headerForCardView = headerForCardView; + this.headerForListView = headerForListView; + this.name = name; + this.inCardView = inCardView; + this.inListView = inListView; + this.virtual = virtual; + } + + getHeaderForCardView(): string { + return this.headerForCardView ?? this.name; + } + + getHeaderForListView(): string { + return this.headerForListView ?? this.name; + } + + setHeaderForCardView(header: string | null): PropertyDefinition { + this.headerForCardView = header; + return this; + } + + setHeaderForListView(header: string | null): PropertyDefinition { + this.headerForListView = header; + return this; + } - function createFormat( - name: string, - format: - | (( - value: any, - rowData: Record, - rowIndex: number - ) => string) - | undefined = undefined, - header: string | null = null, - virtual: boolean = false - ): PropertyFormat { - return { format, header, name, virtual }; + setInCardView(inCardView: boolean = true): PropertyDefinition { + this.inCardView = inCardView; + return this; + } + + setInListView(inListView: boolean = true): PropertyDefinition { + this.inListView = inListView; + return this; + } + + setVirtual(virtual: boolean = true): PropertyDefinition { + this.virtual = virtual; + return this; + } } - const BUTTON_CLASSES_ALERT = + const BUTTON_CLASSES_SIDE_ALERT = "w-12 h-12 bg-white hover:bg-gray-200 text-gray-500 hover:text-red-500"; - const BUTTON_CLASSES_NORMAL = + const BUTTON_CLASSES_SIDE_NORMAL = "w-12 h-12 bg-white hover:bg-gray-200 text-gray-500 hover:text-blue-500"; - const BUTTON_CLASSES_TOOLBAR = + const BUTTON_CLASSES_TOOLBAR_ACTIVE = + "w-4 h-8 bg-cyan-400 hover:bg-cyan-600 text-white hover:text-blue-200"; + const BUTTON_CLASSES_TOOLBAR_NORMAL = + "w-4 h-8 bg-gray-400 hover:bg-gray-600 text-white hover:text-blue-200"; + const BUTTON_CLASSES_TOOLBAR_LARGE = "w-12 h-12 bg-gray-400 hover:bg-gray-600 text-white hover:text-blue-200"; - const COMMON_PROPERTIES_GENERAL: Array = [ - createFormat("Format", transformDefault), - createFormat("FileSize", transformSize, "Size"), - createFormat("Duration", transformDuration), - createFormat("Time", transformTime, null, true), - createFormat("Title", transformDefault), - createFormat("Encoded_Date", transformDefault, "Encoded Date"), + const COMMON_PROPERTIES_GENERAL: Array = [ + new PropertyDefinition("Format") + .setHeaderForListView("File Format") + .setInCardView() + .setInListView(), + new PropertyDefinition("FileSize", transformSize, "Size", "File Size") + .setInCardView() + .setInListView(), + new PropertyDefinition("Duration", transformDuration) + .setInCardView() + .setInListView(), + new PropertyDefinition("Time", transformTime) + .setVirtual() + .setInCardView() + .setInListView(), + new PropertyDefinition("Title") + .setHeaderForListView("File Title") + .setInCardView() + .setInListView(), + new PropertyDefinition("Encoded_Date") + .setHeaderForCardView("Encoded Date") + .setHeaderForListView("Encoded Date") + .setInCardView() + .setInListView(), ]; - const COMMON_PROPERTIES_VIDEO: Array = [ - createFormat("ID", transformDefault), - createFormat("Format", transformDefault), - createFormat("Language", transformDefault), - createFormat("Title", transformDefault), - createFormat("Resolution", transformResolution, null, true), - createFormat("HDR_Format_Compatibility", transformDefault, "HDR"), - createFormat("ScanType", transformDefault, "Scan Type"), - createFormat("Default", transformDefault, "D"), - createFormat("Forced", transformDefault, "F"), - createFormat("FrameRate", transformDefault, "FPS"), - createFormat("BitRate", transformBitRate, "Bit Rate"), - createFormat("StreamSize", transformSize, "Size"), - createFormat("Width"), - createFormat("Height"), + const COMMON_PROPERTIES_VIDEO: Array = [ + new PropertyDefinition("ID").setInCardView(), + new PropertyDefinition("Format") + .setHeaderForListView("Video Format") + .setInCardView() + .setInListView(), + new PropertyDefinition("Language") + .setHeaderForListView("Video Language") + .setInCardView() + .setInListView(), + new PropertyDefinition("Title") + .setHeaderForListView("Video Title") + .setInCardView() + .setInListView(), + new PropertyDefinition("Resolution", transformResolution) + .setVirtual() + .setInCardView() + .setInListView(), + new PropertyDefinition("HDR_Format_Compatibility") + .setHeaderForCardView("HDR") + .setHeaderForListView("HDR") + .setInCardView() + .setInListView(), + new PropertyDefinition("ScanType") + .setHeaderForCardView("Scan Type") + .setHeaderForListView("Scan Type") + .setInCardView() + .setInListView(), + new PropertyDefinition("Default").setHeaderForCardView("D"), + new PropertyDefinition("Forced").setHeaderForCardView("F"), + new PropertyDefinition("BitDepth") + .setHeaderForCardView("Depth") + .setHeaderForListView("Video Bit Depth") + .setInCardView() + .setInListView(), + new PropertyDefinition("FrameRate") + .setHeaderForCardView("FPS") + .setHeaderForListView("FPS") + .setInCardView() + .setInListView(), + new PropertyDefinition( + "BitRate", + transformBitRate, + "Bit Rate", + "Video Bit Rate" + ) + .setInCardView() + .setInListView(), + new PropertyDefinition("StreamSize", transformSize, "Size", "Video Size") + .setInCardView() + .setInListView(), + new PropertyDefinition("Width"), + new PropertyDefinition("Height"), ]; - const COMMON_PROPERTIES_AUDIO: Array = [ - createFormat("ID", transformDefault), - createFormat("Format_Commercial", transformDefault, "Format"), - createFormat("Language", transformDefault), - createFormat("Title", transformDefault), - createFormat("Channel(s)", transformDefault, "CH"), - createFormat("BitDepth", transformDefault, "Depth"), - createFormat("SamplingRate", transformSamplingRate, "Sampling"), - createFormat("Default", transformDefault, "D"), - createFormat("Forced", transformDefault, "F"), - createFormat("BitRate_Mode", transformDefault, "Mode"), - createFormat("BitRate", transformBitRate, "Bit Rate"), - createFormat("StreamSize", transformSize, "Size"), + const COMMON_PROPERTIES_AUDIO: Array = [ + new PropertyDefinition("ID").setInCardView(), + new PropertyDefinition("Format_Commercial") + .setHeaderForCardView("Format") + .setHeaderForListView("Audio Format") + .setInCardView() + .setInListView(), + new PropertyDefinition("Language") + .setHeaderForListView("Audio Language") + .setInCardView() + .setInListView(), + new PropertyDefinition("Title") + .setHeaderForListView("Audio Title") + .setInCardView() + .setInListView(), + new PropertyDefinition("Channel(s)") + .setHeaderForCardView("CH") + .setHeaderForListView("CH") + .setInCardView() + .setInListView(), + new PropertyDefinition("BitDepth") + .setHeaderForCardView("Depth") + .setHeaderForListView("Audio Bit Depth") + .setInCardView() + .setInListView(), + new PropertyDefinition( + "SamplingRate", + transformSamplingRate, + "Sampling", + "Audio Sampling Rate" + ) + .setInCardView() + .setInListView(), + new PropertyDefinition("Default").setHeaderForCardView("D").setInCardView(), + new PropertyDefinition("Forced").setHeaderForCardView("F").setInCardView(), + new PropertyDefinition("BitRate_Mode") + .setHeaderForCardView("Mode") + .setHeaderForListView("Audio Bit Rate Mode") + .setInCardView() + .setInListView(), + new PropertyDefinition( + "BitRate", + transformBitRate, + "Bit Rate", + "Audio Bit Rate" + ) + .setInCardView() + .setInListView(), + new PropertyDefinition("StreamSize", transformSize, "Size", "Audio Size") + .setInCardView() + .setInListView(), ]; - const COMMON_PROPERTIES_TEXT: Array = [ - createFormat("ID", transformDefault), - createFormat("Format", transformDefault), - createFormat("Language", transformDefault), - createFormat("Title", transformDefault), - createFormat("Default", transformDefault, "D"), - createFormat("Forced", transformDefault, "F"), - createFormat("BitRate", transformBitRate, "Bit Rate"), - createFormat("StreamSize", transformSize, "Size"), + const COMMON_PROPERTIES_TEXT: Array = [ + new PropertyDefinition("ID").setInCardView(), + new PropertyDefinition("Format") + .setHeaderForListView("Text Format") + .setInCardView() + .setInListView(), + new PropertyDefinition("Language") + .setHeaderForListView("Text Language") + .setInCardView() + .setInListView(), + new PropertyDefinition("Title") + .setHeaderForListView("Text Title") + .setInCardView() + .setInListView(), + new PropertyDefinition("Default").setHeaderForCardView("D").setInCardView(), + new PropertyDefinition("Forced").setHeaderForCardView("F").setInCardView(), + new PropertyDefinition( + "BitRate", + transformBitRate, + "Bit Rate", + "Text Bit Rate" + ) + .setInCardView() + .setInListView(), + new PropertyDefinition("StreamSize", transformSize, "Size", "Text Size") + .setInCardView() + .setInListView(), ]; const COMMON_PROPERTIES_MAP = new Map< Protocol.StreamKind, - Array + Array >([ [Protocol.StreamKind.General, COMMON_PROPERTIES_GENERAL], [Protocol.StreamKind.Video, COMMON_PROPERTIES_VIDEO], @@ -143,6 +313,7 @@ let files: string[] = []; let query: string | null = null; + let viewType: ViewType = ViewType.Card; let fileToAllPropertiesMap: Map< string, @@ -158,13 +329,54 @@ Map > = new Map(); - $: fileToPropertyMap = generateFileToPropertyMap( + $: buttonCardViewClass = + viewType == ViewType.Card + ? BUTTON_CLASSES_TOOLBAR_ACTIVE + : BUTTON_CLASSES_TOOLBAR_NORMAL; + + $: buttonListViewClass = + viewType == ViewType.List + ? BUTTON_CLASSES_TOOLBAR_ACTIVE + : BUTTON_CLASSES_TOOLBAR_NORMAL; + + $: fileToPropertyMaps = generateFileToPropertyMaps( query, files, fileToStreamCountMap, fileToCommonPropertyMap ); + $: dataOfListView = [...fileToPropertyMaps.entries()].map( + ([file, propertyMaps]) => + propertyMaps + .filter((map) => map.num == 0) + .map((map) => { + const newProperty: Record = {}; + Object.entries(map.propertyMap).map(([property, value]) => { + newProperty[`${map.stream}:${property}`] = value; + }); + return newProperty; + }) + .reduce((acc, cur) => ({ ...acc, ...cur }), {}) + ); + + $: columnsOfListView = [...COMMON_PROPERTIES_MAP.entries()] + .map(([stream, commonProperties]) => + commonProperties.map((propertyDefinition) => { + return { stream, propertyDefinition }; + }) + ) + .flatMap((streamAndPropertyDefinition) => streamAndPropertyDefinition) + .filter(({ stream, propertyDefinition }) => propertyDefinition.inListView) + .map(({ stream, propertyDefinition }) => { + return { + name: `${stream}:${propertyDefinition.name}`, + header: propertyDefinition.getHeaderForListView(), + align: "left", + format: propertyDefinition.format, + } as ColumnDef>; + }); + onMount(() => { mediaFileToAllPropertiesMap.subscribe((value) => { fileToAllPropertiesMap = value; @@ -241,7 +453,7 @@ }); }); - function generateFileToPropertyMap( + function generateFileToPropertyMaps( query: string | null, files: string[], streamCountMap: Map>, @@ -317,7 +529,7 @@
{:else} +
+ + + + + + + + +
- {#if fileToPropertyMap.size == 0} + {#if fileToPropertyMaps.size == 0}
Not Found
- {:else} + {:else if viewType == ViewType.Card} {#each files as file} - {#if fileToPropertyMap.has(file)} + {#if fileToPropertyMaps.has(file)}
{#each [...COMMON_PROPERTIES_MAP.entries()] as commonPropertiesEntry} - {#if fileToPropertyMap + {#if fileToPropertyMaps .get(file) ?.some((map) => map.stream === commonPropertiesEntry[0])} map.stream === commonPropertiesEntry[0]) ?.map((map) => map.propertyMap)} columns={commonPropertiesEntry[1] - .filter((property) => property.format !== undefined) + .filter((property) => property.inCardView) .map((property) => { return { name: property.name, - header: property.header - ? property.header - : property.name, + header: property.getHeaderForCardView(), align: "left", format: property.format, }; @@ -415,6 +649,16 @@ {/if} {/each} + {:else if viewType == ViewType.List} +
{/if} {/if} diff --git a/src/routes/toolbar.svelte b/src/routes/toolbar.svelte index 77ef0ef..27013e3 100644 --- a/src/routes/toolbar.svelte +++ b/src/routes/toolbar.svelte @@ -29,10 +29,10 @@ } from "../lib/store"; import * as Protocol from "../lib/protocol"; + const BUTTON_CLASSES_ACTIVE = + "w-4 h-8 bg-cyan-400 hover:bg-cyan-600 text-white hover:text-blue-200"; const BUTTON_CLASSES_NORMAL = "w-4 h-8 bg-gray-400 hover:bg-gray-600 text-white hover:text-blue-200"; - const BUTTON_CLASSES_VISIBLE = - "w-4 h-8 bg-cyan-400 hover:bg-cyan-600 text-white hover:text-blue-200"; let buttonAboutClasses = BUTTON_CLASSES_NORMAL; let buttonSettingsClasses = BUTTON_CLASSES_NORMAL; @@ -51,13 +51,13 @@ buttonAboutClasses = value === Protocol.ControlStatus.Hidden ? BUTTON_CLASSES_NORMAL - : BUTTON_CLASSES_VISIBLE; + : BUTTON_CLASSES_ACTIVE; }); tabSettingsStatus.subscribe((value) => { buttonSettingsClasses = value === Protocol.ControlStatus.Hidden ? BUTTON_CLASSES_NORMAL - : BUTTON_CLASSES_VISIBLE; + : BUTTON_CLASSES_ACTIVE; }); document.addEventListener("keyup", onKeyUp); });