mirror of
https://github.com/xfgryujk/blivechat.git
synced 2025-03-23 08:10:56 +08:00
Merge branch 'dev'
This commit is contained in:
commit
cb150effbb
@ -7,10 +7,15 @@ build/
|
|||||||
**/node_modules/
|
**/node_modules/
|
||||||
|
|
||||||
# IDEs and editors
|
# IDEs and editors
|
||||||
/.idea
|
.idea/
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
**/.git*
|
**/.git*
|
||||||
*.spec
|
*.spec
|
||||||
screenshots/
|
screenshots/
|
||||||
README.md
|
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
|
&& ln -s /node-v10.16.0-linux-x64/bin/npm /usr/local/bin/npm
|
||||||
|
|
||||||
# 后端依赖
|
# 后端依赖
|
||||||
COPY requirements.txt /blivechat/
|
WORKDIR /blivechat
|
||||||
RUN pip3 install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r /blivechat/requirements.txt
|
COPY requirements.txt ./
|
||||||
|
RUN pip3 install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
|
||||||
|
|
||||||
# 前端依赖
|
# 前端依赖
|
||||||
WORKDIR /blivechat/frontend
|
WORKDIR ./frontend
|
||||||
COPY frontend/package*.json ./
|
COPY frontend/package.json frontend/package-lock.json ./
|
||||||
RUN npm i --registry=https://registry.npm.taobao.org
|
RUN npm i --registry=https://registry.npm.taobao.org
|
||||||
|
|
||||||
# 编译
|
# 编译前端
|
||||||
COPY . /blivechat
|
COPY . ../
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# 运行
|
# 运行
|
||||||
WORKDIR /blivechat
|
WORKDIR ..
|
||||||
|
VOLUME /blivechat/data /blivechat/log /blivechat/frontend/dist
|
||||||
EXPOSE 12450
|
EXPOSE 12450
|
||||||
ENTRYPOINT ["python3", "main.py"]
|
ENTRYPOINT ["python3", "main.py"]
|
||||||
CMD ["--host", "0.0.0.0", "--port", "12450"]
|
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版)
|
1. 下载[发布版](https://github.com/xfgryujk/blivechat/releases)(仅提供x64 Windows版)
|
||||||
2. 双击`blivechat.exe`运行服务器,或者用命令行可以指定host和端口号:
|
2. 双击`blivechat.exe`运行服务器,或者用命令行可以指定host和端口号:
|
||||||
```bat
|
```bat
|
||||||
@ -28,18 +28,25 @@
|
|||||||
|
|
||||||
**注意事项:**
|
**注意事项:**
|
||||||
|
|
||||||
* 应该先启动blivechat后启动OBS,否则网页会加载失败,这时应该刷新OBS的浏览器源,显示Loaded则加载成功
|
|
||||||
* 本地使用时不要关闭blivechat.exe那个黑框,否则不能继续获取弹幕
|
* 本地使用时不要关闭blivechat.exe那个黑框,否则不能继续获取弹幕
|
||||||
* 样式生成器没有列出所有本地字体,但是可以手动输入本地字体
|
* 样式生成器没有列出所有本地字体,但是可以手动输入本地字体
|
||||||
|
|
||||||
### 公共服务器
|
### 二、公共服务器
|
||||||
请优先在本地使用,使用公共服务器会有更大的弹幕延迟,而且服务器故障时可能出现直播事故
|
请优先在本地使用,使用公共服务器会有更大的弹幕延迟,而且服务器故障时可能发生直播事故
|
||||||
|
|
||||||
* [第三方公共服务器](http://chat.bilisc.com/)
|
* [第三方公共服务器](http://chat.bilisc.com/)
|
||||||
* [仅样式生成器](https://style.vtbs.moe/)
|
* [仅样式生成器](https://style.vtbs.moe/)
|
||||||
|
|
||||||
### 源代码版
|
### 三、源代码版(自建服务器或在Windows以外平台)
|
||||||
1. 编译前端(需要安装Node.js和npm):
|
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
|
```sh
|
||||||
cd frontend
|
cd frontend
|
||||||
npm i
|
npm i
|
||||||
@ -56,8 +63,73 @@
|
|||||||
```
|
```
|
||||||
3. 用浏览器打开[http://localhost:12450](http://localhost:12450),以下略
|
3. 用浏览器打开[http://localhost:12450](http://localhost:12450),以下略
|
||||||
|
|
||||||
### Docker
|
### 四、Docker(自建服务器)
|
||||||
1. ```sh
|
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),以下略
|
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):
|
def __parse_buy_guard(self, command):
|
||||||
data = command['data']
|
data = command['data']
|
||||||
return self._on_buy_guard(blivedm.GuardBuyMessage(
|
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
|
None, None, data['start_time'], None
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -149,34 +149,21 @@ class Room(blivedm.BLiveClient):
|
|||||||
|
|
||||||
id_ = uuid.uuid4().hex
|
id_ = uuid.uuid4().hex
|
||||||
# 为了节省带宽用list而不是dict
|
# 为了节省带宽用list而不是dict
|
||||||
self.send_message(Command.ADD_TEXT, [
|
self.send_message(Command.ADD_TEXT, make_text_message(
|
||||||
# 0: avatarUrl
|
|
||||||
await models.avatar.get_avatar_url(danmaku.uid),
|
await models.avatar.get_avatar_url(danmaku.uid),
|
||||||
# 1: timestamp
|
|
||||||
int(danmaku.timestamp / 1000),
|
int(danmaku.timestamp / 1000),
|
||||||
# 2: authorName
|
|
||||||
danmaku.uname,
|
danmaku.uname,
|
||||||
# 3: authorType
|
|
||||||
author_type,
|
author_type,
|
||||||
# 4: content
|
|
||||||
danmaku.msg,
|
danmaku.msg,
|
||||||
# 5: privilegeType
|
|
||||||
danmaku.privilege_type,
|
danmaku.privilege_type,
|
||||||
# 6: isGiftDanmaku
|
danmaku.msg_type,
|
||||||
1 if danmaku.msg_type else 0,
|
|
||||||
# 7: authorLevel
|
|
||||||
danmaku.user_level,
|
danmaku.user_level,
|
||||||
# 8: isNewbie
|
danmaku.urank < 10000,
|
||||||
1 if danmaku.urank < 10000 else 0,
|
danmaku.mobile_verify,
|
||||||
# 9: isMobileVerified
|
|
||||||
1 if danmaku.mobile_verify else 0,
|
|
||||||
# 10: medalLevel
|
|
||||||
0 if danmaku.room_id != self.room_id else danmaku.medal_level,
|
0 if danmaku.room_id != self.room_id else danmaku.medal_level,
|
||||||
# 11: id
|
|
||||||
id_,
|
id_,
|
||||||
# 12: translation
|
|
||||||
translation
|
translation
|
||||||
])
|
))
|
||||||
|
|
||||||
if need_translate:
|
if need_translate:
|
||||||
await self._translate_and_response(danmaku.msg, id_)
|
await self._translate_and_response(danmaku.msg, id_)
|
||||||
@ -206,7 +193,8 @@ class Room(blivedm.BLiveClient):
|
|||||||
'id': id_,
|
'id': id_,
|
||||||
'avatarUrl': await models.avatar.get_avatar_url(message.uid),
|
'avatarUrl': await models.avatar.get_avatar_url(message.uid),
|
||||||
'timestamp': message.start_time,
|
'timestamp': message.start_time,
|
||||||
'authorName': message.username
|
'authorName': message.username,
|
||||||
|
'privilegeType': message.guard_level
|
||||||
})
|
})
|
||||||
|
|
||||||
async def _on_super_chat(self, message: blivedm.SuperChatMessage):
|
async def _on_super_chat(self, message: blivedm.SuperChatMessage):
|
||||||
@ -244,8 +232,10 @@ class Room(blivedm.BLiveClient):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def _need_translate(self, text):
|
def _need_translate(self, text):
|
||||||
|
cfg = config.get_config()
|
||||||
return (
|
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 self.auto_translate_count > 0
|
||||||
and models.translate.need_translate(text)
|
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:
|
class RoomManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._rooms: Dict[int, Room] = {}
|
self._rooms: Dict[int, Room] = {}
|
||||||
@ -284,8 +307,7 @@ class RoomManager:
|
|||||||
if client.auto_translate:
|
if client.auto_translate:
|
||||||
room.auto_translate_count += 1
|
room.auto_translate_count += 1
|
||||||
|
|
||||||
if client.application.settings['debug']:
|
await client.on_join_room()
|
||||||
await client.send_test_message()
|
|
||||||
|
|
||||||
def del_client(self, room_id, client: 'ChatHandler'):
|
def del_client(self, room_id, client: 'ChatHandler'):
|
||||||
room = self._rooms.get(room_id, None)
|
room = self._rooms.get(room_id, None)
|
||||||
@ -390,6 +412,41 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
|||||||
return True
|
return True
|
||||||
return super().check_origin(origin)
|
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):
|
async def send_test_message(self):
|
||||||
base_data = {
|
base_data = {
|
||||||
@ -397,37 +454,25 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
|||||||
'timestamp': int(time.time()),
|
'timestamp': int(time.time()),
|
||||||
'authorName': 'xfgryujk',
|
'authorName': 'xfgryujk',
|
||||||
}
|
}
|
||||||
text_data = [
|
text_data = make_text_message(
|
||||||
# 0: avatarUrl
|
|
||||||
base_data['avatarUrl'],
|
base_data['avatarUrl'],
|
||||||
# 1: timestamp
|
|
||||||
base_data['timestamp'],
|
base_data['timestamp'],
|
||||||
# 2: authorName
|
|
||||||
base_data['authorName'],
|
base_data['authorName'],
|
||||||
# 3: authorType
|
|
||||||
0,
|
0,
|
||||||
# 4: content
|
|
||||||
'我能吞下玻璃而不伤身体',
|
'我能吞下玻璃而不伤身体',
|
||||||
# 5: privilegeType
|
|
||||||
0,
|
0,
|
||||||
# 6: isGiftDanmaku
|
False,
|
||||||
0,
|
|
||||||
# 7: authorLevel
|
|
||||||
20,
|
20,
|
||||||
# 8: isNewbie
|
False,
|
||||||
|
True,
|
||||||
0,
|
0,
|
||||||
# 9: isMobileVerified
|
|
||||||
1,
|
|
||||||
# 10: medalLevel
|
|
||||||
0,
|
|
||||||
# 11: id
|
|
||||||
uuid.uuid4().hex,
|
uuid.uuid4().hex,
|
||||||
# 12: translation
|
|
||||||
''
|
''
|
||||||
]
|
)
|
||||||
member_data = {
|
member_data = {
|
||||||
**base_data,
|
**base_data,
|
||||||
'id': uuid.uuid4().hex
|
'id': uuid.uuid4().hex,
|
||||||
|
'privilegeType': 3
|
||||||
}
|
}
|
||||||
gift_data = {
|
gift_data = {
|
||||||
**base_data,
|
**base_data,
|
||||||
@ -461,14 +506,3 @@ class ChatHandler(tornado.websocket.WebSocketHandler):
|
|||||||
gift_data['totalCoin'] = 1245000
|
gift_data['totalCoin'] = 1245000
|
||||||
gift_data['giftName'] = '小电视飞船'
|
gift_data['giftName'] = '小电视飞船'
|
||||||
self.send_message(Command.ADD_GIFT, gift_data)
|
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):
|
class MainHandler(tornado.web.StaticFileHandler):
|
||||||
"""为了使用Vue Router的history模式,把所有请求转发到index.html"""
|
"""为了使用Vue Router的history模式,把不存在的文件请求转发到index.html"""
|
||||||
async def get(self, *args, **kwargs):
|
async def get(self, path, include_body=True):
|
||||||
await super().get('index.html', *args, **kwargs)
|
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
|
# noinspection PyAbstractClass
|
||||||
@ -20,6 +26,7 @@ class ServerInfoHandler(api.base.ApiHandler):
|
|||||||
self.write({
|
self.write({
|
||||||
'version': update.VERSION,
|
'version': update.VERSION,
|
||||||
'config': {
|
'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():
|
def init():
|
||||||
reload()
|
if reload():
|
||||||
|
return
|
||||||
|
logger.warning('Using default config')
|
||||||
|
global _config
|
||||||
|
_config = AppConfig()
|
||||||
|
|
||||||
|
|
||||||
def reload():
|
def reload():
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
if config.load(CONFIG_PATH):
|
if not config.load(CONFIG_PATH):
|
||||||
global _config
|
return False
|
||||||
_config = config
|
global _config
|
||||||
|
_config = config
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_config():
|
def get_config():
|
||||||
@ -31,14 +37,29 @@ class AppConfig:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.database_url = 'sqlite:///data/database.db'
|
self.database_url = 'sqlite:///data/database.db'
|
||||||
self.enable_translate = True
|
self.enable_translate = True
|
||||||
|
self.allow_translate_rooms = {}
|
||||||
|
self.tornado_xheaders = False
|
||||||
|
self.loader_url = ''
|
||||||
|
|
||||||
def load(self, path):
|
def load(self, path):
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(path)
|
|
||||||
try:
|
try:
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(path)
|
||||||
|
|
||||||
app_section = config['app']
|
app_section = config['app']
|
||||||
self.database_url = app_section['database_url']
|
self.database_url = app_section['database_url']
|
||||||
self.enable_translate = app_section.getboolean('enable_translate')
|
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):
|
except (KeyError, ValueError):
|
||||||
logger.exception('Failed to load config:')
|
logger.exception('Failed to load config:')
|
||||||
return False
|
return False
|
||||||
|
@ -1,11 +1,25 @@
|
|||||||
[app]
|
[app]
|
||||||
# See https://docs.sqlalchemy.org/en/13/core/engines.html#database-urls
|
# See https://docs.sqlalchemy.org/en/13/core/engines.html#database-urls
|
||||||
database_url = sqlite:///data/database.db
|
database_url = sqlite:///data/database.db
|
||||||
|
|
||||||
# Enable auto translate to Japanese
|
# Enable auto translate to Japanese
|
||||||
enable_translate = true
|
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
|
# DON'T modify this section
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
database_url = sqlite:///data/database.db
|
database_url = sqlite:///data/database.db
|
||||||
enable_translate = true
|
enable_translate = true
|
||||||
|
allow_translate_rooms =
|
||||||
|
tornado_xheaders = false
|
||||||
|
loader_url =
|
||||||
|
@ -5,7 +5,7 @@ export const DEFAULT_CONFIG = {
|
|||||||
showDanmaku: true,
|
showDanmaku: true,
|
||||||
showGift: true,
|
showGift: true,
|
||||||
showGiftName: false,
|
showGiftName: false,
|
||||||
mergeSimilarDanmaku: true,
|
mergeSimilarDanmaku: false,
|
||||||
mergeGift: true,
|
mergeGift: true,
|
||||||
maxNumber: 60,
|
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)
|
return 'CN¥' + utils.formatCurrency(this.price)
|
||||||
},
|
},
|
||||||
timeText() {
|
timeText() {
|
||||||
return utils.getTimeTextMinSec(this.time)
|
return utils.getTimeTextHourMin(this.time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
timeText() {
|
timeText() {
|
||||||
return utils.getTimeTextMinSec(this.time)
|
return utils.getTimeTextHourMin(this.time)
|
||||||
},
|
},
|
||||||
authorTypeText() {
|
authorTypeText() {
|
||||||
return constants.AUTHOR_TYPE_TO_TEXT[this.authorType]
|
return constants.AUTHOR_TYPE_TO_TEXT[this.authorType]
|
||||||
|
@ -26,11 +26,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="pinnedMessage">
|
<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"
|
class="style-scope yt-live-chat-ticker-renderer"
|
||||||
:avatarUrl="pinnedMessage.avatarUrl" :title="pinnedMessage.title" :content="pinnedMessage.content"
|
:avatarUrl="pinnedMessage.avatarUrl" :authorName="pinnedMessage.authorName" :privilegeType="pinnedMessage.privilegeType"
|
||||||
:time="pinnedMessage.time"
|
:title="pinnedMessage.title" :time="pinnedMessage.time"
|
||||||
></legacy-paid-message>
|
></membership-item>
|
||||||
<paid-message :key="pinnedMessage.id" v-else
|
<paid-message :key="pinnedMessage.id" v-else
|
||||||
class="style-scope yt-live-chat-ticker-renderer"
|
class="style-scope yt-live-chat-ticker-renderer"
|
||||||
:price="pinnedMessage.price" :avatarUrl="pinnedMessage.avatarUrl" :authorName="pinnedMessage.authorName"
|
:price="pinnedMessage.price" :avatarUrl="pinnedMessage.avatarUrl" :authorName="pinnedMessage.authorName"
|
||||||
@ -44,7 +44,7 @@
|
|||||||
import * as config from '@/api/config'
|
import * as config from '@/api/config'
|
||||||
import {formatCurrency} from '@/utils'
|
import {formatCurrency} from '@/utils'
|
||||||
import ImgShadow from './ImgShadow.vue'
|
import ImgShadow from './ImgShadow.vue'
|
||||||
import LegacyPaidMessage from './LegacyPaidMessage.vue'
|
import MembershipItem from './MembershipItem.vue'
|
||||||
import PaidMessage from './PaidMessage.vue'
|
import PaidMessage from './PaidMessage.vue'
|
||||||
import * as constants from './constants'
|
import * as constants from './constants'
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ export default {
|
|||||||
name: 'Ticker',
|
name: 'Ticker',
|
||||||
components: {
|
components: {
|
||||||
ImgShadow,
|
ImgShadow,
|
||||||
LegacyPaidMessage,
|
MembershipItem,
|
||||||
PaidMessage
|
PaidMessage
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
|
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
|
||||||
:time="message.time" :content="getGiftShowContent(message)"
|
:time="message.time" :content="getGiftShowContent(message)"
|
||||||
></paid-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"
|
class="style-scope yt-live-chat-item-list-renderer"
|
||||||
:avatarUrl="message.avatarUrl" :title="message.title" :content="message.content"
|
:avatarUrl="message.avatarUrl" :authorName="message.authorName" :privilegeType="message.privilegeType"
|
||||||
:time="message.time"
|
:title="message.title" :time="message.time"
|
||||||
></legacy-paid-message>
|
></membership-item>
|
||||||
<paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
|
<paid-message :key="message.id" v-else-if="message.type === MESSAGE_TYPE_SUPER_CHAT"
|
||||||
class="style-scope yt-live-chat-item-list-renderer"
|
class="style-scope yt-live-chat-item-list-renderer"
|
||||||
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
|
:price="message.price" :avatarUrl="message.avatarUrl" :authorName="message.authorName"
|
||||||
@ -45,7 +45,7 @@
|
|||||||
import * as config from '@/api/config'
|
import * as config from '@/api/config'
|
||||||
import Ticker from './Ticker.vue'
|
import Ticker from './Ticker.vue'
|
||||||
import TextMessage from './TextMessage.vue'
|
import TextMessage from './TextMessage.vue'
|
||||||
import LegacyPaidMessage from './LegacyPaidMessage.vue'
|
import MembershipItem from './MembershipItem.vue'
|
||||||
import PaidMessage from './PaidMessage.vue'
|
import PaidMessage from './PaidMessage.vue'
|
||||||
import * as constants from './constants'
|
import * as constants from './constants'
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
Ticker,
|
Ticker,
|
||||||
TextMessage,
|
TextMessage,
|
||||||
LegacyPaidMessage,
|
MembershipItem,
|
||||||
PaidMessage
|
PaidMessage
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@ -281,16 +281,25 @@ export default {
|
|||||||
if (this.estimatedEnqueueInterval) {
|
if (this.estimatedEnqueueInterval) {
|
||||||
estimatedNextEnqueueRemainTime = Math.max(this.lastEnqueueTime - new Date() + this.estimatedEnqueueInterval, 1)
|
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
|
let groupNumToEmit
|
||||||
if (this.smoothedMessageQueue.length < estimatedNextEnqueueRemainTime / 80) {
|
if (shouldEmitGroupNum < maxCanEmitCount) {
|
||||||
// 队列中消息数很少,每次发1条也能发完
|
// 队列中消息数很少,每次发1条也能发到最多剩3条
|
||||||
groupNumToEmit = 1
|
groupNumToEmit = 1
|
||||||
} else {
|
} else {
|
||||||
// 每次发1条以上,保证按最快速度能发完
|
// 每次发1条以上,保证按最快速度能发到最多剩3条
|
||||||
groupNumToEmit = Math.ceil(this.smoothedMessageQueue.length / (estimatedNextEnqueueRemainTime / 80))
|
groupNumToEmit = Math.ceil(shouldEmitGroupNum / maxCanEmitCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发消息
|
||||||
let messageGroups = this.smoothedMessageQueue.splice(0, groupNumToEmit)
|
let messageGroups = this.smoothedMessageQueue.splice(0, groupNumToEmit)
|
||||||
let mergedGroup = []
|
let mergedGroup = []
|
||||||
for (let messageGroup of messageGroups) {
|
for (let messageGroup of messageGroups) {
|
||||||
@ -303,19 +312,20 @@ export default {
|
|||||||
if (this.smoothedMessageQueue.length <= 0) {
|
if (this.smoothedMessageQueue.length <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 消息没发完,计算下次发消息时间
|
||||||
let sleepTime
|
let sleepTime
|
||||||
if (groupNumToEmit == 1) {
|
if (groupNumToEmit === 1) {
|
||||||
// 队列中消息数很少,随便定个80-1000ms的时间
|
// 队列中消息数很少,随便定个[MIN_SLEEP_TIME, MAX_SLEEP_TIME]的时间
|
||||||
sleepTime = estimatedNextEnqueueRemainTime / this.smoothedMessageQueue.length
|
sleepTime = estimatedNextEnqueueRemainTime / this.smoothedMessageQueue.length
|
||||||
sleepTime *= 0.5 + Math.random()
|
sleepTime *= 0.5 + Math.random()
|
||||||
if (sleepTime > 1000) {
|
if (sleepTime > MAX_SLEEP_TIME) {
|
||||||
sleepTime = 1000
|
sleepTime = MAX_SLEEP_TIME
|
||||||
} else if (sleepTime < 80) {
|
} else if (sleepTime < MIN_SLEEP_TIME) {
|
||||||
sleepTime = 80
|
sleepTime = MIN_SLEEP_TIME
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 按最快速度发
|
// 按最快速度发
|
||||||
sleepTime = 80
|
sleepTime = MIN_SLEEP_TIME
|
||||||
}
|
}
|
||||||
this.emitSmoothedMessageTimerId = window.setTimeout(this.emitSmoothedMessages, sleepTime)
|
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-author-name-font-size: 13px;
|
||||||
--yt-pdg-paid-stickers-margin-left: 38px;
|
--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>
|
</style>
|
||||||
|
|
||||||
<!-- yt-live-chat-app -->
|
<!-- yt-live-chat-app -->
|
||||||
|
@ -5,7 +5,6 @@ export default {
|
|||||||
help: 'Help',
|
help: 'Help',
|
||||||
projectAddress: 'Project address',
|
projectAddress: 'Project address',
|
||||||
giftRecordOfficial: 'Official Super Chat record',
|
giftRecordOfficial: 'Official Super Chat record',
|
||||||
giftRecord: 'Super Chat record'
|
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
roomIdEmpty: "Room ID can't be empty",
|
roomIdEmpty: "Room ID can't be empty",
|
||||||
@ -53,6 +52,7 @@ export default {
|
|||||||
avatarSize: 'Avatar size',
|
avatarSize: 'Avatar size',
|
||||||
|
|
||||||
userNames: 'User names',
|
userNames: 'User names',
|
||||||
|
showUserNames: 'Show user names',
|
||||||
font: 'Font',
|
font: 'Font',
|
||||||
fontSize: 'Font size',
|
fontSize: 'Font size',
|
||||||
lineHeight: 'Line height (0 for default)',
|
lineHeight: 'Line height (0 for default)',
|
||||||
|
@ -5,7 +5,6 @@ export default {
|
|||||||
help: 'ヘルプ',
|
help: 'ヘルプ',
|
||||||
projectAddress: 'プロジェクトアドレス',
|
projectAddress: 'プロジェクトアドレス',
|
||||||
giftRecordOfficial: '公式スーパーチャット記録',
|
giftRecordOfficial: '公式スーパーチャット記録',
|
||||||
giftRecord: 'スーパーチャット記録'
|
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
roomIdEmpty: 'ルームのIDを空白にすることはできません',
|
roomIdEmpty: 'ルームのIDを空白にすることはできません',
|
||||||
@ -53,6 +52,7 @@ export default {
|
|||||||
avatarSize: 'アイコンのサイズ',
|
avatarSize: 'アイコンのサイズ',
|
||||||
|
|
||||||
userNames: 'ユーザー名',
|
userNames: 'ユーザー名',
|
||||||
|
showUserNames: 'ユーザー名を表示する',
|
||||||
font: 'フォント',
|
font: 'フォント',
|
||||||
fontSize: 'フォントサイズ',
|
fontSize: 'フォントサイズ',
|
||||||
lineHeight: '行の高さ(0はデフォルト)',
|
lineHeight: '行の高さ(0はデフォルト)',
|
||||||
|
@ -5,7 +5,6 @@ export default {
|
|||||||
help: '帮助',
|
help: '帮助',
|
||||||
projectAddress: '项目地址',
|
projectAddress: '项目地址',
|
||||||
giftRecordOfficial: '官方打赏记录',
|
giftRecordOfficial: '官方打赏记录',
|
||||||
giftRecord: '打赏记录'
|
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
roomIdEmpty: '房间ID不能为空',
|
roomIdEmpty: '房间ID不能为空',
|
||||||
@ -53,6 +52,7 @@ export default {
|
|||||||
avatarSize: '头像尺寸',
|
avatarSize: '头像尺寸',
|
||||||
|
|
||||||
userNames: '用户名',
|
userNames: '用户名',
|
||||||
|
showUserNames: '显示用户名',
|
||||||
font: '字体',
|
font: '字体',
|
||||||
fontSize: '字体尺寸',
|
fontSize: '字体尺寸',
|
||||||
lineHeight: '行高(0为默认)',
|
lineHeight: '行高(0为默认)',
|
||||||
|
@ -26,11 +26,6 @@
|
|||||||
<i class="el-icon-share"></i>{{$t('sidebar.giftRecordOfficial')}}
|
<i class="el-icon-share"></i>{{$t('sidebar.giftRecordOfficial')}}
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
</a>
|
</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">
|
<el-submenu index="null">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<i class="el-icon-chat-line-square"></i>Language
|
<i class="el-icon-chat-line-square"></i>Language
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="version">
|
<div class="version">
|
||||||
v1.4.4
|
v1.4.5
|
||||||
</div>
|
</div>
|
||||||
<sidebar></sidebar>
|
<sidebar></sidebar>
|
||||||
</el-aside>
|
</el-aside>
|
||||||
|
@ -27,8 +27,8 @@ export function formatCurrency (price) {
|
|||||||
}).format(price)
|
}).format(price)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTimeTextMinSec (date) {
|
export function getTimeTextHourMin (date) {
|
||||||
|
let hour = date.getHours()
|
||||||
let min = ('00' + date.getMinutes()).slice(-2)
|
let min = ('00' + date.getMinutes()).slice(-2)
|
||||||
let sec = ('00' + date.getSeconds()).slice(-2)
|
return `${hour}:${min}`
|
||||||
return `${min}:${sec}`
|
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
<el-input v-model="form.blockUsers" type="textarea" :rows="5" :placeholder="$t('home.onePerLine')"></el-input>
|
<el-input v-model="form.blockUsers" type="textarea" :rows="5" :placeholder="$t('home.onePerLine')"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('home.blockMedalLevel')">
|
<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-form-item>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
@ -66,7 +66,7 @@
|
|||||||
|
|
||||||
<el-divider></el-divider>
|
<el-divider></el-divider>
|
||||||
<el-form-item :label="$t('home.roomUrl')">
|
<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-button type="primary" @click="copyUrl">{{$t('home.copy')}}</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
@ -90,7 +90,8 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
serverConfig: {
|
serverConfig: {
|
||||||
enableTranslate: true
|
enableTranslate: true,
|
||||||
|
loaderUrl: ''
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
roomId: parseInt(window.localStorage.roomId || '1'),
|
roomId: parseInt(window.localStorage.roomId || '1'),
|
||||||
@ -107,6 +108,17 @@ export default {
|
|||||||
delete query.roomId
|
delete query.roomId
|
||||||
let resolved = this.$router.resolve({name: 'room', params: {roomId: this.form.roomId}, query})
|
let resolved = this.$router.resolve({name: 'room', params: {roomId: this.form.roomId}, query})
|
||||||
return `${window.location.protocol}//${window.location.host}${resolved.href}`
|
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: {
|
watch: {
|
||||||
@ -121,7 +133,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
async updateServerConfig() {
|
async updateServerConfig() {
|
||||||
try {
|
try {
|
||||||
this.serverConfig = (await axios.get(`/server_info`)).data.config
|
this.serverConfig = (await axios.get('/api/server_info')).data.config
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$message.error('Failed to fetch server information: ' + e)
|
this.$message.error('Failed to fetch server information: ' + e)
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ export default {
|
|||||||
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
|
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||||
// 开发时使用localhost:12450
|
// 开发时使用localhost:12450
|
||||||
const host = process.env.NODE_ENV === 'development' ? 'localhost:12450' : window.location.host
|
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 = new WebSocket(url)
|
||||||
this.websocket.onopen = this.onWsOpen
|
this.websocket.onopen = this.onWsOpen
|
||||||
this.websocket.onclose = this.onWsClose
|
this.websocket.onclose = this.onWsClose
|
||||||
@ -188,8 +188,8 @@ export default {
|
|||||||
avatarUrl: data.avatarUrl,
|
avatarUrl: data.avatarUrl,
|
||||||
time: new Date(data.timestamp * 1000),
|
time: new Date(data.timestamp * 1000),
|
||||||
authorName: data.authorName,
|
authorName: data.authorName,
|
||||||
title: 'NEW MEMBER!',
|
privilegeType: data.privilegeType,
|
||||||
content: `Welcome ${data.authorName}!`
|
title: 'New member'
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case COMMAND_ADD_SUPER_CHAT:
|
case COMMAND_ADD_SUPER_CHAT:
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<h3>{{$t('stylegen.userNames')}}</h3>
|
<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-form-item :label="$t('stylegen.font')">
|
||||||
<el-autocomplete v-model="form.userNameFont" :fetch-suggestions="getFontSuggestions"></el-autocomplete>
|
<el-autocomplete v-model="form.userNameFont" :fetch-suggestions="getFontSuggestions"></el-autocomplete>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -219,15 +222,15 @@ let textMessageTemplate = {
|
|||||||
repeated: 1,
|
repeated: 1,
|
||||||
translation: ''
|
translation: ''
|
||||||
}
|
}
|
||||||
let legacyPaidMessageTemplate = {
|
let membershipItemTemplate = {
|
||||||
id: 0,
|
id: 0,
|
||||||
addTime: time,
|
addTime: time,
|
||||||
type: constants.MESSAGE_TYPE_MEMBER,
|
type: constants.MESSAGE_TYPE_MEMBER,
|
||||||
avatarUrl: 'https://static.hdslb.com/images/member/noface.gif',
|
avatarUrl: 'https://static.hdslb.com/images/member/noface.gif',
|
||||||
time: time,
|
time: time,
|
||||||
authorName: '',
|
authorName: '',
|
||||||
title: 'NEW MEMBER!',
|
privilegeType: 3,
|
||||||
content: ''
|
title: 'New member'
|
||||||
}
|
}
|
||||||
let paidMessageTemplate = {
|
let paidMessageTemplate = {
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -266,15 +269,14 @@ const EXAMPLE_MESSAGES = [
|
|||||||
content: 'kksk'
|
content: 'kksk'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...legacyPaidMessageTemplate,
|
...membershipItemTemplate,
|
||||||
id: (nextId++).toString(),
|
id: (nextId++).toString(),
|
||||||
authorName: '进击的冰糖',
|
authorName: '艾米亚official'
|
||||||
content: 'Welcome 进击的冰糖!'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...paidMessageTemplate,
|
...paidMessageTemplate,
|
||||||
id: (nextId++).toString(),
|
id: (nextId++).toString(),
|
||||||
authorName: '无火的残渣',
|
authorName: '愛里紗メイプル',
|
||||||
price: 66600,
|
price: 66600,
|
||||||
content: 'Sent 小电视飞船x100'
|
content: 'Sent 小电视飞船x100'
|
||||||
},
|
},
|
||||||
@ -288,7 +290,7 @@ const EXAMPLE_MESSAGES = [
|
|||||||
{
|
{
|
||||||
...paidMessageTemplate,
|
...paidMessageTemplate,
|
||||||
id: (nextId++).toString(),
|
id: (nextId++).toString(),
|
||||||
authorName: '夏色祭保護協会会長',
|
authorName: 'AstralisUP',
|
||||||
price: 30,
|
price: 30,
|
||||||
content: '言いたいことがあるんだよ!'
|
content: '言いたいことがあるんだよ!'
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ export const DEFAULT_CONFIG = {
|
|||||||
showAvatars: true,
|
showAvatars: true,
|
||||||
avatarSize: 24,
|
avatarSize: 24,
|
||||||
|
|
||||||
|
showUserNames: true,
|
||||||
userNameFont: 'Changa One',
|
userNameFont: 'Changa One',
|
||||||
userNameFontSize: 20,
|
userNameFontSize: 20,
|
||||||
userNameLineHeight: 0,
|
userNameLineHeight: 0,
|
||||||
@ -16,7 +17,7 @@ export const DEFAULT_CONFIG = {
|
|||||||
ownerUserNameColor: '#ffd600',
|
ownerUserNameColor: '#ffd600',
|
||||||
moderatorUserNameColor: '#5e84f1',
|
moderatorUserNameColor: '#5e84f1',
|
||||||
memberUserNameColor: '#0f9d58',
|
memberUserNameColor: '#0f9d58',
|
||||||
showBadges: false,
|
showBadges: true,
|
||||||
showColon: true,
|
showColon: true,
|
||||||
|
|
||||||
messageFont: 'Imprima',
|
messageFont: 'Imprima',
|
||||||
@ -107,13 +108,13 @@ yt-live-chat-author-chip #author-name {
|
|||||||
/* Outlines */
|
/* Outlines */
|
||||||
yt-live-chat-renderer * {
|
yt-live-chat-renderer * {
|
||||||
${getShowOutlinesStyle(config)}
|
${getShowOutlinesStyle(config)}
|
||||||
font-family: "${config.messageFont}"${FALLBACK_FONTS};
|
font-family: "${cssEscapeStr(config.messageFont)}"${FALLBACK_FONTS};
|
||||||
font-size: ${config.messageFontSize}px !important;
|
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-text-message-renderer #content,
|
||||||
yt-live-chat-legacy-paid-message-renderer #content {
|
yt-live-chat-membership-item-renderer #content {
|
||||||
overflow: initial !important;
|
overflow: initial !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,12 +134,7 @@ yt-live-chat-message-input-renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Reduce side padding. */
|
/* Reduce side padding. */
|
||||||
yt-live-chat-text-message-renderer,
|
yt-live-chat-text-message-renderer {
|
||||||
yt-live-chat-legacy-paid-message-renderer {
|
|
||||||
${getPaddingStyle(config)}
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-paid-message-renderer #header {
|
|
||||||
${getPaddingStyle(config)}
|
${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-text-message-renderer #author-photo img,
|
||||||
yt-live-chat-paid-message-renderer #author-photo,
|
yt-live-chat-paid-message-renderer #author-photo,
|
||||||
yt-live-chat-paid-message-renderer #author-photo img,
|
yt-live-chat-paid-message-renderer #author-photo img,
|
||||||
yt-live-chat-legacy-paid-message-renderer #author-photo,
|
yt-live-chat-membership-item-renderer #author-photo,
|
||||||
yt-live-chat-legacy-paid-message-renderer #author-photo img {
|
yt-live-chat-membership-item-renderer #author-photo img {
|
||||||
${config.showAvatars ? '' : 'display: none !important;'}
|
${config.showAvatars ? '' : 'display: none !important;'}
|
||||||
width: ${config.avatarSize}px !important;
|
width: ${config.avatarSize}px !important;
|
||||||
height: ${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 {
|
yt-live-chat-text-message-renderer #timestamp {
|
||||||
display: ${config.showTime ? 'inline' : 'none'} !important;
|
display: ${config.showTime ? 'inline' : 'none'} !important;
|
||||||
${config.timeColor ? `color: ${config.timeColor} !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;
|
font-size: ${config.timeFontSize}px !important;
|
||||||
line-height: ${config.timeLineHeight || 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. */
|
/* Channel names. */
|
||||||
yt-live-chat-text-message-renderer #author-name {
|
yt-live-chat-text-message-renderer #author-name {
|
||||||
|
${config.showUserNames ? '' : 'display: none !important;'}
|
||||||
${config.userNameColor ? `color: ${config.userNameColor} !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;
|
font-size: ${config.userNameFontSize}px !important;
|
||||||
line-height: ${config.userNameLineHeight || 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,
|
||||||
yt-live-chat-text-message-renderer #message * {
|
yt-live-chat-text-message-renderer #message * {
|
||||||
${config.messageColor ? `color: ${config.messageColor} !important;` : ''}
|
${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;
|
font-size: ${config.messageFontSize}px !important;
|
||||||
line-height: ${config.messageLineHeight || 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. */
|
/* SuperChat/Fan Funding Messages. */
|
||||||
yt-live-chat-paid-message-renderer #author-name,
|
yt-live-chat-paid-message-renderer #author-name,
|
||||||
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-membership-item-renderer #header-content-inner-column,
|
||||||
yt-live-chat-legacy-paid-message-renderer #event-text * {
|
yt-live-chat-membership-item-renderer #header-content-inner-column * {
|
||||||
${config.firstLineColor ? `color: ${config.firstLineColor} !important;` : ''}
|
${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;
|
font-size: ${config.firstLineFontSize}px !important;
|
||||||
line-height: ${config.firstLineLineHeight || 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-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-membership-item-renderer #header-subtext,
|
||||||
yt-live-chat-legacy-paid-message-renderer #detail-text * {
|
yt-live-chat-membership-item-renderer #header-subtext * {
|
||||||
${config.secondLineColor ? `color: ${config.secondLineColor} !important;` : ''}
|
${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;
|
font-size: ${config.secondLineFontSize}px !important;
|
||||||
line-height: ${config.secondLineLineHeight || 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,
|
||||||
yt-live-chat-paid-message-renderer #content * {
|
yt-live-chat-paid-message-renderer #content * {
|
||||||
${config.scContentColor ? `color: ${config.scContentColor} !important;` : ''}
|
${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;
|
font-size: ${config.scContentFontSize}px !important;
|
||||||
line-height: ${config.scContentLineHeight || 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;
|
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)}
|
${getShowNewMemberBgStyle(config)}
|
||||||
}
|
}
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer a,
|
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;
|
text-decoration: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer[is-deleted],
|
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;
|
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,
|
||||||
yt-live-chat-ticker-sponsor-item-renderer * {
|
yt-live-chat-ticker-sponsor-item-renderer * {
|
||||||
${config.secondLineColor ? `color: ${config.secondLineColor} !important;` : ''}
|
${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,
|
yt-live-chat-mode-change-message-renderer,
|
||||||
@ -339,6 +337,31 @@ function getShowOutlinesStyle (config) {
|
|||||||
return `text-shadow: ${shadow.join(', ')};`
|
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) {
|
function getPaddingStyle (config) {
|
||||||
return `padding-left: ${config.useBarsInsteadOfBg ? 20 : 4}px !important;
|
return `padding-left: ${config.useBarsInsteadOfBg ? 20 : 4}px !important;
|
||||||
padding-right: 4px !important;`
|
padding-right: 4px !important;`
|
||||||
@ -399,7 +422,8 @@ ${keyframes.join('\n')}
|
|||||||
}
|
}
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer,
|
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: anim ${totalTime}ms;
|
||||||
animation-fill-mode: both;
|
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 argparse
|
||||||
import logging
|
import logging
|
||||||
|
import logging.handlers
|
||||||
import os
|
import os
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
@ -18,15 +19,19 @@ import update
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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 = [
|
routes = [
|
||||||
|
(r'/api/server_info', api.main.ServerInfoHandler),
|
||||||
|
(r'/api/chat', api.chat.ChatHandler),
|
||||||
|
|
||||||
|
# TODO 兼容旧版,下版本移除
|
||||||
(r'/server_info', api.main.ServerInfoHandler),
|
(r'/server_info', api.main.ServerInfoHandler),
|
||||||
(r'/chat', api.chat.ChatHandler),
|
(r'/chat', api.chat.ChatHandler),
|
||||||
|
|
||||||
(r'/((css|fonts|img|js|static)/.*)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
|
(r'/(.*)', api.main.MainHandler, {'path': WEB_ROOT, 'default_filename': 'index.html'})
|
||||||
(r'/(favicon\.ico)', tornado.web.StaticFileHandler, {'path': WEB_ROOT}),
|
|
||||||
(r'/.*', api.main.MainHandler, {'path': WEB_ROOT})
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -45,7 +50,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
def parse_args():
|
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('--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('--port', help='服务器端口,默认为12450', type=int, default=12450)
|
||||||
parser.add_argument('--debug', help='调试模式', action='store_true')
|
parser.add_argument('--debug', help='调试模式', action='store_true')
|
||||||
@ -53,11 +58,16 @@ def parse_args():
|
|||||||
|
|
||||||
|
|
||||||
def init_logging(debug):
|
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(
|
logging.basicConfig(
|
||||||
format='{asctime} {levelname} [{name}]: {message}',
|
format='{asctime} {levelname} [{name}]: {message}',
|
||||||
datefmt='%Y-%m-%d %H:%M:%S',
|
datefmt='%Y-%m-%d %H:%M:%S',
|
||||||
style='{',
|
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,
|
debug=debug,
|
||||||
autoreload=False
|
autoreload=False
|
||||||
)
|
)
|
||||||
|
cfg = config.get_config()
|
||||||
try:
|
try:
|
||||||
app.listen(port, host)
|
app.listen(
|
||||||
|
port,
|
||||||
|
host,
|
||||||
|
xheaders=cfg.tornado_xheaders
|
||||||
|
)
|
||||||
except OSError:
|
except OSError:
|
||||||
logger.warning('Address is used %s:%d', host, port)
|
logger.warning('Address is used %s:%d', host, port)
|
||||||
return
|
return
|
||||||
|
@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
NO_TRANSLATE_TEXTS = {
|
NO_TRANSLATE_TEXTS = {
|
||||||
'草', '草草', '草草草', '草生', '大草原', '上手', '上手上手', '理解', '理解理解', '天才', '天才天才',
|
'草', '草草', '草草草', '草生', '大草原', '上手', '上手上手', '理解', '理解理解', '天才', '天才天才',
|
||||||
'强', '余裕', '余裕余裕', '大丈夫', '再放送', '放送事故'
|
'强', '余裕', '余裕余裕', '大丈夫', '再放送', '放送事故', '清楚', '清楚清楚'
|
||||||
}
|
}
|
||||||
|
|
||||||
_main_event_loop = asyncio.get_event_loop()
|
_main_event_loop = asyncio.get_event_loop()
|
||||||
|
Loading…
Reference in New Issue
Block a user