mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-05-01 05:27:22 +08:00
feat: 添加心动模式播放
This commit is contained in:
@@ -66,7 +66,17 @@ export default {
|
|||||||
favorite: 'Favorite {name}',
|
favorite: 'Favorite {name}',
|
||||||
unFavorite: 'Unfavorite {name}',
|
unFavorite: 'Unfavorite {name}',
|
||||||
playbackSpeed: 'Playback Speed',
|
playbackSpeed: 'Playback Speed',
|
||||||
advancedControls: 'Advanced Controls'
|
advancedControls: 'Advanced Controls',
|
||||||
|
intelligenceMode: {
|
||||||
|
title: 'Intelligence Mode',
|
||||||
|
needCookieLogin: 'Please login with Cookie method to use Intelligence Mode',
|
||||||
|
noFavoritePlaylist: 'Favorite playlist not found',
|
||||||
|
noLikedSongs: 'You have no liked songs yet',
|
||||||
|
loading: 'Loading Intelligence Mode',
|
||||||
|
success: 'Loaded {count} songs',
|
||||||
|
failed: 'Failed to get Intelligence Mode list',
|
||||||
|
error: 'Intelligence Mode error'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
eq: {
|
eq: {
|
||||||
title: 'Equalizer',
|
title: 'Equalizer',
|
||||||
|
|||||||
@@ -67,7 +67,17 @@ export default {
|
|||||||
unFavorite: '{name}をお気に入りから削除しました',
|
unFavorite: '{name}をお気に入りから削除しました',
|
||||||
miniPlayBar: 'ミニ再生バー',
|
miniPlayBar: 'ミニ再生バー',
|
||||||
playbackSpeed: '再生速度',
|
playbackSpeed: '再生速度',
|
||||||
advancedControls: 'その他の設定'
|
advancedControls: 'その他の設定',
|
||||||
|
intelligenceMode: {
|
||||||
|
title: 'インテリジェンスモード',
|
||||||
|
needCookieLogin: 'Cookie方式でログインしてからインテリジェンスモードを使用してください',
|
||||||
|
noFavoritePlaylist: '「お気に入りの音楽」プレイリストが見つかりません',
|
||||||
|
noLikedSongs: 'まだ「いいね」した楽曲がありません',
|
||||||
|
loading: 'インテリジェンスモードを読み込み中',
|
||||||
|
success: '{count} 曲を読み込みました',
|
||||||
|
failed: 'インテリジェンスモードのリスト取得に失敗しました',
|
||||||
|
error: 'インテリジェンスモードの再生でエラーが発生しました'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
eq: {
|
eq: {
|
||||||
title: 'イコライザー',
|
title: 'イコライザー',
|
||||||
|
|||||||
@@ -67,7 +67,17 @@ export default {
|
|||||||
unFavorite: '{name} 즐겨찾기 해제됨',
|
unFavorite: '{name} 즐겨찾기 해제됨',
|
||||||
miniPlayBar: '미니 재생바',
|
miniPlayBar: '미니 재생바',
|
||||||
playbackSpeed: '재생 속도',
|
playbackSpeed: '재생 속도',
|
||||||
advancedControls: '고급 설정'
|
advancedControls: '고급 설정',
|
||||||
|
intelligenceMode: {
|
||||||
|
title: '인텔리전스 모드',
|
||||||
|
needCookieLogin: '쿠키 방식으로 로그인한 후 인텔리전스 모드를 사용할 수 있습니다',
|
||||||
|
noFavoritePlaylist: '내가 좋아하는 음악 재생목록을 찾을 수 없습니다',
|
||||||
|
noLikedSongs: '아직 좋아한 노래가 없습니다',
|
||||||
|
loading: '인텔리전스 모드를 불러오는 중',
|
||||||
|
success: '총 {count}곡을 불러왔습니다',
|
||||||
|
failed: '인텔리전스 모드 목록을 가져오는 데 실패했습니다',
|
||||||
|
error: '인텔리전스 모드 재생 오류'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
eq: {
|
eq: {
|
||||||
title: '이퀄라이저',
|
title: '이퀄라이저',
|
||||||
|
|||||||
@@ -67,7 +67,17 @@ export default {
|
|||||||
unFavorite: '已取消收藏{name}',
|
unFavorite: '已取消收藏{name}',
|
||||||
miniPlayBar: '迷你播放栏',
|
miniPlayBar: '迷你播放栏',
|
||||||
playbackSpeed: '播放速度',
|
playbackSpeed: '播放速度',
|
||||||
advancedControls: '更多设置s'
|
advancedControls: '更多设置',
|
||||||
|
intelligenceMode: {
|
||||||
|
title: '心动模式',
|
||||||
|
needCookieLogin: '请使用 Cookie 方式登录后使用心动模式',
|
||||||
|
noFavoritePlaylist: '未找到我喜欢的音乐歌单',
|
||||||
|
noLikedSongs: '您还没有喜欢的歌曲',
|
||||||
|
loading: '正在加载心动模式',
|
||||||
|
success: '已加载 {count} 首歌曲',
|
||||||
|
failed: '获取心动模式列表失败',
|
||||||
|
error: '心动模式播放出错'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
eq: {
|
eq: {
|
||||||
title: '均衡器',
|
title: '均衡器',
|
||||||
|
|||||||
@@ -67,7 +67,17 @@ export default {
|
|||||||
unFavorite: '已取消收藏{name}',
|
unFavorite: '已取消收藏{name}',
|
||||||
miniPlayBar: '迷你播放列',
|
miniPlayBar: '迷你播放列',
|
||||||
playbackSpeed: '播放速度',
|
playbackSpeed: '播放速度',
|
||||||
advancedControls: '更多設定s'
|
advancedControls: '更多設定',
|
||||||
|
intelligenceMode: {
|
||||||
|
title: '心動模式',
|
||||||
|
needCookieLogin: '請使用 Cookie 方式登入後使用心動模式',
|
||||||
|
noFavoritePlaylist: '未找到我喜歡的音樂歌單',
|
||||||
|
noLikedSongs: '您還沒有喜歡的歌曲',
|
||||||
|
loading: '正在載入心動模式',
|
||||||
|
success: '已載入 {count} 首歌曲',
|
||||||
|
failed: '取得心動模式清單失敗',
|
||||||
|
error: '心動模式播放出錯'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
eq: {
|
eq: {
|
||||||
title: '等化器',
|
title: '等化器',
|
||||||
|
|||||||
@@ -210,3 +210,15 @@ export function getHistoryRecommendSongs(date: string) {
|
|||||||
params: { date }
|
params: { date }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心动模式/智能播放
|
||||||
|
* @param params id: 歌曲id, pid: 歌单id, sid: 要开始播放的歌曲id(可选)
|
||||||
|
*/
|
||||||
|
export function getIntelligenceList(params: { id: number; pid: number; sid?: number }) {
|
||||||
|
return request({
|
||||||
|
url: '/playmode/intelligence/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -343,7 +343,7 @@
|
|||||||
<i class="ri-arrow-down-s-line"></i>
|
<i class="ri-arrow-down-s-line"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="side-button" @click="togglePlayMode">
|
<div class="side-button" @click="togglePlayMode">
|
||||||
<i :class="playModeIcon"></i>
|
<i :class="[playModeIcon, { 'intelligence-active': playMode === 3 }]"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="main-button prev" @click="prevSong">
|
<div class="main-button prev" @click="prevSong">
|
||||||
<i class="ri-skip-back-fill"></i>
|
<i class="ri-skip-back-fill"></i>
|
||||||
@@ -382,6 +382,7 @@ import {
|
|||||||
useLyricProgress
|
useLyricProgress
|
||||||
} from '@/hooks/MusicHook';
|
} from '@/hooks/MusicHook';
|
||||||
import { useArtist } from '@/hooks/useArtist';
|
import { useArtist } from '@/hooks/useArtist';
|
||||||
|
import { usePlayMode } from '@/hooks/usePlayMode';
|
||||||
import { usePlayerStore } from '@/store/modules/player';
|
import { usePlayerStore } from '@/store/modules/player';
|
||||||
import { DEFAULT_LYRIC_CONFIG, LyricConfig } from '@/types/lyric';
|
import { DEFAULT_LYRIC_CONFIG, LyricConfig } from '@/types/lyric';
|
||||||
import { getImgUrl, secondToMinute } from '@/utils';
|
import { getImgUrl, secondToMinute } from '@/utils';
|
||||||
@@ -394,19 +395,9 @@ const playerStore = usePlayerStore();
|
|||||||
// 播放控制相关
|
// 播放控制相关
|
||||||
const play = computed(() => playerStore.isPlay);
|
const play = computed(() => playerStore.isPlay);
|
||||||
const playIcon = computed(() => (play.value ? 'ri-pause-fill' : 'ri-play-fill'));
|
const playIcon = computed(() => (play.value ? 'ri-pause-fill' : 'ri-play-fill'));
|
||||||
const playMode = computed(() => playerStore.playMode);
|
|
||||||
const playModeIcon = computed(() => {
|
// 播放模式
|
||||||
switch (playMode.value) {
|
const { playMode, playModeIcon, playModeText, togglePlayMode: togglePlayModeBase } = usePlayMode();
|
||||||
case 0:
|
|
||||||
return 'ri-repeat-line';
|
|
||||||
case 1:
|
|
||||||
return 'ri-repeat-one-line';
|
|
||||||
case 2:
|
|
||||||
return 'ri-shuffle-line';
|
|
||||||
default:
|
|
||||||
return 'ri-repeat-line';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 打开播放列表
|
// 打开播放列表
|
||||||
const showPlaylist = () => {
|
const showPlaylist = () => {
|
||||||
playerStore.setPlayListDrawerVisible(true);
|
playerStore.setPlayListDrawerVisible(true);
|
||||||
@@ -963,12 +954,8 @@ const prevSong = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const togglePlayMode = () => {
|
const togglePlayMode = () => {
|
||||||
playerStore.togglePlayMode();
|
togglePlayModeBase();
|
||||||
showBottomToast(
|
showBottomToast(playModeText.value);
|
||||||
[t('player.playMode.sequence'), t('player.playMode.loop'), t('player.playMode.random')][
|
|
||||||
playMode.value
|
|
||||||
]
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeMusicFull = () => {
|
const closeMusicFull = () => {
|
||||||
@@ -1525,6 +1512,10 @@ const getWordStyle = (lineIndex: number, _wordIndex: number, word: any) => {
|
|||||||
i {
|
i {
|
||||||
@apply text-2xl;
|
@apply text-2xl;
|
||||||
color: var(--text-color-primary);
|
color: var(--text-color-primary);
|
||||||
|
|
||||||
|
&.intelligence-active {
|
||||||
|
@apply text-green-500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@@ -105,15 +105,23 @@
|
|||||||
</div>
|
</div>
|
||||||
<n-tooltip v-if="!isMobile" trigger="hover" :z-index="9999999">
|
<n-tooltip v-if="!isMobile" trigger="hover" :z-index="9999999">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<i class="iconfont" :class="playModeIcon" @click="togglePlayMode"></i>
|
<i
|
||||||
|
class="iconfont"
|
||||||
|
:class="[playModeIcon, { 'intelligence-active': playMode === 3 }]"
|
||||||
|
@click="togglePlayMode"
|
||||||
|
></i>
|
||||||
</template>
|
</template>
|
||||||
{{ playModeText }}
|
{{ playModeText }}
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
<n-tooltip v-if="!isMobile" trigger="hover" :z-index="9999999">
|
<n-tooltip v-if="!isMobile" trigger="hover" :z-index="9999999">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<i
|
<i
|
||||||
class="iconfont icon-likefill"
|
class="iconfont"
|
||||||
:class="{ 'like-active': isFavorite }"
|
:class="{
|
||||||
|
'like-active': isFavorite,
|
||||||
|
'ri-heart-3-fill': isFavorite,
|
||||||
|
'ri-heart-3-line': !isFavorite
|
||||||
|
}"
|
||||||
@click="toggleFavorite"
|
@click="toggleFavorite"
|
||||||
></i>
|
></i>
|
||||||
</template>
|
</template>
|
||||||
@@ -174,6 +182,7 @@ import {
|
|||||||
textColors
|
textColors
|
||||||
} from '@/hooks/MusicHook';
|
} from '@/hooks/MusicHook';
|
||||||
import { useArtist } from '@/hooks/useArtist';
|
import { useArtist } from '@/hooks/useArtist';
|
||||||
|
import { usePlayMode } from '@/hooks/usePlayMode';
|
||||||
import { audioService } from '@/services/audioService';
|
import { audioService } from '@/services/audioService';
|
||||||
import { isBilibiliIdMatch, usePlayerStore } from '@/store/modules/player';
|
import { isBilibiliIdMatch, usePlayerStore } from '@/store/modules/player';
|
||||||
import { useSettingsStore } from '@/store/modules/settings';
|
import { useSettingsStore } from '@/store/modules/settings';
|
||||||
@@ -282,38 +291,10 @@ const handleVolumeWheel = (e: WheelEvent) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 播放模式
|
// 播放模式
|
||||||
const playMode = computed(() => playerStore.playMode);
|
const { playMode, playModeIcon, playModeText, togglePlayMode } = usePlayMode();
|
||||||
const playModeIcon = computed(() => {
|
|
||||||
switch (playMode.value) {
|
|
||||||
case 0:
|
|
||||||
return 'ri-repeat-2-line';
|
|
||||||
case 1:
|
|
||||||
return 'ri-repeat-one-line';
|
|
||||||
case 2:
|
|
||||||
return 'ri-shuffle-line';
|
|
||||||
default:
|
|
||||||
return 'ri-repeat-2-line';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const playModeText = computed(() => {
|
|
||||||
switch (playMode.value) {
|
|
||||||
case 0:
|
|
||||||
return t('player.playBar.playMode.sequence');
|
|
||||||
case 1:
|
|
||||||
return t('player.playBar.playMode.loop');
|
|
||||||
case 2:
|
|
||||||
return t('player.playBar.playMode.random');
|
|
||||||
default:
|
|
||||||
return t('player.playBar.playMode.sequence');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 播放速度控制
|
// 播放速度控制
|
||||||
const { playbackRate } = storeToRefs(playerStore);
|
const { playbackRate } = storeToRefs(playerStore);
|
||||||
// 切换播放模式
|
|
||||||
const togglePlayMode = () => {
|
|
||||||
playerStore.togglePlayMode();
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleNext() {
|
function handleNext() {
|
||||||
playerStore.nextPlay();
|
playerStore.nextPlay();
|
||||||
@@ -645,6 +626,10 @@ const openPlayListDrawer = () => {
|
|||||||
@apply text-red-500 hover:text-red-600 !important;
|
@apply text-red-500 hover:text-red-600 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.intelligence-active {
|
||||||
|
@apply text-green-500 hover:text-green-600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.disabled-icon {
|
.disabled-icon {
|
||||||
@apply opacity-50 cursor-not-allowed !important;
|
@apply opacity-50 cursor-not-allowed !important;
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@@ -20,7 +20,10 @@
|
|||||||
<div class="controls-section">
|
<div class="controls-section">
|
||||||
<div class="left-controls">
|
<div class="left-controls">
|
||||||
<button class="control-btn small-btn" @click="togglePlayMode">
|
<button class="control-btn small-btn" @click="togglePlayMode">
|
||||||
<i class="iconfont" :class="playModeIcon"></i>
|
<i
|
||||||
|
class="iconfont"
|
||||||
|
:class="[playModeIcon, { 'intelligence-active': playMode === 3 }]"
|
||||||
|
></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -73,6 +76,7 @@
|
|||||||
import { computed, onMounted, ref, watch } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { allTime, nowTime, playMusic } from '@/hooks/MusicHook';
|
import { allTime, nowTime, playMusic } from '@/hooks/MusicHook';
|
||||||
|
import { usePlayMode } from '@/hooks/usePlayMode';
|
||||||
import { audioService } from '@/services/audioService';
|
import { audioService } from '@/services/audioService';
|
||||||
import { usePlayerStore } from '@/store/modules/player';
|
import { usePlayerStore } from '@/store/modules/player';
|
||||||
import { secondToMinute } from '@/utils';
|
import { secondToMinute } from '@/utils';
|
||||||
@@ -93,24 +97,7 @@ const playBarRef = ref<HTMLElement | null>(null);
|
|||||||
const play = computed(() => playerStore.isPlay);
|
const play = computed(() => playerStore.isPlay);
|
||||||
|
|
||||||
// 播放模式
|
// 播放模式
|
||||||
const playMode = computed(() => playerStore.playMode);
|
const { playMode, playModeIcon, togglePlayMode } = usePlayMode();
|
||||||
const playModeIcon = computed(() => {
|
|
||||||
switch (playMode.value) {
|
|
||||||
case 0:
|
|
||||||
return 'ri-repeat-2-line';
|
|
||||||
case 1:
|
|
||||||
return 'ri-repeat-one-line';
|
|
||||||
case 2:
|
|
||||||
return 'ri-shuffle-line';
|
|
||||||
default:
|
|
||||||
return 'ri-repeat-2-line';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 切换播放模式
|
|
||||||
const togglePlayMode = () => {
|
|
||||||
playerStore.togglePlayMode();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 音量控制
|
// 音量控制
|
||||||
const audioVolume = ref(
|
const audioVolume = ref(
|
||||||
@@ -527,4 +514,8 @@ onMounted(() => {
|
|||||||
color: var(--fill-color);
|
color: var(--fill-color);
|
||||||
text-shadow: 0 0 8px var(--fill-color-transparent);
|
text-shadow: 0 0 8px var(--fill-color-transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.intelligence-active {
|
||||||
|
@apply text-green-500;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -758,7 +758,6 @@ export const getLrcTimeRange = (index: number) => ({
|
|||||||
watch(
|
watch(
|
||||||
() => lrcArray.value,
|
() => lrcArray.value,
|
||||||
(newLrcArray) => {
|
(newLrcArray) => {
|
||||||
console.log('lrcArray.value', lrcArray.value);
|
|
||||||
if (newLrcArray.length > 0 && isElectron && isLyricWindowOpen.value) {
|
if (newLrcArray.length > 0 && isElectron && isLyricWindowOpen.value) {
|
||||||
sendLyricToWin();
|
sendLyricToWin();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import { usePlayerStore } from '@/store/modules/player';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放模式相关的 Hook
|
||||||
|
* 提供播放模式的图标、文本和切换功能
|
||||||
|
*/
|
||||||
|
export function usePlayMode() {
|
||||||
|
const { t } = useI18n();
|
||||||
|
const playerStore = usePlayerStore();
|
||||||
|
|
||||||
|
// 当前播放模式
|
||||||
|
const playMode = computed(() => playerStore.playMode);
|
||||||
|
|
||||||
|
// 播放模式图标
|
||||||
|
const playModeIcon = computed(() => {
|
||||||
|
switch (playMode.value) {
|
||||||
|
case 0:
|
||||||
|
return 'ri-repeat-2-line';
|
||||||
|
case 1:
|
||||||
|
return 'ri-repeat-one-line';
|
||||||
|
case 2:
|
||||||
|
return 'ri-shuffle-line';
|
||||||
|
case 3:
|
||||||
|
return 'ri-heart-pulse-line';
|
||||||
|
default:
|
||||||
|
return 'ri-repeat-2-line';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 播放模式文本
|
||||||
|
const playModeText = computed(() => {
|
||||||
|
switch (playMode.value) {
|
||||||
|
case 0:
|
||||||
|
return t('player.playBar.playMode.sequence');
|
||||||
|
case 1:
|
||||||
|
return t('player.playBar.playMode.loop');
|
||||||
|
case 2:
|
||||||
|
return t('player.playBar.playMode.random');
|
||||||
|
case 3:
|
||||||
|
return t('player.playBar.intelligenceMode.title');
|
||||||
|
default:
|
||||||
|
return t('player.playBar.playMode.sequence');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 切换播放模式
|
||||||
|
const togglePlayMode = () => {
|
||||||
|
playerStore.togglePlayMode();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
playMode,
|
||||||
|
playModeIcon,
|
||||||
|
playModeText,
|
||||||
|
togglePlayMode
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -853,10 +853,6 @@ class AudioService {
|
|||||||
const isLoading = this.isLoading();
|
const isLoading = this.isLoading();
|
||||||
const contextRunning = Howler.ctx && Howler.ctx.state === 'running';
|
const contextRunning = Howler.ctx && Howler.ctx.state === 'running';
|
||||||
|
|
||||||
console.log(
|
|
||||||
`实际播放状态检查: playing=${isPlaying}, loading=${isLoading}, contextRunning=${contextRunning}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// 只有在三个条件都满足时才认为是真正在播放
|
// 只有在三个条件都满足时才认为是真正在播放
|
||||||
return isPlaying && !isLoading && contextRunning;
|
return isPlaying && !isLoading && contextRunning;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -330,8 +330,6 @@ export const loadLrc = async (id: string | number): Promise<ILyric> => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('lyrics', lyrics);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lrcTimeArray: times,
|
lrcTimeArray: times,
|
||||||
lrcArray: lyrics,
|
lrcArray: lyrics,
|
||||||
@@ -537,6 +535,13 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
// 原始播放列表 - 保存切换到随机模式前的顺序
|
// 原始播放列表 - 保存切换到随机模式前的顺序
|
||||||
const originalPlayList = ref<SongResult[]>(getLocalStorageItem('originalPlayList', []));
|
const originalPlayList = ref<SongResult[]>(getLocalStorageItem('originalPlayList', []));
|
||||||
|
|
||||||
|
// 心动模式状态
|
||||||
|
const isIntelligenceMode = ref(getLocalStorageItem('isIntelligenceMode', false));
|
||||||
|
const intelligenceModeInfo = ref<{
|
||||||
|
playlistId: number;
|
||||||
|
seedSongId: number;
|
||||||
|
} | null>(getLocalStorageItem('intelligenceModeInfo', null));
|
||||||
|
|
||||||
// 通用洗牌函数 - Fisher-Yates 算法
|
// 通用洗牌函数 - Fisher-Yates 算法
|
||||||
const performShuffle = (list: SongResult[], currentSong?: SongResult): SongResult[] => {
|
const performShuffle = (list: SongResult[], currentSong?: SongResult): SongResult[] => {
|
||||||
if (list.length <= 1) return [...list];
|
if (list.length <= 1) return [...list];
|
||||||
@@ -960,7 +965,19 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
musicFull.value = value;
|
musicFull.value = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPlayList = (list: SongResult[], keepIndex: boolean = false) => {
|
const setPlayList = (
|
||||||
|
list: SongResult[],
|
||||||
|
keepIndex: boolean = false,
|
||||||
|
fromIntelligenceMode: boolean = false
|
||||||
|
) => {
|
||||||
|
// 如果不是从心动模式调用,则清除心动模式状态
|
||||||
|
if (!fromIntelligenceMode && isIntelligenceMode.value) {
|
||||||
|
isIntelligenceMode.value = false;
|
||||||
|
intelligenceModeInfo.value = null;
|
||||||
|
localStorage.removeItem('isIntelligenceMode');
|
||||||
|
localStorage.removeItem('intelligenceModeInfo');
|
||||||
|
}
|
||||||
|
|
||||||
if (list.length === 0) {
|
if (list.length === 0) {
|
||||||
playList.value = [];
|
playList.value = [];
|
||||||
playListIndex.value = 0;
|
playListIndex.value = 0;
|
||||||
@@ -1341,10 +1358,21 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
// 节流
|
// 节流
|
||||||
const prevPlay = useThrottleFn(_prevPlay, 500);
|
const prevPlay = useThrottleFn(_prevPlay, 500);
|
||||||
|
|
||||||
const togglePlayMode = () => {
|
const togglePlayMode = async () => {
|
||||||
const newMode = (playMode.value + 1) % 3;
|
const userStore = useUserStore();
|
||||||
|
const wasIntelligence = playMode.value === 3;
|
||||||
|
const newMode = (playMode.value + 1) % 4; // 扩展到4种模式
|
||||||
const wasRandom = playMode.value === 2;
|
const wasRandom = playMode.value === 2;
|
||||||
const isRandom = newMode === 2;
|
const isRandom = newMode === 2;
|
||||||
|
const isIntelligence = newMode === 3;
|
||||||
|
|
||||||
|
// 如果要切换到心动模式,但用户未使用cookie登录,则跳过心动模式
|
||||||
|
if (isIntelligence && (!userStore.user || userStore.loginType !== 'cookie')) {
|
||||||
|
console.log('跳过心动模式:需要cookie登录');
|
||||||
|
playMode.value = 0; // 跳到顺序模式
|
||||||
|
localStorage.setItem('playMode', JSON.stringify(playMode.value));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
playMode.value = newMode;
|
playMode.value = newMode;
|
||||||
localStorage.setItem('playMode', JSON.stringify(playMode.value));
|
localStorage.setItem('playMode', JSON.stringify(playMode.value));
|
||||||
@@ -1356,10 +1384,25 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 当从随机模式切换出去时,恢复原始顺序
|
// 当从随机模式切换出去时,恢复原始顺序
|
||||||
if (!isRandom && wasRandom) {
|
if (!isRandom && wasRandom && !isIntelligence) {
|
||||||
restoreOriginalOrder();
|
restoreOriginalOrder();
|
||||||
console.log('切换出随机模式,恢复原始顺序');
|
console.log('切换出随机模式,恢复原始顺序');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当切换到心动模式时,触发心动模式播放
|
||||||
|
if (isIntelligence && !wasIntelligence) {
|
||||||
|
console.log('切换到心动模式');
|
||||||
|
await playIntelligenceMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当从心动模式切换出去时,清除心动模式状态
|
||||||
|
if (!isIntelligence && wasIntelligence) {
|
||||||
|
console.log('退出心动模式');
|
||||||
|
isIntelligenceMode.value = false;
|
||||||
|
intelligenceModeInfo.value = null;
|
||||||
|
localStorage.setItem('isIntelligenceMode', JSON.stringify(false));
|
||||||
|
localStorage.removeItem('intelligenceModeInfo');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addToFavorite = async (id: number | string) => {
|
const addToFavorite = async (id: number | string) => {
|
||||||
@@ -1444,9 +1487,12 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
const settingStore = useSettingsStore();
|
const settingStore = useSettingsStore();
|
||||||
const savedPlayList = getLocalStorageItem('playList', []);
|
const savedPlayList = getLocalStorageItem('playList', []);
|
||||||
const savedPlayMusic = getLocalStorageItem<SongResult | null>('currentPlayMusic', null);
|
const savedPlayMusic = getLocalStorageItem<SongResult | null>('currentPlayMusic', null);
|
||||||
|
// 恢复心动模式状态
|
||||||
|
const savedIntelligenceMode = getLocalStorageItem('isIntelligenceMode', false);
|
||||||
|
|
||||||
if (savedPlayList.length > 0) {
|
if (savedPlayList.length > 0) {
|
||||||
setPlayList(savedPlayList);
|
// 如果是心动模式,保持状态
|
||||||
|
setPlayList(savedPlayList, false, savedIntelligenceMode);
|
||||||
|
|
||||||
// 重启后恢复随机播放状态
|
// 重启后恢复随机播放状态
|
||||||
if (playMode.value === 2) {
|
if (playMode.value === 2) {
|
||||||
@@ -1747,6 +1793,86 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
return newVolume;
|
return newVolume;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 心动模式播放
|
||||||
|
const playIntelligenceMode = async () => {
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { t } = i18n.global;
|
||||||
|
|
||||||
|
// 检查是否使用cookie登录
|
||||||
|
if (!userStore.user || userStore.loginType !== 'cookie') {
|
||||||
|
message.warning(t('player.playBar.intelligenceMode.needCookieLogin'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取用户歌单列表
|
||||||
|
if (userStore.playList.length === 0) {
|
||||||
|
await userStore.initializePlaylist();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到"我喜欢的音乐"歌单(通常是第一个歌单)
|
||||||
|
const favoritePlaylist = userStore.playList.find(
|
||||||
|
(pl: any) => pl.userId === userStore.user?.userId && pl.specialType === 5
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!favoritePlaylist) {
|
||||||
|
message.warning(t('player.playBar.intelligenceMode.noFavoritePlaylist'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取喜欢的歌曲列表
|
||||||
|
const likedListRes = await getLikedList(userStore.user.userId);
|
||||||
|
const likedIds = likedListRes.data?.ids || [];
|
||||||
|
|
||||||
|
if (likedIds.length === 0) {
|
||||||
|
message.warning(t('player.playBar.intelligenceMode.noLikedSongs'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机选择一首歌曲
|
||||||
|
const randomSongId = likedIds[Math.floor(Math.random() * likedIds.length)];
|
||||||
|
|
||||||
|
// 调用心动模式API
|
||||||
|
const { getIntelligenceList } = await import('@/api/music');
|
||||||
|
const res = await getIntelligenceList({
|
||||||
|
id: randomSongId,
|
||||||
|
pid: favoritePlaylist.id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.data?.data && res.data.data.length > 0) {
|
||||||
|
const intelligenceSongs = res.data.data.map((item: any) => ({
|
||||||
|
id: item.id,
|
||||||
|
name: item.songInfo.name,
|
||||||
|
picUrl: item.songInfo.al?.picUrl,
|
||||||
|
source: 'netease' as Platform,
|
||||||
|
song: item.songInfo,
|
||||||
|
...item.songInfo,
|
||||||
|
playLoading: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 设置心动模式状态
|
||||||
|
isIntelligenceMode.value = true;
|
||||||
|
intelligenceModeInfo.value = {
|
||||||
|
playlistId: favoritePlaylist.id,
|
||||||
|
seedSongId: randomSongId
|
||||||
|
};
|
||||||
|
playMode.value = 3; // 设置播放模式为心动模式
|
||||||
|
localStorage.setItem('isIntelligenceMode', JSON.stringify(true));
|
||||||
|
localStorage.setItem('intelligenceModeInfo', JSON.stringify(intelligenceModeInfo.value));
|
||||||
|
localStorage.setItem('playMode', JSON.stringify(playMode.value));
|
||||||
|
|
||||||
|
// 替换播放列表并开始播放
|
||||||
|
await setPlayList(intelligenceSongs, false, true);
|
||||||
|
await handlePlayMusic(intelligenceSongs[0], true);
|
||||||
|
} else {
|
||||||
|
message.error(t('player.playBar.intelligenceMode.failed'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('心动模式播放失败:', error);
|
||||||
|
message.error(t('player.playBar.intelligenceMode.error'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
play,
|
play,
|
||||||
isPlay,
|
isPlay,
|
||||||
@@ -1812,6 +1938,11 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
originalPlayList,
|
originalPlayList,
|
||||||
shufflePlayList,
|
shufflePlayList,
|
||||||
restoreOriginalOrder,
|
restoreOriginalOrder,
|
||||||
preloadNextSongs
|
preloadNextSongs,
|
||||||
|
|
||||||
|
// 心动模式
|
||||||
|
playIntelligenceMode,
|
||||||
|
isIntelligenceMode,
|
||||||
|
intelligenceModeInfo
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user