添加标注打赏用户名读音

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

View File

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

View File

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

View File

@@ -24,12 +24,12 @@
<template v-if="pinnedMessage"> <template v-if="pinnedMessage">
<membership-item :key="pinnedMessage.id" v-if="pinnedMessage.type === MESSAGE_TYPE_MEMBER" <membership-item :key="pinnedMessage.id" v-if="pinnedMessage.type === MESSAGE_TYPE_MEMBER"
class="style-scope yt-live-chat-ticker-renderer" 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" :title="pinnedMessage.title" :time="pinnedMessage.time"
></membership-item> ></membership-item>
<paid-message :key="pinnedMessage.id" v-else <paid-message :key="pinnedMessage.id" v-else
class="style-scope yt-live-chat-ticker-renderer" 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" :time="pinnedMessage.time" :content="pinnedMessageShowContent"
></paid-message> ></paid-message>
</template> </template>
@@ -98,6 +98,7 @@ export default {
window.clearInterval(this.updateTimerId) window.clearInterval(this.updateTimerId)
}, },
methods: { methods: {
getShowAuthorName: constants.getShowAuthorName,
needToShow(message) { needToShow(message) {
let pinTime = this.getPinTime(message) let pinTime = this.getPinTime(message)
return (new Date() - message.addTime) / (60 * 1000) < pinTime 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] return PRICE_CONFIGS[PRICE_CONFIGS.length - 1]
} }
export function getShowContent(message) { export function getShowContent (message) {
if (message.translation) { if (message.translation) {
return `${message.content}${message.translation}` return `${message.content}${message.translation}`
} }
return message.content return message.content
} }
export function getGiftShowContent(message, showGiftName) { export function getGiftShowContent (message, showGiftName) {
if (!showGiftName) { if (!showGiftName) {
return '' return ''
} }
return `Sent ${message.giftName}x${message.num}` 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> ></text-message>
<paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_GIFT" <paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_GIFT"
class="style-scope yt-live-chat-item-list-renderer" 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)" :time="message.time" :content="getGiftShowContent(message)"
></paid-message> ></paid-message>
<membership-item :key="message.id" v-else-if="message.type === MESSAGE_TYPE_MEMBER" <membership-item :key="message.id" v-else-if="message.type === MESSAGE_TYPE_MEMBER"
class="style-scope yt-live-chat-item-list-renderer" 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" :title="message.title" :time="message.time"
></membership-item> ></membership-item>
<paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT" <paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
class="style-scope yt-live-chat-item-list-renderer" 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)" :time="message.time" :content="getShowContent(message)"
></paid-message> ></paid-message>
</template> </template>
@@ -132,6 +132,7 @@ export default {
return constants.getGiftShowContent(message, this.showGiftName) return constants.getGiftShowContent(message, this.showGiftName)
}, },
getShowContent: constants.getShowContent, getShowContent: constants.getShowContent,
getShowAuthorName: constants.getShowAuthorName,
addMessage(message) { addMessage(message) {
this.addMessages([message]) this.addMessages([message])

View File

@@ -33,6 +33,10 @@ export default {
advanced: 'Advanced', advanced: 'Advanced',
relayMessagesByServer: 'Relay messages by server', relayMessagesByServer: 'Relay messages by server',
autoTranslate: 'Auto translate messages to Japanese (requires 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', roomUrl: 'Room URL',
copy: 'Copy', copy: 'Copy',

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,8 @@ import VueRouter from 'vue-router'
import VueI18n from 'vue-i18n' import VueI18n from 'vue-i18n'
import { import {
Aside, Autocomplete, Badge, Button, Col, ColorPicker, Container, Divider, Form, FormItem, Image, 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' } from 'element-ui'
import axios from 'axios' import axios from 'axios'
@@ -42,6 +43,8 @@ Vue.use(Input)
Vue.use(Main) Vue.use(Main)
Vue.use(Menu) Vue.use(Menu)
Vue.use(MenuItem) Vue.use(MenuItem)
Vue.use(Radio)
Vue.use(RadioGroup)
Vue.use(Row) Vue.use(Row)
Vue.use(Scrollbar) Vue.use(Scrollbar)
Vue.use(Slider) 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-form-item :label="$t('home.autoTranslate')">
<el-switch v-model="form.autoTranslate" :disabled="!serverConfig.enableTranslate || !form.relayMessagesByServer"></el-switch> <el-switch v-model="form.autoTranslate" :disabled="!serverConfig.enableTranslate || !form.relayMessagesByServer"></el-switch>
</el-form-item> </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-tab-pane>
</el-tabs> </el-tabs>

View File

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