mirror of
https://github.com/xfgryujk/blivechat.git
synced 2025-03-13 03:10:47 +08:00
Merge branch 'dev'
This commit is contained in:
commit
cb150effbb
@ -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/*
|
||||
|
16
Dockerfile
16
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"]
|
||||
|
88
README.md
88
README.md
@ -16,7 +16,7 @@
|
||||
* 支持自动翻译弹幕、醒目留言到日语
|
||||
|
||||
## 使用方法
|
||||
### 本地使用
|
||||
### 一、本地使用
|
||||
1. 下载[发布版](https://github.com/xfgryujk/blivechat/releases)(仅提供x64 Windows版)
|
||||
2. 双击`blivechat.exe`运行服务器,或者用命令行可以指定host和端口号:
|
||||
```bat
|
||||
@ -28,18 +28,25 @@
|
||||
|
||||
**注意事项:**
|
||||
|
||||
* 应该先启动blivechat后启动OBS,否则网页会加载失败,这时应该刷新OBS的浏览器源,显示Loaded则加载成功
|
||||
* 本地使用时不要关闭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 +63,73 @@
|
||||
```
|
||||
3. 用浏览器打开[http://localhost:12450](http://localhost:12450),以下略
|
||||
|
||||
### Docker
|
||||
### 四、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),以下略
|
||||
|
||||
### nginx配置(可选)
|
||||
自建服务器时使用,`sudo vim /etc/nginx/sites-enabled/blivechat.conf`
|
||||
|
||||
```conf
|
||||
upstream blivechat {
|
||||
keepalive 8;
|
||||
# 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;
|
||||
|
||||
# 代理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;
|
||||
|
||||
# 静态文件
|
||||
location / {
|
||||
root /PATH/TO/BLIVECHAT/frontend/dist;
|
||||
# 如果文件不存在,交给前端路由
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
# 动态API
|
||||
location /api {
|
||||
proxy_pass http://blivechat;
|
||||
}
|
||||
# websocket
|
||||
location = /api/chat {
|
||||
proxy_pass http://blivechat;
|
||||
|
||||
# 代理websocket必须设置
|
||||
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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
140
api/chat.py
140
api/chat.py
@ -71,7 +71,7 @@ class Room(blivedm.BLiveClient):
|
||||
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
|
||||
))
|
||||
|
||||
@ -149,34 +149,21 @@ class Room(blivedm.BLiveClient):
|
||||
|
||||
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_)
|
||||
@ -206,7 +193,8 @@ class Room(blivedm.BLiveClient):
|
||||
'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):
|
||||
@ -244,8 +232,10 @@ class Room(blivedm.BLiveClient):
|
||||
})
|
||||
|
||||
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)
|
||||
)
|
||||
@ -266,6 +256,39 @@ class Room(blivedm.BLiveClient):
|
||||
)
|
||||
|
||||
|
||||
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] = {}
|
||||
@ -284,8 +307,7 @@ class RoomManager:
|
||||
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)
|
||||
@ -390,6 +412,41 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
||||
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 = {
|
||||
@ -397,37 +454,25 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
||||
'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
|
||||
'id': uuid.uuid4().hex,
|
||||
'privilegeType': 3
|
||||
}
|
||||
gift_data = {
|
||||
**base_data,
|
||||
@ -461,14 +506,3 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
||||
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()
|
||||
|
15
api/main.py
15
api/main.py
@ -8,9 +8,15 @@ import update
|
||||
|
||||
|
||||
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
|
||||
@ -20,6 +26,7 @@ class ServerInfoHandler(api.base.ApiHandler):
|
||||
self.write({
|
||||
'version': update.VERSION,
|
||||
'config': {
|
||||
'enableTranslate': cfg.enable_translate
|
||||
'enableTranslate': cfg.enable_translate,
|
||||
'loaderUrl': cfg.loader_url
|
||||
}
|
||||
})
|
||||
|
2
blivedm
2
blivedm
@ -1 +1 @@
|
||||
Subproject commit d173228c5f83c2f5f94551259e0e6c01e929d92c
|
||||
Subproject commit 4c64c1bd1e9fe634894d7b781eab1fef0e753907
|
33
config.py
33
config.py
@ -13,14 +13,20 @@ _config: Optional['AppConfig'] = None
|
||||
|
||||
|
||||
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,14 +37,29 @@ class AppConfig:
|
||||
def __init__(self):
|
||||
self.database_url = 'sqlite:///data/database.db'
|
||||
self.enable_translate = True
|
||||
self.allow_translate_rooms = {}
|
||||
self.tornado_xheaders = False
|
||||
self.loader_url = ''
|
||||
|
||||
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']
|
||||
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')
|
||||
self.loader_url = app_section['loader_url']
|
||||
|
||||
except (KeyError, ValueError):
|
||||
logger.exception('Failed to load config:')
|
||||
return False
|
||||
|
@ -1,11 +1,25 @@
|
||||
[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
|
||||
|
||||
# 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]
|
||||
database_url = sqlite:///data/database.db
|
||||
enable_translate = true
|
||||
allow_translate_rooms =
|
||||
tornado_xheaders = false
|
||||
loader_url =
|
||||
|
@ -5,7 +5,7 @@ export const DEFAULT_CONFIG = {
|
||||
showDanmaku: true,
|
||||
showGift: true,
|
||||
showGiftName: false,
|
||||
mergeSimilarDanmaku: true,
|
||||
mergeSimilarDanmaku: false,
|
||||
mergeGift: true,
|
||||
maxNumber: 60,
|
||||
|
||||
|
@ -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>
|
421
frontend/src/components/ChatRenderer/MembershipItem.vue
Normal file
421
frontend/src/components/ChatRenderer/MembershipItem.vue
Normal file
@ -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>
|
@ -56,7 +56,7 @@ export default {
|
||||
return 'CN¥' + utils.formatCurrency(this.price)
|
||||
},
|
||||
timeText() {
|
||||
return utils.getTimeTextMinSec(this.time)
|
||||
return utils.getTimeTextHourMin(this.time)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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: {
|
||||
|
@ -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: {
|
||||
@ -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)
|
||||
},
|
||||
@ -628,6 +638,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 -->
|
||||
|
@ -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",
|
||||
@ -53,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)',
|
||||
|
@ -5,7 +5,6 @@ export default {
|
||||
help: 'ヘルプ',
|
||||
projectAddress: 'プロジェクトアドレス',
|
||||
giftRecordOfficial: '公式スーパーチャット記録',
|
||||
giftRecord: 'スーパーチャット記録'
|
||||
},
|
||||
home: {
|
||||
roomIdEmpty: 'ルームのIDを空白にすることはできません',
|
||||
@ -53,6 +52,7 @@ export default {
|
||||
avatarSize: 'アイコンのサイズ',
|
||||
|
||||
userNames: 'ユーザー名',
|
||||
showUserNames: 'ユーザー名を表示する',
|
||||
font: 'フォント',
|
||||
fontSize: 'フォントサイズ',
|
||||
lineHeight: '行の高さ(0はデフォルト)',
|
||||
|
@ -5,7 +5,6 @@ export default {
|
||||
help: '帮助',
|
||||
projectAddress: '项目地址',
|
||||
giftRecordOfficial: '官方打赏记录',
|
||||
giftRecord: '打赏记录'
|
||||
},
|
||||
home: {
|
||||
roomIdEmpty: '房间ID不能为空',
|
||||
@ -53,6 +52,7 @@ export default {
|
||||
avatarSize: '头像尺寸',
|
||||
|
||||
userNames: '用户名',
|
||||
showUserNames: '显示用户名',
|
||||
font: '字体',
|
||||
fontSize: '字体尺寸',
|
||||
lineHeight: '行高(0为默认)',
|
||||
|
@ -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
|
||||
|
@ -9,7 +9,7 @@
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="version">
|
||||
v1.4.4
|
||||
v1.4.5
|
||||
</div>
|
||||
<sidebar></sidebar>
|
||||
</el-aside>
|
||||
|
@ -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}`
|
||||
}
|
||||
|
@ -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>
|
||||
|
||||
@ -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="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'),
|
||||
@ -107,6 +108,17 @@ 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}`
|
||||
},
|
||||
obsRoomUrl() {
|
||||
if (this.roomUrl === '') {
|
||||
return ''
|
||||
}
|
||||
if (this.serverConfig.loaderUrl === '') {
|
||||
return this.roomUrl
|
||||
}
|
||||
let url = new URL(this.serverConfig.loaderUrl)
|
||||
url.searchParams.append('url', this.roomUrl)
|
||||
return url.href
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -121,7 +133,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)
|
||||
}
|
||||
|
@ -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
|
||||
@ -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:
|
||||
|
@ -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: '言いたいことがあるんだよ!'
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ export const DEFAULT_CONFIG = {
|
||||
showAvatars: true,
|
||||
avatarSize: 24,
|
||||
|
||||
showUserNames: true,
|
||||
userNameFont: 'Changa One',
|
||||
userNameFontSize: 20,
|
||||
userNameLineHeight: 0,
|
||||
@ -16,7 +17,7 @@ export const DEFAULT_CONFIG = {
|
||||
ownerUserNameColor: '#ffd600',
|
||||
moderatorUserNameColor: '#5e84f1',
|
||||
memberUserNameColor: '#0f9d58',
|
||||
showBadges: false,
|
||||
showBadges: true,
|
||||
showColon: true,
|
||||
|
||||
messageFont: 'Imprima',
|
||||
@ -107,13 +108,13 @@ 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}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;
|
||||
@ -166,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;
|
||||
}
|
||||
@ -189,8 +185,9 @@ 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-family: "${cssEscapeStr(config.userNameFont)}"${FALLBACK_FONTS};
|
||||
font-size: ${config.userNameFontSize}px !important;
|
||||
line-height: ${config.userNameLineHeight || config.userNameFontSize}px !important;
|
||||
}
|
||||
@ -201,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,20 +210,20 @@ ${!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-family: "${cssEscapeStr(config.firstLineFont)}"${FALLBACK_FONTS};
|
||||
font-size: ${config.firstLineFontSize}px !important;
|
||||
line-height: ${config.firstLineLineHeight || config.firstLineFontSize}px !important;
|
||||
}
|
||||
|
||||
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-family: "${cssEscapeStr(config.secondLineFont)}"${FALLBACK_FONTS};
|
||||
font-size: ${config.secondLineFontSize}px !important;
|
||||
line-height: ${config.secondLineLineHeight || config.secondLineFontSize}px !important;
|
||||
}
|
||||
@ -234,7 +231,7 @@ yt-live-chat-legacy-paid-message-renderer #detail-text * {
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -275,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,
|
||||
@ -339,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;`
|
||||
@ -399,7 +422,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;
|
||||
}`
|
||||
|
0
log/.gitkeep
Normal file
0
log/.gitkeep
Normal file
29
main.py
29
main.py
@ -2,6 +2,7 @@
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import webbrowser
|
||||
|
||||
@ -18,15 +19,19 @@ import update
|
||||
|
||||
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),
|
||||
(r'/api/chat', api.chat.ChatHandler),
|
||||
|
||||
# TODO 兼容旧版,下版本移除
|
||||
(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'})
|
||||
]
|
||||
|
||||
|
||||
@ -45,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')
|
||||
@ -53,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]
|
||||
)
|
||||
|
||||
|
||||
@ -68,8 +78,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
|
||||
|
@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
NO_TRANSLATE_TEXTS = {
|
||||
'草', '草草', '草草草', '草生', '大草原', '上手', '上手上手', '理解', '理解理解', '天才', '天才天才',
|
||||
'强', '余裕', '余裕余裕', '大丈夫', '再放送', '放送事故'
|
||||
'强', '余裕', '余裕余裕', '大丈夫', '再放送', '放送事故', '清楚', '清楚清楚'
|
||||
}
|
||||
|
||||
_main_event_loop = asyncio.get_event_loop()
|
||||
|
Loading…
Reference in New Issue
Block a user