ffplayout/pages/media.vue
2023-03-27 10:40:11 +02:00

321 lines
11 KiB
Vue

<template>
<div>
<Menu />
<div class="container-fluid browser-container">
<Browser ref="browser" />
</div>
<div class="btn-group media-button">
<button
type="button"
class="btn btn-primary"
title="Create Folder"
data-bs-toggle="modal"
data-bs-target="#folderModal"
>
<i class="bi-folder-plus" />
</button>
<button
type="button"
class="btn btn-primary"
title="Upload File"
data-bs-toggle="modal"
data-bs-target="#uploadModal"
>
<i class="bi-upload" />
</button>
</div>
<div id="folderModal" class="modal" tabindex="-1" aria-labelledby="folderModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="folderModalLabel">Create Folder</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cancel"></button>
</div>
<form @submit.prevent="onSubmitCreateFolder" @reset="onCancelCreateFolder">
<div class="modal-body">
<input type="text" class="form-control" v-model="folderName" />
</div>
<div class="modal-footer">
<button type="reset" class="btn btn-primary" data-bs-dismiss="modal" aria-label="Cancel">
Cancel
</button>
<button type="submit" class="btn btn-primary" data-bs-dismiss="modal">Ok</button>
</div>
</form>
</div>
</div>
</div>
<div
id="uploadModal"
ref="uploadModal"
class="modal"
tabindex="-1"
aria-labelledby="uploadModalLabel"
data-bs-backdrop="static"
>
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="uploadModalLabel">Upload Files</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cancel"></button>
</div>
<form @submit.prevent="onSubmitUpload" @reset="onResetUpload">
<div class="modal-body">
<input
class="form-control"
type="file"
:accept="extensions"
v-on:change="onFileChange"
multiple
/>
<div class="row">
<div class="col-10">
<div class="row progress-row">
<div class="col-1" style="min-width: 125px">
Overall ({{ currentNumber }}/{{ inputFiles.length }}):
</div>
<div class="col-10">
<div class="progress">
<div
class="progress-bar bg-warning"
role="progressbar"
:aria-valuenow="overallProgress"
:style="`width: ${overallProgress}%`"
/>
</div>
</div>
<div class="w-100" />
<div class="col-1" style="min-width: 125px">Current:</div>
<div class="col-10">
<div class="progress">
<div
class="progress-bar bg-warning"
role="progressbar"
:aria-valuenow="currentProgress"
:style="`width: ${currentProgress}%`"
/>
</div>
</div>
<div class="w-100" />
<div class="col-1" style="min-width: 125px">Uploading:</div>
<div class="col-10">
<strong>{{ uploadTask }}</strong>
</div>
</div>
</div>
<div class="col-2">
<div class="media-button">
<button type="reset" class="btn btn-primary me-2" data-bs-dismiss="modal">
Cancel
</button>
<button type="submit" class="btn btn-primary">Upload</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useAuth } from '~/stores/auth'
import { useConfig } from '~/stores/config'
import { useIndex } from '~/stores/index'
import { useMedia } from '~/stores/media'
import Browser from '../components/Browser.vue'
const { $bootstrap } = useNuxtApp()
const authStore = useAuth()
const configStore = useConfig()
const indexStore = useIndex()
const mediaStore = useMedia()
const contentType = { 'content-type': 'application/json;charset=UTF-8' }
definePageMeta({
middleware: ['auth'],
})
useHead({
title: 'Media | ffplayout'
})
const browser = ref()
const uploadModal = ref()
const extensions = ref('')
const folderName = ref('')
const inputFiles = ref([] as File[])
const currentNumber = ref(0)
const uploadTask = ref('')
const overallProgress = ref(0)
const currentProgress = ref(0)
const lastPath = ref('')
const thisUploadModal = ref()
const xhr = ref(new XMLHttpRequest())
onMounted(async () => {
const exts = [
...configStore.configPlayout.storage.extensions,
...configStore.configGui[configStore.configID].extra_extensions.split(','),
].map((ext) => {
return `.${ext}`
})
extensions.value = exts.join(', ')
thisUploadModal.value = $bootstrap.Modal.getOrCreateInstance(uploadModal.value)
})
async function onSubmitCreateFolder(evt: any) {
evt.preventDefault()
const path = `${mediaStore.folderTree.source}/${folderName.value}`.replace(/\/[/]+/g, '/')
lastPath.value = mediaStore.folderTree.source
if (mediaStore.folderTree.folders.includes(folderName.value)) {
indexStore.alertVariant = 'alert-warning'
indexStore.alertMsg = `Folder "${folderName.value}" exists already!`
indexStore.showAlert = true
return
}
await $fetch(`/api/file/${configStore.configGui[configStore.configID].id}/create-folder/`, {
method: 'POST',
headers: { ...contentType, ...authStore.authHeader },
body: JSON.stringify({ source: path }),
})
.then(() => {
indexStore.alertVariant = 'alert-success'
indexStore.alertMsg = 'Folder create done...'
})
.catch((e: string) => {
indexStore.alertVariant = 'alert-danger'
indexStore.alertMsg = `Folder create error: ${e}`
})
indexStore.showAlert = true
folderName.value = ''
setTimeout(() => {
indexStore.alertMsg = ''
indexStore.showAlert = false
}, 2000)
browser.value.getPath(lastPath.value)
}
function onCancelCreateFolder(evt: any) {
evt.preventDefault()
folderName.value = ''
}
function onFileChange(evt: any) {
const files = evt.target.files || evt.dataTransfer.files
if (!files.length) {
return
}
inputFiles.value = files
}
async function onSubmitUpload(evt: any) {
evt.preventDefault()
lastPath.value = mediaStore.folderTree.source
for (let i = 0; i < inputFiles.value.length; i++) {
const file = inputFiles.value[i]
uploadTask.value = file.name
currentNumber.value = i + 1
const formData = new FormData()
formData.append(file.name, file)
xhr.value = new XMLHttpRequest()
xhr.value.open(
'PUT',
`/api/file/${configStore.configGui[configStore.configID].id}/upload/?path=${encodeURIComponent(
mediaStore.crumbs[mediaStore.crumbs.length - 1].path
)}`
)
xhr.value.setRequestHeader('Authorization', `Bearer ${authStore.jwtToken}`)
xhr.value.upload.onprogress = function (event) {
currentProgress.value = Math.round((100 * event.loaded) / event.total)
}
xhr.value.upload.onerror = function () {
indexStore.alertVariant = 'alert-danger'
indexStore.alertMsg = `Upload error: ${xhr.value.status}`
indexStore.showAlert = true
}
// upload completed successfully
xhr.value.onload = function () {
overallProgress.value = (currentNumber.value * 100) / inputFiles.value.length
currentProgress.value = 100
}
xhr.value.send(formData)
}
uploadTask.value = 'Done...'
browser.value.getPath(lastPath.value)
setTimeout(() => {
thisUploadModal.value.hide()
currentNumber.value = 0
currentProgress.value = 0
overallProgress.value = 0
inputFiles.value = []
indexStore.showAlert = false
}, 1000)
}
function onResetUpload(evt: any) {
evt.preventDefault()
inputFiles.value = []
overallProgress.value = 0
currentProgress.value = 0
uploadTask.value = ''
xhr.value.abort()
}
</script>
<style lang="scss">
.browser-container {
position: relative;
width: 100%;
max-width: 100%;
height: calc(100% - 140px);
}
.browser-container > div {
height: 100%;
}
.progress-row {
margin-top: 1em;
}
.progress-row .col-1 {
min-width: 60px;
}
.progress-row .col-10 {
margin: auto 0 auto 0;
}
.progress {
padding: 0;
}
</style>