mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 03:14:57 +08:00
@@ -1,9 +1,18 @@
|
||||
# 根配置文件
|
||||
## 编辑器在查找配置时会停止查找更高层次的配置文件
|
||||
root = true
|
||||
|
||||
# 通配符,匹配所有文件
|
||||
[*]
|
||||
# 设置字符集为 UTF-8,确保文件中的文本使用 UTF-8 编码
|
||||
charset = utf-8
|
||||
# 使用空格作为缩进风格
|
||||
indent_style = space
|
||||
# 设置每个缩进级别的空格数量为 2
|
||||
indent_size = 2
|
||||
# 设置行尾换行符为LF(Line Feed)
|
||||
end_of_line = lf
|
||||
# 在文件的末尾插入一个新行
|
||||
insert_final_newline = true
|
||||
# 删除每一行末尾的尾随空格
|
||||
trim_trailing_whitespace = true
|
||||
@@ -229,7 +229,6 @@ docker run -d --name SPlayer -p 7899:7899 imsyy/splayer:latest
|
||||
- [NeteaseCloudMusicApi](https://github.com/Binaryify/NeteaseCloudMusicApi)
|
||||
- [YesPlayMusic](https://github.com/qier222/YesPlayMusic)
|
||||
- [UnblockNeteaseMusic](https://github.com/UnblockNeteaseMusic/server)
|
||||
- [BlurLyric](https://github.com/Project-And-Factory/BlurLyric)
|
||||
- [Vue-mmPlayer](https://github.com/maomao1996/Vue-mmPlayer)
|
||||
|
||||
## 📢 免责声明
|
||||
|
||||
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -77,7 +77,6 @@ declare module 'vue' {
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSkeleton: typeof import('naive-ui')['NSkeleton']
|
||||
NSlider: typeof import('naive-ui')['NSlider']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
NSpin: typeof import('naive-ui')['NSpin']
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
NTab: typeof import('naive-ui')['NTab']
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ipcMain, dialog, app, clipboard, shell } from "electron";
|
||||
import { readDirAsync } from "@main/utils/readDirAsync";
|
||||
import { parseFile } from "music-metadata";
|
||||
import { write } from "node-id3";
|
||||
import { download } from "electron-dl";
|
||||
import getNeteaseMusicUrl from "@main/utils/getNeteaseMusicUrl";
|
||||
import NodeID3 from "node-id3";
|
||||
import axios from "axios";
|
||||
import fs from "fs/promises";
|
||||
|
||||
@@ -203,6 +203,8 @@ const mainIpcMain = (win) => {
|
||||
directory: path,
|
||||
filename: `${songName}.${songType}`,
|
||||
});
|
||||
// 若不为 mp3,则不进行元信息写入
|
||||
if (songType !== "mp3") return true;
|
||||
// 下载封面
|
||||
const coverDownload = await download(win, songData.cover, {
|
||||
directory: path,
|
||||
@@ -218,10 +220,10 @@ const mainIpcMain = (win) => {
|
||||
image: coverDownload.getSavePath(),
|
||||
};
|
||||
// 保存修改后的元数据
|
||||
write(songTag, songDownload.getSavePath());
|
||||
const isSuccess = NodeID3.write(songTag, songDownload.getSavePath());
|
||||
// 删除封面
|
||||
await fs.unlink(coverDownload.getSavePath());
|
||||
return true;
|
||||
return isSuccess;
|
||||
} else {
|
||||
console.log(`目录不存在:${path}`);
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { dialog, shell } from "electron";
|
||||
import { is } from "@electron-toolkit/utils";
|
||||
import { autoUpdater } from "electron-updater";
|
||||
import pkg from "electron-updater";
|
||||
|
||||
const { autoUpdater } = pkg;
|
||||
|
||||
// 更新弹窗
|
||||
const hasNewVersion = (info) => {
|
||||
|
||||
29
package.json
29
package.json
@@ -8,9 +8,10 @@
|
||||
"github": "https://github.com/imsyy/SPlayer",
|
||||
"repository": "github:imsyy/SPlayer",
|
||||
"engines": {
|
||||
"node": ">=16.16.0"
|
||||
"node": ">=18.16.0",
|
||||
"npm": ">=9.6.7",
|
||||
"pnpm": ">=8.14.0"
|
||||
},
|
||||
"packageManager": "pnpm@8.12.0",
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix",
|
||||
@@ -23,8 +24,8 @@
|
||||
"build:linux": "npm run build && electron-builder --linux --config"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^2.0.0",
|
||||
"@electron-toolkit/utils": "^2.0.1",
|
||||
"@electron-toolkit/preload": "^3.0.0",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"@material/material-color-utilities": "^0.2.7",
|
||||
"NeteaseCloudMusicApi": "^4.14.0",
|
||||
"axios": "^1.6.5",
|
||||
@@ -33,11 +34,11 @@
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
"express": "^4.18.2",
|
||||
"express-http-proxy": "^1.6.3",
|
||||
"express-http-proxy": "^2.0.0",
|
||||
"howler": "^2.2.4",
|
||||
"js-cookie": "^3.0.5",
|
||||
"localforage": "^1.10.0",
|
||||
"music-metadata": "7.13.4",
|
||||
"music-metadata": "7.14.0",
|
||||
"node-id3": "^0.2.6",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
@@ -49,22 +50,22 @@
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config": "^1.0.2",
|
||||
"@rushstack/eslint-patch": "^1.6.1",
|
||||
"@vitejs/plugin-vue": "^4.6.2",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.2",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"ajv": "^8.12.0",
|
||||
"electron": "^27.2.1",
|
||||
"electron": "^28.1.2",
|
||||
"electron-builder": "^24.9.1",
|
||||
"electron-log": "^5.0.3",
|
||||
"electron-vite": "^1.0.29",
|
||||
"electron-vite": "^2.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"naive-ui": "^2.37.0",
|
||||
"naive-ui": "^2.37.3",
|
||||
"prettier": "^3.1.1",
|
||||
"sass": "^1.69.7",
|
||||
"terser": "^5.26.0",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"vite": "^4.5.1",
|
||||
"unplugin-auto-import": "^0.17.3",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.0.11",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-pwa": "^0.17.4",
|
||||
"vue": "3.4.4"
|
||||
|
||||
492
pnpm-lock.yaml
generated
492
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -90,5 +90,6 @@
|
||||
"password": "M2 17h20v2H2v-2zm1.15-4.05L4 11.47l.85 1.48l1.3-.75l-.85-1.48H7v-1.5H5.3l.85-1.47L4.85 7L4 8.47L3.15 7l-1.3.75l.85 1.47H1v1.5h1.7l-.85 1.48l1.3.75zm6.7-.75l1.3.75l.85-1.48l.85 1.48l1.3-.75l-.85-1.48H15v-1.5h-1.7l.85-1.47l-1.3-.75L12 8.47L11.15 7l-1.3.75l.85 1.47H9v1.5h1.7l-.85 1.48zM23 9.22h-1.7l.85-1.47l-1.3-.75L20 8.47L19.15 7l-1.3.75l.85 1.47H17v1.5h1.7l-.85 1.48l1.3.75l.85-1.48l.85 1.48l1.3-.75l-.85-1.48H23v-1.5z",
|
||||
"star": "m12 17.27l4.15 2.51c.76.46 1.69-.22 1.49-1.08l-1.1-4.72l3.67-3.18c.67-.58.31-1.68-.57-1.75l-4.83-.41l-1.89-4.46c-.34-.81-1.5-.81-1.84 0L9.19 8.63l-4.83.41c-.88.07-1.24 1.17-.57 1.75l3.67 3.18l-1.1 4.72c-.2.86.73 1.54 1.49 1.08l4.15-2.5z",
|
||||
"record": "M17 18.25v3.25H7v-3.25c0-1.38 2.24-2.5 5-2.5s5 1.12 5 2.5M12 5.5a6.5 6.5 0 0 1 6.5 6.5c0 1.25-.35 2.42-.96 3.41L16 14.04c.32-.61.5-1.31.5-2.04c0-2.5-2-4.5-4.5-4.5s-4.5 2-4.5 4.5c0 .73.18 1.43.5 2.04l-1.54 1.37c-.61-.99-.96-2.16-.96-3.41A6.5 6.5 0 0 1 12 5.5m0-4A10.5 10.5 0 0 1 22.5 12c0 2.28-.73 4.39-1.96 6.11l-1.5-1.35c.92-1.36 1.46-3 1.46-4.76A8.5 8.5 0 0 0 12 3.5A8.5 8.5 0 0 0 3.5 12c0 1.76.54 3.4 1.46 4.76l-1.5 1.35A10.473 10.473 0 0 1 1.5 12A10.5 10.5 0 0 1 12 1.5m0 8a2.5 2.5 0 0 1 2.5 2.5a2.5 2.5 0 0 1-2.5 2.5A2.5 2.5 0 0 1 9.5 12A2.5 2.5 0 0 1 12 9.5Z",
|
||||
"storage": "M4 20h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2s.9 2 2 2m0-3h2v2H4zM2 6c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2m4 1H4V5h2zm-2 7h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2s.9 2 2 2m0-3h2v2H4z"
|
||||
"storage": "M4 20h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2s.9 2 2 2m0-3h2v2H4zM2 6c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2m4 1H4V5h2zm-2 7h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2s.9 2 2 2m0-3h2v2H4z",
|
||||
"lrc-text": "M13.8 22H5c-1.7 0-3-1.3-3-3v-1h11.1c-.1.3-.1.7-.1 1c0 1.1.3 2.1.8 3m0-6H5V5c0-1.7 1.3-3 3-3h11c1.7 0 3 1.3 3 3v1h-2V5c0-.6-.4-1-1-1s-1 .4-1 1v8.1c-1.8.3-3.3 1.4-4.2 2.9M8 8h7V6H8zm0 4h6v-2H8zm9 4v6l5-3z"
|
||||
}
|
||||
|
||||
@@ -49,9 +49,14 @@
|
||||
<div v-show="playerControlShow" class="menu">
|
||||
<div class="left">
|
||||
<!-- 歌词模式 -->
|
||||
<div v-if="isHasLrc" class="n-icon" @click="pureLyricMode = !pureLyricMode">
|
||||
<n-text>词</n-text>
|
||||
</div>
|
||||
<n-icon
|
||||
v-if="isHasLrc"
|
||||
:class="['lrc-open', { open: pureLyricMode }]"
|
||||
size="28"
|
||||
@click="pureLyricMode = !pureLyricMode"
|
||||
>
|
||||
<SvgIcon icon="lrc-text" />
|
||||
</n-icon>
|
||||
</div>
|
||||
<div class="right">
|
||||
<!-- 全屏切换 -->
|
||||
@@ -406,17 +411,6 @@ onUnmounted(() => {
|
||||
justify-content: flex-end;
|
||||
flex: 1;
|
||||
}
|
||||
.left {
|
||||
justify-content: flex-start;
|
||||
.n-icon {
|
||||
margin-left: 0;
|
||||
margin-right: 12px;
|
||||
.n-text {
|
||||
font-size: 26px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-icon {
|
||||
margin-left: 12px;
|
||||
width: 40px;
|
||||
@@ -441,6 +435,17 @@ onUnmounted(() => {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
.left {
|
||||
justify-content: flex-start;
|
||||
.n-icon {
|
||||
margin-left: 0;
|
||||
&.lrc-open {
|
||||
&.open {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.main-player {
|
||||
display: flex;
|
||||
|
||||
@@ -779,6 +779,41 @@ const getPlaySongName = () => {
|
||||
return songName + " - " + songArtist;
|
||||
};
|
||||
|
||||
export const playAllSongs = async (playlist, mode = "normal") => {
|
||||
try {
|
||||
// pinia
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
if (!playlist) return false;
|
||||
// 关闭心动模式
|
||||
status.playHeartbeatMode = false;
|
||||
// 更改模式和歌单
|
||||
status.playMode = mode;
|
||||
music.playList = playlist.slice();
|
||||
// 是否处于歌单内
|
||||
const songId = music.getPlaySongData?.id;
|
||||
const existingIndex = playlist.findIndex((song) => song.id === songId);
|
||||
// 若不处于
|
||||
if (existingIndex === -1 || !songId) {
|
||||
console.log("不在歌单内");
|
||||
music.playSongData = playlist[0];
|
||||
status.playIndex = 0;
|
||||
// 初始化播放器
|
||||
await initPlayer(true);
|
||||
} else {
|
||||
console.log("处于歌单内");
|
||||
music.playSongData = playlist[existingIndex];
|
||||
status.playIndex = existingIndex;
|
||||
// 播放
|
||||
fadePlayOrPause();
|
||||
}
|
||||
$message.info("已开始播放", { showIcon: false });
|
||||
} catch (error) {
|
||||
console.error("播放全部歌曲出错:", error);
|
||||
$message.error("播放全部歌曲出现错误");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* 清除定时器
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,17 @@
|
||||
<!-- 功能区 -->
|
||||
<n-flex class="menu" justify="space-between">
|
||||
<n-flex class="left">
|
||||
<n-button type="primary" class="play" circle strong secondary @click="playAllSongs">
|
||||
<n-button
|
||||
:disabled="userCloudData?.length === 0"
|
||||
:focusable="false"
|
||||
type="primary"
|
||||
class="play"
|
||||
tag="div"
|
||||
circle
|
||||
strong
|
||||
secondary
|
||||
@click="playAllSongs(userCloudData)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon size="32">
|
||||
<SvgIcon icon="play-arrow-rounded" />
|
||||
@@ -82,19 +92,14 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { storeToRefs } from "pinia";
|
||||
import { musicData, siteStatus, indexedDBData } from "@/stores";
|
||||
import { indexedDBData } from "@/stores";
|
||||
import { getUserCloud } from "@/api/cloud";
|
||||
import { fuzzySearch } from "@/utils/helper";
|
||||
import { fadePlayOrPause, initPlayer } from "@/utils/Player";
|
||||
import { playAllSongs } from "@/utils/Player";
|
||||
import debounce from "@/utils/debounce";
|
||||
import formatData from "@/utils/formatData";
|
||||
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const indexedDB = indexedDBData();
|
||||
const { playList, playSongData } = storeToRefs(music);
|
||||
const { playIndex, playMode, playHeartbeatMode } = storeToRefs(status);
|
||||
|
||||
// 云盘数据
|
||||
const userCloudSpace = ref([]);
|
||||
@@ -160,31 +165,6 @@ const localSearch = debounce((val) => {
|
||||
searchData.value = result;
|
||||
}, 300);
|
||||
|
||||
// 播放歌单全部歌曲
|
||||
const playAllSongs = async () => {
|
||||
if (!userCloudData.value || !Object.keys(userCloudData.value).length) return false;
|
||||
// 关闭心动模式
|
||||
playHeartbeatMode.value = false;
|
||||
// 更改模式和歌单
|
||||
playMode.value = "normal";
|
||||
playList.value = userCloudData.value.slice();
|
||||
// 是否处于歌单内
|
||||
const songId = playSongData.value?.id;
|
||||
const existingIndex = userCloudData.value.findIndex((song) => song.id === songId);
|
||||
// 若不处于
|
||||
if (existingIndex === -1 || !songId) {
|
||||
playSongData.value = userCloudData.value[0];
|
||||
playIndex.value = 0;
|
||||
// 初始化播放器
|
||||
await initPlayer(true);
|
||||
} else {
|
||||
playSongData.value = userCloudData.value[existingIndex];
|
||||
playIndex.value = existingIndex;
|
||||
fadePlayOrPause();
|
||||
}
|
||||
$message.info("已开始播放", { showIcon: false });
|
||||
};
|
||||
|
||||
// 云盘扩容
|
||||
const goBuy = () => {
|
||||
window.open("https://music.163.com/#/store/product/detail?id=34001");
|
||||
|
||||
@@ -13,7 +13,16 @@
|
||||
</div>
|
||||
<!-- 操作 -->
|
||||
<n-flex class="control">
|
||||
<n-button size="large" tag="div" round strong secondary @click="playAllSongs">
|
||||
<n-button
|
||||
:disabled="dailySongsData.data?.length === 0"
|
||||
:focusable="false"
|
||||
size="large"
|
||||
tag="div"
|
||||
round
|
||||
strong
|
||||
secondary
|
||||
@click="playAllSongs(dailySongsData.data)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="play-arrow-rounded" />
|
||||
@@ -40,16 +49,12 @@
|
||||
<script setup>
|
||||
import { NIcon } from "naive-ui";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { musicData, siteData, siteStatus } from "@/stores";
|
||||
import { fadePlayOrPause, initPlayer } from "@/utils/Player";
|
||||
import { siteData } from "@/stores";
|
||||
import { playAllSongs } from "@/utils/Player";
|
||||
import SvgIcon from "@/components/Global/SvgIcon";
|
||||
|
||||
const data = siteData();
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const { dailySongsData } = storeToRefs(data);
|
||||
const { playList, playSongData } = storeToRefs(music);
|
||||
const { playIndex, playMode, playHeartbeatMode } = storeToRefs(status);
|
||||
|
||||
const showTime = ref(false);
|
||||
const showTimeOut = ref(null);
|
||||
@@ -85,34 +90,6 @@ const moreOptions = computed(() => [
|
||||
},
|
||||
]);
|
||||
|
||||
// 播放歌单全部歌曲
|
||||
const playAllSongs = async () => {
|
||||
if (!dailySongsData.value.data) return false;
|
||||
// 关闭心动模式
|
||||
playHeartbeatMode.value = false;
|
||||
// 更改模式和歌单
|
||||
playMode.value = "normal";
|
||||
playList.value = dailySongsData.value.data.slice();
|
||||
// 是否处于歌单内
|
||||
const songId = music.getPlaySongData?.id;
|
||||
const existingIndex = dailySongsData.value.data.findIndex((song) => song.id === songId);
|
||||
// 若不处于
|
||||
if (existingIndex === -1 || !songId) {
|
||||
console.log("不在歌单内");
|
||||
playSongData.value = dailySongsData.value.data[0];
|
||||
playIndex.value = 0;
|
||||
// 初始化播放器
|
||||
await initPlayer(true);
|
||||
} else {
|
||||
console.log("处于歌单内");
|
||||
playSongData.value = dailySongsData.value.data[existingIndex];
|
||||
playIndex.value = existingIndex;
|
||||
// 播放
|
||||
fadePlayOrPause();
|
||||
}
|
||||
$message.info("已开始播放", { showIcon: false });
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
showTimeOut.value = setTimeout(() => {
|
||||
showTime.value = true;
|
||||
|
||||
@@ -105,13 +105,14 @@
|
||||
<n-flex class="left">
|
||||
<n-button
|
||||
:disabled="albumData === 'empty'"
|
||||
:focusable="false"
|
||||
type="primary"
|
||||
class="play"
|
||||
tag="div"
|
||||
circle
|
||||
strong
|
||||
secondary
|
||||
@click="playAllSongs"
|
||||
@click="playAllSongs(albumData)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon size="32">
|
||||
@@ -120,6 +121,7 @@
|
||||
</template>
|
||||
</n-button>
|
||||
<n-button
|
||||
:focusable="false"
|
||||
class="like"
|
||||
size="large"
|
||||
tag="div"
|
||||
@@ -138,7 +140,7 @@
|
||||
{{ isLikeOrDislike(albumId) ? "收藏专辑" : "取消收藏" }}
|
||||
</n-button>
|
||||
<n-dropdown :options="moreOptions" trigger="hover" placement="bottom-start">
|
||||
<n-button class="more" size="large" tag="div" circle strong secondary>
|
||||
<n-button :focusable="false" class="more" size="large" tag="div" circle strong secondary>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="format-list-bulleted" />
|
||||
@@ -193,7 +195,9 @@
|
||||
</div>
|
||||
<div v-else class="title">
|
||||
<n-text class="key">参数不完整</n-text>
|
||||
<n-button class="back" strong secondary @click="router.go(-1)"> 返回上一页 </n-button>
|
||||
<n-button :focusable="false" class="back" strong secondary @click="router.go(-1)">
|
||||
返回上一页
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -201,12 +205,12 @@
|
||||
import { NIcon } from "naive-ui";
|
||||
import { useRouter } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { musicData, siteData, siteStatus } from "@/stores";
|
||||
import { siteData } from "@/stores";
|
||||
import { getSongDetail } from "@/api/song";
|
||||
import { getAlbumDetail, likeAlbum } from "@/api/album";
|
||||
import { formatNumber, fuzzySearch } from "@/utils/helper";
|
||||
import { getTimestampTime } from "@/utils/timeTools";
|
||||
import { fadePlayOrPause, initPlayer } from "@/utils/Player";
|
||||
import { playAllSongs } from "@/utils/Player";
|
||||
import { isLogin } from "@/utils/auth";
|
||||
import debounce from "@/utils/debounce";
|
||||
import formatData from "@/utils/formatData";
|
||||
@@ -214,11 +218,7 @@ import SvgIcon from "@/components/Global/SvgIcon";
|
||||
|
||||
const router = useRouter();
|
||||
const data = siteData();
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const { userLikeData } = storeToRefs(data);
|
||||
const { playList, playSongData } = storeToRefs(music);
|
||||
const { playIndex, playMode, playHeartbeatMode } = storeToRefs(status);
|
||||
|
||||
// 专辑 ID
|
||||
const albumId = ref(router.currentRoute.value.query.id || null);
|
||||
@@ -269,34 +269,6 @@ const getAlbumAllData = async (id, justDetail = false) => {
|
||||
albumData.value = formatData(songsDetail.songs, "song");
|
||||
};
|
||||
|
||||
// 播放专辑全部歌曲
|
||||
const playAllSongs = async () => {
|
||||
if (!albumData.value) return false;
|
||||
// 关闭心动模式
|
||||
playHeartbeatMode.value = false;
|
||||
// 更改模式和歌单
|
||||
playMode.value = "normal";
|
||||
playList.value = albumData.value.slice();
|
||||
// 是否处于专辑内
|
||||
const songId = playSongData.value?.id;
|
||||
const existingIndex = albumData.value.findIndex((song) => song.id === songId);
|
||||
// 若不处于
|
||||
if (existingIndex === -1 || !songId) {
|
||||
console.log("不在专辑内");
|
||||
playSongData.value = albumData.value[0];
|
||||
playIndex.value = 0;
|
||||
// 初始化播放器
|
||||
await initPlayer(true);
|
||||
} else {
|
||||
console.log("处于专辑内");
|
||||
playSongData.value = albumData.value[existingIndex];
|
||||
playIndex.value = existingIndex;
|
||||
// 播放
|
||||
fadePlayOrPause();
|
||||
}
|
||||
$message.info("已开始播放", { showIcon: false });
|
||||
};
|
||||
|
||||
// 歌曲模糊搜索
|
||||
const localSearch = debounce((val) => {
|
||||
const searchValue = val?.trim();
|
||||
|
||||
@@ -100,13 +100,14 @@
|
||||
<n-flex class="left">
|
||||
<n-button
|
||||
:disabled="djData === 'empty'"
|
||||
:focusable="false"
|
||||
type="primary"
|
||||
class="play"
|
||||
tag="div"
|
||||
circle
|
||||
strong
|
||||
secondary
|
||||
@click="playAllSongs"
|
||||
@click="playAllSongs(djData, 'dj')"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon size="32">
|
||||
@@ -115,6 +116,7 @@
|
||||
</template>
|
||||
</n-button>
|
||||
<n-button
|
||||
:focusable="false"
|
||||
class="like"
|
||||
size="large"
|
||||
tag="div"
|
||||
@@ -133,7 +135,7 @@
|
||||
{{ isLikeOrDislike(djId) ? "订阅电台" : "取消订阅" }}
|
||||
</n-button>
|
||||
<n-dropdown :options="moreOptions" trigger="hover" placement="bottom-start">
|
||||
<n-button class="more" size="large" tag="div" circle strong secondary>
|
||||
<n-button :focusable="false" class="more" size="large" tag="div" circle strong secondary>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="format-list-bulleted" />
|
||||
@@ -197,7 +199,9 @@
|
||||
</div>
|
||||
<div v-else class="title">
|
||||
<n-text class="key">参数不完整</n-text>
|
||||
<n-button class="back" strong secondary @click="router.go(-1)"> 返回上一页 </n-button>
|
||||
<n-button :focusable="false" class="back" strong secondary @click="router.go(-1)">
|
||||
返回上一页
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -205,25 +209,21 @@
|
||||
import { NIcon } from "naive-ui";
|
||||
import { useRouter } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { musicData, siteData, siteSettings, siteStatus } from "@/stores";
|
||||
import { siteData, siteSettings } from "@/stores";
|
||||
import { getDjDetail, getDjProgram, likeDj } from "@/api/dj";
|
||||
import { fuzzySearch } from "@/utils/helper";
|
||||
import { isLogin } from "@/utils/auth";
|
||||
import { getTimestampTime } from "@/utils/timeTools";
|
||||
import { fadePlayOrPause, initPlayer } from "@/utils/Player";
|
||||
import { playAllSongs } from "@/utils/Player";
|
||||
import debounce from "@/utils/debounce";
|
||||
import formatData from "@/utils/formatData";
|
||||
import SvgIcon from "@/components/Global/SvgIcon";
|
||||
|
||||
const router = useRouter();
|
||||
const data = siteData();
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const settings = siteSettings();
|
||||
const { userLikeData } = storeToRefs(data);
|
||||
const { loadSize } = storeToRefs(settings);
|
||||
const { playList, playSongData } = storeToRefs(music);
|
||||
const { playIndex, playMode, playHeartbeatMode } = storeToRefs(status);
|
||||
|
||||
// 电台数据
|
||||
const djId = ref(router.currentRoute.value.query.id);
|
||||
@@ -290,34 +290,6 @@ const getDjProgramData = async (id, limit = loadSize.value, offset = 0) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 播放电台全部节目
|
||||
const playAllSongs = async () => {
|
||||
if (!djData.value) return false;
|
||||
// 关闭心动模式
|
||||
playHeartbeatMode.value = false;
|
||||
// 更改模式和电台
|
||||
playMode.value = "dj";
|
||||
playList.value = djData.value.slice();
|
||||
// 是否处于电台内
|
||||
const songId = music.getPlaySongData?.id;
|
||||
const existingIndex = djData.value.findIndex((song) => song.id === songId);
|
||||
// 若不处于
|
||||
if (existingIndex === -1 || !songId) {
|
||||
console.log("不在电台内");
|
||||
playSongData.value = djData.value[0];
|
||||
playIndex.value = 0;
|
||||
// 初始化播放器
|
||||
await initPlayer(true);
|
||||
} else {
|
||||
console.log("处于电台内");
|
||||
playSongData.value = djData.value[existingIndex];
|
||||
playIndex.value = existingIndex;
|
||||
// 播放
|
||||
fadePlayOrPause();
|
||||
}
|
||||
$message.info("已开始播放", { showIcon: false });
|
||||
};
|
||||
|
||||
// 节目模糊搜索
|
||||
const localSearch = debounce((val) => {
|
||||
const searchValue = val?.trim();
|
||||
|
||||
@@ -112,13 +112,14 @@
|
||||
<n-flex class="left">
|
||||
<n-button
|
||||
:disabled="playListData === null || playListData === 'empty' || loadingMsg !== null"
|
||||
:focusable="false"
|
||||
type="primary"
|
||||
class="play"
|
||||
tag="div"
|
||||
circle
|
||||
strong
|
||||
secondary
|
||||
@click="playAllSongs"
|
||||
@click="playAllSongs(playListData)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon size="32">
|
||||
@@ -128,6 +129,7 @@
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="!isUserPLayList"
|
||||
:focusable="false"
|
||||
class="like"
|
||||
size="large"
|
||||
tag="div"
|
||||
@@ -149,6 +151,7 @@
|
||||
</n-button>
|
||||
<n-button
|
||||
v-else
|
||||
:focusable="false"
|
||||
class="like"
|
||||
size="large"
|
||||
tag="div"
|
||||
@@ -165,7 +168,15 @@
|
||||
编辑歌单
|
||||
</n-button>
|
||||
<n-dropdown :options="moreOptions" trigger="hover" placement="bottom-start">
|
||||
<n-button class="more" size="large" tag="div" circle strong secondary>
|
||||
<n-button
|
||||
:focusable="false"
|
||||
class="more"
|
||||
size="large"
|
||||
tag="div"
|
||||
circle
|
||||
strong
|
||||
secondary
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="format-list-bulleted" />
|
||||
@@ -218,7 +229,9 @@
|
||||
</div>
|
||||
<div v-else class="title">
|
||||
<n-text class="key">参数不完整</n-text>
|
||||
<n-button class="back" strong secondary @click="router.go(-1)"> 返回上一页 </n-button>
|
||||
<n-button :focusable="false" class="back" strong secondary @click="router.go(-1)">
|
||||
返回上一页
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -226,7 +239,7 @@
|
||||
import { NIcon } from "naive-ui";
|
||||
import { useRouter } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { musicData, siteData, siteStatus } from "@/stores";
|
||||
import { siteData } from "@/stores";
|
||||
import {
|
||||
getPlayListDetail,
|
||||
getAllPlayList,
|
||||
@@ -238,18 +251,14 @@ import { getSongDetail } from "@/api/song";
|
||||
import { formatNumber, fuzzySearch } from "@/utils/helper";
|
||||
import { isLogin } from "@/utils/auth";
|
||||
import { getTimestampTime } from "@/utils/timeTools";
|
||||
import { fadePlayOrPause, initPlayer } from "@/utils/Player";
|
||||
import { playAllSongs } from "@/utils/Player";
|
||||
import debounce from "@/utils/debounce";
|
||||
import formatData from "@/utils/formatData";
|
||||
import SvgIcon from "@/components/Global/SvgIcon";
|
||||
|
||||
const router = useRouter();
|
||||
const data = siteData();
|
||||
const music = musicData();
|
||||
const status = siteStatus();
|
||||
const { userLikeData, userData } = storeToRefs(data);
|
||||
const { playList, playSongData } = storeToRefs(music);
|
||||
const { playIndex, playMode, playHeartbeatMode } = storeToRefs(status);
|
||||
|
||||
// 歌单 ID
|
||||
const playlistId = ref(
|
||||
@@ -385,34 +394,6 @@ const getBigPlayListData = async (id, count) => {
|
||||
loadingMsg.value = null;
|
||||
};
|
||||
|
||||
// 播放歌单全部歌曲
|
||||
const playAllSongs = async () => {
|
||||
if (!playListData.value) return false;
|
||||
// 关闭心动模式
|
||||
playHeartbeatMode.value = false;
|
||||
// 更改模式和歌单
|
||||
playMode.value = "normal";
|
||||
playList.value = playListData.value.slice();
|
||||
// 是否处于歌单内
|
||||
const songId = music.getPlaySongData?.id;
|
||||
const existingIndex = playListData.value.findIndex((song) => song.id === songId);
|
||||
// 若不处于
|
||||
if (existingIndex === -1 || !songId) {
|
||||
console.log("不在歌单内");
|
||||
playSongData.value = playListData.value[0];
|
||||
playIndex.value = 0;
|
||||
// 初始化播放器
|
||||
await initPlayer(true);
|
||||
} else {
|
||||
console.log("处于歌单内");
|
||||
playSongData.value = playListData.value[existingIndex];
|
||||
playIndex.value = existingIndex;
|
||||
// 播放
|
||||
fadePlayOrPause();
|
||||
}
|
||||
$message.info("已开始播放", { showIcon: false });
|
||||
};
|
||||
|
||||
// 歌曲模糊搜索
|
||||
const localSearch = debounce((val) => {
|
||||
const searchValue = val?.trim();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
v-if="allAlbumData"
|
||||
:style="{
|
||||
height: `calc(100vh - ${
|
||||
Object.keys(music.playSongData)?.length && status.showPlayBar ? 380 : 300
|
||||
Object.keys(music.playSongData)?.length && status.showPlayBar ? 445 : 365
|
||||
}px)`,
|
||||
}"
|
||||
class="local-album"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
class="local-artists"
|
||||
:style="{
|
||||
height: `calc(100vh - ${
|
||||
Object.keys(music.playSongData)?.length && status.showPlayBar ? 380 : 300
|
||||
Object.keys(music.playSongData)?.length && status.showPlayBar ? 445 : 365
|
||||
}px)`,
|
||||
}"
|
||||
type="card"
|
||||
|
||||
@@ -26,18 +26,51 @@
|
||||
GB
|
||||
</div>
|
||||
</n-flex>
|
||||
<!-- 标签页 -->
|
||||
<n-tabs v-model:value="tabValue" class="tabs" type="segment" @update:value="tabChange">
|
||||
<n-tab name="local-songs"> 歌曲 </n-tab>
|
||||
<n-tab name="local-artists"> 歌手 </n-tab>
|
||||
<n-tab name="local-albums"> 专辑 </n-tab>
|
||||
<template #suffix>
|
||||
<!-- 功能区 -->
|
||||
<n-flex class="menu" justify="space-between">
|
||||
<n-flex class="left">
|
||||
<n-button
|
||||
:disabled="!localSongList?.length"
|
||||
:focusable="false"
|
||||
type="primary"
|
||||
class="play"
|
||||
tag="div"
|
||||
circle
|
||||
strong
|
||||
secondary
|
||||
@click="playAllSongs(localSongList)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon size="32">
|
||||
<SvgIcon icon="play-arrow-rounded" />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
<!-- 目录管理 -->
|
||||
<n-button
|
||||
:focusable="false"
|
||||
class="local-path"
|
||||
tag="div"
|
||||
round
|
||||
strong
|
||||
secondary
|
||||
@click="localPathShow = true"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="folder-cog" />
|
||||
</n-icon>
|
||||
</template>
|
||||
目录管理
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<n-flex class="right">
|
||||
<!-- 模糊搜索 -->
|
||||
<div v-if="localSongList?.length" class="search">
|
||||
<n-input
|
||||
v-if="localSongList?.length"
|
||||
v-model:value="searchValue"
|
||||
:input-props="{ autoComplete: false }"
|
||||
class="local-search"
|
||||
class="search"
|
||||
placeholder="搜索"
|
||||
clearable
|
||||
@input="localSearch"
|
||||
@@ -48,19 +81,13 @@
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-input>
|
||||
</div>
|
||||
<!-- 目录管理 -->
|
||||
<div class="local-path">
|
||||
<n-button strong secondary @click="localPathShow = true">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<SvgIcon icon="folder-cog" />
|
||||
</n-icon>
|
||||
</template>
|
||||
目录管理
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<!-- 标签页 -->
|
||||
<n-tabs v-model:value="tabValue" class="tabs" type="segment" @update:value="tabChange">
|
||||
<n-tab name="local-songs"> 歌曲 </n-tab>
|
||||
<n-tab name="local-artists"> 歌手 </n-tab>
|
||||
<n-tab name="local-albums"> 专辑 </n-tab>
|
||||
</n-tabs>
|
||||
<!-- 路由页面 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
@@ -172,6 +199,7 @@
|
||||
import { musicData, indexedDBData } from "@/stores";
|
||||
import { useRouter } from "vue-router";
|
||||
import { fuzzySearch } from "@/utils/helper";
|
||||
import { playAllSongs } from "@/utils/Player";
|
||||
import debounce from "@/utils/debounce";
|
||||
|
||||
const indexedDB = indexedDBData();
|
||||
@@ -335,25 +363,40 @@ onBeforeMount(async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
.tabs {
|
||||
margin-bottom: 20px;
|
||||
.search {
|
||||
height: 100%;
|
||||
margin-right: 16px;
|
||||
.local-search {
|
||||
display: flex;
|
||||
.menu {
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
margin: 20px 0;
|
||||
.left {
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
.play {
|
||||
--n-width: 46px;
|
||||
--n-height: 46px;
|
||||
}
|
||||
.local-path {
|
||||
height: 100%;
|
||||
:deep(.n-button) {
|
||||
--n-height: 100%;
|
||||
--n-border-radius: 6px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.search {
|
||||
height: 40px;
|
||||
width: 130px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 40px;
|
||||
transition:
|
||||
width 0.3s,
|
||||
background-color 0.3s;
|
||||
&.n-input--focus {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tabs {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
.local-list {
|
||||
|
||||
Reference in New Issue
Block a user