generated from VLADIMIR/template_frontend
up
This commit is contained in:
@@ -103,22 +103,30 @@ function uploadTrack(options: { file: UploadFileInfo }) {
|
||||
style="min-width: 220px"
|
||||
@update:value="(value) => emit('setMusic', { playlistID: value || 0, trackIndex: 0, playback: 'MUSIC_PLAYBACK_STOPPED' })"
|
||||
/>
|
||||
<n-button
|
||||
v-if="props.music?.playlist?.tracks?.[props.music.trackIndex || 0]?.ID"
|
||||
size="small"
|
||||
type="error"
|
||||
ghost
|
||||
@click="emit('deleteTrack', props.music.playlist.tracks[props.music.trackIndex || 0].ID!)"
|
||||
>
|
||||
Удалить трек
|
||||
</n-button>
|
||||
<n-select
|
||||
v-if="props.music?.playlist?.tracks?.length"
|
||||
:value="props.music.trackIndex"
|
||||
:options="props.music.playlist.tracks.map((track, index) => ({ label: track.name || track.filename || `Трек ${index + 1}`, value: index }))"
|
||||
style="min-width: 220px"
|
||||
@update:value="(value) => emit('setMusic', { trackIndex: value })"
|
||||
@update:value="(value) => emit('setMusic', { trackIndex: value, positionMs: 0 })"
|
||||
/>
|
||||
<div v-if="props.music?.playlist" class="track-list">
|
||||
<p v-if="!props.music.playlist.tracks?.length" class="track-empty">В плейлисте пока нет треков.</p>
|
||||
<div
|
||||
v-for="(track, index) in props.music.playlist.tracks"
|
||||
:key="track.ID"
|
||||
class="track-row"
|
||||
:class="{ 'track-row-active': index === (props.music.trackIndex ?? 0) }"
|
||||
>
|
||||
<n-button class="track-name" text @click="emit('setMusic', { trackIndex: index, positionMs: 0 })">
|
||||
{{ track.name || track.filename || `Трек ${index + 1}` }}
|
||||
</n-button>
|
||||
<span class="track-filename">{{ track.filename }}</span>
|
||||
<n-button size="small" type="error" ghost @click="emit('deleteTrack', track.ID!)">
|
||||
Удалить
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
<n-button size="small" @click="emit('setMusic', { playback: 'MUSIC_PLAYBACK_PLAYING' })">Старт</n-button>
|
||||
<n-button size="small" @click="emit('setMusic', { playback: 'MUSIC_PLAYBACK_PAUSED' })">Пауза</n-button>
|
||||
<n-button size="small" @click="emit('setMusic', { playback: 'MUSIC_PLAYBACK_STOPPED', positionMs: 0 })">Стоп</n-button>
|
||||
@@ -187,4 +195,43 @@ function uploadTrack(options: { file: UploadFileInfo }) {
|
||||
color: #f2a2a2;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.track-list {
|
||||
flex-basis: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.track-empty {
|
||||
color: #aaa;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.track-row {
|
||||
align-items: center;
|
||||
border-top: 1px solid rgb(255 255 255 / 10%);
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.track-row-active {
|
||||
background: rgb(255 255 255 / 5%);
|
||||
}
|
||||
|
||||
.track-name {
|
||||
justify-content: flex-start;
|
||||
min-width: 180px;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.track-filename {
|
||||
color: #aaa;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -74,6 +74,11 @@ const musicTrack = computed(() => {
|
||||
const index = state.value?.music?.trackIndex || 0
|
||||
return applicationFileURL(tracks[index]?.filename)
|
||||
})
|
||||
const musicSyncState = computed(() => [
|
||||
state.value?.music?.playlist?.ID,
|
||||
state.value?.music?.trackIndex,
|
||||
state.value?.music?.playback,
|
||||
] as const)
|
||||
const screenBackground = computed(() => {
|
||||
const image = state.value?.backgroundImage || state.value?.coverImage
|
||||
return image ? { backgroundImage: `linear-gradient(rgb(0 0 0 / 55%), rgb(0 0 0 / 55%)), url("${image}")` } : {}
|
||||
@@ -194,16 +199,33 @@ watch(state, async (value) => {
|
||||
}
|
||||
})
|
||||
|
||||
function waitForMusicMetadata(media: HTMLAudioElement): Promise<void> {
|
||||
if (media.readyState >= HTMLMediaElement.HAVE_METADATA) return Promise.resolve()
|
||||
return new Promise((resolve) => {
|
||||
const finish = () => {
|
||||
media.removeEventListener('loadedmetadata', finish)
|
||||
media.removeEventListener('error', finish)
|
||||
resolve()
|
||||
}
|
||||
media.addEventListener('loadedmetadata', finish, { once: true })
|
||||
media.addEventListener('error', finish, { once: true })
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.value?.music,
|
||||
async (value) => {
|
||||
const media = musicAudioElement.value
|
||||
if (!media || !value || !value.playlist?.tracks?.length) return
|
||||
musicSyncState,
|
||||
async (value, previous) => {
|
||||
const music = state.value?.music
|
||||
if (!music || !music.playlist?.tracks?.length) return
|
||||
await nextTick()
|
||||
media.volume = Math.max(0, Math.min(1, (value.volume ?? 100) / 100))
|
||||
media.muted = !state.value?.soundEnabled
|
||||
media.currentTime = (value.positionMs || 0) / 1000
|
||||
if (value.playback === 'MUSIC_PLAYBACK_PLAYING') {
|
||||
const media = musicAudioElement.value
|
||||
if (!media) return
|
||||
const trackChanged = !previous || value[0] !== previous[0] || value[1] !== previous[1]
|
||||
const playbackChanged = !previous || value[2] !== previous[2]
|
||||
if (!trackChanged && !playbackChanged) return
|
||||
await waitForMusicMetadata(media)
|
||||
media.currentTime = trackChanged ? 0 : (music.positionMs || 0) / 1000
|
||||
if (music.playback === 'MUSIC_PLAYBACK_PLAYING') {
|
||||
void media.play().catch(() => {
|
||||
mediaError.value = 'Браузер заблокировал воспроизведение музыки. Нажмите «Подготовить экран».'
|
||||
})
|
||||
@@ -211,7 +233,18 @@ watch(
|
||||
media.pause()
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => [state.value?.music?.volume, state.value?.soundEnabled] as const,
|
||||
([volume, soundEnabled]) => {
|
||||
const media = musicAudioElement.value
|
||||
if (!media) return
|
||||
media.volume = Math.max(0, Math.min(1, (volume ?? 100) / 100))
|
||||
media.muted = !soundEnabled
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
async function advanceMusic() {
|
||||
|
||||
Reference in New Issue
Block a user