From 49d58a9a6067681db700d763db85bb5697883680 Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sun, 19 Jul 2020 16:31:49 +0800 Subject: [PATCH 01/18] =?UTF-8?q?=E5=88=A0=E9=99=A4BiliSC=E9=93=BE?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/lang/en.js | 1 - frontend/src/lang/ja.js | 1 - frontend/src/lang/zh.js | 1 - frontend/src/layout/Sidebar.vue | 5 ----- 4 files changed, 8 deletions(-) diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 245dd609..5968f9da 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -5,7 +5,6 @@ export default { help: 'Help', projectAddress: 'Project address', giftRecordOfficial: 'Official Super Chat record', - giftRecord: 'Super Chat record' }, home: { roomIdEmpty: "Room ID can't be empty", diff --git a/frontend/src/lang/ja.js b/frontend/src/lang/ja.js index 7d017a0f..8ce58f57 100644 --- a/frontend/src/lang/ja.js +++ b/frontend/src/lang/ja.js @@ -5,7 +5,6 @@ export default { help: 'ヘルプ', projectAddress: 'プロジェクトアドレス', giftRecordOfficial: '公式スーパーチャット記録', - giftRecord: 'スーパーチャット記録' }, home: { roomIdEmpty: 'ルームのIDを空白にすることはできません', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index 6bc6b4ca..f9b86bad 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -5,7 +5,6 @@ export default { help: '帮助', projectAddress: '项目地址', giftRecordOfficial: '官方打赏记录', - giftRecord: '打赏记录' }, home: { roomIdEmpty: '房间ID不能为空', diff --git a/frontend/src/layout/Sidebar.vue b/frontend/src/layout/Sidebar.vue index 2c4a45df..d87021c2 100644 --- a/frontend/src/layout/Sidebar.vue +++ b/frontend/src/layout/Sidebar.vue @@ -26,11 +26,6 @@ <i class="el-icon-share"></i>{{$t('sidebar.giftRecordOfficial')}} </el-menu-item> </a> - <a href="https://bilisc.com/" target="_blank"> - <el-menu-item> - <i class="el-icon-share"></i>{{$t('sidebar.giftRecord')}} - </el-menu-item> - </a> <el-submenu index="null"> <template slot="title"> <i class="el-icon-chat-line-square"></i>Language From a89b64b45416f08b45d60d947b3d45257a1910df Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sun, 19 Jul 2020 21:33:26 +0800 Subject: [PATCH 02/18] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/chat.py | 8 +- .../ChatRenderer/LegacyPaidMessage.vue | 306 ------------- .../ChatRenderer/MembershipItem.vue | 421 ++++++++++++++++++ .../components/ChatRenderer/PaidMessage.vue | 2 +- .../components/ChatRenderer/TextMessage.vue | 2 +- .../src/components/ChatRenderer/Ticker.vue | 12 +- .../src/components/ChatRenderer/index.vue | 294 +++++++++++- frontend/src/lang/en.js | 1 + frontend/src/lang/ja.js | 1 + frontend/src/lang/zh.js | 1 + frontend/src/utils.js | 6 +- frontend/src/views/Room.vue | 4 +- frontend/src/views/StyleGenerator/index.vue | 18 +- frontend/src/views/StyleGenerator/stylegen.js | 35 +- 14 files changed, 757 insertions(+), 354 deletions(-) delete mode 100644 frontend/src/components/ChatRenderer/LegacyPaidMessage.vue create mode 100644 frontend/src/components/ChatRenderer/MembershipItem.vue diff --git a/api/chat.py b/api/chat.py index a87ceb7d..6dd152af 100644 --- a/api/chat.py +++ b/api/chat.py @@ -71,7 +71,7 @@ def __parse_gift(self, command): def __parse_buy_guard(self, command): data = command['data'] return self._on_buy_guard(blivedm.GuardBuyMessage( - data['uid'], data['username'], None, None, None, + data['uid'], data['username'], data['guard_level'], None, None, None, None, data['start_time'], None )) @@ -206,7 +206,8 @@ async def __on_buy_guard(self, message: blivedm.GuardBuyMessage): 'id': id_, 'avatarUrl': await models.avatar.get_avatar_url(message.uid), 'timestamp': message.start_time, - 'authorName': message.username + 'authorName': message.username, + 'privilegeType': message.guard_level }) async def _on_super_chat(self, message: blivedm.SuperChatMessage): @@ -427,7 +428,8 @@ async def send_test_message(self): ] member_data = { **base_data, - 'id': uuid.uuid4().hex + 'id': uuid.uuid4().hex, + 'privilegeType': 3 } gift_data = { **base_data, diff --git a/frontend/src/components/ChatRenderer/LegacyPaidMessage.vue b/frontend/src/components/ChatRenderer/LegacyPaidMessage.vue deleted file mode 100644 index ebd7cc59..00000000 --- a/frontend/src/components/ChatRenderer/LegacyPaidMessage.vue +++ /dev/null @@ -1,306 +0,0 @@ -<template> - <yt-live-chat-legacy-paid-message-renderer class="style-scope yt-live-chat-item-list-renderer"> - <div id="card" class="style-scope yt-live-chat-legacy-paid-message-renderer"> - <img-shadow id="author-photo" height="40" width="40" class="style-scope yt-live-chat-legacy-paid-message-renderer" - :imgUrl="avatarUrl" - ></img-shadow> - <div id="content" class="style-scope yt-live-chat-legacy-paid-message-renderer"> - <div id="content-primary-column" class="style-scope yt-live-chat-legacy-paid-message-renderer"> - <div id="author-name" class="style-scope yt-live-chat-legacy-paid-message-renderer">{{authorName}}</div> - <div id="event-text" class="style-scope yt-live-chat-legacy-paid-message-renderer">{{title}}</div> - <div id="detail-text" class="style-scope yt-live-chat-legacy-paid-message-renderer">{{content}}</div> - </div> - <div id="timestamp" class="style-scope yt-live-chat-legacy-paid-message-renderer">{{timeText}}</div> - </div> - </div> - <div id="inline-action-button-container" class="style-scope yt-live-chat-legacy-paid-message-renderer" aria-hidden="true"> - <div id="inline-action-buttons" class="style-scope yt-live-chat-legacy-paid-message-renderer"></div> - </div> - </yt-live-chat-legacy-paid-message-renderer> -</template> - -<script> -import ImgShadow from './ImgShadow.vue' -import * as utils from '@/utils' - -export default { - name: 'LegacyPaidMessage', - components: { - ImgShadow - }, - props: { - avatarUrl: String, - authorName: String, - title: String, - content: String, - time: Date - }, - computed: { - timeText() { - return utils.getTimeTextMinSec(this.time) - } - } -} -</script> - -<!-- yt-live-chat-legacy-paid-message-renderer --> -<style> -canvas.yt-live-chat-legacy-paid-message-renderer, caption.yt-live-chat-legacy-paid-message-renderer, center.yt-live-chat-legacy-paid-message-renderer, cite.yt-live-chat-legacy-paid-message-renderer, code.yt-live-chat-legacy-paid-message-renderer, dd.yt-live-chat-legacy-paid-message-renderer, del.yt-live-chat-legacy-paid-message-renderer, dfn.yt-live-chat-legacy-paid-message-renderer, div.yt-live-chat-legacy-paid-message-renderer, dl.yt-live-chat-legacy-paid-message-renderer, dt.yt-live-chat-legacy-paid-message-renderer, em.yt-live-chat-legacy-paid-message-renderer, embed.yt-live-chat-legacy-paid-message-renderer, fieldset.yt-live-chat-legacy-paid-message-renderer, font.yt-live-chat-legacy-paid-message-renderer, form.yt-live-chat-legacy-paid-message-renderer, h1.yt-live-chat-legacy-paid-message-renderer, h2.yt-live-chat-legacy-paid-message-renderer, h3.yt-live-chat-legacy-paid-message-renderer, h4.yt-live-chat-legacy-paid-message-renderer, h5.yt-live-chat-legacy-paid-message-renderer, h6.yt-live-chat-legacy-paid-message-renderer, hr.yt-live-chat-legacy-paid-message-renderer, i.yt-live-chat-legacy-paid-message-renderer, iframe.yt-live-chat-legacy-paid-message-renderer, img.yt-live-chat-legacy-paid-message-renderer, ins.yt-live-chat-legacy-paid-message-renderer, kbd.yt-live-chat-legacy-paid-message-renderer, label.yt-live-chat-legacy-paid-message-renderer, legend.yt-live-chat-legacy-paid-message-renderer, li.yt-live-chat-legacy-paid-message-renderer, menu.yt-live-chat-legacy-paid-message-renderer, object.yt-live-chat-legacy-paid-message-renderer, ol.yt-live-chat-legacy-paid-message-renderer, p.yt-live-chat-legacy-paid-message-renderer, pre.yt-live-chat-legacy-paid-message-renderer, q.yt-live-chat-legacy-paid-message-renderer, s.yt-live-chat-legacy-paid-message-renderer, samp.yt-live-chat-legacy-paid-message-renderer, small.yt-live-chat-legacy-paid-message-renderer, span.yt-live-chat-legacy-paid-message-renderer, strike.yt-live-chat-legacy-paid-message-renderer, strong.yt-live-chat-legacy-paid-message-renderer, sub.yt-live-chat-legacy-paid-message-renderer, sup.yt-live-chat-legacy-paid-message-renderer, table.yt-live-chat-legacy-paid-message-renderer, tbody.yt-live-chat-legacy-paid-message-renderer, td.yt-live-chat-legacy-paid-message-renderer, tfoot.yt-live-chat-legacy-paid-message-renderer, th.yt-live-chat-legacy-paid-message-renderer, thead.yt-live-chat-legacy-paid-message-renderer, tr.yt-live-chat-legacy-paid-message-renderer, tt.yt-live-chat-legacy-paid-message-renderer, u.yt-live-chat-legacy-paid-message-renderer, ul.yt-live-chat-legacy-paid-message-renderer, var.yt-live-chat-legacy-paid-message-renderer { - margin: 0; - padding: 0; - border: 0; - background: transparent; -} - -.yt-live-chat-legacy-paid-message-renderer[hidden] { - display: none !important; -} - -#timestamp.yt-live-chat-legacy-paid-message-renderer { - display: var(--yt-live-chat-item-timestamp-display, inline); - margin: var(--yt-live-chat-item-timestamp-margin, 0 8px 0 0); - color: var(--yt-live-chat-tertiary-text-color); - font-size: 11px; -} - -#author-photo.yt-live-chat-legacy-paid-message-renderer { - display: block; - margin-right: 16px; - overflow: hidden; - border-radius: 50%; - -ms-flex: none; - -webkit-flex: none; - flex: none; -} - -#menu-button.yt-live-chat-legacy-paid-message-renderer { - width: 40px; - height: 40px; - padding: 8px; -} - -#menu.yt-live-chat-legacy-paid-message-renderer { - position: absolute; - top: 0; - bottom: 0; - right: 0; - transform: translateX(100px); -} - -yt-live-chat-legacy-paid-message-renderer:hover #menu.yt-live-chat-legacy-paid-message-renderer, yt-live-chat-legacy-paid-message-renderer[menu-visible] #menu.yt-live-chat-legacy-paid-message-renderer { - transform: none; -} - -yt-live-chat-legacy-paid-message-renderer:focus-within #menu.yt-live-chat-legacy-paid-message-renderer { - transform: none; -} - -#inline-action-button-container.yt-live-chat-legacy-paid-message-renderer { - position: absolute; - top: -4px; - right: 0; - bottom: -4px; - left: 0; - background-color: var(--yt-live-chat-moderation-mode-hover-background-color); - display: none; - -ms-flex-align: center; - -webkit-align-items: center; - align-items: center; - -ms-flex-pack: center; - -webkit-justify-content: center; - justify-content: center; -} - -yt-live-chat-legacy-paid-message-renderer[has-inline-action-buttons]:hover #inline-action-button-container.yt-live-chat-legacy-paid-message-renderer { - display: flex; - -ms-flex-direction: row; - -webkit-flex-direction: row; - flex-direction: row; - display: var(--yt-live-chat-inline-action-button-container-display, none); -} - -yt-live-chat-legacy-paid-message-renderer[has-inline-action-buttons][hide-inline-action-buttons]:hover #inline-action-button-container.yt-live-chat-legacy-paid-message-renderer { - display: none; -} - -yt-live-chat-legacy-paid-message-renderer[has-inline-action-buttons]:hover #menu.yt-live-chat-legacy-paid-message-renderer { - display: var(--yt-live-chat-item-with-inline-actions-context-menu-display, block); -} - -#inline-action-buttons.yt-live-chat-legacy-paid-message-renderer>*.yt-live-chat-legacy-paid-message-renderer, #additional-inline-action-buttons.yt-live-chat-legacy-paid-message-renderer>*.yt-live-chat-legacy-paid-message-renderer { - --yt-button-icon-size: 36px; - --yt-button-icon-padding: 6px; - color: hsl(0, 0%, 100%); - border-radius: 2px; -} - -#inline-action-buttons.yt-live-chat-legacy-paid-message-renderer>*.yt-live-chat-legacy-paid-message-renderer { - background: hsla(0, 0%, 6.7%, .8); -} - -#inline-action-buttons.yt-live-chat-legacy-paid-message-renderer>.yt-live-chat-legacy-paid-message-renderer:hover { - background: hsl(0, 0%, 6.7%); -} - -#additional-inline-action-buttons.yt-live-chat-legacy-paid-message-renderer>*.yt-live-chat-legacy-paid-message-renderer { - color: var(--yt-live-chat-additional-inline-action-button-color); - background: var(--yt-live-chat-additional-inline-action-button-background-color); -} - -#additional-inline-action-buttons.yt-live-chat-legacy-paid-message-renderer>.yt-live-chat-legacy-paid-message-renderer:hover { - background: var(--yt-live-chat-additional-inline-action-button-background-color-hover); -} - -#additional-inline-action-buttons.yt-live-chat-legacy-paid-message-renderer:not(:empty) { - margin-left: 32px; -} - -#inline-action-buttons.yt-live-chat-legacy-paid-message-renderer>*.yt-live-chat-legacy-paid-message-renderer:not(:first-child), #additional-inline-action-buttons.yt-live-chat-legacy-paid-message-renderer>*.yt-live-chat-legacy-paid-message-renderer:not(:first-child) { - margin-left: 8px; -} - -yt-live-chat-legacy-paid-message-renderer { - position: relative; - display: block; - --yt-live-chat-sponsor-color: #0f9d58; - --yt-live-chat-item-timestamp-display: var(--yt-live-chat-paid-message-timestamp-display, none); - padding: 4px 24px; -} - -yt-live-chat-legacy-paid-message-renderer[dashboard-money-feed] { - padding: 0; -} - -#card.yt-live-chat-legacy-paid-message-renderer { - position: relative; - padding: 8px 16px; - background-color: var(--yt-live-chat-sponsor-color); - border-radius: 4px; - color: #fff; - font-size: 14px; - min-height: 40px; - display: flex; - -ms-flex-direction: row; - -webkit-flex-direction: row; - flex-direction: row; - -ms-flex-align: center; - -webkit-align-items: center; - align-items: center; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); -} - -yt-live-chat-legacy-paid-message-renderer[dashboard-money-feed] #card.yt-live-chat-legacy-paid-message-renderer { - border-radius: 0; - box-shadow: none; - background-color: var(--yt-live-chat-background-color); - color: rgba(0, 0, 0, 0.87); -} - -#author-photo.yt-live-chat-legacy-paid-message-renderer { - -ms-align-self: flex-start; - -webkit-align-self: flex-start; - align-self: flex-start; -} - -#author-name.yt-live-chat-legacy-paid-message-renderer { - display: none; -} - -yt-live-chat-legacy-paid-message-renderer[dashboard-money-feed] #author-name.yt-live-chat-legacy-paid-message-renderer { - display: block; - margin-right: 8px; - color: var(--yt-live-chat-secondary-text-color); - font-weight: 500; -} - -#content.yt-live-chat-legacy-paid-message-renderer { - -ms-flex: 1 1 0.000000001px; - -webkit-flex: 1; - flex: 1; - -webkit-flex-basis: 0.000000001px; - flex-basis: 0.000000001px; -} - -yt-live-chat-legacy-paid-message-renderer[dashboard-money-feed] #content.yt-live-chat-legacy-paid-message-renderer { - display: flex; - -ms-flex-direction: column; - -webkit-flex-direction: column; - flex-direction: column; -} - -yt-live-chat-legacy-paid-message-renderer[dashboard-money-feed] #content-primary-column.yt-live-chat-legacy-paid-message-renderer { - display: flex; - -ms-flex-direction: row; - -webkit-flex-direction: row; - flex-direction: row; - -ms-flex-align: baseline; - -webkit-align-items: baseline; - align-items: baseline; -} - -#event-text.yt-live-chat-legacy-paid-message-renderer { - color: rgba(255, 255, 255, 0.7); - font-weight: 500; -} - -yt-live-chat-legacy-paid-message-renderer[dashboard-money-feed] #event-text.yt-live-chat-legacy-paid-message-renderer { - display: inline; - height: 24px; - min-width: 16px; - border-radius: 12px; - margin-right: 8px; - padding: 0 12px; - background-color: var(--yt-live-chat-sponsor-color); - color: hsl(0, 0%, 100%); - display: inline-flex; - -ms-flex-align: center; - -webkit-align-items: center; - align-items: center; - -ms-flex-pack: center; - -webkit-justify-content: center; - justify-content: center; - font-size: 1.2rem; - font-weight: 500; - line-height: 1.2rem; -} - -#detail-text.yt-live-chat-legacy-paid-message-renderer { - font-size: 15px; - word-wrap: break-word; - word-break: break-word; -} - -#detail-text.yt-live-chat-legacy-paid-message-renderer .emoji.yt-live-chat-legacy-paid-message-renderer { - width: var(--yt-live-chat-emoji-size); - height: var(--yt-live-chat-emoji-size); - margin: -1px 2px 1px 2px; - vertical-align: middle; -} - -yt-live-chat-legacy-paid-message-renderer[dashboard-money-feed] #detail-text.yt-live-chat-legacy-paid-message-renderer { - display: none; -} - -a.yt-live-chat-legacy-paid-message-renderer { - display: inline; - text-decoration: underline; -} - -#detail-text.yt-live-chat-legacy-paid-message-renderer a.yt-live-chat-legacy-paid-message-renderer { - word-break: break-all; -} - -#detail-text.yt-live-chat-legacy-paid-message-renderer a.yt-live-chat-legacy-paid-message-renderer .mention.yt-live-chat-legacy-paid-message-renderer { - text-decoration: underline; -} - -#menu.yt-live-chat-legacy-paid-message-renderer { - background: linear-gradient(to right, transparent, var(--yt-live-chat-sponsor-color) 100%); - border-radius: 0 4px 4px 0; -} - -yt-live-chat-legacy-paid-message-renderer[dashboard-money-feed] #menu.yt-live-chat-legacy-paid-message-renderer { - margin-top: 8px; - background: linear-gradient(to right, transparent, var(--yt-live-chat-background-color) 40%); -} -</style> diff --git a/frontend/src/components/ChatRenderer/MembershipItem.vue b/frontend/src/components/ChatRenderer/MembershipItem.vue new file mode 100644 index 00000000..3bd5525e --- /dev/null +++ b/frontend/src/components/ChatRenderer/MembershipItem.vue @@ -0,0 +1,421 @@ +<template> + <yt-live-chat-membership-item-renderer class="style-scope yt-live-chat-item-list-renderer" show-only-header> + <div id="card" class="style-scope yt-live-chat-membership-item-renderer"> + <div id="header" class="style-scope yt-live-chat-membership-item-renderer"> + <img-shadow id="author-photo" height="40" width="40" class="style-scope yt-live-chat-membership-item-renderer" + :imgUrl="avatarUrl" + ></img-shadow> + <div id="header-content" class="style-scope yt-live-chat-membership-item-renderer"> + <div id="header-content-primary-column" class="style-scope yt-live-chat-membership-item-renderer"> + <div id="header-content-inner-column" class="style-scope yt-live-chat-membership-item-renderer"> + <yt-live-chat-author-chip class="style-scope yt-live-chat-membership-item-renderer"> + <span id="author-name" dir="auto" class="member style-scope yt-live-chat-author-chip">{{ + authorName + }}<!-- 这里是已验证勋章 --> + <span id="chip-badges" class="style-scope yt-live-chat-author-chip"></span> + </span> + <span id="chat-badges" class="style-scope yt-live-chat-author-chip"> + <author-badge class="style-scope yt-live-chat-author-chip" + :isAdmin="false" :privilegeType="privilegeType" + ></author-badge> + </span> + </yt-live-chat-author-chip> + </div> + <div id="header-subtext" class="style-scope yt-live-chat-membership-item-renderer">{{title}}</div> + </div> + <div id="timestamp" class="style-scope yt-live-chat-membership-item-renderer">{{timeText}}</div> + </div> + </div> + </div> + </yt-live-chat-membership-item-renderer> +</template> + +<script> +import ImgShadow from './ImgShadow.vue' +import AuthorBadge from './AuthorBadge.vue' +import * as utils from '@/utils' + +export default { + name: 'MembershipItem', + components: { + ImgShadow, + AuthorBadge + }, + props: { + avatarUrl: String, + authorName: String, + privilegeType: Number, + title: String, + time: Date + }, + computed: { + timeText() { + return utils.getTimeTextHourMin(this.time) + } + } +} +</script> + +<!-- yt-live-chat-membership-item-renderer --> +<style> +#timestamp.yt-live-chat-membership-item-renderer { + display: var(--yt-live-chat-item-timestamp-display, inline); + margin: var(--yt-live-chat-item-timestamp-margin, 0 8px 0 0); + color: var(--yt-live-chat-tertiary-text-color); + font-size: 11px; +} + +#author-photo.yt-live-chat-membership-item-renderer { + display: block; + margin-right: 16px; + + overflow: hidden; + border-radius: 50%; + + -ms-flex: var(--layout-flex-none_-_-ms-flex); + -webkit-flex: var(--layout-flex-none_-_-webkit-flex); + flex: var(--layout-flex-none_-_flex); +} + +#menu-button.yt-live-chat-membership-item-renderer { + width: var(--yt-live-chat-32px-icon-button_-_width); + height: var(--yt-live-chat-32px-icon-button_-_height); + padding: var(--yt-live-chat-32px-icon-button_-_padding); +} + +#menu.yt-live-chat-membership-item-renderer { + position: absolute; + top: 0; + bottom: 0; + right: 0; + + + transform: translateX(100px); +} + +yt-live-chat-membership-item-renderer:hover #menu.yt-live-chat-membership-item-renderer, +yt-live-chat-membership-item-renderer[menu-visible] #menu.yt-live-chat-membership-item-renderer { + transform: none; +} + +yt-live-chat-membership-item-renderer:focus-within #menu.yt-live-chat-membership-item-renderer { + transform: none; +} + +#inline-action-button-container.yt-live-chat-membership-item-renderer { + position: absolute; + top: -4px; + right: 0; + bottom: -4px; + left: 0; + + background-color: var(--yt-live-chat-moderation-mode-hover-background-color); + display: none; + + -ms-flex-align: var(--layout-center-center_-_-ms-flex-align); + -webkit-align-items: var(--layout-center-center_-_-webkit-align-items); + align-items: var(--layout-center-center_-_align-items); + -ms-flex-pack: var(--layout-center-center_-_-ms-flex-pack); + -webkit-justify-content: var(--layout-center-center_-_-webkit-justify-content); + justify-content: var(--layout-center-center_-_justify-content); +} + +yt-live-chat-membership-item-renderer[has-inline-action-buttons]:hover #inline-action-button-container.yt-live-chat-membership-item-renderer { + display: var(--layout-horizontal_-_display); + -ms-flex-direction: var(--layout-horizontal_-_-ms-flex-direction); + -webkit-flex-direction: var(--layout-horizontal_-_-webkit-flex-direction); + flex-direction: var(--layout-horizontal_-_flex-direction); + + + display: var(--yt-live-chat-inline-action-button-container-display, none); +} + +yt-live-chat-membership-item-renderer[has-inline-action-buttons][hide-inline-action-buttons]:hover #inline-action-button-container.yt-live-chat-membership-item-renderer { + display: none; +} + +yt-live-chat-membership-item-renderer[has-inline-action-buttons]:hover #menu.yt-live-chat-membership-item-renderer { + display: var(--yt-live-chat-item-with-inline-actions-context-menu-display, block); +} + +#inline-action-buttons.yt-live-chat-membership-item-renderer>*.yt-live-chat-membership-item-renderer, +#additional-inline-action-buttons.yt-live-chat-membership-item-renderer>*.yt-live-chat-membership-item-renderer { + --yt-button-icon-size: 36px; + --yt-button-icon-padding: 6px; + + color: var(--yt-white); + border-radius: 2px; +} + +#inline-action-buttons.yt-live-chat-membership-item-renderer>*.yt-live-chat-membership-item-renderer { + background: var(--yt-luna-black-opacity-lighten-1); +} + +#inline-action-buttons.yt-live-chat-membership-item-renderer>.yt-live-chat-membership-item-renderer:hover { + background: var(--yt-luna-black); +} + +#additional-inline-action-buttons.yt-live-chat-membership-item-renderer>*.yt-live-chat-membership-item-renderer { + color: var(--yt-live-chat-additional-inline-action-button-color); + background: var(--yt-live-chat-additional-inline-action-button-background-color); +} + +#additional-inline-action-buttons.yt-live-chat-membership-item-renderer>.yt-live-chat-membership-item-renderer:hover { + background: var(--yt-live-chat-additional-inline-action-button-background-color-hover); +} + +#additional-inline-action-buttons.yt-live-chat-membership-item-renderer:not(:empty) { + margin-left: 32px; +} + +#inline-action-buttons.yt-live-chat-membership-item-renderer>*.yt-live-chat-membership-item-renderer:not(:first-child), +#additional-inline-action-buttons.yt-live-chat-membership-item-renderer>*.yt-live-chat-membership-item-renderer:not(:first-child) { + margin-left: 8px; +} + +yt-live-chat-membership-item-renderer { + position: relative; + display: block; + + --yt-live-chat-sponsor-header-color: #0a8043; + --yt-live-chat-sponsor-color: #0f9d58; + --yt-live-chat-sponsor-text-color: #fff; + --yt-live-chat-item-timestamp-display: var(--yt-live-chat-paid-message-timestamp-display, none); + + padding: 4px 24px; +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] { + padding: 0; + + --yt-live-chat-item-timestamp-display: block; +} + +#card.yt-live-chat-membership-item-renderer { + overflow: hidden; + font-size: 14px; + border-radius: 4px; + + display: var(--layout-vertical_-_display); + -ms-flex-direction: var(--layout-vertical_-_-ms-flex-direction); + -webkit-flex-direction: var(--layout-vertical_-_-webkit-flex-direction); + flex-direction: var(--layout-vertical_-_flex-direction); + box-shadow: var(--shadow-elevation-2dp_-_box-shadow); +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] #card.yt-live-chat-membership-item-renderer { + border-radius: 0; + box-shadow: none; +} + +#header.yt-live-chat-membership-item-renderer { + position: relative; + + background-color: var(--yt-live-chat-sponsor-header-color); + padding: 8px 16px; + color: #fff; + min-height: 20px; + + display: var(--layout-horizontal_-_display); + -ms-flex-direction: var(--layout-horizontal_-_-ms-flex-direction); + -webkit-flex-direction: var(--layout-horizontal_-_-webkit-flex-direction); + flex-direction: var(--layout-horizontal_-_flex-direction); + -ms-flex-align: var(--layout-center_-_-ms-flex-align); + -webkit-align-items: var(--layout-center_-_-webkit-align-items); + align-items: var(--layout-center_-_align-items); +} + +yt-live-chat-membership-item-renderer[show-only-header] #header.yt-live-chat-membership-item-renderer { + background-color: var(--yt-live-chat-sponsor-color); +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] #header.yt-live-chat-membership-item-renderer { + color: var(--yt-live-chat-secondary-text-color); + background-color: var(--yt-live-chat-background-color); + -ms-flex-align: var(--layout-start_-_-ms-flex-align); + -webkit-align-items: var(--layout-start_-_-webkit-align-items); + align-items: var(--layout-start_-_align-items); +} + +#header-content.yt-live-chat-membership-item-renderer { + display: var(--layout-horizontal_-_display); + -ms-flex-direction: var(--layout-horizontal_-_-ms-flex-direction); + -webkit-flex-direction: var(--layout-horizontal_-_-webkit-flex-direction); + flex-direction: var(--layout-horizontal_-_flex-direction); + -ms-flex-pack: var(--layout-justified_-_-ms-flex-pack); + -webkit-justify-content: var(--layout-justified_-_-webkit-justify-content); + justify-content: var(--layout-justified_-_justify-content); + -ms-flex: var(--layout-flex_-_-ms-flex); + -webkit-flex: var(--layout-flex_-_-webkit-flex); + flex: var(--layout-flex_-_flex); + -webkit-flex-basis: var(--layout-flex_-_-webkit-flex-basis); + flex-basis: var(--layout-flex_-_flex-basis); + -ms-flex-align: var(--layout-baseline_-_-ms-flex-align); + -webkit-align-items: var(--layout-baseline_-_-webkit-align-items); + align-items: var(--layout-baseline_-_align-items); +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] #header-content.yt-live-chat-membership-item-renderer { + display: var(--layout-vertical_-_display); + -ms-flex-direction: var(--layout-vertical_-_-ms-flex-direction); + -webkit-flex-direction: var(--layout-vertical_-_-webkit-flex-direction); + flex-direction: var(--layout-vertical_-_flex-direction); +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] #header-content-inner-column.yt-live-chat-membership-item-renderer { + margin-bottom: 4px; + + display: var(--layout-horizontal_-_display); + -ms-flex-direction: var(--layout-horizontal_-_-ms-flex-direction); + -webkit-flex-direction: var(--layout-horizontal_-_-webkit-flex-direction); + flex-direction: var(--layout-horizontal_-_flex-direction); + -ms-flex-align: var(--layout-center_-_-ms-flex-align); + -webkit-align-items: var(--layout-center_-_-webkit-align-items); + align-items: var(--layout-center_-_align-items); + -ms-flex: var(--layout-flex-none_-_-ms-flex); + -webkit-flex: var(--layout-flex-none_-_-webkit-flex); + flex: var(--layout-flex-none_-_flex); +} + +#author-photo.yt-live-chat-membership-item-renderer { + width: 40px; + height: 40px; +} + +yt-icon#author-photo.yt-live-chat-membership-item-renderer { + display: none; +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] yt-icon#author-photo.yt-live-chat-membership-item-renderer { + display: block; +} + +yt-live-chat-membership-item-renderer:not([dashboard-money-feed]) yt-live-chat-author-chip.yt-live-chat-membership-item-renderer { + --yt-live-chat-sponsor-color: var(--yt-live-chat-sponsor-text-color); + --yt-live-chat-secondary-text-color: var(--yt-live-chat-sponsor-text-color); +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] yt-live-chat-author-chip.yt-live-chat-membership-item-renderer { + margin-right: 8px; + font-weight: 500; + --yt-live-chat-sponsor-color: var(--yt-live-chat-secondary-text-color); +} + +#header-subtext.yt-live-chat-membership-item-renderer { + margin-top: 2px; + color: rgba(255, 255, 255, 0.7); + font-weight: 500; + font-size: 15px; +} + +#header-subtext.yt-live-chat-membership-item-renderer:empty { + display: none; +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] #header-subtext.yt-live-chat-membership-item-renderer { + margin: 4px 0 13px; + font-size: 11px; + font-weight: normal; + color: var(--yt-live-chat-secondary-text-color); +} + +#header-primary-text.yt-live-chat-membership-item-renderer { + word-wrap: break-word; + word-break: break-word; + font-weight: 500; + color: rgba(255, 255, 255, 1); +} + +#header-primary-text.yt-live-chat-membership-item-renderer:empty { + display: none; +} + +yt-live-chat-membership-item-renderer[has-primary-header-text]:not([dashboard-money-feed]) yt-live-chat-author-chip.yt-live-chat-membership-item-renderer, +yt-live-chat-membership-item-renderer[has-primary-header-text]:not([dashboard-money-feed]) #header-subtext.yt-live-chat-membership-item-renderer { + font-size: 12px; +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] #header-primary-text.yt-live-chat-membership-item-renderer { + display: inline; + height: 24px; + min-width: 16px; + border-radius: 12px; + margin-right: 8px; + padding: 0 12px; + background-color: var(--yt-live-chat-sponsor-color); + color: var(--yt-white); + display: var(--layout-inline_-_display, inline); + -ms-flex-align: var(--layout-center-center_-_-ms-flex-align); + -webkit-align-items: var(--layout-center-center_-_-webkit-align-items); + align-items: var(--layout-center-center_-_align-items); + -ms-flex-pack: var(--layout-center-center_-_-ms-flex-pack); + -webkit-justify-content: var(--layout-center-center_-_-webkit-justify-content); + justify-content: var(--layout-center-center_-_justify-content); + font-size: var(--ytd-badge_-_font-size); + font-weight: var(--ytd-badge_-_font-weight); + line-height: var(--ytd-badge_-_line-height); +} + +#content.yt-live-chat-membership-item-renderer { + background-color: var(--yt-live-chat-sponsor-color); + color: var(--yt-live-chat-sponsor-text-color); + padding: 8px 16px; + word-wrap: break-word; + word-break: break-word; + font-size: 15px; + line-height: 20px; +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] #content.yt-live-chat-membership-item-renderer { + background-color: unset; + font-size: unset; + color: var(--yt-live-chat-secondary-text-color); + padding: 0 0 16px 72px; +} + +#content.yt-live-chat-membership-item-renderer img.yt-live-chat-membership-item-renderer { + width: var(--yt-live-chat-emoji-size); + height: var(--yt-live-chat-emoji-size); + + margin: -1px 2px 1px 2px; + vertical-align: middle; +} + +yt-live-chat-membership-item-renderer[show-only-header] #content.yt-live-chat-membership-item-renderer, +#deleted-state.yt-live-chat-membership-item-renderer:empty { + display: none; +} + +#deleted-state.yt-live-chat-membership-item-renderer { + display: block; + font-style: italic; + opacity: 0.7; +} + +a.yt-live-chat-membership-item-renderer { + display: inline; + text-decoration: underline; +} + +#message.yt-live-chat-membership-item-renderer a.yt-live-chat-membership-item-renderer { + word-break: break-all; +} + +#message.yt-live-chat-membership-item-renderer a.yt-live-chat-membership-item-renderer .mention.yt-live-chat-membership-item-renderer { + text-decoration: underline; +} + +#menu.yt-live-chat-membership-item-renderer { + background: linear-gradient(to right, transparent, var(--yt-live-chat-sponsor-header-color) 100%); + border-radius: 0 4px 4px 0; +} + +yt-live-chat-membership-item-renderer[show-only-header] #menu.yt-live-chat-membership-item-renderer { + background: linear-gradient(to right, transparent, var(--yt-live-chat-sponsor-color) 100%); +} + +yt-live-chat-membership-item-renderer[dashboard-money-feed] #menu.yt-live-chat-membership-item-renderer { + margin-top: 8px; + background: linear-gradient(to right, transparent, var(--yt-live-chat-background-color) 40%); +} +</style> diff --git a/frontend/src/components/ChatRenderer/PaidMessage.vue b/frontend/src/components/ChatRenderer/PaidMessage.vue index 5c87d2e2..4809d7d2 100644 --- a/frontend/src/components/ChatRenderer/PaidMessage.vue +++ b/frontend/src/components/ChatRenderer/PaidMessage.vue @@ -56,7 +56,7 @@ export default { return 'CN¥' + utils.formatCurrency(this.price) }, timeText() { - return utils.getTimeTextMinSec(this.time) + return utils.getTimeTextHourMin(this.time) } } } diff --git a/frontend/src/components/ChatRenderer/TextMessage.vue b/frontend/src/components/ChatRenderer/TextMessage.vue index c3c3a101..6d0ebd7c 100644 --- a/frontend/src/components/ChatRenderer/TextMessage.vue +++ b/frontend/src/components/ChatRenderer/TextMessage.vue @@ -54,7 +54,7 @@ export default { }, computed: { timeText() { - return utils.getTimeTextMinSec(this.time) + return utils.getTimeTextHourMin(this.time) }, authorTypeText() { return constants.AUTHOR_TYPE_TO_TEXT[this.authorType] diff --git a/frontend/src/components/ChatRenderer/Ticker.vue b/frontend/src/components/ChatRenderer/Ticker.vue index 28abd494..0f8e4599 100644 --- a/frontend/src/components/ChatRenderer/Ticker.vue +++ b/frontend/src/components/ChatRenderer/Ticker.vue @@ -26,11 +26,11 @@ </div> </div> <template v-if="pinnedMessage"> - <legacy-paid-message :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" - :avatarUrl="pinnedMessage.avatarUrl" :title="pinnedMessage.title" :content="pinnedMessage.content" - :time="pinnedMessage.time" - ></legacy-paid-message> + :avatarUrl="pinnedMessage.avatarUrl" :authorName="pinnedMessage.authorName" :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" @@ -44,7 +44,7 @@ import * as config from '@/api/config' import {formatCurrency} from '@/utils' import ImgShadow from './ImgShadow.vue' -import LegacyPaidMessage from './LegacyPaidMessage.vue' +import MembershipItem from './MembershipItem.vue' import PaidMessage from './PaidMessage.vue' import * as constants from './constants' @@ -52,7 +52,7 @@ export default { name: 'Ticker', components: { ImgShadow, - LegacyPaidMessage, + MembershipItem, PaidMessage }, props: { diff --git a/frontend/src/components/ChatRenderer/index.vue b/frontend/src/components/ChatRenderer/index.vue index 0f9f91ea..dce06cf5 100644 --- a/frontend/src/components/ChatRenderer/index.vue +++ b/frontend/src/components/ChatRenderer/index.vue @@ -23,11 +23,11 @@ :price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName" :time="message.time" :content="getGiftShowContent(message)" ></paid-message> - <legacy-paid-message :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" - :avatarUrl="message.avatarUrl" :title="message.title" :content="message.content" - :time="message.time" - ></legacy-paid-message> + :avatarUrl="message.avatarUrl" :authorName="message.authorName" :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" @@ -45,7 +45,7 @@ import * as config from '@/api/config' import Ticker from './Ticker.vue' import TextMessage from './TextMessage.vue' -import LegacyPaidMessage from './LegacyPaidMessage.vue' +import MembershipItem from './MembershipItem.vue' import PaidMessage from './PaidMessage.vue' import * as constants from './constants' @@ -57,7 +57,7 @@ export default { components: { Ticker, TextMessage, - LegacyPaidMessage, + MembershipItem, PaidMessage }, props: { @@ -628,6 +628,288 @@ html:not(.style-scope) { --yt-pdg-paid-stickers-author-name-font-size: 13px; --yt-pdg-paid-stickers-margin-left: 38px; } + +html:not(.style-scope) { + --layout_-_display: flex; + ; + + --layout-inline_-_display: inline-flex; + ; + + --layout-horizontal_-_display: var(--layout_-_display); + --layout-horizontal_-_-ms-flex-direction: row; + --layout-horizontal_-_-webkit-flex-direction: row; + --layout-horizontal_-_flex-direction: row; + ; + + --layout-horizontal-reverse_-_display: var(--layout_-_display); + --layout-horizontal-reverse_-_-ms-flex-direction: row-reverse; + --layout-horizontal-reverse_-_-webkit-flex-direction: row-reverse; + --layout-horizontal-reverse_-_flex-direction: row-reverse; + ; + + --layout-vertical_-_display: var(--layout_-_display); + --layout-vertical_-_-ms-flex-direction: column; + --layout-vertical_-_-webkit-flex-direction: column; + --layout-vertical_-_flex-direction: column; + ; + + --layout-vertical-reverse_-_display: var(--layout_-_display); + --layout-vertical-reverse_-_-ms-flex-direction: column-reverse; + --layout-vertical-reverse_-_-webkit-flex-direction: column-reverse; + --layout-vertical-reverse_-_flex-direction: column-reverse; + ; + + --layout-wrap_-_-ms-flex-wrap: wrap; + --layout-wrap_-_-webkit-flex-wrap: wrap; + --layout-wrap_-_flex-wrap: wrap; + ; + + --layout-wrap-reverse_-_-ms-flex-wrap: wrap-reverse; + --layout-wrap-reverse_-_-webkit-flex-wrap: wrap-reverse; + --layout-wrap-reverse_-_flex-wrap: wrap-reverse; + ; + + --layout-flex-auto_-_-ms-flex: 1 1 auto; + --layout-flex-auto_-_-webkit-flex: 1 1 auto; + --layout-flex-auto_-_flex: 1 1 auto; + ; + + --layout-flex-none_-_-ms-flex: none; + --layout-flex-none_-_-webkit-flex: none; + --layout-flex-none_-_flex: none; + ; + + --layout-flex_-_-ms-flex: 1 1 0.000000001px; + --layout-flex_-_-webkit-flex: 1; + --layout-flex_-_flex: 1; + --layout-flex_-_-webkit-flex-basis: 0.000000001px; + --layout-flex_-_flex-basis: 0.000000001px; + ; + + --layout-flex-2_-_-ms-flex: 2; + --layout-flex-2_-_-webkit-flex: 2; + --layout-flex-2_-_flex: 2; + ; + + --layout-flex-3_-_-ms-flex: 3; + --layout-flex-3_-_-webkit-flex: 3; + --layout-flex-3_-_flex: 3; + ; + + --layout-flex-4_-_-ms-flex: 4; + --layout-flex-4_-_-webkit-flex: 4; + --layout-flex-4_-_flex: 4; + ; + + --layout-flex-5_-_-ms-flex: 5; + --layout-flex-5_-_-webkit-flex: 5; + --layout-flex-5_-_flex: 5; + ; + + --layout-flex-6_-_-ms-flex: 6; + --layout-flex-6_-_-webkit-flex: 6; + --layout-flex-6_-_flex: 6; + ; + + --layout-flex-7_-_-ms-flex: 7; + --layout-flex-7_-_-webkit-flex: 7; + --layout-flex-7_-_flex: 7; + ; + + --layout-flex-8_-_-ms-flex: 8; + --layout-flex-8_-_-webkit-flex: 8; + --layout-flex-8_-_flex: 8; + ; + + --layout-flex-9_-_-ms-flex: 9; + --layout-flex-9_-_-webkit-flex: 9; + --layout-flex-9_-_flex: 9; + ; + + --layout-flex-10_-_-ms-flex: 10; + --layout-flex-10_-_-webkit-flex: 10; + --layout-flex-10_-_flex: 10; + ; + + --layout-flex-11_-_-ms-flex: 11; + --layout-flex-11_-_-webkit-flex: 11; + --layout-flex-11_-_flex: 11; + ; + + --layout-flex-12_-_-ms-flex: 12; + --layout-flex-12_-_-webkit-flex: 12; + --layout-flex-12_-_flex: 12; + ; + + + + --layout-start_-_-ms-flex-align: start; + --layout-start_-_-webkit-align-items: flex-start; + --layout-start_-_align-items: flex-start; + ; + + --layout-center_-_-ms-flex-align: center; + --layout-center_-_-webkit-align-items: center; + --layout-center_-_align-items: center; + ; + + --layout-end_-_-ms-flex-align: end; + --layout-end_-_-webkit-align-items: flex-end; + --layout-end_-_align-items: flex-end; + ; + + --layout-baseline_-_-ms-flex-align: baseline; + --layout-baseline_-_-webkit-align-items: baseline; + --layout-baseline_-_align-items: baseline; + ; + + + + --layout-start-justified_-_-ms-flex-pack: start; + --layout-start-justified_-_-webkit-justify-content: flex-start; + --layout-start-justified_-_justify-content: flex-start; + ; + + --layout-center-justified_-_-ms-flex-pack: center; + --layout-center-justified_-_-webkit-justify-content: center; + --layout-center-justified_-_justify-content: center; + ; + + --layout-end-justified_-_-ms-flex-pack: end; + --layout-end-justified_-_-webkit-justify-content: flex-end; + --layout-end-justified_-_justify-content: flex-end; + ; + + --layout-around-justified_-_-ms-flex-pack: distribute; + --layout-around-justified_-_-webkit-justify-content: space-around; + --layout-around-justified_-_justify-content: space-around; + ; + + --layout-justified_-_-ms-flex-pack: justify; + --layout-justified_-_-webkit-justify-content: space-between; + --layout-justified_-_justify-content: space-between; + ; + + --layout-center-center_-_-ms-flex-align: var(--layout-center_-_-ms-flex-align); + --layout-center-center_-_-webkit-align-items: var(--layout-center_-_-webkit-align-items); + --layout-center-center_-_align-items: var(--layout-center_-_align-items); + --layout-center-center_-_-ms-flex-pack: var(--layout-center-justified_-_-ms-flex-pack); + --layout-center-center_-_-webkit-justify-content: var(--layout-center-justified_-_-webkit-justify-content); + --layout-center-center_-_justify-content: var(--layout-center-justified_-_justify-content); + ; + + + + --layout-self-start_-_-ms-align-self: flex-start; + --layout-self-start_-_-webkit-align-self: flex-start; + --layout-self-start_-_align-self: flex-start; + ; + + --layout-self-center_-_-ms-align-self: center; + --layout-self-center_-_-webkit-align-self: center; + --layout-self-center_-_align-self: center; + ; + + --layout-self-end_-_-ms-align-self: flex-end; + --layout-self-end_-_-webkit-align-self: flex-end; + --layout-self-end_-_align-self: flex-end; + ; + + --layout-self-stretch_-_-ms-align-self: stretch; + --layout-self-stretch_-_-webkit-align-self: stretch; + --layout-self-stretch_-_align-self: stretch; + ; + + --layout-self-baseline_-_-ms-align-self: baseline; + --layout-self-baseline_-_-webkit-align-self: baseline; + --layout-self-baseline_-_align-self: baseline; + ; + + + + --layout-start-aligned_-_-ms-flex-line-pack: start; + --layout-start-aligned_-_-ms-align-content: flex-start; + --layout-start-aligned_-_-webkit-align-content: flex-start; + --layout-start-aligned_-_align-content: flex-start; + ; + + --layout-end-aligned_-_-ms-flex-line-pack: end; + --layout-end-aligned_-_-ms-align-content: flex-end; + --layout-end-aligned_-_-webkit-align-content: flex-end; + --layout-end-aligned_-_align-content: flex-end; + ; + + --layout-center-aligned_-_-ms-flex-line-pack: center; + --layout-center-aligned_-_-ms-align-content: center; + --layout-center-aligned_-_-webkit-align-content: center; + --layout-center-aligned_-_align-content: center; + ; + + --layout-between-aligned_-_-ms-flex-line-pack: justify; + --layout-between-aligned_-_-ms-align-content: space-between; + --layout-between-aligned_-_-webkit-align-content: space-between; + --layout-between-aligned_-_align-content: space-between; + ; + + --layout-around-aligned_-_-ms-flex-line-pack: distribute; + --layout-around-aligned_-_-ms-align-content: space-around; + --layout-around-aligned_-_-webkit-align-content: space-around; + --layout-around-aligned_-_align-content: space-around; + ; + + + + --layout-block_-_display: block; + ; + + --layout-invisible_-_visibility: hidden !important; + ; + + --layout-relative_-_position: relative; + ; + + --layout-fit_-_position: absolute; + --layout-fit_-_top: 0; + --layout-fit_-_right: 0; + --layout-fit_-_bottom: 0; + --layout-fit_-_left: 0; + ; + + --layout-scroll_-_-webkit-overflow-scrolling: touch; + --layout-scroll_-_overflow: auto; + ; + + --layout-fullbleed_-_margin: 0; + --layout-fullbleed_-_height: 100vh; + ; + + + + --layout-fixed-top_-_position: fixed; + --layout-fixed-top_-_top: 0; + --layout-fixed-top_-_left: 0; + --layout-fixed-top_-_right: 0; + ; + + --layout-fixed-right_-_position: fixed; + --layout-fixed-right_-_top: 0; + --layout-fixed-right_-_right: 0; + --layout-fixed-right_-_bottom: 0; + ; + + --layout-fixed-bottom_-_position: fixed; + --layout-fixed-bottom_-_right: 0; + --layout-fixed-bottom_-_bottom: 0; + --layout-fixed-bottom_-_left: 0; + ; + + --layout-fixed-left_-_position: fixed; + --layout-fixed-left_-_top: 0; + --layout-fixed-left_-_bottom: 0; + --layout-fixed-left_-_left: 0; + ; +} </style> <!-- yt-live-chat-app --> diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 5968f9da..7813c4cb 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -52,6 +52,7 @@ export default { avatarSize: 'Avatar size', userNames: 'User names', + showUserNames: 'Show user names', font: 'Font', fontSize: 'Font size', lineHeight: 'Line height (0 for default)', diff --git a/frontend/src/lang/ja.js b/frontend/src/lang/ja.js index 8ce58f57..fcadfb6c 100644 --- a/frontend/src/lang/ja.js +++ b/frontend/src/lang/ja.js @@ -52,6 +52,7 @@ export default { avatarSize: 'アイコンのサイズ', userNames: 'ユーザー名', + showUserNames: 'ユーザー名を表示する', font: 'フォント', fontSize: 'フォントサイズ', lineHeight: '行の高さ(0はデフォルト)', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index f9b86bad..819d87bb 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -52,6 +52,7 @@ export default { avatarSize: '头像尺寸', userNames: '用户名', + showUserNames: '显示用户名', font: '字体', fontSize: '字体尺寸', lineHeight: '行高(0为默认)', diff --git a/frontend/src/utils.js b/frontend/src/utils.js index d5a69329..a753a4f7 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -27,8 +27,8 @@ export function formatCurrency (price) { }).format(price) } -export function getTimeTextMinSec (date) { +export function getTimeTextHourMin (date) { + let hour = date.getHours() let min = ('00' + date.getMinutes()).slice(-2) - let sec = ('00' + date.getSeconds()).slice(-2) - return `${min}:${sec}` + return `${hour}:${min}` } diff --git a/frontend/src/views/Room.vue b/frontend/src/views/Room.vue index b5204db5..77f35aeb 100644 --- a/frontend/src/views/Room.vue +++ b/frontend/src/views/Room.vue @@ -188,8 +188,8 @@ export default { avatarUrl: data.avatarUrl, time: new Date(data.timestamp * 1000), authorName: data.authorName, - title: 'NEW MEMBER!', - content: `Welcome ${data.authorName}!` + privilegeType: data.privilegeType, + title: 'New member' } break case COMMAND_ADD_SUPER_CHAT: diff --git a/frontend/src/views/StyleGenerator/index.vue b/frontend/src/views/StyleGenerator/index.vue index 1973a608..54d029b0 100644 --- a/frontend/src/views/StyleGenerator/index.vue +++ b/frontend/src/views/StyleGenerator/index.vue @@ -22,6 +22,9 @@ </el-form-item> <h3>{{$t('stylegen.userNames')}}</h3> + <el-form-item :label="$t('stylegen.showUserNames')"> + <el-switch v-model="form.showUserNames"></el-switch> + </el-form-item> <el-form-item :label="$t('stylegen.font')"> <el-autocomplete v-model="form.userNameFont" :fetch-suggestions="getFontSuggestions"></el-autocomplete> </el-form-item> @@ -219,15 +222,15 @@ let textMessageTemplate = { repeated: 1, translation: '' } -let legacyPaidMessageTemplate = { +let membershipItemTemplate = { id: 0, addTime: time, type: constants.MESSAGE_TYPE_MEMBER, avatarUrl: 'https://static.hdslb.com/images/member/noface.gif', time: time, authorName: '', - title: 'NEW MEMBER!', - content: '' + privilegeType: 3, + title: 'New member' } let paidMessageTemplate = { id: 0, @@ -266,15 +269,14 @@ const EXAMPLE_MESSAGES = [ content: 'kksk' }, { - ...legacyPaidMessageTemplate, + ...membershipItemTemplate, id: (nextId++).toString(), - authorName: '进击的冰糖', - content: 'Welcome 进击的冰糖!' + authorName: '艾米亚official' }, { ...paidMessageTemplate, id: (nextId++).toString(), - authorName: '无火的残渣', + authorName: '愛里紗メイプル', price: 66600, content: 'Sent 小电视飞船x100' }, @@ -288,7 +290,7 @@ const EXAMPLE_MESSAGES = [ { ...paidMessageTemplate, id: (nextId++).toString(), - authorName: '夏色祭保護協会会長', + authorName: 'AstralisUP', price: 30, content: '言いたいことがあるんだよ!' } diff --git a/frontend/src/views/StyleGenerator/stylegen.js b/frontend/src/views/StyleGenerator/stylegen.js index dff4cb13..5e96ab03 100644 --- a/frontend/src/views/StyleGenerator/stylegen.js +++ b/frontend/src/views/StyleGenerator/stylegen.js @@ -9,6 +9,7 @@ export const DEFAULT_CONFIG = { showAvatars: true, avatarSize: 24, + showUserNames: true, userNameFont: 'Changa One', userNameFontSize: 20, userNameLineHeight: 0, @@ -109,11 +110,11 @@ yt-live-chat-renderer * { ${getShowOutlinesStyle(config)} font-family: "${config.messageFont}"${FALLBACK_FONTS}; font-size: ${config.messageFontSize}px !important; - line-height: ${config.messageLineHeight}px !important; + line-height: ${config.messageLineHeight || config.messageFontSize}px !important; } yt-live-chat-text-message-renderer #content, -yt-live-chat-legacy-paid-message-renderer #content { +yt-live-chat-membership-item-renderer #content { overflow: initial !important; } @@ -133,12 +134,7 @@ yt-live-chat-message-input-renderer { } /* Reduce side padding. */ -yt-live-chat-text-message-renderer, -yt-live-chat-legacy-paid-message-renderer { - ${getPaddingStyle(config)} -} - -yt-live-chat-paid-message-renderer #header { +yt-live-chat-text-message-renderer { ${getPaddingStyle(config)} } @@ -147,8 +143,8 @@ yt-live-chat-text-message-renderer #author-photo, yt-live-chat-text-message-renderer #author-photo img, yt-live-chat-paid-message-renderer #author-photo, yt-live-chat-paid-message-renderer #author-photo img, -yt-live-chat-legacy-paid-message-renderer #author-photo, -yt-live-chat-legacy-paid-message-renderer #author-photo img { +yt-live-chat-membership-item-renderer #author-photo, +yt-live-chat-membership-item-renderer #author-photo img { ${config.showAvatars ? '' : 'display: none !important;'} width: ${config.avatarSize}px !important; height: ${config.avatarSize}px !important; @@ -189,6 +185,7 @@ yt-live-chat-text-message-renderer yt-live-chat-author-badge-renderer[type="memb /* Channel names. */ yt-live-chat-text-message-renderer #author-name { + ${config.showUserNames ? '' : 'display: none !important;'} ${config.userNameColor ? `color: ${config.userNameColor} !important;` : ''} font-family: "${config.userNameFont}"${FALLBACK_FONTS}; font-size: ${config.userNameFontSize}px !important; @@ -213,8 +210,8 @@ ${!config.messageOnNewLine ? '' : `yt-live-chat-text-message-renderer #message { /* SuperChat/Fan Funding Messages. */ yt-live-chat-paid-message-renderer #author-name, yt-live-chat-paid-message-renderer #author-name *, -yt-live-chat-legacy-paid-message-renderer #event-text, -yt-live-chat-legacy-paid-message-renderer #event-text * { +yt-live-chat-membership-item-renderer #header-content-inner-column, +yt-live-chat-membership-item-renderer #header-content-inner-column * { ${config.firstLineColor ? `color: ${config.firstLineColor} !important;` : ''} font-family: "${config.firstLineFont}"${FALLBACK_FONTS}; font-size: ${config.firstLineFontSize}px !important; @@ -223,8 +220,8 @@ yt-live-chat-legacy-paid-message-renderer #event-text * { yt-live-chat-paid-message-renderer #purchase-amount, yt-live-chat-paid-message-renderer #purchase-amount *, -yt-live-chat-legacy-paid-message-renderer #detail-text, -yt-live-chat-legacy-paid-message-renderer #detail-text * { +yt-live-chat-membership-item-renderer #header-subtext, +yt-live-chat-membership-item-renderer #header-subtext * { ${config.secondLineColor ? `color: ${config.secondLineColor} !important;` : ''} font-family: "${config.secondLineFont}"${FALLBACK_FONTS}; font-size: ${config.secondLineFontSize}px !important; @@ -243,17 +240,18 @@ yt-live-chat-paid-message-renderer { margin: 4px 0 !important; } -yt-live-chat-legacy-paid-message-renderer #card { +yt-live-chat-membership-item-renderer #card, +yt-live-chat-membership-item-renderer #header { ${getShowNewMemberBgStyle(config)} } yt-live-chat-text-message-renderer a, -yt-live-chat-legacy-paid-message-renderer a { +yt-live-chat-membership-item-renderer a { text-decoration: none !important; } yt-live-chat-text-message-renderer[is-deleted], -yt-live-chat-legacy-paid-message-renderer[is-deleted] { +yt-live-chat-membership-item-renderer[is-deleted] { display: none !important; } @@ -399,7 +397,8 @@ ${keyframes.join('\n')} } yt-live-chat-text-message-renderer, -yt-live-chat-legacy-paid-message-renderer { +yt-live-chat-membership-item-renderer, +yt-live-chat-paid-message-renderer { animation: anim ${totalTime}ms; animation-fill-mode: both; }` From 602a9f2a441210c488633660e411b0c2ab12abf0 Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sun, 19 Jul 2020 22:19:05 +0800 Subject: [PATCH 03/18] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E6=88=BF=E9=97=B4API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blivedm b/blivedm index d173228c..6618bf5c 160000 --- a/blivedm +++ b/blivedm @@ -1 +1 @@ -Subproject commit d173228c5f83c2f5f94551259e0e6c01e929d92c +Subproject commit 6618bf5c484cbe5a6f7e32f8e5d65ed0b74849c7 From 96070ac473ba8e41c3f3e50db6b80b3222798fc8 Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sun, 16 Aug 2020 10:10:49 +0800 Subject: [PATCH 04/18] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3d12a0f2..9877b7d1 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ * 支持自动翻译弹幕、醒目留言到日语 ## 使用方法 -### 本地使用 +### 一、本地使用 1. 下载[发布版](https://github.com/xfgryujk/blivechat/releases)(仅提供x64 Windows版) 2. 双击`blivechat.exe`运行服务器,或者用命令行可以指定host和端口号: ```bat @@ -32,14 +32,22 @@ * 本地使用时不要关闭blivechat.exe那个黑框,否则不能继续获取弹幕 * 样式生成器没有列出所有本地字体,但是可以手动输入本地字体 -### 公共服务器 -请优先在本地使用,使用公共服务器会有更大的弹幕延迟,而且服务器故障时可能出现直播事故 +### 二、公共服务器 +请优先在本地使用,使用公共服务器会有更大的弹幕延迟,而且服务器故障时可能发生直播事故 * [第三方公共服务器](http://chat.bilisc.com/) * [仅样式生成器](https://style.vtbs.moe/) -### 源代码版 -1. 编译前端(需要安装Node.js和npm): +### 三、源代码版(自建服务器或在Windows以外平台) +0. 由于使用了git子模块,clone时需要加上`--recursive`参数: + ```sh + git clone --recursive https://github.com/xfgryujk/blivechat.git + ``` + 如果已经clone,拉子模块的方法: + ```sh + git submodule update --init --recursive + ``` +1. 编译前端(需要安装Node.js): ```sh cd frontend npm i @@ -56,8 +64,70 @@ ``` 3. 用浏览器打开[http://localhost:12450](http://localhost:12450),以下略 -### Docker +### 四、Docker(自建服务器) 1. ```sh docker run -d -p 12450:12450 xfgryujk/blivechat:latest ``` 2. 用浏览器打开[http://localhost:12450](http://localhost:12450),以下略 + +### nginx配置(可选) +自建服务器时使用,`sudo vim /etc/nginx/sites-enabled/blivechat.conf` + +```conf +upstream blivechat { + # blivechat地址 + server 127.0.0.1:12450; +} + +# 强制HTTPS +server { + listen 80; + listen [::]:80; + server_name YOUR.DOMAIN.NAME; + + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name YOUR.DOMAIN.NAME; + + # SSL + ssl_certificate /PATH/TO/CERT.crt; + ssl_certificate_key /PATH/TO/CERT_KEY.key; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; + ssl_prefer_server_ciphers on; + + # 代理header + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # 静态文件 + location / { + root /PATH/TO/BLIVECHAT/frontend/dist; + # 如果文件不存在,交给前端路由 + try_files $uri $uri/ /index.html; + } + # 动态API + location = /server_info { + proxy_pass http://blivechat; + } + # websocket + location = /chat { + proxy_pass http://blivechat; + + # 代理websocket必须设置 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + # 由于这个块有proxy_set_header,这些不会自动继承 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` From 127344802d4b0a2ec995c71dc736f0929e2f829b Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sun, 16 Aug 2020 12:13:53 +0800 Subject: [PATCH 05/18] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E7=BF=BB=E8=AF=91=E7=9A=84=E6=88=BF=E9=97=B4?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/chat.py | 132 ++++++++++++++++++++++++++++++------------------ config.py | 21 ++++++-- data/config.ini | 4 ++ 3 files changed, 103 insertions(+), 54 deletions(-) diff --git a/api/chat.py b/api/chat.py index 6dd152af..befab3b2 100644 --- a/api/chat.py +++ b/api/chat.py @@ -149,34 +149,21 @@ async def __on_receive_danmaku(self, danmaku: blivedm.DanmakuMessage): id_ = uuid.uuid4().hex # 为了节省带宽用list而不是dict - self.send_message(Command.ADD_TEXT, [ - # 0: avatarUrl + self.send_message(Command.ADD_TEXT, make_text_message( await models.avatar.get_avatar_url(danmaku.uid), - # 1: timestamp int(danmaku.timestamp / 1000), - # 2: authorName danmaku.uname, - # 3: authorType author_type, - # 4: content danmaku.msg, - # 5: privilegeType danmaku.privilege_type, - # 6: isGiftDanmaku - 1 if danmaku.msg_type else 0, - # 7: authorLevel + danmaku.msg_type, danmaku.user_level, - # 8: isNewbie - 1 if danmaku.urank < 10000 else 0, - # 9: isMobileVerified - 1 if danmaku.mobile_verify else 0, - # 10: medalLevel + danmaku.urank < 10000, + danmaku.mobile_verify, 0 if danmaku.room_id != self.room_id else danmaku.medal_level, - # 11: id id_, - # 12: translation translation - ]) + )) if need_translate: await self._translate_and_response(danmaku.msg, id_) @@ -245,8 +232,10 @@ async def _on_super_chat_delete(self, message: blivedm.SuperChatDeleteMessage): }) def _need_translate(self, text): + cfg = config.get_config() return ( - config.get_config().enable_translate + cfg.enable_translate + and (not cfg.allow_translate_rooms or self.room_id in cfg.allow_translate_rooms) and self.auto_translate_count > 0 and models.translate.need_translate(text) ) @@ -267,6 +256,39 @@ async def _translate_and_response(self, text, msg_id): ) +def make_text_message(avatar_url, timestamp, author_name, author_type, content, privilege_type, + is_gift_danmaku, author_level, is_newbie, is_mobile_verified, medal_level, + id_, translation): + return [ + # 0: avatarUrl + avatar_url, + # 1: timestamp + timestamp, + # 2: authorName + author_name, + # 3: authorType + author_type, + # 4: content + content, + # 5: privilegeType + privilege_type, + # 6: isGiftDanmaku + 1 if is_gift_danmaku else 0, + # 7: authorLevel + author_level, + # 8: isNewbie + 1 if is_newbie else 0, + # 9: isMobileVerified + 1 if is_mobile_verified else 0, + # 10: medalLevel + medal_level, + # 11: id + id_, + # 12: translation + translation + ] + + class RoomManager: def __init__(self): self._rooms: Dict[int, Room] = {} @@ -285,8 +307,7 @@ async def add_client(self, room_id, client: 'ChatHandler'): if client.auto_translate: room.auto_translate_count += 1 - if client.application.settings['debug']: - await client.send_test_message() + await client.on_join_room() def del_client(self, room_id, client: 'ChatHandler'): room = self._rooms.get(room_id, None) @@ -391,6 +412,41 @@ def check_origin(self, origin): return True return super().check_origin(origin) + @property + def has_joined_room(self): + return self.room_id is not None + + def send_message(self, cmd, data): + body = json.dumps({'cmd': cmd, 'data': data}) + try: + self.write_message(body) + except tornado.websocket.WebSocketClosedError: + self.on_close() + + async def on_join_room(self): + if self.application.settings['debug']: + await self.send_test_message() + + # 不允许自动翻译的提示 + if self.auto_translate: + cfg = config.get_config() + if cfg.allow_translate_rooms and self.room_id not in cfg.allow_translate_rooms: + self.send_message(Command.ADD_TEXT, make_text_message( + models.avatar.DEFAULT_AVATAR_URL, + int(time.time()), + 'blivechat', + 2, + 'Translation is not allowed in this room, please download to use translation', + 0, + False, + 60, + False, + True, + 0, + uuid.uuid4().hex, + '' + )) + # 测试用 async def send_test_message(self): base_data = { @@ -398,34 +454,21 @@ async def send_test_message(self): 'timestamp': int(time.time()), 'authorName': 'xfgryujk', } - text_data = [ - # 0: avatarUrl + text_data = make_text_message( base_data['avatarUrl'], - # 1: timestamp base_data['timestamp'], - # 2: authorName base_data['authorName'], - # 3: authorType 0, - # 4: content '我能吞下玻璃而不伤身体', - # 5: privilegeType - 0, - # 6: isGiftDanmaku 0, - # 7: authorLevel + False, 20, - # 8: isNewbie + False, + True, 0, - # 9: isMobileVerified - 1, - # 10: medalLevel - 0, - # 11: id uuid.uuid4().hex, - # 12: translation '' - ] + ) member_data = { **base_data, 'id': uuid.uuid4().hex, @@ -463,14 +506,3 @@ async def send_test_message(self): gift_data['totalCoin'] = 1245000 gift_data['giftName'] = '小电视飞船' self.send_message(Command.ADD_GIFT, gift_data) - - @property - def has_joined_room(self): - return self.room_id is not None - - def send_message(self, cmd, data): - body = json.dumps({'cmd': cmd, 'data': data}) - try: - self.write_message(body) - except tornado.websocket.WebSocketClosedError: - self.on_close() diff --git a/config.py b/config.py index 47f348fd..3c540f3d 100644 --- a/config.py +++ b/config.py @@ -13,14 +13,20 @@ def init(): - reload() + if reload(): + return + logger.warning('Using default config') + global _config + _config = AppConfig() def reload(): config = AppConfig() - if config.load(CONFIG_PATH): - global _config - _config = config + if not config.load(CONFIG_PATH): + return False + global _config + _config = config + return True def get_config(): @@ -31,6 +37,7 @@ class AppConfig: def __init__(self): self.database_url = 'sqlite:///data/database.db' self.enable_translate = True + self.allow_translate_rooms = {} def load(self, path): config = configparser.ConfigParser() @@ -39,6 +46,12 @@ def load(self, path): app_section = config['app'] self.database_url = app_section['database_url'] self.enable_translate = app_section.getboolean('enable_translate') + allow_translate_rooms = app_section['allow_translate_rooms'].strip() + if allow_translate_rooms == '': + self.allow_translate_rooms = {} + else: + allow_translate_rooms = allow_translate_rooms.split(',') + self.allow_translate_rooms = set(map(lambda id_: int(id_.strip()), allow_translate_rooms)) except (KeyError, ValueError): logger.exception('Failed to load config:') return False diff --git a/data/config.ini b/data/config.ini index 8e6edd67..3091bcac 100644 --- a/data/config.ini +++ b/data/config.ini @@ -3,9 +3,13 @@ database_url = sqlite:///data/database.db # Enable auto translate to Japanese enable_translate = true +# Comma separated room IDs in which translation are not allowed. If empty, all are allowed +# Example: allow_translate_rooms = 4895312,22347054,21693691 +allow_translate_rooms = # DON'T modify this section [DEFAULT] database_url = sqlite:///data/database.db enable_translate = true +allow_translate_rooms = From d25a32beafba4b567a4c9cd3523be7d12a3399cc Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sun, 16 Aug 2020 12:15:01 +0800 Subject: [PATCH 06/18] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=89=8C=E5=AD=90?= =?UTF-8?q?=E7=AD=89=E7=BA=A7=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/Home.vue | 2 +- models/translate.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue index 23c57918..a05496bc 100644 --- a/frontend/src/views/Home.vue +++ b/frontend/src/views/Home.vue @@ -53,7 +53,7 @@ <el-input v-model="form.blockUsers" type="textarea" :rows="5" :placeholder="$t('home.onePerLine')"></el-input> </el-form-item> <el-form-item :label="$t('home.blockMedalLevel')"> - <el-slider v-model="form.blockMedalLevel" show-input :min="0" :max="20"></el-slider> + <el-slider v-model="form.blockMedalLevel" show-input :min="0" :max="40"></el-slider> </el-form-item> </el-tab-pane> diff --git a/models/translate.py b/models/translate.py index 205e0d0f..19d340b2 100644 --- a/models/translate.py +++ b/models/translate.py @@ -17,7 +17,7 @@ NO_TRANSLATE_TEXTS = { '草', '草草', '草草草', '草生', '大草原', '上手', '上手上手', '理解', '理解理解', '天才', '天才天才', - '强', '余裕', '余裕余裕', '大丈夫', '再放送', '放送事故' + '强', '余裕', '余裕余裕', '大丈夫', '再放送', '放送事故', '清楚', '清楚清楚' } _main_event_loop = asyncio.get_event_loop() From bfa605489909864a9ada9447f47e8179f265439c Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Tue, 18 Aug 2020 21:48:33 +0800 Subject: [PATCH 07/18] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9A=84=E6=88=BF?= =?UTF-8?q?=E9=97=B4URL=E6=94=B9=E6=88=90=E5=8A=A0=E8=BD=BD=E5=99=A8URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/main.py | 12 +++++++++--- frontend/src/views/Home.vue | 10 +++++++++- main.py | 4 +--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/api/main.py b/api/main.py index 1846619f..bbe2716a 100644 --- a/api/main.py +++ b/api/main.py @@ -8,9 +8,15 @@ class MainHandler(tornado.web.StaticFileHandler): - """为了使用Vue Router的history模式,把所有请求转发到index.html""" - async def get(self, *args, **kwargs): - await super().get('index.html', *args, **kwargs) + """为了使用Vue Router的history模式,把不存在的文件请求转发到index.html""" + async def get(self, path, include_body=True): + try: + await super().get(path, include_body) + except tornado.web.HTTPError as e: + if e.status_code != 404: + raise + # 不存在的文件请求转发到index.html,交给前端路由 + await super().get('index.html', include_body) # noinspection PyAbstractClass diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue index a05496bc..c31b9e08 100644 --- a/frontend/src/views/Home.vue +++ b/frontend/src/views/Home.vue @@ -66,7 +66,7 @@ <el-divider></el-divider> <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-input ref="roomUrlInput" readonly :value="loaderUrl" 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> @@ -107,6 +107,14 @@ export default { 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}` + }, + loaderUrl() { + if (this.roomUrl === '') { + return '' + } + let url = new URL('https://xfgryujk.sinacloud.net/blivechat/loader.html') + url.searchParams.append('url', this.roomUrl) + return url.href } }, watch: { diff --git a/main.py b/main.py index ad7db0ea..1190e6ba 100644 --- a/main.py +++ b/main.py @@ -24,9 +24,7 @@ (r'/server_info', api.main.ServerInfoHandler), (r'/chat', api.chat.ChatHandler), - (r'/((css|fonts|img|js|static)/.*)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}), - (r'/(favicon\.ico)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}), - (r'/.*', api.main.MainHandler, {'path': WEB_ROOT}) + (r'/(.*)', api.main.MainHandler, {'path': WEB_ROOT, 'default_filename': 'index.html'}) ] From 4e7cc7a782b4b085fe9735a5f4ef2d9d8e22051e Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Wed, 19 Aug 2020 22:55:54 +0800 Subject: [PATCH 08/18] =?UTF-8?q?=E6=9B=B4=E6=96=B0nginx=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9877b7d1..60a50fe5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ **注意事项:** -* 应该先启动blivechat后启动OBS,否则网页会加载失败,这时应该刷新OBS的浏览器源,显示Loaded则加载成功 * 本地使用时不要关闭blivechat.exe那个黑框,否则不能继续获取弹幕 * 样式生成器没有列出所有本地字体,但是可以手动输入本地字体 @@ -75,6 +74,7 @@ ```conf upstream blivechat { + keepalive 8; # blivechat地址 server 127.0.0.1:12450; } @@ -96,12 +96,11 @@ server { # SSL ssl_certificate /PATH/TO/CERT.crt; ssl_certificate_key /PATH/TO/CERT_KEY.key; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; - ssl_prefer_server_ciphers on; # 代理header + proxy_http_version 1.1; proxy_set_header Host $host; + proxy_set_header Connection ""; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -120,10 +119,9 @@ server { proxy_pass http://blivechat; # 代理websocket必须设置 - proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; - + # 由于这个块有proxy_set_header,这些不会自动继承 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; From 7cfa94c68c761874762f3fa0a9eb785631b1fff2 Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Wed, 19 Aug 2020 23:16:01 +0800 Subject: [PATCH 09/18] =?UTF-8?q?=E6=94=B9API=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- frontend/src/views/Home.vue | 2 +- frontend/src/views/Room.vue | 2 +- main.py | 4 ++++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 60a50fe5..987d8be0 100644 --- a/README.md +++ b/README.md @@ -111,11 +111,11 @@ server { try_files $uri $uri/ /index.html; } # 动态API - location = /server_info { + location /api { proxy_pass http://blivechat; } # websocket - location = /chat { + location = /api/chat { proxy_pass http://blivechat; # 代理websocket必须设置 diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue index c31b9e08..a6a91606 100644 --- a/frontend/src/views/Home.vue +++ b/frontend/src/views/Home.vue @@ -129,7 +129,7 @@ export default { methods: { async updateServerConfig() { try { - this.serverConfig = (await axios.get(`/server_info`)).data.config + this.serverConfig = (await axios.get('/api/server_info')).data.config } catch (e) { this.$message.error('Failed to fetch server information: ' + e) } diff --git a/frontend/src/views/Room.vue b/frontend/src/views/Room.vue index 77f35aeb..f31c7d72 100644 --- a/frontend/src/views/Room.vue +++ b/frontend/src/views/Room.vue @@ -84,7 +84,7 @@ export default { const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws' // 开发时使用localhost:12450 const host = process.env.NODE_ENV === 'development' ? 'localhost:12450' : window.location.host - const url = `${protocol}://${host}/chat` + const url = `${protocol}://${host}/api/chat` this.websocket = new WebSocket(url) this.websocket.onopen = this.onWsOpen this.websocket.onclose = this.onWsClose diff --git a/main.py b/main.py index 1190e6ba..190544a2 100644 --- a/main.py +++ b/main.py @@ -21,6 +21,10 @@ WEB_ROOT = os.path.join(os.path.dirname(__file__), 'frontend', 'dist') routes = [ + (r'/api/server_info', api.main.ServerInfoHandler), + (r'/api/chat', api.chat.ChatHandler), + + # TODO 兼容旧版,下版本移除 (r'/server_info', api.main.ServerInfoHandler), (r'/chat', api.chat.ChatHandler), From 38774252cca28338f67beab63557e219e8b6631d Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Wed, 19 Aug 2020 23:17:29 +0800 Subject: [PATCH 10/18] =?UTF-8?q?=E6=9B=B4=E6=96=B0blivedm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blivedm b/blivedm index 6618bf5c..4c64c1bd 160000 --- a/blivedm +++ b/blivedm @@ -1 +1 @@ -Subproject commit 6618bf5c484cbe5a6f7e32f8e5d65ed0b74849c7 +Subproject commit 4c64c1bd1e9fe634894d7b781eab1fef0e753907 From 1252e1942d899f3de5db905cecd77129ebeb2ae5 Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sat, 22 Aug 2020 18:38:52 +0800 Subject: [PATCH 11/18] =?UTF-8?q?=E6=B7=BB=E5=8A=A0tornado=20xheaders?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/chat.py | 2 +- config.py | 10 ++++++++-- data/config.ini | 6 ++++++ main.py | 7 ++++++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/api/chat.py b/api/chat.py index befab3b2..9ea1ad77 100644 --- a/api/chat.py +++ b/api/chat.py @@ -436,7 +436,7 @@ async def on_join_room(self): int(time.time()), 'blivechat', 2, - 'Translation is not allowed in this room, please download to use translation', + 'Translation is not allowed in this room. Please download to use translation', 0, False, 60, diff --git a/config.py b/config.py index 3c540f3d..2e0f60a0 100644 --- a/config.py +++ b/config.py @@ -38,20 +38,26 @@ def __init__(self): self.database_url = 'sqlite:///data/database.db' self.enable_translate = True self.allow_translate_rooms = {} + self.tornado_xheaders = False def load(self, path): - config = configparser.ConfigParser() - config.read(path) try: + config = configparser.ConfigParser() + config.read(path) + app_section = config['app'] self.database_url = app_section['database_url'] self.enable_translate = app_section.getboolean('enable_translate') + allow_translate_rooms = app_section['allow_translate_rooms'].strip() if allow_translate_rooms == '': self.allow_translate_rooms = {} else: allow_translate_rooms = allow_translate_rooms.split(',') self.allow_translate_rooms = set(map(lambda id_: int(id_.strip()), allow_translate_rooms)) + + self.tornado_xheaders = app_section.getboolean('tornado_xheaders') + except (KeyError, ValueError): logger.exception('Failed to load config:') return False diff --git a/data/config.ini b/data/config.ini index 3091bcac..0a4c7040 100644 --- a/data/config.ini +++ b/data/config.ini @@ -1,15 +1,21 @@ [app] # See https://docs.sqlalchemy.org/en/13/core/engines.html#database-urls database_url = sqlite:///data/database.db + # Enable auto translate to Japanese enable_translate = true + # Comma separated room IDs in which translation are not allowed. If empty, all are allowed # Example: allow_translate_rooms = 4895312,22347054,21693691 allow_translate_rooms = +# Set to true if you are using a reverse proxy server such as nginx +tornado_xheaders = false + # DON'T modify this section [DEFAULT] database_url = sqlite:///data/database.db enable_translate = true allow_translate_rooms = +tornado_xheaders = false diff --git a/main.py b/main.py index 190544a2..6bb7dad5 100644 --- a/main.py +++ b/main.py @@ -70,8 +70,13 @@ def run_server(host, port, debug): debug=debug, autoreload=False ) + cfg = config.get_config() try: - app.listen(port, host) + app.listen( + port, + host, + xheaders=cfg.tornado_xheaders + ) except OSError: logger.warning('Address is used %s:%d', host, port) return From 28b04f27eefeb92d8534feb8c6e55d64546e53bb Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sat, 29 Aug 2020 14:27:15 +0800 Subject: [PATCH 12/18] =?UTF-8?q?=E6=94=B9=E4=B8=80=E4=BA=9B=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/config.js | 2 +- frontend/src/views/StyleGenerator/stylegen.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/api/config.js b/frontend/src/api/config.js index 96c1c7e8..a4a44a2b 100644 --- a/frontend/src/api/config.js +++ b/frontend/src/api/config.js @@ -5,7 +5,7 @@ export const DEFAULT_CONFIG = { showDanmaku: true, showGift: true, showGiftName: false, - mergeSimilarDanmaku: true, + mergeSimilarDanmaku: false, mergeGift: true, maxNumber: 60, diff --git a/frontend/src/views/StyleGenerator/stylegen.js b/frontend/src/views/StyleGenerator/stylegen.js index 5e96ab03..9af34825 100644 --- a/frontend/src/views/StyleGenerator/stylegen.js +++ b/frontend/src/views/StyleGenerator/stylegen.js @@ -17,7 +17,7 @@ export const DEFAULT_CONFIG = { ownerUserNameColor: '#ffd600', moderatorUserNameColor: '#5e84f1', memberUserNameColor: '#0f9d58', - showBadges: false, + showBadges: true, showColon: true, messageFont: 'Imprima', From 4d58245ad98e9e1615183d4a1befa897e3774960 Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sun, 30 Aug 2020 17:46:04 +0800 Subject: [PATCH 13/18] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E5=99=A8URL=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/main.py | 3 ++- config.py | 4 +++- data/config.ini | 4 ++++ frontend/src/views/Home.vue | 12 ++++++++---- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/api/main.py b/api/main.py index bbe2716a..a24ef521 100644 --- a/api/main.py +++ b/api/main.py @@ -26,6 +26,7 @@ async def get(self): self.write({ 'version': update.VERSION, 'config': { - 'enableTranslate': cfg.enable_translate + 'enableTranslate': cfg.enable_translate, + 'loaderUrl': cfg.loader_url } }) diff --git a/config.py b/config.py index 2e0f60a0..ec3dc925 100644 --- a/config.py +++ b/config.py @@ -39,6 +39,7 @@ def __init__(self): self.enable_translate = True self.allow_translate_rooms = {} self.tornado_xheaders = False + self.loader_url = '' def load(self, path): try: @@ -49,7 +50,7 @@ def load(self, path): self.database_url = app_section['database_url'] self.enable_translate = app_section.getboolean('enable_translate') - allow_translate_rooms = app_section['allow_translate_rooms'].strip() + allow_translate_rooms = app_section['allow_translate_rooms'] if allow_translate_rooms == '': self.allow_translate_rooms = {} else: @@ -57,6 +58,7 @@ def load(self, path): self.allow_translate_rooms = set(map(lambda id_: int(id_.strip()), allow_translate_rooms)) self.tornado_xheaders = app_section.getboolean('tornado_xheaders') + self.loader_url = app_section['loader_url'] except (KeyError, ValueError): logger.exception('Failed to load config:') diff --git a/data/config.ini b/data/config.ini index 0a4c7040..b0df8cea 100644 --- a/data/config.ini +++ b/data/config.ini @@ -12,6 +12,9 @@ allow_translate_rooms = # Set to true if you are using a reverse proxy server such as nginx tornado_xheaders = false +# Use a loader so that you can run OBS before blivechat. If empty, no loader is used +loader_url = https://xfgryujk.sinacloud.net/blivechat/loader.html + # DON'T modify this section [DEFAULT] @@ -19,3 +22,4 @@ database_url = sqlite:///data/database.db enable_translate = true allow_translate_rooms = tornado_xheaders = false +loader_url = diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue index a6a91606..8ce59d3d 100644 --- a/frontend/src/views/Home.vue +++ b/frontend/src/views/Home.vue @@ -66,7 +66,7 @@ <el-divider></el-divider> <el-form-item :label="$t('home.roomUrl')"> - <el-input ref="roomUrlInput" readonly :value="loaderUrl" style="width: calc(100% - 8em); margin-right: 1em;"></el-input> + <el-input ref="roomUrlInput" readonly :value="obsRoomUrl" 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> @@ -90,7 +90,8 @@ export default { data() { return { serverConfig: { - enableTranslate: true + enableTranslate: true, + loaderUrl: '' }, form: { roomId: parseInt(window.localStorage.roomId || '1'), @@ -108,11 +109,14 @@ export default { let resolved = this.$router.resolve({name: 'room', params: {roomId: this.form.roomId}, query}) return `${window.location.protocol}//${window.location.host}${resolved.href}` }, - loaderUrl() { + obsRoomUrl() { if (this.roomUrl === '') { return '' } - let url = new URL('https://xfgryujk.sinacloud.net/blivechat/loader.html') + if (this.serverConfig.loaderUrl === '') { + return this.roomUrl + } + let url = new URL(this.serverConfig.loaderUrl) url.searchParams.append('url', this.roomUrl) return url.href } From ca55f9917da8d657825bd2b46255be32421747a6 Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Thu, 3 Sep 2020 20:01:54 +0800 Subject: [PATCH 14/18] =?UTF-8?q?=E6=97=A5=E5=BF=97=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E5=88=B0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- log/.gitkeep | 0 main.py | 14 +++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 log/.gitkeep diff --git a/log/.gitkeep b/log/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/main.py b/main.py index 6bb7dad5..d6d2641b 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ import argparse import logging +import logging.handlers import os import webbrowser @@ -18,7 +19,9 @@ logger = logging.getLogger(__name__) -WEB_ROOT = os.path.join(os.path.dirname(__file__), 'frontend', 'dist') +BASE_PATH = os.path.dirname(os.path.realpath(__file__)) +WEB_ROOT = os.path.join(BASE_PATH, 'frontend', 'dist') +LOG_FILE_NAME = os.path.join(BASE_PATH, 'log', 'blivechat.log') routes = [ (r'/api/server_info', api.main.ServerInfoHandler), @@ -47,7 +50,7 @@ def main(): def parse_args(): - parser = argparse.ArgumentParser(description='用于OBS的仿YouTube风格的bilibili直播聊天层') + parser = argparse.ArgumentParser(description='用于OBS的仿YouTube风格的bilibili直播评论栏') parser.add_argument('--host', help='服务器host,默认为127.0.0.1', default='127.0.0.1') parser.add_argument('--port', help='服务器端口,默认为12450', type=int, default=12450) parser.add_argument('--debug', help='调试模式', action='store_true') @@ -55,11 +58,16 @@ def parse_args(): def init_logging(debug): + stream_handler = logging.StreamHandler() + file_handler = logging.handlers.TimedRotatingFileHandler( + LOG_FILE_NAME, encoding='utf-8', when='midnight', backupCount=7, delay=True + ) logging.basicConfig( format='{asctime} {levelname} [{name}]: {message}', datefmt='%Y-%m-%d %H:%M:%S', style='{', - level=logging.INFO if not debug else logging.DEBUG + level=logging.INFO if not debug else logging.DEBUG, + handlers=[stream_handler, file_handler] ) From 626099f835bbc70682794d357490548c8acde92d Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sat, 5 Sep 2020 08:45:19 +0800 Subject: [PATCH 15/18] =?UTF-8?q?docker=E6=9A=B4=E9=9C=B2=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 7 ++++++- Dockerfile | 16 +++++++++------- README.md | 6 +++++- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.dockerignore b/.dockerignore index 7549a713..ab6ea41b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,10 +7,15 @@ build/ **/node_modules/ # IDEs and editors -/.idea +.idea/ # misc **/.git* *.spec screenshots/ README.md + +# runtime data +data/* +!data/config.ini +log/* diff --git a/Dockerfile b/Dockerfile index 58889d71..82bc1a22 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,20 +14,22 @@ RUN wget https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.xz \ && ln -s /node-v10.16.0-linux-x64/bin/npm /usr/local/bin/npm # 后端依赖 -COPY requirements.txt /blivechat/ -RUN pip3 install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r /blivechat/requirements.txt +WORKDIR /blivechat +COPY requirements.txt ./ +RUN pip3 install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt # 前端依赖 -WORKDIR /blivechat/frontend -COPY frontend/package*.json ./ +WORKDIR ./frontend +COPY frontend/package.json frontend/package-lock.json ./ RUN npm i --registry=https://registry.npm.taobao.org -# 编译 -COPY . /blivechat +# 编译前端 +COPY . ../ RUN npm run build # 运行 -WORKDIR /blivechat +WORKDIR .. +VOLUME /blivechat/data /blivechat/log /blivechat/frontend/dist EXPOSE 12450 ENTRYPOINT ["python3", "main.py"] CMD ["--host", "0.0.0.0", "--port", "12450"] diff --git a/README.md b/README.md index 987d8be0..7c34d926 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,11 @@ ### 四、Docker(自建服务器) 1. ```sh - docker run -d -p 12450:12450 xfgryujk/blivechat:latest + docker run --name blivechat -d -p 12450:12450 \ + --mount source=blc-data,target=/blivechat/data \ + --mount source=blc-log,target=/blivechat/log \ + --mount source=blc-frontend,target=/blivechat/frontend/dist \ + xfgryujk/blivechat:latest ``` 2. 用浏览器打开[http://localhost:12450](http://localhost:12450),以下略 From 7a244757d29bd69c3cb9e9a5b506b6df3947c3af Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sat, 5 Sep 2020 09:44:40 +0800 Subject: [PATCH 16/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8DOBS=2025.0.8=E4=B8=8D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=B8=AD=E6=96=87=E5=AD=97=E4=BD=93=E5=90=8D?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/StyleGenerator/stylegen.js | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/frontend/src/views/StyleGenerator/stylegen.js b/frontend/src/views/StyleGenerator/stylegen.js index 9af34825..6155afd4 100644 --- a/frontend/src/views/StyleGenerator/stylegen.js +++ b/frontend/src/views/StyleGenerator/stylegen.js @@ -108,7 +108,7 @@ yt-live-chat-author-chip #author-name { /* Outlines */ yt-live-chat-renderer * { ${getShowOutlinesStyle(config)} - font-family: "${config.messageFont}"${FALLBACK_FONTS}; + font-family: "${cssEscapeStr(config.messageFont)}"${FALLBACK_FONTS}; font-size: ${config.messageFontSize}px !important; line-height: ${config.messageLineHeight || config.messageFontSize}px !important; } @@ -162,7 +162,7 @@ yt-live-chat-text-message-renderer #chat-badges { yt-live-chat-text-message-renderer #timestamp { display: ${config.showTime ? 'inline' : 'none'} !important; ${config.timeColor ? `color: ${config.timeColor} !important;` : ''} - font-family: "${config.timeFont}"${FALLBACK_FONTS}; + font-family: "${cssEscapeStr(config.timeFont)}"${FALLBACK_FONTS}; font-size: ${config.timeFontSize}px !important; line-height: ${config.timeLineHeight || config.timeFontSize}px !important; } @@ -187,7 +187,7 @@ yt-live-chat-text-message-renderer yt-live-chat-author-badge-renderer[type="memb yt-live-chat-text-message-renderer #author-name { ${config.showUserNames ? '' : 'display: none !important;'} ${config.userNameColor ? `color: ${config.userNameColor} !important;` : ''} - font-family: "${config.userNameFont}"${FALLBACK_FONTS}; + font-family: "${cssEscapeStr(config.userNameFont)}"${FALLBACK_FONTS}; font-size: ${config.userNameFontSize}px !important; line-height: ${config.userNameLineHeight || config.userNameFontSize}px !important; } @@ -198,7 +198,7 @@ ${getShowColonStyle(config)} yt-live-chat-text-message-renderer #message, yt-live-chat-text-message-renderer #message * { ${config.messageColor ? `color: ${config.messageColor} !important;` : ''} - font-family: "${config.messageFont}"${FALLBACK_FONTS}; + font-family: "${cssEscapeStr(config.messageFont)}"${FALLBACK_FONTS}; font-size: ${config.messageFontSize}px !important; line-height: ${config.messageLineHeight || config.messageFontSize}px !important; } @@ -213,7 +213,7 @@ yt-live-chat-paid-message-renderer #author-name *, yt-live-chat-membership-item-renderer #header-content-inner-column, yt-live-chat-membership-item-renderer #header-content-inner-column * { ${config.firstLineColor ? `color: ${config.firstLineColor} !important;` : ''} - font-family: "${config.firstLineFont}"${FALLBACK_FONTS}; + font-family: "${cssEscapeStr(config.firstLineFont)}"${FALLBACK_FONTS}; font-size: ${config.firstLineFontSize}px !important; line-height: ${config.firstLineLineHeight || config.firstLineFontSize}px !important; } @@ -223,7 +223,7 @@ yt-live-chat-paid-message-renderer #purchase-amount *, yt-live-chat-membership-item-renderer #header-subtext, yt-live-chat-membership-item-renderer #header-subtext * { ${config.secondLineColor ? `color: ${config.secondLineColor} !important;` : ''} - font-family: "${config.secondLineFont}"${FALLBACK_FONTS}; + font-family: "${cssEscapeStr(config.secondLineFont)}"${FALLBACK_FONTS}; font-size: ${config.secondLineFontSize}px !important; line-height: ${config.secondLineLineHeight || config.secondLineFontSize}px !important; } @@ -231,7 +231,7 @@ yt-live-chat-membership-item-renderer #header-subtext * { yt-live-chat-paid-message-renderer #content, yt-live-chat-paid-message-renderer #content * { ${config.scContentColor ? `color: ${config.scContentColor} !important;` : ''} - font-family: "${config.scContentFont}"${FALLBACK_FONTS}; + font-family: "${cssEscapeStr(config.scContentFont)}"${FALLBACK_FONTS}; font-size: ${config.scContentFontSize}px !important; line-height: ${config.scContentLineHeight || config.scContentFontSize}px !important; } @@ -273,7 +273,7 @@ yt-live-chat-ticker-paid-message-item-renderer *, yt-live-chat-ticker-sponsor-item-renderer, yt-live-chat-ticker-sponsor-item-renderer * { ${config.secondLineColor ? `color: ${config.secondLineColor} !important;` : ''} - font-family: "${config.secondLineFont}"${FALLBACK_FONTS}; + font-family: "${cssEscapeStr(config.secondLineFont)}"${FALLBACK_FONTS}; } yt-live-chat-mode-change-message-renderer, @@ -337,6 +337,31 @@ function getShowOutlinesStyle (config) { return `text-shadow: ${shadow.join(', ')};` } +function cssEscapeStr (str) { + let res = [] + for (let char of str) { + res.push(cssEscapeChar(char)) + } + return res.join('') +} + +function cssEscapeChar (char) { + if (!needEscapeChar(char)) { + return char + } + let hexCode = char.codePointAt(0).toString(16) + // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point + return `\\${hexCode} ` +} + +function needEscapeChar (char) { + let code = char.codePointAt(0) + if (0x20 <= code && code <= 0x7E) { + return char === '"' + } + return true +} + function getPaddingStyle (config) { return `padding-left: ${config.useBarsInsteadOfBg ? 20 : 4}px !important; padding-right: 4px !important;` From 152db9f00a48599d38ce176be03a049044d0d3ea Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sat, 5 Sep 2020 12:05:01 +0800 Subject: [PATCH 17/18] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E5=B9=B3=E6=BB=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ChatRenderer/index.vue | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/ChatRenderer/index.vue b/frontend/src/components/ChatRenderer/index.vue index dce06cf5..34674336 100644 --- a/frontend/src/components/ChatRenderer/index.vue +++ b/frontend/src/components/ChatRenderer/index.vue @@ -281,16 +281,25 @@ export default { if (this.estimatedEnqueueInterval) { estimatedNextEnqueueRemainTime = Math.max(this.lastEnqueueTime - new Date() + this.estimatedEnqueueInterval, 1) } - // 最快80ms/条,计算发送的消息数,保证在下次进队列之前消费完队列 + // 最快80ms/条,计算发送的消息数,保证在下次进队列之前消费队列到最多剩3条消息,不消费完是为了防止消息速度变慢时突然停顿 + const MIN_SLEEP_TIME = 80 + const MAX_SLEEP_TIME = 1000 + const MAX_REMAIN_GROUP_NUM = 3 + // 下次进队列之前应该发多少条消息 + let shouldEmitGroupNum = Math.max(this.smoothedMessageQueue.length - MAX_REMAIN_GROUP_NUM, 0) + // 下次进队列之前最多能发多少次 + let maxCanEmitCount = estimatedNextEnqueueRemainTime / MIN_SLEEP_TIME + // 这次发多少条消息 let groupNumToEmit - if (this.smoothedMessageQueue.length < estimatedNextEnqueueRemainTime / 80) { - // 队列中消息数很少,每次发1条也能发完 + if (shouldEmitGroupNum < maxCanEmitCount) { + // 队列中消息数很少,每次发1条也能发到最多剩3条 groupNumToEmit = 1 } else { - // 每次发1条以上,保证按最快速度能发完 - groupNumToEmit = Math.ceil(this.smoothedMessageQueue.length / (estimatedNextEnqueueRemainTime / 80)) + // 每次发1条以上,保证按最快速度能发到最多剩3条 + groupNumToEmit = Math.ceil(shouldEmitGroupNum / maxCanEmitCount) } + // 发消息 let messageGroups = this.smoothedMessageQueue.splice(0, groupNumToEmit) let mergedGroup = [] for (let messageGroup of messageGroups) { @@ -303,19 +312,20 @@ export default { if (this.smoothedMessageQueue.length <= 0) { return } + // 消息没发完,计算下次发消息时间 let sleepTime - if (groupNumToEmit == 1) { - // 队列中消息数很少,随便定个80-1000ms的时间 + if (groupNumToEmit === 1) { + // 队列中消息数很少,随便定个[MIN_SLEEP_TIME, MAX_SLEEP_TIME]的时间 sleepTime = estimatedNextEnqueueRemainTime / this.smoothedMessageQueue.length sleepTime *= 0.5 + Math.random() - if (sleepTime > 1000) { - sleepTime = 1000 - } else if (sleepTime < 80) { - sleepTime = 80 + if (sleepTime > MAX_SLEEP_TIME) { + sleepTime = MAX_SLEEP_TIME + } else if (sleepTime < MIN_SLEEP_TIME) { + sleepTime = MIN_SLEEP_TIME } } else { // 按最快速度发 - sleepTime = 80 + sleepTime = MIN_SLEEP_TIME } this.emitSmoothedMessageTimerId = window.setTimeout(this.emitSmoothedMessages, sleepTime) }, From 47d7528b61f6908ea784e47ef3961c7cf888f03d Mon Sep 17 00:00:00 2001 From: John Smith <xfgryujk@126.com> Date: Sat, 5 Sep 2020 12:08:17 +0800 Subject: [PATCH 18/18] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7v1.4.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/layout/index.vue | 2 +- update.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue index 5f371956..153c22a9 100644 --- a/frontend/src/layout/index.vue +++ b/frontend/src/layout/index.vue @@ -9,7 +9,7 @@ </router-link> </div> <div class="version"> - v1.4.4 + v1.4.5 </div> <sidebar></sidebar> </el-aside> diff --git a/update.py b/update.py index c3238478..6d66c6f8 100644 --- a/update.py +++ b/update.py @@ -4,7 +4,7 @@ import aiohttp -VERSION = 'v1.4.4' +VERSION = 'v1.4.5' def check_update():