Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

import music by tags #120

Merged
merged 20 commits into from
Mar 8, 2024
10 changes: 10 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ extends:
- plugin:react/recommended
- plugin:react-hooks/recommended
- prettier
settings:
react:
version: "detect"
parserOptions:
ecmaVersion: latest
sourceType: module
Expand All @@ -20,5 +23,12 @@ rules:
"@typescript-eslint/no-unsafe-argument": off
"@typescript-eslint/explicit-function-return-type": off
"@typescript-eslint/promise-function-async": off
"@typescript-eslint/strict-boolean-expressions": off
"@typescript-eslint/consistent-type-imports": off
"@typescript-eslint/no-misused-promises": off
"@typescript-eslint/no-non-null-assertion": off
"@typescript-eslint/no-floating-promises": off
"@typescript-eslint/array-type": off
"@typescript-eslint/prefer-nullish-coalescing": off

"react/react-in-jsx-scope": off
5 changes: 1 addition & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"files.exclude": {
"**/design": true
}
"typescript.tsdk": "node_modules/typescript/lib"
}
9 changes: 7 additions & 2 deletions apps/cli/dev_server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path';
import startServer from './src/commands/start_server';
import exitWithMessage from './src/utils/exit_with_message';
import { Mode } from './src/config';
import { DEFAULT_CONFIG, Mode } from './src/config';

