From aa12585b204efff0014b6e966beecbc3480ac0cf Mon Sep 17 00:00:00 2001 From: ChrisScott9456 Date: Mon, 15 Jun 2026 10:48:18 -0400 Subject: [PATCH] lint fixes --- src/main/features/core/tag-editor/index.ts | 10 ++---- .../core/tag-editor/taglib-service.ts | 35 +++++++++---------- src/preload/utils.ts | 2 +- src/renderer/api/controller.ts | 24 ++++++------- src/renderer/api/jellyfin/jellyfin-api.ts | 20 +++++------ .../api/jellyfin/jellyfin-controller.ts | 28 +++++++-------- .../api/navidrome/navidrome-controller.ts | 2 +- src/renderer/api/subsonic/subsonic-api.ts | 32 ++++++++--------- .../api/subsonic/subsonic-controller.ts | 22 ++++++------ .../actions/edit-metadata-action.tsx | 8 ++--- .../menus/playlist-song-context-menu.tsx | 2 +- .../context-menu/menus/song-context-menu.tsx | 2 +- .../components/artwork-panel.module.css | 12 +++---- .../tag-editor/components/artwork-panel.tsx | 13 +++---- .../components/song-edit-modal.module.css | 16 ++++----- .../tag-editor/components/song-edit-modal.tsx | 7 ++-- .../components/tag-field-row.module.css | 4 +-- .../tag-editor/components/tag-field-row.tsx | 10 +++--- .../tag-editor/hooks/use-metadata-editor.ts | 2 ++ .../features/tag-editor/utils/known-tags.ts | 6 ++-- src/shared/api/subsonic/subsonic-types.ts | 8 ++--- src/shared/types/domain-types.ts | 23 ++++++------ src/shared/types/tag-editor.ts | 20 +++++------ 23 files changed, 149 insertions(+), 159 deletions(-) diff --git a/src/main/features/core/tag-editor/index.ts b/src/main/features/core/tag-editor/index.ts index 2d3077c42..7fdf67ff8 100644 --- a/src/main/features/core/tag-editor/index.ts +++ b/src/main/features/core/tag-editor/index.ts @@ -6,11 +6,7 @@ import type { import { ipcMain, type IpcMainInvokeEvent } from 'electron'; -import { - readFilesMetadataBatch, - readLocalImageFile, - writeFilesTags, -} from './taglib-service'; +import { readFilesMetadataBatch, readLocalImageFile, writeFilesTags } from './taglib-service'; const sendBatchProgress = (event: IpcMainInvokeEvent, processed: number, total: number) => { event.sender.send('batch-progress', { processed, total }); @@ -46,8 +42,8 @@ ipcMain.handle( artworkKind: 'none', error: result.failedFiles[0]?.error ?? 'No readable audio files in selection', failedFiles: result.failedFiles, - success: false, readCount: result.readCount, + success: false, totalCount: result.totalCount, }; } @@ -55,8 +51,8 @@ ipcMain.handle( return { artworkKind: result.artworkKind, failedFiles: result.failedFiles.length > 0 ? result.failedFiles : undefined, - success: true, readCount: result.readCount, + success: true, tagSummary: result.tagSummary, totalCount: result.totalCount, ...(result.artworkData diff --git a/src/main/features/core/tag-editor/taglib-service.ts b/src/main/features/core/tag-editor/taglib-service.ts index 3f62f13bd..10b1781de 100644 --- a/src/main/features/core/tag-editor/taglib-service.ts +++ b/src/main/features/core/tag-editor/taglib-service.ts @@ -1,12 +1,11 @@ import type { ArtworkKind, ArtworkOp, BatchFileError } from '/@/shared/types/tag-editor'; import type { ICommonTagsResult } from 'music-metadata'; -import { EDITOR_FIELD_KEYS } from '/@/shared/types/tag-editor'; - import { constants, promises as fsPromises } from 'fs'; import * as mm from 'music-metadata'; import { TagLib } from 'taglib-wasm'; +import { EDITOR_FIELD_KEYS } from '/@/shared/types/tag-editor'; import { getImageMimeTypeFromPath } from '/@/shared/utils/image-mime'; let _taglib: null | TagLib = null; @@ -65,20 +64,6 @@ const MM_CUSTOM: Partial> = { trackNumber: (c) => c.track.no, }; -/** Maps a music-metadata `ICommonTagsResult` to the flat camelCase key map used by the editor. */ -export function flattenMusicMetadata(common: ICommonTagsResult): Record { - const flat: Record = {}; - for (const key of EDITOR_FIELD_KEYS) { - const custom = MM_CUSTOM[key]; - const raw: unknown = custom - ? custom(common) - : common[(MM_RENAMES[key] ?? key) as keyof ICommonTagsResult]; - const value = Array.isArray(raw) ? raw[0] : raw; - if (value != null && value !== '') flat[key] = String(value); - } - return flat; -} - /** Returns an error entry for each path that is missing or not writable by the current process. */ export async function checkPathsWritable(paths: string[]): Promise { const failed: BatchFileError[] = []; @@ -97,6 +82,20 @@ export async function checkPathsWritable(paths: string[]): Promise { + const flat: Record = {}; + for (const key of EDITOR_FIELD_KEYS) { + const custom = MM_CUSTOM[key]; + const raw: unknown = custom + ? custom(common) + : common[(MM_RENAMES[key] ?? key) as keyof ICommonTagsResult]; + const value = Array.isArray(raw) ? raw[0] : raw; + if (value != null && value !== '') flat[key] = String(value); + } + return flat; +} + /** Runs `fn` over `items` with at most `concurrency` tasks in flight at once. Stops early if `signal` is aborted. */ const mapWithConcurrency = async ( items: T[], @@ -130,8 +129,8 @@ export async function readFilesMetadataBatch( artworkKind: ArtworkKind; artworkMimeType?: string; failedFiles: BatchFileError[]; - success: boolean; readCount: number; + success: boolean; tagSummary: Record; totalCount: number; }> { @@ -216,8 +215,8 @@ export async function readFilesMetadataBatch( return { artworkKind, failedFiles, - success: readCount > 0, readCount, + success: readCount > 0, tagSummary, totalCount, ...(artworkData ? { artworkData, artworkMimeType } : {}), diff --git a/src/preload/utils.ts b/src/preload/utils.ts index 48181f7e8..a6c344d7d 100644 --- a/src/preload/utils.ts +++ b/src/preload/utils.ts @@ -1,4 +1,4 @@ -import { type IpcRendererEvent, ipcRenderer, webFrame } from 'electron'; +import { ipcRenderer, type IpcRendererEvent, webFrame } from 'electron'; import type { ArtworkOp, diff --git a/src/renderer/api/controller.ts b/src/renderer/api/controller.ts index 9326560cf..d72f95aab 100644 --- a/src/renderer/api/controller.ts +++ b/src/renderer/api/controller.ts @@ -864,6 +864,18 @@ export const controller: GeneralController = { server.type, )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); }, + startScan(args) { + const server = getServerById(args.apiClientProps.serverId); + + if (!server) { + throw new Error(`${i18n.t('error.apiRouteError')}: startScan`); + } + + return apiController( + 'startScan', + server.type, + )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); + }, updateInternetRadioStation(args) { const server = getServerById(args.apiClientProps.serverId); @@ -924,16 +936,4 @@ export const controller: GeneralController = { server.type, )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); }, - startScan(args) { - const server = getServerById(args.apiClientProps.serverId); - - if (!server) { - throw new Error(`${i18n.t('error.apiRouteError')}: startScan`); - } - - return apiController( - 'startScan', - server.type, - )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); - }, }; diff --git a/src/renderer/api/jellyfin/jellyfin-api.ts b/src/renderer/api/jellyfin/jellyfin-api.ts index 75b97a427..57872e125 100644 --- a/src/renderer/api/jellyfin/jellyfin-api.ts +++ b/src/renderer/api/jellyfin/jellyfin-api.ts @@ -365,6 +365,16 @@ export const contract = c.router({ 400: jfType._response.error, }, }, + startScan: { + body: z.null(), + method: 'POST', + path: 'Library/Refresh', + query: z.object({}), + responses: { + 204: z.null(), + 400: jfType._response.error, + }, + }, updatePlaylist: { body: jfType._parameters.updatePlaylist, method: 'POST', @@ -392,16 +402,6 @@ export const contract = c.router({ 400: jfType._response.error, }, }, - startScan: { - body: z.null(), - method: 'POST', - path: 'Library/Refresh', - query: z.object({}), - responses: { - 204: z.null(), - 400: jfType._response.error, - }, - }, }); const axiosClient = axios.create({}); diff --git a/src/renderer/api/jellyfin/jellyfin-controller.ts b/src/renderer/api/jellyfin/jellyfin-controller.ts index 395257851..f736c3fff 100644 --- a/src/renderer/api/jellyfin/jellyfin-controller.ts +++ b/src/renderer/api/jellyfin/jellyfin-controller.ts @@ -1931,6 +1931,20 @@ export const JellyfinController: InternalControllerEndpoint = { return null; }, + startScan: async (args) => { + const { apiClientProps } = args; + + const res = await jfApiClient(apiClientProps).startScan({ + body: null, + query: {}, + }); + + if (res.status !== 204) { + throw new Error('Failed to start scan'); + } + + return null; + }, updateInternetRadioStation: async (args) => { const { apiClientProps, body, query } = args; @@ -1996,20 +2010,6 @@ export const JellyfinController: InternalControllerEndpoint = { 'Failed to upload playlist image', ); }, - startScan: async (args) => { - const { apiClientProps } = args; - - const res = await jfApiClient(apiClientProps).startScan({ - body: null, - query: {}, - }); - - if (res.status !== 204) { - throw new Error('Failed to start scan'); - } - - return null; - }, }; function getLibraryId(musicFolderId?: string | string[]) { diff --git a/src/renderer/api/navidrome/navidrome-controller.ts b/src/renderer/api/navidrome/navidrome-controller.ts index b5fc0763a..3b0cd021a 100644 --- a/src/renderer/api/navidrome/navidrome-controller.ts +++ b/src/renderer/api/navidrome/navidrome-controller.ts @@ -1261,6 +1261,7 @@ export const NavidromeController: InternalControllerEndpoint = { id: res.body.data.id, }; }, + startScan: SubsonicController.startScan, updateInternetRadioStation: async (args) => { const { apiClientProps, body, query } = args; @@ -1411,5 +1412,4 @@ export const NavidromeController: InternalControllerEndpoint = { return res.data?.status === 'ok'; }, - startScan: SubsonicController.startScan, }; diff --git a/src/renderer/api/subsonic/subsonic-api.ts b/src/renderer/api/subsonic/subsonic-api.ts index 10ac13257..3f9898cba 100644 --- a/src/renderer/api/subsonic/subsonic-api.ts +++ b/src/renderer/api/subsonic/subsonic-api.ts @@ -186,6 +186,14 @@ export const contract = c.router({ 200: ssType._response.randomSongList, }, }, + getScanStatus: { + method: 'GET', + path: 'getScanStatus.view', + query: ssType._parameters.getScanStatus, + responses: { + 200: ssType._response.getScanStatus, + }, + }, getServerInfo: { method: 'GET', path: 'getOpenSubsonicExtensions.view', @@ -337,6 +345,14 @@ export const contract = c.router({ 200: ssType._response.setRating, }, }, + startScan: { + method: 'GET', + path: 'startScan.view', + query: ssType._parameters.startScan, + responses: { + 200: ssType._response.startScan, + }, + }, updateInternetRadioStation: { method: 'GET', path: 'updateInternetRadioStation.view', @@ -353,22 +369,6 @@ export const contract = c.router({ 200: ssType._response.baseResponse, }, }, - startScan: { - method: 'GET', - path: 'startScan.view', - query: ssType._parameters.startScan, - responses: { - 200: ssType._response.startScan, - }, - }, - getScanStatus: { - method: 'GET', - path: 'getScanStatus.view', - query: ssType._parameters.getScanStatus, - responses: { - 200: ssType._response.getScanStatus, - }, - }, }); const axiosClient = axios.create({}); diff --git a/src/renderer/api/subsonic/subsonic-controller.ts b/src/renderer/api/subsonic/subsonic-controller.ts index 4984b33ea..3d75d0569 100644 --- a/src/renderer/api/subsonic/subsonic-controller.ts +++ b/src/renderer/api/subsonic/subsonic-controller.ts @@ -2461,6 +2461,17 @@ export const SubsonicController: InternalControllerEndpoint = { return null; }, + startScan: async (args) => { + const { apiClientProps } = args; + + const res = await ssApiClient(apiClientProps).startScan({ query: {} }); + + if (res.status !== 200) { + throw new Error('Failed to start scan'); + } + + return res.body.scanStatus; + }, updateInternetRadioStation: async (args) => { const { apiClientProps, body, query } = args; @@ -2497,17 +2508,6 @@ export const SubsonicController: InternalControllerEndpoint = { return null; }, - startScan: async (args) => { - const { apiClientProps } = args; - - const res = await ssApiClient(apiClientProps).startScan({ query: {} }); - - if (res.status !== 200) { - throw new Error('Failed to start scan'); - } - - return res.body.scanStatus; - }, }; function getLibraryId(musicFolderId?: string | string[]) { diff --git a/src/renderer/features/context-menu/actions/edit-metadata-action.tsx b/src/renderer/features/context-menu/actions/edit-metadata-action.tsx index 72d5d273c..7dc40b02d 100644 --- a/src/renderer/features/context-menu/actions/edit-metadata-action.tsx +++ b/src/renderer/features/context-menu/actions/edit-metadata-action.tsx @@ -1,13 +1,13 @@ +import { openModal } from '@mantine/modals'; import isElectron from 'is-electron'; -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { openModal } from '@mantine/modals'; import { controller } from '/@/renderer/api/controller'; +import { SongEditModal } from '/@/renderer/features/tag-editor/components/song-edit-modal'; import { useCurrentServer } from '/@/renderer/store'; import { ContextMenu } from '/@/shared/components/context-menu/context-menu'; import { Song } from '/@/shared/types/domain-types'; -import { SongEditModal } from '/@/renderer/features/tag-editor/components/song-edit-modal'; interface EditMetadataActionProps { albumIds?: string[]; @@ -31,7 +31,7 @@ const getAlbumSongs = async (albumIds: string[], serverId: string): Promise { const { t } = useTranslation(); const server = useCurrentServer(); - const songs = songItems?.filter((s) => s.path) ?? []; + const songs = useMemo(() => songItems?.filter((s) => s.path) ?? [], [songItems]); const count = albumIds?.length ?? songs.length; const onSelect = useCallback(async () => { diff --git a/src/renderer/features/context-menu/menus/playlist-song-context-menu.tsx b/src/renderer/features/context-menu/menus/playlist-song-context-menu.tsx index 9dcdd4e3f..49ed111f5 100644 --- a/src/renderer/features/context-menu/menus/playlist-song-context-menu.tsx +++ b/src/renderer/features/context-menu/menus/playlist-song-context-menu.tsx @@ -1,8 +1,8 @@ import { useMemo } from 'react'; import { AddToPlaylistAction } from '/@/renderer/features/context-menu/actions/add-to-playlist-action'; -import { EditMetadataAction } from '/@/renderer/features/context-menu/actions/edit-metadata-action'; import { DownloadAction } from '/@/renderer/features/context-menu/actions/download-action'; +import { EditMetadataAction } from '/@/renderer/features/context-menu/actions/edit-metadata-action'; import { GetInfoAction } from '/@/renderer/features/context-menu/actions/get-info-action'; import { GoToAction } from '/@/renderer/features/context-menu/actions/go-to-action'; import { PlayAction } from '/@/renderer/features/context-menu/actions/play-action'; diff --git a/src/renderer/features/context-menu/menus/song-context-menu.tsx b/src/renderer/features/context-menu/menus/song-context-menu.tsx index ea307be22..7fbca2050 100644 --- a/src/renderer/features/context-menu/menus/song-context-menu.tsx +++ b/src/renderer/features/context-menu/menus/song-context-menu.tsx @@ -1,8 +1,8 @@ import { useMemo } from 'react'; import { AddToPlaylistAction } from '/@/renderer/features/context-menu/actions/add-to-playlist-action'; -import { EditMetadataAction } from '/@/renderer/features/context-menu/actions/edit-metadata-action'; import { DownloadAction } from '/@/renderer/features/context-menu/actions/download-action'; +import { EditMetadataAction } from '/@/renderer/features/context-menu/actions/edit-metadata-action'; import { GetInfoAction } from '/@/renderer/features/context-menu/actions/get-info-action'; import { GoToAction } from '/@/renderer/features/context-menu/actions/go-to-action'; import { PlayAction } from '/@/renderer/features/context-menu/actions/play-action'; diff --git a/src/renderer/features/tag-editor/components/artwork-panel.module.css b/src/renderer/features/tag-editor/components/artwork-panel.module.css index f964a7d07..2a5c2315d 100644 --- a/src/renderer/features/tag-editor/components/artwork-panel.module.css +++ b/src/renderer/features/tag-editor/components/artwork-panel.module.css @@ -1,15 +1,15 @@ -.artworkBox { - aspect-ratio: 1 / 1; +.artwork-box { width: 100%; + aspect-ratio: 1 / 1; + container-type: inline-size; overflow: hidden; cursor: pointer; background: var(--mantine-color-dark-6); border: 1px solid var(--mantine-color-default-border); border-radius: var(--mantine-radius-sm); - container-type: inline-size; } -.artworkImage { +.artwork-image { width: 100%; height: 100%; object-fit: cover; @@ -20,10 +20,10 @@ opacity: 0.4; } -.placeholderText { +.placeholder-text { font-size: 5cqw; } -.removeButton { +.remove-button { color: var(--theme-colors-state-error); } diff --git a/src/renderer/features/tag-editor/components/artwork-panel.tsx b/src/renderer/features/tag-editor/components/artwork-panel.tsx index 0b3de5bc9..7b2075aa4 100644 --- a/src/renderer/features/tag-editor/components/artwork-panel.tsx +++ b/src/renderer/features/tag-editor/components/artwork-panel.tsx @@ -1,10 +1,10 @@ -import { DragDropZone } from '/@/shared/components/drag-drop-zone/drag-drop-zone'; +import styles from './artwork-panel.module.css'; + import { Button } from '/@/shared/components/button/button'; +import { DragDropZone } from '/@/shared/components/drag-drop-zone/drag-drop-zone'; import { Stack } from '/@/shared/components/stack/stack'; import { Text } from '/@/shared/components/text/text'; -import styles from './artwork-panel.module.css'; - interface ArtworkPanelProps { artworkDisplayUrl: null | string; artworkIsMixed: boolean; @@ -49,12 +49,7 @@ export const ArtworkPanel = ({ )} {showRemoveButton && ( - )} diff --git a/src/renderer/features/tag-editor/components/song-edit-modal.module.css b/src/renderer/features/tag-editor/components/song-edit-modal.module.css index 5d69680ba..677559c3e 100644 --- a/src/renderer/features/tag-editor/components/song-edit-modal.module.css +++ b/src/renderer/features/tag-editor/components/song-edit-modal.module.css @@ -1,26 +1,26 @@ -.tabLabel { +.tab-label { font-size: 1rem; } -.tableScroller { +.table-scroller { max-height: 55vh; - overflow-y: auto; - margin-top: var(--theme-spacing-sm); padding-top: var(--theme-spacing-sm); padding-bottom: var(--theme-spacing-sm); + margin-top: var(--theme-spacing-sm); + overflow-y: auto; border-top: 1px solid var(--mantine-color-default-border); } -.tableCell { +.table-cell { padding: var(--theme-spacing-xs) var(--theme-spacing-sm); vertical-align: middle; } -.tableHeader { +.table-header { width: 1%; - color: var(--theme-colors-foreground-muted); - font-weight: 500; padding: var(--theme-spacing-xs) var(--theme-spacing-sm); + font-weight: 500; vertical-align: middle; + color: var(--theme-colors-foreground-muted); white-space: nowrap; } diff --git a/src/renderer/features/tag-editor/components/song-edit-modal.tsx b/src/renderer/features/tag-editor/components/song-edit-modal.tsx index a94bc1aba..f20d7eeb5 100644 --- a/src/renderer/features/tag-editor/components/song-edit-modal.tsx +++ b/src/renderer/features/tag-editor/components/song-edit-modal.tsx @@ -2,7 +2,10 @@ import { closeAllModals } from '@mantine/modals'; import { useRef } from 'react'; import { useTranslation } from 'react-i18next'; +import { useMetadataEditor } from '../hooks/use-metadata-editor'; +import { ArtworkPanel } from './artwork-panel'; import styles from './song-edit-modal.module.css'; +import { TagFieldRow } from './tag-field-row'; import { Button } from '/@/shared/components/button/button'; import { Checkbox } from '/@/shared/components/checkbox/checkbox'; @@ -15,10 +18,6 @@ import { Tabs } from '/@/shared/components/tabs/tabs'; import { Text } from '/@/shared/components/text/text'; import { Song } from '/@/shared/types/domain-types'; -import { useMetadataEditor } from '../hooks/use-metadata-editor'; -import { ArtworkPanel } from './artwork-panel'; -import { TagFieldRow } from './tag-field-row'; - export const SongEditModal = ({ songs }: { songs: Song[] }) => { const { t } = useTranslation(); const tableContainerRef = useRef(null); diff --git a/src/renderer/features/tag-editor/components/tag-field-row.module.css b/src/renderer/features/tag-editor/components/tag-field-row.module.css index 99c01d9a4..db922c226 100644 --- a/src/renderer/features/tag-editor/components/tag-field-row.module.css +++ b/src/renderer/features/tag-editor/components/tag-field-row.module.css @@ -1,8 +1,8 @@ -.removeCell { +.remove-cell { width: 32px; } -.removeButton { +.remove-button { font-size: 1.05rem; line-height: 1; } diff --git a/src/renderer/features/tag-editor/components/tag-field-row.tsx b/src/renderer/features/tag-editor/components/tag-field-row.tsx index 105fca24e..f89b8ecb2 100644 --- a/src/renderer/features/tag-editor/components/tag-field-row.tsx +++ b/src/renderer/features/tag-editor/components/tag-field-row.tsx @@ -1,15 +1,15 @@ import { RiCloseLine } from 'react-icons/ri'; +import type { KnownTag } from '../utils/known-tags'; + +import styles from './tag-field-row.module.css'; + import { Button } from '/@/shared/components/button/button'; import { Checkbox } from '/@/shared/components/checkbox/checkbox'; import { NumberInput } from '/@/shared/components/number-input/number-input'; import { Table } from '/@/shared/components/table/table'; -import { Textarea } from '/@/shared/components/textarea/textarea'; import { TextInput } from '/@/shared/components/text-input/text-input'; - -import type { KnownTag } from '../utils/known-tags'; - -import styles from './tag-field-row.module.css'; +import { Textarea } from '/@/shared/components/textarea/textarea'; interface TagFieldRowProps { isMixed: boolean; diff --git a/src/renderer/features/tag-editor/hooks/use-metadata-editor.ts b/src/renderer/features/tag-editor/hooks/use-metadata-editor.ts index f19064b25..48099eaf2 100644 --- a/src/renderer/features/tag-editor/hooks/use-metadata-editor.ts +++ b/src/renderer/features/tag-editor/hooks/use-metadata-editor.ts @@ -4,12 +4,14 @@ import type { BatchProgress, TagEditorUtils, } from '/@/shared/types/tag-editor'; + import { closeAllModals } from '@mantine/modals'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { FIELD_PRIORITY, KNOWN_TAG_MAP, KNOWN_TAGS, type KnownTag } from '../utils/known-tags'; import { base64ToBytes, filterTagSummary, formatBatchFileErrors } from '../utils/utils'; + import { controller } from '/@/renderer/api/controller'; import { useCurrentServer } from '/@/renderer/store'; import { toast } from '/@/shared/components/toast/toast'; diff --git a/src/renderer/features/tag-editor/utils/known-tags.ts b/src/renderer/features/tag-editor/utils/known-tags.ts index 1c693d858..536dbbfda 100644 --- a/src/renderer/features/tag-editor/utils/known-tags.ts +++ b/src/renderer/features/tag-editor/utils/known-tags.ts @@ -49,15 +49,15 @@ const FIELD_TYPE_OVERRIDES: Partial< lyrics: 'textarea', }; -/** Which form control to render for a tag row. */ -export type TagFieldType = 'boolean' | 'number' | 'string' | 'textarea'; - export interface KnownTag { key: string; label: string; type: TagFieldType; } +/** Which form control to render for a tag row. */ +export type TagFieldType = 'boolean' | 'number' | 'string' | 'textarea'; + const humanizeKey = (key: string): string => key .replace(/([A-Z])/g, ' $1') diff --git a/src/shared/api/subsonic/subsonic-types.ts b/src/shared/api/subsonic/subsonic-types.ts index 85a1db4c3..bcf114ce3 100644 --- a/src/shared/api/subsonic/subsonic-types.ts +++ b/src/shared/api/subsonic/subsonic-types.ts @@ -842,6 +842,7 @@ export const ssType = { getMusicDirectory: getMusicDirectoryParameters, getPlaylist: getPlaylistParameters, getPlaylists: getPlaylistsParameters, + getScanStatus: getScanStatusParameters, getSong: getSongParameters, getSongsByGenre: getSongsByGenreParameters, getStarred: getStarredParameters, @@ -852,13 +853,12 @@ export const ssType = { reportPlayback: reportPlaybackParameters, savePlayQueueByIndex: savePlayQueueByIndexParameters, saveQueue: saveQueueParameters, - getScanStatus: getScanStatusParameters, scrobble: scrobbleParameters, search3: search3Parameters, - startScan: startScanParameters, setRating: setRatingParameters, similarSongs: similarSongsParameters, similarSongs2: similarSongs2Parameters, + startScan: startScanParameters, structuredLyrics: structuredLyricsParameters, topSongsList: topSongsListParameters, updateInternetRadioStation: updateInternetRadioStationParameters, @@ -892,6 +892,7 @@ export const ssType = { getMusicDirectory, getPlaylist, getPlaylists, + getScanStatus, getSong, getSongsByGenre, getStarred, @@ -907,15 +908,14 @@ export const ssType = { removeFavorite, reportPlayback, saveQueue, - getScanStatus, scrobble, search3, - startScan, serverInfo, setRating, similarSongs, similarSongs2, song, + startScan, structuredLyrics, topSongsList, updateInternetRadioStation, diff --git a/src/shared/types/domain-types.ts b/src/shared/types/domain-types.ts index 1c9b6f09e..3cf5d1793 100644 --- a/src/shared/types/domain-types.ts +++ b/src/shared/types/domain-types.ts @@ -1376,16 +1376,6 @@ export type ScrobbleQuery = { // Scrobble export type ScrobbleResponse = null; -export type StartScanArgs = BaseEndpointArgs; - -export type StartScanResponse = { - count: number; - folderCount: number; - lastScan?: string; - scanning: boolean; -} | null; - - export type SearchAlbumArtistsQuery = { albumArtistLimit?: number; albumArtistStartIndex?: number; @@ -1428,6 +1418,15 @@ export type SearchSongsQuery = { songStartIndex?: number; }; +export type StartScanArgs = BaseEndpointArgs; + +export type StartScanResponse = null | { + count: number; + folderCount: number; + lastScan?: string; + scanning: boolean; +}; + export type SynchronizedLyricsArray = Array<[number, string]>; export type TopSongListArgs = BaseEndpointArgs & { query: TopSongListQuery }; @@ -1541,6 +1540,7 @@ export type ControllerEndpoint = { setPlaylistSongs: (args: SetPlaylistSongsArgs) => Promise; setRating?: (args: SetRatingArgs) => Promise; shareItem?: (args: ShareItemArgs) => Promise; + startScan: (args: StartScanArgs) => Promise; updateInternetRadioStation: ( args: UpdateInternetRadioStationArgs, ) => Promise; @@ -1550,7 +1550,6 @@ export type ControllerEndpoint = { args: UploadInternetRadioStationImageArgs, ) => Promise; uploadPlaylistImage?: (args: UploadPlaylistImageArgs) => Promise; - startScan: (args: StartScanArgs) => Promise; }; export type DownloadArgs = BaseEndpointArgs & { @@ -1710,6 +1709,7 @@ export type InternalControllerEndpoint = { ) => Promise; setRating?: (args: ReplaceApiClientProps) => Promise; shareItem?: (args: ReplaceApiClientProps) => Promise; + startScan: (args: ReplaceApiClientProps) => Promise; updateInternetRadioStation: ( args: ReplaceApiClientProps, ) => Promise; @@ -1725,7 +1725,6 @@ export type InternalControllerEndpoint = { uploadPlaylistImage?: ( args: ReplaceApiClientProps, ) => Promise; - startScan: (args: ReplaceApiClientProps) => Promise; }; export type LyricGetQuery = { diff --git a/src/shared/types/tag-editor.ts b/src/shared/types/tag-editor.ts index 549cd8d6c..a622f0d94 100644 --- a/src/shared/types/tag-editor.ts +++ b/src/shared/types/tag-editor.ts @@ -48,8 +48,6 @@ export const EDITOR_FIELD_KEYS = [ 'acoustidId', ] as const; -export type EditorFieldKey = (typeof EDITOR_FIELD_KEYS)[number]; - export type ArtworkKind = 'common' | 'mixed' | 'none'; export type ArtworkOp = { bytes: Uint8Array; mimeType: string; type: 'set' } | { type: 'clear' }; @@ -64,10 +62,7 @@ export interface BatchProgress { total: number; } -interface IpcResult { - error?: string; - success: boolean; -} +export type EditorFieldKey = (typeof EDITOR_FIELD_KEYS)[number]; export interface ReadLocalImageResult extends IpcResult { data?: string; @@ -84,10 +79,6 @@ export interface ReadSongMetadataBatchResult extends IpcResult { totalCount?: number; } -export interface WriteSongTagsBatchResult extends IpcResult { - failedFiles?: BatchFileError[]; -} - /** Subset of `window.api.utils` consumed by the metadata editor. */ export interface TagEditorUtils { cancelReadSongMetadata: () => void; @@ -102,3 +93,12 @@ export interface TagEditorUtils { artworkOp?: ArtworkOp, ) => Promise; } + +export interface WriteSongTagsBatchResult extends IpcResult { + failedFiles?: BatchFileError[]; +} + +interface IpcResult { + error?: string; + success: boolean; +}