从webpack迁移到vite
@@ -0,0 +1,15 @@
|
||||
/* eslint-env node */
|
||||
const { base, typescript } = require('../../.eslintrc.base.cjs')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
...base,
|
||||
overrides: [
|
||||
{
|
||||
...typescript,
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { join } from 'path'
|
||||
import { homedir } from 'os'
|
||||
import path from 'node:path'
|
||||
import os from 'node:os'
|
||||
|
||||
const isMac = process.platform == 'darwin'
|
||||
const isWin = process.platform == 'win32'
|
||||
@@ -102,7 +102,7 @@ const defaultSetting: LX.AppSetting = {
|
||||
'list.actionButtonsVisible': false,
|
||||
|
||||
'download.enable': false,
|
||||
'download.savePath': join(homedir(), 'Desktop'),
|
||||
'download.savePath': path.join(os.homedir(), 'Desktop'),
|
||||
'download.fileName': '歌名 - 歌手',
|
||||
'download.maxDownloadNum': 3,
|
||||
'download.skipExistFile': true,
|
||||
|
||||
@@ -6,7 +6,7 @@ const ignoreErrorMessage = [
|
||||
]
|
||||
|
||||
process.on('uncaughtException', err => {
|
||||
if (ignoreErrorMessage.includes(err.message)) return
|
||||
if (ignoreErrorMessage.includes(err?.message)) return
|
||||
console.error('An uncaught error occurred!')
|
||||
console.error(err)
|
||||
log.error(err)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @param {string} c1 rgb(a) color2
|
||||
* @returns color
|
||||
*/
|
||||
exports.RGB_Linear_Blend=(p,c0,c1)=>{
|
||||
export const RGB_Linear_Blend=(p,c0,c1)=>{
|
||||
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
|
||||
return"rgb"+(x?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+i(e[3]=="a"?e.slice(5):e.slice(4))*p)+","+r(i(b)*P+i(f)*p)+","+r(i(c)*P+i(g)*p)+j;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ exports.RGB_Linear_Blend=(p,c0,c1)=>{
|
||||
* @param {string} c1 rgb(a) color2
|
||||
* @returns color
|
||||
*/
|
||||
exports.RGB_Log_Blend=(p,c0,c1)=>{
|
||||
export const RGB_Log_Blend=(p,c0,c1)=>{
|
||||
var i=parseInt,r=Math.round,P=1-p,[a,b,c,d]=c0.split(","),[e,f,g,h]=c1.split(","),x=d||h,j=x?","+(!d?h:!h?d:r((parseFloat(d)*P+parseFloat(h)*p)*1000)/1000+")"):")";
|
||||
return"rgb"+(x?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+p*i(e[3]=="a"?e.slice(5):e.slice(4))**2)**0.5)+","+r((P*i(b)**2+p*i(f)**2)**0.5)+","+r((P*i(c)**2+p*i(g)**2)**0.5)+j;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ exports.RGB_Log_Blend=(p,c0,c1)=>{
|
||||
* @param {string} c0 rgb(a) color
|
||||
* @returns color
|
||||
*/
|
||||
exports.RGB_Linear_Shade=(p,c0)=>{
|
||||
export const RGB_Linear_Shade=(p,c0)=>{
|
||||
var i=parseInt,r=Math.round,[a,b,c,d]=c0.split(","),n=p<0,t=n?0:255*p,P=n?1+p:1-p;
|
||||
return"rgb"+(d?"a(":"(")+r(i(a[3]=="a"?a.slice(5):a.slice(4))*P+t)+","+r(i(b)*P+t)+","+r(i(c)*P+t)+(d?","+d:")");
|
||||
}
|
||||
@@ -44,7 +44,7 @@ exports.RGB_Linear_Shade=(p,c0)=>{
|
||||
* @param {string} c0 rgb(a) color
|
||||
* @returns color
|
||||
*/
|
||||
exports.RGB_Log_Shade=(p,c0)=>{
|
||||
export const RGB_Log_Shade=(p,c0)=>{
|
||||
var i=parseInt,r=Math.round,[a,b,c,d]=c0.split(","),n=p<0,t=n?0:p*255**2,P=n?1+p:1-p;
|
||||
return"rgb"+(d?"a(":"(")+r((P*i(a[3]=="a"?a.slice(5):a.slice(4))**2+t)**0.5)+","+r((P*i(b)**2+t)**0.5)+","+r((P*i(c)**2+t)**0.5)+(d?","+d:")");
|
||||
}
|
||||
@@ -56,7 +56,7 @@ exports.RGB_Log_Shade=(p,c0)=>{
|
||||
* @param {string} color
|
||||
* @returns color
|
||||
*/
|
||||
exports.RGB_Alpha_Shade = (p, color) => {
|
||||
export const RGB_Alpha_Shade = (p, color) => {
|
||||
var i = parseInt
|
||||
var n = p < 0
|
||||
var [r, g, b, a] = color.split(",")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! 更新默认主题配置后,需要执行 npm run build:theme 重新构建index.json
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { createThemeColors } = require('./utils')
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { createThemeColors } from './utils'
|
||||
|
||||
const defaultThemes = [
|
||||
{
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 353 KiB After Width: | Height: | Size: 353 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 997 KiB After Width: | Height: | Size: 997 KiB |
@@ -1,6 +1,6 @@
|
||||
const { RGB_Linear_Shade, RGB_Alpha_Shade } = require('./colorUtils')
|
||||
import { RGB_Linear_Shade, RGB_Alpha_Shade } from './colorUtils'
|
||||
|
||||
exports.createThemeColors = (rgbaColor, fontRgbaColor, isDark) => {
|
||||
export const createThemeColors = (rgbaColor, fontRgbaColor, isDark) => {
|
||||
const colors = {
|
||||
'--color-primary': rgbaColor,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@common/*": ["common/*"],
|
||||
},
|
||||
"typeRoots": [
|
||||
"./types"
|
||||
],
|
||||
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
"@common/*": ["common/*"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { I18n } from '@/lang/i18n'
|
||||
import type { I18n } from '@root/lang/i18n'
|
||||
|
||||
declare global {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Message } from '@/lang'
|
||||
import { type Message } from '@root/lang'
|
||||
|
||||
// interface DownloadList {
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import log from 'electron-log'
|
||||
|
||||
log.transports.file.level = 'info'
|
||||
|
||||
export const isLinux = process.platform == 'linux'
|
||||
export const isWin = process.platform == 'win32'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { getNow, TimeoutTools } = require('./utils')
|
||||
import { getNow, TimeoutTools } from './utils'
|
||||
|
||||
// const fontFormateRxp = /(?=<\d+,\d+>).*?/g
|
||||
const fontSplitRxp = /(?=<\d+,\d+>).*?/g
|
||||
@@ -25,7 +25,7 @@ const createAnimation = (dom, duration, isVertical) => new window.Animation(new
|
||||
// https://jsfiddle.net/ceqpnbky/
|
||||
// https://jsfiddle.net/ceqpnbky/1/
|
||||
|
||||
module.exports = class FontPlayer {
|
||||
export default class FontPlayer {
|
||||
constructor({
|
||||
time = 0,
|
||||
rate = 1,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const LinePlayer = require('./line-player')
|
||||
const FontPlayer = require('./font-player')
|
||||
import LinePlayer from './line-player'
|
||||
import FontPlayer from './font-player'
|
||||
|
||||
const fontTimeExp = /<(\d+),(\d+)>/g
|
||||
|
||||
module.exports = class Lyric {
|
||||
export default class Lyric {
|
||||
constructor({
|
||||
lyric = '',
|
||||
extendedLyrics = [],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { getNow, TimeoutTools } = require('./utils')
|
||||
import { getNow, TimeoutTools } from './utils'
|
||||
|
||||
const timeFieldExp = /^(?:\[[\d:.]+\])+/g
|
||||
const timeExp = /\d{1,3}(:\d{1,3}){0,2}(?:\.\d{1,3})/g
|
||||
@@ -43,7 +43,7 @@ const parseExtendedLyric = (lrcLinesMap, extendedLyric) => {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = class LinePlayer {
|
||||
export default class LinePlayer {
|
||||
constructor({ offset = 0, rate = 1, onPlay = function() { }, onSetLyric = function() { } } = {}) {
|
||||
this.tags = {}
|
||||
this.lines = null
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
const getNow = exports.getNow = typeof performance == 'object' && window.performance.now ? window.performance.now.bind(window.performance) : Date.now.bind(Date)
|
||||
export const getNow = typeof performance == 'object' && window.performance.now ? window.performance.now.bind(window.performance) : Date.now.bind(Date)
|
||||
|
||||
exports.TimeoutTools = class TimeoutTools {
|
||||
export class TimeoutTools {
|
||||
constructor(thresholdTime = 80) {
|
||||
this.invokeTime = 0
|
||||
this.animationFrameId = null
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import fs from 'fs'
|
||||
import crypto from 'crypto'
|
||||
import { gzip, gunzip } from 'zlib'
|
||||
import fs from 'node:fs'
|
||||
import crypto from 'node:crypto'
|
||||
import { gzip, gunzip } from 'node:zlib'
|
||||
import path from 'node:path'
|
||||
import { log } from '@common/utils'
|
||||
import path from 'path'
|
||||
|
||||
export const joinPath = (...paths: string[]): string => path.join(...paths)
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/* eslint-env node */
|
||||
const { base, typescript } = require('../../.eslintrc.base.cjs')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
...base,
|
||||
overrides: [
|
||||
{
|
||||
...typescript,
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* eslint-env node */
|
||||
const { base, typescript } = require('../../.eslintrc.base.cjs')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
...base,
|
||||
overrides: [
|
||||
{
|
||||
...typescript,
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
},
|
||||
],
|
||||
ignorePatterns: [
|
||||
'vendors',
|
||||
],
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { join, dirname } from 'path'
|
||||
import path from 'node:path'
|
||||
import { existsSync, mkdirSync, renameSync } from 'fs'
|
||||
import { app, shell, screen, nativeTheme, dialog } from 'electron'
|
||||
import { URL_SCHEME_RXP } from '@common/constants'
|
||||
@@ -10,22 +10,19 @@ import { createAppEvent, createListEvent } from '@main/event'
|
||||
import { isMac, log } from '@common/utils'
|
||||
import createWorkers from './worker'
|
||||
import { migrateDBData } from './utils/migrate'
|
||||
import { openDirInExplorer } from '@common/utils/electron'
|
||||
import { encodePath, openDirInExplorer } from '@common/utils/electron'
|
||||
|
||||
export const initGlobalData = () => {
|
||||
global.isDev = process.env.NODE_ENV !== 'production'
|
||||
const envParams = parseEnvParams()
|
||||
global.envParams = {
|
||||
cmdParams: envParams.cmdParams,
|
||||
deeplink: envParams.deeplink,
|
||||
}
|
||||
|
||||
if (global.isDev) {
|
||||
// eslint-disable-next-line no-undef
|
||||
global.staticPath = webpackStaticPath
|
||||
} else {
|
||||
global.staticPath = join(__dirname, '/static')
|
||||
}
|
||||
global.staticPath =
|
||||
process.env.NODE_ENV !== 'production'
|
||||
? __STATIC_PATH__
|
||||
: path.join(encodePath(__dirname), '../static')
|
||||
}
|
||||
|
||||
export const initSingleInstanceHandle = () => {
|
||||
@@ -75,10 +72,10 @@ export const applyElectronEnvParams = () => {
|
||||
export const setUserDataPath = () => {
|
||||
// windows平台下如果应用目录下存在 portable 文件夹则将数据存在此文件下
|
||||
if (process.platform == 'win32') {
|
||||
const portablePath = join(dirname(app.getPath('exe')), '/portable')
|
||||
const portablePath = path.join(path.dirname(app.getPath('exe')), '/portable')
|
||||
if (existsSync(portablePath)) {
|
||||
app.setPath('appData', portablePath)
|
||||
const appDataPath = join(portablePath, '/userData')
|
||||
const appDataPath = path.join(portablePath, '/userData')
|
||||
if (!existsSync(appDataPath)) mkdirSync(appDataPath)
|
||||
app.setPath('userData', appDataPath)
|
||||
}
|
||||
@@ -86,12 +83,12 @@ export const setUserDataPath = () => {
|
||||
|
||||
const userDataPath = app.getPath('userData')
|
||||
global.lxOldDataPath = userDataPath
|
||||
global.lxDataPath = join(userDataPath, 'LxDatas')
|
||||
global.lxDataPath = path.join(userDataPath, 'LxDatas')
|
||||
if (!existsSync(global.lxDataPath)) mkdirSync(global.lxDataPath)
|
||||
}
|
||||
|
||||
export const registerDeeplink = (startApp: () => void) => {
|
||||
if (global.isDev && process.platform === 'win32') {
|
||||
if (process.env.NODE_ENV !== 'production' && process.platform === 'win32') {
|
||||
// Set the path of electron.exe and your app.
|
||||
// These two additional parameters are only available on windows.
|
||||
// console.log(process.execPath, process.argv)
|
||||
@@ -117,8 +114,8 @@ export const registerDeeplink = (startApp: () => void) => {
|
||||
export const listenerAppEvent = (startApp: () => void) => {
|
||||
app.on('web-contents-created', (event, contents) => {
|
||||
contents.on('will-navigate', (event, navigationUrl) => {
|
||||
if (global.isDev) {
|
||||
console.log('navigation to url:', navigationUrl)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.log('navigation to url:', navigationUrl.length > 130 ? navigationUrl.substring(0, 130) + '...' : navigationUrl)
|
||||
return
|
||||
}
|
||||
if (!navigationUrlWhiteList.some(url => url.test(navigationUrl))) {
|
||||
@@ -240,13 +237,13 @@ export const initAppSetting = async() => {
|
||||
if (!isInitialized) {
|
||||
let dbFileExists = await global.lx.worker.dbService.init(global.lxDataPath)
|
||||
if (dbFileExists === null) {
|
||||
const backPath = join(global.lxDataPath, `lx.data.db.${Date.now()}.bak`)
|
||||
const backPath = path.join(global.lxDataPath, `lx.data.db.${Date.now()}.bak`)
|
||||
dialog.showMessageBoxSync({
|
||||
type: 'warning',
|
||||
message: 'Database verify failed',
|
||||
detail: `数据库表结构校验失败,我们将把有问题的数据库备份到:${backPath}\n若此问题导致你的数据丢失,你可以尝试从备份文件找回它们。\n\nThe database table structure verification failed, we will back up the problematic database to: ${backPath}\nIf this problem causes your data to be lost, you can try to retrieve them from the backup file.`,
|
||||
})
|
||||
renameSync(join(global.lxDataPath, 'lx.data.db'), backPath)
|
||||
renameSync(path.join(global.lxDataPath, 'lx.data.db'), backPath)
|
||||
openDirInExplorer(backPath)
|
||||
dbFileExists = await global.lx.worker.dbService.init(global.lxDataPath)
|
||||
}
|
||||
|
||||
@@ -26,5 +26,5 @@ app.on('ready', () => {
|
||||
})
|
||||
|
||||
// Require `main` process to boot app
|
||||
require('./index')
|
||||
import('./index')
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { app } from 'electron'
|
||||
import './utils/logInit'
|
||||
import '@common/error'
|
||||
import {
|
||||
initGlobalData,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import WebSocket from 'ws'
|
||||
import { encryptMsg, decryptMsg } from './utils'
|
||||
import * as modules from './modules'
|
||||
// import { action as commonAction } from '@/store/modules/common'
|
||||
// import { getStore } from '@/store'
|
||||
// import { action as commonAction } from '@root/store/modules/common'
|
||||
// import { getStore } from '@root/store'
|
||||
import registerSyncListHandler from './syncList'
|
||||
import log from '../log'
|
||||
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import handleAuth from './auth'
|
||||
import { connect as socketConnect, disconnect as socketDisconnect, sendSyncStatus, sendSyncMessage } from './client'
|
||||
// import { getSyncHost } from '@/utils/data'
|
||||
// import { getSyncHost } from '@root/utils/data'
|
||||
import { SYNC_CODE } from '@common/constants'
|
||||
import log from '../log'
|
||||
import { parseUrl } from './utils'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Tray, Menu, nativeImage } from 'electron'
|
||||
import { isWin } from '@common/utils'
|
||||
import { join } from 'path'
|
||||
import path from 'node:path'
|
||||
import {
|
||||
hideWindow as hideMainWindow,
|
||||
isExistWindow as isExistMainWindow,
|
||||
@@ -45,7 +45,7 @@ export const createTray = () => {
|
||||
|
||||
themeId = global.lx.appSetting['tray.themeId']
|
||||
let theme = themeList.find(item => item.id === themeId) ?? themeList[0]
|
||||
const iconPath = join(global.staticPath, 'images/tray', theme.fileName + '.png')
|
||||
const iconPath = path.join(global.staticPath, 'images/tray', theme.fileName + '.png')
|
||||
|
||||
// 托盘
|
||||
tray = new Tray(nativeImage.createFromPath(iconPath))
|
||||
@@ -140,7 +140,7 @@ export const createMenu = () => {
|
||||
export const setTrayImage = (themeId: number) => {
|
||||
if (!tray) return
|
||||
let theme = themeList.find(item => item.id === themeId) ?? themeList[0]
|
||||
const iconPath = join(global.staticPath, 'images/tray', theme.fileName + '.png')
|
||||
const iconPath = path.join(global.staticPath, 'images/tray', theme.fileName + '.png')
|
||||
tray.setImage(nativeImage.createFromPath(iconPath))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
exports.userApis = []
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export const userApis: LX.UserApi.UserApiInfo[] = []
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mainSend } from '@common/mainIpc'
|
||||
import { BrowserWindow } from 'electron'
|
||||
import fs from 'fs'
|
||||
import { join } from 'path'
|
||||
import path from 'node:path'
|
||||
import { openDevTools as handleOpenDevTools } from '@main/utils'
|
||||
import { encodePath } from '@common/utils/electron'
|
||||
|
||||
@@ -27,15 +27,16 @@ const winEvent = () => {
|
||||
|
||||
export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
|
||||
await closeWindow()
|
||||
dir ??= global.isDev ? webpackUserApiPath : join(encodePath(__dirname), 'userApi')
|
||||
dir ??= process.env.NODE_ENV !== 'production' ? path.join(__USER_API_PATH__, 'renderer') : encodePath(__dirname)
|
||||
|
||||
if (!html) {
|
||||
html = await fs.promises.readFile(join(dir, 'renderer/user-api.html'), 'utf8')
|
||||
html = await fs.promises.readFile(path.join(dir, 'user-api.html'), 'utf8')
|
||||
}
|
||||
const preloadUrl = global.isDev
|
||||
? `${join(encodePath(__dirname), '../dist/user-api-preload.js')}`
|
||||
: `${join(encodePath(__dirname), 'user-api-preload.js')}`
|
||||
// console.log(preloadUrl)
|
||||
const preloadUrl = process.env.NODE_ENV !== 'production'
|
||||
? `${path.join(encodePath(__dirname), '../dist/user-api-preload.js')}`
|
||||
: `${path.join(encodePath(__dirname), '../preload/user-api-preload.js')}`
|
||||
|
||||
// console.log(preloadUrl, html)
|
||||
|
||||
/**
|
||||
* Initial window options
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
const needle = require('needle')
|
||||
const zlib = require('zlib')
|
||||
const { createCipheriv, publicEncrypt, constants, randomBytes, createHash } = require('crypto')
|
||||
const USER_API_RENDERER_EVENT_NAME = require('../rendererEvent/name')
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import needle from 'needle'
|
||||
import zlib from 'zlib'
|
||||
import { createCipheriv, publicEncrypt, constants, randomBytes, createHash } from 'crypto'
|
||||
import USER_API_RENDERER_EVENT_NAME from '../rendererEvent/name'
|
||||
|
||||
for (const key of Object.keys(process.env)) {
|
||||
if (/^(?:http_proxy|https_proxy|NO_PROXY)$/i.test(key)) delete process.env[key]
|
||||
|
||||
@@ -10,4 +10,5 @@ const names = {
|
||||
for (const key of Object.keys(names)) {
|
||||
names[key] = `userApi_${key}`
|
||||
}
|
||||
module.exports = names
|
||||
|
||||
export default names
|
||||
@@ -104,7 +104,7 @@ export const loadApi = async(apiId: string) => {
|
||||
|
||||
// // const path = require('path')
|
||||
// // // eslint-disable-next-line no-undef
|
||||
// // userApi.script = require('fs').readFileSync(join(global.isDev ? __userApi : __dirname, 'renderer/test-api.js')).toString()
|
||||
// // userApi.script = require('fs').readFileSync(join(process.env.NODE_ENV !== 'production' ? __userApi : __dirname, 'renderer/test-api.js')).toString()
|
||||
// console.log('load api', userApi.name)
|
||||
// mainSend(global.modules.userApiWindow, USER_API_RENDERER_EVENT_NAME.init, { userApi })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { join } from 'path'
|
||||
import path from 'node:path'
|
||||
import { BrowserWindow } from 'electron'
|
||||
import { debounce, isLinux, isWin } from '@common/utils'
|
||||
import { initWindowSize } from './utils'
|
||||
@@ -139,7 +139,7 @@ export const createWindow = () => {
|
||||
},
|
||||
})
|
||||
|
||||
const winURL = global.isDev ? 'http://localhost:9081/lyric.html' : `file://${join(encodePath(__dirname), 'lyric.html')}`
|
||||
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9081' : `file://${path.join(encodePath(__dirname), '../renderer-lyric/index.html')}`
|
||||
void browserWindow.loadURL(winURL + `?dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
|
||||
|
||||
winEvent()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BrowserWindow, dialog } from 'electron'
|
||||
import { join } from 'path'
|
||||
import path from 'node:path'
|
||||
import { createTaskBarButtons, getWindowSizeInfo } from './utils'
|
||||
import { isLinux, isWin } from '@common/utils'
|
||||
import { openDevTools as handleOpenDevTools } from '@main/utils'
|
||||
@@ -96,7 +96,7 @@ export const createWindow = () => {
|
||||
}
|
||||
browserWindow = new BrowserWindow(options)
|
||||
|
||||
const winURL = global.isDev ? 'http://localhost:9080' : `file://${join(encodePath(__dirname), 'index.html')}`
|
||||
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9080' : `file://${path.join(encodePath(__dirname), '../renderer/index.html')}`
|
||||
void browserWindow.loadURL(winURL + `?dt=${!!global.envParams.cmdParams.dt}&dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
|
||||
|
||||
winEvent()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createInflate, constants as zlibConstants } from 'node:zlib'
|
||||
// import path from 'path'
|
||||
import path from 'node:path'
|
||||
import { mainHandle } from '@common/mainIpc'
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
|
||||
@@ -38,9 +38,9 @@ const decode = async(str: string): Promise<string> => {
|
||||
const handleDecode = async(lrc: string, tlrc: string, rlrc: string) => {
|
||||
if (!qrc_decode) {
|
||||
// const nativeBindingPath = path.join(__dirname, '../build/Release/qrc_decode.node')
|
||||
// const nativeBindingPath = isDev ? path.join(__dirname, '../build/Release/qrc_decode.node')
|
||||
// const nativeBindingPath = process.env.NODE_ENV !== 'production' ? path.join(__dirname, '../build/Release/qrc_decode.node')
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const addon = require('qrc_decode.node')
|
||||
const addon = require(path.join(__QRC_DECODE_NODE_PATH__, 'qrc_decode.node'))
|
||||
// console.log(addon)
|
||||
qrc_decode = addon.qrc_decode
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// import fs from 'fs'
|
||||
import { join } from 'path'
|
||||
import path from 'node:path'
|
||||
import { type WindowSize, windowSizeList } from '@common/config'
|
||||
import { nativeImage } from 'electron'
|
||||
|
||||
@@ -8,7 +8,7 @@ export const getWindowSizeInfo = (windowSizeId: number | string): WindowSize =>
|
||||
}
|
||||
|
||||
const getIconPath = (name: string): Electron.NativeImage => {
|
||||
return nativeImage.createFromPath(join(global.staticPath, 'images/taskbar', name + '.png'))
|
||||
return nativeImage.createFromPath(path.join(global.staticPath, 'images/taskbar', name + '.png'))
|
||||
}
|
||||
|
||||
export const createTaskBarButtons = ({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// "module": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"typeRoots": [
|
||||
"./types"
|
||||
],
|
||||
|
||||
@@ -43,7 +43,7 @@ declare global {
|
||||
// }
|
||||
// }
|
||||
|
||||
var isDev: boolean
|
||||
// var isDev: boolean
|
||||
var envParams: LX.EnvParams
|
||||
var staticPath: string
|
||||
var lxDataPath: string
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
// // }
|
||||
// // }
|
||||
|
||||
declare const webpackStaticPath: string
|
||||
declare const webpackUserApiPath: string
|
||||
declare const __STATIC_PATH__: string
|
||||
declare const __USER_API_PATH__: string
|
||||
declare const __QRC_DECODE_NODE_PATH__: string
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import log from 'electron-log'
|
||||
|
||||
log.transports.file.level = 'info'
|
||||
log.initialize()
|
||||
@@ -1,8 +1,8 @@
|
||||
import Store from 'electron-store'
|
||||
import { dialog, shell } from 'electron'
|
||||
import { join } from 'path'
|
||||
import fs from 'fs'
|
||||
import log from 'electron-log'
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs'
|
||||
import { log } from '@common/utils'
|
||||
|
||||
type Stores = Record<string, Store>
|
||||
|
||||
@@ -27,8 +27,8 @@ export default (name: string, isIgnoredError = true, isShowErrorAlert = true): S
|
||||
if (!isIgnoredError) throw error
|
||||
|
||||
|
||||
const backPath = join(global.lxDataPath, name + '.json.bak')
|
||||
fs.renameSync(join(global.lxDataPath, name + '.json'), backPath)
|
||||
const backPath = path.join(global.lxDataPath, name + '.json.bak')
|
||||
fs.renameSync(path.join(global.lxDataPath, name + '.json'), backPath)
|
||||
if (isShowErrorAlert) {
|
||||
dialog.showMessageBoxSync({
|
||||
type: 'error',
|
||||
|
||||
@@ -18,20 +18,20 @@ const initTables = (db: Database.Database) => {
|
||||
// 打开、初始化数据库
|
||||
export const init = (lxDataPath: string): boolean | null => {
|
||||
const databasePath = path.join(lxDataPath, 'lx.data.db')
|
||||
const nativeBinding = path.join(__dirname, '../node_modules/better-sqlite3/build/Release/better_sqlite3.node')
|
||||
const nativeBinding = path.join(__dirname, '../../node_modules/better-sqlite3/build/Release/better_sqlite3.node')
|
||||
let dbFileExists = true
|
||||
|
||||
try {
|
||||
db = new Database(databasePath, {
|
||||
fileMustExist: true,
|
||||
nativeBinding,
|
||||
verbose: global.isDev ? console.log : undefined,
|
||||
verbose: process.env.NODE_ENV !== 'production' ? console.log : undefined,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
db = new Database(databasePath, {
|
||||
nativeBinding,
|
||||
verbose: global.isDev ? console.log : undefined,
|
||||
verbose: process.env.NODE_ENV !== 'production' ? console.log : undefined,
|
||||
})
|
||||
initTables(db)
|
||||
dbFileExists = false
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Worker } from 'worker_threads'
|
||||
import { Worker } from 'node:worker_threads'
|
||||
import * as Comlink from 'comlink'
|
||||
import nodeEndpoint from 'comlink/dist/esm/node-adapter'
|
||||
import path from 'node:path'
|
||||
// import dbService from '../dbService/index'
|
||||
|
||||
export type DBSeriveTypes = Comlink.Remote<LX.WorkerDBSeriveListTypes>
|
||||
export declare type DBSeriveTypes = Comlink.Remote<LX.WorkerDBSeriveListTypes>
|
||||
|
||||
export const createDBServiceWorker = () => {
|
||||
const worker: Worker = new Worker(new URL(
|
||||
/* webpackChunkName: 'dbService.worker' */
|
||||
'../dbService',
|
||||
import.meta.url,
|
||||
))
|
||||
// console.log(new URL('../dbService', import.meta.url))
|
||||
// console.log(__dirname)
|
||||
const worker: Worker = new Worker(path.join(__dirname, './dbService.worker'))
|
||||
return Comlink.wrap<LX.WorkerDBSeriveListTypes>(nodeEndpoint(worker))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { parentPort } from 'worker_threads'
|
||||
import worker from 'node:worker_threads'
|
||||
import * as Comlink from 'comlink'
|
||||
import nodeEndpoint from 'comlink/dist/esm/node-adapter'
|
||||
|
||||
|
||||
export const exposeWorker = (obj: any) => {
|
||||
if (parentPort == null) return
|
||||
Comlink.expose(obj, nodeEndpoint(parentPort))
|
||||
if (worker.parentPort == null) return
|
||||
Comlink.expose(obj, nodeEndpoint(worker.parentPort))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/* eslint-env node */
|
||||
const { base, typescript, vue } = require('../../.eslintrc.base.cjs')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
...base,
|
||||
overrides: [
|
||||
vue,
|
||||
{
|
||||
...typescript,
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,25 +1,22 @@
|
||||
import upperFirst from 'lodash/upperFirst'
|
||||
import camelCase from 'lodash/camelCase'
|
||||
|
||||
const requireComponent = require.context('./', true, /\.vue$/)
|
||||
|
||||
const requireComponent = import.meta.glob(['./**/*.vue', '!./**/components/**/*.vue'], { eager: true })
|
||||
const vueFileRxp = /\.vue$/
|
||||
const vueIndexFileRxp = /\/index\.vue$/
|
||||
|
||||
|
||||
export default app => {
|
||||
requireComponent.keys().forEach(fileName => {
|
||||
const filePath = fileName.replace(/^\.\//, '')
|
||||
Object.entries(requireComponent).forEach(([path, module]) => {
|
||||
path = path.replace(/^\.\//, '')
|
||||
let fileName = vueIndexFileRxp.test(path)
|
||||
? path.replace(vueIndexFileRxp, '')
|
||||
: path.replace(vueFileRxp, '')
|
||||
|
||||
if (!filePath.split('/').every((path, index, arr) => {
|
||||
const char = path.charAt(0)
|
||||
return vueFileRxp.test(path) || char.toUpperCase() !== char || arr[index + 1] == 'index.vue'
|
||||
})) return
|
||||
let componentName = upperFirst(camelCase(fileName))
|
||||
|
||||
const componentConfig = requireComponent(fileName)
|
||||
// console.log(componentName)
|
||||
|
||||
let componentName = upperFirst(camelCase(filePath.replace(/\.\w+$/, '')))
|
||||
|
||||
if (componentName.endsWith('Index')) componentName = componentName.replace(/Index$/, '')
|
||||
|
||||
app.component(componentName, componentConfig.default || componentConfig)
|
||||
app.component(componentName, module.default)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
<script>
|
||||
import { ref } from '@common/utils/vueTools'
|
||||
import { setting, themeList } from '@lyric/store/state'
|
||||
import { setting } from '@lyric/store/state'
|
||||
import { updateSetting } from '@lyric/store/action'
|
||||
|
||||
export default {
|
||||
@@ -105,7 +105,6 @@ export default {
|
||||
}
|
||||
return {
|
||||
setting,
|
||||
themeList,
|
||||
isShowThemeList,
|
||||
|
||||
handleClose,
|
||||
|
||||
@@ -198,7 +198,7 @@ export default {
|
||||
// // -webkit-text-fill-color: #fff;
|
||||
// // -webkit-text-stroke: thin #124628;
|
||||
// }
|
||||
.lyric-space {
|
||||
.lyricSpace {
|
||||
height: 80%;
|
||||
}
|
||||
// .lyric-text {
|
||||
@@ -224,7 +224,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
.lrc-active-zoom {
|
||||
.lrcActiveZoom {
|
||||
:global {
|
||||
.line-content {
|
||||
&.active {
|
||||
@@ -246,21 +246,21 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
.font-weight-font {
|
||||
.fontWeightFont {
|
||||
:global {
|
||||
.font-mode > .line {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
.font-weight-line {
|
||||
.fontWeightLine {
|
||||
:global {
|
||||
.line-mode > .line {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
.font-weight-extended {
|
||||
.fontWeightExtended {
|
||||
:global {
|
||||
.extended {
|
||||
font-weight: bold;
|
||||
|
||||
@@ -196,7 +196,7 @@ export default {
|
||||
// // -webkit-text-fill-color: #fff;
|
||||
// // -webkit-text-stroke: thin #124628;
|
||||
// }
|
||||
.lyric-space {
|
||||
.lyricSpace {
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -223,7 +223,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
.lrc-active-zoom {
|
||||
.lrcActiveZoom {
|
||||
:global {
|
||||
.line-content {
|
||||
&.active {
|
||||
@@ -245,21 +245,21 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
.font-weight-font {
|
||||
.fontWeightFont {
|
||||
:global {
|
||||
.font-mode > .line {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
.font-weight-line {
|
||||
.fontWeightLine {
|
||||
:global {
|
||||
.line-mode > .line {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
.font-weight-extended {
|
||||
.fontWeightExtended {
|
||||
:global {
|
||||
.extended {
|
||||
font-weight: bold;
|
||||
|
||||
@@ -27,5 +27,6 @@
|
||||
}
|
||||
if (/theme=(.+)(#|$)/.test(window.location.search)) applyThemeColor(RegExp.$1)
|
||||
</script>
|
||||
<script type="module" src="/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -6,7 +6,7 @@ import mountComponents from './components'
|
||||
|
||||
import App from './App.vue'
|
||||
|
||||
import '@/common/error'
|
||||
import '@root/common/error'
|
||||
import { getSetting, onMainWindowInited, onSettingChanged, sendConnectMainWindowEvent } from './utils/ipc'
|
||||
import { initSetting, mergeSetting } from './store/action'
|
||||
import { init as initMainWindowChannel } from './core/mainWindowChannel'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { I18n } from '@/lang'
|
||||
import { createI18n, i18nPlugin, useI18n } from '@/lang'
|
||||
import type { I18n } from '@root/lang'
|
||||
import { createI18n, i18nPlugin, useI18n } from '@root/lang'
|
||||
|
||||
window.i18n = createI18n()
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"isolatedModules": true,
|
||||
"moduleResolution": "nodenext",
|
||||
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
"@common/*": ["common/*"],
|
||||
// "@renderer/*": ["renderer/*"],
|
||||
"@lyric/*": ["renderer-lyric/*"],
|
||||
"@static/*": ["static/*"],
|
||||
"@/*": ["./*"],
|
||||
"@root/*": ["./*"],
|
||||
},
|
||||
"typeRoots": [ /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
"./types"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/* eslint-env node */
|
||||
const { base, typescript, vue } = require('../../.eslintrc.base.cjs')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
...base,
|
||||
overrides: [
|
||||
vue,
|
||||
{
|
||||
...typescript,
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
},
|
||||
],
|
||||
ignorePatterns: [
|
||||
'vendors',
|
||||
],
|
||||
}
|
||||
@@ -51,7 +51,7 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:model-value', 'change'],
|
||||
emits: ['update:modelValue', 'change'],
|
||||
data() {
|
||||
return {
|
||||
checked: false,
|
||||
@@ -80,7 +80,7 @@ export default {
|
||||
modelValue = checked
|
||||
} else modelValue = checked ? this.value : ''
|
||||
}
|
||||
this.$emit('update:model-value', modelValue)
|
||||
this.$emit('update:modelValue', modelValue)
|
||||
this.$emit('change', modelValue)
|
||||
},
|
||||
setValue(value) {
|
||||
|
||||
@@ -52,7 +52,7 @@ export default {
|
||||
// default: true,
|
||||
// },
|
||||
},
|
||||
emits: ['update:model-value', 'submit', 'change'],
|
||||
emits: ['update:modelValue', 'submit', 'change'],
|
||||
methods: {
|
||||
handleInput(event) {
|
||||
let value = event.target.value
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
value = value.trim()
|
||||
event.target.value = value
|
||||
}
|
||||
this.$emit('update:model-value', value)
|
||||
this.$emit('update:modelValue', value)
|
||||
},
|
||||
focus() {
|
||||
this.$refs.dom_input.focus()
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
// if (dom_input.selectionStart == dom_input.selectionEnd) {
|
||||
const value = text.substring(0, dom_input.selectionStart) + str + text.substring(dom_input.selectionEnd, text.length)
|
||||
event.target.value = value
|
||||
this.$emit('update:model-value', value)
|
||||
this.$emit('update:modelValue', value)
|
||||
// } else {
|
||||
// clipboardWriteText(text.substring(dom_input.selectionStart, dom_input.selectionEnd))
|
||||
// }
|
||||
|
||||
@@ -48,13 +48,13 @@ export default {
|
||||
default: 'name',
|
||||
},
|
||||
},
|
||||
emits: ['update:model-value', 'menu-click'],
|
||||
emits: ['update:modelValue', 'menu-click'],
|
||||
setup(props, { emit }) {
|
||||
const visible = computed(() => props.modelValue)
|
||||
const location = computed(() => props.xy)
|
||||
|
||||
const onHide = () => {
|
||||
emit('update:model-value', false)
|
||||
emit('update:modelValue', false)
|
||||
menuClick(null)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
emits: ['update:model-value', 'change'],
|
||||
emits: ['update:modelValue', 'change'],
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
@@ -84,7 +84,7 @@ export default {
|
||||
handleClick(item) {
|
||||
// console.log(this.modelValue)
|
||||
if (item === this.modelValue) return
|
||||
this.$emit('update:model-value', this.itemKey ? item[this.itemKey] : item)
|
||||
this.$emit('update:modelValue', this.itemKey ? item[this.itemKey] : item)
|
||||
this.$emit('change', item)
|
||||
},
|
||||
handleShow() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div :class="[$style.sliderContent, { [$style.disabled]: disabled }, className]">
|
||||
<div :class="[$style.slider ]">
|
||||
<div :class="[$style.slider]">
|
||||
<div ref="dom_sliderBar" :class="$style.sliderBar" :style="{ transform: `scaleX(${(value - min) / (max - min) || 0})` }" />
|
||||
</div>
|
||||
<div :class="$style.sliderMask" @mousedown="handleSliderMsDown" />
|
||||
@@ -86,7 +86,7 @@ export default {
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.slider-content {
|
||||
.sliderContent {
|
||||
flex: none;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
@@ -125,7 +125,7 @@ export default {
|
||||
// opacity: .5;
|
||||
// }
|
||||
|
||||
.slider-bar {
|
||||
.sliderBar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<ul :class="[$style.list, $style[align]]" role="tablist">
|
||||
<li
|
||||
v-for="item in list" :key="item[itemKey]" :class="[$style.listItem, {[$style.active]: modelValue == item[itemKey]}]" tabindex="-1"
|
||||
role="tab" :aria-label="item[itemLabel]" ignore-tip :aria-selected="modelValue == item[itemKey]"
|
||||
@click="handleToggle(item[itemKey])"
|
||||
v-for="item in list"
|
||||
:key="item[itemKey]" :class="[$style.listItem, {[$style.active]: modelValue == item[itemKey]}]" tabindex="-1" role="tab"
|
||||
:aria-label="item[itemLabel]" ignore-tip :aria-selected="modelValue == item[itemKey]" @click="handleToggle(item[itemKey])"
|
||||
>
|
||||
<span :class="$style.label">{{ item[itemLabel] }}</span>
|
||||
</li>
|
||||
@@ -37,11 +37,11 @@ export default {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
emits: ['update:model-value', 'change'],
|
||||
emits: ['update:modelValue', 'change'],
|
||||
setup(props, { emit }) {
|
||||
const handleToggle = id => {
|
||||
if (id == props.modelValue) return
|
||||
emit('update:model-value', id)
|
||||
emit('update:modelValue', id)
|
||||
emit('change', id)
|
||||
}
|
||||
|
||||
|
||||
@@ -54,19 +54,19 @@
|
||||
|
||||
// // div.list-item(@click="handleListItemClick($event, index)" @contextmenu="handleListItemRightClick($event, index)"
|
||||
// // :class="[{ selected: rightClickSelectedIndex == index }, { active: selectedList.includes(item) }]")
|
||||
// // div.list-item-cell.nobreak.center(:style="{ width: rowWidth.r1 }" style="padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
|
||||
// // div.list-item-cell.nobreak.center(:style="{ width: rowWidth.r1 }" style="padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{ index + 1 }}
|
||||
// // div.list-item-cell.auto(:style="{ width: rowWidth.r2 }" :aria-label="item.name + (item.meta._qualitys.flac32bit ? ` - ${$t('tag__lossless_24bit')}` : (item.meta._qualitys.ape || item.meta._qualitys.flac || item.meta._qualitys.wav) ? ` - ${$t('tag__lossless')}` : item.meta._qualitys['320k'] ? ` - ${$t('tag__high_quality')}` : '') + (sourceTag ? ` - ${item.source}` : '')")
|
||||
// // span.select {{item.name}}
|
||||
// // span.badge.badge-theme-primary(:class="[$style.labelQuality, $style.noSelect]" v-if="item.meta._qualitys.flac32bit") {{$t('tag__lossless_24bit')}}
|
||||
// // span.badge.badge-theme-primary(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item.meta._qualitys.ape || item.meta._qualitys.flac || item.meta._qualitys.wav") {{$t('tag__lossless')}}
|
||||
// // span.badge.badge-theme-secondary(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item.meta._qualitys['320k']") {{$t('tag__high_quality')}}
|
||||
// // span.badge.badge-theme-tertiary(:class="[$style.labelQuality, $style.noSelect]" v-if="sourceTag") {{item.source}}
|
||||
// // span.select {{ item.name }}
|
||||
// // span.badge.badge-theme-primary(:class="[$style.labelQuality, $style.noSelect]" v-if="item.meta._qualitys.flac32bit") {{ $t('tag__lossless_24bit') }}
|
||||
// // span.badge.badge-theme-primary(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item.meta._qualitys.ape || item.meta._qualitys.flac || item.meta._qualitys.wav") {{ $t('tag__lossless') }}
|
||||
// // span.badge.badge-theme-secondary(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item.meta._qualitys['320k']") {{ $t('tag__high_quality') }}
|
||||
// // span.badge.badge-theme-tertiary(:class="[$style.labelQuality, $style.noSelect]" v-if="sourceTag") {{ item.source }}
|
||||
// // div.list-item-cell(:style="{ width: rowWidth.r3 }" :aria-label="item.singer")
|
||||
// // span.select {{item.singer}}
|
||||
// // span.select {{ item.singer }}
|
||||
// // div.list-item-cell(:style="{ width: rowWidth.r4 }" :aria-label="item.albumName")
|
||||
// // span.select {{item.meta.albumName}}
|
||||
// // span.select {{ item.meta.albumName }}
|
||||
// // div.list-item-cell(:style="{ width: rowWidth.r5 }")
|
||||
// // span(:class="[$style.time, $style.noSelect]") {{item.meta.interval || '--/--'}}
|
||||
// // span(:class="[$style.time, $style.noSelect]") {{ item.meta.interval || '--/--' }}
|
||||
// // div.list-item-cell(:style="{ width: rowWidth.r6 }" style="padding-left: 0; padding-right: 0;")
|
||||
// // material-list-buttons(:index="index" :class="$style.btns"
|
||||
// // :remove-btn="false" @btn-click="handleListBtnClick"
|
||||
|
||||
@@ -22,7 +22,7 @@ import { watch, ref, onBeforeUnmount } from '@common/utils/vueTools'
|
||||
import { defaultList, loveList, userLists } from '@renderer/store/list/state'
|
||||
import { addListMusics, moveListMusics, createUserList, getMusicExistListIds } from '@renderer/store/list/action'
|
||||
import useKeyDown from '@renderer/utils/compositions/useKeyDown'
|
||||
import { useI18n } from '@/lang'
|
||||
import { useI18n } from '@root/lang'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -207,7 +207,7 @@ export default {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.btn-content {
|
||||
.btnContent {
|
||||
flex: auto;
|
||||
max-height: 100%;
|
||||
padding-right: 15px;
|
||||
|
||||
@@ -21,7 +21,7 @@ import { computed } from '@common/utils/vueTools'
|
||||
import { defaultList, loveList, userLists } from '@renderer/store/list/state'
|
||||
import { addListMusics, moveListMusics, createUserList } from '@renderer/store/list/action'
|
||||
import useKeyDown from '@renderer/utils/compositions/useKeyDown'
|
||||
import { useI18n } from '@/lang'
|
||||
import { useI18n } from '@root/lang'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -164,7 +164,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.btn-content {
|
||||
.btnContent {
|
||||
flex: auto;
|
||||
max-height: 100%;
|
||||
padding-right: 15px;
|
||||
|
||||
@@ -104,7 +104,7 @@ export default {
|
||||
position: relative;
|
||||
border-radius: 40px;
|
||||
}
|
||||
.progress-mask {
|
||||
.progressMask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
@@ -112,7 +112,7 @@ export default {
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.progress-bar {
|
||||
.progressBar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
@@ -120,22 +120,22 @@ export default {
|
||||
height: 100%;
|
||||
transform-origin: 0;
|
||||
}
|
||||
.progress-bar1 {
|
||||
.progressBar1 {
|
||||
background-color: var(--color-primary-light-100-alpha-600);
|
||||
}
|
||||
|
||||
.progress-bar2 {
|
||||
.progressBar2 {
|
||||
background-color: var(--color-primary-light-100-alpha-400);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.progress-bar3 {
|
||||
.progressBar3 {
|
||||
background-color: var(--color-primary-light-100-alpha-200);
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.bar-transition {
|
||||
.barTransition {
|
||||
transition-property: transform;
|
||||
transition-timing-function: ease-out;
|
||||
transition-duration: 0.2s;
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<template>
|
||||
<base-btn min :class="[$style.newPreset, {[$style.editing]: isEditing}]" :aria-label="$t('player__sound_effect_biquad_filter_save_btn')" @click="handleEditing($event)">
|
||||
<svg-icon name="plus" />
|
||||
<base-input ref="input" :class="$style.newPresetInput" :value="newPresetName" :placeholder="$t('player__sound_effect_biquad_filter_save_input')" @keyup.enter="handleSave($event)" @blur="handleSave($event)" />
|
||||
</base-btn>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, nextTick } from '@common/utils/vueTools'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { saveUserPitchShifterPreset } from '@renderer/store/soundEffect'
|
||||
|
||||
const isEditing = ref(false)
|
||||
const input = ref(false)
|
||||
const newPresetName = ref('')
|
||||
|
||||
const handleEditing = () => {
|
||||
if (isEditing.value) return
|
||||
// if (!this.newPresetName) this.newPresetName = this.listName
|
||||
isEditing.value = true
|
||||
nextTick(() => {
|
||||
input.value.$el.focus()
|
||||
})
|
||||
}
|
||||
|
||||
const handleSave = (event) => {
|
||||
let name = event.target.value.trim()
|
||||
newPresetName.value = event.target.value = ''
|
||||
isEditing.value = false
|
||||
if (!name) return
|
||||
if (name.length > 20) name = name.substring(0, 20)
|
||||
saveUserPitchShifterPreset({
|
||||
id: Date.now().toString(),
|
||||
name,
|
||||
playbackRate: appSetting['player.soundEffect.pitchShifter.playbackRate'],
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.newPreset {
|
||||
position: relative;
|
||||
border: 1px dashed var(--color-primary-font-hover);
|
||||
// background-color: var(--color-main-background);
|
||||
color: var(--color-primary-font-hover);
|
||||
opacity: .7;
|
||||
height: 22px;
|
||||
|
||||
&.editing {
|
||||
opacity: 1;
|
||||
width: 90px;
|
||||
|
||||
svg {
|
||||
display: none;
|
||||
}
|
||||
.newPresetInput {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
:global {
|
||||
.svg-icon {
|
||||
vertical-align: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.newPresetInput {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// line-height: 16px;
|
||||
background: none !important;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
font-family: inherit;
|
||||
box-sizing: border-box;
|
||||
padding: 0 3px;
|
||||
border-radius: 0;
|
||||
display: none;
|
||||
&::placeholder {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -38,7 +38,7 @@
|
||||
import { ref, onMounted, computed } from '@common/utils/vueTools'
|
||||
import { appSetting, updateSetting } from '@renderer/store/setting'
|
||||
import { convolutions } from '@renderer/plugins/player'
|
||||
import AddConvolutionPresetBtn from './AddConvolutionPresetBtn'
|
||||
import AddConvolutionPresetBtn from './AddConvolutionPresetBtn.vue'
|
||||
import { getUserConvolutionPresetList, removeUserConvolutionPreset } from '@renderer/store/soundEffect'
|
||||
|
||||
const updateConvolution = val => {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
import { onMounted, ref } from '@common/utils/vueTools'
|
||||
import { freqs, freqsPreset } from '@renderer/plugins/player'
|
||||
import { appSetting, updateSetting } from '@renderer/store/setting'
|
||||
import AddEQPresetBtn from './AddEQPresetBtn'
|
||||
import AddEQPresetBtn from './AddEQPresetBtn.vue'
|
||||
import { getUserEQPresetList, removeUserEQPreset } from '@renderer/store/soundEffect'
|
||||
|
||||
const labels = freqs.map(num => num < 1000 ? num : `${num / 1000}k`)
|
||||
|
||||
@@ -29,9 +29,9 @@ import { ref } from '@common/utils/vueTools'
|
||||
// import { saveVolumeIsMute } from '@renderer/store/setting'
|
||||
// import { volume, isMute } from '@renderer/store/player/volume'
|
||||
// import fs from 'node:fs'
|
||||
import BiquadFilter from './BiquadFilter'
|
||||
import AudioPanner from './AudioPanner'
|
||||
import AudioConvolution from './AudioConvolution'
|
||||
import BiquadFilter from './BiquadFilter.vue'
|
||||
import AudioPanner from './AudioPanner.vue'
|
||||
import AudioConvolution from './AudioConvolution.vue'
|
||||
import PitchShifter from './PitchShifter.vue'
|
||||
|
||||
defineProps({
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
import upperFirst from 'lodash/upperFirst'
|
||||
import camelCase from 'lodash/camelCase'
|
||||
|
||||
const requireComponent = require.context('./', true, /\.vue$/)
|
||||
|
||||
const requireComponent = import.meta.glob(['./**/*.vue', '!./**/components/**/*.vue'], { eager: true })
|
||||
const vueFileRxp = /\.vue$/
|
||||
const vueIndexFileRxp = /\/index\.vue$/
|
||||
|
||||
|
||||
export default app => {
|
||||
requireComponent.keys().forEach(fileName => {
|
||||
const filePath = fileName.replace(/^\.\//, '')
|
||||
Object.entries(requireComponent).forEach(([path, module]) => {
|
||||
path = path.replace(/^\.\//, '')
|
||||
let fileName = vueIndexFileRxp.test(path)
|
||||
? path.replace(vueIndexFileRxp, '')
|
||||
: path.replace(vueFileRxp, '')
|
||||
|
||||
if (!filePath.split('/').every((path, index, arr) => {
|
||||
const char = path.charAt(0)
|
||||
return vueFileRxp.test(path) || char.toUpperCase() !== char || arr[index + 1] == 'index.vue'
|
||||
})) return
|
||||
let componentName = upperFirst(camelCase(fileName))
|
||||
|
||||
const componentConfig = requireComponent(fileName)
|
||||
// console.log(componentName)
|
||||
|
||||
let componentName = upperFirst(camelCase(filePath.replace(/\.\w+$/, '')))
|
||||
|
||||
if (componentName.endsWith('Index')) componentName = componentName.replace(/Index$/, '')
|
||||
|
||||
app.component(componentName, componentConfig.default || componentConfig)
|
||||
app.component(componentName, module.default)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
import { isFullscreen } from '@renderer/store'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
|
||||
import ControlBtns from './ControlBtns'
|
||||
import NavBar from './NavBar'
|
||||
import ControlBtns from './ControlBtns.vue'
|
||||
import NavBar from './NavBar.vue'
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
import { computed } from '@common/utils/vueTools'
|
||||
import { useRouter } from '@common/utils/vueRouter'
|
||||
import { clipboardWriteText } from '@common/utils/electron'
|
||||
import ControlBtns from './ControlBtns'
|
||||
import ControlBtns from './ControlBtns.vue'
|
||||
// import PlayProgress from './PlayProgress'
|
||||
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
|
||||
// import { lyric } from '@renderer/core/share/lyric'
|
||||
@@ -284,7 +284,7 @@ export default {
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.play-btn {
|
||||
.playBtn {
|
||||
flex: none;
|
||||
height: 52%;
|
||||
// margin-top: -2px;
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
import { computed } from '@common/utils/vueTools'
|
||||
import { useRouter } from '@common/utils/vueRouter'
|
||||
import { clipboardWriteText } from '@common/utils/electron'
|
||||
import ControlBtns from './ControlBtns'
|
||||
import ControlBtns from './ControlBtns.vue'
|
||||
// import PlayProgress from './PlayProgress'
|
||||
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
|
||||
// import { lyric } from '@renderer/core/share/lyric'
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
import { computed } from '@common/utils/vueTools'
|
||||
import { useRouter } from '@common/utils/vueRouter'
|
||||
import { clipboardWriteText } from '@common/utils/electron'
|
||||
import ControlBtns from './ControlBtns'
|
||||
import PlayProgress from './PlayProgress'
|
||||
import ControlBtns from './ControlBtns.vue'
|
||||
import PlayProgress from './PlayProgress.vue'
|
||||
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
|
||||
// import { lyric } from '@renderer/core/share/lyric'
|
||||
import {
|
||||
@@ -314,7 +314,7 @@ export default {
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.play-btn {
|
||||
.playBtn {
|
||||
flex: none;
|
||||
height: 52%;
|
||||
// margin-top: -2px;
|
||||
|
||||
@@ -56,7 +56,7 @@ import {
|
||||
} from '@renderer/store/player/action'
|
||||
import { onMounted, onBeforeUnmount, computed, reactive, ref, nextTick, watch } from '@common/utils/vueTools'
|
||||
import useLyric from '@renderer/utils/compositions/useLyric'
|
||||
import LyricMenu from './components/LyricMenu'
|
||||
import LyricMenu from './components/LyricMenu.vue'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { setLyricOffset } from '@renderer/core/lyric'
|
||||
|
||||
@@ -263,7 +263,7 @@ export default {
|
||||
// font-size: 1.2em;
|
||||
// }
|
||||
}
|
||||
.lrc-active-zoom {
|
||||
.lrcActiveZoom {
|
||||
:global {
|
||||
.line-content {
|
||||
&.active {
|
||||
@@ -343,12 +343,12 @@ export default {
|
||||
.lyricSelectlineExtended {
|
||||
font-size: 14px;
|
||||
}
|
||||
.lrc-active {
|
||||
.lrcActive {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.lyric-space {
|
||||
.lyricSpace {
|
||||
height: 70%;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ import { playNext, playPrev, togglePlay } from '@renderer/core/player'
|
||||
import { status, isPlay } from '@renderer/store/player/state'
|
||||
import usePlayProgress from '@renderer/utils/compositions/usePlayProgress'
|
||||
|
||||
import ControlBtns from './components/ControlBtns'
|
||||
import ControlBtns from './components/ControlBtns.vue'
|
||||
|
||||
const {
|
||||
nowPlayTimeStr,
|
||||
@@ -71,13 +71,13 @@ const {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
.progressContainer {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
.progress-content {
|
||||
.progressContent {
|
||||
position: relative;
|
||||
height: 16px;
|
||||
padding: 5px 0;
|
||||
@@ -87,12 +87,12 @@ const {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bar-transition {
|
||||
.barTransition {
|
||||
transition-property: transform;
|
||||
transition-timing-function: ease-out;
|
||||
transition-duration: 0.2s;
|
||||
}
|
||||
.time-label {
|
||||
.timeLabel {
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
display: flex;
|
||||
@@ -104,7 +104,7 @@ const {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.play-control {
|
||||
.playControl {
|
||||
flex: none;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
@@ -113,7 +113,7 @@ const {
|
||||
padding: 0 25px;
|
||||
color: var(--color-button-font);
|
||||
}
|
||||
.play-btn {
|
||||
.playBtn {
|
||||
height: 40%;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
@@ -125,7 +125,7 @@ const {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
|
||||
+.play-btn {
|
||||
+.playBtn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
svg {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<teleport to="#root">
|
||||
<div ref="dom_menu" :class="$style.container" :style="menuStyles" :aria-hidden="!modelValue">
|
||||
<!-- <div :class="$style.group">
|
||||
<div :class="$style.title">{{$t('lyric_menu__align')}}</div>
|
||||
<div :class="$style.title">{{ $t('lyric_menu__align') }}</div>
|
||||
<div :class="$style.subGroup">
|
||||
<div :class="[$style.btn, { [$style.active]: appSetting['playDetail.style.align'] == 'left' }]" role="button" @click="setFontAlign('left')" ignore-tip :aria-label="$t('lyric_menu__align_left')">{{$t('lyric_menu__align_left')}}</div>
|
||||
<div :class="[$style.btn, { [$style.active]: appSetting['playDetail.style.align'] == 'center' }]" role="button" @click="setFontAlign('center')" ignore-tip :aria-label="$t('lyric_menu__align_center')">{{$t('lyric_menu__align_center')}}</div>
|
||||
<div :class="[$style.btn, { [$style.active]: appSetting['playDetail.style.align'] == 'left' }]" role="button" @click="setFontAlign('left')" ignore-tip :aria-label="$t('lyric_menu__align_left')">{{ $t('lyric_menu__align_left') }}</div>
|
||||
<div :class="[$style.btn, { [$style.active]: appSetting['playDetail.style.align'] == 'center' }]" role="button" @click="setFontAlign('center')" ignore-tip :aria-label="$t('lyric_menu__align_center')">{{ $t('lyric_menu__align_center') }}</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div :class="$style.group">
|
||||
@@ -83,7 +83,7 @@ export default {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['updateLyric', 'update:model-value'],
|
||||
emits: ['updateLyric', 'update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
// const appSetting = useRefGetter('appSetting')
|
||||
// const playDetailSetting = useRefGetter('playDetailSetting')
|
||||
@@ -98,7 +98,7 @@ export default {
|
||||
const location = computed(() => props.xy)
|
||||
|
||||
const onHide = () => {
|
||||
emit('update:model-value', false)
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
const setFontAlign = val => {
|
||||
|
||||
@@ -11,11 +11,11 @@ div(:class="$style.container")
|
||||
div.select(:class="$style.name") {{ item.userName }}
|
||||
div(:class="$style.metaInfo")
|
||||
time(v-if="item.timeStr" :class="$style.label") {{ timeFormat(item.timeStr) }}
|
||||
div(v-if="item.location" :class="$style.label") {{$t('comment__location', { location: item.location })}}
|
||||
div(v-if="item.location" :class="$style.label") {{ $t('comment__location', { location: item.location }) }}
|
||||
div(v-if="item.likedCount != null" :class="$style.likes")
|
||||
svg(:class="$style.likesIcon" version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" space="preserve")
|
||||
use(xlink:href="#icon-thumbs-up")
|
||||
| {{item.likedCount}}
|
||||
| {{ item.likedCount }}
|
||||
p.select(:class="$style.comment_text") {{ item.text }}
|
||||
div(v-if="item.images?.length" :class="$style.comment_images")
|
||||
img(v-for="(url, index) in item.images" :key="index" :src="url" loading="lazy" decoding="async")
|
||||
|
||||
@@ -33,14 +33,14 @@ div.comment(ref="dom_container" :class="$style.comment")
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="newComment.total" :btn-length="5" :limit="newComment.limit" :page="newComment.page" @btn-click="handleToggleCommentPage")
|
||||
div(v-else :class="$style.unavailable")
|
||||
p {{$t('comment__unavailable')}}
|
||||
p {{ $t('comment__unavailable') }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { toOldMusicInfo } from '@renderer/utils'
|
||||
import music from '@renderer/utils/musicSdk'
|
||||
import CommentFloor from './CommentFloor'
|
||||
import CommentFloor from './CommentFloor.vue'
|
||||
|
||||
export default {
|
||||
name: 'MusicComment',
|
||||
|
||||
@@ -44,7 +44,7 @@ transition(enter-active-class="animated slideInRight" leave-active-class="animat
|
||||
div.description(:class="$style.description")
|
||||
p {{ $t('player__music_name') }}{{ musicInfo.name }}
|
||||
p {{ $t('player__music_singer') }}{{ musicInfo.singer }}
|
||||
p(v-if="musicInfo.album") {{ $t('player__music_album') }}{{musicInfo.album}}
|
||||
p(v-if="musicInfo.album") {{ $t('player__music_album') }}{{ musicInfo.album }}
|
||||
|
||||
transition(enter-active-class="animated fadeIn" leave-active-class="animated fadeOut")
|
||||
LyricPlayer(v-if="visibled")
|
||||
@@ -70,9 +70,9 @@ import {
|
||||
setShowPlayComment,
|
||||
setShowPlayLrcSelectContentLrc,
|
||||
} from '@renderer/store/player/action'
|
||||
import LyricPlayer from './LyricPlayer'
|
||||
import PlayBar from './PlayBar'
|
||||
import MusicComment from './components/MusicComment'
|
||||
import LyricPlayer from './LyricPlayer.vue'
|
||||
import PlayBar from './PlayBar.vue'
|
||||
import MusicComment from './components/MusicComment/index.vue'
|
||||
import { registerAutoHideMounse, unregisterAutoHideMounse } from './autoHideMounse'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { closeWindow, maxWindow, minWindow, setFullScreen } from '@renderer/utils/ipc'
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<script setup>
|
||||
import { isFullscreen } from '@renderer/store'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import ControlBtns from './ControlBtns'
|
||||
import SearchInput from './SearchInput'
|
||||
import ControlBtns from './ControlBtns.vue'
|
||||
import SearchInput from './SearchInput.vue'
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ export default {
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
.song-list {
|
||||
.songList {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
:placeholder="placeholder"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@input="$emit('update:model-value', text)"
|
||||
@input="$emit('update:modelValue', text)"
|
||||
@change="sendEvent('change')"
|
||||
@keyup.enter="handleSearch"
|
||||
@keydown.arrow-down.arrow-up.prevent
|
||||
@@ -82,7 +82,7 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:model-value', 'event'],
|
||||
emits: ['update:modelValue', 'event'],
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
@@ -182,11 +182,11 @@ export default {
|
||||
str = str.replace(/\s+/g, ' ')
|
||||
let dom_input = this.$refs.dom_input
|
||||
this.text = this.text.substring(0, dom_input.selectionStart) + str + this.text.substring(dom_input.selectionEnd, this.text.length)
|
||||
this.$emit('update:model-value', this.text)
|
||||
this.$emit('update:modelValue', this.text)
|
||||
},
|
||||
handleClearList() {
|
||||
this.text = ''
|
||||
this.$emit('update:model-value', this.text)
|
||||
this.$emit('update:modelValue', this.text)
|
||||
this.sendEvent('submit')
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,12 +3,13 @@ import { getDuration, getPlaybackRate, getCurrentTime } from '@renderer/plugins/
|
||||
import { isPlay, musicInfo, playMusicInfo } from '@renderer/store/player/state'
|
||||
import { playProgress } from '@renderer/store/player/playProgress'
|
||||
import { pause, play, playNext, playPrev, stop } from '@renderer/core/player'
|
||||
import silenceAudio from '@renderer/assets/medias/Silence02s.mp3'
|
||||
|
||||
export default () => {
|
||||
// 创建一个空白音频以保持对 Media Session 的注册
|
||||
const emptyAudio = new Audio()
|
||||
emptyAudio.autoplay = false
|
||||
emptyAudio.src = require('@renderer/assets/medias/Silence02s.mp3')
|
||||
emptyAudio.src = silenceAudio
|
||||
emptyAudio.controls = false
|
||||
emptyAudio.preload = 'auto'
|
||||
emptyAudio.onplaying = () => {
|
||||
|
||||
@@ -16,40 +16,41 @@ import {
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
|
||||
const cache = new Map<string, AudioBuffer>()
|
||||
const loadBuffer = async(name: string) => new Promise<AudioBuffer>((resolve, reject) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const path = require('@renderer/assets/medias/filters/' + name) as string
|
||||
if (cache.has(path)) {
|
||||
resolve(cache.get(path) as AudioBuffer)
|
||||
return
|
||||
}
|
||||
// Load buffer asynchronously
|
||||
let request = new XMLHttpRequest()
|
||||
request.open('GET', path, true)
|
||||
request.responseType = 'arraybuffer'
|
||||
const loadBuffer = async(name: string) => {
|
||||
const path = (await import(`../../../assets/medias/filters/${name.replace('.wav', '')}.wav`)).default as string
|
||||
return new Promise<AudioBuffer>((resolve, reject) => {
|
||||
if (cache.has(path)) {
|
||||
resolve(cache.get(path) as AudioBuffer)
|
||||
return
|
||||
}
|
||||
// Load buffer asynchronously
|
||||
let request = new XMLHttpRequest()
|
||||
request.open('GET', path, true)
|
||||
request.responseType = 'arraybuffer'
|
||||
|
||||
request.onload = function() {
|
||||
// Asynchronously decode the audio file data in request.response
|
||||
void getAudioContext().decodeAudioData(request.response, (buffer) => {
|
||||
if (!buffer) {
|
||||
reject(new Error('error decoding file data: ' + path))
|
||||
return
|
||||
}
|
||||
cache.set(path, buffer)
|
||||
resolve(buffer)
|
||||
},
|
||||
function(error) {
|
||||
reject(error)
|
||||
console.error('decodeAudioData error', error)
|
||||
})
|
||||
}
|
||||
request.onload = function() {
|
||||
// Asynchronously decode the audio file data in request.response
|
||||
void getAudioContext().decodeAudioData(request.response, (buffer) => {
|
||||
if (!buffer) {
|
||||
reject(new Error('error decoding file data: ' + path))
|
||||
return
|
||||
}
|
||||
cache.set(path, buffer)
|
||||
resolve(buffer)
|
||||
},
|
||||
function(error) {
|
||||
reject(error)
|
||||
console.error('decodeAudioData error', error)
|
||||
})
|
||||
}
|
||||
|
||||
request.onerror = function() {
|
||||
reject(new Error('XHR error'))
|
||||
}
|
||||
request.onerror = function() {
|
||||
reject(new Error('XHR error'))
|
||||
}
|
||||
|
||||
request.send()
|
||||
})
|
||||
request.send()
|
||||
})
|
||||
}
|
||||
|
||||
export default () => {
|
||||
// console.log(appSetting['player.soundEffect.panner.enable'])
|
||||
|
||||
@@ -2,14 +2,14 @@ import { watch } from '@common/utils/vueTools'
|
||||
import { isFullscreen, proxy, sync, windowSizeList } from '@renderer/store'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { sendSyncAction, setTaskBarProgress, setWindowSize } from '@renderer/utils/ipc'
|
||||
import { setLanguage } from '@/lang'
|
||||
import { setLanguage } from '@root/lang'
|
||||
import { setUserApi } from '../apiSource'
|
||||
// import { applyTheme, getThemes } from '@renderer/store/utils'
|
||||
|
||||
|
||||
export default () => {
|
||||
watch(() => appSetting['common.windowSizeId'], (index) => {
|
||||
let info = index == null ? windowSizeList[2] : windowSizeList[index]
|
||||
const info = index == null ? windowSizeList[2] : windowSizeList[index]
|
||||
setWindowSize(info.width, info.height)
|
||||
})
|
||||
watch(() => appSetting['common.fontSize'], (fontSize) => {
|
||||
|
||||
@@ -31,5 +31,6 @@
|
||||
}
|
||||
if (/theme=(.+)(#|$)/.test(window.location.search)) applyThemeColor(RegExp.$1)
|
||||
</script>
|
||||
<script type="module" src="/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import '@common/error'
|
||||
import 'virtual:svg-icons-register'
|
||||
import { createApp } from 'vue'
|
||||
|
||||
import './core/globalData'
|
||||
@@ -18,8 +19,8 @@ import router from './router'
|
||||
|
||||
|
||||
import { getSetting, updateSetting } from './utils/ipc'
|
||||
import { langList } from '@/lang'
|
||||
import type { I18n } from '@/lang/i18n'
|
||||
import { langList } from '@root/lang'
|
||||
import type { I18n } from '@root/lang/i18n'
|
||||
|
||||
import { initSetting } from './store/setting'
|
||||
// import { bubbleCursor } from './utils/cursor-effects/bubbleCursor'
|
||||
@@ -43,7 +44,7 @@ void getSetting().then(setting => {
|
||||
// Set language automatically
|
||||
if (!setting['common.langId'] || !window.i18n.availableLocales.includes(setting['common.langId'])) {
|
||||
let langId: I18n['locale'] | null = null
|
||||
let locale = window.navigator.language.toLocaleLowerCase() as I18n['locale']
|
||||
const locale = window.navigator.language.toLocaleLowerCase() as I18n['locale']
|
||||
if (window.i18n.availableLocales.includes(locale)) {
|
||||
langId = locale
|
||||
} else {
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Modal from '@renderer/components/material/Modal'
|
||||
import Btn from '@renderer/components/base/Btn'
|
||||
import Modal from '@renderer/components/material/Modal.vue'
|
||||
import Btn from '@renderer/components/base/Btn.vue'
|
||||
import { useI18n } from '@renderer/plugins/i18n'
|
||||
import { computed } from '@common/utils/vueTools'
|
||||
export default {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Dialog from './Dialog'
|
||||
import Dialog from './Dialog.vue'
|
||||
import { createApp } from 'vue'
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import SvgIcon from './SvgIcon'
|
||||
import SvgIcon from './SvgIcon.vue'
|
||||
|
||||
const req = require.context('@renderer/assets/svgs', false, /\.svg$/)
|
||||
const requireAll = requireContext => requireContext.keys().map(requireContext)
|
||||
requireAll(req)
|
||||
// const req = require.context('@renderer/assets/svgs', false, /\.svg$/)
|
||||
// const requireAll = requireContext => requireContext.keys().map(requireContext)
|
||||
// requireAll(req)
|
||||
|
||||
export default app => {
|
||||
app.component('svg-icon', SvgIcon)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { I18n } from '@/lang'
|
||||
import { createI18n, i18nPlugin, useI18n } from '@/lang'
|
||||
import type { I18n } from '@root/lang'
|
||||
import { createI18n, i18nPlugin, useI18n } from '@root/lang'
|
||||
|
||||
window.i18n = createI18n()
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import PhaseVocoder from './pitch-shifter/phase-vocoder.js?worker&url'
|
||||
|
||||
let audio: HTMLAudioElement | null = null
|
||||
let audioContext: AudioContext
|
||||
let mediaSource: MediaElementAudioSourceNode
|
||||
@@ -276,11 +278,7 @@ const loadPitchShifterNode = () => {
|
||||
pitchShifterNodeLoadStatus = 'loading'
|
||||
initAdvancedAudioFeatures()
|
||||
// source -> analyser -> biquadFilter -> audioWorklet(pitch shifter) -> [(convolver & convolverSource)->convolverDynamicsCompressor] -> panner -> gain
|
||||
void audioContext.audioWorklet.addModule(new URL(
|
||||
/* webpackChunkName: 'pitch_shifter.audioWorklet' */
|
||||
'./pitch-shifter/phase-vocoder.js',
|
||||
import.meta.url,
|
||||
)).then(() => {
|
||||
void audioContext.audioWorklet.addModule(PhaseVocoder).then(() => {
|
||||
console.log('pitch shifter audio worklet loaded')
|
||||
// https://github.com/olvb/phaze/issues/26#issuecomment-1574629971
|
||||
pitchShifterNode = new AudioWorkletNode(audioContext, 'phase-vocoder-processor', { outputChannelCount: [2] })
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
// import Vue from 'vue'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
import SearchView from './views/Search/index.vue'
|
||||
import SongListView from './views/songList/List/index.vue'
|
||||
import SongListDetailView from './views/songList/Detail/index.vue'
|
||||
import LeaderboardView from './views/Leaderboard/index.vue'
|
||||
import ListView from './views/List/index.vue'
|
||||
import DownloadView from './views/Download/index.vue'
|
||||
import SettingView from './views/Setting/index.vue'
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
@@ -9,7 +17,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/search',
|
||||
name: 'Search',
|
||||
component: require('./views/Search/index.vue').default,
|
||||
component: SearchView,
|
||||
meta: {
|
||||
name: 'Search',
|
||||
},
|
||||
@@ -17,7 +25,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/songList/list',
|
||||
name: 'SongList',
|
||||
component: require('./views/songList/List/index.vue').default,
|
||||
component: SongListView,
|
||||
meta: {
|
||||
name: 'SongList',
|
||||
},
|
||||
@@ -25,7 +33,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/songList/detail',
|
||||
name: 'SongListDetail',
|
||||
component: require('./views/songList/Detail/index.vue').default,
|
||||
component: SongListDetailView,
|
||||
meta: {
|
||||
name: 'SongList',
|
||||
},
|
||||
@@ -33,7 +41,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/leaderboard',
|
||||
name: 'Leaderboard',
|
||||
component: require('./views/Leaderboard/index.vue').default,
|
||||
component: LeaderboardView,
|
||||
meta: {
|
||||
name: 'Leaderboard',
|
||||
},
|
||||
@@ -41,7 +49,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/list',
|
||||
name: 'List',
|
||||
component: require('./views/List/index.vue').default,
|
||||
component: ListView,
|
||||
meta: {
|
||||
name: 'List',
|
||||
},
|
||||
@@ -49,7 +57,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/download',
|
||||
name: 'Download',
|
||||
component: require('./views/Download/index.vue').default,
|
||||
component: DownloadView,
|
||||
meta: {
|
||||
name: 'Download',
|
||||
},
|
||||
@@ -57,7 +65,7 @@ const router = createRouter({
|
||||
{
|
||||
path: '/setting',
|
||||
name: 'Setting',
|
||||
component: require('./views/Setting/index.vue').default,
|
||||
component: SettingView,
|
||||
meta: {
|
||||
name: 'Setting',
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ref, reactive, shallowRef, markRaw, computed, watch } from '@common/utils/vueTools'
|
||||
import { windowSizeList as configWindowSizeList } from '@common/config'
|
||||
import { appSetting } from './setting'
|
||||
import pkg from '../../../package.json'
|
||||
import { version } from '../../../package.json'
|
||||
import { type ProgressInfo } from 'electron-updater'
|
||||
import music from '@renderer/utils/musicSdk'
|
||||
process.versions.app = pkg.version
|
||||
process.versions.app = version
|
||||
|
||||
export const apiSource = ref<string | null>(null)
|
||||
export const proxy: {
|
||||
@@ -115,7 +115,7 @@ export const versionInfo = window.lxData.versionInfo = reactive<{
|
||||
status: LX.UpdateStatus
|
||||
downloadProgress: ProgressInfo | null
|
||||
}>({
|
||||
version: pkg.version,
|
||||
version,
|
||||
newVersion: null,
|
||||
showModal: false,
|
||||
reCheck: false,
|
||||
|
||||
@@ -2,22 +2,25 @@
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"isolatedModules": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "nodenext",
|
||||
"resolveJsonModule": true,
|
||||
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
"@common/*": ["common/*"],
|
||||
"@renderer/*": ["renderer/*"],
|
||||
// "@lyric/*": ["renderer-lyric/*"],
|
||||
"@static/*": ["static/*"],
|
||||
"@/*": ["./*"],
|
||||
"@root/*": ["./*"],
|
||||
},
|
||||
"typeRoots": [ /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
"renderer/types"
|
||||
"./types"
|
||||
],
|
||||
},
|
||||
// "vueCompilerOptions": {
|
||||
// "plugins": [
|
||||
// "@volar/vue-language-plugin-pug"
|
||||
// ]
|
||||
// }
|
||||
"vueCompilerOptions": {
|
||||
"plugins": [
|
||||
"@vue/language-plugin-pug"
|
||||
]
|
||||
}
|
||||
// "include": [
|
||||
// "./**/*.ts",
|
||||
// // "./**/*.js",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
||||
/// <reference types="vite/client" />
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type I18n } from '@/lang'
|
||||
import { type I18n } from '@root/lang'
|
||||
|
||||
declare module 'vue' {
|
||||
interface ComponentCustomProperties {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Support qualitys: 128k 320k flac wav
|
||||
|
||||
module.exports = [
|
||||
export default [
|
||||
{
|
||||
id: 'test',
|
||||
name: '测试接口',
|
||||
|
||||