const data = process.env.CICADA_DATA;
if (!data) {
Expand All @@ -12,4 +12,9 @@ if (!data) {
const absoluteData = path.isAbsolute(data!)
? data!
: path.resolve(process.cwd(), data!);
startServer({ mode: Mode.DEVELOPMENT, port: 8000, data: absoluteData });
startServer({
mode: Mode.DEVELOPMENT,
port: 8000,
data: absoluteData,
jwtExpiry: DEFAULT_CONFIG.jwtExpiry,
});
6 changes: 2 additions & 4 deletions apps/cli/src/commands/fix_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,11 @@ export default async ({ data }: { data: string }) => {
let spinner: Spinner;

// eslint-disable-next-line prefer-const
spinner = createSpinner();
spinner.start({ text: "Fixing music's year..." });
spinner = createSpinner().start({ text: "Fixing music's year..." });
await fixMusicYear();
spinner.success({ text: "Music's year has fixed" });

spinner = createSpinner();
spinner.start({ text: 'Fixing DB snapshots...' });
spinner = createSpinner().start({ text: 'Fixing DB snapshots...' });
await fixDBSnapshots();
spinner.success({ text: 'DB snapshots have fixed' });

Expand Down
109 changes: 62 additions & 47 deletions apps/cli/src/commands/import_music.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,13 @@ import {
MusicSingerRelationProperty,
} from '@/constants/db_definition';
import { getAssetDirectory, updateConfig } from '../config';

/**
* 文件格式, singer1,singer2 - name.format
* @author mebtte<hi@mebtte.com>
*/
const MATCH_FILENAME = /^((([^,]+)(,?))+)(\s+)-(\s+)(.+)\.(\S+)$/;
import getMusicFileMetadata, {
Metadata,
} from '#/utils/get_music_file_metadata';

let successful = 0;
let ignored = 0;

/**
* 合并多个空格以及移除头尾空格
* @author mebtte<hi@mebtte.com>
*/
function handleSpace(s: string) {
return s.replace(/\s+/g, ' ').trim();
}

/**
* 检查音乐是否已存在
* @author mebtte<hi@mebtte.com>
Expand All @@ -58,7 +47,7 @@ async function checkMusicExist({
>(
`
SELECT
${MusicProperty.ID},
${MusicProperty.ID},
${MusicProperty.NAME}
FROM ${MUSIC_TABLE_NAME}
WHERE ${MusicProperty.NAME} = ?
Expand Down Expand Up @@ -88,7 +77,7 @@ async function checkMusicExist({
}

async function importFile(
file: string,
filepath: string,
{
skipExistenceCheck,
uid,
Expand All @@ -97,46 +86,38 @@ async function importFile(
uid: string;
},
) {
const spinner = createSpinner(file);
spinner.start();

/**
* 检查文件名
* @author mebtte<hi@mebtte.com>
*/
if (!MATCH_FILENAME.test(path.parse(file).base)) {
ignored += 1;
return spinner.warn({
text: `[ ${file} ] isn't a valid filename, ignored`,
});
}
const spinner = createSpinner(filepath).start();

/**
* 检查文件类型
* @author mebtte<hi@mebtte.com>
*/
const ft = await fileType.fromFile(file);
const ft = await fileType.fromFile(filepath);
const { acceptTypes } = ASSET_TYPE_MAP[AssetType.MUSIC];
if (ft && acceptTypes.includes(ft.mime)) {
const [singerString, originalName] = path.parse(file).name.split(' - ');
const name = handleSpace(originalName);
const singers = singerString
.split(',')
.map((s) => handleSpace(s))
.filter((s) => s.length > 0);

let musicTag: Metadata;
try {
musicTag = await getMusicFileMetadata(filepath);
} catch (error) {
ignored += 1;
return spinner.warn({
text: `[ ${filepath} ] can not be parsed and been ignored`,
});
}
const name = musicTag.title || 'Unknown';
const singers = musicTag.artist?.split(',') || ['Unknown'];
if (!skipExistenceCheck) {
const exist = await checkMusicExist({ singers, name });
if (exist) {
ignored += 1;
return spinner.warn({
text: `[ ${file} ] has been database already and ignored, using [ --skip-existence-check ] will skip existence check`,
text: `[ ${filepath} ] has been in database already and ignored, using [ --skip-existence-check ] will skip existence check`,
});
}
}

const fileData = await fs.readFile(file);
const assetName = md5(fileData) + path.parse(file).ext;
const fileData = await fs.readFile(filepath);
const assetName = md5(fileData) + path.parse(filepath).ext;
await fs.writeFile(
`${getAssetDirectory(AssetType.MUSIC)}/${assetName}`,
fileData,
Expand Down Expand Up @@ -169,14 +150,46 @@ async function importFile(
);
}

spinner.success({
text: `[ ${file} ] imported`,
});
if (musicTag.year) {
await getDB().run(
`
UPDATE ${MUSIC_TABLE_NAME} SET ${MusicProperty.YEAR} = ?
WHERE ${MusicProperty.ID} = ?
`,
[musicTag.year, id],
);
}

if (musicTag.picture) {
const buffer = Buffer.from(
musicTag.picture.dataURI.split(',')[1],
'base64',
);
const coverFilename = `${md5(buffer)}.${
musicTag.picture.format.split('/')[1]
}`;
await Promise.all([
fs.writeFile(
`${getAssetDirectory(AssetType.MUSIC_COVER)}/${coverFilename}`,
buffer,
),
getDB().run(
`
UPDATE ${MUSIC_TABLE_NAME} SET ${MusicProperty.COVER} = ?
WHERE ${MusicProperty.ID} = ?
`,
[coverFilename, id],
),
]);
}

successful += 1;
spinner.success({
text: `[ ${filepath} ] imported`,
});
} else {
spinner.warn({ text: `[ ${file} ] isn't a valid format, ignored` });
ignored += 1;
spinner.warn({ text: `[ ${filepath} ] isn't a valid format, ignored` });
}
}

Expand Down Expand Up @@ -217,7 +230,7 @@ async function importDirectory(
}
}

export default async ({
async function importMusic({
source,
data,
uid,
Expand All @@ -229,7 +242,7 @@ export default async ({
uid: string;
recursive: boolean;
skipExistenceCheck: boolean;
}) => {
}) {
updateConfig({ data });

/**
Expand All @@ -252,4 +265,6 @@ export default async ({
text: `Successful ${successful}, ignored ${ignored}`,
});
return process.exit();
};
}

export default importMusic;
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default async (ctx: Context) => {
if (
typeof id !== 'string' ||
!id.length ||
// @ts-expect-error
// @ts-expect-error: key is unknown
!Object.values(AllowUpdateKey).includes(key)
) {
return ctx.except(ExceptionCode.WRONG_PARAMETER);
Expand Down
8 changes: 6 additions & 2 deletions apps/cli/src/commands/start_server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,31 @@ import { getFormApp } from './form_app';
import { getPwaApp } from './pwa_app';
import { getBaseApp } from './base_app';
import i18n from './middlewares/i18n';
import ms from 'ms';

function printConfig() {
const config = getConfig();
const printConfigKeys: (keyof Config)[] = ['mode', 'port', 'data'];
const printConfigKeys: Array<keyof Config> = ['mode', 'port', 'data'];
console.log('---');
for (const key of printConfigKeys) {
console.log(`${key}: ${config[key]}`);
}
console.log(`jwtExpiry: ${ms(config.jwtExpiry)}`);
console.log('---');
}

export default async ({
mode,
port,
data,
jwtExpiry,
}: {
mode: Mode;
port: number;
data: string;
jwtExpiry: number;
}) => {
updateConfig({ mode, port, data });
updateConfig({ mode, port, data, jwtExpiry });
printConfig();

await initialize();
Expand Down
29 changes: 12 additions & 17 deletions apps/cli/src/commands/upgrade_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getDB } from '@/db';
import generateRandomString from '#/utils/generate_random_string';

function dropLoginCodeSalt() {
return fs.unlinkSync(`${getConfig().data}/login_code_salt`);
return fsPromises.unlink(`${getConfig().data}/login_code_salt`);
}

function dropLoginCode() {
Expand Down Expand Up @@ -105,15 +105,15 @@ async function addUserPassword() {
}

async function addUserTwoFASecret() {
return getDB().run(
return await getDB().run(
`
ALTER TABLE user ADD twoFASecret TEXT DEFAULT NULL
`,
);
}

async function addUserTokenIdentifier() {
return getDB().run(
return await getDB().run(
`
ALTER TABLE user ADD tokenIdentifier TEXT NOT NULL DEFAULT ''
`,
Expand Down Expand Up @@ -154,38 +154,33 @@ export default async ({ data }: { data: string }) => {

let spinner: Spinner;

spinner = createSpinner();
spinner.start({ text: 'Dropping login code...' });
spinner = createSpinner().start({ text: 'Dropping login code...' });
await dropLoginCode();
spinner.success({ text: 'Login code has dropped' });

spinner = createSpinner();
spinner.start({ text: 'Dropping login code salt...' });
spinner = createSpinner().start({ text: 'Dropping login code salt...' });
await dropLoginCodeSalt();
spinner.success({ text: 'Login code salt has dropped' });

spinner = createSpinner();
spinner.start({ text: 'Renaming user.email to user.username...' });
spinner = createSpinner().start({
text: 'Renaming user.email to user.username...',
});
await renameUserEmailToUsername();
spinner.success({ text: 'user.email has renamed to user.username' });

spinner = createSpinner();
spinner.start({ text: 'Adding user.password...' });
spinner = createSpinner().start({ text: 'Adding user.password...' });
const userList = await addUserPassword();
spinner.success({ text: 'user.password has added' });

spinner = createSpinner();
spinner.start({ text: 'Adding user.twoFASecret...' });
spinner = createSpinner().start({ text: 'Adding user.twoFASecret...' });
await addUserTwoFASecret();
spinner.success({ text: 'user.twoFASecret has added' });

spinner = createSpinner();
spinner.start({ text: 'Adding user.tokenIdentifier...' });
spinner = createSpinner().start({ text: 'Adding user.tokenIdentifier...' });
await addUserTokenIdentifier();
spinner.success({ text: 'user.tokenIdentifier has added' });

spinner = createSpinner();
spinner.start({ text: 'Writting new version of data...' });
spinner = createSpinner().start({ text: 'Writting new version of data...' });
await writeNewVersion();
spinner.success({ text: 'New version of data has wrote' });

Expand Down
3 changes: 3 additions & 0 deletions apps/cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ export interface Config {

data: string;
port: number;

jwtExpiry: number;
}

export const DEFAULT_CONFIG: Config = {
mode: Mode.PRODUCTION,

data: `${process.cwd()}/cicada`,
port: 8000,
jwtExpiry: 1000 * 60 * 60 * 24 * 180,
};

let config: Config = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
Expand Down
Loading
Loading