前端配置放在URL参数

This commit is contained in:
John Smith 2020-02-01 21:27:50 +08:00
parent 8d4e8e6f35
commit cae06858fc
17 changed files with 77 additions and 192 deletions

View File

@ -23,7 +23,7 @@
```
3. 用浏览器打开[http://localhost:12450](http://localhost:12450)输入房间ID保存配置复制房间URL
4. 用样式生成器生成样式复制CSS
5. 在OBS中添加浏览器源输入URL和自定义CSS或者可以在首页的样式设置里输入CSS
5. 在OBS中添加浏览器源输入URL和自定义CSS
### 公共服务器
请优先在本地使用,使用公共服务器会有更大的弹幕延迟,而且服务器故障时可能出现直播事故

View File

@ -1,5 +1,3 @@
import axios from 'axios'
import {mergeConfig} from '@/utils'
export const DEFAULT_CONFIG = {
@ -15,9 +13,7 @@ export const DEFAULT_CONFIG = {
blockNotMobileVerified: true,
blockKeywords: '',
blockUsers: '',
blockMedalLevel: 0,
css: ''
blockMedalLevel: 0
}
export function setLocalConfig (config) {
@ -31,27 +27,3 @@ export function getLocalConfig () {
}
return mergeConfig(JSON.parse(window.localStorage.config), DEFAULT_CONFIG)
}
export async function createRemoteConfig (config) {
config = mergeConfig(config, DEFAULT_CONFIG)
return (await axios.post('/config', config)).data
}
export async function setRemoteConfig (id, config) {
config = mergeConfig(config, DEFAULT_CONFIG)
return (await axios.put(`/config/${id}`, config)).data
}
export async function getRemoteConfig (id) {
let config = (await axios.get(`/config/${id}`)).data
return mergeConfig(config, DEFAULT_CONFIG)
}
export default {
DEFAULT_CONFIG,
setLocalConfig,
getLocalConfig,
createRemoteConfig,
setRemoteConfig,
getRemoteConfig
}

View File

@ -21,7 +21,7 @@
<script>
import ImgShadow from './ImgShadow.vue'
import utils from '@/utils'
import * as utils from '@/utils'
export default {
name: 'LegacyPaidMessage',

View File

@ -34,7 +34,7 @@
<script>
import ImgShadow from './ImgShadow.vue'
import * as constants from './constants'
import utils from '@/utils'
import * as utils from '@/utils'
export default {
name: 'PaidMessage',

View File

@ -31,7 +31,7 @@
import ImgShadow from './ImgShadow.vue'
import AuthorBadge from './AuthorBadge.vue'
import * as constants from './constants'
import utils from '@/utils'
import * as utils from '@/utils'
// HSL
const REPEATED_MARK_COLOR_START = [210, 100.0, 62.5]

View File

@ -33,7 +33,7 @@
</template>
<script>
import config from '@/api/config'
import * as config from '@/api/config'
import Ticker from './Ticker.vue'
import TextMessage from './TextMessage.vue'
import LegacyPaidMessage from './LegacyPaidMessage.vue'

View File

@ -28,17 +28,12 @@ export default {
blockUsers: 'Block users',
blockMedalLevel: 'Block medal level lower than',
style: 'Style',
roomUrl: 'Room URL',
copy: 'Copy',
saveConfig: 'Save config',
enterRoom: 'Enter room',
exportConfig: 'Export config',
importConfig: 'Import config',
failedToSave: 'Failed to save: ',
successfullySaved: 'Successfully saved',
failedToParseConfig: 'Failed to parse config: '
},
stylegen: {

View File

@ -28,17 +28,12 @@ export default {
blockUsers: 'ブロックユーザー',
blockMedalLevel: 'ブロック勲章等級がx未満',
style: 'スタイル',
roomUrl: 'ルームのURL',
copy: 'コピー',
saveConfig: 'コンフィグを保存する',
enterRoom: 'ルームに入る',
exportConfig: 'コンフィグの導出',
importConfig: 'コンフィグの導入',
failedToSave: '保存に失敗しました:',
successfullySaved: '保存に成功しました',
failedToParseConfig: 'コンフィグ解析に失敗しました'
},
stylegen: {

View File

@ -28,17 +28,12 @@ export default {
blockUsers: '屏蔽用户',
blockMedalLevel: '屏蔽当前直播间勋章等级低于',
style: '样式',
roomUrl: '房间URL',
copy: '复制',
saveConfig: '保存配置',
enterRoom: '进入房间',
exportConfig: '导出配置',
importConfig: '导入配置',
failedToSave: '保存失败:',
successfullySaved: '保存成功',
failedToParseConfig: '配置解析失败:'
},
stylegen: {

View File

@ -6,6 +6,21 @@ export function mergeConfig (config, defaultConfig) {
return res
}
export function toBool (val) {
if (typeof val === 'string') {
return val !== 'false' && val !== ''
}
return !!val
}
export function toInt (val, _default) {
let res = parseInt(val)
if (isNaN(res)) {
res = _default
}
return res
}
export function formatCurrency (price) {
return new Intl.NumberFormat('zh-CN', {
minimumFractionDigits: price < 100 ? 2 : 0
@ -17,9 +32,3 @@ export function getTimeTextMinSec (date) {
let sec = ('00' + date.getSeconds()).slice(-2)
return `${min}:${sec}`
}
export default {
mergeConfig,
formatCurrency,
getTimeTextMinSec
}

View File

@ -50,21 +50,14 @@
<el-slider v-model="form.blockMedalLevel" show-input :min="0" :max="20"></el-slider>
</el-form-item>
</el-tab-pane>
<el-tab-pane :label="$t('home.style')">
<el-form-item label="CSS">
<el-input v-model="form.css" type="textarea" :rows="20"></el-input>
</el-form-item>
</el-tab-pane>
</el-tabs>
<el-divider></el-divider>
<el-form-item :label="$t('home.roomUrl')" v-show="roomUrl">
<el-input ref="roomUrlInput" readonly :value="roomUrl" style="width: calc(100% - 6em); margin-right: 1em;"></el-input>
<el-form-item :label="$t('home.roomUrl')">
<el-input ref="roomUrlInput" readonly :value="roomUrl" style="width: calc(100% - 8em); margin-right: 1em;"></el-input>
<el-button type="primary" @click="copyUrl">{{$t('home.copy')}}</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveConfig">{{$t('home.saveConfig')}}</el-button>
<el-button type="primary" :disabled="!roomUrl" @click="enterRoom">{{$t('home.enterRoom')}}</el-button>
<el-button type="primary" @click="exportConfig">{{$t('home.exportConfig')}}</el-button>
<el-button type="primary" @click="importConfig">{{$t('home.importConfig')}}</el-button>
@ -73,10 +66,11 @@
</template>
<script>
import _ from 'lodash'
import download from 'downloadjs'
import {mergeConfig} from '@/utils'
import config from '@/api/config'
import * as config from '@/api/config'
export default {
name: 'Home',
@ -85,40 +79,27 @@ export default {
form: {
roomId: parseInt(window.localStorage.roomId || '1'),
...config.getLocalConfig()
},
roomUrl: ''
}
}
},
computed: {
roomUrl() {
if (this.form.roomId === '') {
return ''
}
let query = {...this.form}
delete query.roomId
let resolved = this.$router.resolve({name: 'room', params: {roomId: this.form.roomId}, query})
return `${window.location.protocol}//${window.location.host}${resolved.href}`
}
},
watch: {
roomUrl: _.debounce(function() {
window.localStorage.roomId = this.form.roomId
config.setLocalConfig(this.form)
}, 500)
},
methods: {
saveConfig() {
this.$refs.form.validate(async valid => {
if (!valid) {
return
}
window.localStorage.roomId = this.form.roomId
config.setLocalConfig(this.form)
try {
if (window.localStorage.configId) {
try {
await config.setRemoteConfig(window.localStorage.configId, this.form)
} catch (e) { // 404
window.localStorage.configId = (await config.createRemoteConfig(this.form)).id
}
} else {
window.localStorage.configId = (await config.createRemoteConfig(this.form)).id
}
} catch (e) {
this.$message.error(this.$t('home.failedToSave') + e)
return
}
this.$message({message: this.$t('home.successfullySaved'), type: 'success'})
let resolved = this.$router.resolve({name: 'room', params: {roomId: this.form.roomId},
query: {config_id: window.localStorage.configId}})
this.roomUrl = `${window.location.protocol}//${window.location.host}${resolved.href}`
})
},
enterRoom() {
window.open(this.roomUrl, `room ${this.form.roomId}`, 'menubar=0,location=0,scrollbars=0,toolbar=0,width=600,height=600')
},

View File

@ -1,9 +1,10 @@
<template>
<chat-renderer ref="renderer" :css="config.css" :maxNumber="config.maxNumber"></chat-renderer>
<chat-renderer ref="renderer" :maxNumber="config.maxNumber"></chat-renderer>
</template>
<script>
import config from '@/api/config'
import {mergeConfig, toBool, toInt} from '@/utils'
import * as config from '@/api/config'
import ChatRenderer from '@/components/ChatRenderer'
import * as constants from '@/components/ChatRenderer/constants'
@ -42,21 +43,35 @@ export default {
},
created() {
this.wsConnect()
if (this.$route.query.config_id) {
this.updateConfig(this.$route.query.config_id)
}
this.updateConfig()
},
beforeDestroy() {
this.isDestroying = true
this.websocket.close()
},
methods: {
async updateConfig(configId) {
try {
this.config = await config.getRemoteConfig(configId)
} catch (e) {
this.$message.error('获取配置失败:' + e)
updateConfig() {
let cfg = {}
// 使
for (let i in this.$route.query) {
if (this.$route.query[i] !== '') {
cfg[i] = this.$route.query[i]
}
}
cfg = mergeConfig(cfg, config.DEFAULT_CONFIG)
cfg.minGiftPrice = toInt(cfg.minGiftPrice, config.DEFAULT_CONFIG.minGiftPrice)
cfg.mergeSimilarDanmaku = toBool(cfg.mergeSimilarDanmaku)
cfg.showDanmaku = toBool(cfg.showDanmaku)
cfg.showGift = toBool(cfg.showGift)
cfg.maxNumber = toInt(cfg.maxNumber, config.DEFAULT_CONFIG.maxNumber)
cfg.blockGiftDanmaku = toBool(cfg.blockGiftDanmaku)
cfg.blockLevel = toInt(cfg.blockLevel, config.DEFAULT_CONFIG.blockLevel)
cfg.blockNewbie = toBool(cfg.blockNewbie)
cfg.blockNotMobileVerified = toBool(cfg.blockNotMobileVerified)
cfg.blockMedalLevel = toInt(cfg.blockMedalLevel, config.DEFAULT_CONFIG.blockMedalLevel)
this.config = cfg
},
wsConnect() {
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
@ -138,7 +153,7 @@ export default {
break
}
case COMMAND_ADD_MEMBER:
if (!this.config.showGift || !this.filterSuperChatMessage(data)) {
if (!this.config.showGift || !this.filterNewMemberMessage(data)) {
break
}
message = {
@ -152,7 +167,7 @@ export default {
}
break
case COMMAND_ADD_SUPER_CHAT:
if (!this.config.showGift) {
if (!this.config.showGift || !this.filterSuperChatMessage(data)) {
break
}
if (data.price < this.config.minGiftPrice) { //
@ -199,6 +214,9 @@ export default {
return false
}
}
return this.filterNewMemberMessage(data)
},
filterNewMemberMessage(data) {
for (let user of this.blockUsers) {
if (data.authorName === user) {
return false

File diff suppressed because one or more lines are too long

View File

@ -200,8 +200,8 @@
<script>
import _ from 'lodash'
import stylegen from './stylegen'
import fonts from './fonts'
import * as stylegen from './stylegen'
import * as fonts from './fonts'
import ChatRenderer from '@/components/ChatRenderer'
import * as constants from '@/components/ChatRenderer/constants'

View File

@ -1,5 +1,5 @@
import {mergeConfig} from '@/utils'
import fonts from './fonts'
import * as fonts from './fonts'
export const DEFAULT_CONFIG = {
showOutlines: true,
@ -404,10 +404,3 @@ yt-live-chat-legacy-paid-message-renderer {
animation-fill-mode: both;
}`
}
export default {
DEFAULT_CONFIG,
setLocalConfig,
getLocalConfig,
getStyle
}

View File

@ -11,7 +11,6 @@ import tornado.web
import update
import views.chat
import views.config
import views.main
logger = logging.getLogger(__name__)
@ -38,8 +37,6 @@ def main():
app = tornado.web.Application(
[
(r'/chat', views.chat.ChatHandler),
(r'/config', views.config.ConfigsHandler),
(r'/config/(.+)', views.config.ConfigHandler),
(r'/((css|fonts|img|js|static)/.*)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
(r'/(favicon\.ico)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),

View File

@ -1,65 +0,0 @@
# -*- coding: utf-8 -*-
import json
import uuid
import views.base
from typing import *
MAX_CONFIG_SIZE = 100 * 1024
configs: Dict[str, dict] = {}
# noinspection PyAbstractClass
class ConfigsHandler(views.base.ApiHandler):
async def post(self):
if not isinstance(self.json_args, dict):
self.set_status(400)
return
config = self.json_args
config_id = str(uuid.uuid4())
config['id'] = config_id
config_str = json.dumps(config)
if len(config_str) > MAX_CONFIG_SIZE:
self.set_status(413)
return
configs[config_id] = config
self.write(config_str)
self.set_status(201)
self.set_header('Content-Type', 'application/json; charset=UTF-8')
if len(configs) > 10000:
for _, key in zip(range(100), configs):
del configs[key]
# noinspection PyAbstractClass
class ConfigHandler(views.base.ApiHandler):
async def put(self, config_id):
if config_id not in configs:
self.set_status(404)
return
if not isinstance(self.json_args, dict):
self.set_status(400)
return
config = self.json_args
config['id'] = config_id
config_str = json.dumps(config)
if len(config_str) > MAX_CONFIG_SIZE:
self.set_status(413)
return
configs[config_id] = config
self.write(config_str)
self.set_header('Content-Type', 'application/json; charset=UTF-8')
async def get(self, config_id):
config = configs.get(config_id, None)
if config is None:
self.set_status(404)
return
self.write(config)