You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
20 KiB
406 lines
20 KiB
<template>
|
|
<v-card class="fill-height align-center">
|
|
<v-dialog v-model="fileSystem">
|
|
<v-card>
|
|
<v-card-title>File System</v-card-title>
|
|
<v-card-text>
|
|
<v-file-input label="Upload" id="ufile"></v-file-input><v-btn @click="uploadfile">upload</v-btn>
|
|
<v-row>
|
|
<v-col cols="12" md="2" v-for="file in files" :key="file">
|
|
<v-card>
|
|
{{file}}
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-dialog>
|
|
<v-dialog v-model="createSceneModel" max-width="300px">
|
|
<v-card>
|
|
<v-card-title>New Scene</v-card-title>
|
|
<v-card-text>
|
|
<v-text-field v-model="sceneName" label="Scene Name"></v-text-field>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-btn text @click="createSceneModel=false">Cancel</v-btn>
|
|
<v-spacer></v-spacer>
|
|
<v-btn text color="primary" @click="createScene">Save</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
<v-dialog v-model="createSceneItem" max-width="300px">
|
|
<v-card>
|
|
<v-card-title>New Source</v-card-title>
|
|
<v-card-text>
|
|
<v-text-field v-model="sourceName" label="Source Name"></v-text-field>
|
|
<v-select v-model="sourceType" item-value="value" item-text="name" :items="[{name:'Image', value:'image_source'},{name: 'VLC Source', value: 'vlc_source'},{name: 'Media Source', value: 'ffmpeg_source'},{name: 'Text', value: 'text_ft2_source_v2'}]" label="Source Type"></v-select>
|
|
<v-text-field label="Image Path" v-model="sourceSettings.file" v-if="sourceType === 'image_source'"></v-text-field>
|
|
<v-text-field label="File Path" v-model="sourceSettings.localFile" v-if="sourceType === 'ffmpeg_source'"></v-text-field>
|
|
<div v-if="sourceType === 'vlc_source'">
|
|
<div v-for="(file, index) in sourceSettings.playlist" :key="index">
|
|
{{file}}
|
|
</div>
|
|
</div>
|
|
<v-text-field v-if="sourceType === 'vlc_source'" v-model="sourceSettings.file"></v-text-field><v-btn small text @click="addPlaylist(sourceSettings.file)">+</v-btn>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-btn text @click="createSceneItem=false">Cancel</v-btn>
|
|
<v-spacer></v-spacer>
|
|
<v-btn text color="primary" @click="createSource">Save</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
<v-card-text>
|
|
<v-row>
|
|
<v-col cols="12" md="9">
|
|
<v-card ref="mainvid" id="mainvid"
|
|
:style="`font-size:10px;height: ${vidheight}px;background: #ccc;position: relative`"
|
|
v-if="selecteScene">
|
|
<div :style="`border:1px solid #ccc;width:${source.cx*vidperm}px;z-index:${selecteScene.sources.length - index};height:${source.cy*vidperm}px;background-size: 100% 100%;top:${source.y*vidperm}px;left:${source.x*vidperm}px;position:absolute;min-width:50px;min-height:50px;background:url()`"
|
|
v-for="(source,index) in selecteScene.sources" :key="index">
|
|
<div style="top:0;left:0;position: absolute;color:#fff;text-shadow: #000 1px 1px">
|
|
{{source.name}} ({{source.id}})
|
|
</div>
|
|
<div style="top:0;right:0;position: absolute;color:#fff;text-shadow: #000 1px 1px">
|
|
{{source.type}} ({{source.source_cx}} x {{source.source_cy}} )
|
|
</div>
|
|
<img v-if="source.srcdata" width="100%" height="100%" :src="source.srcdata"/>
|
|
</div>
|
|
</v-card>
|
|
</v-col>
|
|
<v-col cols="12" md="3">
|
|
<v-card v-if="selectedItemProps">
|
|
<v-card-text>
|
|
<v-text-field label="Name" v-model="selectedItemProps.name"></v-text-field>
|
|
<v-text-field :label="`Width (Source: ${selectedItemProps.sourceWidth})`"
|
|
v-model="selectedItemProps.width" @keyup="selectedItemProps.height=selectedItemProps.width * (selectedItemProps.sourceHeight/selectedItemProps.sourceWidth)"></v-text-field>
|
|
<v-text-field :label="`Height (Source: ${selectedItemProps.sourceHeight})`"
|
|
v-model="selectedItemProps.height"></v-text-field>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn small @click="saveSelectedItem">Save</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
<br/>
|
|
<v-card v-if="selectedItemSourceProps">
|
|
<v-card-title>Source Options
|
|
<v-spacer></v-spacer>
|
|
<v-btn icon x-smal @click="panels.source = !panels.source">
|
|
<v-icon>mdi-menu</v-icon>
|
|
</v-btn>
|
|
</v-card-title>
|
|
<v-card-text v-show="panels.source">
|
|
<v-text-field v-if="selectedItemSourceProps.sourceType === 'xcomposite_input'"
|
|
label="Capture Window"
|
|
v-model="selectedItemSourceProps.sourceSettings.capture_window"></v-text-field>
|
|
<v-text-field v-if="selectedItemSourceProps.sourceType === 'image_source'" label="File"
|
|
v-model="selectedItemSourceProps.sourceSettings.file"></v-text-field>
|
|
<div v-if="selectedItemSourceProps.sourceType === 'vlc_source'">
|
|
<div v-for="(item, index) in selectedItemSourceProps.sourceSettings.playlist"
|
|
:key="index">
|
|
<v-row>
|
|
<v-col cols="12" md="10">{{item.value}}</v-col>
|
|
<v-col cols="12" md="2">
|
|
<v-btn icon small
|
|
@click="selectedItemSourceProps.sourceSettings.playlist.splice(index,1)">
|
|
<v-icon>mdi-delete</v-icon>
|
|
</v-btn>
|
|
</v-col>
|
|
</v-row>
|
|
</div>
|
|
<v-text-field v-if="selectedItemSourceProps.sourceType === 'vlc_source'" label="File"
|
|
v-model="selectedItemSourceProps.file"></v-text-field>
|
|
|
|
<v-spacer></v-spacer>
|
|
<v-btn small
|
|
@click="selectedItemSourceProps.sourceSettings.playlist.push({ hidden: false, selected: false, value: selectedItemSourceProps.file}); selectedItemSourceProps.file=''">
|
|
Add To Playlist
|
|
</v-btn>
|
|
|
|
</div>
|
|
</v-card-text>
|
|
<v-card-actions v-show="panels.source">
|
|
<v-spacer></v-spacer>
|
|
<v-btn small>Save</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
<br/>
|
|
<v-card v-if="selectedItemProps">
|
|
<v-card-title>Position
|
|
<v-spacer></v-spacer>
|
|
<v-btn icon x-smal @click="panels.position = !panels.position" l>
|
|
<v-icon>mdi-menu</v-icon>
|
|
</v-btn>
|
|
</v-card-title>
|
|
<v-card-text v-show="panels.position">
|
|
<v-text-field label="X" v-model="selectedItemProps.position.x"></v-text-field>
|
|
<v-text-field label="Y" v-model="selectedItemProps.position.y"></v-text-field>
|
|
<v-text-field label="Rotation" v-model="selectedItemProps.rotation"></v-text-field>
|
|
</v-card-text>
|
|
<v-card-actions v-show="panels.position">
|
|
<v-spacer></v-spacer>
|
|
<v-btn small>Save</v-btn>
|
|
</v-card-actions>
|
|
|
|
</v-card>
|
|
<br/>
|
|
|
|
<v-card v-if="selectedItemProps">
|
|
<v-card-title>Crop
|
|
<v-spacer></v-spacer>
|
|
<v-btn icon x-smal @click="panels.crop = !panels.crop">
|
|
<v-icon>mdi-menu</v-icon>
|
|
</v-btn>
|
|
</v-card-title>
|
|
<v-card-text v-show="panels.crop">
|
|
<v-text-field label="Top" v-model="selectedItemProps.crop.top"></v-text-field>
|
|
<v-text-field label="Left" v-model="selectedItemProps.crop.left"></v-text-field>
|
|
<v-text-field label="Bottom" v-model="selectedItemProps.crop.bottom"></v-text-field>
|
|
<v-text-field label="Right" v-model="selectedItemProps.crop.right"></v-text-field>
|
|
</v-card-text>
|
|
<v-card-actions v-show="panels.crop">
|
|
<v-spacer></v-spacer>
|
|
<v-btn small>Save</v-btn>
|
|
</v-card-actions>
|
|
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
<v-row>
|
|
<v-col cols="12" md="3">
|
|
<v-list>
|
|
<v-list-item-group v-model="selecteSceneName" color="primary">
|
|
|
|
<v-list-item v-for="(scene,index) in scenes" :key="index" @click="setSelect(scene)">
|
|
<v-list-item-content>
|
|
<v-list-item-title v-text="scene.name"></v-list-item-title>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-list-item-content>
|
|
<v-list-item-title @click="createSceneModel=true">New Scene</v-list-item-title>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
</v-list-item-group>
|
|
</v-list>
|
|
</v-col>
|
|
|
|
<v-col cols="12" md="3">
|
|
<v-list v-if="selecteScene">
|
|
<v-list-item v-for="(source,index) in selecteScene.sources" :key="index"
|
|
@click="setItemSelect(source)">
|
|
<v-list-item-content>
|
|
<v-list-item-title v-text="source.name"></v-list-item-title>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<v-list-item-content>
|
|
<v-list-item-title @click="createSceneItem=true">New Item</v-list-item-title>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
</v-list>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
</template>
|
|
|
|
<script>
|
|
const OBSWebSocket = require('obs-websocket-js');
|
|
|
|
export default {
|
|
name: "ObsVue",
|
|
props: {
|
|
server: String
|
|
},
|
|
data: () => ({
|
|
obs: null,
|
|
selectedItemProps: null,
|
|
selectedItemSourceProps: null,
|
|
createSceneModel: false,
|
|
createSceneItem: false,
|
|
sourceSettings: {
|
|
playlist: [],
|
|
file: '',
|
|
localFile: ''
|
|
},
|
|
files: [
|
|
'test.jpg',
|
|
'test.png'
|
|
],
|
|
sourceType: '',
|
|
sourceName: '',
|
|
scenes: [],
|
|
sources: [],
|
|
panels: {
|
|
source: false,
|
|
position: false,
|
|
crop: false
|
|
},
|
|
previewInterval: 0,
|
|
selecteScene: null,
|
|
sceneName: '',
|
|
vidheight: 0,
|
|
vidperm: 0,
|
|
selecteSceneName: null,
|
|
fileSystem: true
|
|
|
|
}),
|
|
mounted() {
|
|
this.obs = new OBSWebSocket()
|
|
this.getfiles()
|
|
this.obs.connect({address: 'ws://localhost:4444', password: 'neverdie'}).then(this.connected)
|
|
this.obs.on('error', err => {
|
|
console.error('socket error:', err);
|
|
});
|
|
},
|
|
methods: {
|
|
uploadfile() {
|
|
var file = document.getElementById('ufile')
|
|
console.log(file.files[0])
|
|
var filereader = new FileReader()
|
|
filereader.onloadend = () => {
|
|
this.$axios.post('http://localhost:5000/api/upload', {"file": filereader.result.split(';base64,')[1], 'file_name': file.files[0].name}).then(this.getfiles)
|
|
}
|
|
|
|
filereader.readAsDataURL(file.files[0]);
|
|
},
|
|
getfiles () {
|
|
this.$axios.get('http://localhost:5000/api/files').then(result => {
|
|
this.files = result.data
|
|
})
|
|
},
|
|
addPlaylist(item) {
|
|
this.sourceSettings.playlist.push({hidden: false, selected: false, value:item})
|
|
console.log(this.sourceSettings)
|
|
},
|
|
createScene() {
|
|
this.obs.send('CreateNewScene', {'sceneName': this.sceneName}).then(response => {
|
|
console.log(response)
|
|
this.sceneName = ''
|
|
this.createSceneModel = false
|
|
this.sceneList()
|
|
})
|
|
},
|
|
createSource() {
|
|
var sourceSetting;
|
|
if (this.sourceType === 'vlc_source') sourceSetting = {playlist: this.sourceSettings.playlist}
|
|
if (this.sourceType === 'ffmpeg_source') sourceSetting = {localfile: this.sourceSettings.localfile}
|
|
if (this.sourceType === 'image_source') sourceSetting = {file: this.sourceSettings.file}
|
|
console.log({sourceName: this.sourceName,sourceSettings: sourceSetting, sourceType: this.sourceType })
|
|
this.obs.send('CreateNewSource', {sourceName: this.sourceName,sourceSettings: sourceSetting, sourceType: this.sourceType }).then(response => {
|
|
console.log(response)
|
|
this.sceneName = ''
|
|
this.createSceneModel = false
|
|
this.obs.send('AddToScene', {sourceName:this.sourceName })
|
|
this.sceneList()
|
|
})
|
|
},
|
|
onAuth() {
|
|
console.log("auth")
|
|
},
|
|
newScene() {
|
|
|
|
},
|
|
setItemSelect(item) {
|
|
this.obs.send("GetSceneItemProperties", {
|
|
item: item.name,
|
|
"scene-name": this.selecteScene.name
|
|
}).then(data => {
|
|
console.log("GetSceneItemProperties")
|
|
console.log(data)
|
|
this.selectedItemProps = data
|
|
})
|
|
this.obs.send("GetSourceSettings", {sourceName: item.name}).then(data => {
|
|
console.log("GetSourceSettings")
|
|
console.log(data)
|
|
this.selectedItemSourceProps = data
|
|
})
|
|
},
|
|
setSelect(item) {
|
|
console.log(item)
|
|
this.selecteScene = item
|
|
this.selecteSceneName = item.name
|
|
this.obs.send('GetSourcesList').then(data => {
|
|
console.log('GetSourcesList')
|
|
console.log(data)
|
|
})
|
|
|
|
if (this.previewInterval > 0) clearInterval(this.previewInterval)
|
|
this.previewInterval = setInterval(() => {
|
|
this.selecteScene.sources.forEach(sitem => {
|
|
this.obs.send('TakeSourceScreenshot', {
|
|
sourceName: sitem.name,
|
|
embedPictureFormat: 'png'
|
|
}).then(data => {
|
|
sitem.srcdata = data.img
|
|
this.selecteScene.__ob__.dep.notify()
|
|
this.vidheight = document.getElementById('mainvid').offsetWidth / (16 / 9)
|
|
this.vidperm = this.vidheight / 1080.0
|
|
}).catch(err => {
|
|
console.log(err)
|
|
})
|
|
})
|
|
}, 500)
|
|
|
|
},
|
|
saveSelectedItem() {
|
|
console.log( {
|
|
'item': this.selectedItemProps.name,
|
|
position: {
|
|
x: parseInt(this.selectedItemProps.position.x),
|
|
y: parseInt(this.selectedItemProps.position.y),
|
|
},
|
|
crop: this.selectedItemProps.crop,
|
|
rotation: this.selectedItemProps.rotation,
|
|
height: this.selectedItemProps.height,
|
|
scale: {x:this.selectedItemProps.width/this.selectedItemProps.sourceWidth,y:this.selectedItemProps.height/this.selectedItemProps.sourceHeight}
|
|
})
|
|
this.obs.send('SetSceneItemProperties', {
|
|
item: this.selectedItemProps.name,
|
|
position: {
|
|
x: parseInt(this.selectedItemProps.position.x),
|
|
y: parseInt(this.selectedItemProps.position.y),
|
|
},
|
|
crop: {
|
|
top:parseInt(this.selectedItemProps.crop.top),
|
|
bottom:parseInt(this.selectedItemProps.crop.bottom),
|
|
left:parseInt(this.selectedItemProps.crop.left),
|
|
right:parseInt(this.selectedItemProps.crop.right),
|
|
},
|
|
rotation: this.selectedItemProps.rotation,
|
|
height: this.selectedItemProps.height,
|
|
scale: {x:this.selectedItemProps.width/this.selectedItemProps.sourceWidth,y:this.selectedItemProps.height/this.selectedItemProps.sourceHeight}
|
|
}).then(data => {
|
|
console.log(data)
|
|
this.sceneList()
|
|
})
|
|
},
|
|
sceneList() {
|
|
this.obs.send('GetSceneList').then(data => {
|
|
console.log("scene", data)
|
|
this.scenes = data.scenes
|
|
data.scenes.forEach(item => {
|
|
if (item.name === data["current-scene"]) {
|
|
this.setSelect(item)
|
|
}
|
|
})
|
|
})
|
|
},
|
|
connected() {
|
|
this.obs.send('GetVersion').then(data => {
|
|
this.obs.send('GetVersion').then(data2 => {
|
|
console.log(data)
|
|
console.log(data2)
|
|
})
|
|
})
|
|
this.sceneList()
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
|
|
</style>
|
|
|