新增“不喜欢歌曲”功能&新增“收藏歌曲”、“取消收藏”、“不喜欢该歌曲”快捷键设置

pull/1583/head
lyswhut 2023-09-04 14:13:47 +08:00
parent 3ca8f09971
commit baf69e397a
40 changed files with 861 additions and 26 deletions

View File

@ -9,6 +9,8 @@
- 新增我的列表名右键菜单-排序歌曲-随机乱序功能,使用它可以对选中列表内歌曲进行随机重排(#1440
- 新增数据同步服务端模式已认证设备列表管理,该功能位置:设置-数据同步-服务端模式-已认证设备列表
- 新增“不喜欢歌曲”功能,可以在我的列表或者在线列表内歌曲的右击菜单使用,还可以去“设置-其他”手动编辑不喜欢规则,注:“上一曲”、“下一曲”功能将跳过符合“不喜欢歌曲”规则的歌曲,但你仍可以手动播放这些歌曲
- 新增软件内快捷键“不喜欢该歌曲”设置,全局快捷键“收藏歌曲”、“取消收藏”、“不喜欢该歌曲”设置
### 优化

View File

@ -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',

View File

@ -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: {

View File

@ -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',

35
src/common/types/dislike_list.d.ts vendored Normal file
View File

@ -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
}
}
}

View File

@ -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?",

View File

@ -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": "这将清理你创建的 所有列表 及收藏的 所有歌曲,是否真的要继续?",

View File

@ -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": "這將清理你創建的 所有列表 及收藏的 所有歌曲,是否真的要繼續?",

View File

@ -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 }) => {

View File

@ -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'

View File

@ -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
}

View File

@ -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;')

View File

@ -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

View File

@ -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)

View File

@ -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()
// }

View File

@ -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()
// }

View File

@ -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
// `)
// }

View File

@ -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'

View File

@ -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

View File

@ -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,
}
},

View File

@ -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) => {

View File

@ -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
}
}

View File

@ -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,
}
}

View File

@ -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)
}

View File

@ -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()

View File

@ -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) {

View File

@ -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)
}) // 初始化上次的歌曲播放信息

View File

@ -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)

View File

@ -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()
// }

View File

@ -0,0 +1,3 @@
export * as action from './action'
export * from './state'

View File

@ -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: '',
})

View File

@ -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)
}

View File

@ -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'

View File

@ -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,

View File

@ -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,
})

View File

@ -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

View File

@ -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,
}
}

View File

@ -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>

View File

@ -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,

View File

@ -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,