添加标注打赏用户名读音

This commit is contained in:
John Smith 2020-11-28 21:53:49 +08:00
parent 8c7fc2780d
commit 4f0964c0b3
18 changed files with 41927 additions and 18 deletions

View File

@ -1,5 +1,5 @@
import axios from 'axios'
import {inflate} from 'pako'
import * as pako from 'pako'
import {getUuid4Hex} from '@/utils'
import * as avatar from './avatar'
@ -172,7 +172,7 @@ export default class ChatClientDirect {
case OP_SEND_MSG_REPLY: {
let body = new Uint8Array(data.buffer, offset + HEADER_SIZE, packLen - HEADER_SIZE)
if (ver == WS_BODY_PROTOCOL_VERSION_DEFLATE) {
body = inflate(body)
body = pako.inflate(body)
this.handlerMessage(body)
} else {
try {

View File

@ -18,7 +18,8 @@ export const DEFAULT_CONFIG = {
blockMedalLevel: 0,
relayMessagesByServer: false,
autoTranslate: false
autoTranslate: false,
giftUsernamePronunciation: ''
}
export function setLocalConfig (config) {

View File

@ -5,7 +5,7 @@
</template>
<script>
import {DEFAULT_AVATAR_URL} from '@/api/chat/avatar'
import * as avatar from '@/api/chat/avatar'
export default {
name: 'ImgShadow',
@ -26,8 +26,8 @@ export default {
},
methods: {
onLoadError() {
if (this.showImgUrl !== DEFAULT_AVATAR_URL) {
this.showImgUrl = DEFAULT_AVATAR_URL
if (this.showImgUrl !== avatar.DEFAULT_AVATAR_URL) {
this.showImgUrl = avatar.DEFAULT_AVATAR_URL
}
}
}

View File

@ -24,12 +24,12 @@
<template v-if="pinnedMessage">
<membership-item :key="pinnedMessage.id" v-if="pinnedMessage.type === MESSAGE_TYPE_MEMBER"
class="style-scope yt-live-chat-ticker-renderer"
:avatarUrl="pinnedMessage.avatarUrl" :authorName="pinnedMessage.authorName" :privilegeType="pinnedMessage.privilegeType"
:avatarUrl="pinnedMessage.avatarUrl" :authorName="getShowAuthorName(pinnedMessage)" :privilegeType="pinnedMessage.privilegeType"
:title="pinnedMessage.title" :time="pinnedMessage.time"
></membership-item>
<paid-message :key="pinnedMessage.id" v-else
class="style-scope yt-live-chat-ticker-renderer"
:price="pinnedMessage.price" :avatarUrl="pinnedMessage.avatarUrl" :authorName="pinnedMessage.authorName"
:price="pinnedMessage.price" :avatarUrl="pinnedMessage.avatarUrl" :authorName="getShowAuthorName(pinnedMessage)"
:time="pinnedMessage.time" :content="pinnedMessageShowContent"
></paid-message>
</template>
@ -98,6 +98,7 @@ export default {
window.clearInterval(this.updateTimerId)
},
methods: {
getShowAuthorName: constants.getShowAuthorName,
needToShow(message) {
let pinTime = this.getPinTime(message)
return (new Date() - message.addTime) / (60 * 1000) < pinTime

View File

@ -122,16 +122,23 @@ export function getPriceConfig (price) {
return PRICE_CONFIGS[PRICE_CONFIGS.length - 1]
}
export function getShowContent(message) {
export function getShowContent (message) {
if (message.translation) {
return `${message.content}${message.translation}`
}
return message.content
}
export function getGiftShowContent(message, showGiftName) {
export function getGiftShowContent (message, showGiftName) {
if (!showGiftName) {
return ''
}
return `Sent ${message.giftName}x${message.num}`
}
export function getShowAuthorName (message) {
if (message.authorNamePronunciation) {
return `${message.authorName}(${message.authorNamePronunciation})`
}
return message.authorName
}

View File

@ -18,17 +18,17 @@
></text-message>
<paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_GIFT"
class="style-scope yt-live-chat-item-list-renderer"
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="getShowAuthorName(message)"
:time="message.time" :content="getGiftShowContent(message)"
></paid-message>
<membership-item :key="message.id" v-else-if="message.type === MESSAGE_TYPE_MEMBER"
class="style-scope yt-live-chat-item-list-renderer"
:avatarUrl="message.avatarUrl" :authorName="message.authorName" :privilegeType="message.privilegeType"
:avatarUrl="message.avatarUrl" :authorName="getShowAuthorName(message)" :privilegeType="message.privilegeType"
:title="message.title" :time="message.time"
></membership-item>
<paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
class="style-scope yt-live-chat-item-list-renderer"
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="getShowAuthorName(message)"
:time="message.time" :content="getShowContent(message)"
></paid-message>
</template>
@ -132,6 +132,7 @@ export default {
return constants.getGiftShowContent(message, this.showGiftName)
},
getShowContent: constants.getShowContent,
getShowAuthorName: constants.getShowAuthorName,
addMessage(message) {
this.addMessages([message])

View File

@ -33,6 +33,10 @@ export default {
advanced: 'Advanced',
relayMessagesByServer: 'Relay messages by server',
autoTranslate: 'Auto translate messages to Japanese (requires relay messages by server)',
giftUsernamePronunciation: 'Pronunciation of gift username',
dontShow: 'None',
pinyin: 'Pinyin',
kana: 'Kana',
roomUrl: 'Room URL',
copy: 'Copy',

View File

@ -33,6 +33,10 @@ export default {
advanced: 'アドバンスド',
relayMessagesByServer: 'サーバを介してメッセージを転送する',
autoTranslate: 'コメントを日本語に翻訳する(サーバを介してメッセージを転送する必要)',
giftUsernamePronunciation: 'スーパーチャットのユーザー名の発音',
dontShow: '非表示',
pinyin: 'ピンイン',
kana: '仮名',
roomUrl: 'ルームのURL',
copy: 'コピー',

View File

@ -33,6 +33,10 @@ export default {
advanced: '高级',
relayMessagesByServer: '通过服务器转发消息',
autoTranslate: '自动翻译弹幕到日语(需要通过服务器转发消息)',
giftUsernamePronunciation: '标注打赏用户名读音',
dontShow: '不显示',
pinyin: '拼音',
kana: '日文假名',
roomUrl: '房间URL',
copy: '复制',

View File

@ -36,7 +36,7 @@ export default {
hideSidebar: true
}
},
created() {
mounted() {
window.addEventListener('resize', this.onResize)
this.onResize()
},

View File

@ -3,7 +3,8 @@ import VueRouter from 'vue-router'
import VueI18n from 'vue-i18n'
import {
Aside, Autocomplete, Badge, Button, Col, ColorPicker, Container, Divider, Form, FormItem, Image,
Input, Main, Menu, MenuItem, Message, Row, Scrollbar, Slider, Submenu, Switch, TabPane, Tabs, Tooltip
Input, Main, Menu, MenuItem, Message, Radio, RadioGroup, Row, Scrollbar, Slider, Submenu, Switch,
TabPane, Tabs, Tooltip
} from 'element-ui'
import axios from 'axios'
@ -42,6 +43,8 @@ Vue.use(Input)
Vue.use(Main)
Vue.use(Menu)
Vue.use(MenuItem)
Vue.use(Radio)
Vue.use(RadioGroup)
Vue.use(Row)
Vue.use(Scrollbar)
Vue.use(Slider)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
export const DICT_PINYIN = 'pinyin'
export const DICT_KANA = 'kana'
export class PronunciationConverter {
constructor () {
this.pronunciationMap = new Map()
}
async loadDict (dictName) {
let promise
switch (dictName) {
case DICT_PINYIN:
promise = import('./dictPinyin')
break
case DICT_KANA:
promise = import('./dictKana')
break
default:
return
}
let dictTxt = (await promise).default
let pronunciationMap = new Map()
for (let item of dictTxt.split('\n')) {
if (item.length === 0) {
continue
}
pronunciationMap.set(item.substring(0, 1), item.substring(1))
}
this.pronunciationMap = pronunciationMap
}
getPronunciation (text) {
let res = []
let lastHasPronunciation = null
for (let char of text) {
let pronunciation = this.pronunciationMap.get(char)
if (pronunciation === undefined) {
if (lastHasPronunciation !== null && lastHasPronunciation) {
res.push(' ')
}
lastHasPronunciation = false
res.push(char)
} else {
if (lastHasPronunciation !== null) {
res.push(' ')
}
lastHasPronunciation = true
res.push(pronunciation)
}
}
return res.join('')
}
}

View File

@ -64,6 +64,13 @@
<el-form-item :label="$t('home.autoTranslate')">
<el-switch v-model="form.autoTranslate" :disabled="!serverConfig.enableTranslate || !form.relayMessagesByServer"></el-switch>
</el-form-item>
<el-form-item :label="$t('home.giftUsernamePronunciation')">
<el-radio-group v-model="form.giftUsernamePronunciation">
<el-radio label="">{{$t('home.dontShow')}}</el-radio>
<el-radio label="pinyin">{{$t('home.pinyin')}}</el-radio>
<el-radio label="kana">{{$t('home.kana')}}</el-radio>
</el-radio-group>
</el-form-item>
</el-tab-pane>
</el-tabs>

View File

@ -4,6 +4,7 @@
<script>
import {mergeConfig, toBool, toInt} from '@/utils'
import * as pronunciation from '@/utils/pronunciation'
import * as chatConfig from '@/api/chatConfig'
import ChatClientDirect from '@/api/chat/ChatClientDirect'
import ChatClientRelay from '@/api/chat/ChatClientRelay'
@ -18,7 +19,8 @@ export default {
data() {
return {
config: {...chatConfig.DEFAULT_CONFIG},
chatClient: null
chatClient: null,
pronunciationConverter: null
}
},
computed: {
@ -29,9 +31,14 @@ export default {
return this.config.blockUsers.split('\n').filter(val => val)
}
},
created() {
mounted() {
this.initConfig()
this.initChatClient()
if (this.config.giftUsernamePronunciation !== '') {
this.pronunciationConverter = new pronunciation.PronunciationConverter()
this.pronunciationConverter.loadDict(this.config.giftUsernamePronunciation)
}
//
this.$message({
message: 'Loaded',
@ -122,6 +129,7 @@ export default {
avatarUrl: data.avatarUrl,
time: new Date(data.timestamp * 1000),
authorName: data.authorName,
authorNamePronunciation: this.getPronunciation(data.authorName),
price: price,
giftName: data.giftName,
num: data.num
@ -138,6 +146,7 @@ export default {
avatarUrl: data.avatarUrl,
time: new Date(data.timestamp * 1000),
authorName: data.authorName,
authorNamePronunciation: this.getPronunciation(data.authorName),
privilegeType: data.privilegeType,
title: 'New member'
}
@ -155,9 +164,11 @@ export default {
type: constants.MESSAGE_TYPE_SUPER_CHAT,
avatarUrl: data.avatarUrl,
authorName: data.authorName,
authorNamePronunciation: this.getPronunciation(data.authorName),
price: data.price,
time: new Date(data.timestamp * 1000),
content: data.content.trim()
content: data.content.trim(),
translation: data.translation
}
this.$refs.renderer.addMessage(message)
},
@ -214,6 +225,12 @@ export default {
return false
}
return this.$refs.renderer.mergeSimilarGift(authorName, price, giftName, num)
},
getPronunciation(text) {
if (this.pronunciationConverter === null) {
return ''
}
return this.pronunciationConverter.getPronunciation(text)
}
}
}

View File

@ -24,3 +24,5 @@ async def _do_check_update():
print('---------------------------------------------')
except aiohttp.ClientConnectionError:
print('Failed to check update: connection failed')
except asyncio.TimeoutError:
print('Failed to check update: timeout')