mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-24 16:27:23 +08:00
✨ feat: 歌曲右键菜单添加下载歌词功能及下载设置中保存歌词文件选项
- 右键菜单新增"下载歌词"选项,支持获取歌词并保存为 .lrc 文件 - 如有翻译歌词会自动合并到 LRC 文件中 - 下载设置面板新增"单独保存歌词文件"开关 - 开启后下载歌曲时自动在同目录生成同名 .lrc 歌词文件 - 主进程新增 save-lyric-file IPC handler - 完成 5 种语言的国际化翻译
This commit is contained in:
@@ -64,6 +64,8 @@ export default {
|
|||||||
noPathSelected: 'Please select download path first',
|
noPathSelected: 'Please select download path first',
|
||||||
select: 'Select Folder',
|
select: 'Select Folder',
|
||||||
open: 'Open Folder',
|
open: 'Open Folder',
|
||||||
|
saveLyric: 'Save Lyrics File',
|
||||||
|
saveLyricDesc: 'Save a separate .lrc lyrics file alongside the downloaded song',
|
||||||
fileFormat: 'Filename Format',
|
fileFormat: 'Filename Format',
|
||||||
fileFormatDesc: 'Set how downloaded music files will be named',
|
fileFormatDesc: 'Set how downloaded music files will be named',
|
||||||
customFormat: 'Custom Format',
|
customFormat: 'Custom Format',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export default {
|
|||||||
play: 'Play',
|
play: 'Play',
|
||||||
playNext: 'Play Next',
|
playNext: 'Play Next',
|
||||||
download: 'Download',
|
download: 'Download',
|
||||||
|
downloadLyric: 'Download Lyrics',
|
||||||
addToPlaylist: 'Add to Playlist',
|
addToPlaylist: 'Add to Playlist',
|
||||||
favorite: 'Like',
|
favorite: 'Like',
|
||||||
unfavorite: 'Unlike',
|
unfavorite: 'Unlike',
|
||||||
@@ -15,7 +16,10 @@ export default {
|
|||||||
downloadFailed: 'Download failed',
|
downloadFailed: 'Download failed',
|
||||||
downloadQueued: 'Added to download queue',
|
downloadQueued: 'Added to download queue',
|
||||||
addedToNextPlay: 'Added to play next',
|
addedToNextPlay: 'Added to play next',
|
||||||
getUrlFailed: 'Failed to get music download URL, please check if logged in'
|
getUrlFailed: 'Failed to get music download URL, please check if logged in',
|
||||||
|
noLyric: 'No lyrics available for this song',
|
||||||
|
lyricDownloaded: 'Lyrics downloaded successfully',
|
||||||
|
lyricDownloadFailed: 'Failed to download lyrics'
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
dislike: {
|
dislike: {
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ export default {
|
|||||||
noPathSelected: 'まずダウンロードパスを選択してください',
|
noPathSelected: 'まずダウンロードパスを選択してください',
|
||||||
select: 'フォルダを選択',
|
select: 'フォルダを選択',
|
||||||
open: 'フォルダを開く',
|
open: 'フォルダを開く',
|
||||||
|
saveLyric: '歌詞ファイルを個別に保存',
|
||||||
|
saveLyricDesc: '楽曲ダウンロード時に .lrc 歌詞ファイルも一緒に保存します',
|
||||||
fileFormat: 'ファイル名形式',
|
fileFormat: 'ファイル名形式',
|
||||||
fileFormatDesc: '音楽ダウンロード時のファイル命名形式を設定',
|
fileFormatDesc: '音楽ダウンロード時のファイル命名形式を設定',
|
||||||
customFormat: 'カスタム形式',
|
customFormat: 'カスタム形式',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export default {
|
|||||||
play: '再生',
|
play: '再生',
|
||||||
playNext: '次に再生',
|
playNext: '次に再生',
|
||||||
download: '楽曲をダウンロード',
|
download: '楽曲をダウンロード',
|
||||||
|
downloadLyric: '歌詞をダウンロード',
|
||||||
addToPlaylist: 'プレイリストに追加',
|
addToPlaylist: 'プレイリストに追加',
|
||||||
favorite: 'いいね',
|
favorite: 'いいね',
|
||||||
unfavorite: 'いいね解除',
|
unfavorite: 'いいね解除',
|
||||||
@@ -15,7 +16,11 @@ export default {
|
|||||||
downloadFailed: 'ダウンロードに失敗しました',
|
downloadFailed: 'ダウンロードに失敗しました',
|
||||||
downloadQueued: 'ダウンロードキューに追加しました',
|
downloadQueued: 'ダウンロードキューに追加しました',
|
||||||
addedToNextPlay: '次の再生に追加しました',
|
addedToNextPlay: '次の再生に追加しました',
|
||||||
getUrlFailed: '音楽ダウンロードアドレスの取得に失敗しました。ログインしているか確認してください'
|
getUrlFailed:
|
||||||
|
'音楽ダウンロードアドレスの取得に失敗しました。ログインしているか確認してください',
|
||||||
|
noLyric: 'この楽曲には歌詞がありません',
|
||||||
|
lyricDownloaded: '歌詞のダウンロードが完了しました',
|
||||||
|
lyricDownloadFailed: '歌詞のダウンロードに失敗しました'
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
dislike: {
|
dislike: {
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ export default {
|
|||||||
noPathSelected: '먼저 다운로드 경로를 선택해주세요',
|
noPathSelected: '먼저 다운로드 경로를 선택해주세요',
|
||||||
select: '폴더 선택',
|
select: '폴더 선택',
|
||||||
open: '폴더 열기',
|
open: '폴더 열기',
|
||||||
|
saveLyric: '가사 파일 별도 저장',
|
||||||
|
saveLyricDesc: '곡 다운로드 시 .lrc 가사 파일도 함께 저장합니다',
|
||||||
fileFormat: '파일명 형식',
|
fileFormat: '파일명 형식',
|
||||||
fileFormatDesc: '음악 다운로드 시 파일 이름 형식 설정',
|
fileFormatDesc: '음악 다운로드 시 파일 이름 형식 설정',
|
||||||
customFormat: '사용자 정의 형식',
|
customFormat: '사용자 정의 형식',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export default {
|
|||||||
play: '재생',
|
play: '재생',
|
||||||
playNext: '다음에 재생',
|
playNext: '다음에 재생',
|
||||||
download: '곡 다운로드',
|
download: '곡 다운로드',
|
||||||
|
downloadLyric: '가사 다운로드',
|
||||||
addToPlaylist: '플레이리스트에 추가',
|
addToPlaylist: '플레이리스트에 추가',
|
||||||
favorite: '좋아요',
|
favorite: '좋아요',
|
||||||
unfavorite: '좋아요 취소',
|
unfavorite: '좋아요 취소',
|
||||||
@@ -15,7 +16,10 @@ export default {
|
|||||||
downloadFailed: '다운로드 실패',
|
downloadFailed: '다운로드 실패',
|
||||||
downloadQueued: '다운로드 대기열에 추가됨',
|
downloadQueued: '다운로드 대기열에 추가됨',
|
||||||
addedToNextPlay: '다음 재생에 추가됨',
|
addedToNextPlay: '다음 재생에 추가됨',
|
||||||
getUrlFailed: '음악 다운로드 주소 가져오기 실패, 로그인 상태를 확인하세요'
|
getUrlFailed: '음악 다운로드 주소 가져오기 실패, 로그인 상태를 확인하세요',
|
||||||
|
noLyric: '이 곡에는 가사가 없습니다',
|
||||||
|
lyricDownloaded: '가사 다운로드 완료',
|
||||||
|
lyricDownloadFailed: '가사 다운로드 실패'
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
dislike: {
|
dislike: {
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ export default {
|
|||||||
noPathSelected: '请先选择下载路径',
|
noPathSelected: '请先选择下载路径',
|
||||||
select: '选择文件夹',
|
select: '选择文件夹',
|
||||||
open: '打开文件夹',
|
open: '打开文件夹',
|
||||||
|
saveLyric: '单独保存歌词文件',
|
||||||
|
saveLyricDesc: '下载歌曲时同时保存一份 .lrc 歌词文件',
|
||||||
fileFormat: '文件名格式',
|
fileFormat: '文件名格式',
|
||||||
fileFormatDesc: '设置下载音乐时的文件命名格式',
|
fileFormatDesc: '设置下载音乐时的文件命名格式',
|
||||||
customFormat: '自定义格式',
|
customFormat: '自定义格式',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export default {
|
|||||||
play: '播放',
|
play: '播放',
|
||||||
playNext: '下一首播放',
|
playNext: '下一首播放',
|
||||||
download: '下载歌曲',
|
download: '下载歌曲',
|
||||||
|
downloadLyric: '下载歌词',
|
||||||
addToPlaylist: '添加到歌单',
|
addToPlaylist: '添加到歌单',
|
||||||
favorite: '喜欢',
|
favorite: '喜欢',
|
||||||
unfavorite: '取消喜欢',
|
unfavorite: '取消喜欢',
|
||||||
@@ -15,7 +16,10 @@ export default {
|
|||||||
downloadFailed: '下载失败',
|
downloadFailed: '下载失败',
|
||||||
downloadQueued: '已加入下载队列',
|
downloadQueued: '已加入下载队列',
|
||||||
addedToNextPlay: '已添加到下一首播放',
|
addedToNextPlay: '已添加到下一首播放',
|
||||||
getUrlFailed: '获取音乐下载地址失败,请检查是否登录'
|
getUrlFailed: '获取音乐下载地址失败,请检查是否登录',
|
||||||
|
noLyric: '该歌曲暂无歌词',
|
||||||
|
lyricDownloaded: '歌词下载成功',
|
||||||
|
lyricDownloadFailed: '歌词下载失败'
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
dislike: {
|
dislike: {
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ export default {
|
|||||||
noPathSelected: '請先選擇下載路徑',
|
noPathSelected: '請先選擇下載路徑',
|
||||||
select: '選擇資料夾',
|
select: '選擇資料夾',
|
||||||
open: '開啟資料夾',
|
open: '開啟資料夾',
|
||||||
|
saveLyric: '單獨儲存歌詞檔案',
|
||||||
|
saveLyricDesc: '下載歌曲時同時儲存一份 .lrc 歌詞檔案',
|
||||||
fileFormat: '檔名格式',
|
fileFormat: '檔名格式',
|
||||||
fileFormatDesc: '設定下載音樂時的檔案命名格式',
|
fileFormatDesc: '設定下載音樂時的檔案命名格式',
|
||||||
customFormat: '自訂格式',
|
customFormat: '自訂格式',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export default {
|
|||||||
play: '播放',
|
play: '播放',
|
||||||
playNext: '下一首播放',
|
playNext: '下一首播放',
|
||||||
download: '下載歌曲',
|
download: '下載歌曲',
|
||||||
|
downloadLyric: '下載歌詞',
|
||||||
addToPlaylist: '新增至播放清單',
|
addToPlaylist: '新增至播放清單',
|
||||||
favorite: '喜歡',
|
favorite: '喜歡',
|
||||||
unfavorite: '取消喜歡',
|
unfavorite: '取消喜歡',
|
||||||
@@ -15,7 +16,10 @@ export default {
|
|||||||
downloadFailed: '下載失敗',
|
downloadFailed: '下載失敗',
|
||||||
downloadQueued: '已加入下載佇列',
|
downloadQueued: '已加入下載佇列',
|
||||||
addedToNextPlay: '已新增至下一首播放',
|
addedToNextPlay: '已新增至下一首播放',
|
||||||
getUrlFailed: '取得音樂下載位址失敗,請檢查是否登入'
|
getUrlFailed: '取得音樂下載位址失敗,請檢查是否登入',
|
||||||
|
noLyric: '該歌曲暫無歌詞',
|
||||||
|
lyricDownloaded: '歌詞下載成功',
|
||||||
|
lyricDownloadFailed: '歌詞下載失敗'
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {
|
||||||
dislike: {
|
dislike: {
|
||||||
|
|||||||
@@ -246,6 +246,33 @@ export function initializeFileManager() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 保存歌词文件
|
||||||
|
ipcMain.handle(
|
||||||
|
'save-lyric-file',
|
||||||
|
async (_, { filename, lrcContent }: { filename: string; lrcContent: string }) => {
|
||||||
|
try {
|
||||||
|
const configStore = getStore();
|
||||||
|
const downloadPath =
|
||||||
|
(configStore.get('set.downloadPath') as string) || app.getPath('downloads');
|
||||||
|
const sanitizedName = sanitizeFilename(filename);
|
||||||
|
let filePath = path.join(downloadPath, `${sanitizedName}.lrc`);
|
||||||
|
|
||||||
|
// 文件已存在时添加序号
|
||||||
|
let counter = 1;
|
||||||
|
while (fs.existsSync(filePath)) {
|
||||||
|
filePath = path.join(downloadPath, `${sanitizedName} (${counter}).lrc`);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.promises.writeFile(filePath, lrcContent, 'utf-8');
|
||||||
|
return { success: true, path: filePath };
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('保存歌词文件失败:', error);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 添加清除下载历史的处理函数
|
// 添加清除下载历史的处理函数
|
||||||
ipcMain.on('clear-downloads-history', () => {
|
ipcMain.on('clear-downloads-history', () => {
|
||||||
downloadStore.set('history', []);
|
downloadStore.set('history', []);
|
||||||
@@ -786,6 +813,17 @@ async function downloadMusic(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果启用了单独保存歌词文件,将歌词保存为 .lrc 文件
|
||||||
|
if (lyricsContent && configStore.get('set.downloadSaveLyric')) {
|
||||||
|
try {
|
||||||
|
const lrcFilePath = finalFilePath.replace(/\.[^.]+$/, '.lrc');
|
||||||
|
await fs.promises.writeFile(lrcFilePath, lyricsContent, 'utf-8');
|
||||||
|
console.log('歌词文件已保存:', lrcFilePath);
|
||||||
|
} catch (lrcError) {
|
||||||
|
console.error('保存歌词文件失败:', lrcError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 保存下载信息
|
// 保存下载信息
|
||||||
try {
|
try {
|
||||||
const songInfos = configStore.get('downloadedSongs', {}) as Record<string, any>;
|
const songInfos = configStore.get('downloadedSongs', {}) as Record<string, any>;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
@play="playMusicEvent(item)"
|
@play="playMusicEvent(item)"
|
||||||
@play-next="handlePlayNext"
|
@play-next="handlePlayNext"
|
||||||
@download="downloadMusic(item)"
|
@download="downloadMusic(item)"
|
||||||
|
@download-lyric="downloadLyric(item)"
|
||||||
@toggle-favorite="toggleFavorite"
|
@toggle-favorite="toggleFavorite"
|
||||||
@toggle-dislike="toggleDislike"
|
@toggle-dislike="toggleDislike"
|
||||||
@remove="$emit('remove-song', $event)"
|
@remove="$emit('remove-song', $event)"
|
||||||
@@ -71,7 +72,8 @@ const {
|
|||||||
handleArtistClick,
|
handleArtistClick,
|
||||||
handleMouseEnter,
|
handleMouseEnter,
|
||||||
handleMouseLeave,
|
handleMouseLeave,
|
||||||
downloadMusic
|
downloadMusic,
|
||||||
|
downloadLyric
|
||||||
} = useSongItem(props);
|
} = useSongItem(props);
|
||||||
|
|
||||||
// 处理图片加载
|
// 处理图片加载
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
@play="onPlayMusic"
|
@play="onPlayMusic"
|
||||||
@play-next="handlePlayNext"
|
@play-next="handlePlayNext"
|
||||||
@download="downloadMusic"
|
@download="downloadMusic"
|
||||||
|
@download-lyric="downloadLyric(item)"
|
||||||
@toggle-favorite="toggleFavorite"
|
@toggle-favorite="toggleFavorite"
|
||||||
@toggle-dislike="toggleDislike"
|
@toggle-dislike="toggleDislike"
|
||||||
@remove="$emit('remove-song', $event)"
|
@remove="$emit('remove-song', $event)"
|
||||||
@@ -121,7 +122,8 @@ const {
|
|||||||
handlePlayNext,
|
handlePlayNext,
|
||||||
handleMenuClick,
|
handleMenuClick,
|
||||||
handleArtistClick,
|
handleArtistClick,
|
||||||
downloadMusic
|
downloadMusic,
|
||||||
|
downloadLyric
|
||||||
} = useSongItem(props);
|
} = useSongItem(props);
|
||||||
|
|
||||||
const onPlayMusic = () => {
|
const onPlayMusic = () => {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const emits = defineEmits([
|
|||||||
'play',
|
'play',
|
||||||
'play-next',
|
'play-next',
|
||||||
'download',
|
'download',
|
||||||
|
'download-lyric',
|
||||||
'add-to-playlist',
|
'add-to-playlist',
|
||||||
'toggle-favorite',
|
'toggle-favorite',
|
||||||
'toggle-dislike',
|
'toggle-dislike',
|
||||||
@@ -153,6 +154,11 @@ const dropdownOptions = computed<MenuOption[]>(() => {
|
|||||||
key: 'download',
|
key: 'download',
|
||||||
icon: () => h('i', { class: 'iconfont ri-download-line' })
|
icon: () => h('i', { class: 'iconfont ri-download-line' })
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t('songItem.menu.downloadLyric'),
|
||||||
|
key: 'downloadLyric',
|
||||||
|
icon: () => h('i', { class: 'iconfont ri-file-text-line' })
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('songItem.menu.addToPlaylist'),
|
label: t('songItem.menu.addToPlaylist'),
|
||||||
key: 'addToPlaylist',
|
key: 'addToPlaylist',
|
||||||
@@ -203,6 +209,9 @@ const handleSelect = (key: string | number) => {
|
|||||||
case 'download':
|
case 'download':
|
||||||
emits('download');
|
emits('download');
|
||||||
break;
|
break;
|
||||||
|
case 'downloadLyric':
|
||||||
|
emits('download-lyric');
|
||||||
|
break;
|
||||||
case 'playNext':
|
case 'playNext':
|
||||||
emits('play-next');
|
emits('play-next');
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useMessage } from 'naive-ui';
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import { getMusicLrc } from '@/api/music';
|
||||||
import { getSongUrl } from '@/store/modules/player';
|
import { getSongUrl } from '@/store/modules/player';
|
||||||
import type { SongResult } from '@/types/music';
|
import type { SongResult } from '@/types/music';
|
||||||
import { isElectron } from '@/utils';
|
import { isElectron } from '@/utils';
|
||||||
@@ -302,9 +303,91 @@ export const useDownload = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载单首歌曲的歌词(.lrc 文件)
|
||||||
|
* @param song 歌曲信息
|
||||||
|
*/
|
||||||
|
const downloadLyric = async (song: SongResult) => {
|
||||||
|
try {
|
||||||
|
const res = await getMusicLrc(song.id as number);
|
||||||
|
const lyricData = res?.data;
|
||||||
|
|
||||||
|
if (!lyricData?.lrc?.lyric) {
|
||||||
|
message.warning(t('songItem.message.noLyric'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 LRC 内容:保留原始歌词,如有翻译则合并
|
||||||
|
let lrcContent = lyricData.lrc.lyric;
|
||||||
|
if (lyricData.tlyric?.lyric) {
|
||||||
|
lrcContent = mergeLrcWithTranslation(lyricData.lrc.lyric, lyricData.tlyric.lyric);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建文件名
|
||||||
|
const artistNames = (song.ar || song.song?.artists)?.map((a) => a.name).join(',');
|
||||||
|
const filename = `${song.name} - ${artistNames}`;
|
||||||
|
|
||||||
|
const result = await ipcRenderer?.invoke('save-lyric-file', { filename, lrcContent });
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
message.success(t('songItem.message.lyricDownloaded'));
|
||||||
|
} else {
|
||||||
|
message.error(t('songItem.message.lyricDownloadFailed'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Download lyric error:', error);
|
||||||
|
message.error(t('songItem.message.lyricDownloadFailed'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isDownloading,
|
isDownloading,
|
||||||
downloadMusic,
|
downloadMusic,
|
||||||
|
downloadLyric,
|
||||||
batchDownloadMusic
|
batchDownloadMusic
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将原文歌词和翻译歌词合并为一个 LRC 字符串
|
||||||
|
*/
|
||||||
|
function mergeLrcWithTranslation(originalText: string, translationText: string): string {
|
||||||
|
const originalMap = parseLrcText(originalText);
|
||||||
|
const translationMap = parseLrcText(translationText);
|
||||||
|
|
||||||
|
const mergedLines: string[] = [];
|
||||||
|
|
||||||
|
for (const [timeTag, content] of originalMap.entries()) {
|
||||||
|
mergedLines.push(`${timeTag}${content}`);
|
||||||
|
const translated = translationMap.get(timeTag);
|
||||||
|
if (translated) {
|
||||||
|
mergedLines.push(`${timeTag}${translated}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按时间排序
|
||||||
|
mergedLines.sort((a, b) => {
|
||||||
|
const ta = a.match(/\[\d{2}:\d{2}(\.\d{1,3})?\]/)?.[0] || '';
|
||||||
|
const tb = b.match(/\[\d{2}:\d{2}(\.\d{1,3})?\]/)?.[0] || '';
|
||||||
|
return ta.localeCompare(tb);
|
||||||
|
});
|
||||||
|
|
||||||
|
return mergedLines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 LRC 文本为 Map<timeTag, content>
|
||||||
|
*/
|
||||||
|
function parseLrcText(text: string): Map<string, string> {
|
||||||
|
const map = new Map<string, string>();
|
||||||
|
for (const line of text.split('\n')) {
|
||||||
|
const tags = line.match(/\[\d{2}:\d{2}(\.\d{1,3})?\]/g);
|
||||||
|
if (!tags) continue;
|
||||||
|
const content = line.replace(/\[\d{2}:\d{2}(\.\d{1,3})?\]/g, '').trim();
|
||||||
|
if (!content) continue;
|
||||||
|
for (const tag of tags) {
|
||||||
|
map.set(tag, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export function useSongItem(props: { item: SongResult; canRemove?: boolean }) {
|
|||||||
const playerStore = usePlayerStore();
|
const playerStore = usePlayerStore();
|
||||||
const recommendStore = useRecommendStore();
|
const recommendStore = useRecommendStore();
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const { downloadMusic } = useDownload();
|
const { downloadMusic, downloadLyric } = useDownload();
|
||||||
const { navigateToArtist } = useArtist();
|
const { navigateToArtist } = useArtist();
|
||||||
|
|
||||||
// 状态变量
|
// 状态变量
|
||||||
@@ -220,6 +220,7 @@ export function useSongItem(props: { item: SongResult; canRemove?: boolean }) {
|
|||||||
handleArtistClick,
|
handleArtistClick,
|
||||||
handleMouseEnter,
|
handleMouseEnter,
|
||||||
handleMouseLeave,
|
handleMouseLeave,
|
||||||
downloadMusic
|
downloadMusic,
|
||||||
|
downloadLyric
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,6 +316,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Save Lyric File -->
|
||||||
|
<div class="setting-group">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-sm font-bold text-neutral-900 dark:text-white">
|
||||||
|
{{ t('download.settingsPanel.saveLyric') }}
|
||||||
|
</h3>
|
||||||
|
<p class="text-xs text-neutral-500 mt-1">
|
||||||
|
{{ t('download.settingsPanel.saveLyricDesc') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<n-switch v-model:value="downloadSettings.saveLyric" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Format Section -->
|
<!-- Format Section -->
|
||||||
<div class="setting-group">
|
<div class="setting-group">
|
||||||
<h3 class="text-sm font-bold text-neutral-900 dark:text-white mb-2">
|
<h3 class="text-sm font-bold text-neutral-900 dark:text-white mb-2">
|
||||||
@@ -875,7 +890,8 @@ const showSettingsDrawer = ref(false);
|
|||||||
const downloadSettings = ref({
|
const downloadSettings = ref({
|
||||||
path: '',
|
path: '',
|
||||||
nameFormat: '{songName} - {artistName}',
|
nameFormat: '{songName} - {artistName}',
|
||||||
separator: ' - '
|
separator: ' - ',
|
||||||
|
saveLyric: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// 格式组件(用于拖拽排序)
|
// 格式组件(用于拖拽排序)
|
||||||
@@ -992,6 +1008,11 @@ const saveDownloadSettings = () => {
|
|||||||
'set.downloadSeparator',
|
'set.downloadSeparator',
|
||||||
downloadSettings.value.separator
|
downloadSettings.value.separator
|
||||||
);
|
);
|
||||||
|
window.electron.ipcRenderer.send(
|
||||||
|
'set-store-value',
|
||||||
|
'set.downloadSaveLyric',
|
||||||
|
downloadSettings.value.saveLyric
|
||||||
|
);
|
||||||
|
|
||||||
// 如果是在已下载页面,刷新列表以更新显示
|
// 如果是在已下载页面,刷新列表以更新显示
|
||||||
if (tabName.value === 'downloaded') {
|
if (tabName.value === 'downloaded') {
|
||||||
@@ -1014,11 +1035,16 @@ const initDownloadSettings = async () => {
|
|||||||
'get-store-value',
|
'get-store-value',
|
||||||
'set.downloadSeparator'
|
'set.downloadSeparator'
|
||||||
);
|
);
|
||||||
|
const saveLyric = await window.electron.ipcRenderer.invoke(
|
||||||
|
'get-store-value',
|
||||||
|
'set.downloadSaveLyric'
|
||||||
|
);
|
||||||
|
|
||||||
downloadSettings.value = {
|
downloadSettings.value = {
|
||||||
path: path || (await window.electron.ipcRenderer.invoke('get-downloads-path')),
|
path: path || (await window.electron.ipcRenderer.invoke('get-downloads-path')),
|
||||||
nameFormat: nameFormat || '{songName} - {artistName}',
|
nameFormat: nameFormat || '{songName} - {artistName}',
|
||||||
separator: separator || ' - '
|
separator: separator || ' - ',
|
||||||
|
saveLyric: saveLyric || false
|
||||||
};
|
};
|
||||||
|
|
||||||
// 初始化排序组件
|
// 初始化排序组件
|
||||||
|
|||||||
Reference in New Issue
Block a user