From 030ecca898649ab16aeda3d2ac0f4a27468f6917 Mon Sep 17 00:00:00 2001 From: ChrisScott9456 Date: Sun, 12 Jul 2026 14:04:53 -0400 Subject: [PATCH] more lint fixes --- .../core/tag-editor/taglib-service.ts | 18 +++++------ src/renderer/api/controller.ts | 24 +++++++-------- src/renderer/api/jellyfin/jellyfin-api.ts | 24 +++++++-------- .../api/jellyfin/jellyfin-controller.ts | 30 +++++++++---------- .../api/navidrome/navidrome-controller.ts | 2 +- .../api/subsonic/subsonic-controller.ts | 22 +++++++------- .../components/song-edit-context-modal.tsx | 2 +- .../tag-editor/components/song-edit-modal.tsx | 16 +++++++--- .../features/tag-editor/utils/known-tags.ts | 20 ++++++------- src/shared/types/domain-types.ts | 12 ++++---- 10 files changed, 88 insertions(+), 82 deletions(-) diff --git a/src/main/features/core/tag-editor/taglib-service.ts b/src/main/features/core/tag-editor/taglib-service.ts index 70565b699..46b78410c 100644 --- a/src/main/features/core/tag-editor/taglib-service.ts +++ b/src/main/features/core/tag-editor/taglib-service.ts @@ -14,15 +14,6 @@ const getTagLib = async (): Promise => { const BATCH_CONCURRENCY = 8; -/** Reads an image file and returns it as base64 + MIME type for IPC transport to the renderer. */ -export async function readLocalImageFile(filePath: string) { - const buf = await fsPromises.readFile(filePath); - return { - data: buf.toString('base64'), - mimeType: getImageMimeTypeFromPath(filePath), - }; -} - /** 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[] = []; @@ -41,6 +32,15 @@ export async function checkPathsWritable(paths: string[]): Promise): Record { const flat: Record = {}; diff --git a/src/renderer/api/controller.ts b/src/renderer/api/controller.ts index 1117ba891..9027a270e 100644 --- a/src/renderer/api/controller.ts +++ b/src/renderer/api/controller.ts @@ -786,6 +786,18 @@ export const controller: GeneralController = { server.type, )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); }, + refreshItems(args) { + const server = getServerById(args.apiClientProps.serverId); + + if (!server) { + throw new Error(`${i18n.t('error.apiRouteError')}: refreshItems`); + } + + return apiController( + 'refreshItems', + server.type, + )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); + }, removeFromPlaylist(args) { const server = getServerById(args.apiClientProps.serverId); @@ -888,18 +900,6 @@ export const controller: GeneralController = { server.type, )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); }, - refreshItems(args) { - const server = getServerById(args.apiClientProps.serverId); - - if (!server) { - throw new Error(`${i18n.t('error.apiRouteError')}: refreshItems`); - } - - return apiController( - 'refreshItems', - server.type, - )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } })); - }, updateInternetRadioStation(args) { const server = getServerById(args.apiClientProps.serverId); diff --git a/src/renderer/api/jellyfin/jellyfin-api.ts b/src/renderer/api/jellyfin/jellyfin-api.ts index e2f0797ff..921a1843e 100644 --- a/src/renderer/api/jellyfin/jellyfin-api.ts +++ b/src/renderer/api/jellyfin/jellyfin-api.ts @@ -301,6 +301,18 @@ export const contract = c.router({ 400: jfType._response.error, }, }, + refreshItem: { + body: z.null(), + method: 'POST', + path: 'Items/:id/Refresh', + query: z.object({ + MetadataRefreshMode: z.string().optional(), + }), + responses: { + 204: z.null(), + 400: jfType._response.error, + }, + }, removeFavorite: { body: jfType._parameters.favorite, method: 'DELETE', @@ -365,18 +377,6 @@ export const contract = c.router({ 400: jfType._response.error, }, }, - refreshItem: { - body: z.null(), - method: 'POST', - path: 'Items/:id/Refresh', - query: z.object({ - MetadataRefreshMode: z.string().optional(), - }), - responses: { - 204: z.null(), - 400: jfType._response.error, - }, - }, updatePlaylist: { body: jfType._parameters.updatePlaylist, method: 'POST', diff --git a/src/renderer/api/jellyfin/jellyfin-controller.ts b/src/renderer/api/jellyfin/jellyfin-controller.ts index 8268974d1..3a7a5e998 100644 --- a/src/renderer/api/jellyfin/jellyfin-controller.ts +++ b/src/renderer/api/jellyfin/jellyfin-controller.ts @@ -1556,6 +1556,21 @@ export const JellyfinController: InternalControllerEndpoint = { throw new Error('Failed to move item in playlist'); } }, + refreshItems: async (args) => { + const { apiClientProps, query } = args; + + await Promise.all( + query.ids.map((id) => + jfApiClient(apiClientProps).refreshItem({ + body: null, + params: { id }, + query: { MetadataRefreshMode: 'FullRefresh' }, + }), + ), + ); + + return null; + }, removeFromPlaylist: async (args) => { const { apiClientProps, query } = args; @@ -1874,21 +1889,6 @@ export const JellyfinController: InternalControllerEndpoint = { return null; }, - refreshItems: async (args) => { - const { apiClientProps, query } = args; - - await Promise.all( - query.ids.map((id) => - jfApiClient(apiClientProps).refreshItem({ - body: null, - params: { id }, - query: { MetadataRefreshMode: 'FullRefresh' }, - }), - ), - ); - - return null; - }, updateInternetRadioStation: async (args) => { const { apiClientProps, body, query } = args; diff --git a/src/renderer/api/navidrome/navidrome-controller.ts b/src/renderer/api/navidrome/navidrome-controller.ts index b33511ad9..dd76d74ce 100644 --- a/src/renderer/api/navidrome/navidrome-controller.ts +++ b/src/renderer/api/navidrome/navidrome-controller.ts @@ -1027,6 +1027,7 @@ export const NavidromeController: InternalControllerEndpoint = { throw new Error('Failed to move item in playlist'); } }, + refreshItems: SubsonicController.refreshItems, removeFromPlaylist: async (args) => { const { apiClientProps, query } = args; @@ -1185,7 +1186,6 @@ export const NavidromeController: InternalControllerEndpoint = { id: res.body.data.id, }; }, - refreshItems: SubsonicController.refreshItems, updateInternetRadioStation: async (args) => { const { apiClientProps, body, query } = args; diff --git a/src/renderer/api/subsonic/subsonic-controller.ts b/src/renderer/api/subsonic/subsonic-controller.ts index fcb26b5dc..51e2c4744 100644 --- a/src/renderer/api/subsonic/subsonic-controller.ts +++ b/src/renderer/api/subsonic/subsonic-controller.ts @@ -2082,6 +2082,17 @@ export const SubsonicController: InternalControllerEndpoint = { return res.body; }, + refreshItems: async (args) => { + const { apiClientProps } = args; + + const res = await ssApiClient(apiClientProps).startScan({ query: {} }); + + if (res.status !== 200) { + throw new Error('Failed to start scan'); + } + + return null; + }, removeFromPlaylist: async ({ apiClientProps, query }) => { const res = await ssApiClient(apiClientProps).updatePlaylist({ query: { @@ -2356,17 +2367,6 @@ export const SubsonicController: InternalControllerEndpoint = { return null; }, - refreshItems: async (args) => { - const { apiClientProps } = args; - - const res = await ssApiClient(apiClientProps).startScan({ query: {} }); - - if (res.status !== 200) { - throw new Error('Failed to start scan'); - } - - return null; - }, updateInternetRadioStation: async (args) => { const { apiClientProps, body, query } = args; diff --git a/src/renderer/features/tag-editor/components/song-edit-context-modal.tsx b/src/renderer/features/tag-editor/components/song-edit-context-modal.tsx index ef0e76056..2f5a6d89e 100644 --- a/src/renderer/features/tag-editor/components/song-edit-context-modal.tsx +++ b/src/renderer/features/tag-editor/components/song-edit-context-modal.tsx @@ -15,7 +15,7 @@ type SongEditInnerProps = { export const SongEditContextModal = ({ innerProps }: ContextModalProps) => { const server = useCurrentServer(); - const [resolvedSongs, setResolvedSongs] = useState(null); + const [resolvedSongs, setResolvedSongs] = useState(null); useEffect(() => { if (innerProps.albumIds) { 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 309a808ce..aeff3f773 100644 --- a/src/renderer/features/tag-editor/components/song-edit-modal.tsx +++ b/src/renderer/features/tag-editor/components/song-edit-modal.tsx @@ -1,8 +1,8 @@ import { Autocomplete } from '@mantine/core'; import { closeAllModals } from '@mantine/modals'; import { useRef, useState } from 'react'; -import { RiAddLine } from 'react-icons/ri'; import { useTranslation } from 'react-i18next'; +import { RiAddLine } from 'react-icons/ri'; import { useMetadataEditor } from '../hooks/use-metadata-editor'; import { KNOWN_TAG_MAP, resolveTagKey } from '../utils/known-tags'; @@ -40,14 +40,17 @@ export const SongEditModal = ({ songs }: { songs: Song[] }) => { trimmedInput && !KNOWN_TAG_MAP.has(trimmedInput) ? trimmedInput.includes('=') ? "Tag key cannot contain '='" - : /[^\x00-\x7F]/.test(trimmedInput) + : // eslint-disable-next-line no-control-regex + /[^\x00-\x7F]/.test(trimmedInput) ? 'Tag key must use ASCII characters only' : null : null; const resolvedInputKey = trimmedInput ? resolveTagKey(trimmedInput) : ''; const duplicateError = - trimmedInput && !customKeyError && editor.sortedFieldEntries.some(([k]) => k === resolvedInputKey) + trimmedInput && + !customKeyError && + editor.sortedFieldEntries.some(([k]) => k === resolvedInputKey) ? 'Field already exists' : null; const fieldError = customKeyError ?? (duplicateAttempted ? duplicateError : null); @@ -55,7 +58,12 @@ export const SongEditModal = ({ songs }: { songs: Song[] }) => { const handleAddField = (key: string): boolean => { const trimmed = key.trim(); if (!trimmed) return false; - if (!KNOWN_TAG_MAP.has(trimmed) && (trimmed.includes('=') || /[^\x00-\x7F]/.test(trimmed))) return false; + if ( + !KNOWN_TAG_MAP.has(trimmed) && + (trimmed.includes('=') || // eslint-disable-next-line no-control-regex + /[^\x00-\x7F]/.test(trimmed)) + ) + return false; const normalizedKey = resolveTagKey(trimmed); if (editor.sortedFieldEntries.some(([k]) => k === normalizedKey)) { setDuplicateAttempted(true); diff --git a/src/renderer/features/tag-editor/utils/known-tags.ts b/src/renderer/features/tag-editor/utils/known-tags.ts index 5cab8d480..382b91a4d 100644 --- a/src/renderer/features/tag-editor/utils/known-tags.ts +++ b/src/renderer/features/tag-editor/utils/known-tags.ts @@ -25,14 +25,20 @@ export type TagFieldType = 'boolean' | 'number' | 'string' | 'textarea'; * label/type overridden; keys absent from PROPERTIES are appended as extra entries. */ const TAG_CONFIG: Record = { + acoustidFingerprint: { type: 'textarea' }, acoustidId: { label: 'AcoustID' }, albumArtist: { label: 'Album Artist' }, albumArtistSort: { label: 'Album Artist Sort' }, albumSort: { label: 'Album Sort' }, + // extras not in PROPERTIES (common MusicBrainz Picard tags) + ARTISTS: { label: 'Artists', type: 'string' }, artistSort: { label: 'Artist Sort' }, + bpm: { type: 'number' }, catalogNumber: { label: 'Catalog Number' }, + comment: { type: 'textarea' }, composerSort: { label: 'Composer Sort' }, discNumber: { label: 'Disc Number' }, + lyrics: { type: 'textarea' }, musicbrainzArtistId: { label: 'MusicBrainz Artist ID' }, musicbrainzReleaseArtistId: { label: 'MusicBrainz Album Artist ID' }, musicbrainzReleaseGroupId: { label: 'MusicBrainz Release Group ID' }, @@ -43,21 +49,15 @@ const TAG_CONFIG: Record = { originalAlbum: { label: 'Original Album' }, originalArtist: { label: 'Original Artist' }, originalDate: { label: 'Original Date' }, + ORIGINALYEAR: { label: 'Original Year', type: 'number' }, + RELEASECOUNTRY: { label: 'Release Country', type: 'string' }, + RELEASESTATUS: { label: 'Release Status', type: 'string' }, + RELEASETYPE: { label: 'Release Type', type: 'string' }, remixedBy: { label: 'Remixer' }, titleSort: { label: 'Title Sort' }, totalDiscs: { label: 'Total Discs' }, totalTracks: { label: 'Total Tracks' }, trackNumber: { label: 'Track Number' }, - acoustidFingerprint: { type: 'textarea' }, - bpm: { type: 'number' }, - comment: { type: 'textarea' }, - lyrics: { type: 'textarea' }, - // extras not in PROPERTIES (common MusicBrainz Picard tags) - ARTISTS: { label: 'Artists', type: 'string' }, - ORIGINALYEAR: { label: 'Original Year', type: 'number' }, - RELEASECOUNTRY: { label: 'Release Country', type: 'string' }, - RELEASESTATUS: { label: 'Release Status', type: 'string' }, - RELEASETYPE: { label: 'Release Type', type: 'string' }, }; const humanizeKey = (key: string): string => diff --git a/src/shared/types/domain-types.ts b/src/shared/types/domain-types.ts index c6638a076..f9d275a9f 100644 --- a/src/shared/types/domain-types.ts +++ b/src/shared/types/domain-types.ts @@ -1384,6 +1384,10 @@ export type RandomSongListQuery = { export type RandomSongListResponse = SongListResponse; +export type RefreshItemsArgs = BaseEndpointArgs & { query: { ids: string[] } }; + +export type RefreshItemsResponse = null; + export type ScrobbleArgs = BaseEndpointArgs & { query: ScrobbleQuery; }; @@ -1443,10 +1447,6 @@ export type SearchSongsQuery = { songStartIndex?: number; }; -export type RefreshItemsArgs = BaseEndpointArgs & { query: { ids: string[] } }; - -export type RefreshItemsResponse = null; - export type SyncedCueLine = { agentId?: string; endMs: number; @@ -1750,9 +1750,7 @@ export type InternalControllerEndpoint = { args: ReplaceApiClientProps, ) => Promise; movePlaylistItem?: (args: ReplaceApiClientProps) => Promise; - refreshItems: ( - args: ReplaceApiClientProps, - ) => Promise; + refreshItems: (args: ReplaceApiClientProps) => Promise; removeFromPlaylist: ( args: ReplaceApiClientProps, ) => Promise;