This commit is contained in:
lyswhut
2022-10-29 11:36:35 +08:00
parent 66b965d9f7
commit e45cee6596
715 changed files with 57096 additions and 31771 deletions
+158
View File
@@ -0,0 +1,158 @@
import Database from 'better-sqlite3'
import path from 'path'
// import migrateData from './migrate'
let db: Database.Database
const initTables = (db: Database.Database) => {
db.exec(`
CREATE TABLE "db_info" (
"id" INTEGER NOT NULL UNIQUE,
"field_name" TEXT,
"field_value" TEXT,
PRIMARY KEY("id" AUTOINCREMENT)
)`)
db.prepare(`INSERT INTO "main"."db_info" ("field_name", "field_value") VALUES
('version', '1')
`).run()
db.exec(`
CREATE TABLE "my_list" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"source" TEXT,
"sourceListId" TEXT,
"position" INTEGER NOT NULL,
"locationUpdateTime" INTEGER,
PRIMARY KEY("id")
)`)
// const insert = db.prepare(`INSERT INTO "main"."my_list" ("id", "name", "source", "sourceListId", "locationUpdateTime") VALUES
// (@id, @name, @source, @sourceListId, @locationUpdateTime)`)
// db.transaction((cats) => {
// for (const cat of cats) insert.run(cat)
// })([
// { id: 'default', name: '默认列表', source: null, sourceListId: null, locationUpdateTime: null },
// { id: 'love', name: '收藏列表', source: null, sourceListId: null, locationUpdateTime: null },
// { id: 'temp', name: '临时列表', source: null, sourceListId: null, locationUpdateTime: null },
// ])
db.exec(`
CREATE TABLE "my_list_music_info" (
"id" TEXT NOT NULL,
"listId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"singer" TEXT NOT NULL,
"source" TEXT NOT NULL,
"interval" TEXT,
"meta" TEXT NOT NULL,
UNIQUE("id","listId")
)`)
db.exec(`
CREATE INDEX "index_my_list_music_info" ON "my_list_music_info" (
"id",
"listId"
)`)
db.exec(`
CREATE TABLE "my_list_music_info_order" (
"listId" TEXT NOT NULL,
"musicInfoId" TEXT NOT NULL,
"order" INTEGER NOT NULL
)`)
db.exec(`
CREATE INDEX "index_my_list_music_info_order" ON "my_list_music_info_order" (
"listId",
"musicInfoId"
)`)
db.exec(`
CREATE TABLE "music_info_other_source" (
"source_id" TEXT NOT NULL,
"id" TEXT NOT NULL,
"source" TEXT NOT NULL,
"name" TEXT NOT NULL,
"singer" TEXT NOT NULL,
"meta" TEXT NOT NULL,
"order" INTEGER NOT NULL,
UNIQUE("source_id","id")
)`)
db.exec(`
CREATE INDEX "index_music_info_other_source" ON "music_info_other_source" (
"source_id",
"id"
)`)
// TODO "meta" TEXT NOT NULL,
db.exec(`
CREATE TABLE "lyric" (
"id" TEXT NOT NULL,
"source" TEXT NOT NULL,
"type" TEXT NOT NULL,
"text" TEXT NOT NULL
)`)
db.exec(`
CREATE TABLE "music_url" (
"id" TEXT NOT NULL,
"url" TEXT NOT NULL
)`)
db.exec(`
CREATE TABLE "download_list" (
"id" TEXT NOT NULL,
"isComplate" INTEGER NOT NULL,
"status" TEXT NOT NULL,
"statusText" TEXT NOT NULL,
"progress_downloaded" INTEGER NOT NULL,
"progress_total" INTEGER NOT NULL,
"url" TEXT,
"quality" TEXT NOT NULL,
"ext" TEXT NOT NULL,
"fileName" TEXT NOT NULL,
"filePath" TEXT NOT NULL,
"musicInfo" TEXT NOT NULL,
"position" INTEGER NOT NULL,
PRIMARY KEY("id")
)`)
}
// 打开、初始化数据库
export const init = (lxDataPath: string): boolean => {
const databasePath = path.join(lxDataPath, 'lx.data.db')
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,
})
} catch (error) {
console.log(error)
db = new Database(databasePath, {
nativeBinding,
verbose: global.isDev ? console.log : undefined,
})
initTables(db)
dbFileExists = false
}
// if (dbFileExists) migrateData(db)
// https://www.sqlite.org/pragma.html#pragma_optimize
db.exec('PRAGMA optimize;')
// https://www.sqlite.org/lang_vacuum.html
// db.exec('VACUUM "main"')
process.on('exit', () => db.close())
console.log('db inited')
// require('./test')
return dbFileExists
}
// 获取数据库实例
export const getDB = (): Database.Database => db
+17
View File
@@ -0,0 +1,17 @@
import { init } from './db'
import { exposeWorker } from '../utils/worker'
import { list, lyric, music_url, music_other_source, download } from './modules/index'
const common = {
init,
}
exposeWorker(Object.assign(common, list, lyric, music_url, music_other_source, download))
export declare type workerDBSeriveTypes = typeof common
& typeof list
& typeof lyric
& typeof music_url
& typeof music_other_source
& typeof download
+32
View File
@@ -0,0 +1,32 @@
import Database from 'better-sqlite3'
const migrateV1 = (db: Database.Database) => {
db.exec('DROP TABLE "main"."download_list"')
db.exec(`
CREATE TABLE "download_list" (
"id" TEXT NOT NULL,
"isComplate" INTEGER NOT NULL,
"status" TEXT NOT NULL,
"statusText" TEXT NOT NULL,
"progress_downloaded" INTEGER NOT NULL,
"progress_total" INTEGER NOT NULL,
"url" TEXT,
"quality" TEXT NOT NULL,
"ext" TEXT NOT NULL,
"fileName" TEXT NOT NULL,
"filePath" TEXT NOT NULL,
"musicInfo" TEXT NOT NULL,
"position" INTEGER NOT NULL,
PRIMARY KEY("id")
)`)
db.prepare('UPDATE "main"."db_info" SET "field_value"=@value WHERE "field_name"=@name').run({ name: 'version', value: '2' })
}
export default (db: Database.Database) => {
const version = db.prepare('SELECT "field_value" FROM "main"."db_info" WHERE "field_name" = ?').get('version').field_value
switch (version) {
case '1':
migrateV1(db)
break
}
}
@@ -0,0 +1,64 @@
import { getDB } from '../../db'
import {
createQueryStatement,
createInsertStatement,
createDeleteStatement,
createUpdateStatement,
createUpdatePositionStatement,
createClearStatement,
} from './statements'
/**
* 查询下载歌曲列表
*/
export const queryDownloadList = (): LX.DBService.DownloadMusicInfo[] => {
const queryStatement = createQueryStatement()
return queryStatement.all()
}
/**
* 批量插入下载歌曲并刷新顺序
* @param mInfos 列表
*/
export const inertDownloadList = (mInfos: LX.DBService.DownloadMusicInfo[], listPositions: Array<{ id: string, position: number }>) => {
const db = getDB()
const insertStatement = createInsertStatement()
const updatePositionStatement = createUpdatePositionStatement()
db.transaction((mInfos: LX.DBService.DownloadMusicInfo[]) => {
for (const info of mInfos) insertStatement.run(info)
for (const info of listPositions) updatePositionStatement.run(info)
})(mInfos)
}
/**
* 批量删除下载歌曲
* @param ids 列表
*/
export const deleteDownloadList = (ids: string[]) => {
const db = getDB()
const deleteStatement = createDeleteStatement()
db.transaction((ids: string[]) => {
for (const id of ids) deleteStatement.run(id)
})(ids)
}
/**
* 批量更新下载歌曲
* @param urlInfo 列表
*/
export const updateDownloadList = (urlInfo: LX.DBService.DownloadMusicInfo[]) => {
const db = getDB()
const updateStatement = createUpdateStatement()
db.transaction((urlInfo: LX.DBService.DownloadMusicInfo[]) => {
for (const info of urlInfo) updateStatement.run(info)
})(urlInfo)
}
/**
* 清空下载歌曲列表
*/
export const clearDownloadList = () => {
const clearStatement = createClearStatement()
clearStatement.run()
}
@@ -0,0 +1,121 @@
import {
queryDownloadList,
inertDownloadList,
updateDownloadList,
deleteDownloadList,
clearDownloadList,
} from './dbHelper'
let list: LX.Download.ListItem[]
const toDBDownloadInfo = (musicInfos: LX.Download.ListItem[], offset: number = 0): LX.DBService.DownloadMusicInfo[] => {
return musicInfos.map((info, index) => {
return {
id: info.id,
isComplate: info.isComplate ? 1 : 0,
status: info.status,
statusText: info.statusText,
progress_downloaded: info.downloaded,
progress_total: info.total,
url: info.metadata.url,
quality: info.metadata.quality,
ext: info.metadata.ext,
fileName: info.metadata.fileName,
filePath: info.metadata.filePath,
musicInfo: JSON.stringify(info.metadata.musicInfo),
position: offset + index,
}
})
}
const initDownloadList = () => {
list = queryDownloadList().map(item => {
const musicInfo = JSON.parse(item.musicInfo) as LX.Music.MusicInfoOnline
return {
id: item.id,
isComplate: item.isComplate == 1,
status: item.status,
statusText: item.statusText,
downloaded: item.progress_downloaded,
total: item.progress_total,
progress: item.progress_total ? parseInt((item.progress_downloaded / item.progress_total).toFixed(2)) * 100 : 0,
speed: '',
metadata: {
musicInfo,
url: item.url,
quality: item.quality,
ext: item.ext,
fileName: item.fileName,
filePath: item.filePath,
},
}
})
}
/**
* 获取下载列表
* @returns 下载列表
*/
export const getDownloadList = (): LX.Download.ListItem[] => {
if (!list) initDownloadList()
return list
}
/**
* 添加下载歌曲信息
* @param downloadInfos url信息
*/
export const downloadInfoSave = (downloadInfos: LX.Download.ListItem[], addMusicLocationType: LX.AddMusicLocationType) => {
if (!list) initDownloadList()
if (addMusicLocationType == 'top') {
let newList = [...list]
newList.unshift(...downloadInfos)
inertDownloadList(toDBDownloadInfo(downloadInfos), newList.slice(downloadInfos.length - 1).map((info, index) => {
return { id: info.id, position: index }
}))
list = newList
} else {
inertDownloadList(toDBDownloadInfo(downloadInfos, list.length), [])
list.push(...downloadInfos)
}
}
/**
* 批量更新列表信息
* @param lists 列表信息
*/
export const downloadInfoUpdate = (lists: LX.Download.ListItem[]) => {
updateDownloadList(toDBDownloadInfo(lists))
if (list) {
for (const item of lists) {
const index = list.findIndex(info => info.id === item.id)
if (index < 0) continue
list.splice(index, 1, item)
}
}
}
/**
* 删除下载列表
* @param ids 歌曲id
*/
export const downloadInfoRemove = (ids: string[]) => {
deleteDownloadList(ids)
if (list) {
for (let i = list.length; i--;) {
let idx = ids.indexOf(list[i].id)
if (idx < 0) continue
list.splice(i, 1)
ids.splice(idx, 1)
}
}
}
/**
* 清空下载列表
*/
export const downloadInfoClear = () => {
clearDownloadList()
}
@@ -0,0 +1,72 @@
import { getDB } from '../../db'
/**
* 创建下载列表查询语句
* @returns 查询语句
*/
export const createQueryStatement = () => {
const db = getDB()
return db.prepare(`
SELECT "id", "isComplate", "status", "statusText", "progress_downloaded", "progress_total", "url", "quality", "ext", "fileName", "filePath", "musicInfo", "position"
FROM download_list
ORDER BY "position" ASC
`)
}
/**
* 创建下载记录插入语句
* @returns 插入语句
*/
export const createInsertStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.DownloadMusicInfo]>(`
INSERT INTO "main"."download_list" ("id", "isComplate", "status", "statusText", "progress_downloaded", "progress_total", "url", "quality", "ext", "fileName", "filePath", "musicInfo", "position")
VALUES (@id, @isComplate, @status, @statusText, @progress_downloaded, @progress_total, @url, @quality, @ext, @fileName, @filePath, @musicInfo, @position)`)
}
/**
* 创建下载记录清空语句
* @returns 清空语句
*/
export const createClearStatement = () => {
const db = getDB()
return db.prepare(`
DELETE FROM "main"."download_list"
`)
}
/**
* 创建下载记录删除语句
* @returns 删除语句
*/
export const createDeleteStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
DELETE FROM "main"."download_list"
WHERE "id"=?
`)
}
/**
* 创建下载记录更新语句
* @returns 更新语句
*/
export const createUpdateStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.DownloadMusicInfo]>(`
UPDATE "main"."download_list"
SET "isComplate"=@isComplate, "status"=@status, "statusText"=@statusText, "progress_downloaded"=@progress_downloaded, "progress_total"=@progress_total, "url"=@url, "filePath"=@filePath
WHERE "id"=@id`)
}
/**
* 创建下载记录顺序更新语句
* @returns 更新语句
*/
export const createUpdatePositionStatement = () => {
const db = getDB()
return db.prepare<[{ id: string, position: number }]>(`
UPDATE "main"."download_list"
SET "position"=@position
WHERE "id"=@id`)
}
@@ -0,0 +1,6 @@
export * as list from './list'
export * as lyric from './lyric'
export * as music_url from './music_url'
export * as music_other_source from './music_other_source'
export * as download from './download'
@@ -0,0 +1,361 @@
import { getDB } from '../../db'
import {
createListQueryStatement,
createListInsertStatement,
createListDeleteStatement,
createListClearStatement,
createListUpdateStatement,
createMusicInfoQueryStatement,
createMusicInfoInsertStatement,
createMusicInfoUpdateStatement,
createMusicInfoDeleteStatement,
createMusicInfoDeleteByListIdStatement,
createMusicInfoOrderInsertStatement,
createMusicInfoOrderDeleteStatement,
createMusicInfoOrderDeleteByListIdStatement,
createMusicInfoClearStatement,
createMusicInfoOrderClearStatement,
createMusicInfoByListAndMusicInfoIdQueryStatement,
createMusicInfoByMusicInfoIdQueryStatement,
} from './statements'
/**
* 获取用户列表
* @returns
*/
export const queryAllUserList = (): LX.DBService.UserListInfo[] => {
return createListQueryStatement().all()
}
/**
* 批量插入用户列表
* @param lists 列表
* @param isClear 是否清空列表
*/
export const inertUserLists = (lists: LX.DBService.UserListInfo[], isClear: boolean = false) => {
const db = getDB()
const listClearStatement = createListClearStatement()
const listInsertStatement = createListInsertStatement()
db.transaction((lists: LX.DBService.UserListInfo[]) => {
if (isClear) listClearStatement.run()
for (const list of lists) {
listInsertStatement.run({
id: list.id,
name: list.name,
source: list.source,
sourceListId: list.sourceListId,
locationUpdateTime: list.locationUpdateTime,
position: list.position,
})
}
})(lists)
}
/**
* 批量删除用户列表及列表内歌曲
* @param listIds 列表id
*/
export const deleteUserLists = (listIds: string[]) => {
const db = getDB()
const listDeleteStatement = createListDeleteStatement()
const musicInfoDeleteByListIdStatement = createMusicInfoDeleteByListIdStatement()
const musicInfoOrderDeleteByListIdStatement = createMusicInfoOrderDeleteByListIdStatement()
db.transaction((listIds: string[]) => {
for (const id of listIds) {
listDeleteStatement.run(id)
musicInfoDeleteByListIdStatement.run(id)
musicInfoOrderDeleteByListIdStatement.run(id)
}
})(listIds)
}
/**
* 批量更新用户列表
* @param lists 列表
*/
export const updateUserLists = (lists: LX.DBService.UserListInfo[]) => {
const db = getDB()
const listUpdateStatement = createListUpdateStatement()
db.transaction((lists: LX.DBService.UserListInfo[]) => {
for (const list of lists) listUpdateStatement.run(list)
})(lists)
}
/**
* 批量添加歌曲
* @param list
*/
export const insertMusicInfoList = (list: LX.DBService.MusicInfo[]) => {
const musicInfoInsertStatement = createMusicInfoInsertStatement()
const musicInfoOrderInsertStatement = createMusicInfoOrderInsertStatement()
const db = getDB()
return db.transaction((musics: LX.DBService.MusicInfo[]) => {
for (const music of musics) {
musicInfoInsertStatement.run(music)
musicInfoOrderInsertStatement.run({
listId: music.listId,
musicInfoId: music.id,
order: music.order,
})
}
})(list)
}
/**
* 批量添加歌曲并刷新排序
* @param list 新增歌曲
* @param listId 列表Id
* @param listAll 原始列表歌曲,列表去重后
*/
export const insertMusicInfoListAndRefreshOrder = (list: LX.DBService.MusicInfo[], listId: string, listAll: LX.DBService.MusicInfo[]) => {
const musicInfoInsertStatement = createMusicInfoInsertStatement()
const musicInfoOrderInsertStatement = createMusicInfoOrderInsertStatement()
const musicInfoOrderDeleteByListIdStatement = createMusicInfoOrderDeleteByListIdStatement()
const db = getDB()
db.transaction((list: LX.DBService.MusicInfo[], listId: string, listAll: LX.DBService.MusicInfo[]) => {
musicInfoOrderDeleteByListIdStatement.run(listId)
for (const music of list) {
musicInfoInsertStatement.run(music)
musicInfoOrderInsertStatement.run({
listId: music.listId,
musicInfoId: music.id,
order: music.order,
})
}
for (const music of listAll) {
musicInfoOrderInsertStatement.run({
listId: music.listId,
musicInfoId: music.id,
order: music.order,
})
}
})(list, listId, listAll)
}
/**
* 批量更新歌曲
* @param list
*/
export const updateMusicInfos = (list: LX.DBService.MusicInfo[]) => {
const musicInfoUpdateStatement = createMusicInfoUpdateStatement()
const db = getDB()
db.transaction((musics: LX.DBService.MusicInfo[]) => {
for (const music of musics) {
musicInfoUpdateStatement.run(music)
}
})(list)
}
/**
* 获取列表内的歌曲
* @param listId 列表Id
* @returns 列表歌曲
*/
export const queryMusicInfoByListId = (listId: string): LX.DBService.MusicInfo[] => {
const musicInfoQueryStatement = createMusicInfoQueryStatement()
return musicInfoQueryStatement.all({ listId })
}
/**
* 批量移动歌曲
* @param fromId 源列表Id
* @param ids 要移动的歌曲
* @param musicInfos 音乐信息
*/
export const moveMusicInfo = (fromId: string, ids: string[], musicInfos: LX.DBService.MusicInfo[]) => {
const musicInfoInsertStatement = createMusicInfoInsertStatement()
const musicInfoOrderInsertStatement = createMusicInfoOrderInsertStatement()
const musicInfoDeleteStatement = createMusicInfoDeleteStatement()
const musicInfoOrderDeleteStatement = createMusicInfoOrderDeleteStatement()
// const musicInfoOrderDeleteByListIdStatement = createMusicInfoOrderDeleteByListIdStatement()
const db = getDB()
db.transaction((fromId: string, ids: string[], musicInfos: LX.DBService.MusicInfo[]) => {
// musicInfoOrderDeleteByListIdStatement.run(fromId)
for (const id of ids) {
musicInfoDeleteStatement.run({ listId: fromId, id })
musicInfoOrderDeleteStatement.run({ listId: fromId, id })
}
for (const music of musicInfos) {
musicInfoInsertStatement.run(music)
musicInfoOrderInsertStatement.run({
listId: music.listId,
musicInfoId: music.id,
order: music.order,
})
}
})(fromId, ids, musicInfos)
}
/**
* 批量移动歌曲并刷新排序
* @param fromId 源列表Id
* @param ids 要移动的歌曲id,原始选择的歌曲
* @param musicInfos 要移动的歌曲,目标列表去重后
* @param toListAll 目标列表歌曲
*/
export const moveMusicInfoAndRefreshOrder = (fromId: string, ids: string[], toId: string, musicInfos: LX.DBService.MusicInfo[], toListAll: LX.DBService.MusicInfo[]) => {
const musicInfoInsertStatement = createMusicInfoInsertStatement()
const musicInfoDeleteStatement = createMusicInfoDeleteStatement()
const musicInfoOrderDeleteStatement = createMusicInfoOrderDeleteStatement()
const musicInfoOrderInsertStatement = createMusicInfoOrderInsertStatement()
const musicInfoOrderDeleteByListIdStatement = createMusicInfoOrderDeleteByListIdStatement()
const db = getDB()
db.transaction((fromId: string, ids: string[], musicInfos: LX.DBService.MusicInfo[], toListAll: LX.DBService.MusicInfo[]) => {
for (const id of ids) {
musicInfoDeleteStatement.run({ listId: fromId, id })
musicInfoOrderDeleteStatement.run({ listId: fromId, id })
}
musicInfoOrderDeleteByListIdStatement.run(toId)
for (const music of musicInfos) {
musicInfoInsertStatement.run(music)
musicInfoOrderInsertStatement.run({
listId: music.listId,
musicInfoId: music.id,
order: music.order,
})
}
for (const music of toListAll) {
musicInfoOrderInsertStatement.run({
listId: music.listId,
musicInfoId: music.id,
order: music.order,
})
}
})(fromId, ids, musicInfos, toListAll)
}
/**
* 批量移除列表内音乐
* @param listId 列表id
* @param ids 音乐id
*/
export const removeMusicInfos = (listId: string, ids: string[]) => {
const musicInfoDeleteStatement = createMusicInfoDeleteStatement()
const musicInfoOrderDeleteStatement = createMusicInfoOrderDeleteStatement()
const db = getDB()
return db.transaction((listId: string, ids: string[]) => {
for (const id of ids) {
musicInfoDeleteStatement.run({ listId, id })
musicInfoOrderDeleteStatement.run({ listId, id })
}
})(listId, ids)
}
/**
* 清空列表内歌曲
* @param listId 列表id
*/
export const removeMusicInfoByListId = (listId: string) => {
const db = getDB()
const musicInfoDeleteByListIdStatement = createMusicInfoDeleteByListIdStatement()
const musicInfoOrderDeleteByListIdStatement = createMusicInfoOrderDeleteByListIdStatement()
db.transaction((listId: string) => {
musicInfoDeleteByListIdStatement.run(listId)
musicInfoOrderDeleteByListIdStatement.run(listId)
})(listId)
}
/**
* 创建根据列表Id与音乐id查询音乐信息
* @param listId 列表id
* @param musicInfoId 音乐id
* @returns
*/
export const queryMusicInfoByListIdAndMusicInfoId = (listId: string, musicInfoId: string): LX.DBService.MusicInfo | null => {
const musicInfoByListAndMusicInfoIdQueryStatement = createMusicInfoByListAndMusicInfoIdQueryStatement()
return musicInfoByListAndMusicInfoIdQueryStatement.get({ listId, musicInfoId })
}
/**
* 创建根据音乐id查询所有列表的音乐信息
* @param id 音乐id
* @returns
*/
export const queryMusicInfoByMusicInfoId = (id: string): LX.DBService.MusicInfo[] => {
const musicInfoByMusicInfoIdQueryStatement = createMusicInfoByMusicInfoIdQueryStatement()
return musicInfoByMusicInfoIdQueryStatement.all(id)
}
/**
* 批量更新歌曲位置
* @param listId 列表id
* @param musicInfoOrders 音乐顺序
*/
export const updateMusicInfoOrder = (listId: string, musicInfoOrders: LX.DBService.MusicInfoOrder[]) => {
const db = getDB()
const musicInfoOrderInsertStatement = createMusicInfoOrderInsertStatement()
const musicInfoOrderDeleteByListIdStatement = createMusicInfoOrderDeleteByListIdStatement()
db.transaction((listId: string, musicInfoOrders: LX.DBService.MusicInfoOrder[]) => {
musicInfoOrderDeleteByListIdStatement.run(listId)
for (const orderInfo of musicInfoOrders) musicInfoOrderInsertStatement.run(orderInfo)
})(listId, musicInfoOrders)
}
/**
* 覆盖列表内的歌曲
* @param listId 列表id
* @param musicInfos 歌曲列表
*/
export const overwriteMusicInfo = (listId: string, musicInfos: LX.DBService.MusicInfo[]) => {
const db = getDB()
const musicInfoDeleteByListIdStatement = createMusicInfoDeleteByListIdStatement()
const musicInfoOrderDeleteByListIdStatement = createMusicInfoOrderDeleteByListIdStatement()
const musicInfoInsertStatement = createMusicInfoInsertStatement()
const musicInfoOrderInsertStatement = createMusicInfoOrderInsertStatement()
db.transaction((listId: string, musicInfos: LX.DBService.MusicInfo[]) => {
musicInfoDeleteByListIdStatement.run(listId)
musicInfoOrderDeleteByListIdStatement.run(listId)
for (const musicInfo of musicInfos) {
musicInfoInsertStatement.run(musicInfo)
musicInfoOrderInsertStatement.run({
listId: musicInfo.listId,
musicInfoId: musicInfo.id,
order: musicInfo.order,
})
}
})(listId, musicInfos)
}
/**
* 覆盖整个列表
* @param lists 列表
* @param musicInfos 歌曲列表
*/
export const overwriteListData = (lists: LX.DBService.UserListInfo[], musicInfos: LX.DBService.MusicInfo[]) => {
const db = getDB()
const listClearStatement = createListClearStatement()
const listInsertStatement = createListInsertStatement()
const musicInfoClearStatement = createMusicInfoClearStatement()
const musicInfoInsertStatement = createMusicInfoInsertStatement()
const musicInfoOrderClearStatement = createMusicInfoOrderClearStatement()
const musicInfoOrderInsertStatement = createMusicInfoOrderInsertStatement()
db.transaction((lists: LX.DBService.UserListInfo[], musicInfos: LX.DBService.MusicInfo[]) => {
listClearStatement.run()
for (const list of lists) {
listInsertStatement.run({
id: list.id,
name: list.name,
source: list.source,
sourceListId: list.sourceListId,
locationUpdateTime: list.locationUpdateTime,
position: list.position,
})
}
musicInfoClearStatement.run()
musicInfoOrderClearStatement.run()
for (const musicInfo of musicInfos) {
musicInfoInsertStatement.run(musicInfo)
musicInfoOrderInsertStatement.run({
listId: musicInfo.listId,
musicInfoId: musicInfo.id,
order: musicInfo.order,
})
}
})(lists, musicInfos)
}
@@ -0,0 +1,407 @@
import { LIST_IDS } from '@common/constants'
import {
deleteUserLists,
inertUserLists,
insertMusicInfoList,
insertMusicInfoListAndRefreshOrder,
moveMusicInfo,
moveMusicInfoAndRefreshOrder,
overwriteListData,
overwriteMusicInfo,
queryAllUserList,
queryMusicInfoByListId,
queryMusicInfoByListIdAndMusicInfoId,
queryMusicInfoByMusicInfoId,
removeMusicInfoByListId,
removeMusicInfos,
updateMusicInfoOrder,
updateMusicInfos,
updateUserLists as updateUserListsFromDB,
} from './dbHelper'
let userLists: LX.DBService.UserListInfo[]
let musicLists = new Map<string, LX.Music.MusicInfo[]>()
const toDBMusicInfo = (musicInfos: LX.Music.MusicInfo[], listId: string, offset: number = 0): LX.DBService.MusicInfo[] => {
return musicInfos.map((info, index) => {
return {
...info,
listId,
meta: JSON.stringify(info.meta),
order: offset + index,
}
})
}
/**
* 获取所有用户列表
* @returns
*/
export const getAllUserList = (): LX.List.UserListInfo[] => {
if (userLists == null) userLists = queryAllUserList()
return userLists.map(list => {
const { position, ...newList } = list
return newList
})
}
/**
* 批量创建列表
* @param position 列表位置
* @param lists 列表信息
*/
export const createUserLists = (position: number, lists: LX.List.UserListInfo[]) => {
if (userLists == null) userLists = queryAllUserList()
if (position < 0 || position >= userLists.length) {
const newLists: LX.DBService.UserListInfo[] = lists.map((list, index) => {
return {
...list,
position: position + index,
}
})
inertUserLists(newLists)
userLists = [...userLists, ...newLists]
} else {
const newUserLists = [...userLists]
// @ts-expect-error
newUserLists.splice(position, 0, ...lists)
newUserLists.forEach((list, index) => {
list.position = index
})
inertUserLists(newUserLists, true)
userLists = newUserLists
}
}
/**
* 覆盖列表
* @param lists 列表信息
*/
export const setUserLists = (lists: LX.List.UserListInfo[]) => {
const newUserLists: LX.DBService.UserListInfo[] = lists.map((list, index) => {
return {
...list,
position: index,
}
})
inertUserLists(newUserLists, true)
userLists = newUserLists
}
/**
* 批量删除列表
* @param ids 列表ids
*/
export const removeUserLists = (ids: string[]) => {
deleteUserLists(ids)
if (userLists) userLists = queryAllUserList()
}
/**
* 批量更新列表信息
* @param lists 列表信息
*/
export const updateUserLists = (lists: LX.List.UserListInfo[]) => {
const positionMap = new Map<string, number>()
for (const list of userLists) {
positionMap.set(list.id, list.position)
}
const dbList: LX.DBService.UserListInfo[] = lists.map(list => {
const position = positionMap.get(list.id)
if (position == null) return null
return {
...list,
position,
}
}).filter(Boolean) as LX.DBService.UserListInfo[]
updateUserListsFromDB(dbList)
if (userLists) userLists = queryAllUserList()
}
/**
* 批量更新列表位置
* @param position 列表位置
* @param ids 列表ids
*/
export const updateUserListsPosition = (position: number, ids: string[]) => {
if (userLists == null) userLists = queryAllUserList()
const newUserLists = [...userLists]
const updateLists: LX.DBService.UserListInfo[] = []
for (let i = newUserLists.length - 1; i >= 0; i--) {
if (ids.includes(newUserLists[i].id)) {
const list = newUserLists.splice(i, 1)[0]
list.locationUpdateTime = Date.now()
updateLists.push(list)
}
}
position = Math.min(newUserLists.length, position)
newUserLists.splice(position, 0, ...updateLists)
newUserLists.forEach((list, index) => {
list.position = index
})
inertUserLists(newUserLists, true)
userLists = newUserLists
}
/**
* 根据列表ID获取列表内歌曲
* @param listId 列表ID
* @returns 列表内歌曲
*/
export const getListMusics = (listId: string): LX.Music.MusicInfo[] => {
let targetList: LX.Music.MusicInfo[] | undefined = musicLists.get(listId)
if (targetList == null) {
targetList = queryMusicInfoByListId(listId).map(info => {
return {
id: info.id,
name: info.name,
singer: info.singer,
source: info.source,
interval: info.interval,
meta: JSON.parse(info.meta),
}
})
musicLists.set(listId, targetList)
}
return targetList
}
/**
* 覆盖列表内的歌曲
* @param listId 列表id
* @param musicInfos 歌曲列表
*/
export const musicOverwrite = (listId: string, musicInfos: LX.Music.MusicInfo[]) => {
let targetList = getListMusics(listId)
overwriteMusicInfo(listId, toDBMusicInfo(musicInfos, listId))
if (targetList) targetList.splice(0, targetList.length, ...musicInfos)
}
/**
* 批量添加歌曲
* @param listId 列表id
* @param musicInfos 添加的歌曲信息
* @param addMusicLocationType 添加在到列表的位置
*/
export const musicsAdd = (listId: string, musicInfos: LX.Music.MusicInfo[], addMusicLocationType: LX.AddMusicLocationType) => {
let targetList = getListMusics(listId)
const set = new Set<string>()
for (const item of targetList) set.add(item.id)
musicInfos = musicInfos.filter(item => {
if (set.has(item.id)) return false
set.add(item.id)
return true
})
switch (addMusicLocationType) {
case 'top':
insertMusicInfoListAndRefreshOrder(toDBMusicInfo(musicInfos, listId), listId, toDBMusicInfo(targetList, listId, musicInfos.length))
targetList.unshift(...musicInfos)
break
case 'bottom':
default:
insertMusicInfoList(toDBMusicInfo(musicInfos, listId, targetList.length))
targetList.push(...musicInfos)
break
}
}
/**
* 批量删除歌曲
* @param listId 列表Id
* @param ids 要删除歌曲的id
*/
export const musicsRemove = (listId: string, ids: string[]) => {
let targetList = getListMusics(listId)
if (!targetList.length) return
ids = [...ids]
removeMusicInfos(listId, ids)
for (let i = targetList.length - 1; i > -1; i--) {
const item = targetList[i]
const index = ids.indexOf(item.id)
if (index < 0) continue
ids.splice(index, 1)
targetList.splice(i, 1)
}
}
/**
* 批量移动歌曲
* @param fromId 源列表id
* @param toId 目标列表id
* @param musicInfos 添加的歌曲信息
* @param addMusicLocationType 添加在到列表的位置
*/
export const musicsMove = (fromId: string, toId: string, musicInfos: LX.Music.MusicInfo[], addMusicLocationType: LX.AddMusicLocationType) => {
const ids = musicInfos.map(musicInfo => musicInfo.id)
let toList = getListMusics(toId)
switch (addMusicLocationType) {
case 'top':
moveMusicInfoAndRefreshOrder(fromId, ids, toId, toDBMusicInfo(musicInfos, toId), toDBMusicInfo(toList, toId, musicInfos.length))
break
case 'bottom':
default:
moveMusicInfo(fromId, ids, toDBMusicInfo(musicInfos, toId, toList.length))
break
}
musicsRemove(fromId, ids)
musicsAdd(toId, musicInfos, addMusicLocationType)
// let fromList = getListMusics(fromId)
// let toList = getListMusics(toId)
// const ids = musicInfos.map(musicInfo => musicInfo.id)
// const map = new Map<string, LX.Music.MusicInfo>()
// for (const item of toList) map.set(item.id, item)
// musicInfos = musicInfos.filter(item => {
// if (map.has(item.id)) return false
// map.set(item.id, item)
// return true
// })
// switch (addMusicLocationType) {
// case 'top':
// moveMusicInfoAndRefreshOrder(fromId, ids, toId, toDBMusicInfo(musicInfos, toId), toDBMusicInfo(toList, toId, musicInfos.length))
// toList.unshift(...musicInfos)
// break
// case 'bottom':
// default:
// moveMusicInfo(fromId, ids, toDBMusicInfo(musicInfos, toId, toList.length))
// toList.push(...musicInfos)
// break
// }
// for (let i = fromList.length - 1; i > -1; i--) {
// const item = fromList[i]
// const index = ids.indexOf(item.id)
// if (index < 0) continue
// ids.splice(index, 1)
// fromList.splice(i, 1)
// }
}
/**
* 批量更新歌曲信息
* @param musicInfos 歌曲&列表信息
*/
export const musicsUpdate = (musicInfos: LX.List.ListActionMusicUpdate) => {
updateMusicInfos(musicInfos.map(({ id, musicInfo }) => {
return {
...musicInfo,
listId: id,
meta: JSON.stringify(musicInfo.meta),
order: 0,
}
}))
for (const { id, musicInfo } of musicInfos) {
const targetList = musicLists.get(id)
if (targetList == null) continue
const targetMusic = targetList.find(item => item.id == musicInfo.id)
if (!targetMusic) continue
targetMusic.name = musicInfo.name
targetMusic.singer = musicInfo.singer
targetMusic.source = musicInfo.source
targetMusic.interval = musicInfo.interval
targetMusic.meta = musicInfo.meta
}
}
/**
* 清空列表内的歌曲
* @param listId 列表Id
*/
export const musicsClear = (listId: string) => {
removeMusicInfoByListId(listId)
const targetList = getListMusics(listId)
if (targetList == null) return
targetList.slice(0, targetList.length)
}
/**
* 批量更新歌曲位置
* @param listId 列表id
* @param position 新位置
* @param ids 要更新位置的歌曲id
*/
export const musicsPositionUpdate = (listId: string, position: number, ids: string[]) => {
let targetList = getListMusics(listId)
if (!targetList.length) return
const newTargetList = [...targetList]
const infos = Array(ids.length)
for (let i = newTargetList.length; i--;) {
const item = newTargetList[i]
const index = ids.indexOf(item.id)
if (index < 0) continue
infos.splice(index, 1, newTargetList.splice(i, 1)[0])
}
newTargetList.splice(Math.min(position, newTargetList.length), 0, ...infos)
updateMusicInfoOrder(listId, newTargetList.map((info, index) => {
return {
listId,
musicInfoId: info.id,
order: index,
}
}))
musicLists.set(listId, newTargetList)
}
/**
* 覆盖所有列表数据
* @param myListData 完整列表数据
*/
export const listDataOverwrite = (myListData: MakeOptional<LX.List.ListDataFull, 'tempList'>) => {
const dbLists: LX.DBService.UserListInfo[] = []
const listData: LX.List.ListDataFull = {
...myListData,
tempList: myListData.tempList ?? getListMusics(LIST_IDS.TEMP),
}
const dbMusicInfos: LX.DBService.MusicInfo[] = [
...toDBMusicInfo(listData.defaultList, LIST_IDS.DEFAULT),
...toDBMusicInfo(listData.loveList, LIST_IDS.LOVE),
...toDBMusicInfo(listData.tempList, LIST_IDS.TEMP),
]
listData.userList.forEach(({ list, ...listInfo }, index) => {
dbLists.push({ ...listInfo, position: index })
dbMusicInfos.push(...toDBMusicInfo(list, listInfo.id))
})
overwriteListData(dbLists, dbMusicInfos)
if (userLists) userLists.splice(0, userLists.length, ...dbLists)
else userLists = dbLists
musicLists.clear()
musicLists.set(LIST_IDS.DEFAULT, listData.defaultList)
musicLists.set(LIST_IDS.LOVE, listData.loveList)
musicLists.set(LIST_IDS.TEMP, listData.tempList)
for (const list of listData.userList) musicLists.set(list.id, list.list)
}
/**
* 检查音乐是否存在列表中
* @param listId 列表id
* @param musicInfoId 音乐id
* @returns
*/
export const checkListExistMusic = (listId: string, musicInfoId: string): boolean => {
const musicInfo = queryMusicInfoByListIdAndMusicInfoId(listId, musicInfoId)
return musicInfo != null
}
/**
* 获取所有存在该音乐的列表id
* @param musicInfoId 音乐id
* @returns
*/
export const getMusicExistListIds = (musicInfoId: string): string[] => {
const musicInfos = queryMusicInfoByMusicInfoId(musicInfoId)
return musicInfos.map(m => m.listId)
}
@@ -0,0 +1,186 @@
import { getDB } from '../../db'
/**
* 创建列表查询语句
* @returns 查询语句
*/
export const createListQueryStatement = () => {
const db = getDB()
return db.prepare(`
SELECT "id", "name", "source", "sourceListId", "position", "locationUpdateTime"
FROM "main"."my_list"
`)
}
/**
* 创建列表插入语句
* @returns 插入语句
*/
export const createListInsertStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.UserListInfo]>(`
INSERT INTO "main"."my_list" ("id", "name", "source", "sourceListId", "position", "locationUpdateTime")
VALUES (@id, @name, @source, @sourceListId, @position, @locationUpdateTime)`)
}
/**
* 创建列表清空语句
* @returns 清空语句
*/
export const createListClearStatement = () => {
const db = getDB()
return db.prepare('DELETE FROM "main"."my_list"')
}
/**
* 创建列表删除语句
* @returns 删除语句
*/
export const createListDeleteStatement = () => {
const db = getDB()
return db.prepare<[string]>('DELETE FROM "main"."my_list" WHERE "id"=?')
}
/**
* 创建列表更新语句
* @returns 更新语句
*/
export const createListUpdateStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.UserListInfo]>(`
UPDATE "main"."my_list"
SET "name"=@name, "source"=@source, "sourceListId"=@sourceListId, "locationUpdateTime"=@locationUpdateTime
WHERE "id"=@id`)
}
/**
* 创建音乐信息查询语句
* @returns 查询语句
*/
export const createMusicInfoQueryStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicInfoQuery]>(`
SELECT mInfo."id", mInfo."name", mInfo."singer", mInfo."source", mInfo."interval", mInfo."meta"
FROM my_list_music_info mInfo
LEFT JOIN my_list_music_info_order O
ON mInfo.id=O.musicInfoId AND O.listId=@listId
WHERE mInfo.listId=@listId
ORDER BY O."order" ASC
`)
}
/**
* 创建音乐信息插入语句
* @returns 插入语句
*/
export const createMusicInfoInsertStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicInfo]>(`
INSERT INTO "main"."my_list_music_info" ("id", "listId", "name", "singer", "source", "interval", "meta")
VALUES (@id, @listId, @name, @singer, @source, @interval, @meta)`)
}
/**
* 创建音乐信息更新语句
* @returns 更新语句
*/
export const createMusicInfoUpdateStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicInfo]>(`
UPDATE "main"."my_list_music_info"
SET "name"=@name, "singer"=@singer, "source"=@source, "interval"=@interval, "meta"=@meta
WHERE "id"=@id AND "listId"=@listId`)
}
/**
* 创建清空音乐信息语句
* @returns 删除语句
*/
export const createMusicInfoClearStatement = () => {
const db = getDB()
return db.prepare('DELETE FROM "main"."my_list_music_info"')
}
/**
* 创建根据列表id批量删除音乐信息语句
* @returns 删除语句
*/
export const createMusicInfoDeleteByListIdStatement = () => {
const db = getDB()
return db.prepare<[string]>('DELETE FROM "main"."my_list_music_info" WHERE "listId"=?')
}
/**
* 创建根据列表Id与音乐id批量删除音乐信息语句
* @returns 删除语句
*/
export const createMusicInfoDeleteStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicInfoRemove]>('DELETE FROM "main"."my_list_music_info" WHERE "id"=@id AND "listId"=@listId')
}
/**
* 创建根据列表Id与音乐id查询音乐信息语句
* @returns 删除语句
*/
export const createMusicInfoByListAndMusicInfoIdQueryStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.ListMusicInfoQuery]>(`SELECT "id", "name", "singer", "source", "interval", "meta"
FROM "main"."my_list_music_info"
WHERE "id"=@musicInfoId
AND "listId"=@listId`)
}
/**
* 创建根据音乐id查询音乐信息语句
* @returns 删除语句
*/
export const createMusicInfoByMusicInfoIdQueryStatement = () => {
const db = getDB()
return db.prepare<[string]>(`SELECT "id", "name", "singer", "source", "interval", "meta", "listId"
FROM "main"."my_list_music_info"
WHERE "id"=?`)
}
/**
* 创建音乐信息排序插入语句
* @returns 插入语句
*/
export const createMusicInfoOrderInsertStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicInfoOrder]>(`
INSERT INTO "main"."my_list_music_info_order" ("listId", "musicInfoId", "order")
VALUES (@listId, @musicInfoId, @order)`)
}
/**
* 创建清空音乐排序语句
* @returns 删除语句
*/
export const createMusicInfoOrderClearStatement = () => {
const db = getDB()
return db.prepare('DELETE FROM "main"."my_list_music_info_order"')
}
/**
* 创建根据列表id删除音乐排序语句
* @returns 删除语句
*/
export const createMusicInfoOrderDeleteByListIdStatement = () => {
const db = getDB()
return db.prepare<[string]>('DELETE FROM "main"."my_list_music_info_order" WHERE "listId"=?')
}
/**
* 创建根据列表Id与音乐id删除音乐排序语句
* @returns 删除语句
*/
export const createMusicInfoOrderDeleteStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicInfoRemove]>('DELETE FROM "main"."my_list_music_info_order" WHERE "musicInfoId"=@id AND "listId"=@listId')
}
@@ -0,0 +1,152 @@
import { getDB } from '../../db'
import {
createLyricQueryStatement,
createRawLyricQueryStatement,
createRawLyricInsertStatement,
createRawLyricDeleteStatement,
createRawLyricUpdateStatement,
createRawLyricClearStatement,
createEditedLyricQueryStatement,
createEditedLyricInsertStatement,
createEditedLyricDeleteStatement,
createEditedLyricUpdateStatement,
createEditedLyricClearStatement,
createEditedLyricCountStatement,
createRawLyricCountStatement,
} from './statements'
/**
* 查询原始歌词
* @param id 歌曲id
* @returns 歌词信息
*/
export const queryLyric = (id: string): LX.DBService.Lyricnfo[] => {
const lyricQueryStatement = createLyricQueryStatement()
return lyricQueryStatement.all(id)
}
/**
* 查询原始歌词
* @param id 歌曲id
* @returns 歌词信息
*/
export const queryRawLyric = (id: string): LX.DBService.Lyricnfo[] => {
const rawLyricQueryStatement = createRawLyricQueryStatement()
return rawLyricQueryStatement.all(id)
}
/**
* 批量插入原始歌词
* @param lyrics 列表
*/
export const inertRawLyric = (lyrics: LX.DBService.Lyricnfo[]) => {
const db = getDB()
const rawLyricInsertStatement = createRawLyricInsertStatement()
db.transaction((lyrics: LX.DBService.Lyricnfo[]) => {
for (const lyric of lyrics) rawLyricInsertStatement.run(lyric)
})(lyrics)
}
/**
* 批量删除原始歌词
* @param ids 列表
*/
export const deleteRawLyric = (ids: string[]) => {
const db = getDB()
const rawLyricDeleteStatement = createRawLyricDeleteStatement()
db.transaction((ids: string[]) => {
for (const id of ids) rawLyricDeleteStatement.run(id)
})(ids)
}
/**
* 批量更新原始歌词
* @param lyrics 列表
*/
export const updateRawLyric = (lyrics: LX.DBService.Lyricnfo[]) => {
const db = getDB()
const rawLyricUpdateStatement = createRawLyricUpdateStatement()
db.transaction((lyrics: LX.DBService.Lyricnfo[]) => {
for (const lyric of lyrics) rawLyricUpdateStatement.run(lyric)
})(lyrics)
}
/**
* 清空原始歌词
*/
export const clearRawLyric = () => {
const rawLyricClearStatement = createRawLyricClearStatement()
rawLyricClearStatement.run()
}
/**
* 统计已编辑歌词数量
*/
export const countRawLyric = () => {
const countStatement = createRawLyricCountStatement()
return countStatement.get().count
}
/**
* 查询已编辑歌词
* @param id 歌曲id
* @returns 歌词信息
*/
export const queryEditedLyric = (id: string): LX.DBService.Lyricnfo[] => {
const rawLyricQueryStatement = createEditedLyricQueryStatement()
return rawLyricQueryStatement.all(id)
}
/**
* 批量插入已编辑歌词
* @param lyrics 列表
*/
export const inertEditedLyric = (lyrics: LX.DBService.Lyricnfo[]) => {
const db = getDB()
const rawLyricInsertStatement = createEditedLyricInsertStatement()
db.transaction((lyrics: LX.DBService.Lyricnfo[]) => {
for (const lyric of lyrics) rawLyricInsertStatement.run(lyric)
})(lyrics)
}
/**
* 批量删除已编辑歌词
* @param ids 列表
*/
export const deleteEditedLyric = (ids: string[]) => {
const db = getDB()
const rawLyricDeleteStatement = createEditedLyricDeleteStatement()
db.transaction((ids: string[]) => {
for (const id of ids) rawLyricDeleteStatement.run(id)
})(ids)
}
/**
* 批量更新已编辑歌词
* @param lyrics 列表
*/
export const updateEditedLyric = (lyrics: LX.DBService.Lyricnfo[]) => {
const db = getDB()
const rawLyricUpdateStatement = createEditedLyricUpdateStatement()
db.transaction((lyrics: LX.DBService.Lyricnfo[]) => {
for (const lyric of lyrics) rawLyricUpdateStatement.run(lyric)
})(lyrics)
}
/**
* 清空已编辑歌词
*/
export const clearEditedLyric = () => {
const rawLyricClearStatement = createEditedLyricClearStatement()
rawLyricClearStatement.run()
}
/**
* 统计已编辑歌词数量
*/
export const countEditedLyric = () => {
const countStatement = createEditedLyricCountStatement()
return countStatement.get().count
}
@@ -0,0 +1,197 @@
import {
queryLyric,
queryRawLyric,
inertRawLyric,
deleteRawLyric,
updateRawLyric,
clearRawLyric,
queryEditedLyric,
inertEditedLyric,
deleteEditedLyric,
updateEditedLyric,
clearEditedLyric,
countEditedLyric,
countRawLyric,
} from './dbHelper'
const keys = ['lyric', 'tlyric', 'rlyric', 'lxlyric'] as const
const toDBLyric = (id: string, source: LX.DBService.Lyricnfo['source'], lyricInfo: LX.Music.LyricInfo): LX.DBService.Lyricnfo[] => {
return (keys.map(k => [k, lyricInfo[k]])
.filter(([k, t]) => t != null) as Array<[LX.DBService.Lyricnfo['type'], string]>)
.map(([k, t]) => {
return {
id,
type: k,
text: Buffer.from(t).toString('base64'),
source,
}
})
}
/**
* 获取歌词
* @param id 歌曲id
* @returns 歌词信息
*/
export const getPlayerLyric = (id: string): LX.Player.LyricInfo => {
const lyrics = queryLyric(id)
let lyricInfo: LX.Music.LyricInfo = {
lyric: '',
}
let rawLyricInfo: LX.Music.LyricInfo = {
lyric: '',
}
for (const lyric of lyrics) {
switch (lyric.source) {
case 'edited':
if (lyric.type == 'lyric') lyricInfo.lyric = Buffer.from(lyric.text, 'base64').toString()
else if (lyric.text != null) lyricInfo[lyric.type] = Buffer.from(lyric.text, 'base64').toString()
break
default:
if (lyric.type == 'lyric') rawLyricInfo.lyric = Buffer.from(lyric.text, 'base64').toString()
else if (lyric.text != null) rawLyricInfo[lyric.type] = Buffer.from(lyric.text, 'base64').toString()
break
}
}
return lyricInfo.lyric ? {
...lyricInfo,
rawlrcInfo: rawLyricInfo,
} : {
...rawLyricInfo,
rawlrcInfo: rawLyricInfo,
}
}
/**
* 获取原始歌词
* @param id 歌曲id
* @returns 歌词信息
*/
export const getRawLyric = (id: string): LX.Music.LyricInfo => {
const lyrics = queryRawLyric(id)
let lyricInfo: LX.Music.LyricInfo = {
lyric: '',
}
for (const lyric of lyrics) {
if (lyric.type == 'lyric') lyricInfo.lyric = Buffer.from(lyric.text, 'base64').toString()
else if (lyric.text != null) lyricInfo[lyric.type] = Buffer.from(lyric.text, 'base64').toString()
}
return lyricInfo
}
/**
* 保存原始歌词信息
* @param id 歌曲id
* @param lyricInfo 歌词信息
*/
export const rawLyricAdd = (id: string, lyricInfo: LX.Music.LyricInfo) => {
inertRawLyric(toDBLyric(id, 'raw', lyricInfo))
}
/**
* 删除原始歌词信息
* @param ids 歌曲id
*/
export const rawLyricRemove = (ids: string[]) => {
deleteRawLyric(ids)
}
/**
* 更新原始歌词信息
* @param id 歌曲id
* @param lyricInfo 歌词信息
*/
export const rawLyricUpdate = (id: string, lyricInfo: LX.Music.LyricInfo) => {
updateRawLyric(toDBLyric(id, 'raw', lyricInfo))
}
/**
* 清空原始歌词信息
*/
export const rawLyricClear = () => {
clearRawLyric()
}
/**
* 统计原始歌词数量
*/
export const rawLyricCount = () => {
return countRawLyric()
}
/**
* 获取已编辑歌词
* @param id 歌曲id
* @returns 歌词信息
*/
export const getEditedLyric = (id: string): LX.Music.LyricInfo => {
const lyrics = queryEditedLyric(id)
let lyricInfo: LX.Music.LyricInfo = {
lyric: '',
}
for (const lyric of lyrics) {
if (lyric.type == 'lyric') lyricInfo.lyric = Buffer.from(lyric.text, 'base64').toString()
else if (lyric.text != null) lyricInfo[lyric.type] = Buffer.from(lyric.text, 'base64').toString()
}
return lyricInfo
}
/**
* 保存已编辑歌词信息
* @param id 歌曲id
* @param lyricInfo 歌词信息
*/
export const editedLyricAdd = (id: string, lyricInfo: LX.Music.LyricInfo) => {
inertEditedLyric(toDBLyric(id, 'edited', lyricInfo))
}
/**
* 删除已编辑歌词信息
* @param ids 歌曲id
*/
export const editedLyricRemove = (ids: string[]) => {
deleteEditedLyric(ids)
}
/**
* 更新已编辑歌词信息
* @param id 歌曲id
* @param lyricInfo 歌词信息
*/
export const editedLyricUpdate = (id: string, lyricInfo: LX.Music.LyricInfo) => {
updateEditedLyric(toDBLyric(id, 'edited', lyricInfo))
}
/**
* 清空已编辑歌词信息
*/
export const editedLyricClear = () => {
clearEditedLyric()
}
/**
* 新增或更新已编辑歌词信息
* @param id 歌曲id
* @param lyricInfo 歌词信息
*/
export const editedLyricUpdateAddAndUpdate = (id: string, lyricInfo: LX.Music.LyricInfo) => {
const lyrics = queryEditedLyric(id)
if (lyrics.length) updateEditedLyric(toDBLyric(id, 'edited', lyricInfo))
else inertEditedLyric(toDBLyric(id, 'edited', lyricInfo))
}
/**
* 统计已编辑歌词数量
*/
export const editedLyricCount = () => {
return countEditedLyric()
}
@@ -0,0 +1,157 @@
import { getDB } from '../../db'
const RAW_LYRIC = 'raw'
const EDITED_LYRIC = 'edited'
/**
* 创建歌词查询语句
* @returns 查询语句
*/
export const createLyricQueryStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
SELECT "type", "text", "source"
FROM "main"."lyric"
WHERE "id"=?
`)
}
/**
* 创建原始歌词查询语句
* @returns 查询语句
*/
export const createRawLyricQueryStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
SELECT "type", "text"
FROM "main"."lyric"
WHERE "id"=? AND "source"='${RAW_LYRIC}'
`)
}
/**
* 创建原始歌词插入语句
* @returns 插入语句
*/
export const createRawLyricInsertStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.Lyricnfo]>(`
INSERT INTO "main"."lyric" ("id", "type", "text", "source")
VALUES (@id, @type, @text, '${RAW_LYRIC}')`)
}
/**
* 创建原始歌词清空语句
* @returns 清空语句
*/
export const createRawLyricClearStatement = () => {
const db = getDB()
return db.prepare(`
DELETE FROM "main"."lyric"
WHERE "source"='${RAW_LYRIC}'
`)
}
/**
* 创建原始歌词删除语句
* @returns 删除语句
*/
export const createRawLyricDeleteStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
DELETE FROM "main"."lyric"
WHERE "id"=? AND "source"='${RAW_LYRIC}'
`)
}
/**
* 创建原始歌词更新语句
* @returns 更新语句
*/
export const createRawLyricUpdateStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.Lyricnfo]>(`
UPDATE "main"."lyric"
SET "text"=@text
WHERE "id"=@id AND "source"='${RAW_LYRIC}' AND "type"=@type`)
}
/**
* 创建原始歌词数量统计语句
* @returns 统计语句
*/
export const createRawLyricCountStatement = () => {
const db = getDB()
return db.prepare(`SELECT COUNT(*) as count FROM "main"."lyric" WHERE "source"='${RAW_LYRIC}'`)
}
/**
* 创建已编辑歌词查询语句
* @returns 查询语句
*/
export const createEditedLyricQueryStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
SELECT "type", "text"
FROM "main"."lyric"
WHERE "id"=? AND "source"='${EDITED_LYRIC}'
`)
}
/**
* 创建已编辑歌词插入语句
* @returns 插入语句
*/
export const createEditedLyricInsertStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.Lyricnfo]>(`
INSERT INTO "main"."lyric" ("id", "type", "text", "source")
VALUES (@id, @type, @text, '${EDITED_LYRIC}')`)
}
/**
* 创建已编辑歌词清空语句
* @returns 清空语句
*/
export const createEditedLyricClearStatement = () => {
const db = getDB()
return db.prepare(`
DELETE FROM "main"."lyric"
WHERE "source"='${EDITED_LYRIC}'
`)
}
/**
* 创建已编辑歌词删除语句
* @returns 删除语句
*/
export const createEditedLyricDeleteStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
DELETE FROM "main"."lyric"
WHERE "id"=? AND "source"='${EDITED_LYRIC}'
`)
}
/**
* 创建已编辑歌词更新语句
* @returns 更新语句
*/
export const createEditedLyricUpdateStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.Lyricnfo]>(`
UPDATE "main"."lyric"
SET "text"=@text
WHERE "id"=@id AND "source"='${EDITED_LYRIC}' AND "type"=@type`)
}
/**
* 创建已编辑歌词数量统计语句
* @returns 统计语句
*/
export const createEditedLyricCountStatement = () => {
const db = getDB()
return db.prepare(`SELECT COUNT(*) as count FROM "main"."lyric" WHERE "source"='${EDITED_LYRIC}'`)
}
@@ -0,0 +1,59 @@
import { getDB } from '../../db'
import {
createMusicInfoQueryStatement,
createMusicInfoInsertStatement,
createMusicInfoDeleteStatement,
createMusicInfoClearStatement,
createCountStatement,
} from './statements'
/**
* 查询歌曲信息
* @param id 歌曲id
* @returns 歌曲信息
*/
export const queryMusicInfo = (id: string): LX.DBService.MusicInfoOtherSource[] => {
const musicInfoQueryStatement = createMusicInfoQueryStatement()
return musicInfoQueryStatement.all(id)
}
/**
* 批量插入歌曲信息
* @param musicInfos 列表
*/
export const inertMusicInfo = (musicInfos: LX.DBService.MusicInfoOtherSource[]) => {
const db = getDB()
const musicInfoInsertStatement = createMusicInfoInsertStatement()
db.transaction((musicInfos: LX.DBService.MusicInfoOtherSource[]) => {
for (const info of musicInfos) musicInfoInsertStatement.run(info)
})(musicInfos)
}
/**
* 批量删除歌曲信息
* @param ids 列表
*/
export const deleteMusicInfo = (ids: string[]) => {
const db = getDB()
const musicInfoDeleteStatement = createMusicInfoDeleteStatement()
db.transaction((ids: string[]) => {
for (const id of ids) musicInfoDeleteStatement.run(id)
})(ids)
}
/**
* 清空歌曲信息
*/
export const clearMusicInfo = () => {
const musicInfoClearStatement = createMusicInfoClearStatement()
musicInfoClearStatement.run()
}
/**
* 统计歌曲信息数量
*/
export const countMusicInfo = () => {
const countStatement = createCountStatement()
return countStatement.get().count
}
@@ -0,0 +1,72 @@
import {
queryMusicInfo,
inertMusicInfo,
deleteMusicInfo,
clearMusicInfo,
countMusicInfo,
} from './dbHelper'
const toDBMusicInfo = (id: string, musicInfos: LX.Music.MusicInfo[]): LX.DBService.MusicInfoOtherSource[] => {
return musicInfos.map((info, index) => {
return {
...info,
meta: JSON.stringify(info.meta),
source_id: id,
order: index,
}
})
}
/**
* 获取歌曲信息
* @param id 歌曲id
* @returns 歌词信息
*/
export const getMusicInfoOtherSource = (id: string): LX.Music.MusicInfoOnline[] => {
const list = queryMusicInfo(id).map(info => {
return {
id: info.id,
name: info.name,
singer: info.singer,
source: info.source,
interval: info.interval,
meta: JSON.parse(info.meta),
}
})
return list
}
/**
* 保存歌曲信息信息
* @param id 歌曲id
* @param musicInfos 歌词信息
*/
export const musicInfoOtherSourceAdd = (id: string, musicInfos: LX.Music.MusicInfoOnline[]) => {
inertMusicInfo(toDBMusicInfo(id, musicInfos))
}
/**
* 删除歌曲信息信息
* @param ids 歌曲id
*/
export const musicInfoOtherSourceRemove = (ids: string[]) => {
deleteMusicInfo(ids)
}
/**
* 清空歌曲信息信息
*/
export const musicInfoOtherSourceClear = () => {
clearMusicInfo()
}
/**
* 统计歌曲信息信息数量
*/
export const musicInfoOtherSourceCount = () => {
return countMusicInfo()
}
@@ -0,0 +1,60 @@
import { getDB } from '../../db'
/**
* 创建歌曲信息查询语句
* @returns 查询语句
*/
export const createMusicInfoQueryStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
SELECT "id", "name", "singer", "source", "meta"
FROM "main"."music_info_other_source"
WHERE source_id=?
ORDER BY "order" ASC
`)
}
/**
* 创建歌曲信息插入语句
* @returns 插入语句
*/
export const createMusicInfoInsertStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicInfoOtherSource]>(`
INSERT INTO "main"."music_info_other_source" ("id", "name", "singer", "source", "meta", "source_id", "order")
VALUES (@id, @name, @singer, @source, @meta, @source_id, @order)
`)
}
/**
* 创建歌曲信息清空语句
* @returns 清空语句
*/
export const createMusicInfoClearStatement = () => {
const db = getDB()
return db.prepare(`
DELETE FROM "main"."music_info_other_source"
`)
}
/**
* 创建歌曲信息删除语句
* @returns 删除语句
*/
export const createMusicInfoDeleteStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
DELETE FROM "main"."music_info_other_source"
WHERE "source_id"=?
`)
}
/**
* 创建数量统计语句
* @returns 统计语句
*/
export const createCountStatement = () => {
const db = getDB()
return db.prepare('SELECT COUNT(*) as count FROM "main"."music_info_other_source"')
}
@@ -0,0 +1,75 @@
import { getDB } from '../../db'
import {
createQueryStatement,
createInsertStatement,
createDeleteStatement,
// createUpdateStatement,
createClearStatement,
createCountStatement,
} from './statements'
/**
* 查询歌曲url
* @param id 歌曲id
* @returns url
*/
export const queryMusicUrl = (id: string): string | null => {
const queryStatement = createQueryStatement()
return queryStatement.get(id)?.url
}
/**
* 批量插入歌曲url
* @param urlInfo 列表
*/
export const inertMusicUrl = (urlInfo: LX.DBService.MusicUrlInfo[]) => {
const db = getDB()
const insertStatement = createInsertStatement()
const deleteStatement = createDeleteStatement()
db.transaction((urlInfo: LX.DBService.MusicUrlInfo[]) => {
for (const info of urlInfo) {
deleteStatement.run(info.id)
insertStatement.run(info)
}
})(urlInfo)
}
/**
* 批量删除歌曲url
* @param ids 列表
*/
export const deleteMusicUrl = (ids: string[]) => {
const db = getDB()
const deleteStatement = createDeleteStatement()
db.transaction((ids: string[]) => {
for (const id of ids) deleteStatement.run(id)
})(ids)
}
/**
* 批量更新歌曲url
* @param urlInfo 列表
*/
// export const updateMusicUrl = (urlInfo: LX.DBService.MusicUrlInfo[]) => {
// const db = getDB()
// const updateStatement = createUpdateStatement()
// db.transaction((urlInfo: LX.DBService.MusicUrlInfo[]) => {
// for (const info of urlInfo) updateStatement.run(info)
// })(urlInfo)
// }
/**
* 清空歌曲url
*/
export const clearMusicUrl = () => {
const clearStatement = createClearStatement()
clearStatement.run()
}
/**
* 统计歌曲信息数量
*/
export const countMusicUrl = () => {
const countStatement = createCountStatement()
return countStatement.get().count
}
@@ -0,0 +1,49 @@
import {
queryMusicUrl,
inertMusicUrl,
deleteMusicUrl,
clearMusicUrl,
countMusicUrl,
} from './dbHelper'
/**
* 获取歌曲url
* @param id 歌曲id
* @returns 歌曲url
*/
export const getMusicUrl = (id: string): string | null => {
const url = queryMusicUrl(id)
return url
}
/**
* 保存歌曲url
* @param urlInfos url信息
*/
export const musicUrlSave = (urlInfos: LX.Music.MusicUrlInfo[]) => {
inertMusicUrl(urlInfos)
}
/**
* 删除歌曲url
* @param ids 歌曲id
*/
export const musicUrlRemove = (ids: string[]) => {
deleteMusicUrl(ids)
}
/**
* 清空歌曲url
*/
export const musicUrlClear = () => {
clearMusicUrl()
}
/**
* 统计歌曲url数量
*/
export const musicUrlCount = () => {
return countMusicUrl()
}
@@ -0,0 +1,69 @@
import { getDB } from '../../db'
/**
* 创建歌曲url查询语句
* @returns 查询语句
*/
export const createQueryStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
SELECT "url"
FROM "main"."music_url"
WHERE "id"=?
`)
}
/**
* 创建歌曲url插入语句
* @returns 插入语句
*/
export const createInsertStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicUrlInfo]>(`
INSERT INTO "main"."music_url" ("id", "url")
VALUES (@id, @url)`)
}
/**
* 创建歌曲url清空语句
* @returns 清空语句
*/
export const createClearStatement = () => {
const db = getDB()
return db.prepare(`
DELETE FROM "main"."music_url"
`)
}
/**
* 创建歌曲url删除语句
* @returns 删除语句
*/
export const createDeleteStatement = () => {
const db = getDB()
return db.prepare<[string]>(`
DELETE FROM "main"."music_url"
WHERE "id"=?
`)
}
/**
* 创建歌曲url更新语句
* @returns 更新语句
*/
export const createUpdateStatement = () => {
const db = getDB()
return db.prepare<[LX.DBService.MusicUrlInfo]>(`
UPDATE "main"."music_url"
SET "url"=@url
WHERE "id"=@id`)
}
/**
* 创建数量统计语句
* @returns 统计语句
*/
export const createCountStatement = () => {
const db = getDB()
return db.prepare('SELECT COUNT(*) as count FROM "main"."music_url"')
}
+9
View File
@@ -0,0 +1,9 @@
import { createDBServiceWorker } from './utils'
export default () => {
return {
dbService: createDBServiceWorker(),
}
}
+15
View File
@@ -0,0 +1,15 @@
import { Worker } from 'worker_threads'
import * as Comlink from 'comlink'
import nodeEndpoint from 'comlink/dist/esm/node-adapter'
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,
))
return Comlink.wrap<LX.WorkerDBSeriveListTypes>(nodeEndpoint(worker))
}
+9
View File
@@ -0,0 +1,9 @@
import { parentPort } from '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))
}