1. 音乐播放模式
This commit is contained in:
@@ -35,6 +35,7 @@ var configSectionKeys = map[string][]string{
|
||||
},
|
||||
"music": {
|
||||
"musicList", "activeMusicUrl", "musicRandomEnabled",
|
||||
"musicCacheEnabled", "musicCacheHours",
|
||||
},
|
||||
"photos": {
|
||||
"photoPages",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ref, computed, onUnmounted } from 'vue'
|
||||
|
||||
/**
|
||||
* 背景音乐控制 composable(支持多曲目)
|
||||
* - tracks: [{ url, name, coverUrl?, lrcUrl? }]
|
||||
* - tracks: [{ url, name, coverUrl?, lrcUrl?, weight? }]
|
||||
* - activeUrl: 当前选中的曲目地址
|
||||
* - 处理浏览器自动播放限制:首次用户交互后再尝试播放
|
||||
*/
|
||||
@@ -14,8 +14,10 @@ export function useAudio() {
|
||||
const currentTime = ref(0)
|
||||
const duration = ref(0)
|
||||
const volume = ref(0.5)
|
||||
const loop = ref(true)
|
||||
let audio = null
|
||||
let unlockHandler = null
|
||||
let endedHandler = null
|
||||
|
||||
const currentTrack = computed(() => tracks.value.find((t) => t.url === activeUrl.value) || null)
|
||||
const currentName = computed(() => currentTrack.value?.name || '背景音乐')
|
||||
@@ -32,7 +34,7 @@ export function useAudio() {
|
||||
const ensureAudio = () => {
|
||||
if (audio) return audio
|
||||
audio = new Audio()
|
||||
audio.loop = true
|
||||
audio.loop = loop.value
|
||||
audio.volume = volume.value
|
||||
audio.preload = 'auto'
|
||||
audio.addEventListener('canplay', () => {
|
||||
@@ -43,16 +45,27 @@ export function useAudio() {
|
||||
audio.addEventListener('timeupdate', syncMeta)
|
||||
audio.addEventListener('play', () => { isPlaying.value = true })
|
||||
audio.addEventListener('pause', () => { isPlaying.value = false })
|
||||
audio.addEventListener('ended', () => {
|
||||
if (audio?.loop) return
|
||||
if (typeof endedHandler === 'function') endedHandler()
|
||||
})
|
||||
return audio
|
||||
}
|
||||
|
||||
const playUrl = async (url) => {
|
||||
if (!url) return false
|
||||
const a = ensureAudio()
|
||||
if (a.src !== url) {
|
||||
const abs = (() => {
|
||||
try { return new URL(url, window.location.href).href } catch { return url }
|
||||
})()
|
||||
if (a.src !== abs && a.src !== url) {
|
||||
a.src = url
|
||||
currentTime.value = 0
|
||||
duration.value = 0
|
||||
} else {
|
||||
// 同曲重播(播完后再次抽中)需归零
|
||||
try { a.currentTime = 0 } catch { /* ignore */ }
|
||||
currentTime.value = 0
|
||||
}
|
||||
activeUrl.value = url
|
||||
try {
|
||||
@@ -80,7 +93,10 @@ export function useAudio() {
|
||||
else {
|
||||
activeUrl.value = url
|
||||
const a = ensureAudio()
|
||||
if (a.src !== url) a.src = url
|
||||
const abs = (() => {
|
||||
try { return new URL(url, window.location.href).href } catch { return url }
|
||||
})()
|
||||
if (a.src !== abs && a.src !== url) a.src = url
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,16 +142,29 @@ export function useAudio() {
|
||||
const a = ensureAudio()
|
||||
if (!Number.isFinite(seconds)) return
|
||||
const d = Number.isFinite(a.duration) ? a.duration : duration.value
|
||||
const next = Math.max(0, Math.min(d || seconds, seconds))
|
||||
a.currentTime = next
|
||||
currentTime.value = next
|
||||
const nextSec = Math.max(0, Math.min(d || seconds, seconds))
|
||||
a.currentTime = nextSec
|
||||
currentTime.value = nextSec
|
||||
}
|
||||
|
||||
const setVolume = (v) => {
|
||||
const next = Math.max(0, Math.min(1, Number(v) || 0))
|
||||
volume.value = next
|
||||
const nextVol = Math.max(0, Math.min(1, Number(v) || 0))
|
||||
volume.value = nextVol
|
||||
const a = ensureAudio()
|
||||
a.volume = next
|
||||
a.volume = nextVol
|
||||
}
|
||||
|
||||
const setLoop = (v) => {
|
||||
loop.value = !!v
|
||||
if (audio) audio.loop = loop.value
|
||||
}
|
||||
|
||||
/** 注册播完回调(仅 loop=false 时触发);返回取消函数 */
|
||||
const onEnded = (fn) => {
|
||||
endedHandler = typeof fn === 'function' ? fn : null
|
||||
return () => {
|
||||
if (endedHandler === fn) endedHandler = null
|
||||
}
|
||||
}
|
||||
|
||||
const armAutoplay = () => {
|
||||
@@ -158,6 +187,7 @@ export function useAudio() {
|
||||
onUnmounted(() => {
|
||||
pause()
|
||||
removeUnlock()
|
||||
endedHandler = null
|
||||
if (audio) {
|
||||
audio.src = ''
|
||||
audio = null
|
||||
@@ -176,6 +206,7 @@ export function useAudio() {
|
||||
currentTime,
|
||||
duration,
|
||||
volume,
|
||||
loop,
|
||||
currentIndex,
|
||||
setTracks,
|
||||
setActive,
|
||||
@@ -186,6 +217,9 @@ export function useAudio() {
|
||||
toggle,
|
||||
seek,
|
||||
setVolume,
|
||||
setLoop,
|
||||
onEnded,
|
||||
playUrl,
|
||||
attemptAutoplay,
|
||||
armAutoplay,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
const STORAGE_KEY = 'wedding_music_pick_v1'
|
||||
const DAY_MS = 24 * 60 * 60 * 1000
|
||||
const HOUR_MS = 60 * 60 * 1000
|
||||
|
||||
/** 随机权重:默认 1,最小 1,最大 1000 */
|
||||
export function normalizeMusicWeight(w) {
|
||||
const n = Number(w)
|
||||
if (!Number.isFinite(n) || n <= 0) return 1
|
||||
return Math.min(1000, Math.round(n))
|
||||
}
|
||||
|
||||
/** 记忆时长(小时):默认 24,范围 0.5~720 */
|
||||
export function normalizeMusicCacheHours(h) {
|
||||
const n = Number(h)
|
||||
if (!Number.isFinite(n) || n <= 0) return 24
|
||||
return Math.min(720, Math.max(0.5, Math.round(n * 10) / 10))
|
||||
}
|
||||
|
||||
function readCache() {
|
||||
try {
|
||||
@@ -20,11 +34,46 @@ export function rememberMusicPick(url) {
|
||||
} catch { /* ignore quota */ }
|
||||
}
|
||||
|
||||
export function clearMusicPickCache() {
|
||||
try {
|
||||
localStorage.removeItem(STORAGE_KEY)
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve which track to play.
|
||||
* When random is enabled, reuse local cache within 24h if still in list; otherwise pick randomly.
|
||||
* 按 weight 加权随机选一首
|
||||
* @param {{ excludeUrl?: string }} [opts] excludeUrl:尽量避开当前曲(仅一首时仍可播它)
|
||||
*/
|
||||
export function pickMusicUrl(musicList, activeMusicUrl, musicRandomEnabled) {
|
||||
export function pickWeightedMusicUrl(musicList, opts = {}) {
|
||||
let list = Array.isArray(musicList) ? musicList.filter((t) => t && t.url) : []
|
||||
if (!list.length) return ''
|
||||
const excludeUrl = opts.excludeUrl || ''
|
||||
if (excludeUrl && list.length > 1) {
|
||||
const filtered = list.filter((t) => t.url !== excludeUrl)
|
||||
if (filtered.length) list = filtered
|
||||
}
|
||||
let total = 0
|
||||
const weights = list.map((t) => {
|
||||
const w = normalizeMusicWeight(t.weight)
|
||||
total += w
|
||||
return w
|
||||
})
|
||||
if (total <= 0) return list[0].url
|
||||
let r = Math.random() * total
|
||||
for (let i = 0; i < list.length; i += 1) {
|
||||
r -= weights[i]
|
||||
if (r <= 0) return list[i].url
|
||||
}
|
||||
return list[list.length - 1].url
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析首播曲目。
|
||||
* options:
|
||||
* - cacheEnabled: 是否启用本地记忆
|
||||
* - cacheHours: 记忆时长(小时)
|
||||
*/
|
||||
export function pickMusicUrl(musicList, activeMusicUrl, musicRandomEnabled, options = {}) {
|
||||
const list = Array.isArray(musicList) ? musicList.filter((t) => t && t.url) : []
|
||||
if (!list.length) return ''
|
||||
|
||||
@@ -33,12 +82,18 @@ export function pickMusicUrl(musicList, activeMusicUrl, musicRandomEnabled) {
|
||||
return list[0].url
|
||||
}
|
||||
|
||||
const cacheEnabled = options.cacheEnabled !== false && options.cacheEnabled !== 0
|
||||
const cacheHours = normalizeMusicCacheHours(options.cacheHours)
|
||||
const cacheMs = cacheHours * HOUR_MS
|
||||
|
||||
if (cacheEnabled) {
|
||||
const cache = readCache()
|
||||
if (cache && Date.now() - cache.ts < DAY_MS && list.some((t) => t.url === cache.url)) {
|
||||
if (cache && Date.now() - cache.ts < cacheMs && list.some((t) => t.url === cache.url)) {
|
||||
return cache.url
|
||||
}
|
||||
}
|
||||
|
||||
const picked = list[Math.floor(Math.random() * list.length)].url
|
||||
rememberMusicPick(picked)
|
||||
const picked = pickWeightedMusicUrl(list, { excludeUrl: options.excludeUrl })
|
||||
if (cacheEnabled) rememberMusicPick(picked)
|
||||
return picked
|
||||
}
|
||||
|
||||
@@ -523,11 +523,12 @@
|
||||
<div class="flex flex-wrap items-start justify-between gap-3 mb-5">
|
||||
<div>
|
||||
<div class="text-sm text-[#2c2c2c] tracking-wide">曲目列表</div>
|
||||
<p class="text-[11px] text-[#8A8680] mt-1 leading-relaxed max-w-md">
|
||||
访客首次交互后自动播放当前曲目。开启随机后,24 小时内本地缓存同一首。
|
||||
<p class="text-[11px] text-[#8A8680] mt-1 leading-relaxed max-w-xl">
|
||||
开启随机后按<strong>权重</strong>抽曲,播完自动再随机下一首(不单曲循环)。
|
||||
可开启「记忆曲目」让同一访客在设定时长内首次打开仍播同一首。
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex flex-wrap items-center gap-3 justify-end">
|
||||
<label class="inline-flex items-center gap-2 text-[12px] text-[#5c5348] cursor-pointer select-none">
|
||||
<a-switch v-model:checked="cfg.musicRandomEnabled" size="small" />
|
||||
随机播放
|
||||
@@ -540,6 +541,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="cfg.musicRandomEnabled"
|
||||
class="mb-4 flex flex-wrap items-center gap-x-4 gap-y-2 rounded-xl border border-[#e7e3db] bg-[#faf8f5] px-3 py-2.5"
|
||||
>
|
||||
<label class="inline-flex items-center gap-2 text-[12px] text-[#5c5348] cursor-pointer select-none">
|
||||
<a-switch v-model:checked="cfg.musicCacheEnabled" size="small" />
|
||||
记忆曲目
|
||||
</label>
|
||||
<div class="inline-flex items-center gap-2 text-[12px] text-[#5c5348]">
|
||||
<span :class="{ 'opacity-40': !cfg.musicCacheEnabled }">记忆时长</span>
|
||||
<a-input-number
|
||||
v-model:value="cfg.musicCacheHours"
|
||||
:min="0.5"
|
||||
:max="720"
|
||||
:step="0.5"
|
||||
size="small"
|
||||
class="!w-[96px]"
|
||||
:disabled="!cfg.musicCacheEnabled"
|
||||
/>
|
||||
<span :class="{ 'opacity-40': !cfg.musicCacheEnabled }" class="text-[#8A8680]">小时</span>
|
||||
</div>
|
||||
<span class="text-[10px] text-[#8A8680]">
|
||||
{{ cfg.musicCacheEnabled
|
||||
? `同一设备 ${cfg.musicCacheHours || 24} 小时内首次打开沿用上次抽中的歌;播完仍会换下一首。`
|
||||
: '关闭后每次打开页面都会重新按权重抽曲。' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="!cfg.musicList.length" class="admin-empty">
|
||||
<div class="admin-empty-icon"><i class="fa-solid fa-music"></i></div>
|
||||
<div class="text-[13px] text-[#5c5348]">尚未上传音乐</div>
|
||||
@@ -585,6 +614,26 @@
|
||||
>正在播放</span>
|
||||
</div>
|
||||
<div class="music-chips">
|
||||
<label
|
||||
class="music-chip music-chip--weight"
|
||||
:class="{ 'opacity-40': !cfg.musicRandomEnabled }"
|
||||
:title="cfg.musicRandomEnabled ? '随机权重,越大越容易抽到' : '开启随机播放后生效'"
|
||||
>
|
||||
<i class="fa-solid fa-scale-balanced"></i>
|
||||
<span>权重</span>
|
||||
<a-input-number
|
||||
v-model:value="t.weight"
|
||||
:min="1"
|
||||
:max="1000"
|
||||
:step="1"
|
||||
size="small"
|
||||
class="music-weight-input"
|
||||
:disabled="!cfg.musicRandomEnabled"
|
||||
/>
|
||||
<span v-if="cfg.musicRandomEnabled" class="music-weight-pct">
|
||||
{{ musicWeightPercent(t) }}%
|
||||
</span>
|
||||
</label>
|
||||
<button type="button" class="music-chip" @click="openLrcUpload(t)">
|
||||
<i class="fa-solid fa-file-lines"></i>
|
||||
{{ t.lrcUrl ? '已有歌词' : '上传歌词' }}
|
||||
@@ -1537,6 +1586,7 @@ import MediaPickerModal from '@/components/MediaPickerModal.vue'
|
||||
import HotelWelcomePosterAdmin from '@/views/admin/HotelWelcomePosterAdmin.vue'
|
||||
import { BORDER_STYLES, borderClass } from '@/composables/borderStyles'
|
||||
import { resolveDanmakuSwatch } from '@/utils/danmakuColors'
|
||||
import { normalizeMusicWeight, normalizeMusicCacheHours } from '@/utils/musicPick'
|
||||
import { GIFT_TYPES, GIFT_LABEL_MAP, emptyGiftCosts, DEFAULT_GIFT_COSTS } from '@/utils/giftTypes'
|
||||
import {
|
||||
getSlotResizeSpecs,
|
||||
@@ -1626,7 +1676,7 @@ const SECTION_KEYS = {
|
||||
'introExitEffect', 'scrollMode', 'freeScrollInterval', 'pageSwitchDuration', 'firstScreenDuration',
|
||||
'petalEnabled', 'bubbleEnabled', 'introEnabled', 'danmakuEnabled', 'danmakuShowTime', 'nineGridAnimate',
|
||||
],
|
||||
music: ['musicList', 'activeMusicUrl', 'musicRandomEnabled'],
|
||||
music: ['musicList', 'activeMusicUrl', 'musicRandomEnabled', 'musicCacheEnabled', 'musicCacheHours'],
|
||||
photos: ['photoPages'],
|
||||
schedule: ['schedule'],
|
||||
}
|
||||
@@ -1934,9 +1984,11 @@ function defaultConfig() {
|
||||
endImg: 'https://images.unsplash.com/photo-1519741497674-611481863552?auto=format&fit=crop&w=800&q=80',
|
||||
endImgResized: '',
|
||||
endImgResizedMeta: '',
|
||||
musicList: [{ url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', name: '背景音乐 1' }],
|
||||
musicList: [{ url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', name: '背景音乐 1', weight: 1 }],
|
||||
activeMusicUrl: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
|
||||
musicRandomEnabled: false,
|
||||
musicCacheEnabled: true,
|
||||
musicCacheHours: 24,
|
||||
danmakuEnabled: true,
|
||||
danmakuShowTime: true,
|
||||
nineGridAnimate: false,
|
||||
@@ -2423,7 +2475,7 @@ function openMusicUpload() {
|
||||
const name = file.name.replace(/\.[^.]+$/, '') || '背景音乐'
|
||||
const url = res.url
|
||||
if (!cfg.musicList) cfg.musicList = []
|
||||
cfg.musicList.push({ url, name, coverUrl: '', lrcUrl: '' })
|
||||
cfg.musicList.push({ url, name, coverUrl: '', lrcUrl: '', weight: 1 })
|
||||
if (!cfg.activeMusicUrl) cfg.activeMusicUrl = url
|
||||
message.success('音乐上传成功')
|
||||
} catch (err) {
|
||||
@@ -2464,6 +2516,13 @@ function removeMusic(i) {
|
||||
cfg.activeMusicUrl = cfg.musicList[0]?.url || ''
|
||||
}
|
||||
}
|
||||
|
||||
function musicWeightPercent(track) {
|
||||
const list = (cfg.musicList || []).filter((t) => t && t.url)
|
||||
const total = list.reduce((s, t) => s + normalizeMusicWeight(t.weight), 0)
|
||||
if (!total) return 0
|
||||
return Math.round((normalizeMusicWeight(track?.weight) / total) * 100)
|
||||
}
|
||||
function shortUrl(url) {
|
||||
if (!url) return ''
|
||||
const parts = url.split('/')
|
||||
@@ -2702,7 +2761,16 @@ function pickSectionData(section) {
|
||||
const keys = SECTION_KEYS[section] || []
|
||||
const data = {}
|
||||
for (const k of keys) data[k] = cfg[k]
|
||||
return JSON.parse(JSON.stringify(data))
|
||||
const cloned = JSON.parse(JSON.stringify(data))
|
||||
if (section === 'music' && Array.isArray(cloned.musicList)) {
|
||||
cloned.musicList = cloned.musicList.map((t) => ({
|
||||
...t,
|
||||
weight: normalizeMusicWeight(t?.weight),
|
||||
}))
|
||||
cloned.musicCacheEnabled = cloned.musicCacheEnabled !== false
|
||||
cloned.musicCacheHours = normalizeMusicCacheHours(cloned.musicCacheHours)
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
function applySectionData(section, loaded) {
|
||||
@@ -2713,9 +2781,12 @@ function applySectionData(section, loaded) {
|
||||
...t,
|
||||
coverUrl: t.coverUrl || '',
|
||||
lrcUrl: t.lrcUrl || '',
|
||||
weight: normalizeMusicWeight(t.weight),
|
||||
}))
|
||||
}
|
||||
if (typeof loaded.musicRandomEnabled !== 'boolean') loaded.musicRandomEnabled = false
|
||||
if (typeof loaded.musicCacheEnabled !== 'boolean') loaded.musicCacheEnabled = true
|
||||
loaded.musicCacheHours = normalizeMusicCacheHours(loaded.musicCacheHours)
|
||||
}
|
||||
if (section === 'photos') {
|
||||
loaded.photoPages = (loaded.photoPages || []).map(ensurePageShape)
|
||||
@@ -3904,6 +3975,28 @@ onUnmounted(() => {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
.music-chip--weight {
|
||||
cursor: default;
|
||||
gap: 6px;
|
||||
}
|
||||
.music-chip--weight:hover {
|
||||
border-color: rgba(168, 140, 107, 0.22);
|
||||
color: #7a6550;
|
||||
}
|
||||
.music-weight-input {
|
||||
width: 72px !important;
|
||||
}
|
||||
.music-weight-input :deep(.ant-input-number-input) {
|
||||
height: 22px;
|
||||
font-size: 11px;
|
||||
padding: 0 4px;
|
||||
text-align: center;
|
||||
}
|
||||
.music-weight-pct {
|
||||
font-size: 10px;
|
||||
color: #a88c6b;
|
||||
min-width: 2.2em;
|
||||
}
|
||||
.music-audio {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
|
||||
@@ -852,7 +852,7 @@ import { ref, reactive, computed, nextTick, onMounted, onUnmounted, watch } from
|
||||
import { getAlbumImages, getConfig, submitRsvp, submitDanmaku, generateAiBlessing, getLikeStatus, submitLike, getGiftStatus, submitGift } from '@/api/wedding'
|
||||
import { useAudio } from '@/composables/useAudio'
|
||||
import { toast } from '@/composables/useToast'
|
||||
import { pickMusicUrl, rememberMusicPick } from '@/utils/musicPick'
|
||||
import { pickMusicUrl, pickWeightedMusicUrl, rememberMusicPick } from '@/utils/musicPick'
|
||||
import { getClientId } from '@/utils/clientId'
|
||||
import { getGuestName, setGuestName } from '@/utils/guestName'
|
||||
import {
|
||||
@@ -878,6 +878,7 @@ import PhotoGallery from '@/components/PhotoGallery.vue'
|
||||
const DEFAULTS = {
|
||||
scrollMode: 'snap', petalEnabled: true, bubbleEnabled: true, introEnabled: true,
|
||||
musicList: [], activeMusicUrl: '', musicRandomEnabled: false,
|
||||
musicCacheEnabled: true, musicCacheHours: 24,
|
||||
danmakuEnabled: true, danmakuShowTime: true, nineGridAnimate: false,
|
||||
freeScrollInterval: 50, // 自由滚动速度:滚动间隔 ms(越小越快)
|
||||
pageSwitchDuration: 800, // 页面切换速度:翻页动画时长 ms
|
||||
@@ -925,13 +926,44 @@ const toggleVolumeSlider = () => {
|
||||
showVolumeSlider.value = !showVolumeSlider.value
|
||||
if (showVolumeSlider.value) showPanelTrackList.value = false
|
||||
}
|
||||
const musicCacheOptions = () => ({
|
||||
cacheEnabled: data.value.musicCacheEnabled !== false,
|
||||
cacheHours: data.value.musicCacheHours,
|
||||
})
|
||||
|
||||
const rememberIfCached = (url) => {
|
||||
if (data.value.musicRandomEnabled && data.value.musicCacheEnabled !== false && url) {
|
||||
rememberMusicPick(url)
|
||||
}
|
||||
}
|
||||
|
||||
/** 随机模式下播完/下一首:按权重再抽,尽量避开当前曲 */
|
||||
const playRandomTrack = async (excludeUrl) => {
|
||||
const url = pickWeightedMusicUrl(data.value.musicList, {
|
||||
excludeUrl: excludeUrl || audio.activeUrl.value,
|
||||
})
|
||||
if (!url) return
|
||||
rememberIfCached(url)
|
||||
await audio.playUrl(url)
|
||||
}
|
||||
|
||||
const syncMusicPlaybackMode = () => {
|
||||
const random = !!data.value.musicRandomEnabled
|
||||
// 随机:不单曲循环,播完再抽;非随机:单曲循环当前曲
|
||||
audio.setLoop(!random)
|
||||
}
|
||||
|
||||
const playPrevTrack = () => {
|
||||
audio.prev()
|
||||
if (data.value.musicRandomEnabled && audio.activeUrl.value) rememberMusicPick(audio.activeUrl.value)
|
||||
rememberIfCached(audio.activeUrl.value)
|
||||
}
|
||||
const playNextTrack = () => {
|
||||
if (data.value.musicRandomEnabled && (data.value.musicList || []).filter((t) => t?.url).length > 1) {
|
||||
playRandomTrack(audio.activeUrl.value)
|
||||
return
|
||||
}
|
||||
audio.next()
|
||||
if (data.value.musicRandomEnabled && audio.activeUrl.value) rememberMusicPick(audio.activeUrl.value)
|
||||
rememberIfCached(audio.activeUrl.value)
|
||||
}
|
||||
const activeLrcIndex = computed(() => findLrcIndex(lrcLines.value, audio.currentTime.value))
|
||||
|
||||
@@ -1836,7 +1868,7 @@ const selectTrack = (url) => {
|
||||
audio.setActive(url)
|
||||
showPanelTrackList.value = false
|
||||
showTrackList.value = false
|
||||
if (data.value.musicRandomEnabled) rememberMusicPick(url)
|
||||
if (data.value.musicRandomEnabled) rememberIfCached(url)
|
||||
}
|
||||
|
||||
const mapKeyword = computed(() => `${data.value.hotel || ''} ${data.value.address || ''}`.replace(/\s+/g, ' ').trim())
|
||||
@@ -2202,8 +2234,14 @@ onMounted(async () => {
|
||||
const pickedUrl = pickMusicUrl(
|
||||
data.value.musicList,
|
||||
data.value.activeMusicUrl,
|
||||
data.value.musicRandomEnabled
|
||||
data.value.musicRandomEnabled,
|
||||
musicCacheOptions(),
|
||||
)
|
||||
syncMusicPlaybackMode()
|
||||
audio.onEnded(() => {
|
||||
if (!data.value.musicRandomEnabled) return
|
||||
playRandomTrack(audio.activeUrl.value)
|
||||
})
|
||||
audio.setTracks(data.value.musicList, pickedUrl)
|
||||
let autoMusicOk = true
|
||||
if (data.value.musicList.length) {
|
||||
|
||||
Reference in New Issue
Block a user