lint fixes

This commit is contained in:
ChrisScott9456
2026-06-15 10:48:18 -04:00
parent f3966e3211
commit aa12585b20
23 changed files with 149 additions and 159 deletions
+3 -7
View File
@@ -6,11 +6,7 @@ import type {
import { ipcMain, type IpcMainInvokeEvent } from 'electron'; import { ipcMain, type IpcMainInvokeEvent } from 'electron';
import { import { readFilesMetadataBatch, readLocalImageFile, writeFilesTags } from './taglib-service';
readFilesMetadataBatch,
readLocalImageFile,
writeFilesTags,
} from './taglib-service';
const sendBatchProgress = (event: IpcMainInvokeEvent, processed: number, total: number) => { const sendBatchProgress = (event: IpcMainInvokeEvent, processed: number, total: number) => {
event.sender.send('batch-progress', { processed, total }); event.sender.send('batch-progress', { processed, total });
@@ -46,8 +42,8 @@ ipcMain.handle(
artworkKind: 'none', artworkKind: 'none',
error: result.failedFiles[0]?.error ?? 'No readable audio files in selection', error: result.failedFiles[0]?.error ?? 'No readable audio files in selection',
failedFiles: result.failedFiles, failedFiles: result.failedFiles,
success: false,
readCount: result.readCount, readCount: result.readCount,
success: false,
totalCount: result.totalCount, totalCount: result.totalCount,
}; };
} }
@@ -55,8 +51,8 @@ ipcMain.handle(
return { return {
artworkKind: result.artworkKind, artworkKind: result.artworkKind,
failedFiles: result.failedFiles.length > 0 ? result.failedFiles : undefined, failedFiles: result.failedFiles.length > 0 ? result.failedFiles : undefined,
success: true,
readCount: result.readCount, readCount: result.readCount,
success: true,
tagSummary: result.tagSummary, tagSummary: result.tagSummary,
totalCount: result.totalCount, totalCount: result.totalCount,
...(result.artworkData ...(result.artworkData
@@ -1,12 +1,11 @@
import type { ArtworkKind, ArtworkOp, BatchFileError } from '/@/shared/types/tag-editor'; import type { ArtworkKind, ArtworkOp, BatchFileError } from '/@/shared/types/tag-editor';
import type { ICommonTagsResult } from 'music-metadata'; import type { ICommonTagsResult } from 'music-metadata';
import { EDITOR_FIELD_KEYS } from '/@/shared/types/tag-editor';
import { constants, promises as fsPromises } from 'fs'; import { constants, promises as fsPromises } from 'fs';
import * as mm from 'music-metadata'; import * as mm from 'music-metadata';
import { TagLib } from 'taglib-wasm'; import { TagLib } from 'taglib-wasm';
import { EDITOR_FIELD_KEYS } from '/@/shared/types/tag-editor';
import { getImageMimeTypeFromPath } from '/@/shared/utils/image-mime'; import { getImageMimeTypeFromPath } from '/@/shared/utils/image-mime';
let _taglib: null | TagLib = null; let _taglib: null | TagLib = null;
@@ -65,20 +64,6 @@ const MM_CUSTOM: Partial<Record<string, TagAccessor>> = {
trackNumber: (c) => c.track.no, 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<string, string> {
const flat: Record<string, string> = {};
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. */ /** Returns an error entry for each path that is missing or not writable by the current process. */
export async function checkPathsWritable(paths: string[]): Promise<BatchFileError[]> { export async function checkPathsWritable(paths: string[]): Promise<BatchFileError[]> {
const failed: BatchFileError[] = []; const failed: BatchFileError[] = [];
@@ -97,6 +82,20 @@ export async function checkPathsWritable(paths: string[]): Promise<BatchFileErro
return failed; return failed;
} }
/** Maps a music-metadata `ICommonTagsResult` to the flat camelCase key map used by the editor. */
export function flattenMusicMetadata(common: ICommonTagsResult): Record<string, string> {
const flat: Record<string, string> = {};
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. */ /** Runs `fn` over `items` with at most `concurrency` tasks in flight at once. Stops early if `signal` is aborted. */
const mapWithConcurrency = async <T>( const mapWithConcurrency = async <T>(
items: T[], items: T[],
@@ -130,8 +129,8 @@ export async function readFilesMetadataBatch(
artworkKind: ArtworkKind; artworkKind: ArtworkKind;
artworkMimeType?: string; artworkMimeType?: string;
failedFiles: BatchFileError[]; failedFiles: BatchFileError[];
success: boolean;
readCount: number; readCount: number;
success: boolean;
tagSummary: Record<string, null | string>; tagSummary: Record<string, null | string>;
totalCount: number; totalCount: number;
}> { }> {
@@ -216,8 +215,8 @@ export async function readFilesMetadataBatch(
return { return {
artworkKind, artworkKind,
failedFiles, failedFiles,
success: readCount > 0,
readCount, readCount,
success: readCount > 0,
tagSummary, tagSummary,
totalCount, totalCount,
...(artworkData ? { artworkData, artworkMimeType } : {}), ...(artworkData ? { artworkData, artworkMimeType } : {}),
+1 -1
View File
@@ -1,4 +1,4 @@
import { type IpcRendererEvent, ipcRenderer, webFrame } from 'electron'; import { ipcRenderer, type IpcRendererEvent, webFrame } from 'electron';
import type { import type {
ArtworkOp, ArtworkOp,
+12 -12
View File
@@ -864,6 +864,18 @@ export const controller: GeneralController = {
server.type, server.type,
)?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); )?.(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) { updateInternetRadioStation(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -924,16 +936,4 @@ export const controller: GeneralController = {
server.type, server.type,
)?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); )?.(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 } }));
},
}; };
+10 -10
View File
@@ -365,6 +365,16 @@ export const contract = c.router({
400: jfType._response.error, 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: { updatePlaylist: {
body: jfType._parameters.updatePlaylist, body: jfType._parameters.updatePlaylist,
method: 'POST', method: 'POST',
@@ -392,16 +402,6 @@ export const contract = c.router({
400: jfType._response.error, 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({}); const axiosClient = axios.create({});
@@ -1931,6 +1931,20 @@ export const JellyfinController: InternalControllerEndpoint = {
return null; 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) => { updateInternetRadioStation: async (args) => {
const { apiClientProps, body, query } = args; const { apiClientProps, body, query } = args;
@@ -1996,20 +2010,6 @@ export const JellyfinController: InternalControllerEndpoint = {
'Failed to upload playlist image', '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[]) { function getLibraryId(musicFolderId?: string | string[]) {
@@ -1261,6 +1261,7 @@ export const NavidromeController: InternalControllerEndpoint = {
id: res.body.data.id, id: res.body.data.id,
}; };
}, },
startScan: SubsonicController.startScan,
updateInternetRadioStation: async (args) => { updateInternetRadioStation: async (args) => {
const { apiClientProps, body, query } = args; const { apiClientProps, body, query } = args;
@@ -1411,5 +1412,4 @@ export const NavidromeController: InternalControllerEndpoint = {
return res.data?.status === 'ok'; return res.data?.status === 'ok';
}, },
startScan: SubsonicController.startScan,
}; };
+16 -16
View File
@@ -186,6 +186,14 @@ export const contract = c.router({
200: ssType._response.randomSongList, 200: ssType._response.randomSongList,
}, },
}, },
getScanStatus: {
method: 'GET',
path: 'getScanStatus.view',
query: ssType._parameters.getScanStatus,
responses: {
200: ssType._response.getScanStatus,
},
},
getServerInfo: { getServerInfo: {
method: 'GET', method: 'GET',
path: 'getOpenSubsonicExtensions.view', path: 'getOpenSubsonicExtensions.view',
@@ -337,6 +345,14 @@ export const contract = c.router({
200: ssType._response.setRating, 200: ssType._response.setRating,
}, },
}, },
startScan: {
method: 'GET',
path: 'startScan.view',
query: ssType._parameters.startScan,
responses: {
200: ssType._response.startScan,
},
},
updateInternetRadioStation: { updateInternetRadioStation: {
method: 'GET', method: 'GET',
path: 'updateInternetRadioStation.view', path: 'updateInternetRadioStation.view',
@@ -353,22 +369,6 @@ export const contract = c.router({
200: ssType._response.baseResponse, 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({}); const axiosClient = axios.create({});
@@ -2461,6 +2461,17 @@ export const SubsonicController: InternalControllerEndpoint = {
return null; 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) => { updateInternetRadioStation: async (args) => {
const { apiClientProps, body, query } = args; const { apiClientProps, body, query } = args;
@@ -2497,17 +2508,6 @@ export const SubsonicController: InternalControllerEndpoint = {
return null; 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[]) { function getLibraryId(musicFolderId?: string | string[]) {
@@ -1,13 +1,13 @@
import { openModal } from '@mantine/modals';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
import { useCallback } from 'react'; import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { openModal } from '@mantine/modals';
import { controller } from '/@/renderer/api/controller'; import { controller } from '/@/renderer/api/controller';
import { SongEditModal } from '/@/renderer/features/tag-editor/components/song-edit-modal';
import { useCurrentServer } from '/@/renderer/store'; import { useCurrentServer } from '/@/renderer/store';
import { ContextMenu } from '/@/shared/components/context-menu/context-menu'; import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
import { Song } from '/@/shared/types/domain-types'; import { Song } from '/@/shared/types/domain-types';
import { SongEditModal } from '/@/renderer/features/tag-editor/components/song-edit-modal';
interface EditMetadataActionProps { interface EditMetadataActionProps {
albumIds?: string[]; albumIds?: string[];
@@ -31,7 +31,7 @@ const getAlbumSongs = async (albumIds: string[], serverId: string): Promise<Song
export const EditMetadataAction = ({ albumIds, songs: songItems }: EditMetadataActionProps) => { export const EditMetadataAction = ({ albumIds, songs: songItems }: EditMetadataActionProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const server = useCurrentServer(); 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 count = albumIds?.length ?? songs.length;
const onSelect = useCallback(async () => { const onSelect = useCallback(async () => {
@@ -1,8 +1,8 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { AddToPlaylistAction } from '/@/renderer/features/context-menu/actions/add-to-playlist-action'; 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 { 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 { GetInfoAction } from '/@/renderer/features/context-menu/actions/get-info-action';
import { GoToAction } from '/@/renderer/features/context-menu/actions/go-to-action'; import { GoToAction } from '/@/renderer/features/context-menu/actions/go-to-action';
import { PlayAction } from '/@/renderer/features/context-menu/actions/play-action'; import { PlayAction } from '/@/renderer/features/context-menu/actions/play-action';
@@ -1,8 +1,8 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { AddToPlaylistAction } from '/@/renderer/features/context-menu/actions/add-to-playlist-action'; 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 { 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 { GetInfoAction } from '/@/renderer/features/context-menu/actions/get-info-action';
import { GoToAction } from '/@/renderer/features/context-menu/actions/go-to-action'; import { GoToAction } from '/@/renderer/features/context-menu/actions/go-to-action';
import { PlayAction } from '/@/renderer/features/context-menu/actions/play-action'; import { PlayAction } from '/@/renderer/features/context-menu/actions/play-action';
@@ -1,15 +1,15 @@
.artworkBox { .artwork-box {
aspect-ratio: 1 / 1;
width: 100%; width: 100%;
aspect-ratio: 1 / 1;
container-type: inline-size;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
background: var(--mantine-color-dark-6); background: var(--mantine-color-dark-6);
border: 1px solid var(--mantine-color-default-border); border: 1px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-sm); border-radius: var(--mantine-radius-sm);
container-type: inline-size;
} }
.artworkImage { .artwork-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
@@ -20,10 +20,10 @@
opacity: 0.4; opacity: 0.4;
} }
.placeholderText { .placeholder-text {
font-size: 5cqw; font-size: 5cqw;
} }
.removeButton { .remove-button {
color: var(--theme-colors-state-error); color: var(--theme-colors-state-error);
} }
@@ -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 { 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 { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text'; import { Text } from '/@/shared/components/text/text';
import styles from './artwork-panel.module.css';
interface ArtworkPanelProps { interface ArtworkPanelProps {
artworkDisplayUrl: null | string; artworkDisplayUrl: null | string;
artworkIsMixed: boolean; artworkIsMixed: boolean;
@@ -49,12 +49,7 @@ export const ArtworkPanel = ({
)} )}
</DragDropZone> </DragDropZone>
{showRemoveButton && ( {showRemoveButton && (
<Button <Button className={styles.removeButton} onClick={onRemove} size="sm" variant="subtle">
className={styles.removeButton}
onClick={onRemove}
size="sm"
variant="subtle"
>
{removeArtworkLabel} {removeArtworkLabel}
</Button> </Button>
)} )}
@@ -1,26 +1,26 @@
.tabLabel { .tab-label {
font-size: 1rem; font-size: 1rem;
} }
.tableScroller { .table-scroller {
max-height: 55vh; max-height: 55vh;
overflow-y: auto;
margin-top: var(--theme-spacing-sm);
padding-top: var(--theme-spacing-sm); padding-top: var(--theme-spacing-sm);
padding-bottom: 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); border-top: 1px solid var(--mantine-color-default-border);
} }
.tableCell { .table-cell {
padding: var(--theme-spacing-xs) var(--theme-spacing-sm); padding: var(--theme-spacing-xs) var(--theme-spacing-sm);
vertical-align: middle; vertical-align: middle;
} }
.tableHeader { .table-header {
width: 1%; width: 1%;
color: var(--theme-colors-foreground-muted);
font-weight: 500;
padding: var(--theme-spacing-xs) var(--theme-spacing-sm); padding: var(--theme-spacing-xs) var(--theme-spacing-sm);
font-weight: 500;
vertical-align: middle; vertical-align: middle;
color: var(--theme-colors-foreground-muted);
white-space: nowrap; white-space: nowrap;
} }
@@ -2,7 +2,10 @@ import { closeAllModals } from '@mantine/modals';
import { useRef } from 'react'; import { useRef } from 'react';
import { useTranslation } from 'react-i18next'; 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 styles from './song-edit-modal.module.css';
import { TagFieldRow } from './tag-field-row';
import { Button } from '/@/shared/components/button/button'; import { Button } from '/@/shared/components/button/button';
import { Checkbox } from '/@/shared/components/checkbox/checkbox'; 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 { Text } from '/@/shared/components/text/text';
import { Song } from '/@/shared/types/domain-types'; 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[] }) => { export const SongEditModal = ({ songs }: { songs: Song[] }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const tableContainerRef = useRef<HTMLDivElement>(null); const tableContainerRef = useRef<HTMLDivElement>(null);
@@ -1,8 +1,8 @@
.removeCell { .remove-cell {
width: 32px; width: 32px;
} }
.removeButton { .remove-button {
font-size: 1.05rem; font-size: 1.05rem;
line-height: 1; line-height: 1;
} }
@@ -1,15 +1,15 @@
import { RiCloseLine } from 'react-icons/ri'; 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 { Button } from '/@/shared/components/button/button';
import { Checkbox } from '/@/shared/components/checkbox/checkbox'; import { Checkbox } from '/@/shared/components/checkbox/checkbox';
import { NumberInput } from '/@/shared/components/number-input/number-input'; import { NumberInput } from '/@/shared/components/number-input/number-input';
import { Table } from '/@/shared/components/table/table'; import { Table } from '/@/shared/components/table/table';
import { Textarea } from '/@/shared/components/textarea/textarea';
import { TextInput } from '/@/shared/components/text-input/text-input'; import { TextInput } from '/@/shared/components/text-input/text-input';
import { Textarea } from '/@/shared/components/textarea/textarea';
import type { KnownTag } from '../utils/known-tags';
import styles from './tag-field-row.module.css';
interface TagFieldRowProps { interface TagFieldRowProps {
isMixed: boolean; isMixed: boolean;
@@ -4,12 +4,14 @@ import type {
BatchProgress, BatchProgress,
TagEditorUtils, TagEditorUtils,
} from '/@/shared/types/tag-editor'; } from '/@/shared/types/tag-editor';
import { closeAllModals } from '@mantine/modals'; import { closeAllModals } from '@mantine/modals';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FIELD_PRIORITY, KNOWN_TAG_MAP, KNOWN_TAGS, type KnownTag } from '../utils/known-tags'; import { FIELD_PRIORITY, KNOWN_TAG_MAP, KNOWN_TAGS, type KnownTag } from '../utils/known-tags';
import { base64ToBytes, filterTagSummary, formatBatchFileErrors } from '../utils/utils'; import { base64ToBytes, filterTagSummary, formatBatchFileErrors } from '../utils/utils';
import { controller } from '/@/renderer/api/controller'; import { controller } from '/@/renderer/api/controller';
import { useCurrentServer } from '/@/renderer/store'; import { useCurrentServer } from '/@/renderer/store';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
@@ -49,15 +49,15 @@ const FIELD_TYPE_OVERRIDES: Partial<
lyrics: 'textarea', lyrics: 'textarea',
}; };
/** Which form control to render for a tag row. */
export type TagFieldType = 'boolean' | 'number' | 'string' | 'textarea';
export interface KnownTag { export interface KnownTag {
key: string; key: string;
label: string; label: string;
type: TagFieldType; type: TagFieldType;
} }
/** Which form control to render for a tag row. */
export type TagFieldType = 'boolean' | 'number' | 'string' | 'textarea';
const humanizeKey = (key: string): string => const humanizeKey = (key: string): string =>
key key
.replace(/([A-Z])/g, ' $1') .replace(/([A-Z])/g, ' $1')
+4 -4
View File
@@ -842,6 +842,7 @@ export const ssType = {
getMusicDirectory: getMusicDirectoryParameters, getMusicDirectory: getMusicDirectoryParameters,
getPlaylist: getPlaylistParameters, getPlaylist: getPlaylistParameters,
getPlaylists: getPlaylistsParameters, getPlaylists: getPlaylistsParameters,
getScanStatus: getScanStatusParameters,
getSong: getSongParameters, getSong: getSongParameters,
getSongsByGenre: getSongsByGenreParameters, getSongsByGenre: getSongsByGenreParameters,
getStarred: getStarredParameters, getStarred: getStarredParameters,
@@ -852,13 +853,12 @@ export const ssType = {
reportPlayback: reportPlaybackParameters, reportPlayback: reportPlaybackParameters,
savePlayQueueByIndex: savePlayQueueByIndexParameters, savePlayQueueByIndex: savePlayQueueByIndexParameters,
saveQueue: saveQueueParameters, saveQueue: saveQueueParameters,
getScanStatus: getScanStatusParameters,
scrobble: scrobbleParameters, scrobble: scrobbleParameters,
search3: search3Parameters, search3: search3Parameters,
startScan: startScanParameters,
setRating: setRatingParameters, setRating: setRatingParameters,
similarSongs: similarSongsParameters, similarSongs: similarSongsParameters,
similarSongs2: similarSongs2Parameters, similarSongs2: similarSongs2Parameters,
startScan: startScanParameters,
structuredLyrics: structuredLyricsParameters, structuredLyrics: structuredLyricsParameters,
topSongsList: topSongsListParameters, topSongsList: topSongsListParameters,
updateInternetRadioStation: updateInternetRadioStationParameters, updateInternetRadioStation: updateInternetRadioStationParameters,
@@ -892,6 +892,7 @@ export const ssType = {
getMusicDirectory, getMusicDirectory,
getPlaylist, getPlaylist,
getPlaylists, getPlaylists,
getScanStatus,
getSong, getSong,
getSongsByGenre, getSongsByGenre,
getStarred, getStarred,
@@ -907,15 +908,14 @@ export const ssType = {
removeFavorite, removeFavorite,
reportPlayback, reportPlayback,
saveQueue, saveQueue,
getScanStatus,
scrobble, scrobble,
search3, search3,
startScan,
serverInfo, serverInfo,
setRating, setRating,
similarSongs, similarSongs,
similarSongs2, similarSongs2,
song, song,
startScan,
structuredLyrics, structuredLyrics,
topSongsList, topSongsList,
updateInternetRadioStation, updateInternetRadioStation,
+11 -12
View File
@@ -1376,16 +1376,6 @@ export type ScrobbleQuery = {
// Scrobble // Scrobble
export type ScrobbleResponse = null; export type ScrobbleResponse = null;
export type StartScanArgs = BaseEndpointArgs;
export type StartScanResponse = {
count: number;
folderCount: number;
lastScan?: string;
scanning: boolean;
} | null;
export type SearchAlbumArtistsQuery = { export type SearchAlbumArtistsQuery = {
albumArtistLimit?: number; albumArtistLimit?: number;
albumArtistStartIndex?: number; albumArtistStartIndex?: number;
@@ -1428,6 +1418,15 @@ export type SearchSongsQuery = {
songStartIndex?: number; 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 SynchronizedLyricsArray = Array<[number, string]>;
export type TopSongListArgs = BaseEndpointArgs & { query: TopSongListQuery }; export type TopSongListArgs = BaseEndpointArgs & { query: TopSongListQuery };
@@ -1541,6 +1540,7 @@ export type ControllerEndpoint = {
setPlaylistSongs: (args: SetPlaylistSongsArgs) => Promise<SetPlaylistSongsResponse>; setPlaylistSongs: (args: SetPlaylistSongsArgs) => Promise<SetPlaylistSongsResponse>;
setRating?: (args: SetRatingArgs) => Promise<RatingResponse>; setRating?: (args: SetRatingArgs) => Promise<RatingResponse>;
shareItem?: (args: ShareItemArgs) => Promise<ShareItemResponse>; shareItem?: (args: ShareItemArgs) => Promise<ShareItemResponse>;
startScan: (args: StartScanArgs) => Promise<StartScanResponse>;
updateInternetRadioStation: ( updateInternetRadioStation: (
args: UpdateInternetRadioStationArgs, args: UpdateInternetRadioStationArgs,
) => Promise<UpdateInternetRadioStationResponse>; ) => Promise<UpdateInternetRadioStationResponse>;
@@ -1550,7 +1550,6 @@ export type ControllerEndpoint = {
args: UploadInternetRadioStationImageArgs, args: UploadInternetRadioStationImageArgs,
) => Promise<UploadInternetRadioStationImageResponse>; ) => Promise<UploadInternetRadioStationImageResponse>;
uploadPlaylistImage?: (args: UploadPlaylistImageArgs) => Promise<UploadPlaylistImageResponse>; uploadPlaylistImage?: (args: UploadPlaylistImageArgs) => Promise<UploadPlaylistImageResponse>;
startScan: (args: StartScanArgs) => Promise<StartScanResponse>;
}; };
export type DownloadArgs = BaseEndpointArgs & { export type DownloadArgs = BaseEndpointArgs & {
@@ -1710,6 +1709,7 @@ export type InternalControllerEndpoint = {
) => Promise<SetPlaylistSongsResponse>; ) => Promise<SetPlaylistSongsResponse>;
setRating?: (args: ReplaceApiClientProps<SetRatingArgs>) => Promise<RatingResponse>; setRating?: (args: ReplaceApiClientProps<SetRatingArgs>) => Promise<RatingResponse>;
shareItem?: (args: ReplaceApiClientProps<ShareItemArgs>) => Promise<ShareItemResponse>; shareItem?: (args: ReplaceApiClientProps<ShareItemArgs>) => Promise<ShareItemResponse>;
startScan: (args: ReplaceApiClientProps<StartScanArgs>) => Promise<StartScanResponse>;
updateInternetRadioStation: ( updateInternetRadioStation: (
args: ReplaceApiClientProps<UpdateInternetRadioStationArgs>, args: ReplaceApiClientProps<UpdateInternetRadioStationArgs>,
) => Promise<UpdateInternetRadioStationResponse>; ) => Promise<UpdateInternetRadioStationResponse>;
@@ -1725,7 +1725,6 @@ export type InternalControllerEndpoint = {
uploadPlaylistImage?: ( uploadPlaylistImage?: (
args: ReplaceApiClientProps<UploadPlaylistImageArgs>, args: ReplaceApiClientProps<UploadPlaylistImageArgs>,
) => Promise<UploadPlaylistImageResponse>; ) => Promise<UploadPlaylistImageResponse>;
startScan: (args: ReplaceApiClientProps<StartScanArgs>) => Promise<StartScanResponse>;
}; };
export type LyricGetQuery = { export type LyricGetQuery = {
+10 -10
View File
@@ -48,8 +48,6 @@ export const EDITOR_FIELD_KEYS = [
'acoustidId', 'acoustidId',
] as const; ] as const;
export type EditorFieldKey = (typeof EDITOR_FIELD_KEYS)[number];
export type ArtworkKind = 'common' | 'mixed' | 'none'; export type ArtworkKind = 'common' | 'mixed' | 'none';
export type ArtworkOp = { bytes: Uint8Array; mimeType: string; type: 'set' } | { type: 'clear' }; export type ArtworkOp = { bytes: Uint8Array; mimeType: string; type: 'set' } | { type: 'clear' };
@@ -64,10 +62,7 @@ export interface BatchProgress {
total: number; total: number;
} }
interface IpcResult { export type EditorFieldKey = (typeof EDITOR_FIELD_KEYS)[number];
error?: string;
success: boolean;
}
export interface ReadLocalImageResult extends IpcResult { export interface ReadLocalImageResult extends IpcResult {
data?: string; data?: string;
@@ -84,10 +79,6 @@ export interface ReadSongMetadataBatchResult extends IpcResult {
totalCount?: number; totalCount?: number;
} }
export interface WriteSongTagsBatchResult extends IpcResult {
failedFiles?: BatchFileError[];
}
/** Subset of `window.api.utils` consumed by the metadata editor. */ /** Subset of `window.api.utils` consumed by the metadata editor. */
export interface TagEditorUtils { export interface TagEditorUtils {
cancelReadSongMetadata: () => void; cancelReadSongMetadata: () => void;
@@ -102,3 +93,12 @@ export interface TagEditorUtils {
artworkOp?: ArtworkOp, artworkOp?: ArtworkOp,
) => Promise<WriteSongTagsBatchResult>; ) => Promise<WriteSongTagsBatchResult>;
} }
export interface WriteSongTagsBatchResult extends IpcResult {
failedFiles?: BatchFileError[];
}
interface IpcResult {
error?: string;
success: boolean;
}