ffplayout/pages/media.vue

631 lines
21 KiB
Vue
Raw Normal View History

2020-01-30 15:25:10 -05:00
<template>
<div>
2020-04-15 12:11:14 -04:00
<Menu />
2020-05-04 06:16:35 -04:00
<b-container
class="browser-container"
@drop.prevent="addFile"
@dragover.prevent
@dragenter.prevent="dragEnter"
@dragleave.prevent="dragLeave"
>
<div class="drag-file" :class="fileDragClass">
<span>
<b-icon-box-arrow-in-down />
</span>
</div>
2020-01-31 07:45:56 -05:00
<div v-if="folderTree.tree" class="browser">
2020-04-20 12:07:12 -04:00
<div class="bread-div">
2020-01-31 07:45:56 -05:00
<b-breadcrumb>
<b-breadcrumb-item
v-for="(crumb, index) in crumbs"
:key="crumb.key"
2020-04-07 11:56:33 -04:00
:active="index === crumbs.length - 1"
2020-04-16 12:30:47 -04:00
@click="getPath(extensions, crumb.path)"
2020-01-31 07:45:56 -05:00
>
{{ crumb.text }}
</b-breadcrumb-item>
</b-breadcrumb>
</div>
2020-01-30 15:25:10 -05:00
2020-04-20 12:07:12 -04:00
<splitpanes class="browser-row default-theme pane-row">
<pane min-size="20" size="24">
2020-01-31 07:45:56 -05:00
<div class="browser-div">
2020-04-20 12:07:12 -04:00
<perfect-scrollbar>
<b-list-group class="folder-list">
<b-list-group-item
v-for="folder in folderTree.tree[1]"
:key="folder.key"
class="browser-item folder"
>
<b-row>
<b-col cols="1" class="browser-icons-col">
<b-icon-folder-fill class="browser-icons" />
</b-col>
<b-col class="browser-item-text">
2020-05-04 07:22:51 -04:00
<b-link @click="getPath(extensions, `/${folderTree.tree[0]}/${folder}`)">
2020-04-20 12:07:12 -04:00
{{ folder }}
</b-link>
</b-col>
<b-col v-if="folder !== '..'" cols="1" class="folder-delete">
2020-05-04 07:22:51 -04:00
<b-link @click="showDeleteModal('Folder', `/${folderTree.tree[0]}/${folder}`)">
2020-04-20 12:07:12 -04:00
<b-icon-x-circle-fill />
</b-link>
</b-col>
</b-row>
</b-list-group-item>
</b-list-group>
</perfect-scrollbar>
2020-01-31 07:45:56 -05:00
</div>
2020-04-20 12:07:12 -04:00
</pane>
<pane class="files-col">
<loading
:active.sync="isLoading"
:can-cancel="false"
:is-full-page="false"
background-color="#485159"
color="#ff9c36"
/>
2020-01-31 07:45:56 -05:00
<div class="browser-div">
2020-04-20 12:07:12 -04:00
<perfect-scrollbar>
<b-list-group class="files-list">
<b-list-group-item
v-for="file in folderTree.tree[2]"
:key="file.key"
class="browser-item"
>
<b-row>
<b-col cols="1" class="browser-icons-col">
<b-icon-film class="browser-icons" />
</b-col>
<b-col class="browser-item-text">
{{ file.file }}
</b-col>
<b-col cols="1" class="browser-play-col">
2020-05-12 15:33:34 -04:00
<b-link title="Preview" @click="showPreviewModal(`/${folderTree.tree[0]}/${file.file}`)">
2020-04-20 12:07:12 -04:00
<b-icon-play-fill />
</b-link>
</b-col>
<b-col cols="1" class="browser-dur-col">
<span class="duration">{{ file.duration | toMin }}</span>
</b-col>
2020-05-12 15:33:34 -04:00
<b-col cols="1" class="small-col">
<b-link title="Rename File" @click="showRenameModal(`/${folderTree.tree[0]}/`, file.file)">
<b-icon-pencil-square />
</b-link>
</b-col>
<b-col cols="1" class="small-col">
<b-link title="Delete File" @click="showDeleteModal('File', `/${folderTree.tree[0]}/${file.file}`)">
2020-04-20 12:07:12 -04:00
<b-icon-x-circle-fill />
</b-link>
</b-col>
</b-row>
</b-list-group-item>
</b-list-group>
</perfect-scrollbar>
</div>
</pane>
</splitpanes>
2020-04-21 09:29:55 -04:00
<b-button-group class="media-button">
<b-button title="Create Folder" variant="primary" @click="showCreateFolderModal()">
2020-04-21 09:29:55 -04:00
<b-icon-folder-plus />
</b-button>
<b-button title="Upload File" variant="primary" @click="showUploadModal()">
2020-04-21 09:29:55 -04:00
<b-icon-upload />
</b-button>
</b-button-group>
2020-04-20 12:07:12 -04:00
</div>
</b-container>
<b-modal
id="preview-modal"
ref="prev-modal"
size="xl"
centered
:title="`Preview: ${previewName}`"
hide-footer
>
<b-img v-if="isImage" :src="previewSource" fluid :alt="previewName" />
<video-player v-else-if="!isImage && previewOptions" reference="previewPlayer" :options="previewOptions" />
</b-modal>
2020-04-21 09:29:55 -04:00
<b-modal
id="folder-modal"
ref="folder-modal"
size="xl"
centered
title="Create Folder"
hide-footer
>
<b-form @submit="onSubmitCreateFolder" @reset="onCancelCreateFolder">
<b-form-input
id="folder-name"
v-model="folderName"
type="text"
required
2020-07-06 08:42:46 -04:00
autofocus
2020-04-21 09:29:55 -04:00
placeholder="Enter a unique folder name"
/>
<div class="media-button">
<b-button type="submit" variant="primary">
Create
</b-button>
<b-button type="reset" variant="primary">
Cancel
</b-button>
</div>
</b-form>
</b-modal>
2020-04-20 12:07:12 -04:00
<b-modal
id="upload-modal"
ref="up-modal"
size="xl"
centered
title="File Upload"
hide-footer
2020-05-24 16:23:02 -04:00
no-close-on-backdrop
2020-04-20 12:07:12 -04:00
>
<b-form @submit="onSubmitUpload" @reset="onResetUpload">
<b-form-file
v-model="inputFiles"
:state="Boolean(inputFiles)"
2020-05-04 06:16:35 -04:00
:placeholder="inputPlaceholder"
2020-04-20 12:07:12 -04:00
drop-placeholder="Drop files here..."
multiple
2020-07-06 05:55:30 -04:00
:accept="extensions.replace(/,/g, ', ')"
2020-04-20 12:07:12 -04:00
:file-name-formatter="formatNames"
/>
<b-row>
<b-col cols="10">
<b-row class="progress-row">
<b-col cols="1">
Overall:
</b-col>
<b-col cols="10">
<b-progress :value="overallProgress" />
</b-col>
<div class="w-100" />
<b-col cols="1">
Current:
</b-col>
<b-col cols="10">
<b-progress :value="currentProgress" />
</b-col>
<div class="w-100" />
<b-col cols="1">
Uploading:
</b-col>
<b-col cols="10">
<strong>{{ uploadTask }}</strong>
</b-col>
</b-row>
</b-col>
<b-col cols="2">
2020-04-21 09:29:55 -04:00
<div class="media-button">
2020-04-20 12:07:12 -04:00
<b-button type="submit" variant="primary">
Upload
</b-button>
<b-button type="reset" variant="primary">
Cancel
</b-button>
2020-01-31 07:45:56 -05:00
</div>
</b-col>
</b-row>
2020-04-20 12:07:12 -04:00
</b-form>
</b-modal>
2020-05-12 15:33:34 -04:00
<b-modal id="rename-modal" title="Rename File" centered hide-footer>
<b-form @submit="renameFile">
<b-form-group
id="input-group-1"
label-for="input-1"
>
<b-form-input
id="input-1"
v-model="renameNewName"
type="text"
placeholder=""
/>
</b-form-group>
<div class="media-button">
<b-button type="submit" variant="primary">
Rename
</b-button>
<b-button variant="primary" @click="cancelRename()">
Cancel
</b-button>
</div>
</b-form>
</b-modal>
2020-04-20 12:07:12 -04:00
<b-modal id="delete-modal" :title="`Delete ${deleteType}`" centered hide-footer>
<p>
Are you sure that you want to delete:<br>
<strong>{{ previewName }}</strong>
</p>
2020-04-21 09:29:55 -04:00
<div class="media-button">
2020-04-20 12:07:12 -04:00
<b-button variant="primary" @click="deleteFileOrFolder()">
Ok
</b-button>
<b-button variant="primary" @click="cancelDelete()">
Cancel
</b-button>
2020-01-31 07:45:56 -05:00
</div>
2020-04-20 12:07:12 -04:00
</b-modal>
2020-01-30 15:25:10 -05:00
</div>
</template>
<script>
2020-01-31 07:45:56 -05:00
import { mapState } from 'vuex'
2020-04-15 12:11:14 -04:00
import Menu from '@/components/Menu.vue'
2020-01-31 07:45:56 -05:00
2020-01-30 15:25:10 -05:00
export default {
2020-01-31 07:45:56 -05:00
name: 'Media',
middleware: 'auth',
2020-01-31 07:45:56 -05:00
2020-04-15 12:11:14 -04:00
components: {
Menu
},
2020-01-30 15:25:10 -05:00
2020-04-13 15:35:24 -04:00
data () {
return {
2020-04-16 12:30:47 -04:00
isLoading: false,
2020-05-04 06:16:35 -04:00
fileDragClass: '',
2020-04-16 12:30:47 -04:00
extensions: '',
2020-04-21 09:29:55 -04:00
folderName: '',
2020-05-04 06:16:35 -04:00
inputFiles: [],
inputPlaceholder: 'Choose files or drop them here...',
2020-04-20 12:07:12 -04:00
previewOptions: {},
previewComp: null,
previewName: '',
previewSource: '',
2020-05-12 15:33:34 -04:00
renamePath: '',
renameOldName: '',
renameNewName: '',
2020-04-20 12:07:12 -04:00
deleteType: 'File',
deleteSource: '',
isImage: false,
uploadTask: '',
overallProgress: 0,
currentProgress: 0,
cancelTokenSource: this.$axios.CancelToken.source(),
lastPath: ''
2020-04-13 15:35:24 -04:00
}
},
2020-01-31 07:45:56 -05:00
computed: {
2020-04-16 12:30:47 -04:00
...mapState('config', ['configGui', 'configPlayout']),
2020-01-31 07:45:56 -05:00
...mapState('media', ['crumbs', 'folderTree'])
},
2020-05-24 07:35:22 -04:00
created () {
2020-07-06 05:55:30 -04:00
this.extensions = [...this.configPlayout.storage.extensions, ...this.configGui.extra_extensions].join(',')
2020-04-16 12:30:47 -04:00
this.getPath(this.extensions, '')
2020-01-30 15:25:10 -05:00
},
methods: {
2020-04-16 12:30:47 -04:00
async getPath (extensions, path) {
2020-04-20 12:07:12 -04:00
this.lastPath = path
2020-04-16 12:30:47 -04:00
this.isLoading = true
await this.$store.dispatch('media/getTree', { extensions, path })
this.isLoading = false
2020-04-13 15:35:24 -04:00
},
2020-05-04 06:16:35 -04:00
dragEnter (evt) {
evt.preventDefault()
this.fileDragClass = 'drop-file-visible'
},
dragLeave (evt) {
evt.preventDefault()
this.fileDragClass = ''
this.inputPlaceholder = 'Choose files or drop them here...'
},
addFile (evt) {
evt.preventDefault()
const droppedFiles = evt.dataTransfer.files
if (!droppedFiles) {
return
}
([...droppedFiles]).forEach((f) => {
this.inputFiles.push(f)
})
if (this.inputFiles.length === 1) {
this.inputPlaceholder = this.inputFiles[0].name
} else {
this.inputPlaceholder = `${this.inputFiles.length} files selected`
}
this.fileDragClass = ''
this.showUploadModal()
},
2020-04-21 09:29:55 -04:00
showCreateFolderModal () {
this.$root.$emit('bv::show::modal', 'folder-modal')
},
async onSubmitCreateFolder (evt) {
evt.preventDefault()
await this.$axios.post(
'api/player/media/op/',
2020-04-30 04:53:13 -04:00
{ folder: this.folderName, path: this.crumbs.map(e => e.text).join('/') }
2020-04-21 09:29:55 -04:00
)
this.$root.$emit('bv::hide::modal', 'folder-modal')
this.getPath(this.extensions, this.lastPath)
},
onCancelCreateFolder (evt) {
evt.preventDefault()
this.$root.$emit('bv::hide::modal', 'folder-modal')
},
2020-04-20 12:07:12 -04:00
showUploadModal () {
this.uploadTask = ''
this.currentProgress = 0
this.overallProgress = 0
this.$root.$emit('bv::show::modal', 'upload-modal')
},
formatNames (files) {
if (files.length === 1) {
return files[0].name
} else {
return `${files.length} files selected`
}
},
async onSubmitUpload (evt) {
2020-04-13 15:35:24 -04:00
evt.preventDefault()
2020-04-20 12:07:12 -04:00
const uploadProgress = fileName => (progressEvent) => {
const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total)
2020-05-24 16:12:53 -04:00
this.$store.dispatch('auth/inspectToken')
2020-04-20 12:07:12 -04:00
this.currentProgress = progress
2020-04-13 15:35:24 -04:00
}
2020-04-20 12:07:12 -04:00
for (const [i, file] of this.inputFiles.entries()) {
this.uploadTask = file.name
const config = {
onUploadProgress: uploadProgress(file.name),
cancelToken: this.cancelTokenSource.token,
headers: { Authorization: 'Bearer ' + this.$store.state.auth.jwtToken }
}
await this.$axios.put(
`api/player/media/upload/${encodeURIComponent(file.name)}?path=${encodeURIComponent(this.crumbs.map(e => e.text).join('/'))}`,
2020-04-20 12:07:12 -04:00
file,
config
)
2020-05-24 16:12:53 -04:00
.then((res) => {
this.overallProgress = (i + 1) * 100 / this.inputFiles.length
2020-04-30 04:53:13 -04:00
this.currentProgress = 0
2020-05-24 16:12:53 -04:00
})
2020-04-20 12:07:12 -04:00
.catch(err => console.log(err))
}
this.uploadTask = 'Done...'
2020-05-04 06:16:35 -04:00
this.inputPlaceholder = 'Choose files or drop them here...'
this.inputFiles = []
2020-04-20 12:07:12 -04:00
this.getPath(this.extensions, this.lastPath)
2020-04-21 09:29:55 -04:00
this.$root.$emit('bv::hide::modal', 'upload-modal')
2020-04-20 12:07:12 -04:00
},
onResetUpload (evt) {
evt.preventDefault()
2020-05-04 06:16:35 -04:00
this.inputFiles = []
2020-04-20 12:07:12 -04:00
this.overallProgress = 0
this.currentProgress = 0
this.uploadTask = ''
2020-05-04 06:16:35 -04:00
this.inputPlaceholder = 'Choose files or drop them here...'
2020-04-20 12:07:12 -04:00
this.cancelTokenSource.cancel('Upload cancelled')
this.getPath(this.extensions, this.lastPath)
this.$root.$emit('bv::hide::modal', 'upload-modal')
},
showPreviewModal (src) {
this.previewSource = src
this.previewName = src.split('/').slice(-1)[0]
const ext = this.previewName.split('.').slice(-1)[0]
if (this.configPlayout.storage.extensions.includes(`.${ext}`)) {
this.isImage = false
this.previewOptions = {
liveui: false,
controls: true,
suppressNotSupportedError: true,
autoplay: false,
preload: 'auto',
sources: [
{
type: `video/${ext}`,
2020-07-07 09:39:29 -04:00
src: '/' + encodeURIComponent(src.replace(/^\//, ''))
2020-04-20 12:07:12 -04:00
}
]
}
} else {
this.isImage = true
}
this.$root.$emit('bv::show::modal', 'preview-modal')
},
2020-05-12 15:33:34 -04:00
showRenameModal (path, file) {
this.renamePath = path
this.renameOldName = file
this.renameNewName = file
this.$root.$emit('bv::show::modal', 'rename-modal')
},
async renameFile (evt) {
evt.preventDefault()
await this.$axios.patch(
'api/player/media/op/',
{ path: this.renamePath.replace(/^\/\//g, '/'), oldname: this.renameOldName, newname: this.renameNewName }
)
this.getPath(this.extensions, this.lastPath)
this.renamePath = ''
this.renameOldName = ''
this.renameNewName = ''
this.$root.$emit('bv::hide::modal', 'rename-modal')
},
cancelRename () {
this.renamePath = ''
this.renameOldName = ''
this.renameNewName = ''
this.$root.$emit('bv::hide::modal', 'rename-modal')
},
2020-04-20 12:07:12 -04:00
showDeleteModal (type, src) {
this.deleteSource = src
if (type === 'File') {
this.previewName = src.split('/').slice(-1)[0]
} else {
this.previewName = src
}
this.deleteType = type
this.$root.$emit('bv::show::modal', 'delete-modal')
},
async deleteFileOrFolder () {
let file
2020-04-21 09:29:55 -04:00
let pathName
2020-04-20 12:07:12 -04:00
if (this.deleteType === 'File') {
file = this.deleteSource.split('/').slice(-1)[0]
2020-04-21 09:29:55 -04:00
pathName = this.deleteSource.substring(0, this.deleteSource.lastIndexOf('/') + 1)
2020-04-20 12:07:12 -04:00
} else {
file = null
2020-04-21 09:29:55 -04:00
pathName = this.deleteSource
2020-04-20 12:07:12 -04:00
}
await this.$axios.delete(`api/player/media/op/?file=${encodeURIComponent(file)}&path=${encodeURIComponent(pathName)}`)
2020-04-13 15:35:24 -04:00
.catch(err => console.log(err))
2020-04-20 12:07:12 -04:00
this.$root.$emit('bv::hide::modal', 'delete-modal')
this.getPath(this.extensions, this.lastPath)
},
cancelDelete () {
this.deleteSource = ''
this.$root.$emit('bv::hide::modal', 'delete-modal')
2020-01-30 15:25:10 -05:00
}
}
}
</script>
<style>
2020-05-04 06:16:35 -04:00
.browser-container {
position: relative;
2020-01-31 07:45:56 -05:00
width: 100%;
max-width: 100%;
2020-04-20 12:07:12 -04:00
height: calc(100% - 40px);
}
2020-05-04 06:16:35 -04:00
.browser {
position: absolute;
width: calc(100% - 30px);
height: calc(100% - 40px);
}
.drag-file {
position: absolute;
display: none;
background: rgba(48, 54, 61, 0.75);
width: calc(100% - 30px);
height: calc(100% - 80px);
text-align: center;
border: 2px solid #ddd;
border-radius: .25em;
z-index: 2;
margin: auto;
}
.drop-file-visible {
display: table;
}
.drag-file span {
display: table-cell;
vertical-align: middle;
font-size: 10em;
}
2020-04-20 12:07:12 -04:00
.bread-div {
height: 50px;
}
.browser-div {
background: #30363d;
height: 100%;
border: 1px solid #000;
border-radius: 5px;
}
.browser-row {
height: calc(100% - 90px);
min-height: 50px;
2020-01-31 07:45:56 -05:00
}
.folder-col {
min-width: 320px;
max-width: 460px;
2020-04-20 12:07:12 -04:00
height: 100%;
2020-01-31 07:45:56 -05:00
}
2020-01-30 15:25:10 -05:00
2020-04-20 12:07:12 -04:00
.folder:hover > div > .folder-delete {
display: inline;
}
.folder-list {
height: 100%;
padding: .5em;
}
.folder-delete {
margin-right: .5em;
display: none;
2020-01-31 07:45:56 -05:00
}
.files-col {
min-width: 320px;
2020-04-20 12:07:12 -04:00
height: 100%;
2020-01-31 07:45:56 -05:00
}
2020-05-12 15:33:34 -04:00
.small-col {
max-width: 50px;
}
2020-04-20 12:07:12 -04:00
.files-list {
width: 99.5%;
2020-01-31 07:45:56 -05:00
height: 100%;
2020-04-20 12:07:12 -04:00
padding: .5em;
}
2020-04-21 09:29:55 -04:00
.media-button {
2020-04-20 12:07:12 -04:00
float: right;
margin-top: 1em;
}
.progress-row {
margin-top: 1em;
}
.progress-row .col-1 {
min-width: 60px
}
.progress-row .col-10 {
margin: auto 0 auto 0
2020-01-31 07:45:56 -05:00
}
2020-01-30 15:25:10 -05:00
</style>