Merge pull request 'ux/qmcv2-keys' (#39) from ux/qmcv2-keys into main
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #39
This commit is contained in:
commit
8f09882470
@ -5,7 +5,7 @@ name: default
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: test & build
|
- name: test & build
|
||||||
image: node:18.16.0-bullseye
|
image: node:18.16.1-bookworm
|
||||||
commands:
|
commands:
|
||||||
# - git config --global --add safe.directory "/drone/src"
|
# - git config --global --add safe.directory "/drone/src"
|
||||||
- corepack enable
|
- corepack enable
|
||||||
@ -17,7 +17,7 @@ steps:
|
|||||||
npm_config_registry: https://registry.npmmirror.com
|
npm_config_registry: https://registry.npmmirror.com
|
||||||
|
|
||||||
- name: publish
|
- name: publish
|
||||||
image: node:18.16.0-bullseye
|
image: node:18.16.1-bookworm
|
||||||
environment:
|
environment:
|
||||||
DRONE_GITEA_SERVER: https://git.unlock-music.dev
|
DRONE_GITEA_SERVER: https://git.unlock-music.dev
|
||||||
GITEA_API_KEY:
|
GITEA_API_KEY:
|
||||||
|
@ -28,7 +28,7 @@ export function ImportSecretModal({ clientName, children, show, onClose, onImpor
|
|||||||
<Modal isOpen={show} onClose={onClose} closeOnOverlayClick={false} scrollBehavior="inside" size="xl">
|
<Modal isOpen={show} onClose={onClose} closeOnOverlayClick={false} scrollBehavior="inside" size="xl">
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>导入密钥数据库</ModalHeader>
|
<ModalHeader>从文件导入密钥</ModalHeader>
|
||||||
<ModalCloseButton />
|
<ModalCloseButton />
|
||||||
<Flex as={ModalBody} gap={2} flexDir="column" flex={1}>
|
<Flex as={ModalBody} gap={2} flexDir="column" flex={1}>
|
||||||
<Center>
|
<Center>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
chakra,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
@ -20,13 +21,15 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
VStack,
|
VStack,
|
||||||
useBreakpointValue,
|
useBreakpointValue,
|
||||||
|
useToast,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { PanelQMCv2Key } from './panels/PanelQMCv2Key';
|
import { PanelQMCv2Key } from './panels/PanelQMCv2Key';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { MdExpandMore, MdMenu, MdOutlineSettingsBackupRestore } from 'react-icons/md';
|
import { MdExpandMore, MdMenu, MdOutlineSettingsBackupRestore } from 'react-icons/md';
|
||||||
import { useAppDispatch } from '~/hooks';
|
import { useAppDispatch, useAppSelector } from '~/hooks';
|
||||||
import { commitStagingChange, discardStagingChanges } from './settingsSlice';
|
import { commitStagingChange, discardStagingChanges } from './settingsSlice';
|
||||||
import { PanelKWMv2Key } from './panels/PanelKWMv2Key';
|
import { PanelKWMv2Key } from './panels/PanelKWMv2Key';
|
||||||
|
import { selectIsSettingsNotSaved } from './settingsSelector';
|
||||||
|
|
||||||
const TABS: { name: string; Tab: () => JSX.Element }[] = [
|
const TABS: { name: string; Tab: () => JSX.Element }[] = [
|
||||||
{ name: 'QMCv2 密钥', Tab: PanelQMCv2Key },
|
{ name: 'QMCv2 密钥', Tab: PanelQMCv2Key },
|
||||||
@ -38,6 +41,7 @@ const TABS: { name: string; Tab: () => JSX.Element }[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export function Settings() {
|
export function Settings() {
|
||||||
|
const toast = useToast();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isLargeWidthDevice =
|
const isLargeWidthDevice =
|
||||||
useBreakpointValue({
|
useBreakpointValue({
|
||||||
@ -49,8 +53,25 @@ export function Settings() {
|
|||||||
const handleTabChange = (idx: number) => {
|
const handleTabChange = (idx: number) => {
|
||||||
setTabIndex(idx);
|
setTabIndex(idx);
|
||||||
};
|
};
|
||||||
const handleResetSettings = () => dispatch(discardStagingChanges());
|
const handleResetSettings = () => {
|
||||||
const handleApplySettings = () => dispatch(commitStagingChange());
|
dispatch(discardStagingChanges());
|
||||||
|
|
||||||
|
toast({
|
||||||
|
status: 'info',
|
||||||
|
title: '未储存的设定已舍弃',
|
||||||
|
description: '已还原到更改前的状态。',
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleApplySettings = () => {
|
||||||
|
dispatch(commitStagingChange());
|
||||||
|
toast({
|
||||||
|
status: 'success',
|
||||||
|
title: '设定已应用',
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const isSettingsNotSaved = useAppSelector(selectIsSettingsNotSaved);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDir="column" flex={1}>
|
<Flex flexDir="column" flex={1}>
|
||||||
@ -104,7 +125,16 @@ export function Settings() {
|
|||||||
<VStack mt="4" alignItems="flex-start" w="full">
|
<VStack mt="4" alignItems="flex-start" w="full">
|
||||||
<Flex flexDir="row" gap="2" w="full">
|
<Flex flexDir="row" gap="2" w="full">
|
||||||
<Center>
|
<Center>
|
||||||
<Box color="gray">设置会在保存后生效。</Box>
|
{isSettingsNotSaved ? (
|
||||||
|
<Box color="gray">
|
||||||
|
有未储存的更改{' '}
|
||||||
|
<chakra.span color="red" wordBreak="keep-all">
|
||||||
|
设定将在保存后生效
|
||||||
|
</chakra.span>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Box color="gray">设定将在保存后生效</Box>
|
||||||
|
)}
|
||||||
</Center>
|
</Center>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<HStack gap="2" justifyContent="flex-end">
|
<HStack gap="2" justifyContent="flex-end">
|
||||||
|
@ -53,7 +53,12 @@ export const KWMv2EKeyItem = memo(({ id, ekey, quality, rid, i }: StagingKWMv2Ke
|
|||||||
<InputLeftElement pr="2">
|
<InputLeftElement pr="2">
|
||||||
<Icon as={MdVpnKey} />
|
<Icon as={MdVpnKey} />
|
||||||
</InputLeftElement>
|
</InputLeftElement>
|
||||||
<Input variant="flushed" placeholder="密钥" value={ekey} onChange={(e) => updateKey('ekey', e)} />
|
<Input
|
||||||
|
variant="flushed"
|
||||||
|
placeholder="密钥,通常包含 364 或 704 位字符,没有空格。"
|
||||||
|
value={ekey}
|
||||||
|
onChange={(e) => updateKey('ekey', e)}
|
||||||
|
/>
|
||||||
<InputRightElement>
|
<InputRightElement>
|
||||||
<Text pl="2" color={ekey.length ? 'green.500' : 'red.500'}>
|
<Text pl="2" color={ekey.length ? 'green.500' : 'red.500'}>
|
||||||
<code>{ekey.length || '?'}</code>
|
<code>{ekey.length || '?'}</code>
|
||||||
|
@ -49,8 +49,14 @@ export function PanelKWMv2Key() {
|
|||||||
const fileBuffer = await file.arrayBuffer();
|
const fileBuffer = await file.arrayBuffer();
|
||||||
keys = MMKVParser.parseKuwoEKey(new DataView(fileBuffer));
|
keys = MMKVParser.parseKuwoEKey(new DataView(fileBuffer));
|
||||||
}
|
}
|
||||||
|
if (keys?.length === 0) {
|
||||||
if (keys) {
|
toast({
|
||||||
|
title: '未导入密钥',
|
||||||
|
description: '选择的密钥数据库文件未发现任何可用的密钥。',
|
||||||
|
isClosable: true,
|
||||||
|
status: 'warning',
|
||||||
|
});
|
||||||
|
} else if (keys) {
|
||||||
dispatch(kwm2ImportKeys(keys));
|
dispatch(kwm2ImportKeys(keys));
|
||||||
setShowImportModal(false);
|
setShowImportModal(false);
|
||||||
toast({
|
toast({
|
||||||
@ -88,7 +94,7 @@ export function PanelKWMv2Key() {
|
|||||||
<MenuButton as={IconButton} icon={<MdExpandMore />}></MenuButton>
|
<MenuButton as={IconButton} icon={<MdExpandMore />}></MenuButton>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem onClick={() => setShowImportModal(true)} icon={<Icon as={MdFileUpload} boxSize={5} />}>
|
<MenuItem onClick={() => setShowImportModal(true)} icon={<Icon as={MdFileUpload} boxSize={5} />}>
|
||||||
从文件导入密钥
|
从文件导入密钥…
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<MenuItem color="red" onClick={clearAll} icon={<Icon as={MdDeleteForever} boxSize={5} />}>
|
<MenuItem color="red" onClick={clearAll} icon={<Icon as={MdDeleteForever} boxSize={5} />}>
|
||||||
|
@ -71,14 +71,20 @@ export function PanelQMCv2Key() {
|
|||||||
qmc2Keys = Array.from(map.entries(), ([name, ekey]) => ({ name: getFileName(name), ekey }));
|
qmc2Keys = Array.from(map.entries(), ([name, ekey]) => ({ name: getFileName(name), ekey }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qmc2Keys) {
|
if (qmc2Keys?.length === 0) {
|
||||||
|
toast({
|
||||||
|
title: '未导入密钥',
|
||||||
|
description: '选择的密钥数据库文件未发现任何可用的密钥。',
|
||||||
|
isClosable: true,
|
||||||
|
status: 'warning',
|
||||||
|
});
|
||||||
|
} else if (qmc2Keys) {
|
||||||
dispatch(qmc2ImportKeys(qmc2Keys));
|
dispatch(qmc2ImportKeys(qmc2Keys));
|
||||||
setShowImportModal(false);
|
setShowImportModal(false);
|
||||||
toast({
|
toast({
|
||||||
title: `导入成功 (${qmc2Keys.length})`,
|
title: `导入成功 (${qmc2Keys.length})`,
|
||||||
description: '记得保存更改来应用。',
|
description: '记得保存更改来应用。',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
duration: 5000,
|
|
||||||
status: 'success',
|
status: 'success',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -93,7 +99,7 @@ export function PanelQMCv2Key() {
|
|||||||
return (
|
return (
|
||||||
<Flex minH={0} flexDir="column" flex={1}>
|
<Flex minH={0} flexDir="column" flex={1}>
|
||||||
<Heading as="h2" size="lg">
|
<Heading as="h2" size="lg">
|
||||||
QMCv2 密钥
|
QMCv2 解密密钥
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
<Text>
|
<Text>
|
||||||
@ -110,7 +116,7 @@ export function PanelQMCv2Key() {
|
|||||||
<MenuButton as={IconButton} icon={<MdExpandMore />}></MenuButton>
|
<MenuButton as={IconButton} icon={<MdExpandMore />}></MenuButton>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem onClick={() => setShowImportModal(true)} icon={<Icon as={MdFileUpload} boxSize={5} />}>
|
<MenuItem onClick={() => setShowImportModal(true)} icon={<Icon as={MdFileUpload} boxSize={5} />}>
|
||||||
从文件导入密钥
|
从文件导入密钥…
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<MenuItem color="red" onClick={clearAll} icon={<Icon as={MdDeleteForever} boxSize={5} />}>
|
<MenuItem color="red" onClick={clearAll} icon={<Icon as={MdDeleteForever} boxSize={5} />}>
|
||||||
@ -141,6 +147,7 @@ export function PanelQMCv2Key() {
|
|||||||
」算法计算相似程度。
|
」算法计算相似程度。
|
||||||
</Text>
|
</Text>
|
||||||
<Text>若密钥数量过多,匹配时可能会造成浏览器卡顿或无响应一段时间。</Text>
|
<Text>若密钥数量过多,匹配时可能会造成浏览器卡顿或无响应一段时间。</Text>
|
||||||
|
<Text>若不确定,请勾选该项。</Text>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -30,13 +30,23 @@ export const QMCv2EKeyItem = memo(({ id, name, ekey, i }: { id: string; name: st
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<VStack flex={1}>
|
<VStack flex={1}>
|
||||||
<Input variant="flushed" placeholder="文件名" value={name} onChange={(e) => updateKey('name', e)} />
|
<Input
|
||||||
|
variant="flushed"
|
||||||
|
placeholder="文件名,包括后缀名。如 “AAA - BBB.mflac”"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => updateKey('name', e)}
|
||||||
|
/>
|
||||||
|
|
||||||
<InputGroup size="xs">
|
<InputGroup size="xs">
|
||||||
<InputLeftElement pr="2">
|
<InputLeftElement pr="2">
|
||||||
<Icon as={MdVpnKey} />
|
<Icon as={MdVpnKey} />
|
||||||
</InputLeftElement>
|
</InputLeftElement>
|
||||||
<Input variant="flushed" placeholder="密钥" value={ekey} onChange={(e) => updateKey('ekey', e)} />
|
<Input
|
||||||
|
variant="flushed"
|
||||||
|
placeholder="密钥,通常包含 364 或 704 位字符,没有空格。"
|
||||||
|
value={ekey}
|
||||||
|
onChange={(e) => updateKey('ekey', e)}
|
||||||
|
/>
|
||||||
<InputRightElement>
|
<InputRightElement>
|
||||||
<Text pl="2" color={ekey.length ? 'green.500' : 'red.500'}>
|
<Text pl="2" color={ekey.length ? 'green.500' : 'red.500'}>
|
||||||
<code>{ekey.length || '?'}</code>
|
<code>{ekey.length || '?'}</code>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { parseKuwoHeader } from '~/crypto/pasreKuwo';
|
import { parseKuwoHeader } from '~/crypto/parseKuwo';
|
||||||
import type { RootState } from '~/store';
|
import type { RootState } from '~/store';
|
||||||
import { closestByLevenshtein } from '~/util/levenshtein';
|
import { closestByLevenshtein } from '~/util/levenshtein';
|
||||||
import { hasOwn } from '~/util/objects';
|
import { hasOwn } from '~/util/objects';
|
||||||
import { kwm2StagingToProductionKey } from './keyFormats';
|
import { kwm2StagingToProductionKey } from './keyFormats';
|
||||||
|
|
||||||
|
export const selectIsSettingsNotSaved = (state: RootState) => state.settings.dirty;
|
||||||
|
|
||||||
export const selectStagingQMCv2Settings = (state: RootState) => state.settings.staging.qmc2;
|
export const selectStagingQMCv2Settings = (state: RootState) => state.settings.staging.qmc2;
|
||||||
export const selectFinalQMCv2Settings = (state: RootState) => state.settings.production.qmc2;
|
export const selectFinalQMCv2Settings = (state: RootState) => state.settings.production.qmc2;
|
||||||
|
|
||||||
|
@ -37,16 +37,18 @@ export interface ProductionSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SettingsState {
|
export interface SettingsState {
|
||||||
|
dirty: boolean;
|
||||||
staging: StagingSettings;
|
staging: StagingSettings;
|
||||||
production: ProductionSettings;
|
production: ProductionSettings;
|
||||||
}
|
}
|
||||||
const initialState: SettingsState = {
|
const initialState: SettingsState = {
|
||||||
|
dirty: false,
|
||||||
staging: {
|
staging: {
|
||||||
qmc2: { allowFuzzyNameSearch: false, keys: [] },
|
qmc2: { allowFuzzyNameSearch: true, keys: [] },
|
||||||
kwm2: { keys: [] },
|
kwm2: { keys: [] },
|
||||||
},
|
},
|
||||||
production: {
|
production: {
|
||||||
qmc2: { allowFuzzyNameSearch: false, keys: {} },
|
qmc2: { allowFuzzyNameSearch: true, keys: {} },
|
||||||
kwm2: { keys: {} },
|
kwm2: { keys: {} },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -77,6 +79,7 @@ export const settingsSlice = createSlice({
|
|||||||
reducers: {
|
reducers: {
|
||||||
setProductionChanges: (_state, { payload }: PayloadAction<ProductionSettings>) => {
|
setProductionChanges: (_state, { payload }: PayloadAction<ProductionSettings>) => {
|
||||||
return {
|
return {
|
||||||
|
dirty: false,
|
||||||
production: payload,
|
production: payload,
|
||||||
staging: productionToStaging(payload),
|
staging: productionToStaging(payload),
|
||||||
};
|
};
|
||||||
@ -84,14 +87,17 @@ export const settingsSlice = createSlice({
|
|||||||
//
|
//
|
||||||
qmc2AddKey(state) {
|
qmc2AddKey(state) {
|
||||||
state.staging.qmc2.keys.push({ id: nanoid(), name: '', ekey: '' });
|
state.staging.qmc2.keys.push({ id: nanoid(), name: '', ekey: '' });
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
qmc2ImportKeys(state, { payload }: PayloadAction<Omit<StagingQMCv2Key, 'id'>[]>) {
|
qmc2ImportKeys(state, { payload }: PayloadAction<Omit<StagingQMCv2Key, 'id'>[]>) {
|
||||||
const newItems = payload.map((item) => ({ id: nanoid(), ...item }));
|
const newItems = payload.map((item) => ({ id: nanoid(), ...item }));
|
||||||
state.staging.qmc2.keys.push(...newItems);
|
state.staging.qmc2.keys.push(...newItems);
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
qmc2DeleteKey(state, { payload: { id } }: PayloadAction<{ id: string }>) {
|
qmc2DeleteKey(state, { payload: { id } }: PayloadAction<{ id: string }>) {
|
||||||
const qmc2 = state.staging.qmc2;
|
const qmc2 = state.staging.qmc2;
|
||||||
qmc2.keys = qmc2.keys.filter((item) => item.id !== id);
|
qmc2.keys = qmc2.keys.filter((item) => item.id !== id);
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
qmc2UpdateKey(
|
qmc2UpdateKey(
|
||||||
state,
|
state,
|
||||||
@ -100,25 +106,31 @@ export const settingsSlice = createSlice({
|
|||||||
const keyItem = state.staging.qmc2.keys.find((item) => item.id === id);
|
const keyItem = state.staging.qmc2.keys.find((item) => item.id === id);
|
||||||
if (keyItem) {
|
if (keyItem) {
|
||||||
keyItem[field] = value;
|
keyItem[field] = value;
|
||||||
|
state.dirty = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
qmc2ClearKeys(state) {
|
qmc2ClearKeys(state) {
|
||||||
state.staging.qmc2.keys = [];
|
state.staging.qmc2.keys = [];
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
qmc2AllowFuzzyNameSearch(state, { payload: { enable } }: PayloadAction<{ enable: boolean }>) {
|
qmc2AllowFuzzyNameSearch(state, { payload: { enable } }: PayloadAction<{ enable: boolean }>) {
|
||||||
state.staging.qmc2.allowFuzzyNameSearch = enable;
|
state.staging.qmc2.allowFuzzyNameSearch = enable;
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
// TODO: reuse the logic somehow?
|
// TODO: reuse the logic somehow?
|
||||||
kwm2AddKey(state) {
|
kwm2AddKey(state) {
|
||||||
state.staging.kwm2.keys.push({ id: nanoid(), ekey: '', quality: '', rid: '' });
|
state.staging.kwm2.keys.push({ id: nanoid(), ekey: '', quality: '', rid: '' });
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
kwm2ImportKeys(state, { payload }: PayloadAction<Omit<StagingKWMv2Key, 'id'>[]>) {
|
kwm2ImportKeys(state, { payload }: PayloadAction<Omit<StagingKWMv2Key, 'id'>[]>) {
|
||||||
const newItems = payload.map((item) => ({ id: nanoid(), ...item }));
|
const newItems = payload.map((item) => ({ id: nanoid(), ...item }));
|
||||||
state.staging.kwm2.keys.push(...newItems);
|
state.staging.kwm2.keys.push(...newItems);
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
kwm2DeleteKey(state, { payload: { id } }: PayloadAction<{ id: string }>) {
|
kwm2DeleteKey(state, { payload: { id } }: PayloadAction<{ id: string }>) {
|
||||||
const kwm2 = state.staging.kwm2;
|
const kwm2 = state.staging.kwm2;
|
||||||
kwm2.keys = kwm2.keys.filter((item) => item.id !== id);
|
kwm2.keys = kwm2.keys.filter((item) => item.id !== id);
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
kwm2UpdateKey(
|
kwm2UpdateKey(
|
||||||
state,
|
state,
|
||||||
@ -127,18 +139,22 @@ export const settingsSlice = createSlice({
|
|||||||
const keyItem = state.staging.kwm2.keys.find((item) => item.id === id);
|
const keyItem = state.staging.kwm2.keys.find((item) => item.id === id);
|
||||||
if (keyItem) {
|
if (keyItem) {
|
||||||
keyItem[field] = value;
|
keyItem[field] = value;
|
||||||
|
state.dirty = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
kwm2ClearKeys(state) {
|
kwm2ClearKeys(state) {
|
||||||
state.staging.kwm2.keys = [];
|
state.staging.kwm2.keys = [];
|
||||||
|
state.dirty = true;
|
||||||
},
|
},
|
||||||
//
|
//
|
||||||
discardStagingChanges: (state) => {
|
discardStagingChanges: (state) => {
|
||||||
|
state.dirty = false;
|
||||||
state.staging = productionToStaging(state.production);
|
state.staging = productionToStaging(state.production);
|
||||||
},
|
},
|
||||||
commitStagingChange: (state) => {
|
commitStagingChange: (state) => {
|
||||||
const production = stagingToProduction(state.staging);
|
const production = stagingToProduction(state.staging);
|
||||||
return {
|
return {
|
||||||
|
dirty: false,
|
||||||
// Sync back to staging
|
// Sync back to staging
|
||||||
staging: productionToStaging(production),
|
staging: productionToStaging(production),
|
||||||
production,
|
production,
|
||||||
|
@ -32,7 +32,12 @@ export class DatabaseKeyExtractor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = db.exec('select file_path, ekey from `audio_file_ekey_table`')[0].values;
|
const result = db.exec('select file_path, ekey from audio_file_ekey_table');
|
||||||
|
if (result.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys = result[0].values;
|
||||||
return keys.map(([path, ekey]) => ({
|
return keys.map(([path, ekey]) => ({
|
||||||
// strip dir name
|
// strip dir name
|
||||||
name: getFileName(String(path)),
|
name: getFileName(String(path)),
|
||||||
|
Loading…
Reference in New Issue
Block a user