新增“不喜欢歌曲”功能&新增“收藏歌曲”、“取消收藏”、“不喜欢该歌曲”快捷键设置
parent
3ca8f09971
commit
baf69e397a
|
@ -9,6 +9,8 @@
|
|||
|
||||
- 新增我的列表名右键菜单-排序歌曲-随机乱序功能,使用它可以对选中列表内歌曲进行随机重排(#1440)
|
||||
- 新增数据同步服务端模式已认证设备列表管理,该功能位置:设置-数据同步-服务端模式-已认证设备列表
|
||||
- 新增“不喜欢歌曲”功能,可以在我的列表或者在线列表内歌曲的右击菜单使用,还可以去“设置-其他”手动编辑不喜欢规则,注:“上一曲”、“下一曲”功能将跳过符合“不喜欢歌曲”规则的歌曲,但你仍可以手动播放这些歌曲
|
||||
- 新增软件内快捷键“不喜欢该歌曲”设置,全局快捷键“收藏歌曲”、“取消收藏”、“不喜欢该歌曲”设置
|
||||
|
||||
### 优化
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
export const URL_SCHEME_RXP = /^lxmusic:\/\//
|
||||
|
||||
export const SPLIT_CHAR = {
|
||||
DISLIKE_NAME: '@',
|
||||
DISLIKE_NAME_ALIAS: '#',
|
||||
} as const
|
||||
|
||||
export const STORE_NAMES = {
|
||||
APP_SETTINGS: 'config_v2',
|
||||
|
|
|
@ -66,6 +66,21 @@ const hotKey = {
|
|||
action: 'volume_mute',
|
||||
type: '',
|
||||
},
|
||||
music_love: {
|
||||
name: 'music_love',
|
||||
action: 'music_love',
|
||||
type: '',
|
||||
},
|
||||
music_unlove: {
|
||||
name: 'music_unlove',
|
||||
action: 'music_unlove',
|
||||
type: '',
|
||||
},
|
||||
music_dislike: {
|
||||
name: 'music_dislike',
|
||||
action: 'music_dislike',
|
||||
type: '',
|
||||
},
|
||||
},
|
||||
desktop_lyric: {
|
||||
toggle_visible: {
|
||||
|
|
|
@ -126,6 +126,10 @@ const modules = {
|
|||
clear_music_url: 'clear_music_url',
|
||||
get_music_url_count: 'get_music_url_count',
|
||||
|
||||
get_dislike_music_infos: 'get_dislike_music_infos',
|
||||
add_dislike_music_infos: 'add_dislike_music_infos',
|
||||
overwrite_dislike_music_infos: 'overwrite_dislike_music_infos',
|
||||
|
||||
sync_action: 'sync_action',
|
||||
sync_get_server_devices: 'sync_get_server_devices',
|
||||
sync_remove_server_device: 'sync_remove_server_device',
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
|
||||
declare namespace LX {
|
||||
namespace Dislike {
|
||||
// interface ListItemMusicText {
|
||||
// id?: string
|
||||
// // type: 'music'
|
||||
// name: string | null
|
||||
// singer: string | null
|
||||
// }
|
||||
// interface ListItemMusic {
|
||||
// id?: number
|
||||
// type: 'musicId'
|
||||
// musicId: string
|
||||
// meta: LX.Music.MusicInfo
|
||||
// }
|
||||
// type ListItem = ListItemMusicText
|
||||
// type ListItem = string
|
||||
// type ListItem = ListItemMusic | ListItemMusicText
|
||||
|
||||
interface DislikeMusicInfo {
|
||||
name: string
|
||||
singer: string
|
||||
}
|
||||
|
||||
interface DislikeInfo {
|
||||
// musicIds: Set<string>
|
||||
names: Set<string>
|
||||
musicNames: Set<string>
|
||||
singerNames: Set<string>
|
||||
// list: LX.Dislike.ListItem[]
|
||||
rules: string
|
||||
}
|
||||
}
|
||||
}
|
|
@ -81,6 +81,7 @@
|
|||
"list__add_to": "Add to ...",
|
||||
"list__collect": "Collect",
|
||||
"list__copy_name": "Copy name",
|
||||
"list__dislike": "Dislike",
|
||||
"list__download": "Download",
|
||||
"list__export_part_desc": "Choose where to save the list file",
|
||||
"list__file": "Locate the file",
|
||||
|
@ -382,6 +383,9 @@
|
|||
"setting__desktop_lyric_shadow_color": "Shadow color",
|
||||
"setting__desktop_lyric_show_taskbar": "Display lyrics progress on the taskbar (this setting is used as a workaround when the screen recording software cannot capture the lyrics window)",
|
||||
"setting__desktop_lyric_unplay_color": "Color not playing",
|
||||
"setting__dislike_list_save_btn": "Save",
|
||||
"setting__dislike_list_tips": "1. If there is a \"@\" symbol in the song or singer's name, you need to replace it with \"#\"\n2. Specify a song of a singer: <Name>@<Singer>\n3. Specify a song: <Name>\n4. Specify a certain singer:@<Singer>",
|
||||
"setting__dislike_list_title": "List of Disliked Song Rules",
|
||||
"setting__download": "Download",
|
||||
"setting__download_data_embed": "Whether to embed the following content in the audio file",
|
||||
"setting__download_embed_lyric": "Embedding lyric",
|
||||
|
@ -426,6 +430,9 @@
|
|||
"setting__hot_key_player_toggle_play": "Play/Pause Control",
|
||||
"setting__hot_key_player_volume_down": "Reduce Volume",
|
||||
"setting__hot_key_player_volume_mute": "Mute Switch",
|
||||
"setting__hot_key_player_music_love": "Favorites Song",
|
||||
"setting__hot_key_player_music_unlove": "Cancel collection",
|
||||
"setting__hot_key_player_music_dislike": "Dislike the song",
|
||||
"setting__hot_key_player_volume_up": "Increase Volume",
|
||||
"setting__hot_key_tip_input": "Please enter a new key",
|
||||
"setting__hot_key_unset_input": "Not Set",
|
||||
|
@ -449,6 +456,9 @@
|
|||
"setting__odc_clear_search_input": "Clear the search box when you are not searching",
|
||||
"setting__odc_clear_search_list": "Clear the search list when you are not searching",
|
||||
"setting__other": "Extras",
|
||||
"setting__other_dislike_list": "dislike song rule",
|
||||
"setting__other_dislike_list_label": "Number of rules:",
|
||||
"setting__other_dislike_list_show_btn": "Edit dislike song rules",
|
||||
"setting__other_listdata": "List Data Cleanup",
|
||||
"setting__other_listdata_clear_btn": "Clear my list data",
|
||||
"setting__other_listdata_clear_tip_confirm": "This will clear all lists you have created and all songs in your favourites, do you really want to continue?",
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"list__add_to": "添加到...",
|
||||
"list__collect": "收藏",
|
||||
"list__copy_name": "复制歌曲名",
|
||||
"list__dislike": "不喜欢",
|
||||
"list__download": "下载",
|
||||
"list__export_part_desc": "选择列表文件保存位置",
|
||||
"list__file": "定位文件",
|
||||
|
@ -381,6 +382,9 @@
|
|||
"setting__desktop_lyric_shadow_color": "阴影颜色",
|
||||
"setting__desktop_lyric_show_taskbar": "在任务栏显示歌词进程(此设置用于在录屏软件无法捕获歌词窗口时的变通解决方法)",
|
||||
"setting__desktop_lyric_unplay_color": "未播放颜色",
|
||||
"setting__dislike_list_save_btn": "保存",
|
||||
"setting__dislike_list_tips": "1. 每条一行,若歌曲或者歌手名字中存在“@”符号,需要将其替换成“#”\n2. 指定某歌手的某首歌:<歌曲名>@<歌手名>\n3. 指定某首歌:<歌曲名>\n4. 指定某歌手:@<歌手名>",
|
||||
"setting__dislike_list_title": "不喜欢的歌曲规则列表",
|
||||
"setting__download": "下载设置",
|
||||
"setting__download_data_embed": "是否将以下内容嵌入到音频文件中",
|
||||
"setting__download_embed_lyric": "歌词嵌入",
|
||||
|
@ -425,6 +429,9 @@
|
|||
"setting__hot_key_player_toggle_play": "播放/暂停控制",
|
||||
"setting__hot_key_player_volume_down": "减少音量",
|
||||
"setting__hot_key_player_volume_mute": "静音切换",
|
||||
"setting__hot_key_player_music_love": "收藏歌曲",
|
||||
"setting__hot_key_player_music_unlove": "取消收藏",
|
||||
"setting__hot_key_player_music_dislike": "不喜欢该歌曲",
|
||||
"setting__hot_key_player_volume_up": "增加音量",
|
||||
"setting__hot_key_tip_input": "请输入新的按键",
|
||||
"setting__hot_key_unset_input": "未设置",
|
||||
|
@ -448,6 +455,9 @@
|
|||
"setting__odc_clear_search_input": "离开搜索界面时清空搜索框",
|
||||
"setting__odc_clear_search_list": "离开搜索界面时清空搜索列表",
|
||||
"setting__other": "其他",
|
||||
"setting__other_dislike_list": "不喜欢的歌曲规则",
|
||||
"setting__other_dislike_list_label": "规则数量:",
|
||||
"setting__other_dislike_list_show_btn": "编辑不喜欢歌曲规则",
|
||||
"setting__other_listdata": "列表数据清理",
|
||||
"setting__other_listdata_clear_btn": "清空我的列表数据",
|
||||
"setting__other_listdata_clear_tip_confirm": "这将清理你创建的 所有列表 及收藏的 所有歌曲,是否真的要继续?",
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
"list__add_to": "添加到...",
|
||||
"list__collect": "收藏",
|
||||
"list__copy_name": "複製歌曲名",
|
||||
"list__dislike": "不喜歡",
|
||||
"list__download": "下載",
|
||||
"list__export_part_desc": "選擇列表文件保存位置",
|
||||
"list__file": "定位文件",
|
||||
|
@ -382,6 +383,9 @@
|
|||
"setting__desktop_lyric_shadow_color": "陰影顏色",
|
||||
"setting__desktop_lyric_show_taskbar": "在任務欄顯示歌詞進程(此設置用於在錄屏軟件無法捕獲歌詞窗口時的變通解決方法)",
|
||||
"setting__desktop_lyric_unplay_color": "未播放顏色",
|
||||
"setting__dislike_list_save_btn": "保存",
|
||||
"setting__dislike_list_tips": "1. 每條一行,若歌曲或者歌手名字中存在“@”符號,需要將其替換成“#”\n2. 指定某歌手的某首歌:<歌曲名>@<歌手名>\n3. 指定某首歌:<歌曲名>\n4. 指定某歌手:@<歌手名>",
|
||||
"setting__dislike_list_title": "不喜歡的歌曲規則列表",
|
||||
"setting__download": "下載設置",
|
||||
"setting__download_data_embed": "是否將以下內容嵌入到音頻文件中",
|
||||
"setting__download_embed_lyric": "歌詞嵌入",
|
||||
|
@ -426,6 +430,9 @@
|
|||
"setting__hot_key_player_toggle_play": "播放/暫停控制",
|
||||
"setting__hot_key_player_volume_down": "減少音量",
|
||||
"setting__hot_key_player_volume_mute": "靜音切換",
|
||||
"setting__hot_key_player_music_love": "收藏歌曲",
|
||||
"setting__hot_key_player_music_unlove": "取消收藏",
|
||||
"setting__hot_key_player_music_dislike": "不喜歡該歌曲",
|
||||
"setting__hot_key_player_volume_up": "增加音量",
|
||||
"setting__hot_key_tip_input": "請輸入新的按鍵",
|
||||
"setting__hot_key_unset_input": "未設置",
|
||||
|
@ -449,6 +456,9 @@
|
|||
"setting__odc_clear_search_input": "離開搜索界面時清空搜索框",
|
||||
"setting__odc_clear_search_list": "離開搜索界面時清空搜索列表",
|
||||
"setting__other": "其他",
|
||||
"setting__other_dislike_list": "不喜歡的歌曲規則",
|
||||
"setting__other_dislike_list_label": "規則數量:",
|
||||
"setting__other_dislike_list_show_btn": "編輯不喜歡歌曲規則",
|
||||
"setting__other_listdata": "列表數據清理",
|
||||
"setting__other_listdata_clear_btn": "清空我的列表數據",
|
||||
"setting__other_listdata_clear_tip_confirm": "這將清理你創建的 所有列表 及收藏的 所有歌曲,是否真的要繼續?",
|
||||
|
|
|
@ -70,6 +70,23 @@ export default () => {
|
|||
return global.lx.worker.dbService.musicInfoOtherSourceCount()
|
||||
})
|
||||
|
||||
// =========================不喜欢的歌曲=========================
|
||||
mainHandle<LX.Dislike.DislikeInfo>(WIN_MAIN_RENDERER_EVENT_NAME.get_dislike_music_infos, async() => {
|
||||
return global.lx.worker.dbService.getDislikeListInfo()
|
||||
})
|
||||
mainHandle<LX.Dislike.DislikeMusicInfo[]>(WIN_MAIN_RENDERER_EVENT_NAME.add_dislike_music_infos, async({ params: infos }) => {
|
||||
await global.lx.worker.dbService.dislikeInfoAdd(infos)
|
||||
})
|
||||
mainHandle<string>(WIN_MAIN_RENDERER_EVENT_NAME.overwrite_dislike_music_infos, async({ params: rules }) => {
|
||||
await global.lx.worker.dbService.dislikeInfoOverwrite(rules)
|
||||
})
|
||||
// mainHandle<string[]>(WIN_MAIN_RENDERER_EVENT_NAME.remove_dislike_music_infos, async({ params: ids }) => {
|
||||
// await global.lx.worker.dbService.dislikeInfoRemove(ids)
|
||||
// })
|
||||
// mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.clear_dislike_music_infos, async() => {
|
||||
// await global.lx.worker.dbService.dislikeInfoClear()
|
||||
// })
|
||||
|
||||
|
||||
// =========================我的列表=========================
|
||||
// mainHandle<boolean>(WIN_MAIN_RENDERER_EVENT_NAME.get_playlist, async({ params: isIgnoredError = false }) => {
|
||||
|
|
|
@ -12,3 +12,4 @@ import '@common/types/desktop_lyric'
|
|||
import '@common/types/theme'
|
||||
import '@common/types/ipc_main'
|
||||
import '@common/types/sound_effect'
|
||||
import '@common/types/dislike_list'
|
||||
|
|
|
@ -74,6 +74,12 @@ declare namespace LX {
|
|||
position: number
|
||||
}
|
||||
|
||||
interface DislikeInfo {
|
||||
// type: 'music'
|
||||
content: string
|
||||
// meta: string | null
|
||||
}
|
||||
|
||||
interface MusicInfoOtherSource extends Omit<MusicInfoOnline, 'listId'> {
|
||||
source_id: string
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import Database from 'better-sqlite3'
|
|||
import path from 'path'
|
||||
import tables from './tables'
|
||||
import verifyDB from './verifyDB'
|
||||
// import migrateData from './migrate'
|
||||
import migrateData from './migrate'
|
||||
|
||||
let db: Database.Database
|
||||
|
||||
|
@ -37,7 +37,7 @@ export const init = (lxDataPath: string): boolean | null => {
|
|||
dbFileExists = false
|
||||
}
|
||||
|
||||
// if (dbFileExists) migrateData(db)
|
||||
if (dbFileExists) migrateData(db)
|
||||
|
||||
// https://www.sqlite.org/pragma.html#pragma_optimize
|
||||
if (dbFileExists) db.exec('PRAGMA optimize;')
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { init } from './db'
|
||||
import { exposeWorker } from '../utils/worker'
|
||||
import { list, lyric, music_url, music_other_source, download } from './modules/index'
|
||||
import { list, lyric, music_url, music_other_source, download, dislike_list } from './modules/index'
|
||||
|
||||
|
||||
const common = {
|
||||
init,
|
||||
}
|
||||
|
||||
exposeWorker(Object.assign(common, list, lyric, music_url, music_other_source, download))
|
||||
exposeWorker(Object.assign(common, list, lyric, music_url, music_other_source, download, dislike_list))
|
||||
|
||||
export type workerDBSeriveTypes = typeof common
|
||||
& typeof list
|
||||
|
@ -15,3 +15,4 @@ export type workerDBSeriveTypes = typeof common
|
|||
& typeof music_url
|
||||
& typeof music_other_source
|
||||
& typeof download
|
||||
& typeof dislike_list
|
||||
|
|
|
@ -1,24 +1,36 @@
|
|||
import type Database from 'better-sqlite3'
|
||||
|
||||
// const migrateV1 = (db: Database.Database) => {
|
||||
// const sql = `
|
||||
// DROP TABLE "main"."download_list";
|
||||
|
||||
// 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.exec(sql)
|
||||
// db.prepare('UPDATE "main"."db_info" SET "field_value"=@value WHERE "field_name"=@name').run({ name: 'version', value: '2' })
|
||||
// }
|
||||
|
||||
const migrateV1 = (db: Database.Database) => {
|
||||
const sql = `
|
||||
DROP TABLE "main"."download_list";
|
||||
|
||||
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")
|
||||
CREATE TABLE "dislike_list" (
|
||||
"type" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"meta" TEXT
|
||||
);
|
||||
`
|
||||
db.exec(sql)
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// import type Database from 'better-sqlite3'
|
||||
import { getDB } from '../../db'
|
||||
import {
|
||||
createQueryStatement,
|
||||
createInsertStatement,
|
||||
// createDeleteStatement,
|
||||
// createUpdateStatement,
|
||||
createClearStatement,
|
||||
} from './statements'
|
||||
|
||||
/**
|
||||
* 查询不喜欢歌曲列表
|
||||
*/
|
||||
export const queryDislikeList = () => {
|
||||
const queryStatement = createQueryStatement()
|
||||
return queryStatement.all() as LX.DBService.DislikeInfo[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入不喜欢歌曲并刷新顺序
|
||||
* @param infos 列表
|
||||
*/
|
||||
export const inertDislikeList = async(infos: LX.DBService.DislikeInfo[]) => {
|
||||
const db = getDB()
|
||||
const insertStatement = createInsertStatement()
|
||||
db.transaction((infos: LX.DBService.DislikeInfo[]) => {
|
||||
for (const info of infos) insertStatement.run(info)
|
||||
})(infos)
|
||||
}
|
||||
|
||||
/**
|
||||
* 覆盖并批量插入不喜欢歌曲并刷新顺序
|
||||
* @param infos 列表
|
||||
*/
|
||||
export const overwirteDislikeList = async(infos: LX.DBService.DislikeInfo[]) => {
|
||||
const db = getDB()
|
||||
const clearStatement = createClearStatement()
|
||||
const insertStatement = createInsertStatement()
|
||||
db.transaction((infos: LX.DBService.DislikeInfo[]) => {
|
||||
clearStatement.run()
|
||||
for (const info of infos) insertStatement.run(info)
|
||||
})(infos)
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 批量删除不喜欢歌曲
|
||||
// * @param ids 列表
|
||||
// */
|
||||
// export const deleteDislikeList = (ids: string[]) => {
|
||||
// const db = getDB()
|
||||
// const deleteStatement = createDeleteStatement()
|
||||
// db.transaction((ids: string[]) => {
|
||||
// for (const id of ids) deleteStatement.run(BigInt(id))
|
||||
// })(ids)
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 批量更新不喜欢歌曲
|
||||
// * @param urlInfo 列表
|
||||
// */
|
||||
// export const updateDislikeList = async(infos: LX.DBService.DislikeInfo[]) => {
|
||||
// const db = getDB()
|
||||
// const updateStatement = createUpdateStatement()
|
||||
// db.transaction((infos: LX.DBService.DislikeInfo[]) => {
|
||||
// for (const info of infos) updateStatement.run(info)
|
||||
// })(infos)
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 清空不喜欢歌曲列表
|
||||
// */
|
||||
// export const clearDislikeList = () => {
|
||||
// const clearStatement = createClearStatement()
|
||||
// clearStatement.run()
|
||||
// }
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import { SPLIT_CHAR } from '@common/constants'
|
||||
import {
|
||||
queryDislikeList,
|
||||
inertDislikeList,
|
||||
overwirteDislikeList,
|
||||
// updateDislikeList,
|
||||
// deleteDislikeList,
|
||||
// clearDislikeList,
|
||||
} from './dbHelper'
|
||||
|
||||
// let dislikeInfo: LX.Dislike.DislikeInfo
|
||||
|
||||
const toDBDislikeInfo = (musicInfos: string[]): LX.DBService.DislikeInfo[] => {
|
||||
const list: LX.DBService.DislikeInfo[] = []
|
||||
for (const item of musicInfos) {
|
||||
if (!item.trim()) continue
|
||||
list.push({
|
||||
content: item,
|
||||
})
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
const initDislikeList = () => {
|
||||
const dislikeInfo: LX.Dislike.DislikeInfo = {
|
||||
// musicIds: new Set<string>(),
|
||||
names: new Set<string>(),
|
||||
singerNames: new Set<string>(),
|
||||
musicNames: new Set<string>(),
|
||||
rules: '',
|
||||
}
|
||||
const list: string[] = []
|
||||
for (const item of queryDislikeList()) {
|
||||
if (!item) continue
|
||||
let [name, singer] = item.content.split(SPLIT_CHAR.DISLIKE_NAME)
|
||||
if (name) {
|
||||
name = name.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
|
||||
if (singer) {
|
||||
singer = singer.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
|
||||
const rule = `${name}${SPLIT_CHAR.DISLIKE_NAME}${singer}`
|
||||
dislikeInfo.names.add(rule)
|
||||
list.push(rule)
|
||||
} else {
|
||||
dislikeInfo.musicNames.add(name)
|
||||
list.push(name)
|
||||
}
|
||||
} else if (singer) {
|
||||
singer = singer.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
|
||||
dislikeInfo.singerNames.add(singer)
|
||||
list.push(`${SPLIT_CHAR.DISLIKE_NAME}${singer}`)
|
||||
}
|
||||
}
|
||||
|
||||
dislikeInfo.rules = list.join('\n') + '\n'
|
||||
|
||||
return dislikeInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取不喜欢列表信息
|
||||
* @returns 不喜欢列表信息
|
||||
*/
|
||||
export const getDislikeListInfo = (): LX.Dislike.DislikeInfo => {
|
||||
// if (!dislikeInfo) initDislikeList()
|
||||
return initDislikeList()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加信息
|
||||
* @param lists 列表信息
|
||||
*/
|
||||
export const dislikeInfoAdd = async(lists: LX.Dislike.DislikeMusicInfo[]) => {
|
||||
await inertDislikeList(lists.map(info => ({ content: `${info.name}${SPLIT_CHAR.DISLIKE_NAME}${info.singer}` })))
|
||||
}
|
||||
|
||||
/**
|
||||
* 覆盖列表信息
|
||||
* @param rules 规则信息
|
||||
*/
|
||||
export const dislikeInfoOverwrite = async(rules: string) => {
|
||||
await overwirteDislikeList(toDBDislikeInfo(rules.split('\n')))
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * 删除不喜欢列表
|
||||
// * @param ids 歌曲id
|
||||
// */
|
||||
// export const dislikeInfoRemove = (ids: string[]) => {
|
||||
// deleteDislikeList(ids)
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 清空不喜欢列表
|
||||
// */
|
||||
// export const dislikeInfoClear = () => {
|
||||
// clearDislikeList()
|
||||
// }
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import { getDB } from '../../db'
|
||||
|
||||
/**
|
||||
* 创建不喜欢列表查询语句
|
||||
* @returns 查询语句
|
||||
*/
|
||||
export const createQueryStatement = () => {
|
||||
const db = getDB()
|
||||
return db.prepare<[]>(`
|
||||
SELECT "content"
|
||||
FROM dislike_list
|
||||
WHERE "type"='music'
|
||||
`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建不喜欢记录插入语句
|
||||
* @returns 插入语句
|
||||
*/
|
||||
export const createInsertStatement = () => {
|
||||
const db = getDB()
|
||||
return db.prepare<[LX.DBService.DislikeInfo]>(`
|
||||
INSERT INTO "main"."dislike_list" ("type", "content")
|
||||
VALUES ('music', @content)`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建不喜欢记录清空语句
|
||||
* @returns 清空语句
|
||||
*/
|
||||
export const createClearStatement = () => {
|
||||
const db = getDB()
|
||||
return db.prepare<[]>(`
|
||||
DELETE FROM "main"."dislike_list"
|
||||
`)
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 创建不喜欢记录删除语句
|
||||
// * @returns 删除语句
|
||||
// */
|
||||
// export const createDeleteStatement = () => {
|
||||
// const db = getDB()
|
||||
// return db.prepare<[bigint]>(`
|
||||
// DELETE FROM "main"."dislike_list"
|
||||
// WHERE "id"=?
|
||||
// `)
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 创建不喜欢记录更新语句
|
||||
// * @returns 更新语句
|
||||
// */
|
||||
// export const createUpdateStatement = () => {
|
||||
// const db = getDB()
|
||||
// return db.prepare<[LX.DBService.DislikeInfo]>(`
|
||||
// UPDATE "main"."dislike_list"
|
||||
// SET "name"=@name, "singer"=@singer
|
||||
// WHERE "id"=@id
|
||||
// `)
|
||||
// }
|
||||
|
|
@ -4,3 +4,4 @@ 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'
|
||||
export * as dislike_list from './dislike_list'
|
||||
|
|
|
@ -205,5 +205,12 @@ tables.set('download_list', `
|
|||
PRIMARY KEY("id")
|
||||
);
|
||||
`)
|
||||
tables.set('dislike_list', `
|
||||
CREATE TABLE "dislike_list" (
|
||||
"type" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"meta" TEXT
|
||||
);
|
||||
`)
|
||||
|
||||
export default tables
|
||||
|
|
|
@ -60,6 +60,10 @@ export default {
|
|||
type: String,
|
||||
default: 'auto',
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: 'auto',
|
||||
},
|
||||
},
|
||||
emits: ['after-enter', 'after-leave', 'close'],
|
||||
data() {
|
||||
|
@ -147,6 +151,7 @@ export default {
|
|||
maxWidth: this.maxWidth,
|
||||
minWidth: this.minWidth,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
maxHeight: this.maxHeight,
|
||||
}
|
||||
},
|
||||
|
|
|
@ -180,6 +180,7 @@ export default {
|
|||
const {
|
||||
handleSearch,
|
||||
handleOpenMusicDetail,
|
||||
handleDislikeMusic,
|
||||
} = useMusicActions({ props })
|
||||
|
||||
const {
|
||||
|
@ -199,6 +200,7 @@ export default {
|
|||
handleSearch,
|
||||
handleShowMusicAddModal,
|
||||
handleOpenMusicDetail,
|
||||
handleDislikeMusic,
|
||||
})
|
||||
|
||||
const handleListItemClick = (event, index) => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { computed, ref, reactive, nextTick } from '@common/utils/vueTools'
|
||||
import musicSdk from '@renderer/utils/musicSdk'
|
||||
import { useI18n } from '@renderer/plugins/i18n'
|
||||
import { hasDislike } from '@renderer/core/dislikeList'
|
||||
|
||||
export default ({
|
||||
props,
|
||||
|
@ -13,6 +14,7 @@ export default ({
|
|||
handleSearch,
|
||||
handleShowMusicAddModal,
|
||||
handleOpenMusicDetail,
|
||||
handleDislikeMusic,
|
||||
}) => {
|
||||
const itemMenuControl = reactive({
|
||||
play: true,
|
||||
|
@ -21,6 +23,7 @@ export default ({
|
|||
download: true,
|
||||
search: true,
|
||||
sourceDetail: true,
|
||||
dislike: true,
|
||||
})
|
||||
const t = useI18n()
|
||||
const menuLocation = reactive({ x: 0, y: 0 })
|
||||
|
@ -58,6 +61,11 @@ export default ({
|
|||
action: 'sourceDetail',
|
||||
disabled: !itemMenuControl.sourceDetail,
|
||||
},
|
||||
{
|
||||
name: t('list__dislike'),
|
||||
action: 'dislike',
|
||||
disabled: !itemMenuControl.dislike,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
|
@ -67,6 +75,8 @@ export default ({
|
|||
// this.listMenu.itemMenuControl.playLater =
|
||||
itemMenuControl.download = assertApiSupport(musicInfo.source)
|
||||
|
||||
itemMenuControl.dislike = !hasDislike(musicInfo)
|
||||
|
||||
if (props.checkApiSource) {
|
||||
itemMenuControl.playLater =
|
||||
itemMenuControl.play =
|
||||
|
@ -110,6 +120,9 @@ export default ({
|
|||
break
|
||||
case 'sourceDetail':
|
||||
handleOpenMusicDetail(index)
|
||||
case 'dislike':
|
||||
handleDislikeMusic(index)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@ import { useRouter } from '@common/utils/vueRouter'
|
|||
import musicSdk from '@renderer/utils/musicSdk'
|
||||
import { openUrl } from '@common/utils/electron'
|
||||
import { toOldMusicInfo } from '@renderer/utils'
|
||||
import { addDislikeInfo, hasDislike } from '@renderer/core/dislikeList'
|
||||
import { playNext } from '@renderer/core/player'
|
||||
import { playMusicInfo } from '@renderer/store/player/state'
|
||||
|
||||
|
||||
export default ({ props }) => {
|
||||
|
@ -24,8 +27,18 @@ export default ({ props }) => {
|
|||
openUrl(url)
|
||||
}
|
||||
|
||||
const handleDislikeMusic = async(index) => {
|
||||
const minfo = props.list[index]
|
||||
await addDislikeInfo([{ name: minfo.name, singer: minfo.singer }])
|
||||
if (!playMusicInfo.isTempPlay && hasDislike(playMusicInfo.musicInfo)) {
|
||||
playNext(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
handleSearch,
|
||||
handleOpenMusicDetail,
|
||||
handleDislikeMusic,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// import { toRaw } from '@common/utils/vueTools'
|
||||
import { action } from '@renderer/store/dislikeList'
|
||||
import {
|
||||
getDislikeListInfo,
|
||||
addDislikeInfo as addDislikeInfoRemote,
|
||||
overwirteDislikeInfo as overwirteDislikeInfoRemote,
|
||||
// updateDislikeInfo as updateDislikeInfoRemote,
|
||||
// removeDislikeInfo as removeDislikeInfoRemote,
|
||||
// clearDislikeInfo as clearDislikeInfoRemote,
|
||||
} from '@renderer/utils/ipc'
|
||||
|
||||
|
||||
export const initDislikeInfo = async() => {
|
||||
action.initDislikeInfo(await getDislikeListInfo())
|
||||
}
|
||||
|
||||
export const addDislikeInfo = async(infos: LX.Dislike.DislikeMusicInfo[]) => {
|
||||
await addDislikeInfoRemote(infos)
|
||||
return action.addDislikeInfo(infos)
|
||||
}
|
||||
|
||||
export const overwirteDislikeInfo = async(rules: string) => {
|
||||
await overwirteDislikeInfoRemote(rules)
|
||||
return action.overwirteDislikeInfo(rules)
|
||||
}
|
||||
|
||||
// export const updateDislikeInfo = async(info: LX.Dislike.ListItem) => {
|
||||
// await updateDislikeInfoRemote([toRaw(info)])
|
||||
// action.updateDislikeInfo(info)
|
||||
// }
|
||||
|
||||
// export const removeDislikeInfo = async(ids: string[]) => {
|
||||
// await removeDislikeInfoRemote(toRaw(ids))
|
||||
// action.removeDislikeInfo(ids)
|
||||
// }
|
||||
|
||||
|
||||
// export const clearDislikeInfo = async() => {
|
||||
// await clearDislikeInfoRemote()
|
||||
// action.clearDislikeInfo()
|
||||
// }
|
||||
|
||||
|
||||
export const hasDislike = (info: LX.Music.MusicInfo | null) => {
|
||||
if (!info) return false
|
||||
return action.hasDislike(info)
|
||||
}
|
||||
|
|
@ -299,6 +299,7 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
|
|||
list: currentList,
|
||||
playedList,
|
||||
playerMusicInfo: currentList[playInfo.playerPlayIndex],
|
||||
isNext: true,
|
||||
})
|
||||
|
||||
if (!filteredList.length) {
|
||||
|
@ -398,6 +399,7 @@ export const playPrev = async(isAutoToggle = false): Promise<void> => {
|
|||
list: currentList,
|
||||
playedList,
|
||||
playerMusicInfo: currentList[playInfo.playerPlayIndex],
|
||||
isNext: false,
|
||||
})
|
||||
if (!filteredList.length) {
|
||||
handleToggleStop()
|
||||
|
|
|
@ -2,6 +2,7 @@ import { toRaw, markRawList } from '@common/utils/vueTools'
|
|||
import { qualityList } from '@renderer/store'
|
||||
import { clearPlayedList } from '@renderer/store/player/action'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { dislikeInfo } from '@renderer/store/dislikeList'
|
||||
|
||||
export const getPlayType = (highQuality: boolean, musicInfo: LX.Music.MusicInfo | LX.Download.ListItem): LX.Quality | null => {
|
||||
if ('progress' in musicInfo || musicInfo.source == 'local') return null
|
||||
|
@ -14,11 +15,12 @@ export const getPlayType = (highQuality: boolean, musicInfo: LX.Music.MusicInfo
|
|||
/**
|
||||
* 过滤列表中已播放的歌曲
|
||||
*/
|
||||
export const filterList = async({ playedList, listId, list, playerMusicInfo }: {
|
||||
export const filterList = async({ playedList, listId, list, playerMusicInfo, isNext }: {
|
||||
playedList: LX.Player.PlayMusicInfo[]
|
||||
listId: string
|
||||
list: Array<LX.Music.MusicInfo | LX.Download.ListItem>
|
||||
playerMusicInfo?: LX.Music.MusicInfo | LX.Download.ListItem
|
||||
isNext: boolean
|
||||
}) => {
|
||||
// if (this.list.listName === null) return
|
||||
// console.log(isCheckFile)
|
||||
|
@ -28,6 +30,8 @@ export const filterList = async({ playedList, listId, list, playerMusicInfo }: {
|
|||
playedList: toRaw(playedList),
|
||||
savePath: appSetting['download.savePath'],
|
||||
playerMusicInfo: toRaw(playerMusicInfo),
|
||||
dislikeInfo: { names: toRaw(dislikeInfo.names), musicNames: toRaw(dislikeInfo.musicNames), singerNames: toRaw(dislikeInfo.singerNames) },
|
||||
isNext,
|
||||
})
|
||||
|
||||
if (!filteredList.length && playedList.length) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { play, playList } from '@renderer/core/player'
|
|||
import { onBeforeUnmount } from '@common/utils/vueTools'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { playMusicInfo } from '@renderer/store/player/state'
|
||||
import { initDislikeInfo } from '@renderer/core/dislikeList'
|
||||
|
||||
const initPrevPlayInfo = async() => {
|
||||
const info = await getPlayInfo()
|
||||
|
@ -48,6 +49,7 @@ export default () => {
|
|||
window.app_event.myListUpdate(ids)
|
||||
})
|
||||
window.lxData.userLists = await getUserLists() // 获取用户列表
|
||||
await initDislikeInfo() // 获取不喜欢列表
|
||||
await initPrevPlayInfo().catch(err => {
|
||||
log.error(err)
|
||||
}) // 初始化上次的歌曲播放信息
|
||||
|
|
|
@ -32,6 +32,9 @@ import { HOTKEY_PLAYER } from '@common/hotKey'
|
|||
import { playNext, pause, playPrev, togglePlay } from '@renderer/core/player'
|
||||
import usePlaybackRate from './usePlaybackRate'
|
||||
import useSoundEffect from './useSoundEffect'
|
||||
import { addListMusics, removeListMusics } from '@renderer/store/list/action'
|
||||
import { loveList } from '@renderer/store/list/state'
|
||||
import { addDislikeInfo } from '@renderer/core/dislikeList'
|
||||
|
||||
|
||||
export default () => {
|
||||
|
@ -90,6 +93,23 @@ export default () => {
|
|||
setStop()
|
||||
}
|
||||
|
||||
const collectMusic = () => {
|
||||
if (!playMusicInfo.musicInfo) return
|
||||
void addListMusics(loveList.id, ['progress' in playMusicInfo.musicInfo ? playMusicInfo.musicInfo.metadata.musicInfo : playMusicInfo.musicInfo])
|
||||
}
|
||||
const unCollectMusic = () => {
|
||||
if (!playMusicInfo.musicInfo) return
|
||||
void removeListMusics({ listId: loveList.id, ids: ['progress' in playMusicInfo.musicInfo ? playMusicInfo.musicInfo.metadata.musicInfo.id : playMusicInfo.musicInfo.id] })
|
||||
}
|
||||
const dislikeMusic = async() => {
|
||||
if (!playMusicInfo.musicInfo) return
|
||||
const minfo = 'progress' in playMusicInfo.musicInfo ? playMusicInfo.musicInfo.metadata.musicInfo : playMusicInfo.musicInfo
|
||||
await addDislikeInfo([{ name: minfo.name, singer: minfo.singer }])
|
||||
if (!playMusicInfo.isTempPlay) {
|
||||
playNext(true)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => appSetting['player.togglePlayMethod'], newValue => {
|
||||
// setLoopPlay(newValue == 'singleLoop')
|
||||
if (playedList.length) clearPlayedList()
|
||||
|
@ -102,6 +122,9 @@ export default () => {
|
|||
window.key_event.on(HOTKEY_PLAYER.next.action, handlePlayNext)
|
||||
window.key_event.on(HOTKEY_PLAYER.prev.action, handlePlayPrev)
|
||||
window.key_event.on(HOTKEY_PLAYER.toggle_play.action, togglePlay)
|
||||
window.key_event.on(HOTKEY_PLAYER.music_love.action, collectMusic)
|
||||
window.key_event.on(HOTKEY_PLAYER.music_unlove.action, unCollectMusic)
|
||||
window.key_event.on(HOTKEY_PLAYER.music_dislike.action, dislikeMusic)
|
||||
|
||||
window.app_event.on('play', setPlayStatus)
|
||||
window.app_event.on('pause', setPauseStatus)
|
||||
|
@ -119,6 +142,10 @@ export default () => {
|
|||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
window.key_event.off(HOTKEY_PLAYER.prev.action, handlePlayPrev)
|
||||
window.key_event.off(HOTKEY_PLAYER.toggle_play.action, togglePlay)
|
||||
window.key_event.off(HOTKEY_PLAYER.music_love.action, collectMusic)
|
||||
window.key_event.off(HOTKEY_PLAYER.music_unlove.action, unCollectMusic)
|
||||
window.key_event.off(HOTKEY_PLAYER.music_dislike.action, dislikeMusic)
|
||||
|
||||
|
||||
window.app_event.off('play', setPlayStatus)
|
||||
window.app_event.off('pause', setPauseStatus)
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import { markRaw } from '@common/utils/vueTools'
|
||||
|
||||
|
||||
import { dislikeInfo } from './state'
|
||||
import { SPLIT_CHAR } from '@common/constants'
|
||||
|
||||
|
||||
export const hasDislike = (info: LX.Music.MusicInfo) => {
|
||||
const name = info.name?.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim() ?? ''
|
||||
const singer = info.singer?.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim() ?? ''
|
||||
|
||||
return dislikeInfo.musicNames.has(name) || dislikeInfo.singerNames.has(singer) ||
|
||||
dislikeInfo.names.has(`${name}${SPLIT_CHAR.DISLIKE_NAME}${singer}`)
|
||||
}
|
||||
|
||||
export const initDislikeInfo = ({ musicNames, rules, names, singerNames }: LX.Dislike.DislikeInfo) => {
|
||||
dislikeInfo.names = markRaw(names)
|
||||
dislikeInfo.singerNames = markRaw(singerNames)
|
||||
dislikeInfo.musicNames = markRaw(musicNames)
|
||||
dislikeInfo.rules = rules
|
||||
}
|
||||
|
||||
const initNameSet = () => {
|
||||
dislikeInfo.names.clear()
|
||||
dislikeInfo.musicNames.clear()
|
||||
dislikeInfo.singerNames.clear()
|
||||
const list: string[] = []
|
||||
for (const item of dislikeInfo.rules.split('\n')) {
|
||||
if (!item) continue
|
||||
let [name, singer] = item.split(SPLIT_CHAR.DISLIKE_NAME)
|
||||
if (name) {
|
||||
name = name.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
|
||||
if (singer) {
|
||||
singer = singer.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
|
||||
const rule = `${name}${SPLIT_CHAR.DISLIKE_NAME}${singer}`
|
||||
dislikeInfo.names.add(rule)
|
||||
list.push(rule)
|
||||
} else {
|
||||
dislikeInfo.musicNames.add(name)
|
||||
list.push(name)
|
||||
}
|
||||
} else if (singer) {
|
||||
singer = singer.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
|
||||
dislikeInfo.singerNames.add(singer)
|
||||
list.push(`${SPLIT_CHAR.DISLIKE_NAME}${singer}`)
|
||||
}
|
||||
}
|
||||
dislikeInfo.rules = list.join('\n') + '\n'
|
||||
}
|
||||
|
||||
export const addDislikeInfo = (infos: LX.Dislike.DislikeMusicInfo[]) => {
|
||||
dislikeInfo.rules += '\n' + infos.map(info => `${info.name ?? ''}${SPLIT_CHAR.DISLIKE_NAME}${info.singer ?? ''}`).join('\n')
|
||||
initNameSet()
|
||||
return dislikeInfo.rules
|
||||
}
|
||||
|
||||
export const overwirteDislikeInfo = (rules: string) => {
|
||||
dislikeInfo.rules = rules
|
||||
initNameSet()
|
||||
return dislikeInfo.rules
|
||||
}
|
||||
|
||||
|
||||
// export const updateDislikeInfo = (info: LX.Dislike.ListItem) => {
|
||||
// const targetInfo = dislikeInfo.list.find(i => i.id == info.id)
|
||||
// if (!targetInfo) return
|
||||
// targetInfo.name = info.name
|
||||
// targetInfo.singer = info.singer
|
||||
// initNameSet()
|
||||
// }
|
||||
|
||||
// export const removeDislikeInfo = (ids: string[]) => {
|
||||
// for (const id of ids) {
|
||||
// dislikeInfo.list.splice(dislikeInfo.list.findIndex(info => info.id == id), 1)
|
||||
// }
|
||||
// initNameSet()
|
||||
// }
|
||||
|
||||
// export const clearDislikeInfo = () => {
|
||||
// dislikeInfo.rules = ''
|
||||
// initNameSet()
|
||||
// }
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
export * as action from './action'
|
||||
export * from './state'
|
|
@ -0,0 +1,11 @@
|
|||
import { markRaw } from '@common/utils/vueTools'
|
||||
|
||||
// import { deduplicationList } from '@common/utils/renderer'
|
||||
|
||||
|
||||
export const dislikeInfo: LX.Dislike.DislikeInfo = markRaw({
|
||||
names: markRaw(new Set()),
|
||||
musicNames: markRaw(new Set()),
|
||||
singerNames: markRaw(new Set()),
|
||||
rules: '',
|
||||
})
|
|
@ -73,7 +73,7 @@ export const setPlayListId = (listId: string | null) => {
|
|||
playInfo.playerListId = listId
|
||||
}
|
||||
|
||||
export const getList = (listId: string | null): LX.Music.MusicInfo[] | LX.Download.ListItem[] => {
|
||||
export const getList = (listId: string | null): Array<LX.Music.MusicInfo | LX.Download.ListItem> => {
|
||||
return listId == LIST_IDS.DOWNLOAD ? downloadList : getListMusicsFromCache(listId)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,3 +15,4 @@ import '@common/types/ipc_renderer'
|
|||
import '@common/types/config_files'
|
||||
import '@common/types/music_metadata'
|
||||
import '@common/types/sound_effect'
|
||||
import '@common/types/dislike_list'
|
||||
|
|
|
@ -40,6 +40,24 @@ export const getOtherSourceCount = async() => {
|
|||
return rendererInvoke<number>(WIN_MAIN_RENDERER_EVENT_NAME.get_other_source_count)
|
||||
}
|
||||
|
||||
export const getDislikeListInfo = async(): Promise<LX.Dislike.DislikeInfo> => {
|
||||
return rendererInvoke<LX.Dislike.DislikeInfo>(WIN_MAIN_RENDERER_EVENT_NAME.get_dislike_music_infos)
|
||||
}
|
||||
export const addDislikeInfo = async(dislikeInfo: LX.Dislike.DislikeMusicInfo[]) => {
|
||||
return rendererInvoke<LX.Dislike.DislikeMusicInfo[]>(WIN_MAIN_RENDERER_EVENT_NAME.add_dislike_music_infos, dislikeInfo)
|
||||
}
|
||||
export const overwirteDislikeInfo = async(dislikeInfo: string) => {
|
||||
return rendererInvoke<string>(WIN_MAIN_RENDERER_EVENT_NAME.overwrite_dislike_music_infos, dislikeInfo)
|
||||
}
|
||||
// export const updateDislikeInfo = async(dislikeInfo: LX.Dislike.ListItem[]) => {
|
||||
// await rendererInvoke<LX.Dislike.ListItem[]>(WIN_MAIN_RENDERER_EVENT_NAME.update_dislike_music_infos, dislikeInfo)
|
||||
// }
|
||||
// export const removeDislikeInfo = async(ids: string[]) => {
|
||||
// await rendererInvoke<string[]>(WIN_MAIN_RENDERER_EVENT_NAME.remove_dislike_music_infos, ids)
|
||||
// }
|
||||
// export const clearDislikeInfo = async() => {
|
||||
// await rendererInvoke(WIN_MAIN_RENDERER_EVENT_NAME.clear_dislike_music_infos)
|
||||
// }
|
||||
|
||||
export const getHotKeyConfig = async() => {
|
||||
return rendererInvoke<LX.HotKeyConfigAll>(WIN_MAIN_RENDERER_EVENT_NAME.get_hot_key)
|
||||
|
@ -332,6 +350,11 @@ export const allHotKeys = markRaw({
|
|||
action: hotKeys.HOTKEY_PLAYER.next.action,
|
||||
type: APP_EVENT_NAMES.winMainName,
|
||||
},
|
||||
{
|
||||
name: hotKeys.HOTKEY_PLAYER.music_dislike.name,
|
||||
action: hotKeys.HOTKEY_PLAYER.music_dislike.action,
|
||||
type: APP_EVENT_NAMES.winMainName,
|
||||
},
|
||||
{
|
||||
name: hotKeys.HOTKEY_COMMON.focusSearchInput.name,
|
||||
action: hotKeys.HOTKEY_COMMON.focusSearchInput.action,
|
||||
|
@ -394,6 +417,21 @@ export const allHotKeys = markRaw({
|
|||
action: hotKeys.HOTKEY_PLAYER.volume_mute.action,
|
||||
type: APP_EVENT_NAMES.winMainName,
|
||||
},
|
||||
{
|
||||
name: hotKeys.HOTKEY_PLAYER.music_love.name,
|
||||
action: hotKeys.HOTKEY_PLAYER.music_love.action,
|
||||
type: APP_EVENT_NAMES.winMainName,
|
||||
},
|
||||
{
|
||||
name: hotKeys.HOTKEY_PLAYER.music_unlove.name,
|
||||
action: hotKeys.HOTKEY_PLAYER.music_unlove.action,
|
||||
type: APP_EVENT_NAMES.winMainName,
|
||||
},
|
||||
{
|
||||
name: hotKeys.HOTKEY_PLAYER.music_dislike.name,
|
||||
action: hotKeys.HOTKEY_PLAYER.music_dislike.action,
|
||||
type: APP_EVENT_NAMES.winMainName,
|
||||
},
|
||||
{
|
||||
name: hotKeys.HOTKEY_DESKTOP_LYRIC.toggle_visible.name,
|
||||
action: hotKeys.HOTKEY_DESKTOP_LYRIC.toggle_visible.action,
|
||||
|
|
|
@ -201,6 +201,7 @@ export default {
|
|||
handleSearch,
|
||||
handleOpenMusicDetail,
|
||||
handleCopyName,
|
||||
handleDislikeMusic,
|
||||
handleRemoveMusic,
|
||||
} = useMusicActions({ props, list, removeAllSelect, selectedList })
|
||||
|
||||
|
@ -223,6 +224,7 @@ export default {
|
|||
handleShowSortModal,
|
||||
handleOpenMusicDetail,
|
||||
handleCopyName,
|
||||
handleDislikeMusic,
|
||||
handleRemoveMusic,
|
||||
})
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { computed, ref, shallowReactive, reactive, nextTick } from '@common/utils/vueTools'
|
||||
import musicSdk from '@renderer/utils/musicSdk'
|
||||
import { useI18n } from '@renderer/plugins/i18n'
|
||||
import { hasDislike } from '@renderer/core/dislikeList'
|
||||
|
||||
export default ({
|
||||
assertApiSupport,
|
||||
|
@ -15,6 +16,7 @@ export default ({
|
|||
handleShowSortModal,
|
||||
handleOpenMusicDetail,
|
||||
handleCopyName,
|
||||
handleDislikeMusic,
|
||||
handleRemoveMusic,
|
||||
}) => {
|
||||
const itemMenuControl = reactive({
|
||||
|
@ -26,6 +28,7 @@ export default ({
|
|||
sort: true,
|
||||
download: true,
|
||||
search: true,
|
||||
dislike: true,
|
||||
remove: true,
|
||||
sourceDetail: true,
|
||||
})
|
||||
|
@ -80,6 +83,11 @@ export default ({
|
|||
action: 'search',
|
||||
disabled: !itemMenuControl.search,
|
||||
},
|
||||
{
|
||||
name: t('list__dislike'),
|
||||
action: 'dislike',
|
||||
disabled: !itemMenuControl.dislike,
|
||||
},
|
||||
{
|
||||
name: t('list__remove'),
|
||||
action: 'remove',
|
||||
|
@ -94,6 +102,8 @@ export default ({
|
|||
// itemMenuControl.playLater =
|
||||
itemMenuControl.download = assertApiSupport(musicInfo.source) && musicInfo.source != 'local'
|
||||
|
||||
itemMenuControl.dislike = !hasDislike(musicInfo)
|
||||
|
||||
menuLocation.x = event.pageX
|
||||
menuLocation.y = event.pageY
|
||||
|
||||
|
@ -138,6 +148,9 @@ export default ({
|
|||
case 'search':
|
||||
handleSearch(index)
|
||||
break
|
||||
case 'dislike':
|
||||
handleDislikeMusic(index)
|
||||
break
|
||||
case 'remove':
|
||||
handleRemoveMusic(index)
|
||||
break
|
||||
|
|
|
@ -6,6 +6,9 @@ import { useI18n } from '@renderer/plugins/i18n'
|
|||
import { removeListMusics } from '@renderer/store/list/action'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { toOldMusicInfo } from '@renderer/utils/index'
|
||||
import { addDislikeInfo, hasDislike } from '@renderer/core/dislikeList'
|
||||
import { playNext } from '@renderer/core/player'
|
||||
import { playMusicInfo } from '@renderer/store/player/state'
|
||||
|
||||
|
||||
export default ({ props, list, selectedList, removeAllSelect }) => {
|
||||
|
@ -34,6 +37,14 @@ export default ({ props, list, selectedList, removeAllSelect }) => {
|
|||
clipboardWriteText(appSetting['download.fileName'].replace('歌名', minfo.name).replace('歌手', minfo.singer))
|
||||
}
|
||||
|
||||
const handleDislikeMusic = async(index) => {
|
||||
const minfo = list.value[index]
|
||||
await addDislikeInfo([{ name: minfo.name, singer: minfo.singer }])
|
||||
if (!playMusicInfo.isTempPlay && hasDislike(playMusicInfo.musicInfo)) {
|
||||
playNext(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveMusic = async(index, single) => {
|
||||
if (selectedList.value.length && !single) {
|
||||
const confirm = await (selectedList.value.length > 1
|
||||
|
@ -55,6 +66,7 @@ export default ({ props, list, selectedList, removeAllSelect }) => {
|
|||
handleSearch,
|
||||
handleOpenMusicDetail,
|
||||
handleCopyName,
|
||||
handleDislikeMusic,
|
||||
handleRemoveMusic,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
<template lang="pug">
|
||||
material-modal(:show="modelValue" teleport="#view" height="80%" width="80%" @close="$emit('update:modelValue', false)")
|
||||
main(:class="$style.main")
|
||||
h2 {{ $t('setting__dislike_list_title') }}
|
||||
div(:class="$style.content")
|
||||
textarea(v-model="rules" :class="$style.textarea")
|
||||
div(:class="$style.footer")
|
||||
div(:class="$style.tips") {{ $t('setting__dislike_list_tips') }}
|
||||
base-btn(:class="$style.btn" @click="handleSave") {{ $t('setting__dislike_list_save_btn') }}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { watch, ref } from '@common/utils/vueTools'
|
||||
import { overwirteDislikeInfo } from '@renderer/core/dislikeList'
|
||||
import { dislikeInfo } from '@renderer/store/dislikeList'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'onRuleUpdate'],
|
||||
setup(props, { emit }) {
|
||||
const rules = ref('')
|
||||
|
||||
const handleSave = async() => {
|
||||
await overwirteDislikeInfo(rules.value)
|
||||
emit('onRuleUpdate')
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, (visible) => {
|
||||
if (!visible) return
|
||||
rules.value = dislikeInfo.rules
|
||||
})
|
||||
|
||||
return {
|
||||
rules,
|
||||
handleSave,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.main {
|
||||
// padding: 15px;
|
||||
// max-width: 400px;
|
||||
// min-width: 460px;
|
||||
// min-height: 200px;
|
||||
// width: ;
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
// min-height: 0;
|
||||
// max-height: 100%;
|
||||
// overflow: hidden;
|
||||
h2 {
|
||||
margin: 20px;
|
||||
font-size: 16px;
|
||||
color: var(--color-font);
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: auto;
|
||||
// min-height: 100px;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
padding: 0 15px;
|
||||
}
|
||||
.textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-primary-light-200-alpha-900);
|
||||
box-sizing: border-box;
|
||||
font-family: inherit;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.footer {
|
||||
box-sizing: border-box;
|
||||
flex: none;
|
||||
// width: @width;
|
||||
padding: 15px 15px;
|
||||
// padding: 2px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.tips {
|
||||
// padding: 10px 15px;
|
||||
font-size: 12px;
|
||||
line-height: 1.25;
|
||||
color: var(--color-550);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.btn {
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -34,6 +34,16 @@ dd
|
|||
base-btn.btn(min :disabled="isDisabledMusicUrlCacheClear" @click="handleClearMusicUrlCache") {{ $t('setting__other_music_url_clear_btn') }}
|
||||
base-btn.btn(min :disabled="isDisabledLyricRawCacheClear" @click="handleClearLyricRawCache") {{ $t('setting__other_lyric_raw_clear_btn') }}
|
||||
|
||||
dd
|
||||
h3#other_lyric_edited {{ $t('setting__other_dislike_list') }}
|
||||
div
|
||||
.p
|
||||
| {{ $t('setting__other_dislike_list_label') }}
|
||||
span.auto-hidden {{ dislikeRuleCount }}
|
||||
.p
|
||||
base-btn.btn(min @click="isShowDislikeList = true") {{ $t('setting__other_dislike_list_show_btn') }}
|
||||
DislikeListModal(v-model="isShowDislikeList" @on-rule-update="handleCountRules")
|
||||
|
||||
dd
|
||||
h3#other_lyric_edited {{ $t('setting__other_lyric_edited_cache') }}
|
||||
div
|
||||
|
@ -65,9 +75,14 @@ import { dialog } from '@renderer/plugins/Dialog'
|
|||
import { useI18n } from '@renderer/plugins/i18n'
|
||||
import { appSetting, updateSetting } from '@renderer/store/setting'
|
||||
import { overwriteListFull } from '@renderer/store/list/listManage'
|
||||
import { dislikeInfo } from '@renderer/store/dislikeList'
|
||||
import DislikeListModal from './DislikeListModal.vue'
|
||||
|
||||
export default {
|
||||
name: 'SettingOther',
|
||||
components: {
|
||||
DislikeListModal,
|
||||
},
|
||||
setup() {
|
||||
const t = useI18n()
|
||||
|
||||
|
@ -135,6 +150,11 @@ export default {
|
|||
}
|
||||
refreshMusicUrlCount()
|
||||
|
||||
const dislikeRuleCount = ref(dislikeInfo.musicNames.size + dislikeInfo.singerNames.size + dislikeInfo.names.size)
|
||||
const isShowDislikeList = ref(false)
|
||||
const handleCountRules = () => {
|
||||
dislikeRuleCount.value = dislikeInfo.musicNames.size + dislikeInfo.singerNames.size + dislikeInfo.names.size
|
||||
}
|
||||
|
||||
const lyricRawCount = ref(0)
|
||||
const isDisabledLyricRawCacheClear = ref(false)
|
||||
|
@ -204,6 +224,10 @@ export default {
|
|||
isDisabledMusicUrlCacheClear,
|
||||
handleClearMusicUrlCache,
|
||||
|
||||
dislikeRuleCount,
|
||||
isShowDislikeList,
|
||||
handleCountRules,
|
||||
|
||||
lyricRawCount,
|
||||
isDisabledLyricRawCacheClear,
|
||||
handleClearLyricRawCache,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// import { throttle } from '@common/utils'
|
||||
|
||||
import { SPLIT_CHAR } from '@common/constants'
|
||||
import { filterFileName, sortInsert, similar, arrPushByPosition, arrShuffle } from '@common/utils/common'
|
||||
import { joinPath, saveStrToFile } from '@common/utils/nodejs'
|
||||
import { createLocalMusicInfo } from '@renderer/utils/music'
|
||||
|
@ -8,7 +9,7 @@ import { createLocalMusicInfo } from '@renderer/utils/music'
|
|||
/**
|
||||
* 过滤列表中已播放的歌曲
|
||||
*/
|
||||
export const filterMusicList = async({ playedList, listId, list, savePath, playerMusicInfo }: {
|
||||
export const filterMusicList = async({ playedList, listId, list, savePath, playerMusicInfo, dislikeInfo, isNext }: {
|
||||
/**
|
||||
* 已播放列表
|
||||
*/
|
||||
|
@ -29,15 +30,34 @@ export const filterMusicList = async({ playedList, listId, list, savePath, playe
|
|||
* 播放器内当前歌曲(`playInfo.playerPlayIndex`指向的歌曲)
|
||||
*/
|
||||
playerMusicInfo?: LX.Music.MusicInfo | LX.Download.ListItem
|
||||
/**
|
||||
* 不喜欢的歌曲名字列表
|
||||
*/
|
||||
dislikeInfo: Omit<LX.Dislike.DislikeInfo, 'rules'>
|
||||
|
||||
isNext: boolean
|
||||
}) => {
|
||||
let playerIndex = -1
|
||||
|
||||
let canPlayList: Array<LX.Music.MusicInfo | LX.Download.ListItem> = []
|
||||
const filteredPlayedList = playedList.filter(pmInfo => pmInfo.listId == listId && !pmInfo.isTempPlay).map(({ musicInfo }) => musicInfo)
|
||||
const hasDislike = (info: LX.Music.MusicInfo) => {
|
||||
const name = info.name?.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim() ?? ''
|
||||
const singer = info.singer?.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim() ?? ''
|
||||
|
||||
return dislikeInfo.musicNames.has(name) || dislikeInfo.singerNames.has(singer) ||
|
||||
dislikeInfo.names.has(`${name}${SPLIT_CHAR.DISLIKE_NAME}${singer}`)
|
||||
}
|
||||
|
||||
let isDislike = false
|
||||
const filteredList: Array<LX.Music.MusicInfo | LX.Download.ListItem> = list.filter(s => {
|
||||
// if (!assertApiSupport(s.source)) return false
|
||||
if ('progress' in s && !s.isComplate) return false
|
||||
if ('progress' in s) {
|
||||
if (!s.isComplate) return false
|
||||
} else if (hasDislike(s)) {
|
||||
if (s.id != playerMusicInfo?.id) return false
|
||||
isDislike = true
|
||||
}
|
||||
|
||||
canPlayList.push(s)
|
||||
|
||||
|
@ -49,7 +69,34 @@ export const filterMusicList = async({ playedList, listId, list, savePath, playe
|
|||
return true
|
||||
})
|
||||
if (playerMusicInfo) {
|
||||
playerIndex = (filteredList.length ? filteredList : canPlayList).findIndex(m => m.id == playerMusicInfo.id)
|
||||
if (isDislike) {
|
||||
if (filteredList.length <= 1) {
|
||||
filteredList.splice(0, 1)
|
||||
if (canPlayList.length > 1) {
|
||||
let currentMusicIndex = canPlayList.findIndex(m => m.id == playerMusicInfo.id)
|
||||
if (isNext) {
|
||||
playerIndex = currentMusicIndex - 1
|
||||
if (playerIndex < 0 && canPlayList.length > 1) playerIndex = canPlayList.length - 2
|
||||
} else {
|
||||
playerIndex = currentMusicIndex
|
||||
if (canPlayList.length <= 1) playerIndex = -1
|
||||
}
|
||||
canPlayList.splice(currentMusicIndex, 1)
|
||||
} else canPlayList.splice(0, 1)
|
||||
} else {
|
||||
let currentMusicIndex = filteredList.findIndex(m => m.id == playerMusicInfo.id)
|
||||
if (isNext) {
|
||||
playerIndex = currentMusicIndex - 1
|
||||
if (playerIndex < 0 && filteredList.length > 1) playerIndex = filteredList.length - 2
|
||||
} else {
|
||||
playerIndex = currentMusicIndex
|
||||
if (filteredList.length <= 1) playerIndex = -1
|
||||
}
|
||||
filteredList.splice(currentMusicIndex, 1)
|
||||
}
|
||||
} else {
|
||||
playerIndex = (filteredList.length ? filteredList : canPlayList).findIndex(m => m.id == playerMusicInfo.id)
|
||||
}
|
||||
}
|
||||
return {
|
||||
filteredList,
|
||||
|
|
Loading…
Reference in New Issue