v5.0.0 - support new API

This commit is contained in:
jb-alvarado 2022-07-06 16:22:27 +02:00
parent b3a7839535
commit 9ed60c027f
21 changed files with 130 additions and 72 deletions

View File

@ -15,25 +15,22 @@ After installations you have to setup ssl for your **https** connections.
## Some Impressions:
#### Login
![login](/docs/assets/login.png)
#### Landing Page
![landing-page](/docs/assets/landing-page.png)
![login](/docs/images/login.png)
#### Control Page
![control](/docs/assets/control.png)
![control](/docs/images/control.png)
#### Media Page
![media](/docs/assets/media.png)
![media](/docs/images/media.png)
#### Media Page / Upload
![media-upload](/docs/assets/media-upload.png)
![media-upload](/docs/images/media-upload.png)
#### Message Page
![message](/docs/assets/message.png)
![message](/docs/images/message.png)
#### Logging Page
![logging](/docs/assets/logging.png)
![logging](/docs/images/logging.png)
#### Configuration Page / GUI
![config-gui](/docs/assets/config-gui.png)
![config-gui](/docs/images/config-gui.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

BIN
docs/images/config-gui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
docs/images/control.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

BIN
docs/images/logging.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
docs/images/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

BIN
docs/images/media.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

BIN
docs/images/message.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -1,7 +1,7 @@
{
"name": "ffplayout",
"name": "ffplayout-frontend",
"version": "5.0.0",
"description": "web GUI for ffplayout engine",
"description": "Web GUI for ffplayout",
"author": "Jonathan Baecker",
"private": true,
"scripts": {

View File

@ -21,7 +21,7 @@
v-for="(crumb, index) in crumbs"
:key="crumb.key"
:active="index === crumbs.length - 1"
@click="getPath(extensions, crumb.path)"
@click="getPath(crumb.path)"
>
{{ crumb.text }}
</b-breadcrumb-item>
@ -43,7 +43,7 @@
<b-icon-folder-fill class="browser-icons" />
</b-col>
<b-col class="browser-item-text">
<b-link @click="getPath(extensions, `/${folderTree.source}/${folder}`)">
<b-link @click="getPath(`/${folderTree.source}/${folder}`)">
{{ folder }}
</b-link>
</b-col>
@ -304,7 +304,7 @@ export default {
watch: {
configID () {
this.getPath(this.extensions, '')
this.getPath('')
}
},
@ -314,14 +314,14 @@ export default {
})
this.extensions = exts.join(',')
this.getPath(this.extensions, '')
this.getPath('')
},
methods: {
async getPath (extensions, path) {
async getPath (path) {
this.lastPath = path
this.isLoading = true
await this.$store.dispatch('media/getTree', { extensions, path })
await this.$store.dispatch('media/getTree', { path })
this.isLoading = false
},
@ -369,7 +369,7 @@ export default {
)
this.$root.$emit('bv::hide::modal', 'folder-modal')
this.getPath(this.extensions, this.lastPath)
this.getPath(this.lastPath)
},
onCancelCreateFolder (evt) {
@ -429,7 +429,7 @@ export default {
this.currentNumber = 0
this.inputPlaceholder = 'Choose files or drop them here...'
this.inputFiles = []
this.getPath(this.extensions, this.lastPath)
this.getPath(this.lastPath)
this.$root.$emit('bv::hide::modal', 'upload-modal')
},
@ -442,7 +442,7 @@ export default {
this.inputPlaceholder = 'Choose files or drop them here...'
this.cancelTokenSource.cancel('Upload cancelled')
this.getPath(this.extensions, this.lastPath)
this.getPath(this.lastPath)
this.$root.$emit('bv::hide::modal', 'upload-modal')
},
@ -489,7 +489,7 @@ export default {
}
)
this.getPath(this.extensions, this.lastPath)
this.getPath(this.lastPath)
this.renamePath = ''
this.renameOldName = ''
@ -537,7 +537,7 @@ export default {
this.$root.$emit('bv::hide::modal', 'delete-modal')
this.getPath(this.extensions, this.lastPath)
this.getPath(this.lastPath)
},
cancelDelete () {

View File

@ -266,13 +266,13 @@ export default {
},
watch: {
selected (id) {
this.getPreset(id)
selected (index) {
this.getPreset(index)
}
},
created () {
this.getPreset('')
this.getPreset(null)
},
methods: {
@ -284,34 +284,34 @@ export default {
return (parseFloat(parseInt(num, 16)) / 255).toFixed(2)
},
async getPreset (id) {
async getPreset (index) {
const response = await this.$axios.get(`api/presets/${this.configGui[this.configID].id}`)
if (response.data && !id) {
if (response.data && index === null) {
this.presets = []
for (const item of response.data) {
this.presets.push({ value: item.id, text: item.name })
for (let index = 0; index < response.data.length; index++) {
const elem = response.data[index]
this.presets.push({ value: index, text: elem.name })
}
} else if (response.data) {
id -= 1
const fColor = response.data[id].fontcolor.split('@')
const bColor = response.data[id].boxcolor.split('@')
const fColor = response.data[index].fontcolor.split('@')
const bColor = response.data[index].boxcolor.split('@')
this.form = {
id: response.data[id].id,
name: response.data[id].name,
text: response.data[id].text,
x: response.data[id].x,
y: response.data[id].y,
fontSize: response.data[id].fontsize,
fontSpacing: response.data[id].line_spacing,
id: response.data[index].id,
name: response.data[index].name,
text: response.data[index].text,
x: response.data[index].x,
y: response.data[index].y,
fontSize: response.data[index].fontsize,
fontSpacing: response.data[index].line_spacing,
fontColor: fColor[0],
fontAlpha: (fColor[1]) ? this.hexToDec(fColor[1]) : 1.0,
showBox: response.data[id].box,
showBox: response.data[index].box,
boxColor: bColor[0],
boxAlpha: (bColor[1]) ? this.hexToDec(bColor[1]) : 1.0,
border: response.data[id].boxborderw,
overallAlpha: response.data[id].alpha
border: response.data[index].boxborderw,
overallAlpha: response.data[index].alpha
}
}
},
@ -344,7 +344,7 @@ export default {
if (response.status === 200) {
this.success = true
this.getPreset('')
this.getPreset(null)
} else {
this.failed = true
}
@ -394,7 +394,7 @@ export default {
}
this.$bvModal.hide('delete-modal')
this.getPreset('')
this.getPreset(null)
},
async submitMessage () {

View File

@ -152,7 +152,7 @@
v-for="(crumb, index) in crumbs"
:key="crumb.key"
:active="index === crumbs.length - 1"
@click="getPath(extensions, crumb.path)"
@click="getPath(crumb.path)"
>
{{ crumb.text }}
</b-breadcrumb-item>
@ -166,7 +166,7 @@
:key="folder.key"
class="browser-item"
>
<b-link @click="getPath(extensions, `/${folderTree.source}/${folder}`)">
<b-link @click="getPath(`/${folderTree.source}/${folder}`)">
<b-icon-folder-fill class="browser-icons" /> {{ folder }}
</b-link>
</b-list-group-item>
@ -310,6 +310,9 @@
<b-button v-if="!configPlayout.playlist.loop" v-b-tooltip.hover title="Loop Clips in Playlist" variant="primary" @click="loopClips()">
<b-icon-view-stacked />
</b-button>
<b-button v-b-tooltip.hover title="Add (remote) Source to Playlist" variant="primary" @click="showAddSource()">
<b-icon-file-earmark-plus />
</b-button>
<b-button v-b-tooltip.hover title="Generate a randomized Playlist" variant="primary" @click="generatePlaylist(listDate)">
<b-icon-sort-down-alt />
</b-button>
@ -351,6 +354,35 @@
>
Delete program from {{ listDate }}
</b-modal>
<b-modal
id="add-source-modal"
ref="add-source-modal"
title="Add (remote) Source"
@ok="handleSource"
>
<form ref="form" @submit.stop.prevent="addSource">
<b-form-group label="In" label-for="in-input">
<b-form-input id="in-input" v-model.number="newSource.in" type="number" inline />
</b-form-group>
<b-form-group label="Out" label-for="out-input" invalid-feedback="Out is required">
<b-form-input id="out-input" v-model.number="newSource.out" type="number" inline required />
</b-form-group>
<b-form-group label="Duration" label-for="duration-input" invalid-feedback="Out is required">
<b-form-input id="duration-input" v-model.number="newSource.duration" type="number" inline required />
</b-form-group>
<b-form-group label="Source" label-for="source-input" invalid-feedback="Source is required">
<b-form-input id="source-input" v-model="newSource.source" required />
</b-form-group>
<b-form-checkbox
id="ad-input"
v-model="newSource.category"
value="advertisement"
:unchecked-value="newSource.category"
>
Advertisement
</b-form-checkbox>
</form>
</b-modal>
</div>
</template>
@ -398,7 +430,6 @@ export default {
listDate: this.$dayjs().tz(this.timezone).format('YYYY-MM-DD'),
targetDate: this.$dayjs().tz(this.timezone).format('YYYY-MM-DD'),
interval: null,
extensions: '',
videoOptions: {
liveui: true,
controls: true,
@ -424,6 +455,14 @@ export default {
scrollOP: {
suppressScrollX: true,
minScrollbarLength: 30
},
newSource: {
begin: 0,
in: 0,
out: 0,
duration: 0,
category: '',
source: ''
}
}
},
@ -460,7 +499,7 @@ export default {
}
]
this.getPath(this.extensions, '')
this.getPath('')
this.getPlaylist()
setTimeout(() => { scrollTo(this) }, 3000)
}
@ -475,8 +514,7 @@ export default {
]
this.getStatus()
this.extensions = this.configPlayout.storage.extensions.join(',')
await this.getPath(this.extensions, '')
await this.getPath('')
const timeInSec = this.$timeToSeconds(this.$dayjs().tz(this.timezone).format('HH:mm:ss'))
const listStartSec = this.$timeToSeconds(this.configPlayout.playlist.day_start)
@ -489,20 +527,15 @@ export default {
},
mounted () {
// if (process.env.NODE_ENV === 'production') {
// this.interval = setInterval(() => {
// this.$store.dispatch('playlist/animClock')
// this.getStatus()
// }, 5000)
// } else {
// this.$store.dispatch('playlist/animClock')
// }
this.interval = setInterval(() => {
if (process.env.NODE_ENV === 'production') {
this.interval = setInterval(() => {
this.$store.dispatch('playlist/playoutStat')
this.getStatus()
}, 5000)
this.$store.dispatch('playlist/playoutStat')
this.getStatus()
}, 5000)
this.$store.dispatch('playlist/playoutStat')
} else {
this.$store.dispatch('playlist/playoutStat')
}
const streamExtension = this.configGui[this.configID].preview_url.split('.').pop()
let player
@ -533,9 +566,9 @@ export default {
},
methods: {
async getPath (extensions, path) {
async getPath (path) {
this.browserIsLoading = true
await this.$store.dispatch('media/getTree', { extensions, path })
await this.$store.dispatch('media/getTree', { path })
this.browserIsLoading = false
},
@ -591,14 +624,12 @@ export default {
this.$root.$emit('bv::show::modal', 'preview-modal')
},
cloneClip ({ file, duration }) {
cloneClip ({ name, duration }) {
const storagePath = this.configPlayout.storage.path
const storagePathArr = storagePath.split('/')
const storageRoot = storagePathArr.pop()
const sourcePath = `${storagePathArr.join('/')}/${this.folderTree.tree[0].substring(this.folderTree.tree[0].indexOf(storageRoot))}`
const sourcePath = `${storagePath}/${this.folderTree.source}/${name}`.replace('//', '/')
return {
source: `${sourcePath}/${file}`,
source: sourcePath,
in: 0,
out: duration,
duration
@ -709,6 +740,36 @@ export default {
showDeleteModal () {
this.$root.$emit('bv::show::modal', 'delete-modal')
},
showAddSource () {
this.$bvModal.show('add-source-modal')
},
handleSource (bvModalEvt) {
// Prevent modal from closing
bvModalEvt.preventDefault()
// Trigger submit handler
this.addSource()
},
addSource () {
const list = this.playlist
list.push(this.newSource)
this.$store.commit('playlist/UPDATE_PLAYLIST', this.$processPlaylist(this.startInSec, list))
this.newSource = {
begin: 0,
in: 0,
out: 0,
duration: 0,
category: '',
source: ''
}
this.$nextTick(() => {
this.$bvModal.hide('add-source-modal')
})
}
}
}

View File

@ -17,7 +17,7 @@ export const mutations = {
}
export const actions = {
async getTree ({ commit, dispatch, state, rootState }, { extensions, path }) {
async getTree ({ commit, dispatch, state, rootState }, { path }) {
const crumbs = []
let root = '/'
const channel = rootState.config.configGui[rootState.config.configID].id