This commit is contained in:
2026-10-27 15:16:47 +07:00
parent eb2d7c54af
commit a5f8b5757b
5 changed files with 48 additions and 21 deletions
+1
View File
@@ -1544,6 +1544,7 @@ message UploadPlaylistTrackReq {
string name = 2; string name = 2;
string filename = 3; string filename = 3;
bytes data = 4; bytes data = 4;
int32 groupID = 5;
} }
message UploadPlaylistTrackRsp { message UploadPlaylistTrackRsp {
@@ -309,6 +309,7 @@ export type UploadPlaylistTrackReq = {
name: string | undefined; name: string | undefined;
filename: string | undefined; filename: string | undefined;
data: string | undefined; data: string | undefined;
groupID: number | undefined;
}; };
export type UploadPlaylistTrackRsp = { export type UploadPlaylistTrackRsp = {
+2 -2
View File
@@ -37,7 +37,7 @@ const emit = defineEmits<{
renamePlaylist: [id: number, name: string] renamePlaylist: [id: number, name: string]
reorderTracks: [playlistID: number, trackIDs: number[]] reorderTracks: [playlistID: number, trackIDs: number[]]
deletePlaylist: [id: number] deletePlaylist: [id: number]
uploadTrack: [playlistID: number, file: File] uploadTrack: [playlistID: number, file: File, groupID: number]
deleteTrack: [playlistID: number, trackID: number] deleteTrack: [playlistID: number, trackID: number]
}>() }>()
@@ -137,7 +137,7 @@ watch(
@rename-playlist="(id, name) => emit('renamePlaylist', id, name)" @rename-playlist="(id, name) => emit('renamePlaylist', id, name)"
@reorder-tracks="(playlistID, trackIDs) => emit('reorderTracks', playlistID, trackIDs)" @reorder-tracks="(playlistID, trackIDs) => emit('reorderTracks', playlistID, trackIDs)"
@delete-playlist="(id) => emit('deletePlaylist', id)" @delete-playlist="(id) => emit('deletePlaylist', id)"
@upload-track="(playlistID, file) => emit('uploadTrack', playlistID, file)" @upload-track="(playlistID, file, groupID) => emit('uploadTrack', playlistID, file, groupID)"
@delete-track="(playlistID, trackID) => emit('deleteTrack', playlistID, trackID)" @delete-track="(playlistID, trackID) => emit('deleteTrack', playlistID, trackID)"
/> />
<n-space v-else vertical class="music-content"> <n-space v-else vertical class="music-content">
+36 -17
View File
@@ -44,7 +44,7 @@ const emit = defineEmits<{
renamePlaylist: [id: number, name: string] renamePlaylist: [id: number, name: string]
reorderTracks: [playlistID: number, trackIDs: number[]] reorderTracks: [playlistID: number, trackIDs: number[]]
deletePlaylist: [id: number] deletePlaylist: [id: number]
uploadTrack: [playlistID: number, file: File] uploadTrack: [playlistID: number, file: File, groupID: number]
deleteTrack: [playlistID: number, trackID: number] deleteTrack: [playlistID: number, trackID: number]
}>() }>()
@@ -108,6 +108,12 @@ const isPlaylistActive = computed(
selectedId.value === props.music?.playlist?.ID && selectedId.value === props.music?.playlist?.ID &&
props.music?.playback !== 'MUSIC_PLAYBACK_STOPPED', props.music?.playback !== 'MUSIC_PLAYBACK_STOPPED',
) )
function isGroupMissing(groupID: number) {
return (
isSelectedMissing.value ||
(groupID !== 0 && !selected.value?.groups?.some((group) => group.ID === groupID))
)
}
function isGroupLocked(groupID: number) { function isGroupLocked(groupID: number) {
return isPlaylistActive.value && (!props.music?.groupId || props.music.groupId === groupID) return isPlaylistActive.value && (!props.music?.groupId || props.music.groupId === groupID)
} }
@@ -150,9 +156,9 @@ function move(id: number, groupID: number, offset: number) {
order.value[from] = targetID order.value[from] = targetID
order.value[to] = id order.value[to] = id
} }
function uploadTrack(options: { file: UploadFileInfo }) { function uploadTrack(options: { file: UploadFileInfo }, groupID: number) {
if (selectedId.value && options.file.file) if (selectedId.value && options.file.file)
emit('uploadTrack', selectedId.value, options.file.file) emit('uploadTrack', selectedId.value, options.file.file, groupID)
} }
watch(selectedId, () => { watch(selectedId, () => {
lastSelected.value = props.playlists.find((item) => item.ID === selectedId.value) lastSelected.value = props.playlists.find((item) => item.ID === selectedId.value)
@@ -279,17 +285,6 @@ reset()
@click="emit('renamePlaylist', selectedId, name.trim())" @click="emit('renamePlaylist', selectedId, name.trim())"
>Сохранить название</n-button >Сохранить название</n-button
> >
<n-upload
accept=".mp3,audio/mpeg"
:default-upload="false"
:show-file-list="false"
:disabled="isBusy || isOrderDirty || hasConflict"
@change="uploadTrack"
>
<n-button size="small" :disabled="isBusy || isOrderDirty || hasConflict"
>Добавить MP3</n-button
>
</n-upload>
<n-button <n-button
v-if="music" v-if="music"
size="small" size="small"
@@ -362,9 +357,7 @@ reset()
<PlaylistGroupEditor <PlaylistGroupEditor
v-if="section.ID" v-if="section.ID"
:group="section" :group="section"
:is-missing=" :is-missing="isGroupMissing(section.ID)"
isSelectedMissing || !selected.groups?.some((group) => group.ID === section.ID)
"
:is-busy="isBusy || isOrderDirty || hasConflict" :is-busy="isBusy || isOrderDirty || hasConflict"
:is-locked="isGroupLocked(section.ID)" :is-locked="isGroupLocked(section.ID)"
:can-move-up="!isPlaylistActive && groupIndex > 0" :can-move-up="!isPlaylistActive && groupIndex > 0"
@@ -374,6 +367,32 @@ reset()
@dirty="dirtyGroups[section.ID] = $event" @dirty="dirtyGroups[section.ID] = $event"
@move="moveGroup(section.ID, $event)" @move="moveGroup(section.ID, $event)"
/> />
<n-upload
accept=".mp3,audio/mpeg"
:default-upload="false"
:show-file-list="false"
:disabled="
isBusy ||
isOrderDirty ||
hasConflict ||
isGroupMissing(section.ID) ||
(section.ID !== 0 && isGroupLocked(section.ID))
"
@change="uploadTrack($event, section.ID)"
>
<n-button
size="small"
:disabled="
isBusy ||
isOrderDirty ||
hasConflict ||
isGroupMissing(section.ID) ||
(section.ID !== 0 && isGroupLocked(section.ID))
"
:aria-label="`Добавить MP3 в группу ${section.name}`"
>Добавить MP3</n-button
>
</n-upload>
<p v-if="!trackIds(section.ID).length">Треков пока нет.</p> <p v-if="!trackIds(section.ID).length">Треков пока нет.</p>
<div v-for="(id, index) in trackIds(section.ID)" :key="id" class="track-row"> <div v-for="(id, index) in trackIds(section.ID)" :key="id" class="track-row">
<span <span
+8 -2
View File
@@ -128,7 +128,7 @@ export function usePlaylistActions(
} }
} }
async function uploadTrack(playlistID: number, file: File) { async function uploadTrack(playlistID: number, file: File, groupID: number) {
if (isMusicBusy.value) return if (isMusicBusy.value) return
if (!/\.mp3$/i.test(file.name) || file.size === 0 || file.size > 64 * 1024 * 1024) { if (!/\.mp3$/i.test(file.name) || file.size === 0 || file.size > 64 * 1024 * 1024) {
message.error('Выберите непустой MP3 размером до 64 МиБ') message.error('Выберите непустой MP3 размером до 64 МиБ')
@@ -138,7 +138,13 @@ export function usePlaylistActions(
onBeforeSave() onBeforeSave()
try { try {
const data = uint8ToBase64(new Uint8Array(await file.arrayBuffer())) const data = uint8ToBase64(new Uint8Array(await file.arrayBuffer()))
await client.UploadPlaylistTrack({ playlistID, name: file.name, filename: file.name, data }) await client.UploadPlaylistTrack({
playlistID,
groupID,
name: file.name,
filename: file.name,
data,
})
await onSaved() await onSaved()
} catch (err) { } catch (err) {
message.error(err instanceof Error ? err.message : 'Не удалось загрузить MP3') message.error(err instanceof Error ? err.message : 'Не удалось загрузить MP3')